Exemple #1
0
        /// <summary>
        /// Search for and read the central directory of a zip file filling the entries
        /// array.  This is called exactly once by the constructors.
        /// </summary>
        /// <exception cref="System.IO.IOException">
        /// An i/o error occurs.
        /// </exception>
        /// <exception cref="NTDLS.IO.Compression.Zip.ZipException">
        /// The central directory is malformed or cannot be found
        /// </exception>
        void ReadEntries()
        {
            // Search for the End Of Central Directory.  When a zip comment is
            // present the directory may start earlier.
            //
            // TODO: The search is limited to 64K which is the maximum size of a trailing comment field to aid speed.
            // This should be compatible with both SFX and ZIP files but has only been tested for Zip files
            // Need to confirm this is valid in all cases.
            // Could also speed this up by reading memory in larger blocks.

            if (baseStream.CanSeek == false)
            {
                throw new ZipException("ZipFile stream must be seekable");
            }

            long locatedCentralDirOffset = LocateBlockWithSignature(ZipConstants.ENDSIG, baseStream.Length, ZipConstants.ENDHDR, 0xffff);

            if (locatedCentralDirOffset < 0)
            {
                throw new ZipException("Cannot find central directory");
            }

            int thisDiskNumber            = ReadLeShort();
            int startCentralDirDisk       = ReadLeShort();
            int entriesForThisDisk        = ReadLeShort();
            int entriesForWholeCentralDir = ReadLeShort();
            int centralDirSize            = ReadLeInt();
            int offsetOfCentralDir        = ReadLeInt();
            int commentSize = ReadLeShort();

            byte[] zipComment = new byte[commentSize];
            baseStream.Read(zipComment, 0, zipComment.Length);
            comment = ZipConstants.ConvertToString(zipComment);

/* Its seems possible that this is too strict, more digging required.
 *                      if (thisDiskNumber != 0 || startCentralDirDisk != 0 || entriesForThisDisk != entriesForWholeCentralDir) {
 *                              throw new ZipException("Spanned archives are not currently handled");
 *                      }
 */

            entries = new ZipEntry[entriesForWholeCentralDir];

            // SFX support, find the offset of the first entry vis the start of the stream
            // This applies to Zip files that are appended to the end of the SFX stub.
            // Zip files created by some archivers have the offsets altered to reflect the true offsets
            // and so dont require any adjustment here...
            if (offsetOfCentralDir < locatedCentralDirOffset - (4 + centralDirSize))
            {
                offsetOfFirstEntry = locatedCentralDirOffset - (4 + centralDirSize + offsetOfCentralDir);
                if (offsetOfFirstEntry <= 0)
                {
                    throw new ZipException("Invalid SFX file");
                }
            }

            baseStream.Seek(offsetOfFirstEntry + offsetOfCentralDir, SeekOrigin.Begin);

            for (int i = 0; i < entriesForThisDisk; i++)
            {
                if (ReadLeInt() != ZipConstants.CENSIG)
                {
                    throw new ZipException("Wrong Central Directory signature");
                }

                int versionMadeBy    = ReadLeShort();
                int versionToExtract = ReadLeShort();
                int bitFlags         = ReadLeShort();
                int method           = ReadLeShort();
                int dostime          = ReadLeInt();
                int crc        = ReadLeInt();
                int csize      = ReadLeInt();
                int size       = ReadLeInt();
                int nameLen    = ReadLeShort();
                int extraLen   = ReadLeShort();
                int commentLen = ReadLeShort();

                int diskStartNo        = ReadLeShort();                  // Not currently used
                int internalAttributes = ReadLeShort();                  // Not currently used

                int externalAttributes = ReadLeInt();
                int offset             = ReadLeInt();

                byte[] buffer = new byte[Math.Max(nameLen, commentLen)];

                baseStream.Read(buffer, 0, nameLen);
                string name = ZipConstants.ConvertToString(buffer, nameLen);

                ZipEntry entry = new ZipEntry(name, versionToExtract, versionMadeBy);
                entry.CompressionMethod = (CompressionMethod)method;
                entry.Crc            = crc & 0xffffffffL;
                entry.Size           = size & 0xffffffffL;
                entry.CompressedSize = csize & 0xffffffffL;
                entry.Flags          = bitFlags;
                entry.DosTime        = (uint)dostime;

                if (extraLen > 0)
                {
                    byte[] extra = new byte[extraLen];
                    baseStream.Read(extra, 0, extraLen);
                    entry.ExtraData = extra;
                }

                if (commentLen > 0)
                {
                    baseStream.Read(buffer, 0, commentLen);
                    entry.Comment = ZipConstants.ConvertToString(buffer, commentLen);
                }

                entry.ZipFileIndex           = i;
                entry.Offset                 = offset;
                entry.ExternalFileAttributes = externalAttributes;

                entries[i] = entry;
            }
        }
Exemple #2
0
        /// <summary>
        /// Advances to the next entry in the archive
        /// </summary>
        /// <returns>
        /// The next <see cref="ZipEntry">entry</see> in the archive or null if there are no more entries.
        /// </returns>
        /// <remarks>
        /// If the previous entry is still open <see cref="CloseEntry">CloseEntry</see> is called.
        /// </remarks>
        /// <exception cref="InvalidOperationException">
        /// Input stream is closed
        /// </exception>
        /// <exception cref="ZipException">
        /// Password is not set, password is invalid, compression method is invalid,
        /// version required to extract is not supported
        /// </exception>
        public ZipEntry GetNextEntry()
        {
            if (crc == null)
            {
                throw new InvalidOperationException("Closed.");
            }

            if (entry != null)
            {
                CloseEntry();
            }

            int header = inputBuffer.ReadLeInt();

            if (header == ZipConstants.CENSIG ||
                header == ZipConstants.ENDSIG ||
                header == ZipConstants.CENDIGITALSIG ||
                header == ZipConstants.CENSIG64)
            {
                // No more individual entries exist
                Close();
                return(null);
            }

            // -jr- 07-Dec-2003 Ignore spanning temporary signatures if found
            // SPANNINGSIG is same as descriptor signature and is untested as yet.
            if (header == ZipConstants.SPANTEMPSIG || header == ZipConstants.SPANNINGSIG)
            {
                header = inputBuffer.ReadLeInt();
            }

            if (header != ZipConstants.LOCSIG)
            {
                throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header));
            }

            short versionRequiredToExtract = (short)inputBuffer.ReadLeShort();

            flags  = inputBuffer.ReadLeShort();
            method = inputBuffer.ReadLeShort();
            uint dostime = (uint)inputBuffer.ReadLeInt();
            int  crc2    = inputBuffer.ReadLeInt();

            csize = inputBuffer.ReadLeInt();
            size  = inputBuffer.ReadLeInt();
            int nameLen  = inputBuffer.ReadLeShort();
            int extraLen = inputBuffer.ReadLeShort();

            bool isCrypted = (flags & 1) == 1;

            byte[] buffer = new byte[nameLen];
            inputBuffer.ReadRawBuffer(buffer);

            string name = ZipConstants.ConvertToString(buffer);

            entry       = new ZipEntry(name, versionRequiredToExtract);
            entry.Flags = flags;

            if (method == (int)CompressionMethod.Stored && (!isCrypted && csize != size || (isCrypted && csize - ZipConstants.CRYPTO_HEADER_SIZE != size)))
            {
                throw new ZipException("Stored, but compressed != uncompressed");
            }

            if (method != (int)CompressionMethod.Stored && method != (int)CompressionMethod.Deflated)
            {
                throw new ZipException("Unknown compression method " + method);
            }

            entry.CompressionMethod = (CompressionMethod)method;

            if ((flags & 8) == 0)
            {
                entry.Crc            = crc2 & 0xFFFFFFFFL;
                entry.Size           = size & 0xFFFFFFFFL;
                entry.CompressedSize = csize & 0xFFFFFFFFL;
            }
            else
            {
                // This allows for GNU, WinZip and possibly other archives, the PKZIP spec says these are zero
                // under these circumstances.
                if (crc2 != 0)
                {
                    entry.Crc = crc2 & 0xFFFFFFFFL;
                }

                if (size != 0)
                {
                    entry.Size = size & 0xFFFFFFFFL;
                }
                if (csize != 0)
                {
                    entry.CompressedSize = csize & 0xFFFFFFFFL;
                }
            }

            entry.DosTime = dostime;

            if (extraLen > 0)
            {
                byte[] extra = new byte[extraLen];
                inputBuffer.ReadRawBuffer(extra);
                entry.ExtraData = extra;
            }

            internalReader = new ReaderDelegate(InitialRead);
            return(entry);
        }