| File: PackageFolderReader.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Packaging\NuGet.Packaging.csproj (NuGet.Packaging) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using NuGet.Common; using NuGet.Frameworks; using NuGet.Packaging.Core; using NuGet.Packaging.Signing; namespace NuGet.Packaging { /// <summary> /// Reads an unzipped nupkg folder. /// </summary> public class PackageFolderReader : PackageReaderBase { private readonly DirectoryInfo _root; /// <summary> /// Package folder reader /// </summary> public PackageFolderReader(string folderPath) : this(folderPath, DefaultFrameworkNameProvider.Instance, DefaultCompatibilityProvider.Instance) { } /// <summary> /// Package folder reader /// </summary> /// <param name="folder">root directory of an extracted nupkg</param> public PackageFolderReader(DirectoryInfo folder) : this(folder, DefaultFrameworkNameProvider.Instance, DefaultCompatibilityProvider.Instance) { } /// <summary> /// Package folder reader /// </summary> /// <param name="folderPath">root directory of an extracted nupkg</param> /// <param name="frameworkProvider">framework mappings</param> /// <param name="compatibilityProvider">framework compatibility provider</param> public PackageFolderReader(string folderPath, IFrameworkNameProvider frameworkProvider, IFrameworkCompatibilityProvider compatibilityProvider) : this(new DirectoryInfo(folderPath), frameworkProvider, compatibilityProvider) { } /// <summary> /// Package folder reader /// </summary> /// <param name="folder">root directory of an extracted nupkg</param> /// <param name="frameworkProvider">framework mappings</param> /// <param name="compatibilityProvider">framework compatibility provider</param> public PackageFolderReader(DirectoryInfo folder, IFrameworkNameProvider frameworkProvider, IFrameworkCompatibilityProvider compatibilityProvider) : base(frameworkProvider, compatibilityProvider) { _root = folder; } public override string GetNuspecFile() { // This needs to be explicitly case insensitive in order to work on XPlat, since GetFiles is normally case sensitive on non-Windows var nuspecFiles = _root.GetFiles("*.*", SearchOption.TopDirectoryOnly).Where(f => f.Name.EndsWith(".nuspec", StringComparison.OrdinalIgnoreCase)).ToArray(); if (nuspecFiles.Length == 0) { var message = new StringBuilder(); message.Append(Strings.Error_MissingNuspecFile); message.AppendFormat(CultureInfo.CurrentCulture, Strings.Message_Path, _root.FullName); throw new PackagingException(NuGetLogCode.NU5037, message.ToString()); } else if (nuspecFiles.Length > 1) { throw new PackagingException(Strings.MultipleNuspecFiles); } return nuspecFiles[0].FullName; } /// <summary> /// Opens a local file in read only mode. /// </summary> public override Stream GetStream(string path) { return GetFile(path).OpenRead(); } private FileInfo GetFile(string path) { var file = new FileInfo(Path.Combine(_root.FullName, path)); if (!file.FullName.StartsWith(_root.FullName, StringComparison.OrdinalIgnoreCase)) { // the given path does not appear under the folder root throw new FileNotFoundException(path); } return file; } public override IEnumerable<string> GetFiles() { // Read all files starting at the root. return GetFiles(folder: string.Empty); } public override IEnumerable<string> GetFiles(string folder) { // Default to retrieve files and throwing if the root // directory is not found. var getFiles = true; var searchFolder = new DirectoryInfo(_root.FullName); if (!string.IsNullOrEmpty(folder)) { // Search in the sub folder if one was specified searchFolder = new DirectoryInfo(Path.Combine(_root.FullName, folder)); // For sub folders verify it exists // The root is expected to exist and should throw if it does not getFiles = searchFolder.Exists; } if (getFiles) { // Enumerate root folder filtering out nupkg files foreach (var file in searchFolder.GetFiles("*", SearchOption.AllDirectories)) { var path = GetRelativePath(_root, file); // disallow nupkgs in the root if (!IsFileInRoot(path) || !IsNupkg(path)) { yield return path; } } } yield break; } /// <summary> /// True if the path does not contain / /// </summary> private static bool IsFileInRoot(string path) { #if NETCOREAPP return path.IndexOf('/', StringComparison.Ordinal) == -1; #else return path.IndexOf('/') == -1; #endif } /// <summary> /// True if the path ends with .nupkg /// </summary> private static bool IsNupkg(string path) { return path.EndsWith(PackagingCoreConstants.NupkgExtension, StringComparison.OrdinalIgnoreCase) == true; } /// <summary> /// Build the relative path in the same format that ZipArchive uses /// </summary> private static string GetRelativePath(DirectoryInfo root, FileInfo file) { var parents = new Stack<DirectoryInfo>(); var parent = file.Directory; while (parent != null && !StringComparer.OrdinalIgnoreCase.Equals(parent.FullName, root.FullName)) { parents.Push(parent); parent = parent.Parent; } if (parent == null) { // the given file path does not appear under root throw new FileNotFoundException(file.FullName); } var parts = parents.Select(d => d.Name).Concat(new string[] { file.Name }); return string.Join("/", parts); } public override IEnumerable<string> CopyFiles( string destination, IEnumerable<string> packageFiles, ExtractPackageFileDelegate extractFile, ILogger logger, CancellationToken token) { var filesCopied = new List<string>(); foreach (var packageFile in packageFiles) { token.ThrowIfCancellationRequested(); var sourceFile = GetFile(packageFile); var targetPath = Path.Combine(destination, packageFile); Directory.CreateDirectory(Path.GetDirectoryName(targetPath)!); using (var fileStream = sourceFile.OpenRead()) { targetPath = extractFile(sourceFile.FullName, targetPath, fileStream); if (targetPath != null) { ZipArchiveExtensions.UpdateFileTime(targetPath, sourceFile.LastWriteTimeUtc); filesCopied.Add(targetPath); } } } return filesCopied; } protected override void Dispose(bool disposing) { // do nothing here } public override Task<PrimarySignature?> GetPrimarySignatureAsync(CancellationToken token) { return TaskResult.Null<PrimarySignature>(); } public override Task<bool> IsSignedAsync(CancellationToken token) { return TaskResult.False; } public override Task ValidateIntegrityAsync(SignatureContent signatureContent, CancellationToken token) { throw new NotImplementedException(); } public override Task<byte[]> GetArchiveHashAsync(HashAlgorithmName hashAlgorithm, CancellationToken token) { throw new NotImplementedException(); } public override bool CanVerifySignedPackages(SignedPackageVerifierSettings verifierSettings) { return false; } public override string GetContentHash(CancellationToken token, Func<string>? GetUnsignedPackageHash = null) { throw new NotImplementedException(); } } }