Beispiel #1
0
        /// <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.ArgumentNullException">
        /// if entry passed is null.
        /// </exception>
        /// <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 ( entry == null ) {
                throw new ArgumentNullException("entry");
            }

            if (entries == null) {
                throw new InvalidOperationException("ZipOutputStream was finished");
            }

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

            if (entries.Count == int.MaxValue) {
                throw new ZipException("Too many entries for Zip file");
            }

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

            // Clear flags that the library manages internally
            entry.Flags &= (int)GeneralBitFlags.UnicodeText;
            patchEntryHeader = false;
            bool headerInfoAvailable = true;

            if (method == CompressionMethod.Stored) {
                // Cant store values in a data descriptor as you cant extract stored files
                // if the length isnt known.
                entry.Flags &= ~8;
                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 {
                        // Can't 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) {
                    // Only way to record size and compressed size is to append a data descriptor
                    // after compressed data.
                    entry.Flags |= 8;
                } else {
                    patchEntryHeader = true;
                }
            }

            if (Password != null) {
                entry.IsCrypted = true;
                if (entry.Crc < 0) {
                    // Need to append a data descriptor as the crc isnt available for use
                    // with encryption, the date is used instead.  Setting the flag
                    // indicates this to the decompressor.
                    entry.Flags |= 8;
                }
            }

            entry.Offset = offset;
            entry.CompressionMethod = (CompressionMethod)method;

            curMethod = method;
            sizePatchPos = -1;

            if ( (useZip64_ == UseZip64.On) || ((entry.Size < 0) && (useZip64_ == UseZip64.Dynamic)) ) {
                entry.ForceZip64();
            }

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

            WriteLeShort(entry.Version);
            WriteLeShort(entry.Flags);
            WriteLeShort((byte)method);
            WriteLeInt((int)entry.DosTime);

            // TODO: Refactor header writing.  Its done in several places.
            if (headerInfoAvailable == true) {
                WriteLeInt((int)entry.Crc);
                if ( entry.LocalHeaderRequiresZip64 ) {
                    WriteLeInt(-1);
                    WriteLeInt(-1);
                }
                else {
                    WriteLeInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
                    WriteLeInt((int)entry.Size);
                }
            } else {
                if (patchEntryHeader == true) {
                    crcPatchPos = baseOutputStream_.Position;
                }
                WriteLeInt(0);	// Crc

                if ( patchEntryHeader ) {
                    sizePatchPos = baseOutputStream_.Position;
                }

                // For local header both sizes appear in Zip64 Extended Information
                if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
                    WriteLeInt(-1);
                    WriteLeInt(-1);
                }
                else {
                    WriteLeInt(0);	// Compressed size
                    WriteLeInt(0);	// Uncompressed size
                }
            }

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

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

            ZipExtraData ed = new ZipExtraData(entry.ExtraData);

            if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
                ed.StartNewEntry();
                if (headerInfoAvailable) {
                    ed.AddLeLong(entry.Size);
                    ed.AddLeLong(entry.CompressedSize);
                }
                else {
                    ed.AddLeLong(-1);
                    ed.AddLeLong(-1);
                }
                ed.AddNewEntry(1);

                if ( !ed.Find(1) ) {
                    throw new ZipException("Internal error cant find extra data");
                }

                if ( patchEntryHeader ) {
                    sizePatchPos = ed.CurrentReadIndex;
                }
            }
            else {
                ed.Delete(1);
            }

            byte[] extra = ed.GetEntryData();

            WriteLeShort(name.Length);
            WriteLeShort(extra.Length);

            if ( name.Length > 0 ) {
                baseOutputStream_.Write(name, 0, name.Length);
            }

            if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
                sizePatchPos += baseOutputStream_.Position;
            }

            if ( extra.Length > 0 ) {
                baseOutputStream_.Write(extra, 0, extra.Length);
            }

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

            // Activate the entry.
            curEntry = entry;
            crc.Reset();
            if (method == CompressionMethod.Deflated) {
                deflater_.Reset();
                deflater_.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);
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// Process extra data fields updating the entry based on the contents.
        /// </summary>
        /// <param name="localHeader">True if the extra data fields should be handled
        /// for a local header, rather than for a central header.
        /// </param>
        internal void ProcessExtraData(bool localHeader)
        {
            ZipExtraData extraData = new ZipExtraData(this.extra);

            if ( extraData.Find(0x0001) ) {
                if ( (versionToExtract & 0xff) < ZipConstants.VersionZip64 ) {
                    throw new ZipException("Zip64 Extended information found but version is not valid");
                }

                // The recorded size will change but remember that this is zip64.
                forceZip64_ = true;

                if ( extraData.ValueLength < 4 ) {
                    throw new ZipException("Extra data extended Zip64 information length is invalid");
                }

                if ( localHeader || (size == uint.MaxValue) ) {
                    size = (ulong)extraData.ReadLong();
                }

                if ( localHeader || (compressedSize == uint.MaxValue) ) {
                    compressedSize = (ulong)extraData.ReadLong();
                }

                if ( !localHeader && (offset == uint.MaxValue) ) {
                    offset = extraData.ReadLong();
                }
            }
            else {
                if (
                    ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) &&
                    ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))
                ) {
                    throw new ZipException("Zip64 Extended information required but is missing.");
                }
            }

            if ( extraData.Find(10) ) {
                // No room for any tags.
                if ( extraData.ValueLength < 8 ) {
                    throw new ZipException("NTFS Extra data invalid");
                }

                extraData.ReadInt(); // Reserved

                while ( extraData.UnreadCount >= 4 ) {
                    int ntfsTag = extraData.ReadShort();
                    int ntfsLength = extraData.ReadShort();
                    if ( ntfsTag == 1 ) {
                        if ( ntfsLength >= 24 ) {
                            long lastModification = extraData.ReadLong();
                            long lastAccess = extraData.ReadLong();
                            long createTime = extraData.ReadLong();

                            DateTime = System.DateTime.FromFileTime(lastModification);
                        }
                        break;
                    }
                    else {
                        // An unknown NTFS tag so simply skip it.
                        extraData.Skip(ntfsLength);
                    }
                }
            }
            else if ( extraData.Find(0x5455) ) {
                int length = extraData.ValueLength;
                int flags = extraData.ReadByte();

                // Can include other times but these are ignored.  Length of data should
                // actually be 1 + 4 * no of bits in flags.
                if ( ((flags & 1) != 0) && (length >= 5) ) {
                    int iTime = extraData.ReadInt();

                    DateTime = (new System.DateTime ( 1970, 1, 1, 0, 0, 0 ).ToUniversalTime() +
                        new TimeSpan ( 0, 0, 0, iTime, 0 )).ToLocalTime();
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Finishes the stream.  This will write the central directory at the
        /// end of the zip file and flush the stream.
        /// </summary>
        /// <remarks>
        /// This is automatically called when the stream is closed.
        /// </remarks>
        /// <exception cref="System.IO.IOException">
        /// An I/O error occurs.
        /// </exception>
        /// <exception cref="ZipException">
        /// Comment exceeds the maximum length<br/>
        /// Entry name exceeds the maximum length
        /// </exception>
        public override void Finish()
        {
            if (entries == null)  {
                return;
            }

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

            long numEntries = entries.Count;
            long sizeEntries = 0;

            foreach (ZipEntry entry in entries) {
                WriteLeInt(ZipConstants.CentralHeaderSignature);
                WriteLeShort(ZipConstants.VersionMadeBy);
                WriteLeShort(entry.Version);
                WriteLeShort(entry.Flags);
                WriteLeShort((short)entry.CompressionMethod);
                WriteLeInt((int)entry.DosTime);
                WriteLeInt((int)entry.Crc);

                if ( entry.IsZip64Forced() ||
                    (entry.CompressedSize >= uint.MaxValue) )
                {
                    WriteLeInt(-1);
                }
                else {
                    WriteLeInt((int)entry.CompressedSize);
                }

                if ( entry.IsZip64Forced() ||
                    (entry.Size >= uint.MaxValue) )
                {
                    WriteLeInt(-1);
                }
                else {
                    WriteLeInt((int)entry.Size);
                }

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

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

                ZipExtraData ed = new ZipExtraData(entry.ExtraData);

                if ( entry.CentralHeaderRequiresZip64 ) {
                    ed.StartNewEntry();
                    if ( entry.IsZip64Forced() ||
                        (entry.Size >= 0xffffffff) )
                    {
                        ed.AddLeLong(entry.Size);
                    }

                    if ( entry.IsZip64Forced() ||
                        (entry.CompressedSize >= 0xffffffff) )
                    {
                        ed.AddLeLong(entry.CompressedSize);
                    }

                    if ( entry.Offset >= 0xffffffff )
                    {
                        ed.AddLeLong(entry.Offset);
                    }

                    ed.AddNewEntry(1);
                }
                else {
                    ed.Delete(1);
                }

                byte[] extra = ed.GetEntryData();

                byte[] entryComment =
                    (entry.Comment != null) ?
                    ZipConstants.ConvertToArray(entry.Flags, entry.Comment) :
                    new byte[0];

                if (entryComment.Length > 0xffff) {
                    throw new ZipException("Comment too long.");
                }

                WriteLeShort(name.Length);
                WriteLeShort(extra.Length);
                WriteLeShort(entryComment.Length);
                WriteLeShort(0);	// disk number
                WriteLeShort(0);	// internal file attributes
                                    // external file attributes

                if (entry.ExternalFileAttributes != -1) {
                    WriteLeInt(entry.ExternalFileAttributes);
                } else {
                    if (entry.IsDirectory) {                         // mark entry as directory (from nikolam.AT.perfectinfo.com)
                        WriteLeInt(16);
                    } else {
                        WriteLeInt(0);
                    }
                }

                if ( entry.Offset >= uint.MaxValue ) {
                    WriteLeInt(-1);
                }
                else {
                    WriteLeInt((int)entry.Offset);
                }

                if ( name.Length > 0 ) {
                    baseOutputStream_.Write(name,    0, name.Length);
                }

                if ( extra.Length > 0 ) {
                    baseOutputStream_.Write(extra,   0, extra.Length);
                }

                if ( entryComment.Length > 0 ) {
                    baseOutputStream_.Write(entryComment, 0, entryComment.Length);
                }

                sizeEntries += ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
            }

            using ( ZipHelperStream zhs = new ZipHelperStream(baseOutputStream_) ) {
                zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, zipComment);
            }

            entries = null;
        }
Beispiel #4
0
        // Write the local file header
        // TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
        void WriteLocalHeader(ZipEntry entry, EntryPatchData patchData)
        {
            CompressionMethod method = entry.CompressionMethod;
            bool headerInfoAvailable = true; // How to get this?
            bool patchEntryHeader = false;

            WriteLEInt(ZipConstants.LocalHeaderSignature);

            WriteLEShort(entry.Version);
            WriteLEShort(entry.Flags);
            WriteLEShort((byte)method);
            WriteLEInt((int)entry.DosTime);

            if (headerInfoAvailable == true) {
                WriteLEInt((int)entry.Crc);
                if ( entry.LocalHeaderRequiresZip64 ) {
                    WriteLEInt(-1);
                    WriteLEInt(-1);
                }
                else {
                    WriteLEInt(entry.IsCrypted ? (int)entry.CompressedSize + ZipConstants.CryptoHeaderSize : (int)entry.CompressedSize);
                    WriteLEInt((int)entry.Size);
                }
            } else {
                if (patchData != null) {
                    patchData.CrcPatchOffset = stream_.Position;
                }
                WriteLEInt(0);	// Crc

                if ( patchData != null ) {
                    patchData.SizePatchOffset = stream_.Position;
                }

                // For local header both sizes appear in Zip64 Extended Information
                if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
                    WriteLEInt(-1);
                    WriteLEInt(-1);
                }
                else {
                    WriteLEInt(0);	// Compressed size
                    WriteLEInt(0);	// Uncompressed size
                }
            }

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

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

            ZipExtraData ed = new ZipExtraData(entry.ExtraData);

            if (entry.LocalHeaderRequiresZip64 && (headerInfoAvailable || patchEntryHeader)) {
                ed.StartNewEntry();
                if (headerInfoAvailable) {
                    ed.AddLeLong(entry.Size);
                    ed.AddLeLong(entry.CompressedSize);
                }
                else {
                    ed.AddLeLong(-1);
                    ed.AddLeLong(-1);
                }
                ed.AddNewEntry(1);

                if ( !ed.Find(1) ) {
                    throw new ZipException("Internal error cant find extra data");
                }

                if ( patchData != null ) {
                    patchData.SizePatchOffset = ed.CurrentReadIndex;
                }
            }
            else {
                ed.Delete(1);
            }

            byte[] extra = ed.GetEntryData();

            WriteLEShort(name.Length);
            WriteLEShort(extra.Length);

            if ( name.Length > 0 ) {
                stream_.Write(name, 0, name.Length);
            }

            if ( entry.LocalHeaderRequiresZip64 && patchEntryHeader ) {
                patchData.SizePatchOffset += stream_.Position;
            }

            if ( extra.Length > 0 ) {
                stream_.Write(extra, 0, extra.Length);
            }
        }