/// <summary> /// Search the entire repository for the packages /// </summary> /// <param name="source">Package Source</param> /// <param name="name">Package name</param> /// <returns></returns> private IEnumerable<PackageItem> SearchForPackages(PackageSource source, string name) { try { Debug(Resources.Messages.DebugInfoCallMethod3, "NuGetRequest", "SearchForPackages", name); // no repository found then returns nothing if (source.Repository == null) { return Enumerable.Empty<PackageItem>(); } var isNameContainsWildCard = false; var searchTerm = Contains.Value ?? string.Empty; // Deal with two cases here: // 1. The package name contains wildcards like "Find-Package -name JQ*". We search based on the name. // 2. A user does not provide the Name parameter, // A user provides name with wildcards. We will use name as the SearchTerm while Contains and Tag will be used // for filtering on the results. if (!String.IsNullOrWhiteSpace(name) && WildcardPattern.ContainsWildcardCharacters(name)) { isNameContainsWildCard = true; // NuGet does not support PowerShell/POSIX style wildcards and supports only '*' in searchTerm // Replace the range from '[' - to ']' with * and ? with * then wildcard pattern is applied on the results var tempName = name; var squareBracketPattern = Regex.Escape("[") + "(.*?)]"; foreach (Match match in Regex.Matches(tempName, squareBracketPattern)) { tempName = tempName.Replace(match.Value, "*"); } //As the nuget does not support wildcard, we remove '?', '*' wildcards from the search string, and then //looking for the longest string in the given string as a keyword for the searching in the repository. //A sample case will be something like find-package sql*Compact*. //When the AllVersions property exists, the query like the following containing the wildcards does not work. We need to remove the wild cards and //replace it with the longest string searhc. //http://www.powershellgallery.com/api/v2/Search()?$orderby=DownloadCount%20desc,Id&searchTerm='tsdprovi*'&targetFramework=''&includePrerelease=false if ((!String.IsNullOrWhiteSpace(name) && source.Location.IndexOf("powershellgallery.com", StringComparison.OrdinalIgnoreCase) == -1) || (AllVersions.Value)) { //get rid of wildcard and search the longest string in the given name for nuget.org tempName = tempName.Split('?', '*').OrderBy(namePart => namePart.Length).Last(); } //Deal with a case when a user type Find-Package * if (String.Equals(tempName, "*", StringComparison.OrdinalIgnoreCase)) { //We use '' instead of "*" because searchterm='*' does not return the entire repository. tempName = string.Empty; } searchTerm = tempName; } // Add the Tags for the search. if (FilterOnTag != null) { // E.g. searchTerm = "tag:dscresource_xFirefox tag:command_Start-Process" searchTerm = FilterOnTag.Value.Where(tag => !string.IsNullOrWhiteSpace(tag)).Aggregate(searchTerm, (current, tag) => current + " tag:" + tag); } Verbose(Resources.Messages.SearchingRepository, source.Repository.Source, searchTerm); // Handling case where a user does not provide Name parameter var pkgs = source.Repository.Search(searchTerm, this); if (!String.IsNullOrWhiteSpace(name)) { //Filter on the results. This is needed because we replace [...] regex in the searchterm at the begining of this method. pkgs = FilterOnName(pkgs, name, isNameContainsWildCard); } pkgs = FilterOnContains(pkgs); var pkgsItem = pkgs.Select(pkg => new PackageItem { Package = pkg, PackageSource = source, FastPath = MakeFastPath(source, pkg.Id, pkg.Version.ToString()) }); return pkgsItem; } catch (Exception e) { e.Dump(this); Warning(e.Message); return Enumerable.Empty<PackageItem>(); } }
private IEnumerable<PackageItem> GetPackageById(PackageSource source, string name, NuGetRequest request, string requiredVersion = null, string minimumVersion = null, string maximumVersion = null, bool minInclusive = true, bool maxInclusive = true) { try { Debug(Resources.Messages.DebugInfoCallMethod3, "NuGetRequest", "GetPackageById", name); // source should be attached to a repository if (source.Repository == null) { return Enumerable.Empty<PackageItem>(); } // otherwise fall back to traditional behavior var pkgs = source.Repository.FindPackagesById(name, request); // exact version is required if required version is not null or empty OR maxversion == minversion and min and max inclusive are true bool exactVersionRequired = (!string.IsNullOrWhiteSpace(requiredVersion)) || (!string.IsNullOrWhiteSpace(maximumVersion) && !string.IsNullOrWhiteSpace(minimumVersion) && (new SemanticVersion(minimumVersion) == new SemanticVersion(maximumVersion)) && minInclusive && maxInclusive); // if required version not specified then don't use unlisted version // unlisted version is the one that has published year as 1900 if (!exactVersionRequired) { pkgs = pkgs.Where(pkg => pkg.Published.HasValue && pkg.Published.Value.Year > 1900); } if (AllVersions.Value){ //Display versions from lastest to oldest pkgs = (from p in pkgs select p).OrderByDescending(x => x.Version); } else if (String.IsNullOrWhiteSpace(requiredVersion) && String.IsNullOrWhiteSpace(minimumVersion) && String.IsNullOrWhiteSpace(maximumVersion)) { if (AllowPrereleaseVersions.Value || source.Repository.IsFile) { //Handling something like Json.NET 7.0.1-beta3 as well as local repository pkgs = from p in pkgs group p by p.Id into newGroup select newGroup.Aggregate((current, next) => (next.Version > current.Version) ? next : current); } else { pkgs = from p in pkgs where p.IsLatestVersion select p; } } else if (!exactVersionRequired && !AllowPrereleaseVersions.Value) { // if exact version is not required and allow prerelease is false, we will have to filter out prerelease version pkgs = from p in pkgs where string.IsNullOrWhiteSpace(p.Version.SpecialVersion) select p; } pkgs = FilterOnContains(pkgs); pkgs = FilterOnTags(pkgs); var results = FilterOnVersion(pkgs, requiredVersion, minimumVersion, maximumVersion, minInclusive, maxInclusive) .Select(pkg => new PackageItem { Package = pkg, PackageSource = source, FastPath = MakeFastPath(source, pkg.Id, pkg.Version.ToString()) }); return results; } catch (Exception e) { e.Dump(this); return Enumerable.Empty<PackageItem>(); } }
private PackageSource ResolvePackageSource(string nameOrLocation) { Debug(Resources.Messages.DebugInfoCallMethod3, "NuGetRequest", "ResolvePackageSource", nameOrLocation); if (IsCanceled) { return null; } var source = FindRegisteredSource(nameOrLocation); if (source != null) { Debug(Resources.Messages.FoundRegisteredSource, nameOrLocation, NuGetConstant.ProviderName); return source; } Debug(Resources.Messages.NotFoundRegisteredSource, nameOrLocation, NuGetConstant.ProviderName); try { // is the given value a filename? if (File.Exists(nameOrLocation)) { Debug(Resources.Messages.SourceIsAFilePath, nameOrLocation); return new PackageSource() { Request = this, IsRegistered = false, IsValidated = true, Location = nameOrLocation, Name = nameOrLocation, Trusted = true, }; } } catch { } try { // is the given value a directory? if (Directory.Exists(nameOrLocation)) { Debug(Resources.Messages.SourceIsADirectory, nameOrLocation); return new PackageSource() { Request = this, IsRegistered = false, IsValidated = true, Location = nameOrLocation, Name = nameOrLocation, Trusted = true, }; } } catch { } if (Uri.IsWellFormedUriString(nameOrLocation, UriKind.Absolute)) { var uri = new Uri(nameOrLocation, UriKind.Absolute); if (!SupportedSchemes.Contains(uri.Scheme.ToLowerInvariant())) { WriteError(ErrorCategory.InvalidArgument, uri.ToString(), Constants.Messages.UriSchemeNotSupported, uri); return null; } // this is an URI, and it looks like one type that we support if (SkipValidate.Value || NuGetPathUtility.ValidateSourceUri(SupportedSchemes, uri, this)) { var uriSource = new PackageSource { Request = this, IsRegistered = false, IsValidated = !SkipValidate.Value, Location = nameOrLocation, Name = nameOrLocation, Trusted = false, }; return uriSource; } } WriteError(ErrorCategory.InvalidArgument, nameOrLocation, Constants.Messages.UnableToResolveSource, nameOrLocation); return null; }
/// <summary> /// Encrypting a path containing package source location, id, version and source /// </summary> /// <param name="source">package source</param> /// <param name="id">package id</param> /// <param name="version">package version</param> /// <returns></returns> internal string MakeFastPath(PackageSource source, string id, string version) { // if this is called from powershellget, append nonupkg at the end if (IsCalledFromPowerShellGet) { return String.Format(CultureInfo.InvariantCulture, @"${0}\{1}\{2}\{3}\{4}", source.Serialized, id.ToBase64(), version.ToBase64(), (Sources ?? new string[0]).Select(each => each.ToBase64()).SafeAggregate((current, each) => current + "|" + each), "powershellget".ToBase64()); } return String.Format(CultureInfo.InvariantCulture, @"${0}\{1}\{2}\{3}", source.Serialized, id.ToBase64(), version.ToBase64(), (Sources ?? new string[0]).Select(each => each.ToBase64()).SafeAggregate((current, each) => current + "|" + each)); }
/// <summary> /// Encrypting a path containing package source location, id, version and source /// </summary> /// <param name="source">package source</param> /// <param name="id">package id</param> /// <param name="version">package version</param> /// <returns></returns> internal string MakeFastPath(PackageSource source, string id, string version) { return String.Format(CultureInfo.InvariantCulture, @"${0}\{1}\{2}\{3}", source.Serialized, id.ToBase64(), version.ToBase64(), (Sources ?? new string[0]).Select(each => each.ToBase64()).SafeAggregate((current, each) => current + "|" + each)); }
/// <summary> /// Download a package from a file repository that matches the given version and name and install it on the local system. /// </summary> /// <param name="packageName">Package name</param> /// <param name="version">Package version</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <param name="source">Package source</param> /// <param name="sourceFilePath">File source path pointing to the package to be installed</param> /// <returns>PackageItem object</returns> internal static PackageItem InstallPackageLocal( string packageName, string version, NuGetRequest request, PackageSource source, string sourceFilePath ) { request.Debug(Messages.DebugInfoCallMethod, "NuGetClient", "InstallPackageLocal"); string tempSourceFilePath = null; string tempSourceDirectory = null; string directoryToDeleteWhenFailed = String.Empty; bool needToDelete = false; try { string destinationFilePath = request.Destination; request.Verbose(string.Format(CultureInfo.InvariantCulture, "InstallPackageLocal' - name='{0}', version='{1}',destination='{2}'", packageName, version, destinationFilePath)); request.Debug(sourceFilePath); if (string.IsNullOrWhiteSpace(sourceFilePath)) { throw new ArgumentNullException(sourceFilePath); } if (!File.Exists(sourceFilePath)) { throw new FileNotFoundException(sourceFilePath); } //Create the destination directory if it does not exist if (!Directory.Exists(destinationFilePath)) { Directory.CreateDirectory(destinationFilePath); directoryToDeleteWhenFailed = destinationFilePath; } //Make a temp folder in the user appdata temp directory tempSourceFilePath = FileUtility.GetTempFileFullPath(fileExtension: NuGetConstant.PackageExtension); //Copy over the source file from the folder repository to the temp folder File.Copy(sourceFilePath, tempSourceFilePath, true); //Unzip it tempSourceDirectory = PackageUtility.DecompressFile(tempSourceFilePath); //Get a packge directory under the destination path to store the package string installedFolder = FileUtility.MakePackageDirectoryName(request.ExcludeVersion.Value, destinationFilePath, packageName, version); // if we did not set the directory before, then the destinationFilePath already exists, so we should not delete it if (string.IsNullOrWhiteSpace(directoryToDeleteWhenFailed)) { directoryToDeleteWhenFailed = installedFolder; } //File folder format of the Nuget packages looks like the following after installed: //Jquery.2.0.1 // - JQuery.2.0.1.nupkg // - contents and other stuff //Copy the unzipped files to under the package installed folder FileUtility.CopyDirectory(tempSourceDirectory, installedFolder, true); //Read the package manifest and return the package object string nuspec = Path.Combine(installedFolder, packageName) + NuGetConstant.ManifestExtension; PackageBase package = PackageUtility.ProcessNuspec(nuspec); var pkgItem = new PackageItem { Package = package, PackageSource = source, FastPath = request.MakeFastPath(source, package.Id, package.Version), FullPath = installedFolder }; // Delete the nuspec file //Get a package file path var nuspecFilePath = Path.Combine(installedFolder, packageName + NuGetConstant.ManifestExtension); if (File.Exists(nuspecFilePath)) { FileUtility.DeleteFile(nuspecFilePath, false); } request.Debug(Messages.DebugInfoReturnCall, "NuGetClient", "InstallPackageLocal"); return pkgItem; } catch (Exception ex) { needToDelete = true; // the warning will be package "packageName" failed to install ex.Dump(request); request.WriteError(ErrorCategory.InvalidResult, packageName, Constants.Messages.PackageFailedInstallOrDownload, packageName, CultureInfo.CurrentCulture.TextInfo.ToLower(Constants.Install)); throw; } finally { if (needToDelete && Directory.Exists(directoryToDeleteWhenFailed)) { FileUtility.DeleteDirectory(directoryToDeleteWhenFailed, true, isThrow: false); } FileUtility.DeleteFile(tempSourceFilePath, isThrow:false); FileUtility.DeleteDirectory(tempSourceDirectory, recursive: true, isThrow: false); } }
/// <summary> /// Download a package that matches the given version and name and install it on the local system. /// </summary> /// <param name="packageName">Package name</param> /// <param name="version">Package version</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <param name="source">Package source</param> /// <param name="queryUrl">Full uri</param> /// <param name="packageHash">the hash of the package</param> /// <param name="packageHashAlgorithm">the hash algorithm of the package</param> /// <returns>PackageItem object</returns> internal static PackageItem InstallPackage( string packageName, string version, NuGetRequest request, PackageSource source, string queryUrl, string packageHash, string packageHashAlgorithm ) { request.Debug(Messages.DebugInfoCallMethod, "NuGetClient", "InstallPackage"); //If the destination folder does not exists, create it string destinationPath = request.Destination; request.Verbose(string.Format(CultureInfo.InvariantCulture, "InstallPackage' - name='{0}', version='{1}',destination='{2}'", packageName, version, destinationPath)); string directoryToDeleteWhenFailed = string.Empty; bool needToDelete = false; string installFullPath = string.Empty; try { if (!Directory.Exists(destinationPath)) { Directory.CreateDirectory(destinationPath); // delete the destinationPath later on if we fail to install and if destinationPath did not exist before directoryToDeleteWhenFailed = destinationPath; } //Create a folder under the destination path to hold the package string installDir = FileUtility.MakePackageDirectoryName(request.ExcludeVersion.Value, destinationPath, packageName, version); if (!Directory.Exists(installDir)) { Directory.CreateDirectory(installDir); // if directoryToDeleteWhenFailed is null then the destinationPath already exists before so we should not delete it if (String.IsNullOrWhiteSpace(directoryToDeleteWhenFailed)) { directoryToDeleteWhenFailed = installDir; } } //Get the package file name based on the version and id string fileName = FileUtility.MakePackageFileName(request.ExcludeVersion.Value, packageName, version); installFullPath = Path.Combine(installDir, fileName); //download to fetch the package DownloadPackage(packageName, version, installFullPath, queryUrl, request); // check that we have the file if (!File.Exists(installFullPath)) { needToDelete = true; request.WriteError(ErrorCategory.ResourceUnavailable, installFullPath, Constants.Messages.PackageFailedInstallOrDownload, packageName, CultureInfo.CurrentCulture.TextInfo.ToLower(Constants.Install)); return null; } #region verify hash //we don't enable checking for hash here because it seems like nuget provider does not //checks that there is hash. Otherwise we don't carry out the install if (string.IsNullOrWhiteSpace(packageHash) || string.IsNullOrWhiteSpace(packageHashAlgorithm)) { // delete the file downloaded. VIRUS!!! needToDelete = true; request.WriteError(ErrorCategory.SecurityError, packageName, Constants.Messages.HashNotFound, packageName); return null; } // Verify the hash using (FileStream stream = File.OpenRead(installFullPath)) { HashAlgorithm hashAlgorithm = null; switch (packageHashAlgorithm.ToLowerInvariant()) { case "sha256": hashAlgorithm = SHA256.Create(); break; case "md5": hashAlgorithm = MD5.Create(); break; case "sha512": // Flows to default case // default to sha512 algorithm default: hashAlgorithm = SHA512.Create(); break; } if (hashAlgorithm == null) { // delete the file downloaded. VIRUS!!! needToDelete = true; request.WriteError(ErrorCategory.SecurityError, packageHashAlgorithm, Constants.Messages.HashNotSupported, packageHashAlgorithm); return null; } // compute the hash byte[] computedHash = hashAlgorithm.ComputeHash(stream); // convert the original hash we got from the feed byte[] downloadedHash = Convert.FromBase64String(packageHash); // if they are not equal, just issue out verbose because there is a current bug in backend // where editing the published module will result in a package with a different hash than the one // provided on the feed if (!Enumerable.SequenceEqual(computedHash, downloadedHash)) { // delete the file downloaded. VIRUS!!! request.Verbose(Constants.Messages.HashNotMatch, packageName); } //parse the package var pkgItem = InstallPackageLocal(packageName, version, request, source, installFullPath); return pkgItem; } #endregion } catch (Exception e) { request.Debug(e.Message); needToDelete = true; } finally { if (needToDelete) { // if the directory exists just delete it because it will contains the file as well if (!String.IsNullOrWhiteSpace(directoryToDeleteWhenFailed) && Directory.Exists(directoryToDeleteWhenFailed)) { try { FileUtility.DeleteDirectory(directoryToDeleteWhenFailed, true, isThrow: false); } catch { } } // if for some reason, we can't delete the directory or if we don't need to delete the directory // then we have to delete installFullPath if (File.Exists(installFullPath)) { FileUtility.DeleteFile(installFullPath, isThrow: false); } } } return null; }
/// <summary> /// Download a nuget package. /// </summary> /// <param name="packageName">Package name</param> /// <param name="version">Package version</param> /// <param name="destination">Destination location to store the downloaded package</param> /// <param name="queryUrl">Uri to query the package</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <param name="pkgSource">source to download the package</param> /// <param name="progressTracker">Utility class to help track progress</param> /// internal static bool DownloadPackage(string packageName, string version, string destination, string queryUrl, NuGetRequest request, PackageSource pkgSource, ProgressTracker progressTracker) { try { request.Verbose(string.Format(CultureInfo.InvariantCulture, "DownloadPackage' - name='{0}', version='{1}',destination='{2}', uri='{3}'", packageName, version, destination, queryUrl)); if (new Uri(queryUrl).IsFile) { throw new ArgumentException(Constants.Messages.UriSchemeNotSupported, queryUrl); } long result = 0; // Do not need to validate here again because the job is done by the httprepository that supplies the queryurl //Downloading the package //request.Verbose(httpquery); result = DownloadDataToFileAsync(destination, queryUrl, request, PathUtility.GetNetworkCredential(request.CredentialUsername, request.CredentialPassword), progressTracker).Result; if (result == 0 || !File.Exists(destination)) { request.Verbose(Messages.FailedDownloadPackage, packageName, queryUrl); request.Warning(Constants.Messages.SourceLocationNotValid, queryUrl); return false; } else { request.Verbose(Messages.CompletedDownload, packageName); return true; } } catch (Exception ex) { ex.Dump(request); request.Warning(Constants.Messages.PackageFailedInstallOrDownload, packageName, CultureInfo.CurrentCulture.TextInfo.ToLower(Constants.Download)); throw; } }