using System; using System.Collections.Generic; using System.IO; using System.Linq; using ICSharpCode.SharpZipLib.Zip; namespace Umbraco.Core.Packaging { internal class PackageExtraction : IPackageExtraction { public string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage) { string retVal = null; bool fileFound = false; string foundDir = null; ReadZipfileEntries(packageFilePath, (entry, stream) => { string fileName = Path.GetFileName(entry.Name); if (string.IsNullOrEmpty(fileName) == false && fileName.Equals(fileToRead, StringComparison.CurrentCultureIgnoreCase)) { foundDir = entry.Name.Substring(0, entry.Name.Length - fileName.Length); fileFound = true; using (var reader = new StreamReader(stream)) { retVal = reader.ReadToEnd(); return false; } } return true; }); if (fileFound == false) { directoryInPackage = null; throw new FileNotFoundException(string.Format("Could not find file in package {0}", packageFilePath), fileToRead); } directoryInPackage = foundDir; return retVal; } private static void CheckPackageExists(string packageFilePath) { if (string.IsNullOrEmpty(packageFilePath)) { throw new ArgumentNullException("packageFilePath"); } if (File.Exists(packageFilePath) == false) { if (File.Exists(packageFilePath) == false) throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath)); } string extension = Path.GetExtension(packageFilePath).ToLower(); var alowedExtension = new[] { ".umb", ".zip" }; // Check if the file is a valid package if (alowedExtension.All(ae => ae.Equals(extension) == false)) { throw new ArgumentException( string.Format("Error - file isn't a package. only extentions: \"{0}\" is allowed", string.Join(", ", alowedExtension))); } } public void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath) { CopyFilesFromArchive(packageFilePath, new[]{new KeyValuePair(fileInPackageName, destinationfilePath) } ); } public void CopyFilesFromArchive(string packageFilePath, IEnumerable> sourceDestination) { var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value); ReadZipfileEntries(packageFilePath, (entry, stream) => { string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower(); if (fileName == string.Empty) { return true; } string destination; if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination)) { using (var streamWriter = File.Open(destination, FileMode.Create)) { stream.CopyTo(streamWriter); } d.Remove(fileName); return d.Any(); } return true; }); if (d.Any()) { throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFilePath)); } } public IEnumerable FindMissingFiles(string packageFilePath, IEnumerable expectedFiles) { var retVal = expectedFiles.ToList(); ReadZipfileEntries(packageFilePath, (zipEntry, stream) => { string fileName = Path.GetFileName(zipEntry.Name); int index = retVal.FindIndex(f => f.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)); if (index != -1) { retVal.RemoveAt(index); } return retVal.Any(); }); return retVal; } public IEnumerable FindDubletFileNames(string packageFilePath) { var dictionary = new Dictionary>(); ReadZipfileEntries(packageFilePath, (entry, stream) => { string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower(); List list; if (dictionary.TryGetValue(fileName, out list) == false) { list = new List(); dictionary.Add(fileName, list); } list.Add(entry.Name); return true; }); return dictionary.Values.Where(v => v.Count > 1).SelectMany(v => v); } public IEnumerable ReadFilesFromArchive(string packageFilePath, IEnumerable filesToGet) { CheckPackageExists(packageFilePath); var files = new HashSet(filesToGet.Select(f => f.ToLower())); using (var fs = File.OpenRead(packageFilePath)) { using (var zipInputStream = new ZipInputStream(fs)) { ZipEntry zipEntry; while ((zipEntry = zipInputStream.GetNextEntry()) != null) { if (zipEntry.IsDirectory) continue; if (files.Contains(zipEntry.Name)) { using (var memStream = new MemoryStream()) { zipInputStream.CopyTo(memStream); yield return memStream.ToArray(); memStream.Close(); } } } zipInputStream.Close(); } fs.Close(); } } private void ReadZipfileEntries(string packageFilePath, Func entryFunc, bool skipsDirectories = true) { CheckPackageExists(packageFilePath); using (var fs = File.OpenRead(packageFilePath)) { using (var zipInputStream = new ZipInputStream(fs)) { ZipEntry zipEntry; while ((zipEntry = zipInputStream.GetNextEntry()) != null) { if (zipEntry.IsDirectory && skipsDirectories) continue; if (entryFunc(zipEntry, zipInputStream) == false) break; } zipInputStream.Close(); } fs.Close(); } } } }