| // Shared.cs | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // Copyright (c) 2006-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: <2011-August-02 19:41:01> | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This module defines some shared utility classes and methods. | 
 | // | 
 | // Created: Tue, 27 Mar 2007  15:30 | 
 | // | 
 |  | 
 | using System; | 
 | using System.IO; | 
 | using System.Security.Permissions; | 
 |  | 
 | namespace OfficeOpenXml.Packaging.Ionic.Zip | 
 | { | 
 |     /// <summary> | 
 |     /// Collects general purpose utility methods. | 
 |     /// </summary> | 
 |     internal static class SharedUtilities | 
 |     { | 
 |         /// private null constructor | 
 |         //private SharedUtilities() { } | 
 |  | 
 |         // workitem 8423 | 
 |         public static Int64 GetFileLength(string fileName) | 
 |         { | 
 |             if (!File.Exists(fileName)) | 
 |                 throw new System.IO.FileNotFoundException(fileName); | 
 |  | 
 |             long fileLength = 0L; | 
 |             FileShare fs = FileShare.ReadWrite; | 
 | #if !NETCF | 
 |             // FileShare.Delete is not defined for the Compact Framework | 
 |             fs |= FileShare.Delete; | 
 | #endif | 
 |             using (var s = File.Open(fileName, FileMode.Open, FileAccess.Read, fs)) | 
 |             { | 
 |                 fileLength = s.Length; | 
 |             } | 
 |             return fileLength; | 
 |         } | 
 |  | 
 |  | 
 |         [System.Diagnostics.Conditional("NETCF")] | 
 |         public static void Workaround_Ladybug318918(Stream s) | 
 |         { | 
 |             // This is a workaround for this issue: | 
 |             // https://connect.microsoft.com/VisualStudio/feedback/details/318918 | 
 |             // It's required only on NETCF. | 
 |             s.Flush(); | 
 |         } | 
 |  | 
 |  | 
 | #if LEGACY | 
 |         /// <summary> | 
 |         /// Round the given DateTime value to an even second value. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <remarks> | 
 |         /// <para> | 
 |         /// Round up in the case of an odd second value.  The rounding does not consider | 
 |         /// fractional seconds. | 
 |         /// </para> | 
 |         /// <para> | 
 |         /// This is useful because the Zip spec allows storage of time only to the nearest | 
 |         /// even second.  So if you want to compare the time of an entry in the archive with | 
 |         /// it's actual time in the filesystem, you need to round the actual filesystem | 
 |         /// time, or use a 2-second threshold for the comparison. | 
 |         /// </para> | 
 |         /// <para> | 
 |         /// This is most nautrally an extension method for the DateTime class but this | 
 |         /// library is built for .NET 2.0, not for .NET 3.5; This means extension methods | 
 |         /// are a no-no. | 
 |         /// </para> | 
 |         /// </remarks> | 
 |         /// <param name="source">The DateTime value to round</param> | 
 |         /// <returns>The ruonded DateTime value</returns> | 
 |         public static DateTime RoundToEvenSecond(DateTime source) | 
 |         { | 
 |             // round to nearest second: | 
 |             if ((source.Second % 2) == 1) | 
 |                 source += new TimeSpan(0, 0, 1); | 
 |  | 
 |             DateTime dtRounded = new DateTime(source.Year, source.Month, source.Day, source.Hour, source.Minute, source.Second); | 
 |             //if (source.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1); | 
 |             return dtRounded; | 
 |         } | 
 | #endif | 
 |  | 
 | #if YOU_LIKE_REDUNDANT_CODE | 
 |         internal static string NormalizePath(string path) | 
 |         { | 
 |             // remove leading single dot slash | 
 |             if (path.StartsWith(".\\")) path = path.Substring(2); | 
 |  | 
 |             // remove intervening dot-slash | 
 |             path = path.Replace("\\.\\", "\\"); | 
 |  | 
 |             // remove double dot when preceded by a directory name | 
 |             var re = new System.Text.RegularExpressions.Regex(@"^(.*\\)?([^\\\.]+\\\.\.\\)(.+)$"); | 
 |             path = re.Replace(path, "$1$3"); | 
 |             return path; | 
 |         } | 
 | #endif | 
 |  | 
 |         private static System.Text.RegularExpressions.Regex doubleDotRegex1 = | 
 |             new System.Text.RegularExpressions.Regex(@"^(.*/)?([^/\\.]+/\\.\\./)(.+)$"); | 
 |  | 
 |         private static string SimplifyFwdSlashPath(string path) | 
 |         { | 
 |             if (path.StartsWith("./")) path = path.Substring(2); | 
 |             path = path.Replace("/./", "/"); | 
 |  | 
 |             // Replace foo/anything/../bar with foo/bar | 
 |             path = doubleDotRegex1.Replace(path, "$1$3"); | 
 |             return path; | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Utility routine for transforming path names from filesystem format (on Windows that means backslashes) to | 
 |         /// a format suitable for use within zipfiles. This means trimming the volume letter and colon (if any) And | 
 |         /// swapping backslashes for forward slashes. | 
 |         /// </summary> | 
 |         /// <param name="pathName">source path.</param> | 
 |         /// <returns>transformed path</returns> | 
 |         public static string NormalizePathForUseInZipFile(string pathName) | 
 |         { | 
 |             // boundary case | 
 |             if (String.IsNullOrEmpty(pathName)) return pathName; | 
 |  | 
 |             // trim volume if necessary | 
 |             if ((pathName.Length >= 2)  && ((pathName[1] == ':') && (pathName[2] == '\\'))) | 
 |                 pathName =  pathName.Substring(3); | 
 |  | 
 |             // swap slashes | 
 |             pathName = pathName.Replace('\\', '/'); | 
 |  | 
 |             // trim all leading slashes | 
 |             while (pathName.StartsWith("/")) pathName = pathName.Substring(1); | 
 |  | 
 |             return SimplifyFwdSlashPath(pathName); | 
 |         } | 
 |  | 
 |  | 
 |         static System.Text.Encoding ibm437 = System.Text.Encoding.GetEncoding("IBM437"); | 
 |         static System.Text.Encoding utf8 = System.Text.Encoding.GetEncoding("UTF-8"); | 
 |  | 
 |         internal static byte[] StringToByteArray(string value, System.Text.Encoding encoding) | 
 |         { | 
 |             byte[] a = encoding.GetBytes(value); | 
 |             return a; | 
 |         } | 
 |         internal static byte[] StringToByteArray(string value) | 
 |         { | 
 |             return StringToByteArray(value, ibm437); | 
 |         } | 
 |  | 
 |         //internal static byte[] Utf8StringToByteArray(string value) | 
 |         //{ | 
 |         //    return StringToByteArray(value, utf8); | 
 |         //} | 
 |  | 
 |         //internal static string StringFromBuffer(byte[] buf, int maxlength) | 
 |         //{ | 
 |         //    return StringFromBuffer(buf, maxlength, ibm437); | 
 |         //} | 
 |  | 
 |         internal static string Utf8StringFromBuffer(byte[] buf) | 
 |         { | 
 |             return StringFromBuffer(buf, utf8); | 
 |         } | 
 |  | 
 |         internal static string StringFromBuffer(byte[] buf, System.Text.Encoding encoding) | 
 |         { | 
 |             // this form of the GetString() method is required for .NET CF compatibility | 
 |             string s = encoding.GetString(buf, 0, buf.Length); | 
 |             return s; | 
 |         } | 
 |  | 
 |  | 
 |         internal static int ReadSignature(System.IO.Stream s) | 
 |         { | 
 |             int x = 0; | 
 |             try { x = _ReadFourBytes(s, "n/a"); } | 
 |             catch (BadReadException) { } | 
 |             return x; | 
 |         } | 
 |  | 
 |  | 
 |         internal static int ReadEntrySignature(System.IO.Stream s) | 
 |         { | 
 |             // handle the case of ill-formatted zip archives - includes a data descriptor | 
 |             // when none is expected. | 
 |             int x = 0; | 
 |             try | 
 |             { | 
 |                 x = _ReadFourBytes(s, "n/a"); | 
 |                 if (x == ZipConstants.ZipEntryDataDescriptorSignature) | 
 |                 { | 
 |                     // advance past data descriptor - 12 bytes if not zip64 | 
 |                     s.Seek(12, SeekOrigin.Current); | 
 |                     // workitem 10178 | 
 |                     Workaround_Ladybug318918(s); | 
 |                     x = _ReadFourBytes(s, "n/a"); | 
 |                     if (x != ZipConstants.ZipEntrySignature) | 
 |                     { | 
 |                         // Maybe zip64 was in use for the prior entry. | 
 |                         // Therefore, skip another 8 bytes. | 
 |                         s.Seek(8, SeekOrigin.Current); | 
 |                         // workitem 10178 | 
 |                         Workaround_Ladybug318918(s); | 
 |                         x = _ReadFourBytes(s, "n/a"); | 
 |                         if (x != ZipConstants.ZipEntrySignature) | 
 |                         { | 
 |                             // seek back to the first spot | 
 |                             s.Seek(-24, SeekOrigin.Current); | 
 |                             // workitem 10178 | 
 |                             Workaround_Ladybug318918(s); | 
 |                             x = _ReadFourBytes(s, "n/a"); | 
 |                         } | 
 |                     } | 
 |                 } | 
 |             } | 
 |             catch (BadReadException) { } | 
 |             return x; | 
 |         } | 
 |  | 
 |  | 
 |         internal static int ReadInt(System.IO.Stream s) | 
 |         { | 
 |             return _ReadFourBytes(s, "Could not read block - no data!  (position 0x{0:X8})"); | 
 |         } | 
 |  | 
 |         private static int _ReadFourBytes(System.IO.Stream s, string message) | 
 |         { | 
 |             int n = 0; | 
 |             byte[] block = new byte[4]; | 
 | #if NETCF | 
 |             // workitem 9181 | 
 |             // Reading here in NETCF sometimes reads "backwards". Seems to happen for | 
 |             // larger files.  Not sure why. Maybe an error in caching.  If the data is: | 
 |             // | 
 |             // 00100210: 9efa 0f00 7072 6f6a 6563 742e 6963 7750  ....project.icwP | 
 |             // 00100220: 4b05 0600 0000 0006 0006 0091 0100 008e  K............... | 
 |             // 00100230: 0010 0000 00                             ..... | 
 |             // | 
 |             // ...and the stream Position is 10021F, then a Read of 4 bytes is returning | 
 |             // 50776369, instead of 06054b50. This seems to happen the 2nd time Read() | 
 |             // is called from that Position.. | 
 |             // | 
 |             // submitted to connect.microsoft.com | 
 |             // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=318918#tabs | 
 |             // | 
 |             for (int i = 0; i < block.Length; i++) | 
 |             { | 
 |                 n+= s.Read(block, i, 1); | 
 |             } | 
 | #else | 
 |             n = s.Read(block, 0, block.Length); | 
 | #endif | 
 |             if (n != block.Length) throw new BadReadException(String.Format(message, s.Position)); | 
 |             int data = unchecked((((block[3] * 256 + block[2]) * 256) + block[1]) * 256 + block[0]); | 
 |             return data; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Finds a signature in the zip stream. This is useful for finding | 
 |         ///   the end of a zip entry, for example, or the beginning of the next ZipEntry. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <remarks> | 
 |         ///   <para> | 
 |         ///     Scans through 64k at a time. | 
 |         ///   </para> | 
 |         /// | 
 |         ///   <para> | 
 |         ///     If the method fails to find the requested signature, the stream Position | 
 |         ///     after completion of this method is unchanged. If the method succeeds in | 
 |         ///     finding the requested signature, the stream position after completion is | 
 |         ///     direct AFTER the signature found in the stream. | 
 |         ///   </para> | 
 |         /// </remarks> | 
 |         /// | 
 |         /// <param name="stream">The stream to search</param> | 
 |         /// <param name="SignatureToFind">The 4-byte signature to find</param> | 
 |         /// <returns>The number of bytes read</returns> | 
 |         internal static long FindSignature(System.IO.Stream stream, int SignatureToFind) | 
 |         { | 
 |             long startingPosition = stream.Position; | 
 |  | 
 |             int BATCH_SIZE = 65536; //  8192; | 
 |             byte[] targetBytes = new byte[4]; | 
 |             targetBytes[0] = (byte)(SignatureToFind >> 24); | 
 |             targetBytes[1] = (byte)((SignatureToFind & 0x00FF0000) >> 16); | 
 |             targetBytes[2] = (byte)((SignatureToFind & 0x0000FF00) >> 8); | 
 |             targetBytes[3] = (byte)(SignatureToFind & 0x000000FF); | 
 |             byte[] batch = new byte[BATCH_SIZE]; | 
 |             int n = 0; | 
 |             bool success = false; | 
 |             do | 
 |             { | 
 |                 n = stream.Read(batch, 0, batch.Length); | 
 |                 if (n != 0) | 
 |                 { | 
 |                     for (int i = 0; i < n; i++) | 
 |                     { | 
 |                         if (batch[i] == targetBytes[3]) | 
 |                         { | 
 |                             long curPosition = stream.Position; | 
 |                             stream.Seek(i - n, System.IO.SeekOrigin.Current); | 
 |                             // workitem 10178 | 
 |                             Workaround_Ladybug318918(stream); | 
 |  | 
 |                             // workitem 7711 | 
 |                             int sig = ReadSignature(stream); | 
 |  | 
 |                             success = (sig == SignatureToFind); | 
 |                             if (!success) | 
 |                             { | 
 |                                 stream.Seek(curPosition, System.IO.SeekOrigin.Begin); | 
 |                                 // workitem 10178 | 
 |                                 Workaround_Ladybug318918(stream); | 
 |                             } | 
 |                             else | 
 |                                 break; // out of for loop | 
 |                         } | 
 |                     } | 
 |                 } | 
 |                 else break; | 
 |                 if (success) break; | 
 |  | 
 |             } while (true); | 
 |  | 
 |             if (!success) | 
 |             { | 
 |                 stream.Seek(startingPosition, System.IO.SeekOrigin.Begin); | 
 |                 // workitem 10178 | 
 |                 Workaround_Ladybug318918(stream); | 
 |                 return -1;  // or throw? | 
 |             } | 
 |  | 
 |             // subtract 4 for the signature. | 
 |             long bytesRead = (stream.Position - startingPosition) - 4; | 
 |  | 
 |             return bytesRead; | 
 |         } | 
 |  | 
 |  | 
 |         // If I have a time in the .NET environment, and I want to use it for | 
 |         // SetWastWriteTime() etc, then I need to adjust it for Win32. | 
 |         internal static DateTime AdjustTime_Reverse(DateTime time) | 
 |         { | 
 |             if (time.Kind == DateTimeKind.Utc) return time; | 
 |             DateTime adjusted = time; | 
 |             if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime()) | 
 |                 adjusted = time - new System.TimeSpan(1, 0, 0); | 
 |  | 
 |             else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime()) | 
 |                 adjusted = time + new System.TimeSpan(1, 0, 0); | 
 |  | 
 |             return adjusted; | 
 |         } | 
 |  | 
 | #if NECESSARY | 
 |         // If I read a time from a file with GetLastWriteTime() (etc), I need | 
 |         // to adjust it for display in the .NET environment. | 
 |         internal static DateTime AdjustTime_Forward(DateTime time) | 
 |         { | 
 |             if (time.Kind == DateTimeKind.Utc) return time; | 
 |             DateTime adjusted = time; | 
 |             if (DateTime.Now.IsDaylightSavingTime() && !time.IsDaylightSavingTime()) | 
 |                 adjusted = time + new System.TimeSpan(1, 0, 0); | 
 |  | 
 |             else if (!DateTime.Now.IsDaylightSavingTime() && time.IsDaylightSavingTime()) | 
 |                 adjusted = time - new System.TimeSpan(1, 0, 0); | 
 |  | 
 |             return adjusted; | 
 |         } | 
 | #endif | 
 |  | 
 |  | 
 |         internal static DateTime PackedToDateTime(Int32 packedDateTime) | 
 |         { | 
 |             // workitem 7074 & workitem 7170 | 
 |             if (packedDateTime == 0xFFFF || packedDateTime == 0) | 
 |                 return new System.DateTime(1995, 1, 1, 0, 0, 0, 0);  // return a fixed date when none is supplied. | 
 |  | 
 |             Int16 packedTime = unchecked((Int16)(packedDateTime & 0x0000ffff)); | 
 |             Int16 packedDate = unchecked((Int16)((packedDateTime & 0xffff0000) >> 16)); | 
 |  | 
 |             int year = 1980 + ((packedDate & 0xFE00) >> 9); | 
 |             int month = (packedDate & 0x01E0) >> 5; | 
 |             int day = packedDate & 0x001F; | 
 |  | 
 |             int hour = (packedTime & 0xF800) >> 11; | 
 |             int minute = (packedTime & 0x07E0) >> 5; | 
 |             //int second = packedTime & 0x001F; | 
 |             int second = (packedTime & 0x001F) * 2; | 
 |  | 
 |             // validation and error checking. | 
 |             // this is not foolproof but will catch most errors. | 
 |             if (second >= 60) { minute++; second = 0; } | 
 |             if (minute >= 60) { hour++; minute = 0; } | 
 |             if (hour >= 24) { day++; hour = 0; } | 
 |  | 
 |             DateTime d = System.DateTime.Now; | 
 |             bool success= false; | 
 |             try | 
 |             { | 
 |                 d = new System.DateTime(year, month, day, hour, minute, second, 0); | 
 |                 success= true; | 
 |             } | 
 |             catch (System.ArgumentOutOfRangeException) | 
 |             { | 
 |                 if (year == 1980 && (month == 0 || day == 0)) | 
 |                 { | 
 |                     try | 
 |                     { | 
 |                         d = new System.DateTime(1980, 1, 1, hour, minute, second, 0); | 
 |                 success= true; | 
 |                     } | 
 |                     catch (System.ArgumentOutOfRangeException) | 
 |                     { | 
 |                         try | 
 |                         { | 
 |                             d = new System.DateTime(1980, 1, 1, 0, 0, 0, 0); | 
 |                 success= true; | 
 |                         } | 
 |                         catch (System.ArgumentOutOfRangeException) { } | 
 |  | 
 |                     } | 
 |                 } | 
 |                 // workitem 8814 | 
 |                 // my god, I can't believe how many different ways applications | 
 |                 // can mess up a simple date format. | 
 |                 else | 
 |                 { | 
 |                     try | 
 |                     { | 
 |                         while (year < 1980) year++; | 
 |                         while (year > 2030) year--; | 
 |                         while (month < 1) month++; | 
 |                         while (month > 12) month--; | 
 |                         while (day < 1) day++; | 
 |                         while (day > 28) day--; | 
 |                         while (minute < 0) minute++; | 
 |                         while (minute > 59) minute--; | 
 |                         while (second < 0) second++; | 
 |                         while (second > 59) second--; | 
 |                         d = new System.DateTime(year, month, day, hour, minute, second, 0); | 
 |                         success= true; | 
 |                     } | 
 |                     catch (System.ArgumentOutOfRangeException) { } | 
 |                 } | 
 |             } | 
 |             if (!success) | 
 |             { | 
 |                 string msg = String.Format("y({0}) m({1}) d({2}) h({3}) m({4}) s({5})", year, month, day, hour, minute, second); | 
 |                 throw new ZipException(String.Format("Bad date/time format in the zip file. ({0})", msg)); | 
 |  | 
 |             } | 
 |             // workitem 6191 | 
 |             //d = AdjustTime_Reverse(d); | 
 |             d = DateTime.SpecifyKind(d, DateTimeKind.Local); | 
 |             return d; | 
 |         } | 
 |  | 
 |  | 
 |         internal | 
 |          static Int32 DateTimeToPacked(DateTime time) | 
 |         { | 
 |             // The time is passed in here only for purposes of writing LastModified to the | 
 |             // zip archive. It should always be LocalTime, but we convert anyway.  And, | 
 |             // since the time is being written out, it needs to be adjusted. | 
 |  | 
 |             time = time.ToLocalTime(); | 
 |             // workitem 7966 | 
 |             //time = AdjustTime_Forward(time); | 
 |  | 
 |             // see http://www.vsft.com/hal/dostime.htm for the format | 
 |             UInt16 packedDate = (UInt16)((time.Day & 0x0000001F) | ((time.Month << 5) & 0x000001E0) | (((time.Year - 1980) << 9) & 0x0000FE00)); | 
 |             UInt16 packedTime = (UInt16)((time.Second / 2 & 0x0000001F) | ((time.Minute << 5) & 0x000007E0) | ((time.Hour << 11) & 0x0000F800)); | 
 |  | 
 |             Int32 result = (Int32)(((UInt32)(packedDate << 16)) | packedTime); | 
 |             return result; | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Create a pseudo-random filename, suitable for use as a temporary | 
 |         ///   file, and open it. | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// <para> | 
 |         ///   The System.IO.Path.GetRandomFileName() method is not available on | 
 |         ///   the Compact Framework, so this library provides its own substitute | 
 |         ///   on NETCF. | 
 |         /// </para> | 
 |         /// <para> | 
 |         ///   This method produces a filename of the form | 
 |         ///   DotNetZip-xxxxxxxx.tmp, where xxxxxxxx is replaced by randomly | 
 |         ///   chosen characters, and creates that file. | 
 |         /// </para> | 
 |         /// </remarks> | 
 |         public static void CreateAndOpenUniqueTempFile(string dir, | 
 |                                                        out Stream fs, | 
 |                                                        out string filename) | 
 |         { | 
 |             // workitem 9763 | 
 |             // http://dotnet.org.za/markn/archive/2006/04/15/51594.aspx | 
 |             // try 3 times: | 
 |             for (int i = 0; i < 3; i++) | 
 |             { | 
 |                 try | 
 |                 { | 
 |                     filename = Path.Combine(dir, InternalGetTempFileName()); | 
 |                     fs = new FileStream(filename, FileMode.CreateNew); | 
 |                     return; | 
 |                 } | 
 |                 catch (IOException) | 
 |                 { | 
 |                     if (i == 2) throw; | 
 |                 } | 
 |             } | 
 |             throw new IOException(); | 
 |         } | 
 |  | 
 | #if NETCF || SILVERLIGHT | 
 |         public static string InternalGetTempFileName() | 
 |         { | 
 |             return "DotNetZip-" + GenerateRandomStringImpl(8,0) + ".tmp"; | 
 |         } | 
 |  | 
 |         internal static string GenerateRandomStringImpl(int length, int delta) | 
 |         { | 
 |             bool WantMixedCase = (delta == 0); | 
 |             System.Random rnd = new System.Random(); | 
 |  | 
 |             string result = ""; | 
 |             char[] a = new char[length]; | 
 |  | 
 |             for (int i = 0; i < length; i++) | 
 |             { | 
 |                // delta == 65 means uppercase | 
 |                // delta == 97 means lowercase | 
 |                 if (WantMixedCase) | 
 |                     delta = (rnd.Next(2) == 0) ? 65 : 97; | 
 |                 a[i] = (char)(rnd.Next(26) + delta); | 
 |             } | 
 |  | 
 |             result = new System.String(a); | 
 |             return result; | 
 |         } | 
 | #else | 
 |         public static string InternalGetTempFileName() | 
 |         { | 
 |             return "DotNetZip-" + Path.GetRandomFileName().Substring(0, 8) + ".tmp"; | 
 |         } | 
 |  | 
 | #endif | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Workitem 7889: handle ERROR_LOCK_VIOLATION during read | 
 |         /// </summary> | 
 |         /// <remarks> | 
 |         /// This could be gracefully handled with an extension attribute, but | 
 |         /// This assembly is built for .NET 2.0, so I cannot use them. | 
 |         /// </remarks> | 
 |         internal static int ReadWithRetry(System.IO.Stream s, byte[] buffer, int offset, int count, string FileName) | 
 |         { | 
 |             int n = 0; | 
 |             bool done = false; | 
 | #if !NETCF && !SILVERLIGHT | 
 |             int retries = 0; | 
 | #endif | 
 |             do | 
 |             { | 
 |                 try | 
 |                 { | 
 |                     n = s.Read(buffer, offset, count); | 
 |                     done = true; | 
 |                 } | 
 | #if NETCF || SILVERLIGHT | 
 |                 catch (System.IO.IOException) | 
 |                 { | 
 |                     throw; | 
 |                 } | 
 | #else | 
 |                 catch (System.IO.IOException ioexc1) | 
 |                 { | 
 |                     // Check if we can call GetHRForException, | 
 |                     // which makes unmanaged code calls. | 
 |                     var p = new SecurityPermission(SecurityPermissionFlag.UnmanagedCode); | 
 |                     if (p.IsUnrestricted()) | 
 |                     { | 
 |                         uint hresult = _HRForException(ioexc1); | 
 |                         if (hresult != 0x80070021)  // ERROR_LOCK_VIOLATION | 
 |                             throw new System.IO.IOException(String.Format("Cannot read file {0}", FileName), ioexc1); | 
 |                         retries++; | 
 |                         if (retries > 10) | 
 |                             throw new System.IO.IOException(String.Format("Cannot read file {0}, at offset 0x{1:X8} after 10 retries", FileName, offset), ioexc1); | 
 |  | 
 |                         // max time waited on last retry = 250 + 10*550 = 5.75s | 
 |                         // aggregate time waited after 10 retries: 250 + 55*550 = 30.5s | 
 |                         System.Threading.Thread.Sleep(250 + retries * 550); | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         // The permission.Demand() failed. Therefore, we cannot call | 
 |                         // GetHRForException, and cannot do the subtle handling of | 
 |                         // ERROR_LOCK_VIOLATION.  Just bail. | 
 |                         throw; | 
 |                     } | 
 |                 } | 
 | #endif | 
 |             } | 
 |             while (!done); | 
 |  | 
 |             return n; | 
 |         } | 
 |  | 
 |  | 
 | #if !NETCF | 
 |         // workitem 8009 | 
 |         // | 
 |         // This method must remain separate. | 
 |         // | 
 |         // Marshal.GetHRForException() is needed to do special exception handling for | 
 |         // the read.  But, that method requires UnmanagedCode permissions, and is marked | 
 |         // with LinkDemand for UnmanagedCode.  In an ASP.NET medium trust environment, | 
 |         // where UnmanagedCode is restricted, will generate a SecurityException at the | 
 |         // time of JIT of the method that calls a method that is marked with LinkDemand | 
 |         // for UnmanagedCode. The SecurityException, if it is restricted, will occur | 
 |         // when this method is JITed. | 
 |         // | 
 |         // The Marshal.GetHRForException() is factored out of ReadWithRetry in order to | 
 |         // avoid the SecurityException at JIT compile time. Because _HRForException is | 
 |         // called only when the UnmanagedCode is allowed.  This means .NET never | 
 |         // JIT-compiles this method when UnmanagedCode is disallowed, and thus never | 
 |         // generates the JIT-compile time exception. | 
 |         // | 
 | #endif | 
 |         private static uint _HRForException(System.Exception ex1) | 
 |         { | 
 |             return unchecked((uint)System.Runtime.InteropServices.Marshal.GetHRForException(ex1)); | 
 |         } | 
 |  | 
 |     } | 
 |  | 
 |  | 
 |  | 
 |     /// <summary> | 
 |     ///   A decorator stream. It wraps another stream, and performs bookkeeping | 
 |     ///   to keep track of the stream Position. | 
 |     /// </summary> | 
 |     /// <remarks> | 
 |     ///   <para> | 
 |     ///     In some cases, it is not possible to get the Position of a stream, let's | 
 |     ///     say, on a write-only output stream like ASP.NET's | 
 |     ///     <c>Response.OutputStream</c>, or on a different write-only stream | 
 |     ///     provided as the destination for the zip by the application.  In this | 
 |     ///     case, programmers can use this counting stream to count the bytes read | 
 |     ///     or written. | 
 |     ///   </para> | 
 |     ///   <para> | 
 |     ///     Consider the scenario of an application that saves a self-extracting | 
 |     ///     archive (SFX), that uses a custom SFX stub. | 
 |     ///   </para> | 
 |     ///   <para> | 
 |     ///     Saving to a filesystem file, the application would open the | 
 |     ///     filesystem file (getting a <c>FileStream</c>), save the custom sfx stub | 
 |     ///     into it, and then call <c>ZipFile.Save()</c>, specifying the same | 
 |     ///     FileStream. <c>ZipFile.Save()</c> does the right thing for the zipentry | 
 |     ///     offsets, by inquiring the Position of the <c>FileStream</c> before writing | 
 |     ///     any data, and then adding that initial offset into any ZipEntry | 
 |     ///     offsets in the zip directory. Everything works fine. | 
 |     ///   </para> | 
 |     ///   <para> | 
 |     ///     Now suppose the application is an ASPNET application and it saves | 
 |     ///     directly to <c>Response.OutputStream</c>. It's not possible for DotNetZip to | 
 |     ///     inquire the <c>Position</c>, so the offsets for the SFX will be wrong. | 
 |     ///   </para> | 
 |     ///   <para> | 
 |     ///     The workaround is for the application to use this class to wrap | 
 |     ///     <c>HttpResponse.OutputStream</c>, then write the SFX stub and the ZipFile | 
 |     ///     into that wrapper stream. Because <c>ZipFile.Save()</c> can inquire the | 
 |     ///     <c>Position</c>, it will then do the right thing with the offsets. | 
 |     ///   </para> | 
 |     /// </remarks> | 
 |     internal class CountingStream : System.IO.Stream | 
 |     { | 
 |         // workitem 12374: this class is now public | 
 |         private System.IO.Stream _s; | 
 |         private Int64 _bytesWritten; | 
 |         private Int64 _bytesRead; | 
 |         private Int64 _initialOffset; | 
 |  | 
 |         /// <summary> | 
 |         /// The constructor. | 
 |         /// </summary> | 
 |         /// <param name="stream">The underlying stream</param> | 
 |         public CountingStream(System.IO.Stream stream) | 
 |             : base() | 
 |         { | 
 |             _s = stream; | 
 |             try | 
 |             { | 
 |                 _initialOffset = _s.Position; | 
 |             } | 
 |             catch | 
 |             { | 
 |                 _initialOffset = 0L; | 
 |             } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Gets the wrapped stream. | 
 |         /// </summary> | 
 |         public Stream WrappedStream | 
 |         { | 
 |             get | 
 |             { | 
 |                 return _s; | 
 |             } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   The count of bytes written out to the stream. | 
 |         /// </summary> | 
 |         public Int64 BytesWritten | 
 |         { | 
 |             get { return _bytesWritten; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   the count of bytes that have been read from the stream. | 
 |         /// </summary> | 
 |         public Int64 BytesRead | 
 |         { | 
 |             get { return _bytesRead; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///    Adjust the byte count on the stream. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <param name='delta'> | 
 |         ///   the number of bytes to subtract from the count. | 
 |         /// </param> | 
 |         /// | 
 |         /// <remarks> | 
 |         ///   <para> | 
 |         ///     Subtract delta from the count of bytes written to the stream. | 
 |         ///     This is necessary when seeking back, and writing additional data, | 
 |         ///     as happens in some cases when saving Zip files. | 
 |         ///   </para> | 
 |         /// </remarks> | 
 |         public void Adjust(Int64 delta) | 
 |         { | 
 |             _bytesWritten -= delta; | 
 |             if (_bytesWritten < 0) | 
 |                 throw new InvalidOperationException(); | 
 |             if (_s as CountingStream != null) | 
 |                 ((CountingStream)_s).Adjust(delta); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   The read method. | 
 |         /// </summary> | 
 |         /// <param name="buffer">The buffer to hold the data read from the stream.</param> | 
 |         /// <param name="offset">the offset within the buffer to copy the first byte read.</param> | 
 |         /// <param name="count">the number of bytes to read.</param> | 
 |         /// <returns>the number of bytes read, after decryption and decompression.</returns> | 
 |         public override int Read(byte[] buffer, int offset, int count) | 
 |         { | 
 |             int n = _s.Read(buffer, offset, count); | 
 |             _bytesRead += n; | 
 |             return n; | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Write data into the stream. | 
 |         /// </summary> | 
 |         /// <param name="buffer">The buffer holding data to write to the stream.</param> | 
 |         /// <param name="offset">the offset within that data array to find the first byte to write.</param> | 
 |         /// <param name="count">the number of bytes to write.</param> | 
 |         public override void Write(byte[] buffer, int offset, int count) | 
 |         { | 
 |             if (count == 0) return; | 
 |             _s.Write(buffer, offset, count); | 
 |             _bytesWritten += count; | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Whether the stream can be read. | 
 |         /// </summary> | 
 |         public override bool CanRead | 
 |         { | 
 |             get { return _s.CanRead; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Whether it is possible to call Seek() on the stream. | 
 |         /// </summary> | 
 |         public override bool CanSeek | 
 |         { | 
 |             get { return _s.CanSeek; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Whether it is possible to call Write() on the stream. | 
 |         /// </summary> | 
 |         public override bool CanWrite | 
 |         { | 
 |             get { return _s.CanWrite; } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Flushes the underlying stream. | 
 |         /// </summary> | 
 |         public override void Flush() | 
 |         { | 
 |             _s.Flush(); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   The length of the underlying stream. | 
 |         /// </summary> | 
 |         public override long Length | 
 |         { | 
 |             get { return _s.Length; }   // bytesWritten?? | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Returns the sum of number of bytes written, plus the initial | 
 |         ///   offset before writing. | 
 |         /// </summary> | 
 |         public long ComputedPosition | 
 |         { | 
 |             get { return _initialOffset + _bytesWritten; } | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   The Position of the stream. | 
 |         /// </summary> | 
 |         public override long Position | 
 |         { | 
 |             get { return _s.Position; } | 
 |             set | 
 |             { | 
 |                 _s.Seek(value, System.IO.SeekOrigin.Begin); | 
 |                 // workitem 10178 | 
 |                 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(_s); | 
 |             } | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Seek in the stream. | 
 |         /// </summary> | 
 |         /// <param name="offset">the offset point to seek to</param> | 
 |         /// <param name="origin">the reference point from which to seek</param> | 
 |         /// <returns>The new position</returns> | 
 |         public override long Seek(long offset, System.IO.SeekOrigin origin) | 
 |         { | 
 |             return _s.Seek(offset, origin); | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         ///   Set the length of the underlying stream.  Be careful with this! | 
 |         /// </summary> | 
 |         /// | 
 |         /// <param name='value'>the length to set on the underlying stream.</param> | 
 |         public override void SetLength(long value) | 
 |         { | 
 |             _s.SetLength(value); | 
 |         } | 
 |     } | 
 |  | 
 |  | 
 | } |