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