Example #1
0
        /// <summary>
        /// Reads a block of bytes from the current zip entry.
        /// </summary>
        /// <returns>
        /// The number of bytes read (this may be less than the length requested, even before the end of stream), or 0 on end of stream.
        /// </returns>
        /// <exception name="IOException">
        /// An i/o error occured.
        /// </exception>
        /// <exception cref="ZipException">
        /// The deflated stream is corrupted.
        /// </exception>
        /// <exception cref="InvalidOperationException">
        /// The stream is not open.
        /// </exception>
        public override int Read(byte[] b, int off, int len)
        {
            if (crc == null)
            {
                throw new InvalidOperationException("Closed.");
            }

            if (entry == null)
            {
                return(0);
            }

            bool finished = false;

            switch (method)
            {
            case (int)CompressionMethod.Deflated:
                len = base.Read(b, off, len);
                if (len <= 0)
                {
                    if (!inf.IsFinished)
                    {
                        throw new ZipException("Inflater not finished!?");
                    }
                    avail = inf.RemainingInput;

                    if ((flags & 8) == 0 && (inf.TotalIn != csize || inf.TotalOut != size))
                    {
                        throw new ZipException("size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut);
                    }
                    inf.Reset();
                    finished = true;
                }
                break;

            case (int)CompressionMethod.Stored:
                if (len > csize && csize >= 0)
                {
                    len = (int)csize;
                }
                len = ReadBuf(b, off, len);
                if (len > 0)
                {
                    csize -= len;
                    size  -= len;
                }

                if (csize == 0)
                {
                    finished = true;
                }
                else
                {
                    if (len < 0)
                    {
                        throw new ZipException("EOF in stored block");
                    }
                }

                // cipher text needs decrypting
                if (cryptbuffer != null)
                {
                    DecryptBlock(b, off, len);
                }

                break;
            }

            if (len > 0)
            {
                crc.Update(b, off, len);
            }

            if (finished)
            {
                StopDecrypting();
                if ((flags & 8) != 0)
                {
                    ReadDataDescriptor();
                }

                if ((crc.Value & 0xFFFFFFFFL) != entry.Crc && entry.Crc != -1)
                {
                    throw new ZipException("CRC mismatch");
                }
                crc.Reset();
                entry = null;
            }
            return(len);
        }
Example #2
0
 /// <summary>
 /// Closes the zip input stream
 /// </summary>
 public override void Close()
 {
     base.Close();
     crc   = null;
     entry = null;
 }
Example #3
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);
        }