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