1 | // Shared.cs
|
---|
2 | // ------------------------------------------------------------------
|
---|
3 | //
|
---|
4 | // Copyright (c) 2006-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: <2011-August-02 19:41:01>
|
---|
18 | //
|
---|
19 | // ------------------------------------------------------------------
|
---|
20 | //
|
---|
21 | // This module defines some shared utility classes and methods.
|
---|
22 | //
|
---|
23 | // Created: Tue, 27 Mar 2007 15:30
|
---|
24 | //
|
---|
25 |
|
---|
26 | using System;
|
---|
27 | using System.IO;
|
---|
28 | using System.Security.Permissions;
|
---|
29 |
|
---|
30 | namespace OfficeOpenXml.Packaging.Ionic.Zip
|
---|
31 | {
|
---|
32 | /// <summary>
|
---|
33 | /// Collects general purpose utility methods.
|
---|
34 | /// </summary>
|
---|
35 | internal static class SharedUtilities
|
---|
36 | {
|
---|
37 | /// private null constructor
|
---|
38 | //private SharedUtilities() { }
|
---|
39 |
|
---|
40 | // workitem 8423
|
---|
41 | public static Int64 GetFileLength(string fileName)
|
---|
42 | {
|
---|
43 | if (!File.Exists(fileName))
|
---|
44 | throw new System.IO.FileNotFoundException(fileName);
|
---|
45 |
|
---|
46 | long fileLength = 0L;
|
---|
47 | FileShare fs = FileShare.ReadWrite;
|
---|
48 | #if !NETCF
|
---|
49 | // FileShare.Delete is not defined for the Compact Framework
|
---|
50 | fs |= FileShare.Delete;
|
---|
51 | #endif
|
---|
52 | using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, fs))
|
---|
53 | {
|
---|
54 | fileLength = s.Length;
|
---|
55 | }
|
---|
56 | return fileLength;
|
---|
57 | }
|
---|
58 |
|
---|
59 |
|
---|
60 | [System.Diagnostics.Conditional("NETCF")]
|
---|
61 | public static void Workaround_Ladybug318918(Stream s)
|
---|
62 | {
|
---|
63 | // This is a workaround for this issue:
|
---|
64 | // https://connect.microsoft.com/VisualStudio/feedback/details/318918
|
---|
65 | // It's required only on NETCF.
|
---|
66 | s.Flush();
|
---|
67 | }
|
---|
68 |
|
---|
69 |
|
---|
70 | #if LEGACY
|
---|
71 | /// <summary>
|
---|
72 | /// Round the given DateTime value to an even second value.
|
---|
73 | /// </summary>
|
---|
74 | ///
|
---|
75 | /// <remarks>
|
---|
76 | /// <para>
|
---|
77 | /// Round up in the case of an odd second value. The rounding does not consider
|
---|
78 | /// fractional seconds.
|
---|
79 | /// </para>
|
---|
80 | /// <para>
|
---|
81 | /// This is useful because the Zip spec allows storage of time only to the nearest
|
---|
82 | /// even second. So if you want to compare the time of an entry in the archive with
|
---|
83 | /// it's actual time in the filesystem, you need to round the actual filesystem
|
---|
84 | /// time, or use a 2-second threshold for the comparison.
|
---|
85 | /// </para>
|
---|
86 | /// <para>
|
---|
87 | /// This is most nautrally an extension method for the DateTime class but this
|
---|
88 | /// library is built for .NET 2.0, not for .NET 3.5; This means extension methods
|
---|
89 | /// are a no-no.
|
---|
90 | /// </para>
|
---|
91 | /// </remarks>
|
---|
92 | /// <param name="source">The DateTime value to round</param>
|
---|
93 | /// <returns>The ruonded DateTime value</returns>
|
---|
94 | public static DateTime RoundToEvenSecond(DateTime source)
|
---|
95 | {
|
---|
96 | // round to nearest second:
|
---|
97 | if ((source.Second % 2) == 1)
|
---|
98 | source += new TimeSpan(0, 0, 1);
|
---|
99 |
|
---|
100 | DateTime dtRounded = new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second);
|
---|
101 | //if (source.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1);
|
---|
102 | return dtRounded;
|
---|
103 | }
|
---|
104 | #endif
|
---|
105 |
|
---|
106 | #if YOU_LIKE_REDUNDANT_CODE
|
---|
107 | internal static string NormalizePath(string path)
|
---|
108 | {
|
---|
109 | // remove leading single dot slash
|
---|
110 | if (path.StartsWith(".\\")) path = path.Substring(2);
|
---|
111 |
|
---|
112 | // remove intervening dot-slash
|
---|
113 | path = path.Replace("\\.\\", "\\");
|
---|
114 |
|
---|
115 | // remove double dot when preceded by a directory name
|
---|
116 | var re = new System.Text.RegularExpressions.Regex(@"^(.*\\)?([^\\\.]+\\\.\.\\)(.+)$");
|
---|
117 | path = re.Replace(path, "$1$3");
|
---|
118 | return path;
|
---|
119 | }
|
---|
120 | #endif
|
---|
121 |
|
---|
122 | private static System.Text.RegularExpressions.Regex doubleDotRegex1 =
|
---|
123 | new System.Text.RegularExpressions.Regex(@"^(.*/)?([^/\\.]+/\\.\\./)(.+)$");
|
---|
124 |
|
---|
125 | private static string SimplifyFwdSlashPath(string path)
|
---|
126 | {
|
---|
127 | if (path.StartsWith("./")) path = path.Substring(2);
|
---|
128 | path = path.Replace("/./", "/");
|
---|
129 |
|
---|
130 | // Replace foo/anything/../bar with foo/bar
|
---|
131 | path = doubleDotRegex1.Replace(path, "$1$3");
|
---|
132 | return path;
|
---|
133 | }
|
---|
134 |
|
---|
135 |
|
---|
136 | /// <summary>
|
---|
137 | /// Utility routine for transforming path names from filesystem format (on Windows that means backslashes) to
|
---|
138 | /// a format suitable for use within zipfiles. This means trimming the volume letter and colon (if any) And
|
---|
139 | /// swapping backslashes for forward slashes.
|
---|
140 | /// </summary>
|
---|
141 | /// <param name="pathName">source path.</param>
|
---|
142 | /// <returns>transformed path</returns>
|
---|
143 | public static string NormalizePathForUseInZipFile(string pathName)
|
---|
144 | {
|
---|
145 | // boundary case
|
---|
146 | if (String.IsNullOrEmpty(pathName)) return pathName;
|
---|
147 |
|
---|
148 | // trim volume if necessary
|
---|
149 | if ((pathName.Length >= 2) && ((pathName[1] == ':') && (pathName[2] == '\\')))
|
---|
150 | pathName = pathName.Substring(3);
|
---|
151 |
|
---|
152 | // swap slashes
|
---|
153 | pathName = pathName.Replace('\\', '/');
|
---|
154 |
|
---|
155 | // trim all leading slashes
|
---|
156 | while (pathName.StartsWith("/")) pathName = pathName.Substring(1);
|
---|
157 |
|
---|
158 | return SimplifyFwdSlashPath(pathName);
|
---|
159 | }
|
---|
160 |
|
---|
161 |
|
---|
162 | static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437");
|
---|
163 | static System.Text.Encoding utf8 = System.Text.Encoding.GetEncoding("UTF-8");
|
---|
164 |
|
---|
165 | internal static byte[] StringToByteArray(string value, System.Text.Encoding encoding)
|
---|
166 | {
|
---|
167 | byte[] a = encoding.GetBytes(value);
|
---|
168 | return a;
|
---|
169 | }
|
---|
170 | internal static byte[] StringToByteArray(string value)
|
---|
171 | {
|
---|
172 | return StringToByteArray(value, ibm437);
|
---|
173 | }
|
---|
174 |
|
---|
175 | //internal static byte[] Utf8StringToByteArray(string value)
|
---|
176 | //{
|
---|
177 | // return StringToByteArray(value, utf8);
|
---|
178 | //}
|
---|
179 |
|
---|
180 | //internal static string StringFromBuffer(byte[] buf, int maxlength)
|
---|
181 | //{
|
---|
182 | // return StringFromBuffer(buf, maxlength, ibm437);
|
---|
183 | //}
|
---|
184 |
|
---|
185 | internal static string Utf8StringFromBuffer(byte[] buf)
|
---|
186 | {
|
---|
187 | return StringFromBuffer(buf, utf8);
|
---|
188 | }
|
---|
189 |
|
---|
190 | internal static string StringFromBuffer(byte[] buf, System.Text.Encoding encoding)
|
---|
191 | {
|
---|
192 | // this form of the GetString() method is required for .NET CF compatibility
|
---|
193 | string s = encoding.GetString(buf, 0, buf.Length);
|
---|
194 | return s;
|
---|
195 | }
|
---|
196 |
|
---|
197 |
|
---|
198 | internal static int ReadSignature(System.IO.Stream s)
|
---|
199 | {
|
---|
200 | int x = 0;
|
---|
201 | try { x = _ReadFourBytes(s, "n/a"); }
|
---|
202 | catch (BadReadException) { }
|
---|
203 | return x;
|
---|
204 | }
|
---|
205 |
|
---|
206 |
|
---|
207 | internal static int ReadEntrySignature(System.IO.Stream s)
|
---|
208 | {
|
---|
209 | // handle the case of ill-formatted zip archives - includes a data descriptor
|
---|
210 | // when none is expected.
|
---|
211 | int x = 0;
|
---|
212 | try
|
---|
213 | {
|
---|
214 | x = _ReadFourBytes(s, "n/a");
|
---|
215 | if (x == ZipConstants.ZipEntryDataDescriptorSignature)
|
---|
216 | {
|
---|
217 | // advance past data descriptor - 12 bytes if not zip64
|
---|
218 | s.Seek(12, SeekOrigin.Current);
|
---|
219 | // workitem 10178
|
---|
220 | Workaround_Ladybug318918(s);
|
---|
221 | x = _ReadFourBytes(s, "n/a");
|
---|
222 | if (x != ZipConstants.ZipEntrySignature)
|
---|
223 | {
|
---|
224 | // Maybe zip64 was in use for the prior entry.
|
---|
225 | // Therefore, skip another 8 bytes.
|
---|
226 | s.Seek(8, SeekOrigin.Current);
|
---|
227 | // workitem 10178
|
---|
228 | Workaround_Ladybug318918(s);
|
---|
229 | x = _ReadFourBytes(s, "n/a");
|
---|
230 | if (x != ZipConstants.ZipEntrySignature)
|
---|
231 | {
|
---|
232 | // seek back to the first spot
|
---|
233 | s.Seek(-24, SeekOrigin.Current);
|
---|
234 | // workitem 10178
|
---|
235 | Workaround_Ladybug318918(s);
|
---|
236 | x = _ReadFourBytes(s, "n/a");
|
---|
237 | }
|
---|
238 | }
|
---|
239 | }
|
---|
240 | }
|
---|
241 | catch (BadReadException) { }
|
---|
242 | return x;
|
---|
243 | }
|
---|
244 |
|
---|
245 |
|
---|
246 | internal static int ReadInt(System.IO.Stream s)
|
---|
247 | {
|
---|
248 | return _ReadFourBytes(s, "Could not read block - no data! (position 0x{0:X8})");
|
---|
249 | }
|
---|
250 |
|
---|
251 | private static int _ReadFourBytes(System.IO.Stream s, string message)
|
---|
252 | {
|
---|
253 | int n = 0;
|
---|
254 | byte[] block = new byte[4];
|
---|
255 | #if NETCF
|
---|
256 | // workitem 9181
|
---|
257 | // Reading here in NETCF sometimes reads "backwards". Seems to happen for
|
---|
258 | // larger files. Not sure why. Maybe an error in caching. If the data is:
|
---|
259 | //
|
---|
260 | // 00100210: 9efa 0f00 7072 6f6a 6563 742e 6963 7750 ....project.icwP
|
---|
261 | // 00100220: 4b05 0600 0000 0006 0006 0091 0100 008e K...............
|
---|
262 | // 00100230: 0010 0000 00 .....
|
---|
263 | //
|
---|
264 | // ...and the stream Position is 10021F, then a Read of 4 bytes is returning
|
---|
265 | // 50776369, instead of 06054b50. This seems to happen the 2nd time Read()
|
---|
266 | // is called from that Position..
|
---|
267 | //
|
---|
268 | // submitted to connect.microsoft.com
|
---|
269 | // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=318918#tabs
|
---|
270 | //
|
---|
271 | for (int i = 0; i < block.Length; i++)
|
---|
272 | {
|
---|
273 | n+= s.Read(block, i, 1);
|
---|
274 | }
|
---|
275 | #else
|
---|
276 | n = s.Read(block, 0, block.Length);
|
---|
277 | #endif
|
---|
278 | if (n != block.Length) throw new BadReadException(String.Format(message, s.Position));
|
---|
279 | int data = unchecked((((block[3] * 256 + block[2]) * 256) + block[1]) * 256 + block[0]);
|
---|
280 | return data;
|
---|
281 | }
|
---|
282 |
|
---|
283 |
|
---|
284 |
|
---|
285 | /// <summary>
|
---|
286 | /// Finds a signature in the zip stream. This is useful for finding
|
---|
287 | /// the end of a zip entry, for example, or the beginning of the next ZipEntry.
|
---|
288 | /// </summary>
|
---|
289 | ///
|
---|
290 | /// <remarks>
|
---|
291 | /// <para>
|
---|
292 | /// Scans through 64k at a time.
|
---|
293 | /// </para>
|
---|
294 | ///
|
---|
295 | /// <para>
|
---|
296 | /// If the method fails to find the requested signature, the stream Position
|
---|
297 | /// after completion of this method is unchanged. If the method succeeds in
|
---|
298 | /// finding the requested signature, the stream position after completion is
|
---|
299 | /// direct AFTER the signature found in the stream.
|
---|
300 | /// </para>
|
---|
301 | /// </remarks>
|
---|
302 | ///
|
---|
303 | /// <param name="stream">The stream to search</param>
|
---|
304 | /// <param name="SignatureToFind">The 4-byte signature to find</param>
|
---|
305 | /// <returns>The number of bytes read</returns>
|
---|
306 | internal static long FindSignature(System.IO.Stream stream, int SignatureToFind)
|
---|
307 | {
|
---|
308 | long startingPosition = stream.Position;
|
---|
309 |
|
---|
310 | int BATCH_SIZE = 65536; // 8192;
|
---|
311 | byte[] targetBytes = new byte[4];
|
---|
312 | targetBytes[0] = (byte)(SignatureToFind >> 24);
|
---|
313 | targetBytes[1] = (byte)((SignatureToFind & 0x00FF0000) >> 16);
|
---|
314 | targetBytes[2] = (byte)((SignatureToFind & 0x0000FF00) >> 8);
|
---|
315 | targetBytes[3] = (byte)(SignatureToFind & 0x000000FF);
|
---|
316 | byte[] batch = new byte[BATCH_SIZE];
|
---|
317 | int n = 0;
|
---|
318 | bool success = false;
|
---|
319 | do
|
---|
320 | {
|
---|
321 | n = stream.Read(batch, 0, batch.Length);
|
---|
322 | if (n != 0)
|
---|
323 | {
|
---|
324 | for (int i = 0; i < n; i++)
|
---|
325 | {
|
---|
326 | if (batch[i] == targetBytes[3])
|
---|
327 | {
|
---|
328 | long curPosition = stream.Position;
|
---|
329 | stream.Seek(i - n, System.IO.SeekOrigin.Current);
|
---|
330 | // workitem 10178
|
---|
331 | Workaround_Ladybug318918(stream);
|
---|
332 |
|
---|
333 | // workitem 7711
|
---|
334 | int sig = ReadSignature(stream);
|
---|
335 |
|
---|
336 | success = (sig == SignatureToFind);
|
---|
337 | if (!success)
|
---|
338 | {
|
---|
339 | stream.Seek(curPosition, System.IO.SeekOrigin.Begin);
|
---|
340 | // workitem 10178
|
---|
341 | Workaround_Ladybug318918(stream);
|
---|
342 | }
|
---|
343 | else
|
---|
344 | break; // out of for loop
|
---|
345 | }
|
---|
346 | }
|
---|
347 | }
|
---|
348 | else break;
|
---|
349 | if (success) break;
|
---|
350 |
|
---|
351 | } while (true);
|
---|
352 |
|
---|
353 | if (!success)
|
---|
354 | {
|
---|
355 | stream.Seek(startingPosition, System.IO.SeekOrigin.Begin);
|
---|
356 | // workitem 10178
|
---|
357 | Workaround_Ladybug318918(stream);
|
---|
358 | return -1; // or throw?
|
---|
359 | }
|
---|
360 |
|
---|
361 | // subtract 4 for the signature.
|
---|
362 | long bytesRead = (stream.Position - startingPosition) - 4;
|
---|
363 |
|
---|
364 | return bytesRead;
|
---|
365 | }
|
---|
366 |
|
---|
367 |
|
---|
368 | // If I have a time in the .NET environment, and I want to use it for
|
---|
369 | // SetWastWriteTime() etc, then I need to adjust it for Win32.
|
---|
370 | internal static DateTime AdjustTime_Reverse(DateTime time)
|
---|
371 | {
|
---|
372 | if (time.Kind == DateTimeKind.Utc) return time;
|
---|
373 | DateTime adjusted = time;
|
---|
374 | if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
---|
375 | adjusted = time - new System.TimeSpan(1, 0, 0);
|
---|
376 |
|
---|
377 | else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
---|
378 | adjusted = time + new System.TimeSpan(1, 0, 0);
|
---|
379 |
|
---|
380 | return adjusted;
|
---|
381 | }
|
---|
382 |
|
---|
383 | #if NECESSARY
|
---|
384 | // If I read a time from a file with GetLastWriteTime() (etc), I need
|
---|
385 | // to adjust it for display in the .NET environment.
|
---|
386 | internal static DateTime AdjustTime_Forward(DateTime time)
|
---|
387 | {
|
---|
388 | if (time.Kind == DateTimeKind.Utc) return time;
|
---|
389 | DateTime adjusted = time;
|
---|
390 | if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime())
|
---|
391 | adjusted = time + new System.TimeSpan(1, 0, 0);
|
---|
392 |
|
---|
393 | else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime())
|
---|
394 | adjusted = time - new System.TimeSpan(1, 0, 0);
|
---|
395 |
|
---|
396 | return adjusted;
|
---|
397 | }
|
---|
398 | #endif
|
---|
399 |
|
---|
400 |
|
---|
401 | internal static DateTime PackedToDateTime(Int32 packedDateTime)
|
---|
402 | {
|
---|
403 | // workitem 7074 & workitem 7170
|
---|
404 | if (packedDateTime == 0xFFFF || packedDateTime == 0)
|
---|
405 | return new System.DateTime(1995, 1, 1, 0, 0, 0, 0); // return a fixed date when none is supplied.
|
---|
406 |
|
---|
407 | Int16 packedTime = unchecked((Int16)(packedDateTime & 0x0000ffff));
|
---|
408 | Int16 packedDate = unchecked((Int16)((packedDateTime & 0xffff0000) >> 16));
|
---|
409 |
|
---|
410 | int year = 1980 + ((packedDate & 0xFE00) >> 9);
|
---|
411 | int month = (packedDate & 0x01E0) >> 5;
|
---|
412 | int day = packedDate & 0x001F;
|
---|
413 |
|
---|
414 | int hour = (packedTime & 0xF800) >> 11;
|
---|
415 | int minute = (packedTime & 0x07E0) >> 5;
|
---|
416 | //int second = packedTime & 0x001F;
|
---|
417 | int second = (packedTime & 0x001F) * 2;
|
---|
418 |
|
---|
419 | // validation and error checking.
|
---|
420 | // this is not foolproof but will catch most errors.
|
---|
421 | if (second >= 60) { minute++; second = 0; }
|
---|
422 | if (minute >= 60) { hour++; minute = 0; }
|
---|
423 | if (hour >= 24) { day++; hour = 0; }
|
---|
424 |
|
---|
425 | DateTime d = System.DateTime.Now;
|
---|
426 | bool success= false;
|
---|
427 | try
|
---|
428 | {
|
---|
429 | d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
---|
430 | success= true;
|
---|
431 | }
|
---|
432 | catch (System.ArgumentOutOfRangeException)
|
---|
433 | {
|
---|
434 | if (year == 1980 && (month == 0 || day == 0))
|
---|
435 | {
|
---|
436 | try
|
---|
437 | {
|
---|
438 | d = new System.DateTime(1980, 1, 1, hour, minute, second, 0);
|
---|
439 | success= true;
|
---|
440 | }
|
---|
441 | catch (System.ArgumentOutOfRangeException)
|
---|
442 | {
|
---|
443 | try
|
---|
444 | {
|
---|
445 | d = new System.DateTime(1980, 1, 1, 0, 0, 0, 0);
|
---|
446 | success= true;
|
---|
447 | }
|
---|
448 | catch (System.ArgumentOutOfRangeException) { }
|
---|
449 |
|
---|
450 | }
|
---|
451 | }
|
---|
452 | // workitem 8814
|
---|
453 | // my god, I can't believe how many different ways applications
|
---|
454 | // can mess up a simple date format.
|
---|
455 | else
|
---|
456 | {
|
---|
457 | try
|
---|
458 | {
|
---|
459 | while (year < 1980) year++;
|
---|
460 | while (year > 2030) year--;
|
---|
461 | while (month < 1) month++;
|
---|
462 | while (month > 12) month--;
|
---|
463 | while (day < 1) day++;
|
---|
464 | while (day > 28) day--;
|
---|
465 | while (minute < 0) minute++;
|
---|
466 | while (minute > 59) minute--;
|
---|
467 | while (second < 0) second++;
|
---|
468 | while (second > 59) second--;
|
---|
469 | d = new System.DateTime(year, month, day, hour, minute, second, 0);
|
---|
470 | success= true;
|
---|
471 | }
|
---|
472 | catch (System.ArgumentOutOfRangeException) { }
|
---|
473 | }
|
---|
474 | }
|
---|
475 | if (!success)
|
---|
476 | {
|
---|
477 | string msg = String.Format("y({0}) m({1}) d({2}) h({3}) m({4}) s({5})", year, month, day, hour, minute, second);
|
---|
478 | throw new ZipException(String.Format("Bad date/time format in the zip file. ({0})", msg));
|
---|
479 |
|
---|
480 | }
|
---|
481 | // workitem 6191
|
---|
482 | //d = AdjustTime_Reverse(d);
|
---|
483 | d = DateTime.SpecifyKind(d, DateTimeKind.Local);
|
---|
484 | return d;
|
---|
485 | }
|
---|
486 |
|
---|
487 |
|
---|
488 | internal
|
---|
489 | static Int32 DateTimeToPacked(DateTime time)
|
---|
490 | {
|
---|
491 | // The time is passed in here only for purposes of writing LastModified to the
|
---|
492 | // zip archive. It should always be LocalTime, but we convert anyway. And,
|
---|
493 | // since the time is being written out, it needs to be adjusted.
|
---|
494 |
|
---|
495 | time = time.ToLocalTime();
|
---|
496 | // workitem 7966
|
---|
497 | //time = AdjustTime_Forward(time);
|
---|
498 |
|
---|
499 | // see http://www.vsft.com/hal/dostime.htm for the format
|
---|
500 | UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00));
|
---|
501 | UInt16 packedTime = (UInt16)((time.Second / 2 & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800));
|
---|
502 |
|
---|
503 | Int32 result = (Int32)(((UInt32)(packedDate << 16)) | packedTime);
|
---|
504 | return result;
|
---|
505 | }
|
---|
506 |
|
---|
507 |
|
---|
508 | /// <summary>
|
---|
509 | /// Create a pseudo-random filename, suitable for use as a temporary
|
---|
510 | /// file, and open it.
|
---|
511 | /// </summary>
|
---|
512 | /// <remarks>
|
---|
513 | /// <para>
|
---|
514 | /// The System.IO.Path.GetRandomFileName() method is not available on
|
---|
515 | /// the Compact Framework, so this library provides its own substitute
|
---|
516 | /// on NETCF.
|
---|
517 | /// </para>
|
---|
518 | /// <para>
|
---|
519 | /// This method produces a filename of the form
|
---|
520 | /// DotNetZip-xxxxxxxx.tmp, where xxxxxxxx is replaced by randomly
|
---|
521 | /// chosen characters, and creates that file.
|
---|
522 | /// </para>
|
---|
523 | /// </remarks>
|
---|
524 | public static void CreateAndOpenUniqueTempFile(string dir,
|
---|
525 | out Stream fs,
|
---|
526 | out string filename)
|
---|
527 | {
|
---|
528 | // workitem 9763
|
---|
529 | // http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx
|
---|
530 | // try 3 times:
|
---|
531 | for (int i = 0; i < 3; i++)
|
---|
532 | {
|
---|
533 | try
|
---|
534 | {
|
---|
535 | filename = Path.Combine(dir, InternalGetTempFileName());
|
---|
536 | fs = new FileStream(filename, FileMode.CreateNew);
|
---|
537 | return;
|
---|
538 | }
|
---|
539 | catch (IOException)
|
---|
540 | {
|
---|
541 | if (i == 2) throw;
|
---|
542 | }
|
---|
543 | }
|
---|
544 | throw new IOException();
|
---|
545 | }
|
---|
546 |
|
---|
547 | #if NETCF || SILVERLIGHT
|
---|
548 | public static string InternalGetTempFileName()
|
---|
549 | {
|
---|
550 | return "DotNetZip-" + GenerateRandomStringImpl(8,0) + ".tmp";
|
---|
551 | }
|
---|
552 |
|
---|
553 | internal static string GenerateRandomStringImpl(int length, int delta)
|
---|
554 | {
|
---|
555 | bool WantMixedCase = (delta == 0);
|
---|
556 | System.Random rnd = new System.Random();
|
---|
557 |
|
---|
558 | string result = "";
|
---|
559 | char[] a = new char[length];
|
---|
560 |
|
---|
561 | for (int i = 0; i < length; i++)
|
---|
562 | {
|
---|
563 | // delta == 65 means uppercase
|
---|
564 | // delta == 97 means lowercase
|
---|
565 | if (WantMixedCase)
|
---|
566 | delta = (rnd.Next(2) == 0) ? 65 : 97;
|
---|
567 | a[i] = (char)(rnd.Next(26) + delta);
|
---|
568 | }
|
---|
569 |
|
---|
570 | result = new System.String(a);
|
---|
571 | return result;
|
---|
572 | }
|
---|
573 | #else
|
---|
574 | public static string InternalGetTempFileName()
|
---|
575 | {
|
---|
576 | return "DotNetZip-" + Path.GetRandomFileName().Substring(0, 8) + ".tmp";
|
---|
577 | }
|
---|
578 |
|
---|
579 | #endif
|
---|
580 |
|
---|
581 |
|
---|
582 | /// <summary>
|
---|
583 | /// Workitem 7889: handle ERROR_LOCK_VIOLATION during read
|
---|
584 | /// </summary>
|
---|
585 | /// <remarks>
|
---|
586 | /// This could be gracefully handled with an extension attribute, but
|
---|
587 | /// This assembly is built for .NET 2.0, so I cannot use them.
|
---|
588 | /// </remarks>
|
---|
589 | internal static int ReadWithRetry(System.IO.Stream s, byte[] buffer, int offset, int count, string FileName)
|
---|
590 | {
|
---|
591 | int n = 0;
|
---|
592 | bool done = false;
|
---|
593 | #if !NETCF && !SILVERLIGHT
|
---|
594 | int retries = 0;
|
---|
595 | #endif
|
---|
596 | do
|
---|
597 | {
|
---|
598 | try
|
---|
599 | {
|
---|
600 | n = s.Read(buffer, offset, count);
|
---|
601 | done = true;
|
---|
602 | }
|
---|
603 | #if NETCF || SILVERLIGHT
|
---|
604 | catch (System.IO.IOException)
|
---|
605 | {
|
---|
606 | throw;
|
---|
607 | }
|
---|
608 | #else
|
---|
609 | catch (System.IO.IOException ioexc1)
|
---|
610 | {
|
---|
611 | // Check if we can call GetHRForException,
|
---|
612 | // which makes unmanaged code calls.
|
---|
613 | var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode);
|
---|
614 | if (p.IsUnrestricted())
|
---|
615 | {
|
---|
616 | uint hresult = _HRForException(ioexc1);
|
---|
617 | if (hresult != 0x80070021) // ERROR_LOCK_VIOLATION
|
---|
618 | throw new System.IO.IOException(String.Format("Cannot read file {0}", FileName), ioexc1);
|
---|
619 | retries++;
|
---|
620 | if (retries > 10)
|
---|
621 | throw new System.IO.IOException(String.Format("Cannot read file {0}, at offset 0x{1:X8} after 10 retries", FileName, offset), ioexc1);
|
---|
622 |
|
---|
623 | // max time waited on last retry = 250 + 10*550 = 5.75s
|
---|
624 | // aggregate time waited after 10 retries: 250 + 55*550 = 30.5s
|
---|
625 | System.Threading.Thread.Sleep(250 + retries * 550);
|
---|
626 | }
|
---|
627 | else
|
---|
628 | {
|
---|
629 | // The permission.Demand() failed. Therefore, we cannot call
|
---|
630 | // GetHRForException, and cannot do the subtle handling of
|
---|
631 | // ERROR_LOCK_VIOLATION. Just bail.
|
---|
632 | throw;
|
---|
633 | }
|
---|
634 | }
|
---|
635 | #endif
|
---|
636 | }
|
---|
637 | while (!done);
|
---|
638 |
|
---|
639 | return n;
|
---|
640 | }
|
---|
641 |
|
---|
642 |
|
---|
643 | #if !NETCF
|
---|
644 | // workitem 8009
|
---|
645 | //
|
---|
646 | // This method must remain separate.
|
---|
647 | //
|
---|
648 | // Marshal.GetHRForException() is needed to do special exception handling for
|
---|
649 | // the read. But, that method requires UnmanagedCode permissions, and is marked
|
---|
650 | // with LinkDemand for UnmanagedCode. In an ASP.NET medium trust environment,
|
---|
651 | // where UnmanagedCode is restricted, will generate a SecurityException at the
|
---|
652 | // time of JIT of the method that calls a method that is marked with LinkDemand
|
---|
653 | // for UnmanagedCode. The SecurityException, if it is restricted, will occur
|
---|
654 | // when this method is JITed.
|
---|
655 | //
|
---|
656 | // The Marshal.GetHRForException() is factored out of ReadWithRetry in order to
|
---|
657 | // avoid the SecurityException at JIT compile time. Because _HRForException is
|
---|
658 | // called only when the UnmanagedCode is allowed. This means .NET never
|
---|
659 | // JIT-compiles this method when UnmanagedCode is disallowed, and thus never
|
---|
660 | // generates the JIT-compile time exception.
|
---|
661 | //
|
---|
662 | #endif
|
---|
663 | private static uint _HRForException(System.Exception ex1)
|
---|
664 | {
|
---|
665 | return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1));
|
---|
666 | }
|
---|
667 |
|
---|
668 | }
|
---|
669 |
|
---|
670 |
|
---|
671 |
|
---|
672 | /// <summary>
|
---|
673 | /// A decorator stream. It wraps another stream, and performs bookkeeping
|
---|
674 | /// to keep track of the stream Position.
|
---|
675 | /// </summary>
|
---|
676 | /// <remarks>
|
---|
677 | /// <para>
|
---|
678 | /// In some cases, it is not possible to get the Position of a stream, let's
|
---|
679 | /// say, on a write-only output stream like ASP.NET's
|
---|
680 | /// <c>Response.OutputStream</c>, or on a different write-only stream
|
---|
681 | /// provided as the destination for the zip by the application. In this
|
---|
682 | /// case, programmers can use this counting stream to count the bytes read
|
---|
683 | /// or written.
|
---|
684 | /// </para>
|
---|
685 | /// <para>
|
---|
686 | /// Consider the scenario of an application that saves a self-extracting
|
---|
687 | /// archive (SFX), that uses a custom SFX stub.
|
---|
688 | /// </para>
|
---|
689 | /// <para>
|
---|
690 | /// Saving to a filesystem file, the application would open the
|
---|
691 | /// filesystem file (getting a <c>FileStream</c>), save the custom sfx stub
|
---|
692 | /// into it, and then call <c>ZipFile.Save()</c>, specifying the same
|
---|
693 | /// FileStream. <c>ZipFile.Save()</c> does the right thing for the zipentry
|
---|
694 | /// offsets, by inquiring the Position of the <c>FileStream</c> before writing
|
---|
695 | /// any data, and then adding that initial offset into any ZipEntry
|
---|
696 | /// offsets in the zip directory. Everything works fine.
|
---|
697 | /// </para>
|
---|
698 | /// <para>
|
---|
699 | /// Now suppose the application is an ASPNET application and it saves
|
---|
700 | /// directly to <c>Response.OutputStream</c>. It's not possible for DotNetZip to
|
---|
701 | /// inquire the <c>Position</c>, so the offsets for the SFX will be wrong.
|
---|
702 | /// </para>
|
---|
703 | /// <para>
|
---|
704 | /// The workaround is for the application to use this class to wrap
|
---|
705 | /// <c>HttpResponse.OutputStream</c>, then write the SFX stub and the ZipFile
|
---|
706 | /// into that wrapper stream. Because <c>ZipFile.Save()</c> can inquire the
|
---|
707 | /// <c>Position</c>, it will then do the right thing with the offsets.
|
---|
708 | /// </para>
|
---|
709 | /// </remarks>
|
---|
710 | internal class CountingStream : System.IO.Stream
|
---|
711 | {
|
---|
712 | // workitem 12374: this class is now public
|
---|
713 | private System.IO.Stream _s;
|
---|
714 | private Int64 _bytesWritten;
|
---|
715 | private Int64 _bytesRead;
|
---|
716 | private Int64 _initialOffset;
|
---|
717 |
|
---|
718 | /// <summary>
|
---|
719 | /// The constructor.
|
---|
720 | /// </summary>
|
---|
721 | /// <param name="stream">The underlying stream</param>
|
---|
722 | public CountingStream(System.IO.Stream stream)
|
---|
723 | : base()
|
---|
724 | {
|
---|
725 | _s = stream;
|
---|
726 | try
|
---|
727 | {
|
---|
728 | _initialOffset = _s.Position;
|
---|
729 | }
|
---|
730 | catch
|
---|
731 | {
|
---|
732 | _initialOffset = 0L;
|
---|
733 | }
|
---|
734 | }
|
---|
735 |
|
---|
736 | /// <summary>
|
---|
737 | /// Gets the wrapped stream.
|
---|
738 | /// </summary>
|
---|
739 | public Stream WrappedStream
|
---|
740 | {
|
---|
741 | get
|
---|
742 | {
|
---|
743 | return _s;
|
---|
744 | }
|
---|
745 | }
|
---|
746 |
|
---|
747 | /// <summary>
|
---|
748 | /// The count of bytes written out to the stream.
|
---|
749 | /// </summary>
|
---|
750 | public Int64 BytesWritten
|
---|
751 | {
|
---|
752 | get { return _bytesWritten; }
|
---|
753 | }
|
---|
754 |
|
---|
755 | /// <summary>
|
---|
756 | /// the count of bytes that have been read from the stream.
|
---|
757 | /// </summary>
|
---|
758 | public Int64 BytesRead
|
---|
759 | {
|
---|
760 | get { return _bytesRead; }
|
---|
761 | }
|
---|
762 |
|
---|
763 | /// <summary>
|
---|
764 | /// Adjust the byte count on the stream.
|
---|
765 | /// </summary>
|
---|
766 | ///
|
---|
767 | /// <param name='delta'>
|
---|
768 | /// the number of bytes to subtract from the count.
|
---|
769 | /// </param>
|
---|
770 | ///
|
---|
771 | /// <remarks>
|
---|
772 | /// <para>
|
---|
773 | /// Subtract delta from the count of bytes written to the stream.
|
---|
774 | /// This is necessary when seeking back, and writing additional data,
|
---|
775 | /// as happens in some cases when saving Zip files.
|
---|
776 | /// </para>
|
---|
777 | /// </remarks>
|
---|
778 | public void Adjust(Int64 delta)
|
---|
779 | {
|
---|
780 | _bytesWritten -= delta;
|
---|
781 | if (_bytesWritten < 0)
|
---|
782 | throw new InvalidOperationException();
|
---|
783 | if (_s as CountingStream != null)
|
---|
784 | ((CountingStream)_s).Adjust(delta);
|
---|
785 | }
|
---|
786 |
|
---|
787 | /// <summary>
|
---|
788 | /// The read method.
|
---|
789 | /// </summary>
|
---|
790 | /// <param name="buffer">The buffer to hold the data read from the stream.</param>
|
---|
791 | /// <param name="offset">the offset within the buffer to copy the first byte read.</param>
|
---|
792 | /// <param name="count">the number of bytes to read.</param>
|
---|
793 | /// <returns>the number of bytes read, after decryption and decompression.</returns>
|
---|
794 | public override int Read(byte[] buffer, int offset, int count)
|
---|
795 | {
|
---|
796 | int n = _s.Read(buffer, offset, count);
|
---|
797 | _bytesRead += n;
|
---|
798 | return n;
|
---|
799 | }
|
---|
800 |
|
---|
801 | /// <summary>
|
---|
802 | /// Write data into the stream.
|
---|
803 | /// </summary>
|
---|
804 | /// <param name="buffer">The buffer holding data to write to the stream.</param>
|
---|
805 | /// <param name="offset">the offset within that data array to find the first byte to write.</param>
|
---|
806 | /// <param name="count">the number of bytes to write.</param>
|
---|
807 | public override void Write(byte[] buffer, int offset, int count)
|
---|
808 | {
|
---|
809 | if (count == 0) return;
|
---|
810 | _s.Write(buffer, offset, count);
|
---|
811 | _bytesWritten += count;
|
---|
812 | }
|
---|
813 |
|
---|
814 | /// <summary>
|
---|
815 | /// Whether the stream can be read.
|
---|
816 | /// </summary>
|
---|
817 | public override bool CanRead
|
---|
818 | {
|
---|
819 | get { return _s.CanRead; }
|
---|
820 | }
|
---|
821 |
|
---|
822 | /// <summary>
|
---|
823 | /// Whether it is possible to call Seek() on the stream.
|
---|
824 | /// </summary>
|
---|
825 | public override bool CanSeek
|
---|
826 | {
|
---|
827 | get { return _s.CanSeek; }
|
---|
828 | }
|
---|
829 |
|
---|
830 | /// <summary>
|
---|
831 | /// Whether it is possible to call Write() on the stream.
|
---|
832 | /// </summary>
|
---|
833 | public override bool CanWrite
|
---|
834 | {
|
---|
835 | get { return _s.CanWrite; }
|
---|
836 | }
|
---|
837 |
|
---|
838 | /// <summary>
|
---|
839 | /// Flushes the underlying stream.
|
---|
840 | /// </summary>
|
---|
841 | public override void Flush()
|
---|
842 | {
|
---|
843 | _s.Flush();
|
---|
844 | }
|
---|
845 |
|
---|
846 | /// <summary>
|
---|
847 | /// The length of the underlying stream.
|
---|
848 | /// </summary>
|
---|
849 | public override long Length
|
---|
850 | {
|
---|
851 | get { return _s.Length; } // bytesWritten??
|
---|
852 | }
|
---|
853 |
|
---|
854 | /// <summary>
|
---|
855 | /// Returns the sum of number of bytes written, plus the initial
|
---|
856 | /// offset before writing.
|
---|
857 | /// </summary>
|
---|
858 | public long ComputedPosition
|
---|
859 | {
|
---|
860 | get { return _initialOffset + _bytesWritten; }
|
---|
861 | }
|
---|
862 |
|
---|
863 |
|
---|
864 | /// <summary>
|
---|
865 | /// The Position of the stream.
|
---|
866 | /// </summary>
|
---|
867 | public override long Position
|
---|
868 | {
|
---|
869 | get { return _s.Position; }
|
---|
870 | set
|
---|
871 | {
|
---|
872 | _s.Seek(value, System.IO.SeekOrigin.Begin);
|
---|
873 | // workitem 10178
|
---|
874 | Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_s);
|
---|
875 | }
|
---|
876 | }
|
---|
877 |
|
---|
878 | /// <summary>
|
---|
879 | /// Seek in the stream.
|
---|
880 | /// </summary>
|
---|
881 | /// <param name="offset">the offset point to seek to</param>
|
---|
882 | /// <param name="origin">the reference point from which to seek</param>
|
---|
883 | /// <returns>The new position</returns>
|
---|
884 | public override long Seek(long offset, System.IO.SeekOrigin origin)
|
---|
885 | {
|
---|
886 | return _s.Seek(offset, origin);
|
---|
887 | }
|
---|
888 |
|
---|
889 | /// <summary>
|
---|
890 | /// Set the length of the underlying stream. Be careful with this!
|
---|
891 | /// </summary>
|
---|
892 | ///
|
---|
893 | /// <param name='value'>the length to set on the underlying stream.</param>
|
---|
894 | public override void SetLength(long value)
|
---|
895 | {
|
---|
896 | _s.SetLength(value);
|
---|
897 | }
|
---|
898 | }
|
---|
899 |
|
---|
900 |
|
---|
901 | }
|
---|