예제 #1
0
        /// <summary>
        ///   Read the next entry from the zip file.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   Call this method just before calling <see cref="Read(byte[], int, int)"/>,
        ///   to position the pointer in the zip file to the next entry that can be
        ///   read.  Subsequent calls to <c>Read()</c>, will decrypt and decompress the
        ///   data in the zip file, until <c>Read()</c> returns 0.
        /// </para>
        ///
        /// <para>
        ///   Each time you call <c>GetNextEntry()</c>, the pointer in the wrapped
        ///   stream is moved to the next entry in the zip file.  If you call <see
        ///   cref="Seek(long, SeekOrigin)"/>, and thus re-position the pointer within
        ///   the file, you will need to call <c>GetNextEntry()</c> again, to insure
        ///   that the file pointer is positioned at the beginning of a zip entry.
        /// </para>
        ///
        /// <para>
        ///   This method returns the <c>ZipEntry</c>. Using a stream approach, you will
        ///   read the raw bytes for an entry in a zip file via calls to <c>Read()</c>.
        ///   Alternatively, you can extract an entry into a file, or a stream, by
        ///   calling <see cref="ZipEntry.Extract()"/>, or one of its siblings.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <returns>
        ///   The <c>ZipEntry</c> read. Returns null (or Nothing in VB) if there are no more
        ///   entries in the zip file.
        /// </returns>
        ///
        public ZipEntry GetNextEntry()
        {
            if (_findRequired)
            {
                // find the next signature
                long d = SharedUtilities.FindSignature(_inputStream, ZipConstants.ZipEntrySignature);
                if (d == -1)
                {
                    return(null);
                }
                // back up 4 bytes: ReadEntry assumes the file pointer is positioned before the entry signature
                _inputStream.Seek(-4, SeekOrigin.Current);
                // workitem 10178
                SharedUtilities.Workaround_Ladybug318918(_inputStream);
            }
            // workitem 10923
            else if (_firstEntry)
            {
                // we've already read one entry.
                // Seek to the end of it.
                _inputStream.Seek(_endOfEntry, SeekOrigin.Begin);
                SharedUtilities.Workaround_Ladybug318918(_inputStream);
            }

            _currentEntry = ZipEntry.ReadEntry(_container, !_firstEntry);
            // ReadEntry leaves the file position after all the entry
            // data and the optional bit-3 data descriptpr.  This is
            // where the next entry would normally start.
            _endOfEntry   = _inputStream.Position;
            _firstEntry   = true;
            _needSetup    = true;
            _findRequired = false;
            return(_currentEntry);
        }
예제 #2
0
        private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
        {
            int bytesRead = 0;

            // change for workitem 8098
            ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position;

            int signature = 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
                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 = 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 = 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
                        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
                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);
        }
예제 #3
0
        private static void ReadIntoInstance(ZipFile zf)
        {
            Stream s = zf.ReadStream;

            try
            {
                zf._readName = zf._name; // workitem 13915
                if (!s.CanSeek)
                {
                    ReadIntoInstance_Orig(zf);
                    return;
                }

                zf.OnReadStarted();

                // change for workitem 8098
                //zf._originPosition = s.Position;

                // Try reading the central directory, rather than scanning the file.

                uint datum = ReadFirstFourBytes(s);

                if (datum == ZipConstants.EndOfCentralDirectorySignature)
                {
                    return;
                }


                // start at the end of the file...
                // seek backwards a bit, then look for the EoCD signature.
                int  nTries  = 0;
                bool success = false;

                // The size of the end-of-central-directory-footer plus 2 bytes is 18.
                // This implies an archive comment length of 0.  We'll add a margin of
                // safety and start "in front" of that, when looking for the
                // EndOfCentralDirectorySignature
                long posn        = s.Length - 64;
                long maxSeekback = Math.Max(s.Length - 0x4000, 10);
                do
                {
                    if (posn < 0)
                    {
                        posn = 0;            // BOF
                    }
                    s.Seek(posn, SeekOrigin.Begin);
                    long bytesRead = SharedUtilities.FindSignature(s, (int)ZipConstants.EndOfCentralDirectorySignature);
                    if (bytesRead != -1)
                    {
                        success = true;
                    }
                    else
                    {
                        if (posn == 0)
                        {
                            break;          // started at the BOF and found nothing
                        }
                        nTries++;
                        // Weird: with NETCF, negative offsets from SeekOrigin.End DO
                        // NOT WORK. So rather than seek a negative offset, we seek
                        // from SeekOrigin.Begin using a smaller number.
                        posn -= (32 * (nTries + 1) * nTries);
                    }
                }while (!success && posn > maxSeekback);

                if (success)
                {
                    // workitem 8299
                    zf._locEndOfCDS = s.Position - 4;

                    byte[] block = new byte[16];
                    s.Read(block, 0, block.Length);

                    zf._diskNumberWithCd = BitConverter.ToUInt16(block, 2);

                    if (zf._diskNumberWithCd == 0xFFFF)
                    {
                        throw new ZipException("Spanned archives with more than 65534 segments are not supported at this time.");
                    }

                    zf._diskNumberWithCd++; // I think the number in the file differs from reality by 1

                    int i = 12;

                    uint offset32 = (uint)BitConverter.ToUInt32(block, i);
                    if (offset32 == 0xFFFFFFFF)
                    {
                        Zip64SeekToCentralDirectory(zf);
                    }
                    else
                    {
                        zf._OffsetOfCentralDirectory = offset32;
                        // change for workitem 8098
                        s.Seek(offset32, SeekOrigin.Begin);
                    }

                    ReadCentralDirectory(zf);
                }
                else
                {
                    // Could not find the central directory.
                    // Fallback to the old method.
                    // workitem 8098: ok
                    //s.Seek(zf._originPosition, SeekOrigin.Begin);
                    s.Seek(0L, SeekOrigin.Begin);
                    ReadIntoInstance_Orig(zf);
                }
            }
            catch (Exception ex1)
            {
                if (zf._ReadStreamIsOurs && zf._readstream != null)
                {
                    try
                    {
#if NETCF
                        zf._readstream.Close();
#else
                        zf._readstream.Dispose();
#endif
                        zf._readstream = null;
                    }
                    finally { }
                }

                throw new ZipException("Cannot read that as a ZipFile", ex1);
            }

            // the instance has been read in
            zf._contentsChanged = false;
        }