Example #1
0
        private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
        {
            int bytesRead = 0;

            ze._RelativeOffsetOfHeader = (int)ze.ArchiveStream.Position;

            int signature = Zip.SharedUtilities.ReadSignature(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, System.IO.SeekOrigin.Current); // unread the signature
                if (ZipDirEntry.IsNotValidSig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
                {
                    throw new BadReadException(String.Format("  ZipEntry::ReadHeader(): 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 = (short)(block[i++] + block[i++] * 256);
            ze._BitField = (short)(block[i++] + block[i++] * 256);
            ze._CompressionMethod = (short)(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 = Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);

            // The PKZIP spec says that if bit 3 is set (0x0008) in the General Purpose BitField, then the CRC,
            // Compressed size, and uncompressed size come directly after the file data.  The only way to find
            // it is to scan the zip archive for the signature of the Data Descriptor, and presume that that
            // signature does not appear in the (compressed) data of the compressed file.

            if ((ze._BitField & 0x0008) != 0x0008)
            {
                ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
                ze._CompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
                ze._UncompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
            }
            else
            {
                // The CRC, compressed size, and uncompressed size are stored later in the stream.
                // Here, we advance the pointer.
                i += 12;
            }

            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 ((ze._BitField & 0x0800) == 0x0800)
            {
                ze._FileNameInArchive = Zip.SharedUtilities.StringFromBuffer(block, block.Length, System.Text.Encoding.UTF8);
                ze.UseUtf8Encoding = true;
            }
            else
            {
                ze._FileNameInArchive = Zip.SharedUtilities.StringFromBuffer(block, block.Length, defaultEncoding);
                ze._encoding = defaultEncoding;
            }

            // when creating an entry by reading, the LocalFileName is the same as the FileNameInArchivre
            ze._LocalFileName = ze._FileNameInArchive;

            if (extraFieldLength > 0)
            {
                ze._Extra = new byte[extraFieldLength];
                n = ze.ArchiveStream.Read(ze._Extra, 0, ze._Extra.Length);
                bytesRead += n;
            }

            // workitem 6607: don't seek if it is a directory.
            if (!ze.FileName.EndsWith("/"))
            // actually get the compressed size and CRC if necessary
            if ((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.
                // 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.

                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.  There are two possibilities:  either we find the
                    // signature or we don't.  If we cannot find it, 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.

                    long d = Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature);
                    if (d == -1) return false;

                    // total size of data read (through all loops of this).
                    SizeOfDataRead += d;

                    // 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;
                    bytesRead += n;
                    i = 0;
                    ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
                    ze._CompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
                    ze._UncompressedSize = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;

                    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, System.IO.SeekOrigin.Current);

                        // Adjust the size to account for the false signature read in
                        // FindSignature().
                        SizeOfDataRead += 4;
                    }
                }

                //if (SizeOfDataRead != ze._CompressedSize)
                //    throw new BadReadException("Data format error (bit 3 is set)");

                // seek back to previous position, to prepare to read file data
                ze.ArchiveStream.Seek(posn, System.IO.SeekOrigin.Begin);
            }

            ze._CompressedFileDataSize = ze._CompressedSize;

            if ((ze._BitField & 0x01) == 0x01)
            {
                // PKZIP encrypts the compressed data stream.  Encrypted files must
                // be decrypted before they can be extracted.

                // Each 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 described elsewhere in this document.

                ze._Encryption = EncryptionAlgorithm.PkzipWeak;

                // read the 12-byte encryption header
                ze._WeakEncryptionHeader = new byte[12];
                n = ze.ArchiveStream.Read(ze._WeakEncryptionHeader, 0, 12);
                if (n != 12) return false;

                bytesRead += n;

                // 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._TotalEntrySize = bytesRead + ze._CompressedFileDataSize;
            ze._LengthOfHeader = bytesRead;

            // The pointer in the file is now at the start of the filedata,
            // which is potentially compressed and encrypted.

            return true;
        }
Example #2
0
 private static void HandleUnexpectedDataDescriptor(ZipEntry entry)
 {
     System.IO.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)Zip.SharedUtilities.ReadInt(s);
     if (datum == entry._Crc32)
     {
         int sz = Zip.SharedUtilities.ReadInt(s);
         if (sz == entry._CompressedSize)
         {
             sz = Zip.SharedUtilities.ReadInt(s);
             if (sz == entry._UncompressedSize)
             {
                 // ignore everything and discard it.
             }
             else
                 s.Seek(-12, System.IO.SeekOrigin.Current); // unread the three blocks
         }
         else
             s.Seek(-8, System.IO.SeekOrigin.Current); // unread the two blocks
     }
     else
         s.Seek(-4, System.IO.SeekOrigin.Current); // unread the block
 }
Example #3
0
        /// <summary>
        /// Reads one ZipEntry from the given stream.  If the entry is encrypted, we don't
        /// actuall decrypt at this point. 
        /// </summary>
        /// <param name="s">the stream to read from.</param>
        /// <param name="defaultEncoding">
        /// The text encoding to use when reading the ZipEntry, if it is not marked as UTF-8.
        /// </param>
        /// <returns>the ZipEntry read from the stream.</returns>
        internal static ZipEntry Read(System.IO.Stream s, System.Text.Encoding defaultEncoding)
        {
            ZipEntry entry = new ZipEntry();
            entry._Source = EntrySource.Zipfile;
            entry._archiveStream = s;
            if (!ReadHeader(entry, defaultEncoding)) return null;

            // store the position in the stream for this entry
            entry.__FileDataPosition = entry.ArchiveStream.Position;

            // seek past the data without reading it. We will read on Extract()
            s.Seek(entry._CompressedFileDataSize, System.IO.SeekOrigin.Current);

            // workitem 6607: don't seek if it is a directory.
            if (!entry.FileName.EndsWith("/"))
            // finally, seek past the (already read) Data descriptor if necessary
            if ((entry._BitField & 0x0008) == 0x0008)
            {
                s.Seek(16, System.IO.SeekOrigin.Current);
            }

            // workitem 5306
            // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306
            HandleUnexpectedDataDescriptor(entry);

            return entry;
        }
Example #4
0
 internal void CopyMetaData(ZipEntry source)
 {
     this.__FileDataPosition = source.__FileDataPosition;
     this.CompressionMethod = source.CompressionMethod;
     this._CompressedFileDataSize = source._CompressedFileDataSize;
     this._UncompressedSize = source._UncompressedSize;
     this._BitField = source._BitField;
     this._LastModified = source._LastModified;
 }
Example #5
0
        internal static ZipEntry Create(String filename, string nameInArchive, System.IO.Stream stream)
        {
            if (String.IsNullOrEmpty(filename))
                throw new Zip.ZipException("The entry name must be non-null and non-empty.");

            ZipEntry entry = new ZipEntry();
            if (stream != null)
            {
                entry._sourceStream = stream;
                entry._LastModified = DateTime.Now;
            }
            else
            {
                entry._LastModified = (System.IO.File.Exists(filename) || System.IO.Directory.Exists(filename))
                    ? SharedUtilities.RoundToEvenSecond(System.IO.File.GetLastWriteTime(filename))
                    : DateTime.Now;

                if (!entry._LastModified.IsDaylightSavingTime() &&
            DateTime.Now.IsDaylightSavingTime())
                {
                    entry._LastModified = entry._LastModified + new System.TimeSpan(1, 0, 0);
                }

                if (entry._LastModified.IsDaylightSavingTime() &&
            !DateTime.Now.IsDaylightSavingTime())
                {
                    entry._LastModified = entry._LastModified - new System.TimeSpan(1, 0, 0);
                }
            }

            entry._LocalFileName = filename; // may include a path
            entry._FileNameInArchive = nameInArchive.Replace('\\', '/');

            // we don't actually slurp in the file until the caller invokes Write on this entry.

            return entry;
        }