|  | // 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); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | } |