| // ZipEntry.Read.cs | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // Copyright (c) 2009-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-09 21:31:28> | 
 | // | 
 | // ------------------------------------------------------------------ | 
 | // | 
 | // This module defines logic for Reading the ZipEntry from a | 
 | // zip file. | 
 | // | 
 | // ------------------------------------------------------------------ | 
 |  | 
 |  | 
 | using System; | 
 | using System.IO; | 
 |  | 
 | namespace OfficeOpenXml.Packaging.Ionic.Zip | 
 | { | 
 |     internal partial class ZipEntry | 
 |     { | 
 |         private int _readExtraDepth; | 
 |         private void ReadExtraField() | 
 |         { | 
 |             _readExtraDepth++; | 
 |             // workitem 8098: ok (restore) | 
 |             long posn = this.ArchiveStream.Position; | 
 |             this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin); | 
 |             // workitem 10178 | 
 |             Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); | 
 |  | 
 |             byte[] block = new byte[30]; | 
 |             this.ArchiveStream.Read(block, 0, block.Length); | 
 |             int i = 26; | 
 |             Int16 filenameLength = (short)(block[i++] + block[i++] * 256); | 
 |             Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); | 
 |  | 
 |             // workitem 8098: ok (relative) | 
 |             this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current); | 
 |             // workitem 10178 | 
 |             Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); | 
 |  | 
 |             ProcessExtraField(this.ArchiveStream, extraFieldLength); | 
 |  | 
 |             // workitem 8098: ok (restore) | 
 |             this.ArchiveStream.Seek(posn, SeekOrigin.Begin); | 
 |             // workitem 10178 | 
 |             Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); | 
 |             _readExtraDepth--; | 
 |         } | 
 |  | 
 |  | 
 |         private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding) | 
 |         { | 
 |             int bytesRead = 0; | 
 |  | 
 |             // change for workitem 8098 | 
 |             ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position; | 
 |  | 
 |             int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream); | 
 |             bytesRead += 4; | 
 |  | 
 |             // Return false if this is not a local file header signature. | 
 |             if (ZipEntry.IsNotValidSig(signature)) | 
 |             { | 
 |                 // Getting "not a ZipEntry signature" is not always wrong or an error. | 
 |                 // This will happen after the last entry in a zipfile.  In that case, we | 
 |                 // expect to read : | 
 |                 //    a ZipDirEntry signature (if a non-empty zip file) or | 
 |                 //    a ZipConstants.EndOfCentralDirectorySignature. | 
 |                 // | 
 |                 // Anything else is a surprise. | 
 |  | 
 |                 ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature | 
 |                 // workitem 10178 | 
 |                 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); | 
 |                 if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature)) | 
 |                 { | 
 |                     throw new BadReadException(String.Format("  Bad signature (0x{0:X8}) at position  0x{1:X8}", signature, ze.ArchiveStream.Position)); | 
 |                 } | 
 |                 return false; | 
 |             } | 
 |  | 
 |             byte[] block = new byte[26]; | 
 |             int n = ze.ArchiveStream.Read(block, 0, block.Length); | 
 |             if (n != block.Length) return false; | 
 |             bytesRead += n; | 
 |  | 
 |             int i = 0; | 
 |             ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256); | 
 |             ze._BitField = (Int16)(block[i++] + block[i++] * 256); | 
 |             ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256); | 
 |             ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256; | 
 |             // transform the time data into something usable (a DateTime) | 
 |             ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob); | 
 |             ze._timestamp |= ZipEntryTimestamp.DOS; | 
 |  | 
 |             if ((ze._BitField & 0x01) == 0x01) | 
 |             { | 
 |                 ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field | 
 |                 ze._sourceIsEncrypted = true; | 
 |             } | 
 |  | 
 |             // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and | 
 |             // CRC values are not true values; the true values will follow the entry data. | 
 |             // But, regardless of the status of bit 3 in the bitfield, the slots for | 
 |             // the three amigos may contain marker values for ZIP64.  So we must read them. | 
 |             { | 
 |                 ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |                 ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |                 ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |  | 
 |                 if ((uint)ze._CompressedSize == 0xFFFFFFFF || | 
 |                     (uint)ze._UncompressedSize == 0xFFFFFFFF) | 
 |  | 
 |                     ze._InputUsesZip64 = true; | 
 |             } | 
 |  | 
 |             Int16 filenameLength = (short)(block[i++] + block[i++] * 256); | 
 |             Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256); | 
 |  | 
 |             block = new byte[filenameLength]; | 
 |             n = ze.ArchiveStream.Read(block, 0, block.Length); | 
 |             bytesRead += n; | 
 |  | 
 |             // if the UTF8 bit is set for this entry, override the | 
 |             // encoding the application requested. | 
 |  | 
 |             if ((ze._BitField & 0x0800) == 0x0800) | 
 |             { | 
 |                 // workitem 12744 | 
 |                 ze.AlternateEncoding = System.Text.Encoding.UTF8; | 
 |                 ze.AlternateEncodingUsage = ZipOption.Always; | 
 |             } | 
 |  | 
 |             // need to use this form of GetString() for .NET CF | 
 |             ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length); | 
 |  | 
 |             // workitem 6898 | 
 |             if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory(); | 
 |  | 
 |             bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength); | 
 |  | 
 |             ze._LengthOfTrailer = 0; | 
 |  | 
 |             // workitem 6607 - don't read for directories | 
 |             // actually get the compressed size and CRC if necessary | 
 |             if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008) | 
 |             { | 
 |                 // This descriptor exists only if bit 3 of the general | 
 |                 // purpose bit flag is set (see below).  It is byte aligned | 
 |                 // and immediately follows the last byte of compressed data, | 
 |                 // as well as any encryption trailer, as with AES. | 
 |                 // This descriptor is used only when it was not possible to | 
 |                 // seek in the output .ZIP file, e.g., when the output .ZIP file | 
 |                 // was standard output or a non-seekable device.  For ZIP64(tm) format | 
 |                 // archives, the compressed and uncompressed sizes are 8 bytes each. | 
 |  | 
 |                 // workitem 8098: ok (restore) | 
 |                 long posn = ze.ArchiveStream.Position; | 
 |  | 
 |                 // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and | 
 |                 // a consistent data record after that.   To be consistent, the data record must | 
 |                 // indicate the length of the entry data. | 
 |                 bool wantMore = true; | 
 |                 long SizeOfDataRead = 0; | 
 |                 int tries = 0; | 
 |                 while (wantMore) | 
 |                 { | 
 |                     tries++; | 
 |                     // We call the FindSignature shared routine to find the specified signature | 
 |                     // in the already-opened zip archive, starting from the current cursor | 
 |                     // position in that filestream.  If we cannot find the signature, then the | 
 |                     // routine returns -1, and the ReadHeader() method returns false, | 
 |                     // indicating we cannot read a legal entry header.  If we have found it, | 
 |                     // then the FindSignature() method returns the number of bytes in the | 
 |                     // stream we had to seek forward, to find the sig.  We need this to | 
 |                     // determine if the zip entry is valid, later. | 
 |  | 
 |                     if (ze._container.ZipFile != null) | 
 |                         ze._container.ZipFile.OnReadBytes(ze); | 
 |  | 
 |                     long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature); | 
 |                     if (d == -1) return false; | 
 |  | 
 |                     // total size of data read (through all loops of this). | 
 |                     SizeOfDataRead += d; | 
 |  | 
 |                     if (ze._InputUsesZip64) | 
 |                     { | 
 |                         // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size) | 
 |                         block = new byte[20]; | 
 |                         n = ze.ArchiveStream.Read(block, 0, block.Length); | 
 |                         if (n != 20) return false; | 
 |  | 
 |                         // do not increment bytesRead - it is for entry header only. | 
 |                         // the data we have just read is a footer (falls after the file data) | 
 |                         //bytesRead += n; | 
 |  | 
 |                         i = 0; | 
 |                         ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |                         ze._CompressedSize = BitConverter.ToInt64(block, i); | 
 |                         i += 8; | 
 |                         ze._UncompressedSize = BitConverter.ToInt64(block, i); | 
 |                         i += 8; | 
 |  | 
 |                         ze._LengthOfTrailer = 24;  // bytes including sig, CRC, Comp and Uncomp sizes | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size) | 
 |                         block = new byte[12]; | 
 |                         n = ze.ArchiveStream.Read(block, 0, block.Length); | 
 |                         if (n != 12) return false; | 
 |  | 
 |                         // do not increment bytesRead - it is for entry header only. | 
 |                         // the data we have just read is a footer (falls after the file data) | 
 |                         //bytesRead += n; | 
 |  | 
 |                         i = 0; | 
 |                         ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |                         ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |                         ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256); | 
 |  | 
 |                         ze._LengthOfTrailer = 16;  // bytes including sig, CRC, Comp and Uncomp sizes | 
 |  | 
 |                     } | 
 |  | 
 |                     wantMore = (SizeOfDataRead != ze._CompressedSize); | 
 |  | 
 |                     if (wantMore) | 
 |                     { | 
 |                         // Seek back to un-read the last 12 bytes  - maybe THEY contain | 
 |                         // the ZipEntryDataDescriptorSignature. | 
 |                         // (12 bytes for the CRC, Comp and Uncomp size.) | 
 |                         ze.ArchiveStream.Seek(-12, SeekOrigin.Current); | 
 |                         // workitem 10178 | 
 |                         Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); | 
 |  | 
 |                         // Adjust the size to account for the false signature read in | 
 |                         // FindSignature(). | 
 |                         SizeOfDataRead += 4; | 
 |                     } | 
 |                 } | 
 |  | 
 |                 // seek back to previous position, to prepare to read file data | 
 |                 // workitem 8098: ok (restore) | 
 |                 ze.ArchiveStream.Seek(posn, SeekOrigin.Begin); | 
 |                 // workitem 10178 | 
 |                 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream); | 
 |             } | 
 |  | 
 |             ze._CompressedFileDataSize = ze._CompressedSize; | 
 |  | 
 |  | 
 |             // bit 0 set indicates that some kind of encryption is in use | 
 |             if ((ze._BitField & 0x01) == 0x01) | 
 |             { | 
 | #if AESCRYPTO | 
 |                 if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 || | 
 |                     ze.Encryption == EncryptionAlgorithm.WinZipAes256) | 
 |                 { | 
 |                     int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile); | 
 |                     // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128. | 
 |                     ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream); | 
 |                     bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes) | 
 |                     // according to WinZip, the CompressedSize includes the AES Crypto framing data. | 
 |                     ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata; | 
 |                     ze._LengthOfTrailer += 10;  // MAC | 
 |                 } | 
 |                 else | 
 | #endif | 
 |                 { | 
 |                     // read in the header data for "weak" encryption | 
 |                     ze._WeakEncryptionHeader = new byte[12]; | 
 |                     bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader); | 
 |                     // decrease the filedata size by 12 bytes | 
 |                     ze._CompressedFileDataSize -= 12; | 
 |                 } | 
 |             } | 
 |  | 
 |             // Remember the size of the blob for this entry. | 
 |             // We also have the starting position in the stream for this entry. | 
 |             ze._LengthOfHeader = bytesRead; | 
 |             ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer; | 
 |  | 
 |  | 
 |             // We've read in the regular entry header, the extra field, and any | 
 |             // encryption header.  The pointer in the file is now at the start of the | 
 |             // filedata, which is potentially compressed and encrypted.  Just ahead in | 
 |             // the file, there are _CompressedFileDataSize bytes of data, followed by | 
 |             // potentially a non-zero length trailer, consisting of optionally, some | 
 |             // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24 | 
 |             // bytes). | 
 |  | 
 |             return true; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer) | 
 |         { | 
 |             // PKZIP encrypts the compressed data stream.  Encrypted files must | 
 |             // be decrypted before they can be extracted. | 
 |  | 
 |             // Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data | 
 |             // area defining the encryption header for that file.  The encryption header is | 
 |             // originally set to random values, and then itself encrypted, using three, 32-bit | 
 |             // keys.  The key values are initialized using the supplied encryption password. | 
 |             // After each byte is encrypted, the keys are then updated using pseudo-random | 
 |             // number generation techniques in combination with the same CRC-32 algorithm used | 
 |             // in PKZIP and implemented in the CRC32.cs module in this project. | 
 |  | 
 |             // read the 12-byte encryption header | 
 |             int additionalBytesRead = s.Read(buffer, 0, 12); | 
 |             if (additionalBytesRead != 12) | 
 |                 throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position)); | 
 |  | 
 |             return additionalBytesRead; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private static bool IsNotValidSig(int signature) | 
 |         { | 
 |             return (signature != ZipConstants.ZipEntrySignature); | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Reads one <c>ZipEntry</c> from the given stream.  The content for | 
 |         ///   the entry does not get decompressed or decrypted.  This method | 
 |         ///   basically reads metadata, and seeks. | 
 |         /// </summary> | 
 |         /// <param name="zc">the ZipContainer this entry belongs to.</param> | 
 |         /// <param name="first"> | 
 |         ///   true of this is the first entry being read from the stream. | 
 |         /// </param> | 
 |         /// <returns>the <c>ZipEntry</c> read from the stream.</returns> | 
 |         internal static ZipEntry ReadEntry(ZipContainer zc, bool first) | 
 |         { | 
 |             ZipFile zf = zc.ZipFile; | 
 |             Stream s = zc.ReadStream; | 
 |             System.Text.Encoding defaultEncoding = zc.AlternateEncoding; | 
 |             ZipEntry entry = new ZipEntry(); | 
 |             entry._Source = ZipEntrySource.ZipFile; | 
 |             entry._container = zc; | 
 |             entry._archiveStream = s; | 
 |             if (zf != null) | 
 |                 zf.OnReadEntry(true, null); | 
 |  | 
 |             if (first) HandlePK00Prefix(s); | 
 |  | 
 |             // Read entry header, including any encryption header | 
 |             if (!ReadHeader(entry, defaultEncoding)) return null; | 
 |  | 
 |             // Store the position in the stream for this entry | 
 |             // change for workitem 8098 | 
 |             entry.__FileDataPosition = entry.ArchiveStream.Position; | 
 |  | 
 |             // seek past the data without reading it. We will read on Extract() | 
 |             s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current); | 
 |             // workitem 10178 | 
 |             Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |  | 
 |             // ReadHeader moves the file pointer to the end of the entry header, | 
 |             // as well as any encryption header. | 
 |  | 
 |             // CompressedFileDataSize includes: | 
 |             //   the maybe compressed, maybe encrypted file data | 
 |             //   the encryption trailer, if any | 
 |             //   the bit 3 descriptor, if any | 
 |  | 
 |             // workitem 5306 | 
 |             // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306 | 
 |             HandleUnexpectedDataDescriptor(entry); | 
 |  | 
 |             if (zf != null) | 
 |             { | 
 |                 zf.OnReadBytes(entry); | 
 |                 zf.OnReadEntry(false, entry); | 
 |             } | 
 |  | 
 |             return entry; | 
 |         } | 
 |  | 
 |  | 
 |         internal static void HandlePK00Prefix(Stream s) | 
 |         { | 
 |             // in some cases, the zip file begins with "PK00".  This is a throwback and is rare, | 
 |             // but we handle it anyway. We do not change behavior based on it. | 
 |             uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s); | 
 |             if (datum != ZipConstants.PackedToRemovableMedia) | 
 |             { | 
 |                 s.Seek(-4, SeekOrigin.Current); // unread the block | 
 |                 // workitem 10178 | 
 |                 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private static void HandleUnexpectedDataDescriptor(ZipEntry entry) | 
 |         { | 
 |             Stream s = entry.ArchiveStream; | 
 |  | 
 |             // In some cases, the "data descriptor" is present, without a signature, even when | 
 |             // bit 3 of the BitField is NOT SET.  This is the CRC, followed | 
 |             //    by the compressed length and the uncompressed length (4 bytes for each | 
 |             //    of those three elements).  Need to check that here. | 
 |             // | 
 |             uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s); | 
 |             if (datum == entry._Crc32) | 
 |             { | 
 |                 int sz = Ionic.Zip.SharedUtilities.ReadInt(s); | 
 |                 if (sz == entry._CompressedSize) | 
 |                 { | 
 |                     sz = Ionic.Zip.SharedUtilities.ReadInt(s); | 
 |                     if (sz == entry._UncompressedSize) | 
 |                     { | 
 |                         // ignore everything and discard it. | 
 |                     } | 
 |                     else | 
 |                     { | 
 |                         s.Seek(-12, SeekOrigin.Current); // unread the three blocks | 
 |  | 
 |                         // workitem 10178 | 
 |                         Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |                     } | 
 |                 } | 
 |                 else | 
 |                 { | 
 |                     s.Seek(-8, SeekOrigin.Current); // unread the two blocks | 
 |  | 
 |                     // workitem 10178 | 
 |                     Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |                 } | 
 |             } | 
 |             else | 
 |             { | 
 |                 s.Seek(-4, SeekOrigin.Current); // unread the block | 
 |  | 
 |                 // workitem 10178 | 
 |                 Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s); | 
 |             } | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   Finds a particular segment in the given extra field. | 
 |         ///   This is used when modifying a previously-generated | 
 |         ///   extra field, in particular when removing the AES crypto | 
 |         ///   segment in the extra field. | 
 |         /// </summary> | 
 |         static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId) | 
 |         { | 
 |             int j = offx; | 
 |             while (j + 3 < extra.Length) | 
 |             { | 
 |                 UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256); | 
 |                 if (headerId == targetHeaderId) return j-2; | 
 |  | 
 |                 // else advance to next segment | 
 |                 Int16 dataSize = (short)(extra[j++] + extra[j++] * 256); | 
 |                 j+= dataSize; | 
 |             } | 
 |  | 
 |             return -1; | 
 |         } | 
 |  | 
 |  | 
 |         /// <summary> | 
 |         ///   At current cursor position in the stream, read the extra | 
 |         ///   field, and set the properties on the ZipEntry instance | 
 |         ///   appropriately.  This can be called when processing the | 
 |         ///   Extra field in the Central Directory, or in the local | 
 |         ///   header. | 
 |         /// </summary> | 
 |         internal int ProcessExtraField(Stream s, Int16 extraFieldLength) | 
 |         { | 
 |             int additionalBytesRead = 0; | 
 |             if (extraFieldLength > 0) | 
 |             { | 
 |                 byte[] buffer = this._Extra = new byte[extraFieldLength]; | 
 |                 additionalBytesRead = s.Read(buffer, 0, buffer.Length); | 
 |                 long posn = s.Position - additionalBytesRead; | 
 |                 int j = 0; | 
 |                 while (j + 3 < buffer.Length) | 
 |                 { | 
 |                     int start = j; | 
 |                     UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256); | 
 |                     Int16 dataSize = (short)(buffer[j++] + buffer[j++] * 256); | 
 |  | 
 |                     switch (headerId) | 
 |                     { | 
 |                         case 0x000a:  // NTFS ctime, atime, mtime | 
 |                             j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn); | 
 |                             break; | 
 |  | 
 |                         case 0x5455:  // Unix ctime, atime, mtime | 
 |                             j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn); | 
 |                             break; | 
 |  | 
 |                         case 0x5855:  // Info-zip Extra field (outdated) | 
 |                             // This is outdated, so the field is supported on | 
 |                             // read only. | 
 |                             j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn); | 
 |                             break; | 
 |  | 
 |                         case 0x7855:  // Unix uid/gid | 
 |                             // ignored. DotNetZip does not handle this field. | 
 |                             break; | 
 |  | 
 |                         case 0x7875:  // ?? | 
 |                             // ignored.  I could not find documentation on this field, | 
 |                             // though it appears in some zip files. | 
 |                             break; | 
 |  | 
 |                         case 0x0001: // ZIP64 | 
 |                             j = ProcessExtraFieldZip64(buffer, j, dataSize, posn); | 
 |                             break; | 
 |  | 
 | #if AESCRYPTO | 
 |                         case 0x9901: // WinZip AES encryption is in use.  (workitem 6834) | 
 |                             // we will handle this extra field only  if compressionmethod is 0x63 | 
 |                             j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn); | 
 |                             break; | 
 | #endif | 
 |                         case 0x0017: // workitem 7968: handle PKWare Strong encryption header | 
 |                             j = ProcessExtraFieldPkwareStrongEncryption(buffer, j); | 
 |                             break; | 
 |                     } | 
 |  | 
 |                     // move to the next Header in the extra field | 
 |                     j = start + dataSize + 4; | 
 |                 } | 
 |             } | 
 |             return additionalBytesRead; | 
 |         } | 
 |  | 
 |         private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j) | 
 |         { | 
 |             //           Value     Size     Description | 
 |             //           -----     ----     ----------- | 
 |             //           0x0017    2 bytes  Tag for this "extra" block type | 
 |             //           TSize     2 bytes  Size of data that follows | 
 |             //           Format    2 bytes  Format definition for this record | 
 |             //           AlgID     2 bytes  Encryption algorithm identifier | 
 |             //           Bitlen    2 bytes  Bit length of encryption key | 
 |             //           Flags     2 bytes  Processing flags | 
 |             //           CertData  TSize-8  Certificate decryption extra field data | 
 |             //                              (refer to the explanation for CertData | 
 |             //                               in the section describing the | 
 |             //                               Certificate Processing Method under | 
 |             //                               the Strong Encryption Specification) | 
 |  | 
 |             j += 2; | 
 |             _UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256); | 
 |             _Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported; | 
 |  | 
 |             // DotNetZip doesn't support this algorithm, but we don't need to throw | 
 |             // here.  we might just be reading the archive, which is fine.  We'll | 
 |             // need to throw if Extract() is called. | 
 |  | 
 |             return j; | 
 |         } | 
 |  | 
 |  | 
 | #if AESCRYPTO | 
 |         private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, Int16 dataSize, long posn) | 
 |         { | 
 |             if (this._CompressionMethod == 0x0063) | 
 |             { | 
 |                 if ((this._BitField & 0x01) != 0x01) | 
 |                     throw new BadReadException(String.Format("  Inconsistent metadata at position 0x{0:X16}", posn)); | 
 |  | 
 |                 this._sourceIsEncrypted = true; | 
 |  | 
 |                 //this._aesCrypto = new WinZipAesCrypto(this); | 
 |                 // see spec at http://www.winzip.com/aes_info.htm | 
 |                 if (dataSize != 7) | 
 |                     throw new BadReadException(String.Format("  Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn)); | 
 |  | 
 |                 this._WinZipAesMethod = BitConverter.ToInt16(buffer, j); | 
 |                 j += 2; | 
 |                 if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02) | 
 |                     throw new BadReadException(String.Format("  Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", | 
 |                         this._WinZipAesMethod, posn)); | 
 |  | 
 |                 Int16 vendorId = BitConverter.ToInt16(buffer, j); | 
 |                 j += 2; | 
 |                 if (vendorId != 0x4541) | 
 |                     throw new BadReadException(String.Format("  Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn)); | 
 |  | 
 |                 int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1; | 
 |                 if (keystrength < 0) | 
 |                     throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength)); | 
 |  | 
 |                 _Encryption_FromZipFile = this._Encryption = (keystrength == 128) | 
 |                     ? EncryptionAlgorithm.WinZipAes128 | 
 |                     : EncryptionAlgorithm.WinZipAes256; | 
 |  | 
 |                 j++; | 
 |  | 
 |                 // set the actual compression method | 
 |                 this._CompressionMethod_FromZipFile = | 
 |                 this._CompressionMethod = BitConverter.ToInt16(buffer, j); | 
 |                 j += 2; // for the next segment of the extra field | 
 |             } | 
 |             return j; | 
 |         } | 
 |  | 
 | #endif | 
 |  | 
 |         private delegate T Func<T>(); | 
 |  | 
 |         private int ProcessExtraFieldZip64(byte[] buffer, int j, Int16 dataSize, long posn) | 
 |         { | 
 |             // The PKWare spec says that any of {UncompressedSize, CompressedSize, | 
 |             // RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header, | 
 |             // and the ZIP64 header may contain one or more of those.  If the | 
 |             // values are present, they will be found in the prescribed order. | 
 |             // There may also be a 4-byte "disk start number." | 
 |             // This means that the DataSize must be 28 bytes or less. | 
 |  | 
 |             this._InputUsesZip64 = true; | 
 |  | 
 |             // workitem 7941: check datasize before reading. | 
 |             if (dataSize > 28) | 
 |                 throw new BadReadException(String.Format("  Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}", | 
 |                                                          dataSize, posn)); | 
 |             int remainingData = dataSize; | 
 |  | 
 |             var slurp = new Func<Int64>( () => { | 
 |                     if (remainingData < 8) | 
 |                         throw new BadReadException(String.Format("  Missing data for ZIP64 extra field, position 0x{0:X16}", posn)); | 
 |                     var x = BitConverter.ToInt64(buffer, j); | 
 |                     j+= 8; | 
 |                     remainingData -= 8; | 
 |                     return x; | 
 |                 }); | 
 |  | 
 |             if (this._UncompressedSize == 0xFFFFFFFF) | 
 |                 this._UncompressedSize = slurp(); | 
 |  | 
 |             if (this._CompressedSize == 0xFFFFFFFF) | 
 |                 this._CompressedSize = slurp(); | 
 |  | 
 |             if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF) | 
 |                 this._RelativeOffsetOfLocalHeader = slurp(); | 
 |  | 
 |             // Ignore anything else. Potentially there are 4 more bytes for the | 
 |             // disk start number.  DotNetZip currently doesn't handle multi-disk | 
 |             // archives. | 
 |             return j; | 
 |         } | 
 |  | 
 |  | 
 |         private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, Int16 dataSize, long posn) | 
 |         { | 
 |             if (dataSize != 12 && dataSize != 8) | 
 |                 throw new BadReadException(String.Format("  Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn)); | 
 |  | 
 |             Int32 timet = BitConverter.ToInt32(buffer, j); | 
 |             this._Mtime = _unixEpoch.AddSeconds(timet); | 
 |             j += 4; | 
 |  | 
 |             timet = BitConverter.ToInt32(buffer, j); | 
 |             this._Atime = _unixEpoch.AddSeconds(timet); | 
 |             j += 4; | 
 |  | 
 |             this._Ctime = DateTime.UtcNow; | 
 |  | 
 |             _ntfsTimesAreSet = true; | 
 |             _timestamp |= ZipEntryTimestamp.InfoZip1; return j; | 
 |         } | 
 |  | 
 |  | 
 |  | 
 |         private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, Int16 dataSize, long posn) | 
 |         { | 
 |             // The Unix filetimes are 32-bit unsigned integers, | 
 |             // storing seconds since Unix epoch. | 
 |  | 
 |             if (dataSize != 13 && dataSize != 9 && dataSize != 5) | 
 |                 throw new BadReadException(String.Format("  Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn)); | 
 |  | 
 |             int remainingData = dataSize; | 
 |  | 
 |             var slurp = new Func<DateTime>( () => { | 
 |                     Int32 timet = BitConverter.ToInt32(buffer, j); | 
 |                     j += 4; | 
 |                     remainingData -= 4; | 
 |                     return _unixEpoch.AddSeconds(timet); | 
 |                 }); | 
 |  | 
 |             if (dataSize == 13 || _readExtraDepth > 0) | 
 |             { | 
 |                 byte flag = buffer[j++]; | 
 |                 remainingData--; | 
 |  | 
 |                 if ((flag & 0x0001) != 0 && remainingData >= 4) | 
 |                     this._Mtime = slurp(); | 
 |  | 
 |                  this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4) | 
 |                      ? slurp() | 
 |                      : DateTime.UtcNow; | 
 |  | 
 |                  this._Ctime =  ((flag & 0x0004) != 0 && remainingData >= 4) | 
 |                      ? slurp() | 
 |                      :DateTime.UtcNow; | 
 |  | 
 |                 _timestamp |= ZipEntryTimestamp.Unix; | 
 |                 _ntfsTimesAreSet = true; | 
 |                 _emitUnixTimes = true; | 
 |             } | 
 |             else | 
 |                 ReadExtraField(); // will recurse | 
 |  | 
 |             return j; | 
 |         } | 
 |  | 
 |  | 
 |         private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, Int16 dataSize, long posn) | 
 |         { | 
 |             // The NTFS filetimes are 64-bit unsigned integers, stored in Intel | 
 |             // (least significant byte first) byte order. They are expressed as the | 
 |             // number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch", | 
 |             // which is "01-Jan-1601 00:00:00 UTC". | 
 |             // | 
 |             // HeaderId   2 bytes    0x000a == NTFS stuff | 
 |             // Datasize   2 bytes    ?? (usually 32) | 
 |             // reserved   4 bytes    ?? | 
 |             // timetag    2 bytes    0x0001 == time | 
 |             // size       2 bytes    24 == 8 bytes each for ctime, mtime, atime | 
 |             // mtime      8 bytes    win32 ticks since win32epoch | 
 |             // atime      8 bytes    win32 ticks since win32epoch | 
 |             // ctime      8 bytes    win32 ticks since win32epoch | 
 |  | 
 |             if (dataSize != 32) | 
 |                 throw new BadReadException(String.Format("  Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn)); | 
 |  | 
 |             j += 4;  // reserved | 
 |             Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256); | 
 |             Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256); | 
 |             j += 4;  // tag and size | 
 |  | 
 |             if (timetag == 0x0001 && addlsize == 24) | 
 |             { | 
 |                 Int64 z = BitConverter.ToInt64(buffer, j); | 
 |                 this._Mtime = DateTime.FromFileTimeUtc(z); | 
 |                 j += 8; | 
 |  | 
 |                 // At this point the library *could* set the LastModified value | 
 |                 // to coincide with the Mtime value.  In theory, they refer to | 
 |                 // the same property of the file, and should be the same anyway, | 
 |                 // allowing for differences in precision.  But they are | 
 |                 // independent quantities in the zip archive, and this library | 
 |                 // will keep them separate in the object model. There is no ill | 
 |                 // effect from this, because as files are extracted, the | 
 |                 // higher-precision value (Mtime) is used if it is present. | 
 |                 // Apps may wish to compare the Mtime versus LastModified | 
 |                 // values, but any difference when both are present is not | 
 |                 // germaine to the correctness of the library. but note: when | 
 |                 // explicitly setting either value, both are set. See the setter | 
 |                 // for LastModified or the SetNtfsTimes() method. | 
 |  | 
 |                 z = BitConverter.ToInt64(buffer, j); | 
 |                 this._Atime = DateTime.FromFileTimeUtc(z); | 
 |                 j += 8; | 
 |  | 
 |                 z = BitConverter.ToInt64(buffer, j); | 
 |                 this._Ctime = DateTime.FromFileTimeUtc(z); | 
 |                 j += 8; | 
 |  | 
 |                 _ntfsTimesAreSet = true; | 
 |                 _timestamp |= ZipEntryTimestamp.Windows; | 
 |                 _emitNtfsTimes = true; | 
 |             } | 
 |             return j; | 
 |         } | 
 |  | 
 |  | 
 |     } | 
 | } |