/// <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;
        }
Example #2
0
        /// <summary>
        /// Creates a copy of the given zip entry.
        /// </summary>
        /// <param name="e">
        /// The entry to copy.
        /// </param>
        public ZipEntry(ZipEntry e)
        {
            known                  = e.known;
            name                   = e.name;
            size                   = e.size;
            compressedSize         = e.compressedSize;
            crc                    = e.crc;
            dosTime                = e.dosTime;
            method                 = e.method;
            ExtraData              = e.ExtraData;     // Note use of property ensuring data is unique
            comment                = e.comment;
            versionToExtract       = e.versionToExtract;
            versionMadeBy          = e.versionMadeBy;
            externalFileAttributes = e.externalFileAttributes;
            flags                  = e.flags;

            zipFileIndex           = -1;
            offset                 = 0;
        }
        /// <summary>
        /// Closes the current zip entry and moves to the next one.
        /// </summary>
        /// <exception cref="InvalidOperationException">
        /// The stream is closed
        /// </exception>
        /// <exception cref="ZipException">
        /// The Zip stream ends early
        /// </exception>
        public void CloseEntry()
        {
            if (crc == null) {
                throw new InvalidOperationException("Closed.");
            }

            if (entry == null) {
                return;
            }

            if (method == (int)CompressionMethod.Deflated) {
                if ((flags & 8) != 0) {
                    // We don't know how much we must skip, read until end.
                    byte[] tmp = new byte[2048];
                    while (Read(tmp, 0, tmp.Length) > 0)
                        ;
                    // read will close this entry
                    return;
                }
                csize -= inf.TotalIn;
                inputBuffer.Available -= inf.RemainingInput;
            }

            if (inputBuffer.Available > csize && csize >= 0) {
                inputBuffer.Available = (int)((long)inputBuffer.Available - csize);
            } else {
                csize -= inputBuffer.Available;
                inputBuffer.Available = 0;
                while (csize != 0) {
                    int skipped = (int)base.Skip(csize & 0xFFFFFFFFL);

                    if (skipped <= 0) {
                        throw new ZipException("Zip archive ends early.");
                    }

                    csize -= skipped;
                }
            }

            size = 0;
            crc.Reset();
            if (method == (int)CompressionMethod.Deflated) {
                inf.Reset();
            }
            entry = null;
        }
 /// <summary>
 /// Closes the zip input stream
 /// </summary>
 public override void Close()
 {
     base.Close();
     crc = null;
     entry = null;
 }
        /// <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 int BodyRead(byte[] b, int off, int len)
        {
            if (crc == null) {
                throw new InvalidOperationException("Closed.");
            }

            if (entry == null || len <= 0 ) {
                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!?");
                        }
                        inputBuffer.Available = 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 = inputBuffer.ReadClearTextBuffer(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");
                        }
                    }
                    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;
        }
        /// <summary>
        /// Starts a new Zip entry. It automatically closes the previous
        /// entry if present.
        /// All entry elements bar name are optional, but must be correct if present.
        /// If the compression method is stored and the output is not patchable
        /// the compression for that entry is automatically changed to deflate level 0
        /// </summary>
        /// <param name="entry">
        /// the entry.
        /// </param>
        /// <exception cref="System.IO.IOException">
        /// if an I/O error occured.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// if stream was finished
        /// </exception>
        /// <exception cref="ZipException">
        /// Too many entries in the Zip file<br/>
        /// Entry name is too long<br/>
        /// Finish has already been called<br/>
        /// </exception>
        public void PutNextEntry(ZipEntry entry)
        {
            if (entries == null) {
                throw new InvalidOperationException("ZipOutputStream was finished");
            }

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

            if (entries.Count >= 0xffff) {
                throw new ZipException("Too many entries for Zip file");
            }

            CompressionMethod method = entry.CompressionMethod;
            int compressionLevel = defaultCompressionLevel;

            entry.Flags = 0;
            patchEntryHeader = false;
            bool headerInfoAvailable = true;

            if (method == CompressionMethod.Stored) {
                if (entry.CompressedSize >= 0) {
                    if (entry.Size < 0) {
                        entry.Size = entry.CompressedSize;
                    } else if (entry.Size != entry.CompressedSize) {
                        throw new ZipException("Method STORED, but compressed size != size");
                    }
                } else {
                    if (entry.Size >= 0) {
                        entry.CompressedSize = entry.Size;
                    }
                }

                if (entry.Size < 0 || entry.Crc < 0) {
                    if (CanPatchEntries == true) {
                        headerInfoAvailable = false;
                    }
                    else {
                  // Cant patch entries so storing is not possible.
                        method = CompressionMethod.Deflated;
                        compressionLevel = 0;
                    }
                }
            }

            if (method == CompressionMethod.Deflated) {
                if (entry.Size == 0) {
               // No need to compress - no data.
                    entry.CompressedSize = entry.Size;
                    entry.Crc = 0;
                    method = CompressionMethod.Stored;
                } else if (entry.CompressedSize < 0 || entry.Size < 0 || entry.Crc < 0) {
                    headerInfoAvailable = false;
                }
            }

            if (headerInfoAvailable == false) {
                if (CanPatchEntries == false) {
                    entry.Flags |= 8;
                } else {
                    patchEntryHeader = true;
                }
            }

            if (Password != null) {
                entry.IsCrypted = true;
                if (entry.Crc < 0) {
               // Need to append data descriptor as crc is used for encryption and its not known.
                    entry.Flags |= 8;
                }
            }
            entry.Offset = (int)offset;
            entry.CompressionMethod = (CompressionMethod)method;

            curMethod    = method;

            // Write the local file header
            WriteLeInt(ZipConstants.LOCSIG);

            WriteLeShort(entry.Version);
            WriteLeShort(entry.Flags);
            WriteLeShort((byte)method);
            WriteLeInt((int)entry.DosTime);
            if (headerInfoAvailable == true) {
                WriteLeInt((int)entry.Crc);
                WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CRYPTO_HEADER_SIZE : (int)entry.CompressedSize);
                WriteLeInt((int)entry.Size);
            } else {
                if (patchEntryHeader == true) {
                    headerPatchPos = baseOutputStream.Position;
                }
                WriteLeInt(0);	// Crc
                WriteLeInt(0);	// Compressed size
                WriteLeInt(0);	// Uncompressed size
            }

            byte[] name = ZipConstants.ConvertToArray(entry.Name);

            if (name.Length > 0xFFFF) {
                throw new ZipException("Entry name too long.");
            }

            byte[] extra = entry.ExtraData;
            if (extra == null) {
                extra = new byte[0];
            }

            if (extra.Length > 0xFFFF) {
                throw new ZipException("Extra data too long.");
            }

            WriteLeShort(name.Length);
            WriteLeShort(extra.Length);
            baseOutputStream.Write(name, 0, name.Length);
            baseOutputStream.Write(extra, 0, extra.Length);

            offset += ZipConstants.LOCHDR + name.Length + extra.Length;

            // Activate the entry.
            curEntry = entry;
            crc.Reset();
            if (method == CompressionMethod.Deflated) {
                def.Reset();
                def.SetLevel(compressionLevel);
            }
            size = 0;

            if (entry.IsCrypted == true) {
                if (entry.Crc < 0) {			// so testing Zip will says its ok
                    WriteEncryptionHeader(entry.DosTime << 16);
                } else {
                    WriteEncryptionHeader(entry.Crc);
                }
            }
        }
        /// <summary>
        /// Closes the current entry, updating header and footer information as required
        /// </summary>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurs.
        /// </exception>
        /// <exception cref="System.InvalidOperationException">
        /// No entry is active.
        /// </exception>
        public void CloseEntry()
        {
            if (curEntry == null) {
                throw new InvalidOperationException("No open entry");
            }

            // First finish the deflater, if appropriate
            if (curMethod == CompressionMethod.Deflated) {
                base.Finish();
            }

            long csize = curMethod == CompressionMethod.Deflated ? def.TotalOut : size;

            if (curEntry.Size < 0) {
                curEntry.Size = size;
            } else if (curEntry.Size != size) {
                throw new ZipException("size was " + size + ", but I expected " + curEntry.Size);
            }

            if (curEntry.CompressedSize < 0) {
                curEntry.CompressedSize = csize;
            } else if (curEntry.CompressedSize != csize) {
                throw new ZipException("compressed size was " + csize + ", but I expected " + curEntry.CompressedSize);
            }

            if (curEntry.Crc < 0) {
                curEntry.Crc = crc.Value;
            } else if (curEntry.Crc != crc.Value) {
                throw new ZipException("crc was " + crc.Value +	", but I expected " + curEntry.Crc);
            }

            offset += csize;

            if (offset > 0xffffffff) {
                throw new ZipException("Maximum Zip file size exceeded");
            }

            if (curEntry.IsCrypted == true) {
                curEntry.CompressedSize += ZipConstants.CRYPTO_HEADER_SIZE;
            }

            // Patch the header if possible
            if (patchEntryHeader == true) {
                long curPos = baseOutputStream.Position;
                baseOutputStream.Seek(headerPatchPos, SeekOrigin.Begin);
                WriteLeInt((int)curEntry.Crc);
                WriteLeInt((int)curEntry.CompressedSize);
                WriteLeInt((int)curEntry.Size);
                baseOutputStream.Seek(curPos, SeekOrigin.Begin);
                patchEntryHeader = false;
            }

            // Add data descriptor if flagged as required
            if ((curEntry.Flags & 8) != 0) {
                WriteLeInt(ZipConstants.EXTSIG);
                WriteLeInt((int)curEntry.Crc);
                WriteLeInt((int)curEntry.CompressedSize);
                WriteLeInt((int)curEntry.Size);
                offset += ZipConstants.EXTHDR;
            }

            entries.Add(curEntry);
            curEntry = null;
        }