context: // 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 Crisis.Ionic.Zip
  {
      public 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
              Crisis.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
              Crisis.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  
              ProcessExtraField(this.ArchiveStream, extraFieldLength);
  
              // workitem 8098: ok (restore)
              this.ArchiveStream.Seek(posn, SeekOrigin.Begin);
              // workitem 10178
              Crisis.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 = Crisis.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
                  Crisis.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 = Crisis.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 = Crisis.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
                          Crisis.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
                  Crisis.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
              Crisis.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.
  
gt:             uint datum = (uint)Crisis.Ionic.Zip.SharedUtilities.ReadInt(s);