// 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 Ionic.Zip { public partial class ZipFile { /// /// Checks a zip file to see if its directory is consistent. /// /// /// /// /// /// 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. /// /// /// This method may take a long time to run for large zip files. /// /// /// This method is not supported in the Reduced or Compact Framework /// versions of DotNetZip. /// /// /// /// Developers using COM can use the ComHelper.CheckZip(String) /// method. /// /// /// /// /// The filename to of the zip file to check. /// /// true if the named zip file checks OK. Otherwise, false. /// /// /// public static bool CheckZip(string zipFileName) { return CheckZip(zipFileName, false, null); } /// /// Checks a zip file to see if its directory is consistent, /// and optionally fixes the directory if necessary. /// /// /// /// /// /// 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 Name_Fixed.zip. /// /// /// /// 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 /// fixIfNecessary is true. /// /// /// /// This method is not supported in the Reduced or Compact /// Framework versions of DotNetZip. /// /// /// /// /// The filename to of the zip file to check. /// /// If true, the method will fix the zip file if /// necessary. /// /// /// a TextWriter in which messages generated while checking will be written. /// /// /// true if the named zip is OK; false if the file needs to be fixed. /// /// /// 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; } /// /// Rewrite the directory within a zipfile. /// /// /// /// /// /// 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. /// /// /// This can take a long time for large zip files. /// /// This won't work if the zip file uses a non-standard /// code page - neither IBM437 nor UTF-8. /// /// /// This method is not supported in the Reduced or Compact Framework /// versions of DotNetZip. /// /// /// /// Developers using COM can use the ComHelper.FixZipDirectory(String) /// method. /// /// /// /// /// The filename to of the zip file to fix. /// /// /// public static void FixZipDirectory(string zipFileName) { using (var zip = new ZipFile()) { zip.FullScan = true; zip.Initialize(zipFileName); zip.Save(zipFileName); } } /// /// Verify the password on a zip file. /// /// /// /// /// 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. /// /// /// There is no way to check a password without doing the /// decryption. So this code decrypts and extracts the given /// zipfile into /// /// /// /// The filename to of the zip file to fix. /// /// The password to check. /// /// a bool indicating whether the password matches. 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; } /// /// Provides a human-readable string with information about the ZipFile. /// /// /// /// /// 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. /// /// /// This information is mostly useful for diagnostic purposes. /// /// 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(); } } } }