| // ZipDirEntry.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 (in emacs): | 
 | // Time-stamp: <2011-July-11 12:03:03> | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This module defines members of the ZipEntry class for reading the | 
 | // Zip file central directory. | 
 | // | 
 | // Created: Tue, 27 Mar 2007  15:30 | 
 | // | 
 | // ------------------------------------------------------------------ | 
 |  | 
 |  | 
 | using System; | 
 | using System.Collections.Generic; | 
 |  | 
 | namespace OfficeOpenXml.Packaging.Ionic.Zip | 
 | { | 
 |  | 
 |     partial class ZipEntry | 
 |     { | 
 |         /// <summary> | 
 |         /// True if the referenced entry is a directory. | 
 |         /// </summary> | 
 |         internal bool AttributesIndicateDirectory | 
 |         { | 
 |             get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); } | 
 |         } | 
 |  | 
 |  | 
 |         internal void ResetDirEntry() | 
 |         { | 
 |             // __FileDataPosition is the position of the file data for an entry. | 
 |             // It is _RelativeOffsetOfLocalHeader + size of local header. | 
 |  | 
 |             // We cannot know the __FileDataPosition until we read the local | 
 |             // header. | 
 |  | 
 |             // The local header is not necessarily the same length as the record | 
 |             // in the central directory. | 
 |  | 
 |             // Set to -1, to indicate we need to read this later. | 
 |             this.__FileDataPosition = -1; | 
 |  | 
 |             // set _LengthOfHeader to 0, to indicate we need to read later. | 
 |             this._LengthOfHeader = 0; | 
 |         } | 
 |  | 
 |         /// <summary> | 
 |         /// Provides a human-readable string with information about the ZipEntry. | 
 |         /// </summary> | 
 |         public string Info | 
 |         { | 
 |             get | 
 |             { | 
 |                 var builder = new System.Text.StringBuilder(); | 
 |                 builder | 
 |                     .Append(string.Format("          ZipEntry: {0}\n", this.FileName)) | 
 |                     .Append(string.Format("   Version Made By: {0}\n", this._VersionMadeBy)) | 
 |                     .Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded)); | 
 |  | 
 |                 if (this._IsDirectory) | 
 |                     builder.Append("        Entry type: directory\n"); | 
 |                 else | 
 |                 { | 
 |                     builder.Append(string.Format("         File type: {0}\n", this._IsText? "text":"binary")) | 
 |                         .Append(string.Format("       Compression: {0}\n", this.CompressionMethod)) | 
 |                         .Append(string.Format("        Compressed: 0x{0:X}\n", this.CompressedSize)) | 
 |                         .Append(string.Format("      Uncompressed: 0x{0:X}\n", this.UncompressedSize)) | 
 |                         .Append(string.Format("             CRC32: 0x{0:X8}\n", this._Crc32)); | 
 |                 } | 
 |                 builder.Append(string.Format("       Disk Number: {0}\n", this._diskNumber)); | 
 |                 if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF) | 
 |                     builder | 
 |                         .Append(string.Format("   Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader)); | 
 |                         else | 
 |                     builder | 
 |                         .Append(string.Format("   Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader)); | 
 |  | 
 |                     builder | 
 |                     .Append(string.Format("         Bit Field: 0x{0:X4}\n", this._BitField)) | 
 |                     .Append(string.Format("        Encrypted?: {0}\n", this._sourceIsEncrypted)) | 
 |                     .Append(string.Format("          Timeblob: 0x{0:X8}\n", this._TimeBlob)) | 
 |                         .Append(string.Format("              Time: {0}\n", Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob))); | 
 |  | 
 |                 builder.Append(string.Format("         Is Zip64?: {0}\n", this._InputUsesZip64)); | 
 |                 if (!string.IsNullOrEmpty(this._Comment)) | 
 |                 { | 
 |                     builder.Append(string.Format("           Comment: {0}\n", this._Comment)); | 
 |                 } | 
 |                 builder.Append("\n"); | 
 |                 return builder.ToString(); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         // workitem 10330 | 
 |         private class CopyHelper | 
 |         { | 
 |             private static System.Text.RegularExpressions.Regex re = | 
 |                 new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$"); | 
 |  | 
 |             private static int callCount = 0; | 
 |  | 
 |             internal static string AppendCopyToFileName(string f) | 
 |             { | 
 |                 callCount++; | 
 |                 if (callCount > 25) | 
 |                     throw new OverflowException("overflow while creating filename"); | 
 |  | 
 |                 int n = 1; | 
 |                 int r = f.LastIndexOf("."); | 
 |  | 
 |                 if (r == -1) | 
 |                 { | 
 |                     // there is no extension | 
 |                     System.Text.RegularExpressions.Match m = re.Match(f); | 
 |                     if (m.Success) | 
 |                     { | 
 |                         n = Int32.Parse(m.Groups[1].Value) + 1; | 
 |                         string copy = String.Format(" (copy {0})", n); | 
 |                         f = f.Substring(0, m.Index) + copy; | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         string copy = String.Format(" (copy {0})", n); | 
 |                         f = f + copy; | 
 |                     } | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     //System.Console.WriteLine("HasExtension"); | 
 |                     System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r)); | 
 |                     if (m.Success) | 
 |                     { | 
 |                         n = Int32.Parse(m.Groups[1].Value) + 1; | 
 |                         string copy = String.Format(" (copy {0})", n); | 
 |                         f = f.Substring(0, m.Index) + copy + f.Substring(r); | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         string copy = String.Format(" (copy {0})", n); | 
 |                         f = f.Substring(0, r) + copy + f.Substring(r); | 
 |                     } | 
 |  | 
 |                     //System.Console.WriteLine("returning f({0})", f); | 
 |                 } | 
 |                 return f; | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Reads one entry from the zip directory structure in the zip file. | 
 |         /// </summary> | 
 |         /// | 
 |         /// <param name="zf"> | 
 |         ///   The zipfile for which a directory entry will be read.  From this param, the | 
 |         ///   method gets the ReadStream and the expected text encoding | 
 |         ///   (ProvisionalAlternateEncoding) which is used if the entry is not marked | 
 |         ///   UTF-8. | 
 |         /// </param> | 
 |         /// | 
 |         /// <param name="previouslySeen"> | 
 |         ///   a list of previously seen entry names; used to prevent duplicates. | 
 |         /// </param> | 
 |         /// | 
 |         /// <returns>the entry read from the archive.</returns> | 
 |         internal static ZipEntry ReadDirEntry(ZipFile zf, | 
 |                                               Dictionary<String,Object> previouslySeen) | 
 |         { | 
 |             System.IO.Stream s = zf.ReadStream; | 
 |             System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always) | 
 |                 ? zf.AlternateEncoding | 
 |                 : ZipFile.DefaultEncoding; | 
 |  | 
 |             int signature = Ionic.Zip.SharedUtilities.ReadSignature(s); | 
 |             // return null if this is not a local file header signature | 
 |             if (IsNotValidZipDirEntrySig(signature)) | 
 |             { | 
 |                 s.Seek(-4, System.IO.SeekOrigin.Current); | 
 |                 // workitem 10178 | 
 |                 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |  | 
 |                 // Getting "not a ZipDirEntry signature" here is not always wrong or an | 
 |                 // error.  This can happen when walking through a zipfile.  After the | 
 |                 // last ZipDirEntry, we expect to read an | 
 |                 // EndOfCentralDirectorySignature.  When we get this is how we know | 
 |                 // we've reached the end of the central directory. | 
 |                 if (signature != ZipConstants.EndOfCentralDirectorySignature && | 
 |                     signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature && | 
 |                     signature != ZipConstants.ZipEntrySignature  // workitem 8299 | 
 |                     ) | 
 |                 { | 
 |                     throw new BadReadException(String.Format("  Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position)); | 
 |                 } | 
 |                 return null; | 
 |             } | 
 |  | 
 |             int bytesRead = 42 + 4; | 
 |             byte[] block = new byte[42]; | 
 |             int n = s.Read(block, 0, block.Length); | 
 |             if (n != block.Length) return null; | 
 |  | 
 |             int i = 0; | 
 |             ZipEntry zde = new ZipEntry(); | 
 |             zde.AlternateEncoding = expectedEncoding; | 
 |             zde._Source = ZipEntrySource.ZipFile; | 
 |             zde._container = new ZipContainer(zf); | 
 |  | 
 |             unchecked | 
 |             { | 
 |                 zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256); | 
 |                 zde._VersionNeeded = (short)(block[i++] + block[i++] * 256); | 
 |                 zde._BitField = (short)(block[i++] + block[i++] * 256); | 
 |                 zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); | 
 |                 zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; | 
 |                 zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob); | 
 |                 zde._timestamp |= ZipEntryTimestamp.DOS; | 
 |  | 
 |                 zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; | 
 |                 zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |                 zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |             } | 
 |  | 
 |             // preserve | 
 |             zde._CompressionMethod_FromZipFile = zde._CompressionMethod; | 
 |  | 
 |             zde._filenameLength = (short)(block[i++] + block[i++] * 256); | 
 |             zde._extraFieldLength = (short)(block[i++] + block[i++] * 256); | 
 |             zde._commentLength = (short)(block[i++] + block[i++] * 256); | 
 |             zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256); | 
 |  | 
 |             zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256); | 
 |             zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; | 
 |  | 
 |             zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |  | 
 |             // workitem 7801 | 
 |             zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01); | 
 |  | 
 |             block = new byte[zde._filenameLength]; | 
 |             n = s.Read(block, 0, block.Length); | 
 |             bytesRead += n; | 
 |             if ((zde._BitField & 0x0800) == 0x0800) | 
 |             { | 
 |                 // UTF-8 is in use | 
 |                 zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); | 
 |             } | 
 |             else | 
 |             { | 
 |                 zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); | 
 |             } | 
 |  | 
 |             // workitem 10330 | 
 |             // insure unique entry names | 
 |             while (previouslySeen.ContainsKey(zde._FileNameInArchive)) | 
 |             { | 
 |                 zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive); | 
 |                 zde._metadataChanged = true; | 
 |             } | 
 |  | 
 |             if (zde.AttributesIndicateDirectory) | 
 |                 zde.MarkAsDirectory();  // may append a slash to filename if nec. | 
 |             // workitem 6898 | 
 |             else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory(); | 
 |  | 
 |             zde._CompressedFileDataSize = zde._CompressedSize; | 
 |             if ((zde._BitField & 0x01) == 0x01) | 
 |             { | 
 |                 // this may change after processing the Extra field | 
 |                 zde._Encryption_FromZipFile = zde._Encryption = | 
 |                     EncryptionAlgorithm.PkzipWeak; | 
 |                 zde._sourceIsEncrypted = true; | 
 |             } | 
 |  | 
 |             if (zde._extraFieldLength > 0) | 
 |             { | 
 |                 zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF || | 
 |                       zde._UncompressedSize == 0xFFFFFFFF || | 
 |                       zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF); | 
 |  | 
 |                 // Console.WriteLine("  Input uses Z64?:      {0}", zde._InputUsesZip64); | 
 |  | 
 |                 bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength); | 
 |                 zde._CompressedFileDataSize = zde._CompressedSize; | 
 |             } | 
 |  | 
 |             // we've processed the extra field, so we know the encryption method is set now. | 
 |             if (zde._Encryption == EncryptionAlgorithm.PkzipWeak) | 
 |             { | 
 |                 // the "encryption header" of 12 bytes precedes the file data | 
 |                 zde._CompressedFileDataSize -= 12; | 
 |             } | 
 | #if AESCRYPTO | 
 |             else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                         zde.Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |             { | 
 |                 zde._CompressedFileDataSize = zde.CompressedSize - | 
 |                     (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10); | 
 |                 zde._LengthOfTrailer = 10; | 
 |             } | 
 | #endif | 
 |  | 
 |             // tally the trailing descriptor | 
 |             if ((zde._BitField & 0x0008) == 0x0008) | 
 |             { | 
 |                 // sig, CRC, Comp and Uncomp sizes | 
 |                 if (zde._InputUsesZip64) | 
 |                     zde._LengthOfTrailer += 24; | 
 |                 else | 
 |                     zde._LengthOfTrailer += 16; | 
 |             } | 
 |  | 
 |             // workitem 12744 | 
 |             zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800) | 
 |                 ? System.Text.Encoding.UTF8 | 
 |                 :expectedEncoding; | 
 |  | 
 |             zde.AlternateEncodingUsage = ZipOption.Always; | 
 |  | 
 |             if (zde._commentLength > 0) | 
 |             { | 
 |                 block = new byte[zde._commentLength]; | 
 |                 n = s.Read(block, 0, block.Length); | 
 |                 bytesRead += n; | 
 |                 if ((zde._BitField & 0x0800) == 0x0800) | 
 |                 { | 
 |                     // UTF-8 is in use | 
 |                     zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block); | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding); | 
 |                 } | 
 |             } | 
 |             //zde._LengthOfDirEntry = bytesRead; | 
 |             return zde; | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         /// Returns true if the passed-in value is a valid signature for a ZipDirEntry. | 
 |         /// </summary> | 
 |         /// <param name="signature">the candidate 4-byte signature value.</param> | 
 |         /// <returns>true, if the signature is valid according to the PKWare spec.</returns> | 
 |         internal static bool IsNotValidZipDirEntrySig(int signature) | 
 |         { | 
 |             return (signature != ZipConstants.ZipDirEntrySignature); | 
 |         } | 
 |  | 
 |  | 
 |         private Int16 _VersionMadeBy; | 
 |         private Int16 _InternalFileAttrs; | 
 |         private Int32 _ExternalFileAttrs; | 
 |  | 
 |         //private Int32 _LengthOfDirEntry; | 
 |         private Int16 _filenameLength; | 
 |         private Int16 _extraFieldLength; | 
 |         private Int16 _commentLength; | 
 |     } | 
 |  | 
 |  | 
 | } |