├── 35MSSharedLib1024.snk ├── Common ├── Extensions │ ├── CollectionExtensions.cs │ ├── EnumerableExtensions.cs │ ├── EqualityComparer.cs │ ├── Extensions.cs │ ├── FilesystemExtensions.cs │ ├── MutableEnumerable.cs │ └── ReEnumerable.cs ├── Utility │ ├── FileUtility.cs │ ├── PathUtility.cs │ └── XmlUtility.cs └── Version │ ├── DependencyVersion.cs │ ├── SemanticVersion.cs │ └── SemanticVersionTypeConverter.cs ├── Extensions ├── CollectionExtensions.cs ├── EnumerableExtensions.cs ├── EqualityComparer.cs ├── Extensions.cs ├── FilesystemExtensions.cs ├── MutableEnumerable.cs ├── PackageListExtensions.cs └── ReEnumerable.cs ├── PackageList ├── ExePackageInstaller.cs ├── NupkgInstaller.cs ├── PackageListParser.cs ├── PackageListProvider.cs ├── PackageListRequest.cs ├── PackageQuery.cs ├── PackageSource.cs ├── PowerShellArtifactInstaller.cs ├── ProgressTracker.cs ├── WebDownloader.cs └── ZipPackageInstaller.cs ├── PackageSourceListProvider.csproj ├── PackageSourceListProvider.sln ├── Properties └── AssemblyInfo.cs ├── README.md ├── Resources ├── Messages.Designer.cs └── Messages.resx ├── Sdk ├── Constants.cs └── Request.cs ├── Utility ├── FileUtility.cs ├── PathUtility.cs └── XmlUtility.cs ├── Version ├── DependencyVersion.cs ├── SemanticVersion.cs └── SemanticVersionTypeConverter.cs ├── appveyor.yml ├── dependencies ├── Microsoft.PackageManagement-win10-1607.dll └── mt.exe ├── docs ├── find-package.png ├── high-level-diagram.md ├── install-package.png └── known-issue.md ├── provider.manifest └── test ├── psl.tests.ps1 └── testpsl.json /35MSSharedLib1024.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneGet/PSLProvider/eb9147dad37ebe2a548806f7391b888a7518c81c/35MSSharedLib1024.snk -------------------------------------------------------------------------------- /Common/Extensions/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Reflection; 20 | using System.Threading.Tasks; 21 | using Internal.Utility.Collections; 22 | using System.Diagnostics.CodeAnalysis; 23 | 24 | internal static class CollectionExtensions { 25 | private static readonly MethodInfo _castMethod = typeof (Enumerable).GetMethod("Cast"); 26 | private static readonly MethodInfo _toArrayMethod = typeof (Enumerable).GetMethod("ToArray"); 27 | private static readonly IDictionary _castMethods = new Dictionary(); 28 | private static readonly IDictionary _toArrayMethods = new Dictionary(); 29 | 30 | /// 31 | /// Determines whether the collection object is either null or an empty collection. 32 | /// 33 | /// 34 | /// The collection. 35 | /// 36 | /// true if [is null or empty] [the specified collection]; otherwise, false . 37 | /// 38 | /// 39 | /// 40 | 41 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 42 | public static bool IsNullOrEmpty(this IEnumerable collection) { 43 | return collection == null || !collection.Any(); 44 | } 45 | 46 | /// 47 | /// Concatenates a single item to an IEnumerable 48 | /// 49 | /// 50 | /// 51 | /// 52 | /// 53 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 54 | public static IEnumerable ConcatSingleItem(this IEnumerable enumerable, T item) { 55 | return enumerable.Concat(new[] { 56 | item 57 | }); 58 | } 59 | 60 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 61 | public static IEnumerable WhereNotNull(this IEnumerable enumerable) { 62 | return enumerable == null ? Enumerable.Empty() : enumerable.Where(each => (object)each != null); 63 | } 64 | 65 | public static IEnumerable ToIEnumerable(this IEnumerator enumerator) { 66 | if (enumerator != null) { 67 | while (enumerator.MoveNext()) { 68 | yield return enumerator.Current; 69 | } 70 | } 71 | } 72 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 73 | public static IEnumerable Concat(this IEnumerable set1, IEnumerator set2) { 74 | var s1 = set1 ?? Enumerable.Empty(); 75 | var s2 = set2 == null ? Enumerable.Empty() : set2.ToIEnumerable(); 76 | return s1.Concat(s2); 77 | } 78 | 79 | public static IEnumerable SingleItemAsEnumerable(this T item) { 80 | return new T[] { 81 | item 82 | }; 83 | } 84 | 85 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 86 | public static void AddRangeLocked(this List list, IEnumerable items) { 87 | if (list == null) { 88 | throw new ArgumentNullException("list"); 89 | } 90 | lock (list) { 91 | list.AddRange(items); 92 | } 93 | } 94 | 95 | //public static void ParallelForEach(this IEnumerable enumerable, Action action) { 96 | // var items = enumerable.ReEnumerable(); 97 | // var first = items.FirstOrDefault(); 98 | // if (first != null) { 99 | // object second = items.Skip(1).FirstOrDefault(); 100 | // if (second != null) { 101 | // Parallel.ForEach(items, new ParallelOptions { 102 | // MaxDegreeOfParallelism = -1, 103 | // TaskScheduler = new ThreadPerTaskScheduler() 104 | // }, action); 105 | // } else { 106 | // action(first); 107 | // } 108 | // } 109 | //} 110 | 111 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 112 | public static void SerialForEach(this IEnumerable enumerable, Action action) { 113 | var items = enumerable.ReEnumerable(); 114 | object first = items.FirstOrDefault(); 115 | if (first != null) { 116 | object second = items.Skip(1).FirstOrDefault(); 117 | if (second != null) { 118 | foreach (var item in items) { 119 | action(item); 120 | } 121 | } else { 122 | action(items.FirstOrDefault()); 123 | } 124 | } 125 | } 126 | 127 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 128 | public static IEnumerable AsyncForEach(this IEnumerable enumerable, Action action) { 129 | return enumerable.Select(i => Task.Factory.StartNew(() => action(i))); 130 | } 131 | 132 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 133 | public static void WaitAll(this IEnumerable tasks) { 134 | Task.WaitAll(tasks.ToArray()); 135 | } 136 | 137 | //public static object ToIEnumerableT(this IEnumerable enumerable, Type elementType) { 138 | // return _castMethods.GetOrAdd(elementType, () => _castMethod.MakeGenericMethod(elementType)).Invoke(null, new object[] {enumerable}); 139 | //} 140 | 141 | //public static object ToArrayT(this IEnumerable enumerable, Type elementType) { 142 | // return _toArrayMethods.GetOrAdd(elementType, () => _toArrayMethod.MakeGenericMethod(elementType)).Invoke(null, new[] {enumerable.ToIEnumerableT(elementType)}); 143 | //} 144 | 145 | /// 146 | /// returns the index position where the predicate matches the value 147 | /// 148 | /// 149 | /// 150 | /// 151 | /// 152 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 153 | public static int IndexWhere(this IEnumerable source, Func predicate) { 154 | if (source != null && predicate != null) { 155 | var index = -1; 156 | if (source.Any(element => predicate(element, ++index))) { 157 | return index; 158 | } 159 | } 160 | return -1; 161 | } 162 | 163 | /// 164 | /// returns the index position where the predicate matches the value 165 | /// 166 | /// 167 | /// 168 | /// 169 | /// 170 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 171 | public static int IndexWhere(this IEnumerable source, Func predicate) { 172 | if (source != null && predicate != null) { 173 | var index = -1; 174 | if (source.Any(element => { 175 | ++index; 176 | return predicate(element); 177 | })) { 178 | return index; 179 | } 180 | } 181 | return -1; 182 | } 183 | } 184 | 185 | // Provides a task scheduler that dedicates a thread per task. 186 | } -------------------------------------------------------------------------------- /Common/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | 20 | internal static class EnumerableExtensions { 21 | /// 22 | /// Returns a ReEnumerable wrapper around the collection which timidly (cautiously) pulls items 23 | /// but still allows you to to re-enumerate without re-running the query. 24 | /// 25 | /// 26 | /// 27 | /// 28 | public static MutableEnumerable ReEnumerable(this IEnumerable collection) { 29 | if (collection == null) { 30 | return new ReEnumerable(Enumerable.Empty()); 31 | } 32 | return collection as MutableEnumerable ?? new ReEnumerable(collection); 33 | } 34 | 35 | public static IEnumerable FilterWithFinalizer(this IEnumerable source, Func predicate, Action onFilterAction) { 36 | foreach (var i in source) { 37 | if (predicate(i)) { 38 | onFilterAction(i); 39 | } else { 40 | yield return i; 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Common/Extensions/EqualityComparer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Collections.Generic; 18 | 19 | internal class EqualityComparer : IEqualityComparer { 20 | private readonly Func _compareFn; 21 | private readonly Func _hashFn; 22 | 23 | public EqualityComparer(Func compareFn, Func hashFn) { 24 | _compareFn = compareFn; 25 | _hashFn = hashFn; 26 | } 27 | 28 | public EqualityComparer(Func compareFn) { 29 | _compareFn = compareFn; 30 | _hashFn = (obj) =>obj.GetHashCode(); 31 | } 32 | 33 | public bool Equals(T x, T y) { 34 | return _compareFn(x, y); 35 | } 36 | 37 | public int GetHashCode(T obj) { 38 | return _hashFn(obj); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Common/Extensions/Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.PackageManagement.Provider.Utility 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Globalization; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Text.RegularExpressions; 12 | using System.Threading.Tasks; 13 | using System.Xml.Linq; 14 | 15 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 16 | internal static class HttpContentExtensions { 17 | 18 | internal static async Task ReadAsFileAsync(this HttpContent content, string fileName) { 19 | if (string.IsNullOrWhiteSpace(fileName)) 20 | { 21 | throw new ArgumentException(fileName); 22 | } 23 | 24 | //Get the absolute path 25 | var pathName = Path.GetFullPath(fileName); 26 | 27 | using (var fileStream = new FileStream(pathName, FileMode.Create, FileAccess.Write, FileShare.None)) { 28 | await content.CopyToAsync(fileStream); 29 | return fileStream.Length; 30 | } 31 | } 32 | } 33 | 34 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 35 | internal static class XElementExtensions 36 | { 37 | internal static IEnumerable ElementsNoNamespace(this XContainer container, string localName) 38 | { 39 | return container.Elements().Where(e => e.Name.LocalName == localName); 40 | } 41 | 42 | /// 43 | /// Get the optional attribute value depends on the localName and the nameSpace. Returns null if we can't find it 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 50 | internal static string GetOptionalAttributeValue(this XElement element, string localName, string namespaceName = null) 51 | { 52 | XAttribute attr; 53 | // Name space is null so don't use it 54 | if (String.IsNullOrWhiteSpace(namespaceName)) 55 | { 56 | attr = element.Attribute(localName); 57 | } 58 | else 59 | { 60 | attr = element.Attribute(XName.Get(localName, namespaceName)); 61 | } 62 | 63 | return attr != null ? attr.Value : null; 64 | } 65 | } 66 | 67 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 68 | internal static class VersionExtensions 69 | { 70 | internal static IEnumerable GetComparableVersionStrings(this SemanticVersion version) 71 | { 72 | Version coreVersion = version.Version; 73 | string specialVersion = String.IsNullOrWhiteSpace(version.SpecialVersion) ? String.Empty : "-" + version.SpecialVersion; 74 | string[] originalVersionComponents = version.GetOriginalVersionComponents(); 75 | 76 | 77 | if (coreVersion.Revision == 0) 78 | { 79 | if (coreVersion.Build == 0) 80 | { 81 | yield return String.Format( 82 | CultureInfo.InvariantCulture, 83 | "{0}.{1}{2}", 84 | originalVersionComponents[0], 85 | originalVersionComponents[1], 86 | specialVersion); 87 | } 88 | 89 | yield return String.Format( 90 | CultureInfo.InvariantCulture, 91 | "{0}.{1}.{2}{3}", 92 | originalVersionComponents[0], 93 | originalVersionComponents[1], 94 | originalVersionComponents[2], 95 | specialVersion); 96 | } 97 | 98 | yield return String.Format( 99 | CultureInfo.InvariantCulture, 100 | "{0}.{1}.{2}.{3}{4}", 101 | originalVersionComponents[0], 102 | originalVersionComponents[1], 103 | originalVersionComponents[2], 104 | originalVersionComponents[3], 105 | specialVersion); 106 | 107 | } 108 | } 109 | 110 | internal static class ObjectExtensions 111 | { 112 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 113 | internal static string ToStringSafe(this object obj) 114 | { 115 | return obj == null ? null : obj.ToString(); 116 | } 117 | } 118 | 119 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 120 | internal static class StringExtensions 121 | { 122 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 123 | internal static string SafeTrim(this string value) 124 | { 125 | return value == null ? null : value.Trim(); 126 | } 127 | 128 | internal static string ToBase64(this string text) 129 | { 130 | if (text == null) 131 | { 132 | return null; 133 | } 134 | return Convert.ToBase64String(text.ToByteArray()); 135 | } 136 | 137 | internal static string FromBase64(this string text) 138 | { 139 | if (text == null) 140 | { 141 | return null; 142 | } 143 | return Convert.FromBase64String(text).ToUtf8String(); 144 | } 145 | 146 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 147 | internal static string FixVersion(this string versionString) 148 | { 149 | if (!String.IsNullOrWhiteSpace(versionString)) 150 | { 151 | if (versionString[0] == '.') 152 | { 153 | // make sure we have a leading zero when someone says .5 154 | versionString = "0" + versionString; 155 | } 156 | 157 | if (versionString.IndexOf('.') == -1) 158 | { 159 | // make sure we make a 1 work like 1.0 160 | versionString = versionString + ".0"; 161 | } 162 | } 163 | 164 | return versionString; 165 | } 166 | 167 | // Encodes the string as an array of UTF8 bytes. 168 | private static byte[] ToByteArray(this string text) 169 | { 170 | return Encoding.UTF8.GetBytes(text); 171 | } 172 | 173 | // Creates a string from a collection of UTF8 bytes 174 | private static string ToUtf8String(this IEnumerable bytes) 175 | { 176 | var data = bytes.ToArray(); 177 | try 178 | { 179 | return Encoding.UTF8.GetString(data); 180 | } 181 | finally 182 | { 183 | Array.Clear(data, 0, data.Length); 184 | } 185 | } 186 | 187 | public static bool EqualsIgnoreCase(this string str, string str2) 188 | { 189 | if (str == null && str2 == null) 190 | { 191 | return true; 192 | } 193 | 194 | if (str == null || str2 == null) 195 | { 196 | return false; 197 | } 198 | 199 | return str.Equals(str2, StringComparison.OrdinalIgnoreCase); 200 | } 201 | 202 | public static bool IsTrue(this string text) 203 | { 204 | return !string.IsNullOrWhiteSpace(text) && text.Equals("true", StringComparison.CurrentCultureIgnoreCase); 205 | } 206 | 207 | public static bool CompareVersion(this string version1, string version2) 208 | { 209 | if(string.IsNullOrWhiteSpace(version1) && string.IsNullOrWhiteSpace(version2)) 210 | { 211 | return true; 212 | } 213 | if (string.IsNullOrWhiteSpace(version1) || string.IsNullOrWhiteSpace(version2)) 214 | { 215 | return false; 216 | } 217 | 218 | var semver1 = new SemanticVersion(version1); 219 | var semver2 = new SemanticVersion(version2); 220 | return (semver1 == semver2); 221 | } 222 | } 223 | 224 | internal static class LinqExtensions 225 | { 226 | internal static Dictionary ToDictionaryNicely(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) 227 | { 228 | if (source == null) 229 | { 230 | throw new ArgumentNullException("source"); 231 | } 232 | if (keySelector == null) 233 | { 234 | throw new ArgumentNullException("keySelector"); 235 | } 236 | if (elementSelector == null) 237 | { 238 | throw new ArgumentNullException("elementSelector"); 239 | } 240 | 241 | var d = new Dictionary(comparer); 242 | foreach (var element in source) 243 | { 244 | d.AddOrSet(keySelector(element), elementSelector(element)); 245 | } 246 | return d; 247 | } 248 | 249 | internal static TValue AddOrSet(this IDictionary dictionary, TKey key, TValue value) 250 | { 251 | lock (dictionary) 252 | { 253 | if (dictionary.ContainsKey(key)) 254 | { 255 | dictionary[key] = value; 256 | } 257 | else 258 | { 259 | dictionary.Add(key, value); 260 | } 261 | } 262 | return value; 263 | } 264 | 265 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 266 | internal static TSource SafeAggregate(this IEnumerable source, Func func) 267 | { 268 | var src = source.ToArray(); 269 | if (source != null && src.Any()) 270 | { 271 | return src.Aggregate(func); 272 | } 273 | return default(TSource); 274 | } 275 | } 276 | } -------------------------------------------------------------------------------- /Common/Extensions/FilesystemExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Diagnostics; 18 | using System.Diagnostics.CodeAnalysis; 19 | using System.Globalization; 20 | using System.IO; 21 | using System.Text; 22 | using System.Text.RegularExpressions; 23 | 24 | internal static class FilesystemExtensions { 25 | private static readonly char[] _pathCharacters = "/\\".ToCharArray(); 26 | private static int _counter = Process.GetCurrentProcess().Id << 16; 27 | public static string OriginalTempFolder; 28 | 29 | static FilesystemExtensions() { 30 | OriginalTempFolder = OriginalTempFolder ?? Path.GetTempPath(); 31 | // ResetTempFolder(); 32 | } 33 | 34 | public static string TempPath {get; private set;} 35 | 36 | public static int Counter { 37 | get { 38 | return ++_counter; 39 | } 40 | } 41 | 42 | public static string CounterHex { 43 | get { 44 | return Counter.ToString("x8", CultureInfo.CurrentCulture); 45 | } 46 | } 47 | 48 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 49 | public static bool LooksLikeAFilename(this string text) { 50 | return text.IndexOfAny(_pathCharacters) > -1; 51 | } 52 | 53 | public static void TryHardToDelete(this string location) 54 | { 55 | if (Directory.Exists(location)) 56 | { 57 | try 58 | { 59 | Directory.Delete(location, true); 60 | } 61 | catch 62 | { 63 | // didn't take, eh? 64 | } 65 | } 66 | 67 | if (File.Exists(location)) 68 | { 69 | try 70 | { 71 | File.Delete(location); 72 | } 73 | catch 74 | { 75 | // didn't take, eh? 76 | } 77 | } 78 | 79 | // if it is still there, move and mark it. 80 | if (File.Exists(location) || Directory.Exists(location)) 81 | { 82 | try 83 | { 84 | // move the file to the tmp file 85 | // and tell the OS to remove it next reboot. 86 | var tmpFilename = location.GenerateTemporaryFilename() + ".delete_me"; // generates a unique filename but not a file! 87 | //MoveFileOverwrite(location, tmpFilename); 88 | 89 | if (File.Exists(location) || Directory.Exists(location)) 90 | { 91 | // of course, if the tmpFile isn't on the same volume as the location, this doesn't work. 92 | // then, last ditch effort, let's rename it in the current directory 93 | // and then we can hide it and mark it for cleanup . 94 | tmpFilename = Path.Combine(Path.GetDirectoryName(location), "tmp." + CounterHex + "." + Path.GetFileName(location) + ".delete_me"); 95 | //MoveFileOverwrite(location, tmpFilename); 96 | if (File.Exists(tmpFilename) || Directory.Exists(location)) 97 | { 98 | // hide the file for convenience. 99 | File.SetAttributes(tmpFilename, File.GetAttributes(tmpFilename) | FileAttributes.Hidden); 100 | } 101 | } 102 | 103 | // Now we mark the locked file to be deleted upon next reboot (or until another coapp app gets there) 104 | // MoveFileOverwrite(File.Exists(tmpFilename) ? tmpFilename : location, null); 105 | } 106 | catch 107 | { 108 | // really. Hmmm. 109 | } 110 | 111 | if (File.Exists(location)) 112 | { 113 | // err("Unable to forcably remove file '{0}'. This can't be good.", location); 114 | } 115 | } 116 | return; 117 | } 118 | 119 | ///// 120 | ///// File move abstraction that can be implemented to handle non-windows platforms 121 | ///// 122 | ///// 123 | ///// 124 | //public static void MoveFileOverwrite(string sourceFile, string destinationFile) { 125 | // NativeMethods.MoveFileEx(sourceFile, destinationFile, MoveFileFlags.ReplaceExisting); 126 | //} 127 | 128 | //public static void MoveFileAtNextBoot(string sourceFile, string destinationFile) { 129 | // NativeMethods.MoveFileEx(sourceFile, destinationFile, MoveFileFlags.DelayUntilReboot); 130 | //} 131 | 132 | public static string GenerateTemporaryFilename(this string filename) { 133 | string ext = null; 134 | string name = null; 135 | string folder = null; 136 | 137 | if (!string.IsNullOrWhiteSpace(filename)) { 138 | ext = Path.GetExtension(filename); 139 | name = Path.GetFileNameWithoutExtension(filename); 140 | folder = Path.GetDirectoryName(filename); 141 | } 142 | 143 | if (string.IsNullOrWhiteSpace(ext)) { 144 | ext = ".tmp"; 145 | } 146 | if (string.IsNullOrWhiteSpace(folder)) { 147 | folder = TempPath; 148 | } 149 | 150 | do 151 | { 152 | name = Path.Combine(folder, "tmpFile." + Path.GetRandomFileName() + (string.IsNullOrWhiteSpace(name) ? ext : "." + name + ext)); 153 | } 154 | while (File.Exists(name)); 155 | 156 | // return MarkFileTemporary(name); 157 | return name; 158 | } 159 | 160 | //public static void ResetTempFolder() { 161 | // // set the temporary folder to be a child of the User temporary folder 162 | // // based on the application name 163 | // var appName = typeof(FilesystemExtensions).GetTypeInfo().Assembly.GetName().Name; 164 | // if (OriginalTempFolder.IndexOf(appName, StringComparison.CurrentCultureIgnoreCase) == -1) { 165 | // var appTempPath = Path.Combine(OriginalTempFolder, appName); 166 | // if (!Directory.Exists(appTempPath)) { 167 | // Directory.CreateDirectory(appTempPath); 168 | // } 169 | 170 | // TempPath = Path.Combine(appTempPath, (Directory.EnumerateDirectories(appTempPath).Count() + 1).ToString(CultureInfo.CurrentCulture)); 171 | // if (!Directory.Exists(TempPath)) { 172 | // Directory.CreateDirectory(TempPath); 173 | // } 174 | 175 | // // make sure this temp directory gets marked for eventual cleanup. 176 | // MoveFileOverwrite(appTempPath, null); 177 | // MoveFileAtNextBoot(TempPath, null); 178 | // } 179 | 180 | // TempPath = TempPath ?? OriginalTempFolder; 181 | //} 182 | 183 | /// 184 | /// This takes a string that is representative of a filename and tries to create a path that can be considered the 185 | /// 'canonical' path. path on drives that are mapped as remote shares are rewritten as their \\server\share\path 186 | /// 187 | /// 188 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 189 | public static string CanonicalizePath(this string path, bool isPotentiallyRelativePath) { 190 | Uri pathUri = null; 191 | try { 192 | pathUri = new Uri(path); 193 | if (!pathUri.IsFile) { 194 | // perhaps try getting the fullpath 195 | try { 196 | pathUri = new Uri(Path.GetFullPath(path)); 197 | } catch { 198 | return null; 199 | } 200 | } 201 | 202 | // is this a unc path? 203 | if (string.IsNullOrWhiteSpace(pathUri.Host)) { 204 | // no, this is a drive:\path path 205 | // use API to resolve out the drive letter to see if it is a remote 206 | var drive = pathUri.Segments[1].Replace('/', '\\'); // the zero segment is always just '/' 207 | 208 | var sb = new StringBuilder(512); 209 | var size = sb.Capacity; 210 | 211 | //TODO 212 | // var error = NativeMethods.WNetGetConnection(drive, sb, ref size); 213 | //if (error == 0) { 214 | // if (pathUri.Segments.Length > 2) { 215 | // return pathUri.Segments.Skip(2).Aggregate(sb.ToString().Trim(), (current, item) => current + item); 216 | // } 217 | // } 218 | } 219 | // not a remote (or resovably-remote) path or 220 | // it is already a path that is in it's correct form (via localpath) 221 | return pathUri.LocalPath; 222 | } catch (UriFormatException) { 223 | // we could try to see if it is a relative path... 224 | if (isPotentiallyRelativePath) { 225 | return CanonicalizePath(Path.GetFullPath(path), false); 226 | } 227 | return null; 228 | } 229 | } 230 | 231 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 232 | public static byte[] ReadBytes(this string path, int maxLength) { 233 | if (path.FileExists()) { 234 | try { 235 | var buffer = new byte[Math.Min(new FileInfo(path).Length, maxLength)]; 236 | using (var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { 237 | file.Read(buffer, 0, buffer.Length); 238 | } 239 | 240 | return buffer; 241 | } catch { 242 | // not openable. whatever. 243 | } 244 | } 245 | return new byte[0]; 246 | } 247 | 248 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 249 | public static bool FileExists(this string path) { 250 | if (!string.IsNullOrWhiteSpace(path)) { 251 | try { 252 | return File.Exists(CanonicalizePath(path, true)); 253 | } catch { 254 | } 255 | } 256 | return false; 257 | } 258 | 259 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 260 | public static bool DirectoryExists(this string path) { 261 | if (!string.IsNullOrWhiteSpace(path)) { 262 | try { 263 | return Directory.Exists(CanonicalizePath(path, true)); 264 | } catch { 265 | } 266 | } 267 | return false; 268 | } 269 | 270 | public static string MakeSafeFileName(this string input) { 271 | return new Regex(@"-+").Replace(new Regex(@"[^\d\w\[\]_\-\.\ ]").Replace(input, "-"), "-").Replace(" ", ""); 272 | } 273 | } 274 | } -------------------------------------------------------------------------------- /Common/Extensions/MutableEnumerable.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System.Collections; 17 | using System.Collections.Generic; 18 | 19 | internal abstract class MutableEnumerable : IEnumerable { 20 | private List _list; 21 | 22 | protected List List { 23 | get { 24 | if (_list == null) { 25 | lock (this) { 26 | if (_list == null) { 27 | _list = new List(); 28 | } 29 | } 30 | } 31 | return _list; 32 | } 33 | } 34 | 35 | public IEnumerable> GetEnumerators(int copies) { 36 | for (var i = 0; i < copies; i++) { 37 | yield return GetEnumerator(); 38 | } 39 | } 40 | 41 | protected abstract bool ItemExists(int index); 42 | 43 | internal class Enumerator : IEnumerator { 44 | private MutableEnumerable _collection; 45 | private int _index = -1; 46 | 47 | internal Enumerator(MutableEnumerable collection) { 48 | _collection = collection; 49 | } 50 | 51 | #region IEnumerator Members 52 | 53 | public TT Current { 54 | get { 55 | return _collection.List[_index]; 56 | } 57 | } 58 | 59 | public void Dispose() { 60 | _collection = null; 61 | } 62 | 63 | object IEnumerator.Current { 64 | get { 65 | return Current; 66 | } 67 | } 68 | 69 | public bool MoveNext() { 70 | _index++; 71 | return _collection.ItemExists(_index); 72 | } 73 | 74 | public void Reset() { 75 | _index = -1; 76 | } 77 | 78 | public IEnumerator Clone() { 79 | return new Enumerator(_collection) { 80 | _index = _index 81 | }; 82 | } 83 | 84 | #endregion 85 | } 86 | 87 | #region IEnumerable Members 88 | 89 | public IEnumerator GetEnumerator() { 90 | return new Enumerator(this); 91 | } 92 | 93 | IEnumerator IEnumerable.GetEnumerator() { 94 | return GetEnumerator(); 95 | } 96 | 97 | #endregion 98 | } 99 | } -------------------------------------------------------------------------------- /Common/Extensions/ReEnumerable.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | 19 | internal class ReEnumerable : MutableEnumerable { 20 | private IEnumerator _sourceIterator; 21 | private readonly IEnumerable _source; 22 | 23 | public ReEnumerable(IEnumerable source) { 24 | _source = source ?? new T[0]; 25 | } 26 | 27 | public ReEnumerable(IEnumerator sourceIterator) { 28 | _source = null; 29 | _sourceIterator = sourceIterator; 30 | } 31 | 32 | public T this[int index] { 33 | get { 34 | if (ItemExists(index)) { 35 | return List[index]; 36 | } 37 | return default(T); 38 | } 39 | } 40 | 41 | public int Count { 42 | get { 43 | return this.Count(); 44 | } 45 | } 46 | 47 | protected override bool ItemExists(int index) { 48 | if (index < List.Count) { 49 | return true; 50 | } 51 | 52 | lock (this) { 53 | if (_sourceIterator == null) { 54 | _sourceIterator = _source.GetEnumerator(); 55 | } 56 | 57 | try { 58 | while (_sourceIterator.MoveNext()) { 59 | List.Add(_sourceIterator.Current); 60 | if (index < List.Count) { 61 | return true; 62 | } 63 | } 64 | } catch { 65 | // if the _sourceIterator is cancelled 66 | // then MoveNext() will throw; that's ok 67 | // that just means we're done 68 | } 69 | } 70 | return false; 71 | } 72 | 73 | public MutableEnumerable Concat(IEnumerable additionalItems) { 74 | return Enumerable.Concat(this, additionalItems).ReEnumerable(); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Common/Utility/FileUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.PackageManagement.Provider.Utility 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text.RegularExpressions; 9 | using System.Threading; 10 | 11 | public static class FileUtility 12 | { 13 | public static string GetTempFileFullPath(string fileExtension) 14 | { 15 | 16 | if (string.IsNullOrWhiteSpace(fileExtension)) 17 | { 18 | return fileExtension; 19 | } 20 | 21 | string tempFolder = Path.GetTempPath(); 22 | 23 | string randomFileName = Path.GetRandomFileName(); 24 | 25 | //get rid of the file extension 26 | randomFileName = Path.GetFileNameWithoutExtension(randomFileName) + fileExtension; 27 | 28 | string file = Path.Combine(tempFolder, randomFileName); 29 | 30 | if (File.Exists(file)) 31 | { 32 | //try it again if the generated file already exists 33 | file = GetTempFileFullPath(fileExtension); 34 | } 35 | 36 | return file; 37 | } 38 | 39 | public static string MakePackageFileName(bool excludeVersion, string packageName, string version, string fileExtension) 40 | { 41 | string fileName = (excludeVersion) ? packageName : (packageName + "." + version); 42 | return fileName + fileExtension; 43 | } 44 | 45 | public static string MakePackageDirectoryName(bool excludeVersion, string destinationPath, string packageName, string version) 46 | { 47 | string baseDir = Path.Combine(destinationPath, packageName); 48 | 49 | return (excludeVersion) ? baseDir : (baseDir + "." + version); 50 | } 51 | 52 | public static void CopyDirectory(string source, string dest, bool copySubDirs) 53 | { 54 | // Get the subdirectories for the specified directory. 55 | DirectoryInfo dir = new DirectoryInfo(source); 56 | 57 | if (!dir.Exists) 58 | { 59 | throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, "Source directory '{0}' does not exist or could not be found.", source)); 60 | } 61 | 62 | // Some packages have directories with spaces. However it shows as $20, e.g. Install-Module -name xHyper-VBackup. 63 | // It has something like Content\Deployment\Module%20References\..., with that, the PowerShellGet provider won't be able to handle it. 64 | // Add the following code to unescape percent-encoding characters 65 | string newdest = Uri.UnescapeDataString(dest); 66 | 67 | // If the destination directory doesn't exist, create it. 68 | if (!Directory.Exists(newdest)) 69 | { 70 | Directory.CreateDirectory(newdest); 71 | } 72 | 73 | // Get the files in the directory and copy them to the new location. 74 | FileInfo[] files = dir.GetFiles(); 75 | foreach (FileInfo file in files) 76 | { 77 | string tempPath = Path.Combine(newdest, file.Name); 78 | // unescape special characters like %20 79 | tempPath = Uri.UnescapeDataString(tempPath); 80 | 81 | file.CopyTo(tempPath, true /*overwrite*/); 82 | } 83 | 84 | // If copying subdirectories, copy them and their contents to new location. 85 | if (copySubDirs) 86 | { 87 | DirectoryInfo[] sourceDirs = dir.GetDirectories(); 88 | 89 | foreach (DirectoryInfo subdir in sourceDirs) 90 | { 91 | string temppath = Path.Combine(newdest, subdir.Name); 92 | CopyDirectory(subdir.FullName, temppath, true); 93 | } 94 | } 95 | } 96 | 97 | public static void DeleteDirectory(string fullPath, bool recursive, bool isThrow) 98 | { 99 | DoSafeAction(() => DeleteDirectory(fullPath, recursive), isThrow); 100 | } 101 | 102 | public static void DeleteFile(string fullPath, bool isThrow) 103 | { 104 | DoSafeAction(() => DeleteFile(fullPath), isThrow); 105 | } 106 | 107 | public static IEnumerable GetFiles(string fullPath, string filter, bool recursive) 108 | { 109 | fullPath = PathUtility.EnsureTrailingSlash(fullPath); 110 | if (String.IsNullOrWhiteSpace(filter)) 111 | { 112 | filter = "*.*"; 113 | } 114 | try 115 | { 116 | if (!Directory.Exists(fullPath)) 117 | { 118 | return Enumerable.Empty(); 119 | } 120 | return Directory.EnumerateFiles(fullPath, filter, recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); 121 | // .Select(MakeRelativePath); 122 | } 123 | catch (UnauthorizedAccessException) 124 | { 125 | 126 | } 127 | catch (DirectoryNotFoundException) 128 | { 129 | 130 | } 131 | 132 | return Enumerable.Empty(); 133 | } 134 | 135 | public static IEnumerable GetDirectories(string fullPath) 136 | { 137 | try 138 | { 139 | fullPath = PathUtility.EnsureTrailingSlash(fullPath); 140 | if (!Directory.Exists(fullPath)) 141 | { 142 | return Enumerable.Empty(); 143 | } 144 | 145 | return Directory.EnumerateDirectories(fullPath); 146 | } 147 | catch (UnauthorizedAccessException) 148 | { 149 | 150 | } 151 | catch (DirectoryNotFoundException) 152 | { 153 | 154 | } 155 | 156 | return Enumerable.Empty(); 157 | } 158 | 159 | public static DateTimeOffset GetLastModified(string fullPath) 160 | { 161 | if (File.Exists(fullPath)) 162 | { 163 | return File.GetLastWriteTime(fullPath); 164 | } 165 | return Directory.GetLastWriteTime(fullPath); 166 | } 167 | 168 | private static void DeleteFile(string path) 169 | { 170 | if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) 171 | { 172 | return; 173 | } 174 | 175 | try 176 | { 177 | //sometimes there are difficulties to delete readonly files 178 | MakeFileWritable(path); 179 | File.Delete(path); 180 | } 181 | catch (FileNotFoundException) 182 | { 183 | } 184 | } 185 | 186 | private static void MakeFileWritable(string path) 187 | { 188 | FileAttributes attributes = File.GetAttributes(path); 189 | if (attributes.HasFlag(FileAttributes.ReadOnly)) 190 | { 191 | File.SetAttributes(path, attributes & ~FileAttributes.ReadOnly); 192 | } 193 | } 194 | 195 | private static void DeleteDirectory(string fullPath, bool recursive) 196 | { 197 | if (string.IsNullOrWhiteSpace(fullPath) || !Directory.Exists(fullPath)) 198 | { 199 | return; 200 | } 201 | 202 | try 203 | { 204 | //path = GetFullPath(path); 205 | Directory.Delete(fullPath, recursive); 206 | 207 | // The directory is not guaranteed to be gone since there could be 208 | // other open handles. Wait, up to half a second, until the directory is gone. 209 | for (int i = 0; Directory.Exists(fullPath) && i < 5; ++i) 210 | { 211 | Thread.Sleep(100); 212 | } 213 | } 214 | catch (DirectoryNotFoundException) 215 | { 216 | } 217 | } 218 | 219 | // [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to log an exception as a warning and move on")] 220 | private static void DoSafeAction(Action action, bool isThrow) 221 | { 222 | try 223 | { 224 | Attempt(action); 225 | } 226 | catch (Exception e) 227 | { 228 | if (isThrow) 229 | { 230 | throw new Exception(e.Message); 231 | } 232 | } 233 | } 234 | 235 | private static void Attempt(Action action, int retries = 5, int delayBeforeRetry = 100) 236 | { 237 | while (retries > 0) 238 | { 239 | try 240 | { 241 | action(); 242 | break; 243 | } 244 | catch 245 | { 246 | retries--; 247 | if (retries == 0) 248 | { 249 | throw; 250 | } 251 | } 252 | Thread.Sleep(delayBeforeRetry); 253 | } 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /Common/Utility/PathUtility.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Security; 3 | 4 | namespace Microsoft.PackageManagement.Provider.Utility 5 | { 6 | using System; 7 | using System.IO; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using System.Runtime.InteropServices; 13 | using System.Globalization; 14 | using System.Diagnostics.CodeAnalysis; 15 | 16 | internal static class PathUtility 17 | { 18 | 19 | internal static string EnsureTrailingSlash(string path) 20 | { 21 | //The value of DirectorySeparatorChar is a slash ("/") on UNIX, and a backslash ("\") on the Windows and Macintosh. 22 | return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar); 23 | } 24 | 25 | private static string EnsureTrailingCharacter(string path, char trailingCharacter) 26 | { 27 | if (path == null) 28 | { 29 | throw new ArgumentNullException("path"); 30 | } 31 | 32 | // if the path is empty, we want to return the original string instead of a single trailing character. 33 | if (path.Length == 0 || path[path.Length - 1] == trailingCharacter) 34 | { 35 | return path; 36 | } 37 | 38 | return path + trailingCharacter; 39 | } 40 | 41 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 42 | internal static HttpResponseMessage GetHttpResponse(HttpClient httpClient, string query, Func isCanceled, ActionlogRetry, Actionverbose, Actiondebug) 43 | { 44 | // try downloading for 3 times 45 | int remainingTry = 3; 46 | object timerLock = new object(); 47 | Timer timer = null; 48 | CancellationTokenSource cts = null; 49 | bool cleanUp = true; 50 | 51 | Action cleanUpAction = () => 52 | { 53 | lock (timerLock) 54 | { 55 | // check whether clean up is already done before or not 56 | if (!cleanUp) 57 | { 58 | try 59 | { 60 | if (timer != null) 61 | { 62 | // stop timer 63 | timer.Change(Timeout.Infinite, Timeout.Infinite); 64 | // dispose it 65 | timer.Dispose(); 66 | } 67 | 68 | // dispose the token 69 | if (cts != null) 70 | { 71 | cts.Cancel(); 72 | cts.Dispose(); 73 | } 74 | } 75 | catch { } 76 | 77 | cleanUp = true; 78 | } 79 | } 80 | }; 81 | 82 | while (remainingTry > 0) 83 | { 84 | // if user cancel the request, no need to do anything 85 | if (isCanceled()) 86 | { 87 | break; 88 | } 89 | 90 | try 91 | { 92 | // decrease try by 1 93 | remainingTry -= 1; 94 | 95 | // create new timer and cancellation token source 96 | lock (timerLock) 97 | { 98 | // check every second to see whether request is cancelled 99 | timer = new Timer(_ => 100 | { 101 | if (isCanceled()) 102 | { 103 | cleanUpAction(); 104 | } 105 | }, null, 500, 1000); 106 | 107 | cts = new CancellationTokenSource(); 108 | 109 | cleanUp = false; 110 | } 111 | 112 | Task task = httpClient.GetAsync(query, cts.Token); 113 | 114 | // start the task 115 | task.Wait(); 116 | 117 | if (task.IsCompleted && task is Task) 118 | { 119 | var result = (task as Task).Result; 120 | 121 | // if success, returns result 122 | if (result.IsSuccessStatusCode) 123 | { 124 | return result; 125 | } 126 | 127 | // otherwise, we have to retry again 128 | } 129 | 130 | // if request is canceled, don't retry 131 | if (isCanceled()) 132 | { 133 | break; 134 | } 135 | 136 | logRetry(query, remainingTry); 137 | } 138 | catch (Exception ex) 139 | { 140 | if (ex is AggregateException) 141 | { 142 | (ex as AggregateException).Handle(singleException => 143 | { 144 | // report each of the exception 145 | verbose(singleException.Message); 146 | debug(singleException.StackTrace); 147 | return true; 148 | }); 149 | } 150 | else 151 | { 152 | // single exception, just report the message and stacktrace 153 | verbose(ex.Message); 154 | debug(ex.StackTrace); 155 | } 156 | 157 | // if there is exception, we will retry too 158 | logRetry(query, remainingTry); 159 | } 160 | finally 161 | { 162 | cleanUpAction(); 163 | } 164 | } 165 | 166 | return null; 167 | } 168 | 169 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 170 | internal static string UriCombine(string query, string append) 171 | { 172 | if (String.IsNullOrWhiteSpace(query)) return append; 173 | if (String.IsNullOrWhiteSpace(append)) return query; 174 | 175 | return query.TrimEnd('/') + "/" + append.TrimStart('/'); 176 | } 177 | 178 | 179 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It's used in Nano")] 180 | internal static string SecureStringToString(SecureString secure) 181 | { 182 | IntPtr value = IntPtr.Zero; 183 | try 184 | { 185 | #if !CORECLR 186 | value = Marshal.SecureStringToCoTaskMemUnicode(secure); 187 | #else 188 | value = SecureStringMarshal.SecureStringToCoTaskMemUnicode(secure); 189 | #endif 190 | return Marshal.PtrToStringUni(value); 191 | } 192 | finally 193 | { 194 | #if !CORECLR 195 | Marshal.ZeroFreeGlobalAllocUnicode(value); 196 | #else 197 | SecureStringMarshal.ZeroFreeCoTaskMemUnicode(value); 198 | #endif 199 | } 200 | } 201 | 202 | internal static NetworkCredential GetNetworkCredential(string username, SecureString password) 203 | { 204 | // if request has username and password, use that 205 | if (!string.IsNullOrWhiteSpace(username) && password != null) 206 | { 207 | #if CORECLR 208 | // networkcredential class on coreclr does not accept securestring so we have to convert 209 | return new NetworkCredential(username, SecureStringToString(password)); 210 | #else 211 | return new NetworkCredential(username, password); 212 | #endif 213 | } 214 | 215 | // if no user name and password, returns null 216 | return null; 217 | } 218 | 219 | internal static HttpClient GetHttpClientHelper(string username, SecureString password, IWebProxy webProxy) 220 | { 221 | var clientHandler = new HttpClientHandler(); 222 | 223 | var networkCredential = GetNetworkCredential(username, password); 224 | 225 | // if we are given a network credential, use that 226 | if (networkCredential != null) 227 | { 228 | // else use the one given to us 229 | clientHandler.Credentials = networkCredential; 230 | clientHandler.PreAuthenticate = true; 231 | } 232 | else 233 | { 234 | clientHandler.UseDefaultCredentials = true; 235 | } 236 | 237 | // do not need to set proxy of httpClient or httpClientHandler because it will use system proxy setting by default 238 | // discussion here (https://github.com/dotnet/corefx/issues/7037) 239 | 240 | if (webProxy != null) 241 | { 242 | // if webproxy is not null, use that 243 | clientHandler.Proxy = webProxy; 244 | } 245 | 246 | var httpClient = new HttpClient(clientHandler); 247 | 248 | // Mozilla/5.0 is the general token that says the browser is Mozilla compatible, and is common to almost every browser today. 249 | httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 NuGet"); 250 | 251 | return httpClient; 252 | } 253 | 254 | } 255 | } -------------------------------------------------------------------------------- /Common/Utility/XmlUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.PackageManagement.Provider.Utility 2 | { 3 | using System.IO; 4 | using System.Xml; 5 | using System.Xml.Linq; 6 | 7 | public static class XmlUtility 8 | { 9 | public static XDocument LoadSafe(string filePath) 10 | { 11 | var settings = CreateSafeSettings(); 12 | using (var reader = XmlReader.Create(filePath, settings)) 13 | { 14 | return XDocument.Load(reader); 15 | } 16 | } 17 | 18 | public static XDocument LoadSafe(Stream input, bool ignoreWhiteSpace) 19 | { 20 | var settings = CreateSafeSettings(ignoreWhiteSpace); 21 | var reader = XmlReader.Create(input, settings); 22 | return XDocument.Load(reader); 23 | } 24 | 25 | private static XmlReaderSettings CreateSafeSettings(bool ignoreWhiteSpace = false) 26 | { 27 | var safeSettings = new XmlReaderSettings 28 | { 29 | DtdProcessing = DtdProcessing.Prohibit, 30 | IgnoreWhitespace = ignoreWhiteSpace 31 | }; 32 | 33 | return safeSettings; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Common/Version/DependencyVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Globalization; 7 | 8 | namespace Microsoft.PackageManagement.Provider.Utility 9 | { 10 | /// 11 | /// Represents dependency version returned by nuget api call 12 | /// 13 | public class DependencyVersion 14 | { 15 | public SemanticVersion MinVersion { get; set; } 16 | /// 17 | /// True if the version we are looking for includes min version 18 | /// 19 | 20 | public bool IsMinInclusive { get; set; } 21 | public SemanticVersion MaxVersion { get; set; } 22 | /// 23 | /// True if the version we are looking for includes the max version 24 | /// 25 | public bool IsMaxInclusive { get; set; } 26 | 27 | /// 28 | /// Parse and return a dependency version 29 | /// The version string is either a simple version or an arithmetic range 30 | /// e.g. 31 | /// 1.0 --> 1.0 ≤ x 32 | /// (,1.0] --> x ≤ 1.0 33 | /// (,1.0) --> x lt 1.0 34 | /// [1.0] --> x == 1.0 35 | /// (1.0,) --> 1.0 lt x 36 | /// (1.0, 2.0) --> 1.0 lt x lt 2.0 37 | /// [1.0, 2.0] --> 1.0 ≤ x ≤ 2.0 38 | /// 39 | /// 40 | /// 41 | /// 42 | public static DependencyVersion ParseDependencyVersion(string value) 43 | { 44 | var depVers = new DependencyVersion(); 45 | 46 | if (String.IsNullOrWhiteSpace(value)) 47 | { 48 | return depVers; 49 | } 50 | 51 | value = value.Trim(); 52 | 53 | char first = value.First(); 54 | char last = value.Last(); 55 | 56 | if (first != '(' && first != '[' && last != ']' && last != ')') 57 | { 58 | // Stand alone version 59 | depVers.IsMinInclusive = true; 60 | depVers.MinVersion = new SemanticVersion(value); 61 | return depVers; 62 | } 63 | 64 | // value must have length greater than 3 65 | if (value.Length < 3) 66 | { 67 | return depVers; 68 | } 69 | 70 | // The first character must be [ or ( 71 | switch (value.First()) 72 | { 73 | case '[': 74 | depVers.IsMinInclusive = true; 75 | break; 76 | case '(': 77 | depVers.IsMinInclusive = false; 78 | break; 79 | default: 80 | // If not, return without setting anything 81 | return depVers; 82 | } 83 | 84 | // The last character must be ] or ) 85 | switch (value.Last()) 86 | { 87 | case ']': 88 | depVers.IsMaxInclusive = true; 89 | break; 90 | case ')': 91 | depVers.IsMaxInclusive = false; 92 | break; 93 | default: 94 | // If not, return without setting anything 95 | return depVers; 96 | } 97 | 98 | // Get rid of the two brackets 99 | value = value.Substring(1, value.Length - 2); 100 | 101 | // Split by comma, and make sure we don't get more than two pieces 102 | string[] parts = value.Split(','); 103 | 104 | // Wrong format if we have more than 2 parts or all the parts are empty 105 | if (parts.Length > 2 || parts.All(String.IsNullOrEmpty)) 106 | { 107 | return depVers; 108 | } 109 | 110 | // First part is min 111 | string minVersionString = parts[0]; 112 | 113 | // If there is only 1 part then first part will also be max 114 | string maxVersionString = (parts.Length == 2) ? parts[1] : parts[0]; 115 | 116 | // Get min version if we have it 117 | if (!String.IsNullOrWhiteSpace(minVersionString)) 118 | { 119 | depVers.MinVersion = new SemanticVersion(minVersionString); 120 | } 121 | 122 | // Get max version if we have it 123 | if (!String.IsNullOrWhiteSpace(maxVersionString)) 124 | { 125 | depVers.MaxVersion = new SemanticVersion(maxVersionString); 126 | } 127 | 128 | return depVers; 129 | } 130 | 131 | public override string ToString() 132 | { 133 | // Returns nothing if no min or max 134 | if (MinVersion == null && MaxVersion == null) 135 | { 136 | return null; 137 | } 138 | 139 | // If we have min and minInclusive but no max, then return min string 140 | if (MinVersion != null && IsMinInclusive && MaxVersion == null && !IsMaxInclusive) 141 | { 142 | return MinVersion.ToString(); 143 | } 144 | 145 | // MinVersion and MaxVersion is the same and both inclusives then return the value 146 | if (MinVersion == MaxVersion && IsMinInclusive && IsMaxInclusive) 147 | { 148 | return String.Format(CultureInfo.InvariantCulture, "[{0}]", MinVersion); 149 | } 150 | 151 | char lhs = IsMinInclusive ? '[' : '('; 152 | char rhs = IsMaxInclusive ? ']' : ')'; 153 | 154 | return String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}{3}", lhs, MinVersion, MaxVersion, rhs); 155 | } 156 | 157 | } 158 | 159 | public class DependencyVersionComparerBasedOnMinVersion : IComparer 160 | { 161 | /// 162 | /// Return 1 if dep1.minversion gt dep2.minversion 163 | /// Return 0 if dep1.minversion eq dep2.minversion 164 | /// Return -1 if dep.minversion lt dep2.minversion 165 | /// We consider null min version as the smallest possible value 166 | /// so if dep1.minversion = null and dep2.minversion = 0.1 then we return -1 since dep1.minversion lt dep2.minversion 167 | /// 168 | /// 169 | /// 170 | /// 171 | public int Compare(DependencyVersion dep1, DependencyVersion dep2) 172 | { 173 | if (dep1.MinVersion == null) 174 | { 175 | if (dep2.MinVersion == null) 176 | { 177 | return 0; 178 | } 179 | 180 | return -1; 181 | } 182 | 183 | // get here means dep1.minversion is not null 184 | if (dep2.MinVersion == null) 185 | { 186 | return 1; 187 | } 188 | 189 | // if they are the same, the one with min inclusive is smaller 190 | if (dep1.MinVersion.Equals(dep2.MinVersion)) 191 | { 192 | if (dep1.IsMinInclusive) 193 | { 194 | if (dep2.IsMinInclusive) 195 | { 196 | return 0; 197 | } 198 | 199 | return -1; 200 | } 201 | 202 | // reach here means dep1 is not min inclusive 203 | if (dep2.IsMinInclusive) 204 | { 205 | return 1; 206 | } 207 | 208 | // here means both are not mean inclusive 209 | return 0; 210 | } 211 | 212 | // reach here means both are not null 213 | return dep1.MinVersion.CompareTo(dep2.MinVersion); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Common/Version/SemanticVersionTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.PackageManagement.Provider.Utility; 2 | 3 | namespace MMicrosoft.PackageManagement.Provider.Utility 4 | { 5 | using System; 6 | using System.ComponentModel; 7 | using System.Globalization; 8 | 9 | /// 10 | /// Convert String to SemanticVersion type 11 | /// 12 | public class SemanticVersionTypeConverter : TypeConverter 13 | { 14 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 15 | { 16 | return sourceType == typeof(string); 17 | } 18 | 19 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 20 | { 21 | var stringValue = value as string; 22 | SemanticVersion semVer; 23 | if (stringValue != null && SemanticVersion.TryParse(stringValue, out semVer)) 24 | { 25 | return semVer; 26 | } 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Extensions/CollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | using System.Reflection; 20 | using System.Threading.Tasks; 21 | using Internal.Utility.Collections; 22 | using System.Diagnostics.CodeAnalysis; 23 | 24 | internal static class CollectionExtensions { 25 | private static readonly MethodInfo _castMethod = typeof (Enumerable).GetMethod("Cast"); 26 | private static readonly MethodInfo _toArrayMethod = typeof (Enumerable).GetMethod("ToArray"); 27 | private static readonly IDictionary _castMethods = new Dictionary(); 28 | private static readonly IDictionary _toArrayMethods = new Dictionary(); 29 | 30 | /// 31 | /// Determines whether the collection object is either null or an empty collection. 32 | /// 33 | /// 34 | /// The collection. 35 | /// 36 | /// true if [is null or empty] [the specified collection]; otherwise, false . 37 | /// 38 | /// 39 | /// 40 | 41 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 42 | public static bool IsNullOrEmpty(this IEnumerable collection) { 43 | return collection == null || !collection.Any(); 44 | } 45 | 46 | /// 47 | /// Concatenates a single item to an IEnumerable 48 | /// 49 | /// 50 | /// 51 | /// 52 | /// 53 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 54 | public static IEnumerable ConcatSingleItem(this IEnumerable enumerable, T item) { 55 | return enumerable.Concat(new[] { 56 | item 57 | }); 58 | } 59 | 60 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 61 | public static IEnumerable WhereNotNull(this IEnumerable enumerable) { 62 | return enumerable == null ? Enumerable.Empty() : enumerable.Where(each => (object)each != null); 63 | } 64 | 65 | public static IEnumerable ToIEnumerable(this IEnumerator enumerator) { 66 | if (enumerator != null) { 67 | while (enumerator.MoveNext()) { 68 | yield return enumerator.Current; 69 | } 70 | } 71 | } 72 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 73 | public static IEnumerable Concat(this IEnumerable set1, IEnumerator set2) { 74 | var s1 = set1 ?? Enumerable.Empty(); 75 | var s2 = set2 == null ? Enumerable.Empty() : set2.ToIEnumerable(); 76 | return s1.Concat(s2); 77 | } 78 | 79 | public static IEnumerable SingleItemAsEnumerable(this T item) { 80 | return new T[] { 81 | item 82 | }; 83 | } 84 | 85 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 86 | public static void AddRangeLocked(this List list, IEnumerable items) { 87 | if (list == null) { 88 | throw new ArgumentNullException("list"); 89 | } 90 | lock (list) { 91 | list.AddRange(items); 92 | } 93 | } 94 | 95 | //public static void ParallelForEach(this IEnumerable enumerable, Action action) { 96 | // var items = enumerable.ReEnumerable(); 97 | // var first = items.FirstOrDefault(); 98 | // if (first != null) { 99 | // object second = items.Skip(1).FirstOrDefault(); 100 | // if (second != null) { 101 | // Parallel.ForEach(items, new ParallelOptions { 102 | // MaxDegreeOfParallelism = -1, 103 | // TaskScheduler = new ThreadPerTaskScheduler() 104 | // }, action); 105 | // } else { 106 | // action(first); 107 | // } 108 | // } 109 | //} 110 | 111 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 112 | public static void SerialForEach(this IEnumerable enumerable, Action action) { 113 | var items = enumerable.ReEnumerable(); 114 | object first = items.FirstOrDefault(); 115 | if (first != null) { 116 | object second = items.Skip(1).FirstOrDefault(); 117 | if (second != null) { 118 | foreach (var item in items) { 119 | action(item); 120 | } 121 | } else { 122 | action(items.FirstOrDefault()); 123 | } 124 | } 125 | } 126 | 127 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 128 | public static IEnumerable AsyncForEach(this IEnumerable enumerable, Action action) { 129 | return enumerable.Select(i => Task.Factory.StartNew(() => action(i))); 130 | } 131 | 132 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 133 | public static void WaitAll(this IEnumerable tasks) { 134 | Task.WaitAll(tasks.ToArray()); 135 | } 136 | 137 | //public static object ToIEnumerableT(this IEnumerable enumerable, Type elementType) { 138 | // return _castMethods.GetOrAdd(elementType, () => _castMethod.MakeGenericMethod(elementType)).Invoke(null, new object[] {enumerable}); 139 | //} 140 | 141 | //public static object ToArrayT(this IEnumerable enumerable, Type elementType) { 142 | // return _toArrayMethods.GetOrAdd(elementType, () => _toArrayMethod.MakeGenericMethod(elementType)).Invoke(null, new[] {enumerable.ToIEnumerableT(elementType)}); 143 | //} 144 | 145 | /// 146 | /// returns the index position where the predicate matches the value 147 | /// 148 | /// 149 | /// 150 | /// 151 | /// 152 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 153 | public static int IndexWhere(this IEnumerable source, Func predicate) { 154 | if (source != null && predicate != null) { 155 | var index = -1; 156 | if (source.Any(element => predicate(element, ++index))) { 157 | return index; 158 | } 159 | } 160 | return -1; 161 | } 162 | 163 | /// 164 | /// returns the index position where the predicate matches the value 165 | /// 166 | /// 167 | /// 168 | /// 169 | /// 170 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 171 | public static int IndexWhere(this IEnumerable source, Func predicate) { 172 | if (source != null && predicate != null) { 173 | var index = -1; 174 | if (source.Any(element => { 175 | ++index; 176 | return predicate(element); 177 | })) { 178 | return index; 179 | } 180 | } 181 | return -1; 182 | } 183 | } 184 | 185 | // Provides a task scheduler that dedicates a thread per task. 186 | } -------------------------------------------------------------------------------- /Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Collections.Generic; 18 | using System.Linq; 19 | 20 | internal static class EnumerableExtensions { 21 | /// 22 | /// Returns a ReEnumerable wrapper around the collection which timidly (cautiously) pulls items 23 | /// but still allows you to to re-enumerate without re-running the query. 24 | /// 25 | /// 26 | /// 27 | /// 28 | public static MutableEnumerable ReEnumerable(this IEnumerable collection) { 29 | if (collection == null) { 30 | return new ReEnumerable(Enumerable.Empty()); 31 | } 32 | return collection as MutableEnumerable ?? new ReEnumerable(collection); 33 | } 34 | 35 | public static IEnumerable FilterWithFinalizer(this IEnumerable source, Func predicate, Action onFilterAction) { 36 | foreach (var i in source) { 37 | if (predicate(i)) { 38 | onFilterAction(i); 39 | } else { 40 | yield return i; 41 | } 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Extensions/EqualityComparer.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Collections.Generic; 18 | 19 | internal class EqualityComparer : IEqualityComparer { 20 | private readonly Func _compareFn; 21 | private readonly Func _hashFn; 22 | 23 | public EqualityComparer(Func compareFn, Func hashFn) { 24 | _compareFn = compareFn; 25 | _hashFn = hashFn; 26 | } 27 | 28 | public EqualityComparer(Func compareFn) { 29 | _compareFn = compareFn; 30 | _hashFn = (obj) =>obj.GetHashCode(); 31 | } 32 | 33 | public bool Equals(T x, T y) { 34 | return _compareFn(x, y); 35 | } 36 | 37 | public int GetHashCode(T obj) { 38 | return _hashFn(obj); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Extensions/Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.PackageManagement.Provider.Utility 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics.CodeAnalysis; 6 | using System.Globalization; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net.Http; 10 | using System.Text; 11 | using System.Text.RegularExpressions; 12 | using System.Threading.Tasks; 13 | using System.Xml.Linq; 14 | 15 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 16 | internal static class HttpContentExtensions { 17 | 18 | internal static async Task ReadAsFileAsync(this HttpContent content, string fileName) { 19 | if (string.IsNullOrWhiteSpace(fileName)) 20 | { 21 | throw new ArgumentException(fileName); 22 | } 23 | 24 | //Get the absolute path 25 | var pathName = Path.GetFullPath(fileName); 26 | 27 | using (var fileStream = new FileStream(pathName, FileMode.Create, FileAccess.Write, FileShare.None)) { 28 | await content.CopyToAsync(fileStream); 29 | return fileStream.Length; 30 | } 31 | } 32 | } 33 | 34 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 35 | internal static class XElementExtensions 36 | { 37 | internal static IEnumerable ElementsNoNamespace(this XContainer container, string localName) 38 | { 39 | return container.Elements().Where(e => e.Name.LocalName == localName); 40 | } 41 | 42 | /// 43 | /// Get the optional attribute value depends on the localName and the nameSpace. Returns null if we can't find it 44 | /// 45 | /// 46 | /// 47 | /// 48 | /// 49 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 50 | internal static string GetOptionalAttributeValue(this XElement element, string localName, string namespaceName = null) 51 | { 52 | XAttribute attr; 53 | // Name space is null so don't use it 54 | if (String.IsNullOrWhiteSpace(namespaceName)) 55 | { 56 | attr = element.Attribute(localName); 57 | } 58 | else 59 | { 60 | attr = element.Attribute(XName.Get(localName, namespaceName)); 61 | } 62 | 63 | return attr != null ? attr.Value : null; 64 | } 65 | } 66 | 67 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 68 | internal static class VersionExtensions 69 | { 70 | internal static IEnumerable GetComparableVersionStrings(this SemanticVersion version) 71 | { 72 | Version coreVersion = version.Version; 73 | string specialVersion = String.IsNullOrWhiteSpace(version.SpecialVersion) ? String.Empty : "-" + version.SpecialVersion; 74 | string[] originalVersionComponents = version.GetOriginalVersionComponents(); 75 | 76 | 77 | if (coreVersion.Revision == 0) 78 | { 79 | if (coreVersion.Build == 0) 80 | { 81 | yield return String.Format( 82 | CultureInfo.InvariantCulture, 83 | "{0}.{1}{2}", 84 | originalVersionComponents[0], 85 | originalVersionComponents[1], 86 | specialVersion); 87 | } 88 | 89 | yield return String.Format( 90 | CultureInfo.InvariantCulture, 91 | "{0}.{1}.{2}{3}", 92 | originalVersionComponents[0], 93 | originalVersionComponents[1], 94 | originalVersionComponents[2], 95 | specialVersion); 96 | } 97 | 98 | yield return String.Format( 99 | CultureInfo.InvariantCulture, 100 | "{0}.{1}.{2}.{3}{4}", 101 | originalVersionComponents[0], 102 | originalVersionComponents[1], 103 | originalVersionComponents[2], 104 | originalVersionComponents[3], 105 | specialVersion); 106 | 107 | } 108 | } 109 | 110 | internal static class ObjectExtensions 111 | { 112 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 113 | internal static string ToStringSafe(this object obj) 114 | { 115 | return obj == null ? null : obj.ToString(); 116 | } 117 | } 118 | 119 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 120 | internal static class StringExtensions 121 | { 122 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 123 | internal static string SafeTrim(this string value) 124 | { 125 | return value == null ? null : value.Trim(); 126 | } 127 | 128 | internal static string ToBase64(this string text) 129 | { 130 | if (text == null) 131 | { 132 | return null; 133 | } 134 | return Convert.ToBase64String(text.ToByteArray()); 135 | } 136 | 137 | internal static string FromBase64(this string text) 138 | { 139 | if (text == null) 140 | { 141 | return null; 142 | } 143 | return Convert.FromBase64String(text).ToUtf8String(); 144 | } 145 | 146 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 147 | internal static string FixVersion(this string versionString) 148 | { 149 | if (!String.IsNullOrWhiteSpace(versionString)) 150 | { 151 | if (versionString[0] == '.') 152 | { 153 | // make sure we have a leading zero when someone says .5 154 | versionString = "0" + versionString; 155 | } 156 | 157 | if (versionString.IndexOf('.') == -1) 158 | { 159 | // make sure we make a 1 work like 1.0 160 | versionString = versionString + ".0"; 161 | } 162 | } 163 | 164 | return versionString; 165 | } 166 | 167 | // Encodes the string as an array of UTF8 bytes. 168 | private static byte[] ToByteArray(this string text) 169 | { 170 | return Encoding.UTF8.GetBytes(text); 171 | } 172 | 173 | // Creates a string from a collection of UTF8 bytes 174 | private static string ToUtf8String(this IEnumerable bytes) 175 | { 176 | var data = bytes.ToArray(); 177 | try 178 | { 179 | return Encoding.UTF8.GetString(data); 180 | } 181 | finally 182 | { 183 | Array.Clear(data, 0, data.Length); 184 | } 185 | } 186 | 187 | public static bool EqualsIgnoreCase(this string str, string str2) 188 | { 189 | if (str == null && str2 == null) 190 | { 191 | return true; 192 | } 193 | 194 | if (str == null || str2 == null) 195 | { 196 | return false; 197 | } 198 | 199 | return str.Equals(str2, StringComparison.OrdinalIgnoreCase); 200 | } 201 | 202 | public static bool IsTrue(this string text) 203 | { 204 | return !string.IsNullOrWhiteSpace(text) && text.Equals("true", StringComparison.CurrentCultureIgnoreCase); 205 | } 206 | 207 | public static bool CompareVersion(this string version1, string version2) 208 | { 209 | if(string.IsNullOrWhiteSpace(version1) && string.IsNullOrWhiteSpace(version2)) 210 | { 211 | return true; 212 | } 213 | if (string.IsNullOrWhiteSpace(version1) || string.IsNullOrWhiteSpace(version2)) 214 | { 215 | return false; 216 | } 217 | 218 | var semver1 = new SemanticVersion(version1); 219 | var semver2 = new SemanticVersion(version2); 220 | return (semver1 == semver2); 221 | } 222 | } 223 | 224 | internal static class LinqExtensions 225 | { 226 | internal static Dictionary ToDictionaryNicely(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) 227 | { 228 | if (source == null) 229 | { 230 | throw new ArgumentNullException("source"); 231 | } 232 | if (keySelector == null) 233 | { 234 | throw new ArgumentNullException("keySelector"); 235 | } 236 | if (elementSelector == null) 237 | { 238 | throw new ArgumentNullException("elementSelector"); 239 | } 240 | 241 | var d = new Dictionary(comparer); 242 | foreach (var element in source) 243 | { 244 | d.AddOrSet(keySelector(element), elementSelector(element)); 245 | } 246 | return d; 247 | } 248 | 249 | internal static TValue AddOrSet(this IDictionary dictionary, TKey key, TValue value) 250 | { 251 | lock (dictionary) 252 | { 253 | if (dictionary.ContainsKey(key)) 254 | { 255 | dictionary[key] = value; 256 | } 257 | else 258 | { 259 | dictionary.Add(key, value); 260 | } 261 | } 262 | return value; 263 | } 264 | 265 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code for extensions.")] 266 | internal static TSource SafeAggregate(this IEnumerable source, Func func) 267 | { 268 | var src = source.ToArray(); 269 | if (source != null && src.Any()) 270 | { 271 | return src.Aggregate(func); 272 | } 273 | return default(TSource); 274 | } 275 | } 276 | } -------------------------------------------------------------------------------- /Extensions/FilesystemExtensions.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System; 17 | using System.Diagnostics; 18 | using System.Diagnostics.CodeAnalysis; 19 | using System.Globalization; 20 | using System.IO; 21 | using System.Text; 22 | using System.Text.RegularExpressions; 23 | 24 | internal static class FilesystemExtensions { 25 | private static readonly char[] _pathCharacters = "/\\".ToCharArray(); 26 | private static int _counter = Process.GetCurrentProcess().Id << 16; 27 | public static string OriginalTempFolder; 28 | 29 | static FilesystemExtensions() { 30 | OriginalTempFolder = OriginalTempFolder ?? Path.GetTempPath(); 31 | // ResetTempFolder(); 32 | } 33 | 34 | public static string TempPath {get; private set;} 35 | 36 | public static int Counter { 37 | get { 38 | return ++_counter; 39 | } 40 | } 41 | 42 | public static string CounterHex { 43 | get { 44 | return Counter.ToString("x8", CultureInfo.CurrentCulture); 45 | } 46 | } 47 | 48 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 49 | public static bool LooksLikeAFilename(this string text) { 50 | return text.IndexOfAny(_pathCharacters) > -1; 51 | } 52 | 53 | public static void TryHardToDelete(this string location) 54 | { 55 | if (Directory.Exists(location)) 56 | { 57 | try 58 | { 59 | Directory.Delete(location, true); 60 | } 61 | catch 62 | { 63 | // didn't take, eh? 64 | } 65 | } 66 | 67 | if (File.Exists(location)) 68 | { 69 | try 70 | { 71 | File.Delete(location); 72 | } 73 | catch 74 | { 75 | // didn't take, eh? 76 | } 77 | } 78 | 79 | // if it is still there, move and mark it. 80 | if (File.Exists(location) || Directory.Exists(location)) 81 | { 82 | try 83 | { 84 | // move the file to the tmp file 85 | // and tell the OS to remove it next reboot. 86 | var tmpFilename = location.GenerateTemporaryFilename() + ".delete_me"; // generates a unique filename but not a file! 87 | //MoveFileOverwrite(location, tmpFilename); 88 | 89 | if (File.Exists(location) || Directory.Exists(location)) 90 | { 91 | // of course, if the tmpFile isn't on the same volume as the location, this doesn't work. 92 | // then, last ditch effort, let's rename it in the current directory 93 | // and then we can hide it and mark it for cleanup . 94 | tmpFilename = Path.Combine(Path.GetDirectoryName(location), "tmp." + CounterHex + "." + Path.GetFileName(location) + ".delete_me"); 95 | //MoveFileOverwrite(location, tmpFilename); 96 | if (File.Exists(tmpFilename) || Directory.Exists(location)) 97 | { 98 | // hide the file for convenience. 99 | File.SetAttributes(tmpFilename, File.GetAttributes(tmpFilename) | FileAttributes.Hidden); 100 | } 101 | } 102 | 103 | // Now we mark the locked file to be deleted upon next reboot (or until another coapp app gets there) 104 | // MoveFileOverwrite(File.Exists(tmpFilename) ? tmpFilename : location, null); 105 | } 106 | catch 107 | { 108 | // really. Hmmm. 109 | } 110 | 111 | if (File.Exists(location)) 112 | { 113 | // err("Unable to forcably remove file '{0}'. This can't be good.", location); 114 | } 115 | } 116 | return; 117 | } 118 | 119 | ///// 120 | ///// File move abstraction that can be implemented to handle non-windows platforms 121 | ///// 122 | ///// 123 | ///// 124 | //public static void MoveFileOverwrite(string sourceFile, string destinationFile) { 125 | // NativeMethods.MoveFileEx(sourceFile, destinationFile, MoveFileFlags.ReplaceExisting); 126 | //} 127 | 128 | //public static void MoveFileAtNextBoot(string sourceFile, string destinationFile) { 129 | // NativeMethods.MoveFileEx(sourceFile, destinationFile, MoveFileFlags.DelayUntilReboot); 130 | //} 131 | 132 | public static string GenerateTemporaryFilename(this string filename) { 133 | string ext = null; 134 | string name = null; 135 | string folder = null; 136 | 137 | if (!string.IsNullOrWhiteSpace(filename)) { 138 | ext = Path.GetExtension(filename); 139 | name = Path.GetFileNameWithoutExtension(filename); 140 | folder = Path.GetDirectoryName(filename); 141 | } 142 | 143 | if (string.IsNullOrWhiteSpace(ext)) { 144 | ext = ".tmp"; 145 | } 146 | if (string.IsNullOrWhiteSpace(folder)) { 147 | folder = TempPath; 148 | } 149 | 150 | do 151 | { 152 | name = Path.Combine(folder, "tmpFile." + Path.GetRandomFileName() + (string.IsNullOrWhiteSpace(name) ? ext : "." + name + ext)); 153 | } 154 | while (File.Exists(name)); 155 | 156 | // return MarkFileTemporary(name); 157 | return name; 158 | } 159 | 160 | //public static void ResetTempFolder() { 161 | // // set the temporary folder to be a child of the User temporary folder 162 | // // based on the application name 163 | // var appName = typeof(FilesystemExtensions).GetTypeInfo().Assembly.GetName().Name; 164 | // if (OriginalTempFolder.IndexOf(appName, StringComparison.CurrentCultureIgnoreCase) == -1) { 165 | // var appTempPath = Path.Combine(OriginalTempFolder, appName); 166 | // if (!Directory.Exists(appTempPath)) { 167 | // Directory.CreateDirectory(appTempPath); 168 | // } 169 | 170 | // TempPath = Path.Combine(appTempPath, (Directory.EnumerateDirectories(appTempPath).Count() + 1).ToString(CultureInfo.CurrentCulture)); 171 | // if (!Directory.Exists(TempPath)) { 172 | // Directory.CreateDirectory(TempPath); 173 | // } 174 | 175 | // // make sure this temp directory gets marked for eventual cleanup. 176 | // MoveFileOverwrite(appTempPath, null); 177 | // MoveFileAtNextBoot(TempPath, null); 178 | // } 179 | 180 | // TempPath = TempPath ?? OriginalTempFolder; 181 | //} 182 | 183 | /// 184 | /// This takes a string that is representative of a filename and tries to create a path that can be considered the 185 | /// 'canonical' path. path on drives that are mapped as remote shares are rewritten as their \\server\share\path 186 | /// 187 | /// 188 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 189 | public static string CanonicalizePath(this string path, bool isPotentiallyRelativePath) { 190 | Uri pathUri = null; 191 | try { 192 | pathUri = new Uri(path); 193 | if (!pathUri.IsFile) { 194 | // perhaps try getting the fullpath 195 | try { 196 | pathUri = new Uri(Path.GetFullPath(path)); 197 | } catch { 198 | return null; 199 | } 200 | } 201 | 202 | // is this a unc path? 203 | if (string.IsNullOrWhiteSpace(pathUri.Host)) { 204 | // no, this is a drive:\path path 205 | // use API to resolve out the drive letter to see if it is a remote 206 | var drive = pathUri.Segments[1].Replace('/', '\\'); // the zero segment is always just '/' 207 | 208 | var sb = new StringBuilder(512); 209 | var size = sb.Capacity; 210 | 211 | //TODO 212 | // var error = NativeMethods.WNetGetConnection(drive, sb, ref size); 213 | //if (error == 0) { 214 | // if (pathUri.Segments.Length > 2) { 215 | // return pathUri.Segments.Skip(2).Aggregate(sb.ToString().Trim(), (current, item) => current + item); 216 | // } 217 | // } 218 | } 219 | // not a remote (or resovably-remote) path or 220 | // it is already a path that is in it's correct form (via localpath) 221 | return pathUri.LocalPath; 222 | } catch (UriFormatException) { 223 | // we could try to see if it is a relative path... 224 | if (isPotentiallyRelativePath) { 225 | return CanonicalizePath(Path.GetFullPath(path), false); 226 | } 227 | return null; 228 | } 229 | } 230 | 231 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 232 | public static byte[] ReadBytes(this string path, int maxLength) { 233 | if (path.FileExists()) { 234 | try { 235 | var buffer = new byte[Math.Min(new FileInfo(path).Length, maxLength)]; 236 | using (var file = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { 237 | file.Read(buffer, 0, buffer.Length); 238 | } 239 | 240 | return buffer; 241 | } catch { 242 | // not openable. whatever. 243 | } 244 | } 245 | return new byte[0]; 246 | } 247 | 248 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 249 | public static bool FileExists(this string path) { 250 | if (!string.IsNullOrWhiteSpace(path)) { 251 | try { 252 | return File.Exists(CanonicalizePath(path, true)); 253 | } catch { 254 | } 255 | } 256 | return false; 257 | } 258 | 259 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 260 | public static bool DirectoryExists(this string path) { 261 | if (!string.IsNullOrWhiteSpace(path)) { 262 | try { 263 | return Directory.Exists(CanonicalizePath(path, true)); 264 | } catch { 265 | } 266 | } 267 | return false; 268 | } 269 | 270 | public static string MakeSafeFileName(this string input) { 271 | return new Regex(@"-+").Replace(new Regex(@"[^\d\w\[\]_\-\.\ ]").Replace(input, "-"), "-").Replace(" ", ""); 272 | } 273 | } 274 | } -------------------------------------------------------------------------------- /Extensions/MutableEnumerable.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System.Collections; 17 | using System.Collections.Generic; 18 | 19 | internal abstract class MutableEnumerable : IEnumerable { 20 | private List _list; 21 | 22 | protected List List { 23 | get { 24 | if (_list == null) { 25 | lock (this) { 26 | if (_list == null) { 27 | _list = new List(); 28 | } 29 | } 30 | } 31 | return _list; 32 | } 33 | } 34 | 35 | public IEnumerable> GetEnumerators(int copies) { 36 | for (var i = 0; i < copies; i++) { 37 | yield return GetEnumerator(); 38 | } 39 | } 40 | 41 | protected abstract bool ItemExists(int index); 42 | 43 | internal class Enumerator : IEnumerator { 44 | private MutableEnumerable _collection; 45 | private int _index = -1; 46 | 47 | internal Enumerator(MutableEnumerable collection) { 48 | _collection = collection; 49 | } 50 | 51 | #region IEnumerator Members 52 | 53 | public TT Current { 54 | get { 55 | return _collection.List[_index]; 56 | } 57 | } 58 | 59 | public void Dispose() { 60 | _collection = null; 61 | } 62 | 63 | object IEnumerator.Current { 64 | get { 65 | return Current; 66 | } 67 | } 68 | 69 | public bool MoveNext() { 70 | _index++; 71 | return _collection.ItemExists(_index); 72 | } 73 | 74 | public void Reset() { 75 | _index = -1; 76 | } 77 | 78 | public IEnumerator Clone() { 79 | return new Enumerator(_collection) { 80 | _index = _index 81 | }; 82 | } 83 | 84 | #endregion 85 | } 86 | 87 | #region IEnumerable Members 88 | 89 | public IEnumerator GetEnumerator() { 90 | return new Enumerator(this); 91 | } 92 | 93 | IEnumerator IEnumerable.GetEnumerator() { 94 | return GetEnumerator(); 95 | } 96 | 97 | #endregion 98 | } 99 | } -------------------------------------------------------------------------------- /Extensions/PackageListExtensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) .NET Foundation. All rights reserved. 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. 3 | 4 | using System; 5 | using System.Reflection; 6 | using System.Collections.Generic; 7 | using System.Globalization; 8 | using System.Linq; 9 | using System.Text.RegularExpressions; 10 | using Microsoft.PackageManagement.Internal.Implementation; 11 | 12 | namespace Microsoft.PackageManagement.PackageSourceListProvider 13 | { 14 | 15 | internal static class ExceptionExtensions 16 | { 17 | internal static void Dump(this Exception ex, Request request) 18 | { 19 | request.Debug(ex.ToString()); 20 | } 21 | } 22 | 23 | internal static class StringExtensions 24 | { 25 | // ReSharper disable InconsistentNaming 26 | /// 27 | /// Formats the specified format string. 28 | /// 29 | /// The format string. 30 | /// The args. 31 | /// 32 | /// 33 | /// 34 | public static string format(this string formatString, params object[] args) 35 | { 36 | if (args == null || args.Length == 0) 37 | { 38 | return formatString; 39 | } 40 | 41 | try 42 | { 43 | var replacedByName = false; 44 | // first, try to replace 45 | formatString = new Regex(@"\$\{(?\w*?)\}").Replace(formatString, new MatchEvaluator((m) => { 46 | var key = m.Groups["macro"].Value; 47 | 48 | var p = args[0].GetType().GetProperty(key); 49 | if (p != null) 50 | { 51 | replacedByName = true; 52 | return p.GetValue(args[0], null).ToString(); 53 | } 54 | return "${{" + m.Groups["macro"].Value + "}}"; 55 | })); 56 | 57 | // if it looks like it doesn't take parameters, (and yet we have args!) 58 | // let's return a fix-me-format string. 59 | if (!replacedByName && formatString.IndexOf('{') < 0) 60 | { 61 | return FixMeFormat(formatString, args); 62 | } 63 | 64 | return String.Format(CultureInfo.CurrentCulture, formatString, args); 65 | } 66 | catch (Exception) 67 | { 68 | // if we got an exception, let's at least return a string that we can use to figure out what parameters should have been matched vs what was passed. 69 | return FixMeFormat(formatString, args); 70 | } 71 | } 72 | 73 | private static string FixMeFormat(string formatString, object[] args) 74 | { 75 | if (args == null || args.Length == 0) 76 | { 77 | // not really any args, and not really expectng any 78 | return formatString.Replace('{', '\u00ab').Replace('}', '\u00bb'); 79 | } 80 | return args.Aggregate(formatString.Replace('{', '\u00ab').Replace('}', '\u00bb'), (current, arg) => current + string.Format(CultureInfo.CurrentCulture, " \u00ab{0}\u00bb", arg)); 81 | } 82 | 83 | } 84 | 85 | internal static class DictionaryExtensions 86 | { 87 | internal static TValue Get(this IDictionary dictionary, TKey key) 88 | { 89 | return dictionary.ContainsKey(key) ? dictionary[key] : default(TValue); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Extensions/ReEnumerable.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.Provider.Utility { 16 | using System.Collections.Generic; 17 | using System.Linq; 18 | 19 | internal class ReEnumerable : MutableEnumerable { 20 | private IEnumerator _sourceIterator; 21 | private readonly IEnumerable _source; 22 | 23 | public ReEnumerable(IEnumerable source) { 24 | _source = source ?? new T[0]; 25 | } 26 | 27 | public ReEnumerable(IEnumerator sourceIterator) { 28 | _source = null; 29 | _sourceIterator = sourceIterator; 30 | } 31 | 32 | public T this[int index] { 33 | get { 34 | if (ItemExists(index)) { 35 | return List[index]; 36 | } 37 | return default(T); 38 | } 39 | } 40 | 41 | public int Count { 42 | get { 43 | return this.Count(); 44 | } 45 | } 46 | 47 | protected override bool ItemExists(int index) { 48 | if (index < List.Count) { 49 | return true; 50 | } 51 | 52 | lock (this) { 53 | if (_sourceIterator == null) { 54 | _sourceIterator = _source.GetEnumerator(); 55 | } 56 | 57 | try { 58 | while (_sourceIterator.MoveNext()) { 59 | List.Add(_sourceIterator.Current); 60 | if (index < List.Count) { 61 | return true; 62 | } 63 | } 64 | } catch { 65 | // if the _sourceIterator is cancelled 66 | // then MoveNext() will throw; that's ok 67 | // that just means we're done 68 | } 69 | } 70 | return false; 71 | } 72 | 73 | public MutableEnumerable Concat(IEnumerable additionalItems) { 74 | return Enumerable.Concat(this, additionalItems).ReEnumerable(); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /PackageList/PackageQuery.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | 16 | namespace Microsoft.PackageManagement.PackageSourceListProvider 17 | { 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using Microsoft.PackageManagement.Internal.Packaging; 22 | using Microsoft.PackageManagement.Provider.Utility; 23 | using System.Globalization; 24 | using System.Diagnostics.CodeAnalysis; 25 | 26 | internal class PackageQuery 27 | { 28 | private Dictionary> _PackageSourceList; 29 | 30 | private PackageQuery() { } 31 | 32 | internal virtual bool IsValid 33 | { 34 | get 35 | { 36 | return _PackageSourceList != null; 37 | } 38 | } 39 | internal PackageQuery(PackageSource packageSource, PackageSourceListRequest request) 40 | { 41 | if (packageSource == null) 42 | { 43 | throw new ArgumentNullException("packageSource"); 44 | } 45 | 46 | if(string.IsNullOrWhiteSpace(packageSource.Location) || !System.IO.File.Exists(packageSource.Location)) 47 | { 48 | request.Warning(Resources.Messages.PackageSourceManifestNotFound, packageSource.Location, Constants.ProviderName); 49 | return; 50 | } 51 | 52 | Uri uri; 53 | 54 | if (Uri.TryCreate(packageSource.Location, UriKind.Absolute, out uri)) 55 | { 56 | if (uri.IsFile) 57 | { 58 | try 59 | { 60 | //process the file 61 | _PackageSourceList = JsonParser.ReadPackageSpec(packageSource, null); 62 | } 63 | catch (Exception ex) 64 | { 65 | request.Warning(ex.Message); 66 | while (ex.InnerException != null) 67 | { 68 | ex = ex.InnerException; 69 | request.Warning(ex.Message); 70 | } 71 | request.Warning(string.Format(CultureInfo.CurrentCulture, Resources.Messages.InvalidPackageListFormat, uri.AbsolutePath)); 72 | ex.Dump(request); 73 | } 74 | } 75 | 76 | } 77 | else 78 | { 79 | //TODO: Check with GP, DSC Settings, 80 | 81 | request.Verbose(Resources.Messages.UnsupportedPackageSource, Constants.ProviderName, packageSource.Location); 82 | return; 83 | } 84 | } 85 | 86 | internal IEnumerable Query() 87 | { 88 | return _PackageSourceList.SelectMany(each => each.Value).Where(item => !item.IsCommonDefinition); ; 89 | 90 | } 91 | 92 | internal IEnumerable Query(string name) 93 | { 94 | return 95 | _PackageSourceList.SelectMany(each => each.Value) 96 | .Where(item => (item.Name.EqualsIgnoreCase(name) || (!string.IsNullOrWhiteSpace(item.DisplayName) && item.DisplayName.EqualsIgnoreCase(name))) && !item.IsCommonDefinition); 97 | 98 | } 99 | 100 | internal IEnumerable Query(string name, string version) 101 | { 102 | return string.IsNullOrWhiteSpace(version) 103 | ? _PackageSourceList.SelectMany(each => each.Value).Where(item => (item.Name.EqualsIgnoreCase(name) || 104 | (!string.IsNullOrWhiteSpace(item.DisplayName) && item.DisplayName.EqualsIgnoreCase(name))) && !item.IsCommonDefinition) 105 | : _PackageSourceList.SelectMany(each => each.Value) 106 | .Where(item => 107 | (item.Name.EqualsIgnoreCase(name) || (!string.IsNullOrWhiteSpace(item.DisplayName) && item.DisplayName.EqualsIgnoreCase(name))) 108 | && !item.IsCommonDefinition 109 | && (string.IsNullOrWhiteSpace(item.Version) || new SemanticVersion(item.Version) == new SemanticVersion(version))); 110 | } 111 | 112 | internal IEnumerable Query(string name, string minimumVersion, string maximumVersion) 113 | { 114 | if (string.IsNullOrEmpty(minimumVersion) && string.IsNullOrEmpty(maximumVersion)) 115 | { 116 | return Query(name); 117 | } 118 | 119 | if (!IsValid || string.IsNullOrEmpty(name)) 120 | { 121 | return Enumerable.Empty(); 122 | } 123 | 124 | var packages = _PackageSourceList.SelectMany(each => each.Value).Where(item => (item.Name.EqualsIgnoreCase(name) || 125 | (!string.IsNullOrWhiteSpace(item.DisplayName) && item.DisplayName.EqualsIgnoreCase(name))) && !item.IsCommonDefinition); 126 | 127 | return packages.Where(pkg => { 128 | 129 | if (!string.IsNullOrWhiteSpace(minimumVersion)) 130 | { 131 | if (SoftwareIdentityVersionComparer.CompareVersions(pkg.VersionScheme, pkg.Version, minimumVersion) < 0) 132 | { 133 | // a minimum version was specified, but the package version is less than the specified minimumversion. 134 | return false; 135 | } 136 | } 137 | 138 | if (!string.IsNullOrWhiteSpace(maximumVersion)) 139 | { 140 | if (SoftwareIdentityVersionComparer.CompareVersions(pkg.VersionScheme, pkg.Version, maximumVersion) > 0) 141 | { 142 | // a maximum version was specified, but the package version is more than the specified maximumversion. 143 | return false; 144 | } 145 | } 146 | 147 | // the version is in the range asked for. 148 | return true; 149 | }); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /PackageList/PackageSource.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | 16 | namespace Microsoft.PackageManagement.PackageSourceListProvider 17 | { 18 | using Microsoft.PackageManagement.Internal.Implementation; 19 | using Microsoft.PackageManagement.Provider.Utility; 20 | internal class PackageSource 21 | { 22 | //Parameters will be filled during the instantiation. 23 | internal string Name { get; set; } 24 | 25 | internal string Location { get; set; } 26 | 27 | internal bool Trusted { get; set; } 28 | 29 | internal bool IsRegistered { get; set; } 30 | 31 | internal bool IsValidated { get; set; } 32 | 33 | internal Request Request { get; set; } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /PackageList/PowerShellArtifactInstaller.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | 16 | namespace Microsoft.PackageManagement.PackageSourceListProvider 17 | { 18 | using System; 19 | using System.Collections.Generic; 20 | using System.Linq; 21 | using System.Globalization; 22 | using Microsoft.PackageManagement.Internal.Api; 23 | using Microsoft.PackageManagement.Packaging; 24 | using Microsoft.PackageManagement.Provider.Utility; 25 | using Microsoft.PackageManagement.Internal.Utility.Plugin; 26 | internal static class PowerShellArtifactInstaller 27 | { 28 | internal static void InstallPowershellArtifacts(PackageJson package, string fastPath, PackageSourceListRequest request) 29 | { 30 | var provider1 = PackageSourceListRequest.FindProvider(request, package.Type, request, true); 31 | if (provider1 == null) return; 32 | 33 | // As the PowerShellGet may access the -source via $request.Options or PackageSources, 34 | // so we need to fill in both options and sources parameters 35 | var installRequest = PackageSourceListRequest.ExtendRequest(new Dictionary 36 | { 37 | {"Source", new[] {package.Source ?? ""}} 38 | }, new[] { package.Source ?? "" }, request); 39 | 40 | var psid = PackageSourceListRequest.CreateCanonicalId(package, Constants.ProviderNames.PowerShellGet); 41 | var pkgs = request.PackageManagementService.FindPackageByCanonicalId(psid, installRequest) 42 | .Where(each => new SemanticVersion(each.Version) == new SemanticVersion(package.Version)).ToArray(); 43 | 44 | switch (pkgs.Length) 45 | { 46 | case 0: 47 | request.Warning(Resources.Messages.CannotFindPackage, Constants.ProviderName, psid, package.Source, Constants.ProviderNames.PowerShellGet); 48 | break; 49 | case 1: 50 | InstallPackageViaPowerShellGet(package, request, pkgs); 51 | break; 52 | 53 | default: 54 | request.Warning(Resources.Messages.FoundMorePackages, Constants.ProviderName, pkgs.Length, psid); 55 | break; 56 | } 57 | return; 58 | } 59 | 60 | private static void InstallPackageViaPowerShellGet(PackageJson packageJson, PackageSourceListRequest request, SoftwareIdentity[] packages) 61 | { 62 | 63 | var provider = PackageSourceListRequest.FindProvider(request, packageJson.Type, request, true); 64 | if (provider == null) return; 65 | 66 | if (!string.IsNullOrWhiteSpace(packageJson.Destination)) 67 | { 68 | request.Verbose(Resources.Messages.DestinationNotSupported, packageJson.Type); 69 | } 70 | 71 | IHostApi installRequest = request; 72 | 73 | if (provider.Name.EqualsIgnoreCase(Constants.ProviderNames.PowerShellGet) && !request.ProviderServices.IsElevated) { 74 | // if we're not elevated, we want powershellget to install to the user scope 75 | installRequest = PackageSourceListRequest.ExtendRequest( 76 | new Dictionary { 77 | {"Scope", new[] {"CurrentUser"}} 78 | }, null, request); 79 | 80 | } else { 81 | installRequest = PackageSourceListRequest.ExtendRequest( 82 | new Dictionary { 83 | {"Destination", new[] {packageJson.Destination ?? ""}} 84 | }, null, request); 85 | } 86 | 87 | request.Debug("Calling '{0}' provider to install the package '{1}.{2}'", provider.Name, packageJson.Name, packageJson.Version); 88 | 89 | var installing = provider.InstallPackage(packages[0], installRequest); 90 | 91 | if (installing == null || !installing.Any()) 92 | { 93 | request.Verbose(Resources.Messages.NumberOfPackagesRecevied, 0, provider.Name, "InstallPackage"); 94 | request.Warning(Resources.Messages.FailToInstallPackage, Constants.ProviderName, packages[0].Name); 95 | return; 96 | } 97 | 98 | int packagesRecevied = 0; 99 | foreach (var i in installing) 100 | { 101 | request.YieldSoftwareIdentity(i.FastPackageReference, i.Name, i.Version, i.VersionScheme, i.Summary, i.Source, i.SearchKey, i.FullPath, i.PackageFilename); 102 | 103 | if (request.IsCanceled) 104 | { 105 | installing.Cancel(); 106 | } 107 | else 108 | { 109 | request.Verbose(Resources.Messages.SuccessfullyInstalled, "{0}.{1}".format(packageJson.Name, packageJson.Version)); 110 | //load provider 111 | if (packageJson.IsPackageProvider) 112 | { 113 | //Per provider development guidance: provider name and module name should be the same otherwise we can not import it. 114 | request.PackageManagementService.ImportPackageProvider(request, packageJson.Name, null, null, null, isRooted: false, force: false); 115 | } 116 | } 117 | packagesRecevied++; 118 | } 119 | 120 | request.Verbose(Resources.Messages.NumberOfPackagesRecevied, packagesRecevied, provider.Name, "install-package"); 121 | } 122 | 123 | internal static void GetInstalledPowershellArtifacts(PackageJson package, string requiredVersion, string minimumVersion, string maximumVersion, Dictionary fastPackReftable, PackageSourceListRequest request) 124 | { 125 | if (request == null) 126 | { 127 | throw new ArgumentNullException("request"); 128 | } 129 | 130 | request.Debug(Resources.Messages.DebugInfoCallMethod, Constants.ProviderName, string.Format(CultureInfo.InvariantCulture, "GetInstalledPowershellArtifacts' - name='{0}', requiredVersion='{1}',minimumVersion='{2}', maximumVersion='{3}'", package.Name, requiredVersion, minimumVersion, maximumVersion)); 131 | 132 | var provider = PackageSourceListRequest.FindProvider(request, package.Type, request); 133 | if (provider == null) return; 134 | 135 | //calling the PowerShellGet provider 136 | request.Debug("Calling '{0}' provider to get installed packages '{1}.{2}'", provider.Name, package.Name, package.Version); 137 | 138 | var packagesInstalled = provider.GetInstalledPackages(package.Name, requiredVersion, minimumVersion, maximumVersion, request).ToArray(); 139 | 140 | if (packagesInstalled == null || !packagesInstalled.Any()) 141 | { 142 | request.Verbose(Resources.Messages.NumberOfPackagesRecevied, 0, provider.Name, "GetInstalledPackages"); 143 | return; 144 | } 145 | 146 | //Make sure the packages found are defined in the psl.json 147 | foreach (var i in packagesInstalled.Where(each => new SemanticVersion(each.Version) == new SemanticVersion(package.Version))) 148 | { 149 | request.Debug("Found an installed package '{0}.{1} from {2}' ", i.Name, i.Version, i.Source); 150 | string userSpecifiedProvider = request.GetOptionValue("ProviderName") ?? request.GetOptionValue("Provider"); 151 | var info = PackageSourceListRequest.MakeFastPathComplex(i.Source, i.Name, "", i.Version, "", userSpecifiedProvider ?? ""); 152 | 153 | fastPackReftable.AddOrSet(info, i); 154 | 155 | // check if the installed version matches with the one specified in the PSL.json. 156 | // If so, we choose PSL.json. 157 | var version = i.Version.CompareVersion(package.Version) ? package.Version : i.Version; 158 | request.YieldSoftwareIdentity(info, i.Name, version, i.VersionScheme, i.Summary, i.Source, i.SearchKey, i.FullPath, i.PackageFilename); 159 | } 160 | } 161 | 162 | /// 163 | /// Uninstalls a package 164 | /// 165 | /// package defined in the PackageSourceList 166 | /// 167 | /// An object passed in from the PackageManagement that contains functions that can be used to interact with its Provider 168 | /// 169 | internal static void UninstallPowershellArtifacts(PackageJson package, string fastPackageReference, PackageSourceListRequest request, Dictionary fastPackReftable) 170 | { 171 | if (request == null) 172 | { 173 | throw new ArgumentNullException("request"); 174 | } 175 | 176 | // If a user explicitly specifies -providerName psl, e.g., uninstall-package -providername psl -name xjea, 177 | // PSL will call PowerShellGet to uninstall the package. 178 | // Otherwise, it is expected that PowerShellGet will handle the uninstall-package and PSL won't 179 | // participate in the uninstall operation, e.g., uninstall-package xjea 180 | string userSpecifiedProvider = request.GetOptionValue("ProviderName") ?? request.GetOptionValue("Provider"); 181 | string providerNameFromPipeline = request.GetProviderNameFromFastPathComplex(fastPackageReference); 182 | 183 | if ((string.IsNullOrWhiteSpace(userSpecifiedProvider) || !Constants.ProviderName.EqualsIgnoreCase(userSpecifiedProvider)) && 184 | string.IsNullOrWhiteSpace(providerNameFromPipeline) || !Constants.ProviderName.EqualsIgnoreCase(providerNameFromPipeline)) 185 | { 186 | request.Verbose(Resources.Messages.UninstallPackageNotSupported, Constants.ProviderName, package.Name, Constants.ProviderNames.PowerShellGet); 187 | return; 188 | } 189 | 190 | request.Debug(Resources.Messages.DebugInfoCallMethod, Constants.ProviderName, "Calling UninstallPowershellArtifacts"); 191 | 192 | var provider = PackageSourceListRequest.FindProvider(request, package.Type, request, true); 193 | if (provider != null) 194 | { 195 | request.Debug("{0}: Using the provider '{1} to uninstall the package '{2}'", Constants.ProviderName, provider.Name, package.Name); 196 | 197 | if (!fastPackReftable.ContainsKey(fastPackageReference)) 198 | { 199 | request.WriteError(Internal.ErrorCategory.InvalidData, fastPackageReference, Resources.Messages.FailedToGetPackageObject, Constants.ProviderName, fastPackageReference); 200 | return; 201 | } 202 | 203 | var p = fastPackReftable[fastPackageReference]; 204 | 205 | //calling NuGet for uninstall 206 | var installing = provider.UninstallPackage(p, request); 207 | 208 | foreach (var i in installing) 209 | { 210 | request.YieldSoftwareIdentity(i.FastPackageReference, i.Name, i.Version, i.VersionScheme, i.Summary, i.Source, i.SearchKey, i.FullPath, i.PackageFilename); 211 | 212 | if (request.IsCanceled) 213 | { 214 | installing.Cancel(); 215 | } 216 | } 217 | } 218 | } 219 | 220 | } 221 | } -------------------------------------------------------------------------------- /PackageList/ProgressTracker.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | 16 | namespace Microsoft.PackageManagement.PackageSourceListProvider 17 | { 18 | using Microsoft.PackageManagement.Internal.Implementation; 19 | using System.Diagnostics.CodeAnalysis; 20 | 21 | internal class ProgressTracker 22 | { 23 | internal int ProgressID; 24 | internal int StartPercent; 25 | internal int EndPercent; 26 | 27 | internal ProgressTracker(int progressID) : this(progressID, 0, 100) { } 28 | 29 | internal ProgressTracker(int progressID, int startPercent, int endPercent) 30 | { 31 | ProgressID = progressID; 32 | StartPercent = startPercent; 33 | EndPercent = endPercent; 34 | } 35 | 36 | internal int ConvertPercentToProgress(double percent) 37 | { 38 | // for example, if startprogress is 50, end progress is 100, and if we want to complete 50% of that, 39 | // then the progress returned would be 75 40 | return StartPercent + (int)((EndPercent - StartPercent) * percent); 41 | } 42 | 43 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 44 | internal static ProgressTracker StartProgress(ProgressTracker parentTracker, string message, Request request) 45 | { 46 | if (request == null) 47 | { 48 | return null; 49 | } 50 | 51 | // if parent tracker is null, use 0 for parent id, else use the progressid of parent tracker 52 | return new ProgressTracker(request.StartProgress(parentTracker == null ? 0 : parentTracker.ProgressID, message)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /PackageSourceListProvider.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {68504D08-5F08-4A96-BC8C-D827764EE22E} 8 | Library 9 | Properties 10 | Microsoft.PackageManagement.PackageSourceListProvider 11 | Microsoft.PackageManagement.PackageSourceListProvider 12 | 512 13 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), solution.props))\solution.props 14 | 15 | v4.5 16 | 17 | 18 | true 19 | 20 | 21 | 35MSSharedLib1024.snk 22 | 23 | 24 | true 25 | 26 | 27 | 28 | 29 | $([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), PackageProvider.sln))\ 30 | $(SolutionDir) 31 | 32 | v4.0 33 | v4.5 34 | $(SolutionRootDir)output\$(TargetFrameworkVersion.Replace(".",""))\$(Configuration)\bin\ 35 | $(SolutionRootDir)intermediate\$(TargetFrameworkVersion.Replace(".",""))\$(Configuration)\$(AssemblyName)\ 36 | $(BaseIntermediateOutputPath) 37 | $(OutputPath)$(AssemblyName).XML 38 | 1591 39 | true 40 | FRAMEWORK$(TargetFrameworkVersion.Replace(".","")) 41 | output\$(TargetFrameworkVersion.Replace(".",""))\$(Configuration)\bin\ 42 | 43 | 44 | true 45 | full 46 | false 47 | $(DefineConstants);DEBUG;TRACE 48 | prompt 49 | 4 50 | output\v40\Debug\bin\Microsoft.PackageManagement.PackageListProvider.xml 51 | false 52 | output\Debug\bin\ 53 | 54 | 55 | pdbonly 56 | true 57 | TRACE 58 | prompt 59 | 4 60 | output\v40\Release\bin\TestPackageProvider.xml 61 | false 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | True 87 | True 88 | Messages.resx 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | ResXFileCodeGenerator 100 | Messages.Designer.cs 101 | Designer 102 | 103 | 104 | 105 | 106 | 107 | 108 | dependencies\Microsoft.PackageManagement-Win10-1607.dll 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | False 117 | C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | :Locate MT.EXE Tool: 132 | set MTEXE=$(ProjectDir)dependencies\mt.exe 133 | 134 | :Run the tool to add the manifest to the binary. 135 | 136 | "%25MTEXE%25" -manifest $(ProjectDir)provider.manifest -outputresource:$(TargetPath);#101 137 | 138 | 145 | -------------------------------------------------------------------------------- /PackageSourceListProvider.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23017.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageSourceListProvider", "PackageSourceListProvider.csproj", "{68504D08-5F08-4A96-BC8C-D827764EE22E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {68504D08-5F08-4A96-BC8C-D827764EE22E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {68504D08-5F08-4A96-BC8C-D827764EE22E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {68504D08-5F08-4A96-BC8C-D827764EE22E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {68504D08-5F08-4A96-BC8C-D827764EE22E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Microsoft.PackageManagement.PackageSourceListProvider")] 9 | [assembly: AssemblyDescription("PSL")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("PackageManagement")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("6141d4af-6ed6-4cfb-8b23-125e8909e873")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | [assembly: AssemblyVersion("1.0.0.210")] 33 | [assembly: AssemblyFileVersion("1.0.0.210")] 34 | -------------------------------------------------------------------------------- /Sdk/Constants.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Microsoft.PackageManagement.PackageSourceListProvider 3 | { 4 | public static class Constants 5 | { 6 | /// 7 | /// Name of this provider 8 | /// 9 | public const string ProviderName = "PSL"; 10 | 11 | /// 12 | /// Version of this provider 13 | /// 14 | public static readonly string ProviderVersion = "1.0.0.210"; 15 | 16 | /// 17 | /// Config file storing the info as a result of registering a package source 18 | /// 19 | public static readonly string SettingsFileName = "PSL.config"; 20 | 21 | /// 22 | /// Sample JSON file containing open powershell entry 23 | /// 24 | public static readonly string JSONFileName = "PSL.json"; 25 | public static readonly string CatFileName = "PSL.cat"; 26 | 27 | internal static class MediaType 28 | { 29 | public const string MsiPackage = "msi"; 30 | public const string MsuPackage = "msu"; 31 | 32 | public const string ExePackage = "exe"; 33 | public const string ZipPackage = "zip"; 34 | public const string NuGetPackage = "nupkg"; 35 | public const string AppxPackage = "appx"; 36 | public const string PsArtifacts = "psartifacts"; 37 | 38 | } 39 | 40 | internal static class ProviderNames 41 | { 42 | public const string PowerShellGet = "PowerShellGet"; 43 | public const string NuGet = "NuGet"; 44 | public const string Msi = "MSI"; 45 | public const string Msu = "MSU"; 46 | public const string Programs = "Programs"; 47 | public const string PSL = ProviderName; 48 | 49 | } 50 | 51 | #region copy common-constants-implementation /internal/public 52 | 53 | internal static string[] Empty = new string[0]; 54 | internal const string MinVersion = "0.0.0.1"; 55 | internal const string MSGPrefix = "MSG:"; 56 | internal const string Download = "Download"; 57 | internal const string Install = "Install"; 58 | public readonly static string[] FeaturePresent = new string[0]; 59 | internal const string CurrentUser = "CurrentUser"; 60 | internal const string AllUsers = "AllUsers"; 61 | internal const string PATH = "Path"; 62 | 63 | 64 | internal static class Features 65 | { 66 | public const string AutomationOnly = "automation-only"; 67 | public const string MagicSignatures = "magic-signatures"; 68 | public const string SupportedExtensions = "file-extensions"; 69 | public const string SupportedSchemes = "uri-schemes"; 70 | public const string PackageManagementMinimumVersion = "packagemanagement-minimum-version"; 71 | public const string SupportsPowerShellModules = "supports-powershell-modules"; 72 | public const string SupportsRegexSearch = "supports-regex-search"; 73 | public const string SupportsSubstringSearch = "supports-substring-search"; 74 | public const string SupportsWildcardSearch = "supports-wildcard-search"; 75 | } 76 | 77 | internal static class Messages 78 | { 79 | public const string UnableToFindDependencyPackage = "MSG:UnableToFindDependencyPackage"; 80 | public const string MissingRequiredParameter = "MSG:MissingRequiredParameter"; 81 | public const string PackageFailedInstallOrDownload = "MSG:PackageFailedInstallOrDownload"; 82 | public const string PackageSourceExists = "MSG:PackageSourceExists"; 83 | public const string ProtocolNotSupported = "MSG:ProtocolNotSupported"; 84 | public const string ProviderPluginLoadFailure = "MSG:ProviderPluginLoadFailure"; 85 | public const string ProviderSwidtagUnavailable = "MSG:ProviderSwidtagUnavailable"; 86 | public const string RemoveEnvironmentVariableRequiresElevation = "MSG:RemoveEnvironmentVariableRequiresElevation"; 87 | public const string SchemeNotSupported = "MSG:SchemeNotSupported"; 88 | public const string SourceLocationNotValid = "MSG:SourceLocationNotValid_Location"; 89 | public const string UnableToCopyFileTo = "MSG:UnableToCopyFileTo"; 90 | public const string UnableToCreateShortcutTargetDoesNotExist = "MSG:UnableToCreateShortcutTargetDoesNotExist"; 91 | public const string UnableToDownload = "MSG:UnableToDownload"; 92 | public const string UnableToOverwriteExistingFile = "MSG:UnableToOverwriteExistingFile"; 93 | public const string UnableToRemoveFile = "MSG:UnableToRemoveFile"; 94 | public const string UnableToResolvePackage = "MSG:UnableToResolvePackage"; 95 | public const string UnableToResolveSource = "MSG:UnableToResolveSource"; 96 | public const string UnableToUninstallPackage = "MSG:UnableToUninstallPackage"; 97 | public const string UnknownFolderId = "MSG:UnknownFolderId"; 98 | public const string UnknownProvider = "MSG:UnknownProvider"; 99 | public const string UnsupportedArchive = "MSG:UnsupportedArchive"; 100 | public const string UnsupportedProviderType = "MSG:UnsupportedProviderType"; 101 | public const string UriSchemeNotSupported = "MSG:UriSchemeNotSupported"; 102 | public const string UserDeclinedUntrustedPackageInstall = "MSG:UserDeclinedUntrustedPackageInstall"; 103 | public const string HashNotFound = "MSG:HashNotFound"; 104 | public const string HashNotMatch = "MSG:HashNotMatch"; 105 | public const string HashNotSupported = "MSG:HashNotSupported"; 106 | public const string DependencyLoopDetected = "MSG:DependencyLoopDetected"; 107 | public const string CouldNotGetResponseFromQuery = "MSG:CouldNotGetResponseFromQuery"; 108 | public const string SkippedDownloadedPackage = "MSG:SkippedDownloadedPackage"; 109 | public const string InstallRequiresCurrentUserScopeParameterForNonAdminUser = "MSG:InstallRequiresCurrentUserScopeParameterForNonAdminUser"; 110 | 111 | } 112 | 113 | internal static class OptionType { 114 | public const string String = "String"; 115 | public const string StringArray = "StringArray"; 116 | public const string Int = "Int"; 117 | public const string Switch = "Switch"; 118 | public const string Folder = "Folder"; 119 | public const string File = "File"; 120 | public const string Path = "Path"; 121 | public const string Uri = "Uri"; 122 | public const string SecureString = "SecureString"; 123 | } 124 | 125 | internal static class Status 126 | { 127 | internal const string TimedOut = "TimedOut"; 128 | } 129 | 130 | 131 | 132 | internal static class PackageStatus 133 | { 134 | public const string Available = "Available"; 135 | public const string Dependency = "Dependency"; 136 | public const string Installed = "Installed"; 137 | public const string Uninstalled = "Uninstalled"; 138 | } 139 | 140 | internal static class Parameters 141 | { 142 | public const string IsUpdate = "IsUpdatePackageSource"; 143 | public const string Name = "Name"; 144 | public const string Location = "Location"; 145 | } 146 | 147 | internal static class Signatures 148 | { 149 | public const string Cab = "4D534346"; 150 | public const string OleCompoundDocument = "D0CF11E0A1B11AE1"; 151 | public const string Zip = "504b0304"; 152 | //public static string[] ZipVariants = new[] {Zip, /* should have EXEs? */}; 153 | } 154 | 155 | internal static class SwidTag 156 | { 157 | public const string SoftwareIdentity = "SoftwareIdentity"; 158 | } 159 | 160 | #endregion 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /Sdk/Request.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) Microsoft Corporation. All rights reserved. 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software 9 | // distributed under the License is distributed on an "AS IS" BASIS, 10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | // See the License for the specific language governing permissions and 12 | // limitations under the License. 13 | // 14 | 15 | namespace Microsoft.PackageManagement.PackageListProvider 16 | { 17 | using System; 18 | using System.Collections.Generic; 19 | using System.Globalization; 20 | using System.Linq; 21 | using System.Security; 22 | using Internal.Api; 23 | using Microsoft.PackageManagement.Internal; 24 | 25 | 26 | public abstract class Requesttemp : IRequest 27 | { 28 | #region core-apis 29 | 30 | public abstract IPackageManagementService PackageManagementService { get; } 31 | 32 | public abstract IProviderServices ProviderServices { get; } 33 | 34 | #endregion 35 | 36 | #region copy host-apis 37 | 38 | /* Synced/Generated code =================================================== */ 39 | public abstract bool IsCanceled { get; } 40 | 41 | public abstract string GetMessageString(string messageText, string defaultText); 42 | 43 | public abstract bool Warning(string messageText); 44 | 45 | public abstract bool Error(string id, string category, string targetObjectValue, string messageText); 46 | 47 | public abstract bool Message(string messageText); 48 | 49 | public abstract bool Verbose(string messageText); 50 | 51 | public abstract bool Debug(string messageText); 52 | 53 | public abstract int StartProgress(int parentActivityId, string messageText); 54 | 55 | public abstract bool Progress(int activityId, int progressPercentage, string messageText); 56 | 57 | public abstract bool CompleteProgress(int activityId, bool isSuccessful); 58 | 59 | /// 60 | /// Used by a provider to request what metadata keys were passed from the user 61 | /// 62 | /// 63 | public abstract IEnumerable OptionKeys { get; } 64 | 65 | /// 66 | /// 67 | /// 68 | /// 69 | public abstract IEnumerable GetOptionValues(string key); 70 | 71 | public abstract IEnumerable Sources { get; } 72 | 73 | public abstract string CredentialUsername { get; } 74 | 75 | public abstract SecureString CredentialPassword { get; } 76 | 77 | public abstract bool ShouldBootstrapProvider(string requestor, string providerName, string providerVersion, string providerType, string location, string destination); 78 | 79 | public abstract bool ShouldContinueWithUntrustedPackageSource(string package, string packageSource); 80 | 81 | public abstract bool ShouldContinue(string query, string caption, ref bool yesToAll, ref bool noToAll); 82 | 83 | public abstract bool ShouldContinue(string query, string caption); 84 | 85 | public abstract bool AskPermission(string permission); 86 | 87 | public abstract bool IsInteractive { get; } 88 | 89 | public abstract int CallCount { get; } 90 | 91 | #endregion 92 | 93 | #region copy response-apis 94 | 95 | /* Synced/Generated code =================================================== */ 96 | 97 | /// 98 | /// Used by a provider to return fields for a SoftwareIdentity. 99 | /// 100 | /// 101 | /// 102 | /// 103 | /// 104 | /// 105 | /// 106 | /// 107 | /// 108 | /// 109 | /// 110 | public abstract string YieldSoftwareIdentity(string fastPath, string name, string version, string versionScheme, string summary, string source, string searchKey, string fullPath, string packageFileName); 111 | 112 | public abstract string YieldSoftwareIdentityXml(string xmlSwidTag, bool commitImmediately); 113 | 114 | public abstract bool IsSwidTagXml(string xmlSwidTag); 115 | 116 | public abstract string AddTagId(string tagId); 117 | 118 | public abstract string AddCulture(string xmlLang); 119 | 120 | public abstract string AddMetadata(string name, string value); 121 | 122 | public abstract string AddMetadata(string elementPath, string name, string value); 123 | 124 | public abstract string AddMetadata(string elementPath, Uri @namespace, string name, string value); 125 | 126 | public abstract string AddMeta(string elementPath); 127 | 128 | public abstract string AddEntity(string name, string regid, string role, string thumbprint); 129 | 130 | public abstract string AddLink(Uri referenceUri, string relationship, string mediaType, string ownership, string use, string appliesToMedia, string artifact); 131 | 132 | public abstract string AddDependency(string providerName, string packageName, string version, string source, string appliesTo); 133 | 134 | public abstract string AddPayload(); 135 | 136 | public abstract string AddEvidence(DateTime date, string deviceId); 137 | 138 | public abstract string AddDirectory(string elementPath, string directoryName, string location, string root, bool isKey); 139 | 140 | public abstract string AddFile(string elementPath, string fileName, string location, string root, bool isKey, long size, string version); 141 | 142 | public abstract string AddProcess(string elementPath, string processName, int pid); 143 | 144 | public abstract string AddResource(string elementPath, string type); 145 | 146 | /// 147 | /// Used by a provider to return fields for a package source (repository) 148 | /// 149 | /// 150 | /// 151 | /// 152 | /// 153 | /// 154 | /// 155 | public abstract bool YieldPackageSource(string name, string location, bool isTrusted, bool isRegistered, bool isValidated); 156 | 157 | /// 158 | /// Used by a provider to return the fields for a Metadata Definition 159 | /// The cmdlets can use this to supply tab-completion for metadata to the user. 160 | /// 161 | /// the provider-defined name of the option 162 | /// one of ['string','int','path','switch'] 163 | /// if the parameter is mandatory 164 | /// 165 | public abstract bool YieldDynamicOption(string name, string expectedType, bool isRequired); 166 | 167 | public bool YieldDynamicOption(string name, string expectedType, bool isRequired, IEnumerable permittedValues) 168 | { 169 | return YieldDynamicOption(name, expectedType, isRequired) && (permittedValues ?? Enumerable.Empty()).All(each => YieldKeyValuePair(name, each)); 170 | } 171 | 172 | public abstract bool YieldKeyValuePair(string key, string value); 173 | 174 | public abstract bool YieldValue(string value); 175 | 176 | #endregion 177 | 178 | 179 | #region declare Request-implementation 180 | /// 181 | /// Yield values in a dictionary as key/value pairs. (one pair for each value in each key) 182 | /// 183 | /// 184 | /// 185 | public bool Yield(Dictionary dictionary) 186 | { 187 | if (dictionary != null) 188 | { 189 | return dictionary.All(Yield); 190 | } 191 | return true; 192 | } 193 | 194 | public bool Yield(KeyValuePair pair) 195 | { 196 | if (pair.Value.Length == 0) 197 | { 198 | return YieldKeyValuePair(pair.Key, null); 199 | } 200 | return pair.Value.All(each => YieldKeyValuePair(pair.Key, each)); 201 | } 202 | 203 | public bool Error(ErrorCategory category, string targetObjectValue, string messageText, params object[] args) 204 | { 205 | return Error(messageText, category.ToString(), targetObjectValue, FormatMessageString(messageText, args)); 206 | } 207 | 208 | public bool Warning(string messageText, params object[] args) 209 | { 210 | return Warning(FormatMessageString(messageText, args)); 211 | } 212 | 213 | public bool Message(string messageText, params object[] args) 214 | { 215 | return Message(FormatMessageString(messageText, args)); 216 | } 217 | 218 | public bool Verbose(string messageText, params object[] args) 219 | { 220 | return Verbose(FormatMessageString(messageText, args)); 221 | } 222 | 223 | public bool Debug(string messageText, params object[] args) 224 | { 225 | return Debug(FormatMessageString(messageText, args)); 226 | } 227 | 228 | public int StartProgress(int parentActivityId, string messageText, params object[] args) 229 | { 230 | return StartProgress(parentActivityId, FormatMessageString(messageText, args)); 231 | } 232 | 233 | public bool Progress(int activityId, int progressPercentage, string messageText, params object[] args) 234 | { 235 | return Progress(activityId, progressPercentage, FormatMessageString(messageText, args)); 236 | } 237 | 238 | public string GetOptionValue(string name) 239 | { 240 | // get the value from the request 241 | return (GetOptionValues(name) ?? Enumerable.Empty()).LastOrDefault(); 242 | } 243 | 244 | private static string FixMeFormat(string formatString, object[] args) 245 | { 246 | if (args == null || args.Length == 0) 247 | { 248 | // not really any args, and not really expectng any 249 | return formatString.Replace('{', '\u00ab').Replace('}', '\u00bb'); 250 | } 251 | return args.Aggregate(formatString.Replace('{', '\u00ab').Replace('}', '\u00bb'), (current, arg) => current + string.Format(CultureInfo.CurrentCulture, " \u00ab{0}\u00bb", arg)); 252 | } 253 | 254 | #endregion 255 | 256 | private string FormatMessageString(string messageText, params object[] args) 257 | { 258 | if (string.IsNullOrWhiteSpace(messageText)) 259 | { 260 | return string.Empty; 261 | } 262 | 263 | if (messageText.IndexOf(Constants.MSGPrefix, StringComparison.CurrentCultureIgnoreCase) == 0) 264 | { 265 | // check with the caller first, then with the local resources, and fallback to using the messageText itself. 266 | messageText = GetMessageString(messageText.Substring(Constants.MSGPrefix.Length), messageText) ?? messageText; 267 | } 268 | 269 | // if it doesn't look like we have the correct number of parameters 270 | // let's return a fix-me-format string. 271 | var c = messageText.ToCharArray().Where(each => each == '{').Count(); 272 | if (c < args.Length) 273 | { 274 | return FixMeFormat(messageText, args); 275 | } 276 | return string.Format(CultureInfo.CurrentCulture, messageText, args); 277 | } 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /Utility/FileUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.PackageManagement.Provider.Utility 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text.RegularExpressions; 9 | using System.Threading; 10 | 11 | public static class FileUtility 12 | { 13 | public static string GetTempFileFullPath(string fileExtension) 14 | { 15 | 16 | if (string.IsNullOrWhiteSpace(fileExtension)) 17 | { 18 | return fileExtension; 19 | } 20 | 21 | string tempFolder = Path.GetTempPath(); 22 | 23 | string randomFileName = Path.GetRandomFileName(); 24 | 25 | //get rid of the file extension 26 | randomFileName = Path.GetFileNameWithoutExtension(randomFileName) + fileExtension; 27 | 28 | string file = Path.Combine(tempFolder, randomFileName); 29 | 30 | if (File.Exists(file)) 31 | { 32 | //try it again if the generated file already exists 33 | file = GetTempFileFullPath(fileExtension); 34 | } 35 | 36 | return file; 37 | } 38 | 39 | public static string MakePackageFileName(bool excludeVersion, string packageName, string version, string fileExtension) 40 | { 41 | string fileName = (excludeVersion) ? packageName : (packageName + "." + version); 42 | return fileName + fileExtension; 43 | } 44 | 45 | public static string MakePackageDirectoryName(bool excludeVersion, string destinationPath, string packageName, string version) 46 | { 47 | string baseDir = Path.Combine(destinationPath, packageName); 48 | 49 | return (excludeVersion) ? baseDir : (baseDir + "." + version); 50 | } 51 | 52 | public static void CopyDirectory(string source, string dest, bool copySubDirs) 53 | { 54 | // Get the subdirectories for the specified directory. 55 | DirectoryInfo dir = new DirectoryInfo(source); 56 | 57 | if (!dir.Exists) 58 | { 59 | throw new DirectoryNotFoundException(string.Format(CultureInfo.InvariantCulture, "Source directory '{0}' does not exist or could not be found.", source)); 60 | } 61 | 62 | // Some packages have directories with spaces. However it shows as $20, e.g. Install-Module -name xHyper-VBackup. 63 | // It has something like Content\Deployment\Module%20References\..., with that, the PowerShellGet provider won't be able to handle it. 64 | // Add the following code to unescape percent-encoding characters 65 | string newdest = Uri.UnescapeDataString(dest); 66 | 67 | // If the destination directory doesn't exist, create it. 68 | if (!Directory.Exists(newdest)) 69 | { 70 | Directory.CreateDirectory(newdest); 71 | } 72 | 73 | // Get the files in the directory and copy them to the new location. 74 | FileInfo[] files = dir.GetFiles(); 75 | foreach (FileInfo file in files) 76 | { 77 | string tempPath = Path.Combine(newdest, file.Name); 78 | // unescape special characters like %20 79 | tempPath = Uri.UnescapeDataString(tempPath); 80 | 81 | file.CopyTo(tempPath, true /*overwrite*/); 82 | } 83 | 84 | // If copying subdirectories, copy them and their contents to new location. 85 | if (copySubDirs) 86 | { 87 | DirectoryInfo[] sourceDirs = dir.GetDirectories(); 88 | 89 | foreach (DirectoryInfo subdir in sourceDirs) 90 | { 91 | string temppath = Path.Combine(newdest, subdir.Name); 92 | CopyDirectory(subdir.FullName, temppath, true); 93 | } 94 | } 95 | } 96 | 97 | public static void DeleteDirectory(string fullPath, bool recursive, bool isThrow) 98 | { 99 | DoSafeAction(() => DeleteDirectory(fullPath, recursive), isThrow); 100 | } 101 | 102 | public static void DeleteFile(string fullPath, bool isThrow) 103 | { 104 | DoSafeAction(() => DeleteFile(fullPath), isThrow); 105 | } 106 | 107 | public static IEnumerable GetFiles(string fullPath, string filter, bool recursive) 108 | { 109 | fullPath = PathUtility.EnsureTrailingSlash(fullPath); 110 | if (String.IsNullOrWhiteSpace(filter)) 111 | { 112 | filter = "*.*"; 113 | } 114 | try 115 | { 116 | if (!Directory.Exists(fullPath)) 117 | { 118 | return Enumerable.Empty(); 119 | } 120 | return Directory.EnumerateFiles(fullPath, filter, recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); 121 | // .Select(MakeRelativePath); 122 | } 123 | catch (UnauthorizedAccessException) 124 | { 125 | 126 | } 127 | catch (DirectoryNotFoundException) 128 | { 129 | 130 | } 131 | 132 | return Enumerable.Empty(); 133 | } 134 | 135 | public static IEnumerable GetDirectories(string fullPath) 136 | { 137 | try 138 | { 139 | fullPath = PathUtility.EnsureTrailingSlash(fullPath); 140 | if (!Directory.Exists(fullPath)) 141 | { 142 | return Enumerable.Empty(); 143 | } 144 | 145 | return Directory.EnumerateDirectories(fullPath); 146 | } 147 | catch (UnauthorizedAccessException) 148 | { 149 | 150 | } 151 | catch (DirectoryNotFoundException) 152 | { 153 | 154 | } 155 | 156 | return Enumerable.Empty(); 157 | } 158 | 159 | public static DateTimeOffset GetLastModified(string fullPath) 160 | { 161 | if (File.Exists(fullPath)) 162 | { 163 | return File.GetLastWriteTime(fullPath); 164 | } 165 | return Directory.GetLastWriteTime(fullPath); 166 | } 167 | 168 | private static void DeleteFile(string path) 169 | { 170 | if (string.IsNullOrWhiteSpace(path) || !File.Exists(path)) 171 | { 172 | return; 173 | } 174 | 175 | try 176 | { 177 | //sometimes there are difficulties to delete readonly files 178 | MakeFileWritable(path); 179 | File.Delete(path); 180 | } 181 | catch (FileNotFoundException) 182 | { 183 | } 184 | } 185 | 186 | private static void MakeFileWritable(string path) 187 | { 188 | FileAttributes attributes = File.GetAttributes(path); 189 | if (attributes.HasFlag(FileAttributes.ReadOnly)) 190 | { 191 | File.SetAttributes(path, attributes & ~FileAttributes.ReadOnly); 192 | } 193 | } 194 | 195 | private static void DeleteDirectory(string fullPath, bool recursive) 196 | { 197 | if (string.IsNullOrWhiteSpace(fullPath) || !Directory.Exists(fullPath)) 198 | { 199 | return; 200 | } 201 | 202 | try 203 | { 204 | //path = GetFullPath(path); 205 | Directory.Delete(fullPath, recursive); 206 | 207 | // The directory is not guaranteed to be gone since there could be 208 | // other open handles. Wait, up to half a second, until the directory is gone. 209 | for (int i = 0; Directory.Exists(fullPath) && i < 5; ++i) 210 | { 211 | Thread.Sleep(100); 212 | } 213 | } 214 | catch (DirectoryNotFoundException) 215 | { 216 | } 217 | } 218 | 219 | // [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We want to log an exception as a warning and move on")] 220 | private static void DoSafeAction(Action action, bool isThrow) 221 | { 222 | try 223 | { 224 | Attempt(action); 225 | } 226 | catch (Exception e) 227 | { 228 | if (isThrow) 229 | { 230 | throw new Exception(e.Message); 231 | } 232 | } 233 | } 234 | 235 | private static void Attempt(Action action, int retries = 5, int delayBeforeRetry = 100) 236 | { 237 | while (retries > 0) 238 | { 239 | try 240 | { 241 | action(); 242 | break; 243 | } 244 | catch 245 | { 246 | retries--; 247 | if (retries == 0) 248 | { 249 | throw; 250 | } 251 | } 252 | Thread.Sleep(delayBeforeRetry); 253 | } 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /Utility/PathUtility.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Security; 3 | 4 | namespace Microsoft.PackageManagement.Provider.Utility 5 | { 6 | using System; 7 | using System.IO; 8 | using System.Net; 9 | using System.Net.Http; 10 | using System.Threading; 11 | using System.Threading.Tasks; 12 | using System.Runtime.InteropServices; 13 | using System.Globalization; 14 | using System.Diagnostics.CodeAnalysis; 15 | 16 | internal static class PathUtility 17 | { 18 | 19 | internal static string EnsureTrailingSlash(string path) 20 | { 21 | //The value of DirectorySeparatorChar is a slash ("/") on UNIX, and a backslash ("\") on the Windows and Macintosh. 22 | return EnsureTrailingCharacter(path, Path.DirectorySeparatorChar); 23 | } 24 | 25 | private static string EnsureTrailingCharacter(string path, char trailingCharacter) 26 | { 27 | if (path == null) 28 | { 29 | throw new ArgumentNullException("path"); 30 | } 31 | 32 | // if the path is empty, we want to return the original string instead of a single trailing character. 33 | if (path.Length == 0 || path[path.Length - 1] == trailingCharacter) 34 | { 35 | return path; 36 | } 37 | 38 | return path + trailingCharacter; 39 | } 40 | 41 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 42 | internal static HttpResponseMessage GetHttpResponse(HttpClient httpClient, string query, Func isCanceled, ActionlogRetry, Actionverbose, Actiondebug) 43 | { 44 | // try downloading for 3 times 45 | int remainingTry = 3; 46 | object timerLock = new object(); 47 | Timer timer = null; 48 | CancellationTokenSource cts = null; 49 | bool cleanUp = true; 50 | 51 | Action cleanUpAction = () => 52 | { 53 | lock (timerLock) 54 | { 55 | // check whether clean up is already done before or not 56 | if (!cleanUp) 57 | { 58 | try 59 | { 60 | if (timer != null) 61 | { 62 | // stop timer 63 | timer.Change(Timeout.Infinite, Timeout.Infinite); 64 | // dispose it 65 | timer.Dispose(); 66 | } 67 | 68 | // dispose the token 69 | if (cts != null) 70 | { 71 | cts.Cancel(); 72 | cts.Dispose(); 73 | } 74 | } 75 | catch { } 76 | 77 | cleanUp = true; 78 | } 79 | } 80 | }; 81 | 82 | while (remainingTry > 0) 83 | { 84 | // if user cancel the request, no need to do anything 85 | if (isCanceled()) 86 | { 87 | break; 88 | } 89 | 90 | try 91 | { 92 | // decrease try by 1 93 | remainingTry -= 1; 94 | 95 | // create new timer and cancellation token source 96 | lock (timerLock) 97 | { 98 | // check every second to see whether request is cancelled 99 | timer = new Timer(_ => 100 | { 101 | if (isCanceled()) 102 | { 103 | cleanUpAction(); 104 | } 105 | }, null, 500, 1000); 106 | 107 | cts = new CancellationTokenSource(); 108 | 109 | cleanUp = false; 110 | } 111 | 112 | Task task = httpClient.GetAsync(query, cts.Token); 113 | 114 | // start the task 115 | task.Wait(); 116 | 117 | if (task.IsCompleted && task is Task) 118 | { 119 | var result = (task as Task).Result; 120 | 121 | // if success, returns result 122 | if (result.IsSuccessStatusCode) 123 | { 124 | return result; 125 | } 126 | 127 | // otherwise, we have to retry again 128 | } 129 | 130 | // if request is canceled, don't retry 131 | if (isCanceled()) 132 | { 133 | break; 134 | } 135 | 136 | logRetry(query, remainingTry); 137 | } 138 | catch (Exception ex) 139 | { 140 | if (ex is AggregateException) 141 | { 142 | (ex as AggregateException).Handle(singleException => 143 | { 144 | // report each of the exception 145 | verbose(singleException.Message); 146 | debug(singleException.StackTrace); 147 | return true; 148 | }); 149 | } 150 | else 151 | { 152 | // single exception, just report the message and stacktrace 153 | verbose(ex.Message); 154 | debug(ex.StackTrace); 155 | } 156 | 157 | // if there is exception, we will retry too 158 | logRetry(query, remainingTry); 159 | } 160 | finally 161 | { 162 | cleanUpAction(); 163 | } 164 | } 165 | 166 | return null; 167 | } 168 | 169 | [SuppressMessage("Microsoft.Performance", "CA1811: Avoid uncalled private coded", Justification = "Common code.")] 170 | internal static string UriCombine(string query, string append) 171 | { 172 | if (String.IsNullOrWhiteSpace(query)) return append; 173 | if (String.IsNullOrWhiteSpace(append)) return query; 174 | 175 | return query.TrimEnd('/') + "/" + append.TrimStart('/'); 176 | } 177 | 178 | 179 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "It's used in Nano")] 180 | internal static string SecureStringToString(SecureString secure) 181 | { 182 | IntPtr value = IntPtr.Zero; 183 | try 184 | { 185 | #if !CORECLR 186 | value = Marshal.SecureStringToCoTaskMemUnicode(secure); 187 | #else 188 | value = SecureStringMarshal.SecureStringToCoTaskMemUnicode(secure); 189 | #endif 190 | return Marshal.PtrToStringUni(value); 191 | } 192 | finally 193 | { 194 | #if !CORECLR 195 | Marshal.ZeroFreeGlobalAllocUnicode(value); 196 | #else 197 | SecureStringMarshal.ZeroFreeCoTaskMemUnicode(value); 198 | #endif 199 | } 200 | } 201 | 202 | internal static NetworkCredential GetNetworkCredential(string username, SecureString password) 203 | { 204 | // if request has username and password, use that 205 | if (!string.IsNullOrWhiteSpace(username) && password != null) 206 | { 207 | #if CORECLR 208 | // networkcredential class on coreclr does not accept securestring so we have to convert 209 | return new NetworkCredential(username, SecureStringToString(password)); 210 | #else 211 | return new NetworkCredential(username, password); 212 | #endif 213 | } 214 | 215 | // if no user name and password, returns null 216 | return null; 217 | } 218 | 219 | internal static HttpClient GetHttpClientHelper(string username, SecureString password, IWebProxy webProxy) 220 | { 221 | var clientHandler = new HttpClientHandler(); 222 | 223 | var networkCredential = GetNetworkCredential(username, password); 224 | 225 | // if we are given a network credential, use that 226 | if (networkCredential != null) 227 | { 228 | // else use the one given to us 229 | clientHandler.Credentials = networkCredential; 230 | clientHandler.PreAuthenticate = true; 231 | } 232 | else 233 | { 234 | clientHandler.UseDefaultCredentials = true; 235 | } 236 | 237 | // do not need to set proxy of httpClient or httpClientHandler because it will use system proxy setting by default 238 | // discussion here (https://github.com/dotnet/corefx/issues/7037) 239 | 240 | if (webProxy != null) 241 | { 242 | // if webproxy is not null, use that 243 | clientHandler.Proxy = webProxy; 244 | } 245 | 246 | var httpClient = new HttpClient(clientHandler); 247 | 248 | // Mozilla/5.0 is the general token that says the browser is Mozilla compatible, and is common to almost every browser today. 249 | httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 NuGet"); 250 | 251 | return httpClient; 252 | } 253 | 254 | } 255 | } -------------------------------------------------------------------------------- /Utility/XmlUtility.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.PackageManagement.Provider.Utility 2 | { 3 | using System.IO; 4 | using System.Xml; 5 | using System.Xml.Linq; 6 | 7 | public static class XmlUtility 8 | { 9 | public static XDocument LoadSafe(string filePath) 10 | { 11 | var settings = CreateSafeSettings(); 12 | using (var reader = XmlReader.Create(filePath, settings)) 13 | { 14 | return XDocument.Load(reader); 15 | } 16 | } 17 | 18 | public static XDocument LoadSafe(Stream input, bool ignoreWhiteSpace) 19 | { 20 | var settings = CreateSafeSettings(ignoreWhiteSpace); 21 | var reader = XmlReader.Create(input, settings); 22 | return XDocument.Load(reader); 23 | } 24 | 25 | private static XmlReaderSettings CreateSafeSettings(bool ignoreWhiteSpace = false) 26 | { 27 | var safeSettings = new XmlReaderSettings 28 | { 29 | DtdProcessing = DtdProcessing.Prohibit, 30 | IgnoreWhitespace = ignoreWhiteSpace 31 | }; 32 | 33 | return safeSettings; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Version/DependencyVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Globalization; 7 | 8 | namespace Microsoft.PackageManagement.Provider.Utility 9 | { 10 | /// 11 | /// Represents dependency version returned by nuget api call 12 | /// 13 | public class DependencyVersion 14 | { 15 | public SemanticVersion MinVersion { get; set; } 16 | /// 17 | /// True if the version we are looking for includes min version 18 | /// 19 | 20 | public bool IsMinInclusive { get; set; } 21 | public SemanticVersion MaxVersion { get; set; } 22 | /// 23 | /// True if the version we are looking for includes the max version 24 | /// 25 | public bool IsMaxInclusive { get; set; } 26 | 27 | /// 28 | /// Parse and return a dependency version 29 | /// The version string is either a simple version or an arithmetic range 30 | /// e.g. 31 | /// 1.0 --> 1.0 ≤ x 32 | /// (,1.0] --> x ≤ 1.0 33 | /// (,1.0) --> x lt 1.0 34 | /// [1.0] --> x == 1.0 35 | /// (1.0,) --> 1.0 lt x 36 | /// (1.0, 2.0) --> 1.0 lt x lt 2.0 37 | /// [1.0, 2.0] --> 1.0 ≤ x ≤ 2.0 38 | /// 39 | /// 40 | /// 41 | /// 42 | public static DependencyVersion ParseDependencyVersion(string value) 43 | { 44 | var depVers = new DependencyVersion(); 45 | 46 | if (String.IsNullOrWhiteSpace(value)) 47 | { 48 | return depVers; 49 | } 50 | 51 | value = value.Trim(); 52 | 53 | char first = value.First(); 54 | char last = value.Last(); 55 | 56 | if (first != '(' && first != '[' && last != ']' && last != ')') 57 | { 58 | // Stand alone version 59 | depVers.IsMinInclusive = true; 60 | depVers.MinVersion = new SemanticVersion(value); 61 | return depVers; 62 | } 63 | 64 | // value must have length greater than 3 65 | if (value.Length < 3) 66 | { 67 | return depVers; 68 | } 69 | 70 | // The first character must be [ or ( 71 | switch (value.First()) 72 | { 73 | case '[': 74 | depVers.IsMinInclusive = true; 75 | break; 76 | case '(': 77 | depVers.IsMinInclusive = false; 78 | break; 79 | default: 80 | // If not, return without setting anything 81 | return depVers; 82 | } 83 | 84 | // The last character must be ] or ) 85 | switch (value.Last()) 86 | { 87 | case ']': 88 | depVers.IsMaxInclusive = true; 89 | break; 90 | case ')': 91 | depVers.IsMaxInclusive = false; 92 | break; 93 | default: 94 | // If not, return without setting anything 95 | return depVers; 96 | } 97 | 98 | // Get rid of the two brackets 99 | value = value.Substring(1, value.Length - 2); 100 | 101 | // Split by comma, and make sure we don't get more than two pieces 102 | string[] parts = value.Split(','); 103 | 104 | // Wrong format if we have more than 2 parts or all the parts are empty 105 | if (parts.Length > 2 || parts.All(String.IsNullOrEmpty)) 106 | { 107 | return depVers; 108 | } 109 | 110 | // First part is min 111 | string minVersionString = parts[0]; 112 | 113 | // If there is only 1 part then first part will also be max 114 | string maxVersionString = (parts.Length == 2) ? parts[1] : parts[0]; 115 | 116 | // Get min version if we have it 117 | if (!String.IsNullOrWhiteSpace(minVersionString)) 118 | { 119 | depVers.MinVersion = new SemanticVersion(minVersionString); 120 | } 121 | 122 | // Get max version if we have it 123 | if (!String.IsNullOrWhiteSpace(maxVersionString)) 124 | { 125 | depVers.MaxVersion = new SemanticVersion(maxVersionString); 126 | } 127 | 128 | return depVers; 129 | } 130 | 131 | public override string ToString() 132 | { 133 | // Returns nothing if no min or max 134 | if (MinVersion == null && MaxVersion == null) 135 | { 136 | return null; 137 | } 138 | 139 | // If we have min and minInclusive but no max, then return min string 140 | if (MinVersion != null && IsMinInclusive && MaxVersion == null && !IsMaxInclusive) 141 | { 142 | return MinVersion.ToString(); 143 | } 144 | 145 | // MinVersion and MaxVersion is the same and both inclusives then return the value 146 | if (MinVersion == MaxVersion && IsMinInclusive && IsMaxInclusive) 147 | { 148 | return String.Format(CultureInfo.InvariantCulture, "[{0}]", MinVersion); 149 | } 150 | 151 | char lhs = IsMinInclusive ? '[' : '('; 152 | char rhs = IsMaxInclusive ? ']' : ')'; 153 | 154 | return String.Format(CultureInfo.InvariantCulture, "{0}{1}, {2}{3}", lhs, MinVersion, MaxVersion, rhs); 155 | } 156 | 157 | } 158 | 159 | public class DependencyVersionComparerBasedOnMinVersion : IComparer 160 | { 161 | /// 162 | /// Return 1 if dep1.minversion gt dep2.minversion 163 | /// Return 0 if dep1.minversion eq dep2.minversion 164 | /// Return -1 if dep.minversion lt dep2.minversion 165 | /// We consider null min version as the smallest possible value 166 | /// so if dep1.minversion = null and dep2.minversion = 0.1 then we return -1 since dep1.minversion lt dep2.minversion 167 | /// 168 | /// 169 | /// 170 | /// 171 | public int Compare(DependencyVersion dep1, DependencyVersion dep2) 172 | { 173 | if (dep1.MinVersion == null) 174 | { 175 | if (dep2.MinVersion == null) 176 | { 177 | return 0; 178 | } 179 | 180 | return -1; 181 | } 182 | 183 | // get here means dep1.minversion is not null 184 | if (dep2.MinVersion == null) 185 | { 186 | return 1; 187 | } 188 | 189 | // if they are the same, the one with min inclusive is smaller 190 | if (dep1.MinVersion.Equals(dep2.MinVersion)) 191 | { 192 | if (dep1.IsMinInclusive) 193 | { 194 | if (dep2.IsMinInclusive) 195 | { 196 | return 0; 197 | } 198 | 199 | return -1; 200 | } 201 | 202 | // reach here means dep1 is not min inclusive 203 | if (dep2.IsMinInclusive) 204 | { 205 | return 1; 206 | } 207 | 208 | // here means both are not mean inclusive 209 | return 0; 210 | } 211 | 212 | // reach here means both are not null 213 | return dep1.MinVersion.CompareTo(dep2.MinVersion); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /Version/SemanticVersion.cs: -------------------------------------------------------------------------------- 1 | using MMicrosoft.PackageManagement.Provider.Utility; 2 | 3 | namespace Microsoft.PackageManagement.Provider.Utility 4 | { 5 | using System; 6 | using System.ComponentModel; 7 | using System.Globalization; 8 | using System.Text.RegularExpressions; 9 | 10 | /// 11 | /// A hybrid implementation of SemVer that supports semantic versioning as described at http://semver.org while not strictly enforcing it to 12 | /// allow older 4-digit versioning schemes to continue working. 13 | /// 14 | /// Example: EntityFramework 6.1.3-beta1 15 | /// Version: 6.1.3 16 | /// SpecialVersion: -beta1 17 | /// OrginalString: 6.1.3-beta1 18 | /// 19 | /// 20 | [TypeConverter(typeof(SemanticVersionTypeConverter))] 21 | public sealed class SemanticVersion : IComparable, IComparable, IEquatable 22 | { 23 | private const RegexOptions _flags = RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture; 24 | internal const string AllowFourPartsVersion = @"(?\d+(\s*\.\s*\d+){0,3})"; 25 | internal const string ThreePartsVersion = @"(?\d+(\.\d+){2})"; 26 | // the pre release regex is of the form - where version is set of identifier delimited by ".". Each identifer can be any characters in [A-z0-9a-z-] 27 | internal const string ReleasePattern = @"(?-[A-Z0-9a-z\-]+(\.[A-Z0-9a-z\-]+)*)?"; 28 | // The build regex is of the same form except with a + instead of - 29 | internal const string BuildPattern = @"(?\+[A-Z0-9a-z\-]+(\.[A-Z0-9a-z\-]+)*)?"; 30 | internal static string SemanticVersionPattern = String.Concat("^", AllowFourPartsVersion, ReleasePattern, BuildPattern, "$"); 31 | 32 | private static readonly Regex _semanticVersionRegex = new Regex(SemanticVersionPattern, _flags); 33 | private static readonly Regex _strictSemanticVersionRegex = new Regex(@"^(?\d+(\.\d+){2})(?-[A-Z0-9a-z\-]+(\.[A-Z0-9a-z\-]+)*)?(?\+[A-Z0-9a-z\-]+(\.[A-Z0-9a-z\-]+)*)?$", _flags); 34 | private readonly string _originalString; 35 | 36 | public SemanticVersion(string version) 37 | : this(Parse(version)) 38 | { 39 | // The constructor normalizes the version string so that it we do not need to normalize it every time we need to operate on it. 40 | // The original string represents the original form in which the version is represented to be used when printing. 41 | _originalString = version; 42 | } 43 | 44 | public SemanticVersion(int major, int minor, int build, int revision) 45 | : this(new Version(major, minor, build, revision)) 46 | { 47 | } 48 | 49 | public SemanticVersion(int major, int minor, int build, string specialVersion) 50 | : this(new Version(major, minor, build), specialVersion) 51 | { 52 | } 53 | 54 | public SemanticVersion(Version version) 55 | : this(version, String.Empty) 56 | { 57 | } 58 | 59 | public SemanticVersion(Version version, string specialVersion) 60 | : this(version, specialVersion, null) 61 | { 62 | } 63 | 64 | private SemanticVersion(Version version, string specialVersion, string originalString) 65 | { 66 | if (version == null) 67 | { 68 | throw new ArgumentNullException("version"); 69 | } 70 | Version = NormalizeVersionValue(version); 71 | SpecialVersion = specialVersion ?? String.Empty; 72 | _originalString = String.IsNullOrWhiteSpace(originalString) ? (version + (!String.IsNullOrWhiteSpace(specialVersion) ? '-' + specialVersion : null)) : originalString; 73 | } 74 | 75 | public SemanticVersion(SemanticVersion semVer) 76 | { 77 | if (semVer == null) { 78 | throw new ArgumentNullException("semVer"); 79 | } 80 | _originalString = semVer.ToString(); 81 | Version = semVer.Version; 82 | SpecialVersion = semVer.SpecialVersion; 83 | } 84 | 85 | /// 86 | /// Gets the normalized version portion. 87 | /// 88 | public Version Version 89 | { 90 | get; 91 | private set; 92 | } 93 | 94 | /// 95 | /// Gets the optional special version. 96 | /// 97 | public string SpecialVersion 98 | { 99 | get; 100 | private set; 101 | } 102 | 103 | /// 104 | /// Get an array of the major, minor, build, and revision. 105 | /// 106 | /// 107 | public string[] GetOriginalVersionComponents() 108 | { 109 | if (!String.IsNullOrWhiteSpace(_originalString)) 110 | { 111 | string original; 112 | 113 | // search the start of the SpecialVersion part, if any 114 | int dashIndex = _originalString.IndexOf('-'); 115 | if (dashIndex != -1) 116 | { 117 | // remove the SpecialVersion part 118 | original = _originalString.Substring(0, dashIndex); 119 | } 120 | else 121 | { 122 | original = _originalString; 123 | } 124 | 125 | return SplitAndPadVersionString(original); 126 | } 127 | else 128 | { 129 | return SplitAndPadVersionString(Version.ToString()); 130 | } 131 | } 132 | 133 | private static string[] SplitAndPadVersionString(string version) 134 | { 135 | string[] a = version.Split('.'); 136 | if (a.Length == 4) 137 | { 138 | return a; 139 | } 140 | else 141 | { 142 | // if 'a' has less than 4 elements, we pad the '0' at the end 143 | // to make it 4. 144 | var b = new string[4] { "0", "0", "0", "0" }; 145 | Array.Copy(a, 0, b, 0, a.Length); 146 | return b; 147 | } 148 | } 149 | 150 | /// 151 | /// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an optional special version. 152 | /// 153 | public static SemanticVersion Parse(string version) 154 | { 155 | if (String.IsNullOrWhiteSpace(version)) 156 | { 157 | throw new ArgumentNullException("version"); 158 | } 159 | 160 | SemanticVersion semVer; 161 | if (!TryParse(version, out semVer)) 162 | { 163 | return null; 164 | } 165 | 166 | return semVer; 167 | } 168 | 169 | /// 170 | /// Parses a version string using loose semantic versioning rules that allows 2-4 version components followed by an optional special version. 171 | /// 172 | public static bool TryParse(string version, out SemanticVersion value) 173 | { 174 | return TryParseInternal(version, _semanticVersionRegex, out value); 175 | } 176 | 177 | /// 178 | /// Parses a version string using strict semantic versioning rules that allows exactly 3 components and an optional special version. 179 | /// 180 | public static bool TryParseStrict(string version, out SemanticVersion value) 181 | { 182 | return TryParseInternal(version, _strictSemanticVersionRegex, out value); 183 | } 184 | 185 | private static bool TryParseInternal(string version, Regex regex, out SemanticVersion semVer) 186 | { 187 | semVer = null; 188 | if (String.IsNullOrWhiteSpace(version)) 189 | { 190 | return false; 191 | } 192 | 193 | var match = regex.Match(version.Trim()); 194 | Version versionValue; 195 | if (!match.Success || !Version.TryParse(match.Groups["Version"].Value, out versionValue)) 196 | { 197 | return false; 198 | } 199 | 200 | semVer = new SemanticVersion(NormalizeVersionValue(versionValue), match.Groups["Release"].Value.TrimStart('-'), version.Replace(" ", "")); 201 | return true; 202 | } 203 | 204 | private static Version NormalizeVersionValue(Version version) 205 | { 206 | return new Version(version.Major, 207 | version.Minor, 208 | Math.Max(version.Build, 0), 209 | Math.Max(version.Revision, 0)); 210 | } 211 | 212 | public int CompareTo(object obj) 213 | { 214 | if (ReferenceEquals(obj, null)) 215 | { 216 | return 1; 217 | } 218 | SemanticVersion other = obj as SemanticVersion; 219 | if (other == null) 220 | { 221 | throw new ArgumentNullException("obj"); 222 | } 223 | return CompareTo(other); 224 | } 225 | 226 | public int CompareTo(SemanticVersion other) 227 | { 228 | if (other == null) 229 | { 230 | return 1; 231 | } 232 | 233 | int result = Version.CompareTo(other.Version); 234 | 235 | if (result != 0) 236 | { 237 | return result; 238 | } 239 | 240 | bool empty = String.IsNullOrWhiteSpace(SpecialVersion); 241 | bool otherEmpty = String.IsNullOrWhiteSpace(other.SpecialVersion); 242 | if (empty && otherEmpty) 243 | { 244 | return 0; 245 | } 246 | else if (empty) 247 | { 248 | return 1; 249 | } 250 | else if (otherEmpty) 251 | { 252 | return -1; 253 | } 254 | return StringComparer.OrdinalIgnoreCase.Compare(SpecialVersion, other.SpecialVersion); 255 | } 256 | 257 | public static bool operator ==(SemanticVersion version1, SemanticVersion version2) 258 | { 259 | if (ReferenceEquals(version1, null)) 260 | { 261 | return ReferenceEquals(version2, null); 262 | } 263 | return version1.Equals(version2); 264 | } 265 | 266 | public static bool operator !=(SemanticVersion version1, SemanticVersion version2) 267 | { 268 | return !(version1 == version2); 269 | } 270 | 271 | public static bool operator <(SemanticVersion version1, SemanticVersion version2) 272 | { 273 | if (version1 == null) 274 | { 275 | throw new ArgumentNullException("version1"); 276 | } 277 | return version1.CompareTo(version2) < 0; 278 | } 279 | 280 | public static bool operator <=(SemanticVersion version1, SemanticVersion version2) 281 | { 282 | return (version1 == version2) || (version1 < version2); 283 | } 284 | 285 | public static bool operator >(SemanticVersion version1, SemanticVersion version2) 286 | { 287 | if (version1 == null) 288 | { 289 | throw new ArgumentNullException("version1"); 290 | } 291 | return version2 < version1; 292 | } 293 | 294 | public static bool operator >=(SemanticVersion version1, SemanticVersion version2) 295 | { 296 | return (version1 == version2) || (version1 > version2); 297 | } 298 | 299 | public override string ToString() 300 | { 301 | return _originalString; 302 | } 303 | 304 | public bool Equals(SemanticVersion other) 305 | { 306 | if (other == null) 307 | { 308 | return ReferenceEquals(Version, null); 309 | } 310 | 311 | return Version.Equals(other.Version) && 312 | SpecialVersion.Equals(other.SpecialVersion, StringComparison.OrdinalIgnoreCase); 313 | } 314 | 315 | public override bool Equals(object obj) 316 | { 317 | SemanticVersion semVer = obj as SemanticVersion; 318 | return !ReferenceEquals(null, semVer) && Equals(semVer); 319 | } 320 | 321 | public override int GetHashCode() 322 | { 323 | int hashCode = Version.GetHashCode(); 324 | if (SpecialVersion != null) 325 | { 326 | hashCode = hashCode * 4567 + SpecialVersion.GetHashCode(); 327 | } 328 | 329 | return hashCode; 330 | } 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /Version/SemanticVersionTypeConverter.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.PackageManagement.Provider.Utility; 2 | 3 | namespace MMicrosoft.PackageManagement.Provider.Utility 4 | { 5 | using System; 6 | using System.ComponentModel; 7 | using System.Globalization; 8 | 9 | /// 10 | /// Convert String to SemanticVersion type 11 | /// 12 | public class SemanticVersionTypeConverter : TypeConverter 13 | { 14 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 15 | { 16 | return sourceType == typeof(string); 17 | } 18 | 19 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 20 | { 21 | var stringValue = value as string; 22 | SemanticVersion semVer; 23 | if (stringValue != null && SemanticVersion.TryParse(stringValue, out semVer)) 24 | { 25 | return semVer; 26 | } 27 | return null; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.0.{build} 2 | configuration: 3 | - Release 4 | - Win10 Release 5 | platform: Any CPU 6 | # Install Pester 7 | install: 8 | - cinst -y pester 9 | clone_folder: c:\projects\PSLProvider 10 | build_script: 11 | - ps: | 12 | $buildConfig = 'Release' 13 | & msbuild 'c:\projects\PSLProvider\PackageSourceListProvider.csproj' /logger:'C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll' /P:Configuration=$buildConfig 14 | #after_build: 15 | #- ps: >- 16 | # Get-ChildItem "C:\projects\psl\output\Release\bin" -Recurse | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name -DeploymentName releasebits -Type Auto } 17 | 18 | before_test: 19 | - ps: >- 20 | & "c:\projects\PSLProvider\Test\psl.tests.ps1" 21 | -------------------------------------------------------------------------------- /dependencies/Microsoft.PackageManagement-win10-1607.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneGet/PSLProvider/eb9147dad37ebe2a548806f7391b888a7518c81c/dependencies/Microsoft.PackageManagement-win10-1607.dll -------------------------------------------------------------------------------- /dependencies/mt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneGet/PSLProvider/eb9147dad37ebe2a548806f7391b888a7518c81c/dependencies/mt.exe -------------------------------------------------------------------------------- /docs/find-package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneGet/PSLProvider/eb9147dad37ebe2a548806f7391b888a7518c81c/docs/find-package.png -------------------------------------------------------------------------------- /docs/high-level-diagram.md: -------------------------------------------------------------------------------- 1 | 2 | # High Level Flow Diagram 3 | 4 | Let's take PowerShell package as an example. 5 | Here is the flow diagram when you type "Find-Package PowerShell". 6 | 7 | ![find-package](find-package.png) 8 | 9 | 10 | The flow diagram when you type "Install-Package PowerShell". 11 | 12 | ![install-package](install-package.png) 13 | 14 | ## Where Packages installed to? 15 | | Package Type | Install Location 16 | |:-------------|:---------------------------- 17 | | msi |Default path, i.e., a particular .msi package decides.
Whether admin privilege is required depends on a particular package. 18 | | exe |Default path, i.e., a particular .exe package decides.
Whether admin privilege is required depends on a particular package. 19 | | PowerShell modules | Well-known PowerShell module install location 20 | | nupkg | Default location:
$env:programfiles\NuGet\Packages\
$env:userprofile\NuGet\Packages\ 
User can also specify the destination path in the json file if they want to install packages to different folder. 21 | | Zip | zip packages are installed under Destination\PackageName\PackageVersion. "Destination" is defined in json file. It's required. 22 | -------------------------------------------------------------------------------- /docs/install-package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OneGet/PSLProvider/eb9147dad37ebe2a548806f7391b888a7518c81c/docs/install-package.png -------------------------------------------------------------------------------- /docs/known-issue.md: -------------------------------------------------------------------------------- 1 | # Known Issues for PSL 2 | 3 | 1. Save-Package is not supported 4 | 2. Proxy support is not supported yet 5 | 3. New Feature: missing OS requirement check 6 | "os": { 7 | "minimumversion": "10.0.2", 8 | "architecture": [ "amd64" ], 9 | "installationOption": [ "Windows Nano Server" ] 10 | 11 | -------------------------------------------------------------------------------- /provider.manifest: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/psl.tests.ps1: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, 10 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | # 14 | # ------------------ PackageManagement Test ----------------------------------- 15 | #ipmo "$PSScriptRoot\utility.psm1" 16 | $InternalGallery = "https://dtlgalleryint.cloudapp.net/api/v2/" 17 | $InternalSource = 'OneGetTestSource' 18 | 19 | 20 | # set to this feed to bootstrap the testing version 21 | #$env:BootstrapProviderTestfeedUrl = "https://raw.githubusercontent.com/OneGet/ProviderRegistry/testing/providers.masterList.feed.swidtag" 22 | 23 | $psl = "psl" 24 | $location ="$PSScriptRoot\testpsl.json" 25 | $source ="psl" 26 | 27 | import-module packagemanagement 28 | 29 | $PSVersionable 30 | & systeminfo.exe 31 | 32 | Get-Packageprovider -verbose 33 | $provider = Get-PackageProvider -verbose 34 | if($provider.Name -notcontains $psl) 35 | { 36 | $a= Find-PackageProvider -Name $psl -verbose -ForceBootstrap 37 | 38 | if($a.Name -eq $psl) 39 | { 40 | Install-PackageProvider $psl -verbose -force 41 | } 42 | } 43 | 44 | Get-Packageprovider -verbose 45 | $sources = Get-PackageSource -ProviderName $psl 46 | foreach ($s in $sources) 47 | { 48 | Unregister-PackageSource -name $s.Name 49 | } 50 | 51 | Register-PackageSource -name $source -ProviderName $psl -Location $location 52 | 53 | Register-PackageSource -Name $InternalSource -Location $InternalGallery -ProviderName 'PowerShellGet' -Trusted -ForceBootstrap -ErrorAction SilentlyContinue 54 | 55 | 56 | 57 | 58 | # ------------------------------------------------------------------------------ 59 | # Actual Tests: 60 | 61 | Describe "psl testing" -Tags @('BVT', 'DRT') { 62 | AfterAll { 63 | #reset the environment variable 64 | $env:BootstrapProviderTestfeedUrl="" 65 | } 66 | 67 | It "find-package" { 68 | 69 | $a=find-package -ProviderName $psl 70 | $a.Name -contains "node.js" | Should Be $true 71 | $a.Name -contains "Notepad++" | Should Be $true 72 | 73 | 74 | $b= find-package -source $source 75 | $b.Name -contains "node.js" | Should Be $true 76 | $b.Name -contains "Notepad++" | Should Be $true 77 | 78 | $c= find-package -name node.js -ProviderName $psl 79 | $c.Name -contains "node.js" | Should Be $true 80 | 81 | } 82 | 83 | It "find-package with version" { 84 | 85 | $a= find-package -name node.js -AllVersions -ProviderName $psl -source $source 86 | 87 | $a.Name -contains "node.js" | Should Be $true 88 | 89 | $a | ?{ $_.Version -eq "6.2.0" } | should not BeNullOrEmpty 90 | $a | ?{ $_.Version -eq "6.1.0" } | should not BeNullOrEmpty 91 | } 92 | 93 | It "install-package with msi" { 94 | 95 | $package = "node.js" 96 | 97 | $exist = get-package $package -ProviderName $psl -ErrorAction SilentlyContinue 98 | if($exist) { 99 | Uninstall-package $package -ProviderName $psl 100 | } 101 | 102 | 103 | $a= install-package -name $package -ProviderName $psl -source $source -force -SkipHashValidation 104 | $a.Name -contains $package | Should Be $true 105 | 106 | $b = get-package $package -verbose -provider $psl 107 | $b.Name -contains $package | Should Be $true 108 | 109 | $c= Uninstall-package $package -verbose -ProviderName $psl 110 | $c.Name -contains $package | Should Be $true 111 | } 112 | 113 | It "install-package with zip" { 114 | 115 | $package = "Sysinternals" 116 | 117 | $a= install-package -name $package -ProviderName $psl -source $source -SkipHashValidation -force 118 | $a.Name -contains $package | Should Be $true 119 | 120 | $b = get-package $package -provider $psl 121 | $b.Name -contains $package | Should Be $true 122 | 123 | $c= Uninstall-package $package -provider $psl 124 | $c.Name -contains $package | Should Be $true 125 | } 126 | 127 | 128 | It "install-package with exe" { 129 | 130 | $package = "notepad++" 131 | 132 | $exist = get-package $package -ProviderName $psl -ErrorAction SilentlyContinue 133 | if($exist) { 134 | Uninstall-package $package -ProviderName $ps 135 | } 136 | 137 | $a= install-package -name $package -ProviderName $psl -source $source -SkipHashValidation -force 138 | $a.Name -contains $package | Should Be $true 139 | 140 | $b = get-package $package -provider $psl 141 | $b.Name -contains $package | Should Be $true 142 | 143 | $c= Uninstall-package $package -ProviderName $psl 144 | $c.Name -contains $package | Should Be $true 145 | } 146 | 147 | 148 | It "install-package with NuGet" { 149 | 150 | $package = "jquery" 151 | 152 | # 3.0.0 of jQuery contains destination definition in psl.json 153 | $a= install-package -name $package -ProviderName $psl -source $source -force -RequiredVersion 3.0.0 154 | $a.Name -contains $package | Should Be $true 155 | 156 | $b = get-package $package -provider $psl -RequiredVersion 3.0.0 157 | $b.Name -contains $package | Should Be $true 158 | 159 | $c= Uninstall-package $package -RequiredVersion 3.0.0 160 | $c.Name -contains $package | Should Be $true 161 | 162 | $d = get-package $package -provider $psl -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -RequiredVersion 3.0.0 163 | $d | ?{ $_.Version.ToString() -eq "3.0.0" } | should BeNullOrEmpty 164 | 165 | # 2.2.4 of jQuery does not contain destination definition in psl.json 166 | $a= install-package -name $package -ProviderName $psl -source $source -force -RequiredVersion 2.2.4 167 | $a.Version -contains "2.2.4" | Should Be $true 168 | 169 | $b = get-package $package -provider $psl -RequiredVersion 2.2.4 170 | $b.Name -contains $package | Should Be $true 171 | 172 | $c= Uninstall-package $package -ProviderName $psl -RequiredVersion 2.2.4 173 | $c.Name -contains $package | Should Be $true 174 | 175 | $d = get-package $package -RequiredVersion 2.2.4 -provider $psl -ErrorAction SilentlyContinue -WarningAction SilentlyContinue 176 | $d | ?{ $_.Version.ToString() -eq "2.2.4" } | should BeNullOrEmpty 177 | } 178 | 179 | It "install-package with PowerShellGet" { 180 | 181 | $package = "GistProvider" 182 | 183 | $a= install-package -name $package -ProviderName $psl -source $source -force -RequiredVersion 1.6 184 | $a.Name -contains $package | Should Be $true 185 | 186 | $b = get-package $package -provider $psl -RequiredVersion 1.6 187 | $b.Name -contains $package | Should Be $true 188 | 189 | $c= Uninstall-package $package -ProviderName $psl -RequiredVersion 1.6 190 | $c.Name -contains $package | Should Be $true 191 | 192 | $d = get-package $package -provider $psl -RequiredVersion 1.6 -ErrorAction SilentlyContinue -WarningAction SilentlyContinue 193 | $d | ?{ $_.Version.ToString() -eq "1.6" } | should BeNullOrEmpty 194 | } 195 | 196 | It "install-package by passing in -source location - expected warning" { 197 | 198 | $package = "Sysinternals" 199 | 200 | $a= install-package -name $package -ProviderName $psl -source $location -SkipHashValidation -force -WarningVariable wv 201 | $a.Name -contains $package | Should Be $true 202 | $wv | Should Not BeNullOrEmpty 203 | 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /test/testpsl.json: -------------------------------------------------------------------------------- 1 | { 2 | "PowerShell": { 3 | "displayName": "PowerShell_6.0.0.9", 4 | "source": "https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-alpha.9/PowerShell_6.0.0.9-alpha.9-win10-x64.msi", 5 | "hash": { 6 | "algorithm": "sha512", 7 | "hashCode": "FFD0F988110BAECDC616ADA5E743581D5ABCE42B68AF78E1A95DB911F82A5AE6CBACCD9BE5712CF263B76FA208737185A093CDB47D4DE9077657C611DAFE4DA9" 8 | }, 9 | "summary": "Powershell", 10 | "type": "msi", 11 | "version": "6.0.0.9" 12 | }, 13 | 14 | "NodeJs": [ 15 | { 16 | "common": { 17 | "displayName": "Node.js", 18 | "summary": "Node.js is a cross-platform runtime environment for developing server-side Web applications.", 19 | "type": "msi", 20 | "os": { 21 | "minimumversion": "10.0.11085", 22 | "architecture": [ "amd64" ], 23 | "installationOption": [ "Windows Desktop", "Windows Server" ] 24 | } 25 | } 26 | }, 27 | { 28 | "source": "https://nodejs.org/dist/v6.2.0/node-v6.2.0-x64.msi", 29 | "version": "6.2.0" 30 | }, 31 | { 32 | "source": "https://nodejs.org/dist/v6.1.0/node-v6.1.0-x64.msi", 33 | "version": "6.1.0" 34 | }, 35 | { 36 | "source": "https://nodejs.org/dist/doesnotexist-x64.appx", 37 | "version": "1.0.1", 38 | "type": "appx", 39 | "os": { 40 | "minimumversion": "10.0.2", 41 | "architecture": [ "amd64" ], 42 | "installationOption": [ "Windows Nano Server" ] 43 | } 44 | } 45 | ], 46 | 47 | "Sysinternals": { 48 | "displayName": "Sysinternals", 49 | "source": "https://live.sysinternals.com/files/SysinternalsSuite.zip", 50 | "summary": "The Sysinternals Troubleshooting Utilities have been rolled up into a single suite of tools.", 51 | "type": "zip", 52 | "version": "2016.4.28.0", 53 | "destination": "C:\\Program Files" 54 | }, 55 | 56 | "Notepad++": { 57 | "displayName": "Notepad++", 58 | "source": "https://notepad-plus-plus.org/repository/6.x/6.9.2/npp.6.9.2.Installer.exe", 59 | "summary": "Notepad++ is a free source code editor.", 60 | "type": "exe", 61 | "installArguments": "/S", 62 | "UnInstallAdditionalArguments": "/S", 63 | "version": "6.9.2" 64 | }, 65 | 66 | "docker": { 67 | "source": "https://get.docker.com/builds/Windows/x86_64/docker-1.12.0.zip", 68 | "hash": { 69 | "algorithm": "sha512", 70 | "hashCode": "A64DB25C5B0CF30BDD8CA253CCBB5F5D97B4F8E68895CEE7AD0E908CAEAC1BA3F1FC0DA5FADA8111D393C7148562362EA465934E61B7E823935F9687E80D8091" 71 | }, 72 | "summary": "Docker zip package", 73 | "type": "zip", 74 | "version": "1.12.0", 75 | "destination": "%programfiles%" 76 | }, 77 | 78 | "jquery": [ 79 | { 80 | "common": { 81 | "source": "https://www.nuget.org/api/v2", 82 | "summary": "jQuery is a fast, small, and feature-rich JavaScript library. It makes things like HTML document traversal and manipulation, event handling, animation, etc.", 83 | "type": "nupkg" 84 | } 85 | }, 86 | 87 | { 88 | "source": "https://www.nuget.org/api/v2", 89 | "version": "3.0.0", 90 | "destination":".\\" 91 | }, 92 | 93 | { 94 | "source": "https://www.nuget.org/api/v2", 95 | "version": "2.2.4" 96 | } 97 | ], 98 | 99 | "GistProvider": { 100 | "type": "psartifacts", 101 | "summary": "A PowerShell Module.", 102 | "license": "", 103 | "author": "Dfinke.", 104 | "version": "1.6", 105 | "source": "https://dtlgalleryint.cloudapp.net/api/v2/" 106 | } 107 | } 108 | --------------------------------------------------------------------------------