/// <summary> /// Returns the validated uri. Returns null if we cannot validate it /// </summary> /// <param name="query"></param> /// <param name="request"></param> /// <returns></returns> internal static Uri ValidateUri(Uri query, NuGetRequest request) { var client = request.ClientWithoutAcceptHeader; var response = PathUtility.GetHttpResponse(client, query.AbsoluteUri, (() => request.IsCanceled), ((msg, num) => request.Verbose(Resources.Messages.RetryingDownload, msg, num)), (msg) => request.Verbose(msg), (msg) => request.Debug(msg)); if (response == null) { return null; } // if response is not success, we need to check for redirection if (!response.IsSuccessStatusCode) { // Check for redirection (http status code 3xx) if (response.StatusCode == HttpStatusCode.MultipleChoices || response.StatusCode == HttpStatusCode.MovedPermanently || response.StatusCode == HttpStatusCode.Found || response.StatusCode == HttpStatusCode.SeeOther || response.StatusCode == HttpStatusCode.TemporaryRedirect) { // get the redirected direction string location = response.Headers.GetValues("Location").FirstOrDefault(); if (String.IsNullOrWhiteSpace(location)) { return null; } // make a new query based on location query = new Uri(location); } else { // other status code is wrong return null; } } else { query = new Uri(response.RequestMessage.RequestUri.AbsoluteUri); } //Making a query like: www.nuget.org/api/v2/FindPackagesById()?id='FoooBarr' to check the server available. //'FoooBarr' is an any random package id string queryUri = "FoooBarr".MakeFindPackageByIdQuery(PathUtility.UriCombine(query.AbsoluteUri, NuGetConstant.FindPackagesById)); response = PathUtility.GetHttpResponse(client, queryUri, (() => request.IsCanceled), ((msg, num) => request.Verbose(Resources.Messages.RetryingDownload, msg, num)), (msg) => request.Verbose(msg), (msg) => request.Debug(msg)); // The link is not valid if (response == null || !response.IsSuccessStatusCode) { return null; } return query; }
/// <summary> /// Find the package via the given uri query. /// </summary> /// <param name="query">A full Uri. A sample Uri looks like "http://www.nuget.org/api/v2/FindPackagesById()?id='Jquery'" </param> /// <param name="request">An object passed in from the PackageManagement that contains functions that can be used to interact with its Provider</param> /// <returns>Package objects</returns> internal static IEnumerable<PackageBase> FindPackage(string query, NuGetRequest request) { request.Debug(Messages.DebugInfoCallMethod, "NuGetClient", "FindPackage"); request.Verbose(Messages.SearchingRepository, query, ""); return HttpClientPackageRepository.SendRequest(query, request); }
/// <summary> /// This is called when the user is adding (or updating) a package source /// /// If this PROVIDER doesn't support user-defined package sources, remove this method. /// </summary> /// <param name="name">The name of the package source. If this parameter is null or empty the PROVIDER should use the location as the name (if the PROVIDER actually stores names of package sources)</param> /// <param name="location">The location (ie, directory, URL, etc) of the package source. If this is null or empty, the PROVIDER should use the name as the location (if valid)</param> /// <param name="trusted">A boolean indicating that the user trusts this package source. Packages returned from this source should be marked as 'trusted'</param> /// <param name="request">An object passed in from the PackageManagement that contains functions that can be used to interact with its Provider</param> public void AddPackageSource(string name, string location, bool trusted, NuGetRequest request) { if (request == null){ throw new ArgumentNullException("request"); } try { request.Debug(string.Format(CultureInfo.InvariantCulture, "AddPackageSource - ProvidenName = '{0}', name='{1}', location='{2}', trusted='{3}'", PackageProviderName, name, location, trusted)); // Error out if a user does not provide package source Name if (string.IsNullOrWhiteSpace(name)) { request.WriteError(ErrorCategory.InvalidArgument, Constants.Parameters.Name, Constants.Messages.MissingRequiredParameter, Constants.Parameters.Name); return; } if (string.IsNullOrWhiteSpace(location)) { request.WriteError(ErrorCategory.InvalidArgument, Constants.Parameters.Location, Constants.Messages.MissingRequiredParameter, Constants.Parameters.Location); return; } request.Debug(Resources.Messages.DebugInfoCallMethod, PackageProviderName, "GetOptionValue"); // if this is supposed to be an update, there will be a dynamic parameter set for IsUpdatePackageSource var isUpdate = request.GetOptionValue(Constants.Parameters.IsUpdate).IsTrue(); request.Debug(Resources.Messages.VariableCheck, "IsUpdate", isUpdate); // if your source supports credentials you get get them too: // string username =request.Username; // SecureString password = request.Password; // feel free to send back an error here if your provider requires credentials for package sources. // check first that we're not clobbering an existing source, unless this is an update request.Debug(Resources.Messages.DebugInfoCallMethod, PackageProviderName, string.Format(CultureInfo.InvariantCulture, "FindRegisteredSource -name'{0}'", name)); var src = request.FindRegisteredSource(name); if (src != null && !isUpdate) { // tell the user that there's one here already request.WriteError(ErrorCategory.InvalidArgument, name, Constants.Messages.PackageSourceExists, name); return; } // conversely, if it didn't find one, and it is an update, that's bad too: if (src == null && isUpdate) { // you can't find that package source? Tell that to the user request.WriteError(ErrorCategory.ObjectNotFound, name, Constants.Messages.UnableToResolveSource, name); return; } // ok, we know that we're ok to save this source // next we check if the location is valid (if we support that kind of thing) var validated = false; if (!request.SkipValidate.Value) { // the user has not opted to skip validating the package source location, so check if the source is valid validated = request.ValidateSourceLocation(location); if (!validated) { request.WriteError(ErrorCategory.InvalidData, name, Constants.Messages.SourceLocationNotValid, location); return; } request.Verbose(Resources.Messages.SuccessfullyValidated, name); } // it's good to check just before you actually write something to see if the user has cancelled the operation if (request.IsCanceled) { return; } // looking good -- store the package source. request.AddPackageSource(name, location, trusted, validated); // Yield the package source back to the caller. request.YieldPackageSource(name, location, trusted, true /*since we just registered it*/, validated); } catch (Exception e) { e.Dump(request); } }
/// <summary> /// This is called when the user is adding (or updating) a package source /// If this PROVIDER doesn't support user-defined package sources, remove this method. /// </summary> /// <param name="name"> /// The name of the package source. If this parameter is null or empty the PROVIDER should use the /// location as the name (if the PROVIDER actually stores names of package sources) /// </param> /// <param name="location"> /// The location (ie, directory, URL, etc) of the package source. If this is null or empty, the /// PROVIDER should use the name as the location (if valid) /// </param> /// <param name="trusted"> /// A boolean indicating that the user trusts this package source. Packages returned from this source /// should be marked as 'trusted' /// </param> /// <param name="request"> /// An object passed in from the CORE that contains functions that can be used to interact with the /// CORE and HOST /// </param> public void AddPackageSource(string name, string location, bool trusted, NuGetRequest request) { // Nice-to-have put a debug message in that tells what's going on. request.Debug("Calling '{0}::AddPackageSource' '{1}','{2}','{3}'", PackageProviderName, name, location, trusted); // if they didn't pass in a name, use the location as a name. (if you support that kind of thing) name = string.IsNullOrEmpty(name) ? location : name; // let's make sure that they've given us everything we need. if (string.IsNullOrEmpty(name)) { request.Error(ErrorCategory.InvalidArgument, Constants.Parameters.Name, Constants.Messages.MissingRequiredParameter, Constants.Parameters.Name); // we're done here. return; } if (string.IsNullOrEmpty(location)) { request.Error(ErrorCategory.InvalidArgument, Constants.Parameters.Location, Constants.Messages.MissingRequiredParameter, Constants.Parameters.Location); // we're done here. return; } // if this is supposed to be an update, there will be a dynamic parameter set for IsUpdatePackageSource var isUpdate = request.GetOptionValue(Constants.Parameters.IsUpdate).IsTrue(); // if your source supports credentials you get get them too: // string username =request.Username; // SecureString password = request.Password; // feel free to send back an error here if your provider requires credentials for package sources. // check first that we're not clobbering an existing source, unless this is an update var src = request.FindRegisteredSource(name); if (src != null && !isUpdate) { // tell the user that there's one here already request.Error(ErrorCategory.InvalidArgument, name ?? location, Constants.Messages.PackageSourceExists, name ?? location); // we're done here. return; } // conversely, if it didn't find one, and it is an update, that's bad too: if (src == null && isUpdate) { // you can't find that package source? Tell that to the user request.Error(ErrorCategory.ObjectNotFound, name ?? location, Constants.Messages.UnableToResolveSource, name ?? location); // we're done here. return; } // ok, we know that we're ok to save this source // next we check if the location is valid (if we support that kind of thing) var validated = false; if (!request.SkipValidate) { // the user has not opted to skip validating the package source location, so check that it's valid (talk to the url, or check if it's a valid directory, etc) // todo: insert code to check if the source is valid validated = request.ValidateSourceLocation(location); if (!validated) { request.Error(ErrorCategory.InvalidData, name ?? location, Constants.Messages.SourceLocationNotValid, location); // we're done here. return; } // we passed validation! } // it's good to check just before you actaully write something to see if the user has cancelled the operation if (request.IsCanceled) { return; } // looking good -- store the package source // todo: create the package source (and store it whereever you store it) request.Verbose("Storing package source {0}", name); // actually yielded by the implementation. request.AddPackageSource(name, location, trusted, validated); // and, before you go, Yield the package source back to the caller. if (!request.YieldPackageSource(name, location, trusted, true /*since we just registered it*/, validated)) { // always check the return value of a yield, since if it returns false, you don't keep returning data // this can happen if they have cancelled the operation. return; } // all done! }
/// <summary> /// Download data from remote via uri query. /// </summary> /// <param name="fileName">A file to store the downloaded data.</param> /// <param name="query">Uri query</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <returns></returns> internal static async Task<long> DownloadDataToFileAsync(string fileName, string query, NuGetRequest request) { request.Verbose(Messages.DownloadingPackage, query); var client = GetHttpClient(request); var response = PathUtility.GetHttpResponse(client, query, request); // Check that response was successful or write error if (response == null || !response.IsSuccessStatusCode) { request.Warning(Resources.Messages.CouldNotGetResponseFromQuery, query); return 0; } // Check that response was successful or throw exception response.EnsureSuccessStatusCode(); // Read response asynchronously and write out a file // The return value is for the caller to wait for the async operation to complete. var fileLength = await response.Content.ReadAsFileAsync(fileName); request.Verbose(Messages.CompletedDownload, query); return fileLength; }
/// <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> internal static void DownloadPackage(string packageName, string version, string destination, string queryUrl, NuGetRequest request) { 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).Result; if (result == 0 || !File.Exists(destination)) { request.Verbose(Messages.FailedDownloadPackage, packageName, queryUrl); request.Warning(Constants.Messages.SourceLocationNotValid, queryUrl); } else { request.Verbose(Messages.CompletedDownload, packageName); } } catch (Exception ex) { ex.Dump(request); request.Warning(Constants.Messages.PackageFailedInstallOrDownload, packageName, CultureInfo.CurrentCulture.TextInfo.ToLower(Constants.Download)); throw; } }
/// <summary> /// Perform package uninstallation. /// </summary> /// <param name="request">Object given by the PackageManagement platform</param> /// <param name="pkg">PackageItem object</param> internal static void UninstallPackage(NuGetRequest request, PackageItem pkg) { request.Debug(Messages.DebugInfoCallMethod, "NuGetClient", "UninstallPackage"); if (pkg == null) { throw new ArgumentNullException(paramName: "pkg"); } var dir = pkg.InstalledDirectory; if (String.IsNullOrWhiteSpace(dir) || !Directory.Exists(dir)) { return; } FileUtility.DeleteDirectory(pkg.InstalledDirectory, recursive:true, isThrow:false); //Inform a user which package is deleted via the packageManagement platform request.Verbose(Messages.UninstalledPackage, "NuGetClient", pkg.Id); request.YieldPackage(pkg, pkg.Id); }
/// <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> /// Returns the package dependencies of packageItem. We only return the dependencies that are not installed in the destination folder of request /// </summary> /// <param name="packageItem"></param> /// <param name="request"></param> private static IEnumerable<PackageItem> GetPackageDependenciesHelper(PackageItem packageItem, NuGetRequest request) { if (packageItem.Package.DependencySetList == null) { yield break; } foreach (var depSet in packageItem.Package.DependencySetList) { if (depSet.Dependencies == null) { continue; } foreach (var dep in depSet.Dependencies) { // Get the min dependencies version string minVersion = dep.DependencyVersion.MinVersion.ToStringSafe(); // Get the max dependencies version string maxVersion = dep.DependencyVersion.MaxVersion.ToStringSafe(); // check whether it is already installed at the destination if (request.GetInstalledPackages(dep.Id, null, minVersion, maxVersion, minInclusive: dep.DependencyVersion.IsMinInclusive, maxInclusive: dep.DependencyVersion.IsMaxInclusive, terminateFirstFound: true)) { request.Verbose(String.Format(CultureInfo.CurrentCulture, Messages.AlreadyInstalled, dep.Id)); // already have a dependency so move on continue; } // get all the packages that match this dependency var dependentPackageItem = request.GetPackageById(dep.Id, request, minimumVersion: minVersion, maximumVersion: maxVersion, minInclusive: dep.DependencyVersion.IsMinInclusive, maxInclusive: dep.DependencyVersion.IsMaxInclusive).ToArray(); if (dependentPackageItem.Length == 0) { request.WriteError(ErrorCategory.ObjectNotFound, dep.Id, Constants.Messages.UnableToFindDependencyPackage, dep.Id); break; } // Get the package that is the latest version yield return dependentPackageItem.OrderByDescending(each => each.Version).FirstOrDefault(); } } }
/// <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 single package to destination without checking for dependencies /// </summary> /// <param name="pkgItem"></param> /// <param name="request"></param> /// <param name="destLocation"></param> /// <returns></returns> internal static bool DownloadSinglePackage(PackageItem pkgItem, NuGetRequest request, string destLocation) { if (string.IsNullOrWhiteSpace(pkgItem.PackageFilename) || pkgItem.PackageSource == null || pkgItem.PackageSource.Location == null || (pkgItem.PackageSource.IsSourceAFile && pkgItem.Package == null)) { request.WriteError(ErrorCategory.ObjectNotFound, pkgItem.Id, Constants.Messages.UnableToResolvePackage, pkgItem.Id); return false; } // this is if the user says -force bool force = request.GetOptionValue("Force") != null; // combine the path and the file name destLocation = Path.Combine(destLocation, pkgItem.PackageFilename); // if the file already exists if (File.Exists(destLocation)) { // if no force, just return if (!force) { request.Verbose(Constants.Messages.SkippedDownloadedPackage, pkgItem.Id); request.YieldPackage(pkgItem, pkgItem.PackageSource.Name); return true; } // here we know it is forced, so delete FileUtility.DeleteFile(destLocation, isThrow: false); // if after we try delete, it is still there, tells the user we can't perform the action if (File.Exists(destLocation)) { request.WriteError(ErrorCategory.ResourceUnavailable, destLocation, Constants.Messages.UnableToOverwriteExistingFile, destLocation); return false; } } try { if (pkgItem.PackageSource.Repository.IsFile) { using (var input = File.OpenRead(pkgItem.Package.FullFilePath)) { using (var output = new FileStream(destLocation, FileMode.Create, FileAccess.Write, FileShare.Read)) { input.CopyTo(output); } } } else { //V2 download package protocol: //sample url: http://www.nuget.org/api/v2/package/jQuery/2.1.3 string append = String.Format(CultureInfo.InvariantCulture, "/package/{0}/{1}", pkgItem.Id, pkgItem.Version); string httpquery = PathUtility.UriCombine(pkgItem.PackageSource.Repository.Source, append); NuGetClient.DownloadPackage(pkgItem.Id, pkgItem.Version, destLocation, string.IsNullOrWhiteSpace(pkgItem.Package.ContentSrcUrl) ? httpquery : pkgItem.Package.ContentSrcUrl, request); } } catch (Exception ex) { ex.Dump(request); return false; } request.Verbose(Resources.Messages.SuccessfullyDownloaded, pkgItem.Id); request.YieldPackage(pkgItem, pkgItem.PackageSource.Name); return true; }
/// <summary> /// Download data from remote via uri query. /// </summary> /// <param name="fileName">A file to store the downloaded data.</param> /// <param name="query">Uri query</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="networkCredential">Credential to pass along to get httpclient</param> /// <param name="progressTracker">Utility class to help track progress</param> /// <returns></returns> internal static async Task<long> DownloadDataToFileAsync(string fileName, string query, NuGetRequest request, NetworkCredential networkCredential, ProgressTracker progressTracker) { request.Verbose(Messages.DownloadingPackage, query); var httpClient = request.Client; // try downloading for 3 times int remainingTry = 3; long totalDownloaded = 0; long totalBytesToReceive = 0; bool cleanUp = false; CancellationTokenSource cts = new CancellationTokenSource(); ; Stream input = null; Timer timer = null; FileStream output = null; object lockObject = new object(); // function to perform cleanup Action cleanUpAction = () => { lock (lockObject) { // if clean up is done before, don't need to do again if (!cleanUp) { try { // dispose timer if (timer != null) { timer.Change(Timeout.Infinite, Timeout.Infinite); timer.Dispose(); } // dispose cts token if (cts != null) { cts.Cancel(); cts.Dispose(); } } catch { } try { // dispose input and output stream if (input != null) { input.Dispose(); } // it is important that we dispose of the output here, otherwise we may not be able to delete the file if (output != null) { output.Dispose(); } // if the download didn't complete, log verbose message if (totalBytesToReceive != totalDownloaded) { request.Verbose(string.Format(Resources.Messages.IncompleteDownload, totalDownloaded, totalBytesToReceive)); } // if we couldn't download anything if (totalDownloaded == 0 && File.Exists(fileName)) { File.Delete(fileName); } } catch { } cleanUp = true; } } }; while (remainingTry > 0) { // if user cancel the request, no need to do anything if (request.IsCanceled) { break; } input = null; output = null; totalDownloaded = 0; try { // decrease try by 1 remainingTry -= 1; // create new timer and cancellation token source lock (lockObject) { // check every second to see whether request is cancelled timer = new Timer(_ => { if (request.IsCanceled) { cleanUpAction(); } }, null, 500, 1000); cts = new CancellationTokenSource(); cleanUp = false; } var response = await httpClient.GetAsync(query, HttpCompletionOption.ResponseHeadersRead, cts.Token); if (response.Content != null && response.Content.Headers != null) { totalBytesToReceive = response.Content.Headers.ContentLength ?? 0; // the total amount of bytes we need to download in megabytes double totalBytesToReceiveMB = (totalBytesToReceive / 1024f) / 1024f; // Read response asynchronously and write out a file // The return value is for the caller to wait for the async operation to complete. input = await response.Content.ReadAsStreamAsync(); // buffer size of 64 KB, this seems to be preferable buffer size, not too small and not too big byte[] bytes = new byte[1024 * 64]; output = File.Open(fileName, FileMode.OpenOrCreate); int current = 0; double lastPercent = 0; // here we read content that we got from the http response stream into the bytes array current = await input.ReadAsync(bytes, 0, bytes.Length, cts.Token); // report initial progress request.Progress(progressTracker.ProgressID, progressTracker.StartPercent, string.Format(CultureInfo.CurrentCulture, Resources.Messages.DownloadingProgress, 0, (totalBytesToReceive / 1024f) / 1024f)); while (current > 0) { totalDownloaded += current; // here we write the bytes array content into the file await output.WriteAsync(bytes, 0, current, cts.Token); double percent = totalDownloaded * 1.0 / totalBytesToReceive; // don't want to report too often (slow down performance) if (percent > lastPercent + 0.1) { lastPercent = percent; // percent between startProgress and endProgress var progressPercent = progressTracker.ConvertPercentToProgress(percent); // report the progress request.Progress(progressTracker.ProgressID, (int)progressPercent, string.Format(CultureInfo.CurrentCulture, Resources.Messages.DownloadingProgress, (totalDownloaded / 1024f) / 1024f, totalBytesToReceiveMB)); } // here we read content that we got from the http response stream into the bytes array current = await input.ReadAsync(bytes, 0, bytes.Length, cts.Token); } // check that we download everything if (totalDownloaded == totalBytesToReceive) { // report that we finished with the download request.Progress(progressTracker.ProgressID, progressTracker.EndPercent, string.Format(CultureInfo.CurrentCulture, Resources.Messages.DownloadingProgress, totalDownloaded, totalBytesToReceive)); request.Verbose(Messages.CompletedDownload, query); break; } // otherwise, we have to retry again } // if request is canceled, don't retry if (request.IsCanceled) { break; } request.Verbose(Resources.Messages.RetryingDownload, query, remainingTry); } catch (Exception ex) { request.Verbose(ex.Message); request.Debug(ex.StackTrace); // if there is exception, we will retry too request.Verbose(Resources.Messages.RetryingDownload, query, remainingTry); } finally { cleanUpAction(); } } return totalDownloaded; }
/// <summary> /// Send an initial request to download data from the server. /// From the initial request, we may change the host of subsequent calls (if a redirection happens in this initial request) /// Also, if the initial request sends us less data than the amount we request, then we do not /// need to issue more requests /// </summary> /// <param name="query"></param> /// <param name="startPoint"></param> /// <param name="bufferSize"></param> /// <param name="request"></param> /// <returns></returns> internal static Stream InitialDownloadDataToStream(UriBuilder query, int startPoint, int bufferSize, NuGetRequest request) { var uri = String.Format(CultureInfo.CurrentCulture, query.Uri.ToString(), startPoint, bufferSize); request.Debug(Messages.DownloadingPackage, uri); var client = request.Client; var response = PathUtility.GetHttpResponse(client, uri, (() => request.IsCanceled), ((msg, num) => request.Verbose(Resources.Messages.RetryingDownload, msg, num)), (msg) => request.Verbose(msg), (msg) => request.Debug(msg)); // Check that response was successful or write error if (response == null || !response.IsSuccessStatusCode) { request.Warning(Resources.Messages.CouldNotGetResponseFromQuery, uri); return null; } // Read response and write out a stream var stream = GetStreamBasedOnEncoding(response); request.Debug(Messages.CompletedDownload, uri); // If the host from the response is different, change the host of the original query if (!String.Equals(response.RequestMessage.RequestUri.Host, query.Host, StringComparison.OrdinalIgnoreCase)) { query.Host = response.RequestMessage.RequestUri.Host; } return stream; }
/// <summary> /// Download data from remote via uri query. /// </summary> /// <param name="query">Uri query</param> /// <param name="request">An object passed in from the PackageManagement platform that contains APIs that can be used to interact with it </param> /// <returns></returns> internal static Stream DownloadDataToStream(string query, NuGetRequest request) { request.Debug(Messages.DownloadingPackage, query); var client = request.Client; var response = PathUtility.GetHttpResponse(client, query, (()=>request.IsCanceled), ((msg, num) => request.Verbose(Resources.Messages.RetryingDownload, msg, num)), (msg)=>request.Verbose(msg), (msg)=>request.Debug(msg)); // Check that response was successful or throw exception if (response == null || !response.IsSuccessStatusCode) { request.Warning(Resources.Messages.CouldNotGetResponseFromQuery, query); return null; } // Read response and write out a stream var stream = GetStreamBasedOnEncoding(response); request.Debug(Messages.CompletedDownload, query); return stream; }
/// <summary> /// Returns the package dependencies of packageItem. We only return the dependencies that are not installed in the destination folder of request /// </summary> /// <param name="packageItem"></param> /// <param name="processedDependencies"></param> /// <param name="request"></param> private static IEnumerable<PackageItem> GetPackageDependenciesHelper(PackageItem packageItem, HashSet<string> processedDependencies, NuGetRequest request) { if (packageItem.Package.DependencySetList == null) { yield break; } bool force = request.GetOptionValue("Force") != null; foreach (var depSet in packageItem.Package.DependencySetList) { if (depSet.Dependencies == null) { continue; } foreach (var dep in depSet.Dependencies) { var depKey = string.Format(CultureInfo.InvariantCulture, "{0}!#!{1}", dep.Id, dep.DependencyVersion.ToStringSafe()); if (processedDependencies.Contains(depKey)) { continue; } // Get the min dependencies version string minVersion = dep.DependencyVersion.MinVersion.ToStringSafe(); // Get the max dependencies version string maxVersion = dep.DependencyVersion.MaxVersion.ToStringSafe(); if (!force) { bool installed = false; var installedPackages = request.InstalledPackages.Value; if (request.InstalledPackages.Value.Count() > 0) { // check the installedpackages options passed in foreach (var installedPackage in request.InstalledPackages.Value) { // if name not match, move on to the next entry if (!string.Equals(installedPackage.Id, dep.Id, StringComparison.OrdinalIgnoreCase)) { continue; } // if no version and if name matches, skip if (string.IsNullOrWhiteSpace(installedPackage.Version)) { // skip this dependency installed = true; break; } SemanticVersion packageVersion = new SemanticVersion(installedPackage.Version); // checks min and max if (request.MinAndMaxVersionMatched(packageVersion, minVersion, maxVersion, dep.DependencyVersion.IsMinInclusive, dep.DependencyVersion.IsMaxInclusive)) { // skip this dependency installed = true; break; } } } // check whether package is installed at destination. only used this option if installedpackages not passed in else if (request.GetInstalledPackages(dep.Id, null, minVersion, maxVersion, minInclusive: dep.DependencyVersion.IsMinInclusive, maxInclusive: dep.DependencyVersion.IsMaxInclusive, terminateFirstFound: true)) { installed = true; } if (installed) { // already processed this so don't need to do this next time processedDependencies.Add(dep.Id); request.Verbose(String.Format(CultureInfo.CurrentCulture, Messages.AlreadyInstalled, dep.Id)); // already have a dependency so move on continue; } } // get all the packages that match this dependency var dependentPackageItem = request.GetPackageById(dep.Id, request, minimumVersion: minVersion, maximumVersion: maxVersion, minInclusive: dep.DependencyVersion.IsMinInclusive, maxInclusive: dep.DependencyVersion.IsMaxInclusive).ToArray(); if (dependentPackageItem.Length == 0) { request.WriteError(ErrorCategory.ObjectNotFound, dep.Id, Constants.Messages.UnableToFindDependencyPackage, dep.Id); break; } // Get the package that is the latest version yield return dependentPackageItem.OrderByDescending(each => each.Version).FirstOrDefault(); processedDependencies.Add(depKey); } } }