Ejemplo n.º 1
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();
            }

            if (this.cryptbuffer != null)
            {
                if (avail == 0 && inf.RemainingInput != 0)
                {
                    avail = inf.RemainingInput - 16;
                    inf.Reset();
                }
                baseInputStream.Position -= this.len;
                baseInputStream.Read(this.buf, 0, this.len);
            }

            if (avail <= 0)
            {
                FillBuf(ZipConstants.LOCHDR);
            }

            int header = 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 = ReadLeInt();
            }

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

            short versionRequiredToExtract = (short)ReadLeShort();

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

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

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

            byte[] buffer = new byte[nameLen];
            ReadFully(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;
                BufferReadSize       = 0;
            }
            else
            {
                if (isCrypted)
                {
                    BufferReadSize = 1;
                }
                else
                {
                    BufferReadSize = 0;
                }

                // 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];
                ReadFully(extra);
                entry.ExtraData = extra;
            }

            // TODO How to handle this?
            // This library cannot handle versions greater than 20
            // Throwing an exception precludes getting at later possibly useable entries.
            // Could also skip this entry entirely
            // Letting it slip past here isnt so great as it wont work
            if (versionRequiredToExtract > 20)
            {
                throw new ZipException("Libray cannot extract this entry version required (" + versionRequiredToExtract.ToString() + ")");
            }

            // test for encryption
            if (isCrypted)
            {
                if (password == null)
                {
                    throw new ZipException("No password set.");
                }
                InitializePassword(password);
                cryptbuffer = new byte[ZipConstants.CRYPTO_HEADER_SIZE];
                ReadFully(cryptbuffer);
                DecryptBlock(cryptbuffer, 0, cryptbuffer.Length);

                if ((flags & 8) == 0)
                {
                    if (cryptbuffer[ZipConstants.CRYPTO_HEADER_SIZE - 1] != (byte)(crc2 >> 24))
                    {
                        throw new ZipException("Invalid password");
                    }
                }
                else
                {
                    if (cryptbuffer[ZipConstants.CRYPTO_HEADER_SIZE - 1] != (byte)((dostime >> 8) & 0xff))
                    {
                        throw new ZipException("Invalid password");
                    }
                }

                if (csize >= ZipConstants.CRYPTO_HEADER_SIZE)
                {
                    csize -= ZipConstants.CRYPTO_HEADER_SIZE;
                }
            }
            else
            {
                cryptbuffer = null;
            }

            if (method == (int)CompressionMethod.Deflated && avail > 0)
            {
                System.Array.Copy(buf, len - (int)avail, buf, 0, (int)avail);
                len   = (int)avail;
                avail = 0;
                if (isCrypted)
                {
                    DecryptBlock(buf, 0, Math.Min((int)csize, len));
                }
                inf.SetInput(buf, 0, len);
            }

            return(entry);
        }
Ejemplo n.º 2
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="Aucent.MAX.AXE.Common.ZipCompressDecompress.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 pos = baseStream.Length - ZipConstants.ENDHDR;

            if (pos <= 0)
            {
                throw new ZipException("File is too small to be a Zip file");
            }

            long giveUpMarker = Math.Max(pos - 0x10000, 0);

            do
            {
                if (pos < giveUpMarker)
                {
                    throw new ZipException("central directory not found, probably not a zip file");
                }
                baseStream.Seek(pos--, SeekOrigin.Begin);
            } while (ReadLeInt() != ZipConstants.ENDSIG);

            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];
            baseStream.Seek(offsetOfCentralDir, SeekOrigin.Begin);

            for (int i = 0; i < entriesForWholeCentralDir; 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;
            }
        }