ZipFile.Check.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // ZipFile.Check.cs
  2. // ------------------------------------------------------------------
  3. //
  4. // Copyright (c) 2009-2011 Dino Chiesa.
  5. // All rights reserved.
  6. //
  7. // This code module is part of DotNetZip, a zipfile class library.
  8. //
  9. // ------------------------------------------------------------------
  10. //
  11. // This code is licensed under the Microsoft Public License.
  12. // See the file License.txt for the license details.
  13. // More info on: http://dotnetzip.codeplex.com
  14. //
  15. // ------------------------------------------------------------------
  16. //
  17. // last saved (in emacs):
  18. // Time-stamp: <2011-July-31 14:40:50>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module defines the methods for doing Checks on zip files.
  23. // These are not necessary to include in the Reduced or CF
  24. // version of the library.
  25. //
  26. // ------------------------------------------------------------------
  27. //
  28. using System;
  29. using System.IO;
  30. using System.Collections.Generic;
  31. namespace Ionic.Zip
  32. {
  33. public partial class ZipFile
  34. {
  35. /// <summary>
  36. /// Checks a zip file to see if its directory is consistent.
  37. /// </summary>
  38. ///
  39. /// <remarks>
  40. ///
  41. /// <para>
  42. /// In cases of data error, the directory within a zip file can get out
  43. /// of synch with the entries in the zip file. This method checks the
  44. /// given zip file and returns true if this has occurred.
  45. /// </para>
  46. ///
  47. /// <para> This method may take a long time to run for large zip files. </para>
  48. ///
  49. /// <para>
  50. /// This method is not supported in the Reduced or Compact Framework
  51. /// versions of DotNetZip.
  52. /// </para>
  53. ///
  54. /// <para>
  55. /// Developers using COM can use the <see
  56. /// cref="ComHelper.CheckZip(String)">ComHelper.CheckZip(String)</see>
  57. /// method.
  58. /// </para>
  59. ///
  60. /// </remarks>
  61. ///
  62. /// <param name="zipFileName">The filename to of the zip file to check.</param>
  63. ///
  64. /// <returns>true if the named zip file checks OK. Otherwise, false. </returns>
  65. ///
  66. /// <seealso cref="FixZipDirectory(string)"/>
  67. /// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/>
  68. public static bool CheckZip(string zipFileName)
  69. {
  70. return CheckZip(zipFileName, false, null);
  71. }
  72. /// <summary>
  73. /// Checks a zip file to see if its directory is consistent,
  74. /// and optionally fixes the directory if necessary.
  75. /// </summary>
  76. ///
  77. /// <remarks>
  78. ///
  79. /// <para>
  80. /// In cases of data error, the directory within a zip file can get out of
  81. /// synch with the entries in the zip file. This method checks the given
  82. /// zip file, and returns true if this has occurred. It also optionally
  83. /// fixes the zipfile, saving the fixed copy in <em>Name</em>_Fixed.zip.
  84. /// </para>
  85. ///
  86. /// <para>
  87. /// This method may take a long time to run for large zip files. It
  88. /// will take even longer if the file actually needs to be fixed, and if
  89. /// <c>fixIfNecessary</c> is true.
  90. /// </para>
  91. ///
  92. /// <para>
  93. /// This method is not supported in the Reduced or Compact
  94. /// Framework versions of DotNetZip.
  95. /// </para>
  96. ///
  97. /// </remarks>
  98. ///
  99. /// <param name="zipFileName">The filename to of the zip file to check.</param>
  100. ///
  101. /// <param name="fixIfNecessary">If true, the method will fix the zip file if
  102. /// necessary.</param>
  103. ///
  104. /// <param name="writer">
  105. /// a TextWriter in which messages generated while checking will be written.
  106. /// </param>
  107. ///
  108. /// <returns>true if the named zip is OK; false if the file needs to be fixed.</returns>
  109. ///
  110. /// <seealso cref="CheckZip(string)"/>
  111. /// <seealso cref="FixZipDirectory(string)"/>
  112. public static bool CheckZip(string zipFileName, bool fixIfNecessary,
  113. TextWriter writer)
  114. {
  115. ZipFile zip1 = null, zip2 = null;
  116. bool isOk = true;
  117. try
  118. {
  119. zip1 = new ZipFile();
  120. zip1.FullScan = true;
  121. zip1.Initialize(zipFileName);
  122. zip2 = ZipFile.Read(zipFileName);
  123. foreach (var e1 in zip1)
  124. {
  125. foreach (var e2 in zip2)
  126. {
  127. if (e1.FileName == e2.FileName)
  128. {
  129. if (e1._RelativeOffsetOfLocalHeader != e2._RelativeOffsetOfLocalHeader)
  130. {
  131. isOk = false;
  132. if (writer != null)
  133. writer.WriteLine("{0}: mismatch in RelativeOffsetOfLocalHeader (0x{1:X16} != 0x{2:X16})",
  134. e1.FileName, e1._RelativeOffsetOfLocalHeader,
  135. e2._RelativeOffsetOfLocalHeader);
  136. }
  137. if (e1._CompressedSize != e2._CompressedSize)
  138. {
  139. isOk = false;
  140. if (writer != null)
  141. writer.WriteLine("{0}: mismatch in CompressedSize (0x{1:X16} != 0x{2:X16})",
  142. e1.FileName, e1._CompressedSize,
  143. e2._CompressedSize);
  144. }
  145. if (e1._UncompressedSize != e2._UncompressedSize)
  146. {
  147. isOk = false;
  148. if (writer != null)
  149. writer.WriteLine("{0}: mismatch in UncompressedSize (0x{1:X16} != 0x{2:X16})",
  150. e1.FileName, e1._UncompressedSize,
  151. e2._UncompressedSize);
  152. }
  153. if (e1.CompressionMethod != e2.CompressionMethod)
  154. {
  155. isOk = false;
  156. if (writer != null)
  157. writer.WriteLine("{0}: mismatch in CompressionMethod (0x{1:X4} != 0x{2:X4})",
  158. e1.FileName, e1.CompressionMethod,
  159. e2.CompressionMethod);
  160. }
  161. if (e1.Crc != e2.Crc)
  162. {
  163. isOk = false;
  164. if (writer != null)
  165. writer.WriteLine("{0}: mismatch in Crc32 (0x{1:X4} != 0x{2:X4})",
  166. e1.FileName, e1.Crc,
  167. e2.Crc);
  168. }
  169. // found a match, so stop the inside loop
  170. break;
  171. }
  172. }
  173. }
  174. zip2.Dispose();
  175. zip2 = null;
  176. if (!isOk && fixIfNecessary)
  177. {
  178. string newFileName = Path.GetFileNameWithoutExtension(zipFileName);
  179. newFileName = System.String.Format("{0}_fixed.zip", newFileName);
  180. zip1.Save(newFileName);
  181. }
  182. }
  183. finally
  184. {
  185. if (zip1 != null) zip1.Dispose();
  186. if (zip2 != null) zip2.Dispose();
  187. }
  188. return isOk;
  189. }
  190. /// <summary>
  191. /// Rewrite the directory within a zipfile.
  192. /// </summary>
  193. ///
  194. /// <remarks>
  195. ///
  196. /// <para>
  197. /// In cases of data error, the directory in a zip file can get out of
  198. /// synch with the entries in the zip file. This method attempts to fix
  199. /// the zip file if this has occurred.
  200. /// </para>
  201. ///
  202. /// <para> This can take a long time for large zip files. </para>
  203. ///
  204. /// <para> This won't work if the zip file uses a non-standard
  205. /// code page - neither IBM437 nor UTF-8. </para>
  206. ///
  207. /// <para>
  208. /// This method is not supported in the Reduced or Compact Framework
  209. /// versions of DotNetZip.
  210. /// </para>
  211. ///
  212. /// <para>
  213. /// Developers using COM can use the <see
  214. /// cref="ComHelper.FixZipDirectory(String)">ComHelper.FixZipDirectory(String)</see>
  215. /// method.
  216. /// </para>
  217. ///
  218. /// </remarks>
  219. ///
  220. /// <param name="zipFileName">The filename to of the zip file to fix.</param>
  221. ///
  222. /// <seealso cref="CheckZip(string)"/>
  223. /// <seealso cref="CheckZip(string,bool,System.IO.TextWriter)"/>
  224. public static void FixZipDirectory(string zipFileName)
  225. {
  226. using (var zip = new ZipFile())
  227. {
  228. zip.FullScan = true;
  229. zip.Initialize(zipFileName);
  230. zip.Save(zipFileName);
  231. }
  232. }
  233. /// <summary>
  234. /// Verify the password on a zip file.
  235. /// </summary>
  236. ///
  237. /// <remarks>
  238. /// <para>
  239. /// Keep in mind that passwords in zipfiles are applied to
  240. /// zip entries, not to the entire zip file. So testing a
  241. /// zipfile for a particular password doesn't work in the
  242. /// general case. On the other hand, it's often the case
  243. /// that a single password will be used on all entries in a
  244. /// zip file. This method works for that case.
  245. /// </para>
  246. /// <para>
  247. /// There is no way to check a password without doing the
  248. /// decryption. So this code decrypts and extracts the given
  249. /// zipfile into <see cref="System.IO.Stream.Null"/>
  250. /// </para>
  251. /// </remarks>
  252. ///
  253. /// <param name="zipFileName">The filename to of the zip file to fix.</param>
  254. ///
  255. /// <param name="password">The password to check.</param>
  256. ///
  257. /// <returns>a bool indicating whether the password matches.</returns>
  258. public static bool CheckZipPassword(string zipFileName, string password)
  259. {
  260. // workitem 13664
  261. bool success = false;
  262. try
  263. {
  264. using (ZipFile zip1 = ZipFile.Read(zipFileName))
  265. {
  266. foreach (var e in zip1)
  267. {
  268. if (!e.IsDirectory && e.UsesEncryption)
  269. {
  270. e.ExtractWithPassword(System.IO.Stream.Null, password);
  271. }
  272. }
  273. }
  274. success = true;
  275. }
  276. catch(Ionic.Zip.BadPasswordException) { }
  277. return success;
  278. }
  279. /// <summary>
  280. /// Provides a human-readable string with information about the ZipFile.
  281. /// </summary>
  282. ///
  283. /// <remarks>
  284. /// <para>
  285. /// The information string contains 10 lines or so, about each ZipEntry,
  286. /// describing whether encryption is in use, the compressed and uncompressed
  287. /// length of the entry, the offset of the entry, and so on. As a result the
  288. /// information string can be very long for zip files that contain many
  289. /// entries.
  290. /// </para>
  291. /// <para>
  292. /// This information is mostly useful for diagnostic purposes.
  293. /// </para>
  294. /// </remarks>
  295. public string Info
  296. {
  297. get
  298. {
  299. var builder = new System.Text.StringBuilder();
  300. builder.Append(string.Format(" ZipFile: {0}\n", this.Name));
  301. if (!string.IsNullOrEmpty(this._Comment))
  302. {
  303. builder.Append(string.Format(" Comment: {0}\n", this._Comment));
  304. }
  305. if (this._versionMadeBy != 0)
  306. {
  307. builder.Append(string.Format(" version made by: 0x{0:X4}\n", this._versionMadeBy));
  308. }
  309. if (this._versionNeededToExtract != 0)
  310. {
  311. builder.Append(string.Format("needed to extract: 0x{0:X4}\n", this._versionNeededToExtract));
  312. }
  313. builder.Append(string.Format(" uses ZIP64: {0}\n", this.InputUsesZip64));
  314. builder.Append(string.Format(" disk with CD: {0}\n", this._diskNumberWithCd));
  315. if (this._OffsetOfCentralDirectory == 0xFFFFFFFF)
  316. builder.Append(string.Format(" CD64 offset: 0x{0:X16}\n", this._OffsetOfCentralDirectory64));
  317. else
  318. builder.Append(string.Format(" CD offset: 0x{0:X8}\n", this._OffsetOfCentralDirectory));
  319. builder.Append("\n");
  320. foreach (ZipEntry entry in this._entries.Values)
  321. {
  322. builder.Append(entry.Info);
  323. }
  324. return builder.ToString();
  325. }
  326. }
  327. }
  328. }