| // ZipFile.Check.cs | |
| // ------------------------------------------------------------------ | |
| // | |
| // Copyright (c) 2009-2011 Dino Chiesa. | |
| // All rights reserved. | |
| // | |
| // This code module is part of DotNetZip, a zipfile class library. | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This code is licensed under the Microsoft Public License. | |
| // See the file License.txt for the license details. | |
| // More info on: http://dotnetzip.codeplex.com | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // last saved (in emacs): | |
| // Time-stamp: <2011-July-31 14:40:50> | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This module defines the methods for doing Checks on zip files. | |
| // These are not necessary to include in the Reduced or CF | |
| // version of the library. | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| using System; | |
| using System.IO; | |
| using System.Collections.Generic; | |
| namespace OfficeOpenXml.Packaging.Ionic.Zip | |
| { | |
| internal partial class ZipFile | |
| { | |
| /// <summary> | |
| /// Checks a zip file to see if its directory is consistent. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// In cases of data error, the directory within a zip file can get out | |
| /// of synch with the entries in the zip file. This method checks the | |
| /// given zip file and returns true if this has occurred. | |
| /// </para> | |
| /// | |
| /// <para> This method may take a long time to run for large zip files. </para> | |
| /// | |
| /// <para> | |
| /// This method is not supported in the Reduced or Compact Framework | |
| /// versions of DotNetZip. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Developers using COM can use the <see | |
| /// cref="ComHelper.CheckZip(String)">ComHelper.CheckZip(String)</see> | |
| /// method. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <param name="zipFileName">The filename to of the zip file to check.</param> | |
| /// | |
| /// <returns>true if the named zip file checks OK. Otherwise, false. </returns> | |
| /// | |
| /// <seealso cref="FixZipDirectory(string)"/> | |
| /// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/> | |
| public static bool CheckZip(string zipFileName) | |
| { | |
| return CheckZip(zipFileName, false, null); | |
| } | |
| /// <summary> | |
| /// Checks a zip file to see if its directory is consistent, | |
| /// and optionally fixes the directory if necessary. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// In cases of data error, the directory within a zip file can get out of | |
| /// synch with the entries in the zip file. This method checks the given | |
| /// zip file, and returns true if this has occurred. It also optionally | |
| /// fixes the zipfile, saving the fixed copy in <em>Name</em>_Fixed.zip. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// This method may take a long time to run for large zip files. It | |
| /// will take even longer if the file actually needs to be fixed, and if | |
| /// <c>fixIfNecessary</c> is true. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// This method is not supported in the Reduced or Compact | |
| /// Framework versions of DotNetZip. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <param name="zipFileName">The filename to of the zip file to check.</param> | |
| /// | |
| /// <param name="fixIfNecessary">If true, the method will fix the zip file if | |
| /// necessary.</param> | |
| /// | |
| /// <param name="writer"> | |
| /// a TextWriter in which messages generated while checking will be written. | |
| /// </param> | |
| /// | |
| /// <returns>true if the named zip is OK; false if the file needs to be fixed.</returns> | |
| /// | |
| /// <seealso cref="CheckZip(string)"/> | |
| /// <seealso cref="FixZipDirectory(string)"/> | |
| public static bool CheckZip(string zipFileName, bool fixIfNecessary, | |
| TextWriter writer) | |
| { | |
| ZipFile zip1 = null, zip2 = null; | |
| bool isOk = true; | |
| try | |
| { | |
| zip1 = new ZipFile(); | |
| zip1.FullScan = true; | |
| zip1.Initialize(zipFileName); | |
| zip2 = ZipFile.Read(zipFileName); | |
| foreach (var e1 in zip1) | |
| { | |
| foreach (var e2 in zip2) | |
| { | |
| if (e1.FileName == e2.FileName) | |
| { | |
| if (e1._RelativeOffsetOfLocalHeader != e2._RelativeOffsetOfLocalHeader) | |
| { | |
| isOk = false; | |
| if (writer != null) | |
| writer.WriteLine("{0}: mismatch in RelativeOffsetOfLocalHeader (0x{1:X16} != 0x{2:X16})", | |
| e1.FileName, e1._RelativeOffsetOfLocalHeader, | |
| e2._RelativeOffsetOfLocalHeader); | |
| } | |
| if (e1._CompressedSize != e2._CompressedSize) | |
| { | |
| isOk = false; | |
| if (writer != null) | |
| writer.WriteLine("{0}: mismatch in CompressedSize (0x{1:X16} != 0x{2:X16})", | |
| e1.FileName, e1._CompressedSize, | |
| e2._CompressedSize); | |
| } | |
| if (e1._UncompressedSize != e2._UncompressedSize) | |
| { | |
| isOk = false; | |
| if (writer != null) | |
| writer.WriteLine("{0}: mismatch in UncompressedSize (0x{1:X16} != 0x{2:X16})", | |
| e1.FileName, e1._UncompressedSize, | |
| e2._UncompressedSize); | |
| } | |
| if (e1.CompressionMethod != e2.CompressionMethod) | |
| { | |
| isOk = false; | |
| if (writer != null) | |
| writer.WriteLine("{0}: mismatch in CompressionMethod (0x{1:X4} != 0x{2:X4})", | |
| e1.FileName, e1.CompressionMethod, | |
| e2.CompressionMethod); | |
| } | |
| if (e1.Crc != e2.Crc) | |
| { | |
| isOk = false; | |
| if (writer != null) | |
| writer.WriteLine("{0}: mismatch in Crc32 (0x{1:X4} != 0x{2:X4})", | |
| e1.FileName, e1.Crc, | |
| e2.Crc); | |
| } | |
| // found a match, so stop the inside loop | |
| break; | |
| } | |
| } | |
| } | |
| zip2.Dispose(); | |
| zip2 = null; | |
| if (!isOk && fixIfNecessary) | |
| { | |
| string newFileName = Path.GetFileNameWithoutExtension(zipFileName); | |
| newFileName = System.String.Format("{0}_fixed.zip", newFileName); | |
| zip1.Save(newFileName); | |
| } | |
| } | |
| finally | |
| { | |
| if (zip1 != null) zip1.Dispose(); | |
| if (zip2 != null) zip2.Dispose(); | |
| } | |
| return isOk; | |
| } | |
| /// <summary> | |
| /// Rewrite the directory within a zipfile. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// In cases of data error, the directory in a zip file can get out of | |
| /// synch with the entries in the zip file. This method attempts to fix | |
| /// the zip file if this has occurred. | |
| /// </para> | |
| /// | |
| /// <para> This can take a long time for large zip files. </para> | |
| /// | |
| /// <para> This won't work if the zip file uses a non-standard | |
| /// code page - neither IBM437 nor UTF-8. </para> | |
| /// | |
| /// <para> | |
| /// This method is not supported in the Reduced or Compact Framework | |
| /// versions of DotNetZip. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Developers using COM can use the <see | |
| /// cref="ComHelper.FixZipDirectory(String)">ComHelper.FixZipDirectory(String)</see> | |
| /// method. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| /// | |
| /// <param name="zipFileName">The filename to of the zip file to fix.</param> | |
| /// | |
| /// <seealso cref="CheckZip(string)"/> | |
| /// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/> | |
| public static void FixZipDirectory(string zipFileName) | |
| { | |
| using (var zip = new ZipFile()) | |
| { | |
| zip.FullScan = true; | |
| zip.Initialize(zipFileName); | |
| zip.Save(zipFileName); | |
| } | |
| } | |
| /// <summary> | |
| /// Verify the password on a zip file. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// Keep in mind that passwords in zipfiles are applied to | |
| /// zip entries, not to the entire zip file. So testing a | |
| /// zipfile for a particular password doesn't work in the | |
| /// general case. On the other hand, it's often the case | |
| /// that a single password will be used on all entries in a | |
| /// zip file. This method works for that case. | |
| /// </para> | |
| /// <para> | |
| /// There is no way to check a password without doing the | |
| /// decryption. So this code decrypts and extracts the given | |
| /// zipfile into <see cref="System.IO.Stream.Null"/> | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <param name="zipFileName">The filename to of the zip file to fix.</param> | |
| /// | |
| /// <param name="password">The password to check.</param> | |
| /// | |
| /// <returns>a bool indicating whether the password matches.</returns> | |
| public static bool CheckZipPassword(string zipFileName, string password) | |
| { | |
| // workitem 13664 | |
| bool success = false; | |
| try | |
| { | |
| using (ZipFile zip1 = ZipFile.Read(zipFileName)) | |
| { | |
| foreach (var e in zip1) | |
| { | |
| if (!e.IsDirectory && e.UsesEncryption) | |
| { | |
| e.ExtractWithPassword(System.IO.Stream.Null, password); | |
| } | |
| } | |
| } | |
| success = true; | |
| } | |
| catch(Ionic.Zip.BadPasswordException) { } | |
| return success; | |
| } | |
| /// <summary> | |
| /// Provides a human-readable string with information about the ZipFile. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// The information string contains 10 lines or so, about each ZipEntry, | |
| /// describing whether encryption is in use, the compressed and uncompressed | |
| /// length of the entry, the offset of the entry, and so on. As a result the | |
| /// information string can be very long for zip files that contain many | |
| /// entries. | |
| /// </para> | |
| /// <para> | |
| /// This information is mostly useful for diagnostic purposes. | |
| /// </para> | |
| /// </remarks> | |
| public string Info | |
| { | |
| get | |
| { | |
| var builder = new System.Text.StringBuilder(); | |
| builder.Append(string.Format(" ZipFile: {0}\n", this.Name)); | |
| if (!string.IsNullOrEmpty(this._Comment)) | |
| { | |
| builder.Append(string.Format(" Comment: {0}\n", this._Comment)); | |
| } | |
| if (this._versionMadeBy != 0) | |
| { | |
| builder.Append(string.Format(" version made by: 0x{0:X4}\n", this._versionMadeBy)); | |
| } | |
| if (this._versionNeededToExtract != 0) | |
| { | |
| builder.Append(string.Format("needed to extract: 0x{0:X4}\n", this._versionNeededToExtract)); | |
| } | |
| builder.Append(string.Format(" uses ZIP64: {0}\n", this.InputUsesZip64)); | |
| builder.Append(string.Format(" disk with CD: {0}\n", this._diskNumberWithCd)); | |
| if (this._OffsetOfCentralDirectory == 0xFFFFFFFF) | |
| builder.Append(string.Format(" CD64 offset: 0x{0:X16}\n", this._OffsetOfCentralDirectory64)); | |
| else | |
| builder.Append(string.Format(" CD offset: 0x{0:X8}\n", this._OffsetOfCentralDirectory)); | |
| builder.Append("\n"); | |
| foreach (ZipEntry entry in this._entries.Values) | |
| { | |
| builder.Append(entry.Info); | |
| } | |
| return builder.ToString(); | |
| } | |
| } | |
| } | |
| } |