Beispiel #1
0
        // For AES the method in the entry is 99, and the real compression method is in the extradata
        //
        private void ProcessAESExtraData(ZipExtraData extraData)
        {
#if !NET_1_1 && !NETCF_2_0
            if (extraData.Find(0x9901))
            {
                // Set version and flag for Zipfile.CreateAndInitDecryptionStream
                versionToExtract = ZipConstants.VERSION_AES;                    // Ver 5.1 = AES see "Version" getter
                // Set StrongEncryption flag for ZipFile.CreateAndInitDecryptionStream
                Flags = Flags | (int)GeneralBitFlags.StrongEncryption;
                //
                // Unpack AES extra data field see http://www.winzip.com/aes_info.htm
                int length = extraData.ValueLength;                     // Data size currently 7
                if (length < 7)
                {
                    throw new ZipException("AES Extra Data Length " + length + " invalid.");
                }
                int ver            = extraData.ReadShort(); // Version number (1=AE-1 2=AE-2)
                int vendorId       = extraData.ReadShort(); // 2-character vendor ID 0x4541 = "AE"
                int encrStrength   = extraData.ReadByte();  // encryption strength 1 = 128 2 = 192 3 = 256
                int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file
                _aesVer = ver;
                _aesEncryptionStrength = encrStrength;
                method = (CompressionMethod)actualCompress;
            }
            else
            {
                throw new ZipException("AES Extra Data missing");
            }
#else
            throw new ZipException("AES unsupported");
#endif
        }
        /// <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;

            // No need to compress - definitely no data.
            if (entry.Size == 0)
            {
                entry.CompressedSize = entry.Size;
                entry.Crc = 0;
                method = CompressionMethod.Stored;
                headerInfoAvailable = true;
            }
            else
            {
                headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc;

                // Switch to deflation if storing isnt possible.
                if (method == CompressionMethod.Stored)
                {
                    if (!headerInfoAvailable)
                    {
                        if (!CanPatchEntries)
                        {
                            // Can't patch entries so storing is not possible.
                            method = CompressionMethod.Deflated;
                            compressionLevel = 0;
                        }
                    }
                    else // entry.size must be > 0
                    {
                        entry.CompressedSize = entry.Size;
                        headerInfoAvailable = entry.HasCrc;
                    }
                }
            }

            if (headerInfoAvailable == false)
            {
                if (CanPatchEntries == false)
                {
                    // Only way to record size and compressed size is to append a data descriptor
                    // after compressed data.

                    // Stored entries of this form have already been converted to deflating.
                    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)entry.CompressionMethodForHeader);
            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)
                {
                    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)
            {
                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);
            }

#if !NET_1_1 && !NETCF_2_0
            if (entry.AESKeySize > 0)
            {
                AddExtraDataAES(entry, ed);
            }
#endif
            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;
            // Fix offsetOfCentraldir for AES
            if (entry.AESKeySize > 0)
                offset += entry.AESOverheadSize;

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

            if (entry.IsCrypted)
            {
#if !NET_1_1 && !NETCF_2_0
                if (entry.AESKeySize > 0)
                {
                    WriteAESHeader(entry);
                }
                else
#endif
                {
                    if (entry.Crc < 0)
                    {			// so testing Zip will says its ok
                        WriteEncryptionHeader(entry.DosTime << 16);
                    }
                    else
                    {
                        WriteEncryptionHeader(entry.Crc);
                    }
                }
            }
        }
Beispiel #3
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))
            {
                // Version required to extract is ignored here as some archivers dont set it correctly
                // in theory it should be version 45 or higher

                // 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();
                }

                // Disk number on which file starts is ignored
            }
            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 < 4)
                {
                    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();
                }
            }
            if (method == CompressionMethod.WinZipAES)
            {
                ProcessAESExtraData(extraData);
            }
        }
Beispiel #4
0
        void WriteLocalEntryHeader(ZipUpdate update)
        {
            ZipEntry entry = update.OutEntry;

            // TODO: Local offset will require adjusting for multi-disk zip files.
            entry.Offset = baseStream_.Position;

            // TODO: Need to clear any entry flags that dont make sense or throw an exception here.
            if (update.Command != UpdateCommand.Copy)
            {
                if (entry.CompressionMethod == CompressionMethod.Deflated)
                {
                    if (entry.Size == 0)
                    {
                        // No need to compress - no data.
                        entry.CompressedSize = entry.Size;
                        entry.Crc = 0;
                        entry.CompressionMethod = CompressionMethod.Stored;
                    }
                }
                else if (entry.CompressionMethod == CompressionMethod.Stored)
                {
                    entry.Flags &= ~(int)GeneralBitFlags.Descriptor;
                }

                if (HaveKeys)
                {
                    entry.IsCrypted = true;
                    if (entry.Crc < 0)
                    {
                        entry.Flags |= (int)GeneralBitFlags.Descriptor;
                    }
                }
                else
                {
                    entry.IsCrypted = false;
                }

                switch (useZip64_)
                {
                    case UseZip64.Dynamic:
                        if (entry.Size < 0)
                        {
                            entry.ForceZip64();
                        }
                        break;

                    case UseZip64.On:
                        entry.ForceZip64();
                        break;

                    case UseZip64.Off:
                        // Do nothing.  The entry itself may be using Zip64 independantly.
                        break;
                }
            }

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

            WriteLEShort(entry.Version);
            WriteLEShort(entry.Flags);

            WriteLEShort((byte)entry.CompressionMethod);
            WriteLEInt((int)entry.DosTime);

            if (!entry.HasCrc)
            {
                // Note patch address for updating CRC later.
                update.CrcPatchOffset = baseStream_.Position;
                WriteLEInt((int)0);
            }
            else
            {
                WriteLEInt(unchecked((int)entry.Crc));
            }

            if (entry.LocalHeaderRequiresZip64)
            {
                WriteLEInt(-1);
                WriteLEInt(-1);
            }
            else
            {
                if ((entry.CompressedSize < 0) || (entry.Size < 0))
                {
                    update.SizePatchOffset = baseStream_.Position;
                }

                WriteLEInt((int)entry.CompressedSize);
                WriteLEInt((int)entry.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)
            {
                ed.StartNewEntry();

                // Local entry header always includes size and compressed size.
                // NOTE the order of these fields is reversed when compared to the normal headers!
                ed.AddLeLong(entry.Size);
                ed.AddLeLong(entry.CompressedSize);
                ed.AddNewEntry(1);
            }
            else
            {
                ed.Delete(1);
            }

            entry.ExtraData = ed.GetEntryData();

            WriteLEShort(name.Length);
            WriteLEShort(entry.ExtraData.Length);

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

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

                update.SizePatchOffset = baseStream_.Position + ed.CurrentReadIndex;
            }

            if (entry.ExtraData.Length > 0)
            {
                baseStream_.Write(entry.ExtraData, 0, entry.ExtraData.Length);
            }
        }
Beispiel #5
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;

            // No need to compress - definitely no data.
            if (entry.Size == 0)
            {
                entry.CompressedSize = entry.Size;
                entry.Crc            = 0;
                method = CompressionMethod.Stored;
                headerInfoAvailable = true;
            }
            else
            {
                headerInfoAvailable = (entry.Size >= 0) && entry.HasCrc;

                // Switch to deflation if storing isnt possible.
                if (method == CompressionMethod.Stored)
                {
                    if (!headerInfoAvailable)
                    {
                        if (!CanPatchEntries)
                        {
                            // Can't patch entries so storing is not possible.
                            method           = CompressionMethod.Deflated;
                            compressionLevel = 0;
                        }
                    }
                    else // entry.size must be > 0
                    {
                        entry.CompressedSize = entry.Size;
                        headerInfoAvailable  = entry.HasCrc;
                    }
                }
            }

            if (headerInfoAvailable == false)
            {
                if (CanPatchEntries == false)
                {
                    // Only way to record size and compressed size is to append a data descriptor
                    // after compressed data.

                    // Stored entries of this form have already been converted to deflating.
                    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)entry.CompressionMethodForHeader);
            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)
                {
                    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)
            {
                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);
            }

#if !NET_1_1 && !NETCF_2_0
            if (entry.AESKeySize > 0)
            {
                AddExtraDataAES(entry, ed);
            }
#endif
            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;
            // Fix offsetOfCentraldir for AES
            if (entry.AESKeySize > 0)
            {
                offset += entry.AESOverheadSize;
            }

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

            if (entry.IsCrypted)
            {
#if !NET_1_1 && !NETCF_2_0
                if (entry.AESKeySize > 0)
                {
                    WriteAESHeader(entry);
                }
                else
#endif
                {
                    if (entry.Crc < 0)
                    {                   // so testing Zip will says its ok
                        WriteEncryptionHeader(entry.DosTime << 16);
                    }
                    else
                    {
                        WriteEncryptionHeader(entry.Crc);
                    }
                }
            }
        }
Beispiel #6
0
        /// <summary>
        /// Test a local header against that provided from the central directory
        /// </summary>
        /// <param name="entry">
        /// The entry to test against
        /// </param>
        /// <param name="tests">The type of <see cref="HeaderTest">tests</see> to carry out.</param>
        /// <returns>The offset of the entries data in the file</returns>
        long TestLocalHeader(ZipEntry entry, HeaderTest tests)
        {
            lock (baseStream_)
            {
                bool testHeader = (tests & HeaderTest.Header) != 0;
                bool testData = (tests & HeaderTest.Extract) != 0;

                baseStream_.Seek(offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin);
                if ((int)ReadLEUint() != ZipConstants.LocalHeaderSignature)
                {
                    throw new ZipException(string.Format("Wrong local header signature @{0:X}", offsetOfFirstEntry + entry.Offset));
                }

                short extractVersion = (short)ReadLEUshort();
                short localFlags = (short)ReadLEUshort();
                short compressionMethod = (short)ReadLEUshort();
                short fileTime = (short)ReadLEUshort();
                short fileDate = (short)ReadLEUshort();
                uint crcValue = ReadLEUint();
                long compressedSize = ReadLEUint();
                long size = ReadLEUint();
                int storedNameLength = ReadLEUshort();
                int extraDataLength = ReadLEUshort();

                byte[] nameData = new byte[storedNameLength];
                StreamUtils.ReadFully(baseStream_, nameData);

                byte[] extraData = new byte[extraDataLength];
                StreamUtils.ReadFully(baseStream_, extraData);

                ZipExtraData localExtraData = new ZipExtraData(extraData);

                // Extra data / zip64 checks
                if (localExtraData.Find(1))
                {
                    // 2010-03-04 Forum 10512: removed checks for version >= ZipConstants.VersionZip64
                    // and size or compressedSize = MaxValue, due to rogue creators.

                    size = localExtraData.ReadLong();
                    compressedSize = localExtraData.ReadLong();

                    if ((localFlags & (int)GeneralBitFlags.Descriptor) != 0)
                    {
                        // These may be valid if patched later
                        if ((size != -1) && (size != entry.Size))
                        {
                            throw new ZipException("Size invalid for descriptor");
                        }

                        if ((compressedSize != -1) && (compressedSize != entry.CompressedSize))
                        {
                            throw new ZipException("Compressed size invalid for descriptor");
                        }
                    }
                }
                else
                {
                    // No zip64 extra data but entry requires it.
                    if ((extractVersion >= ZipConstants.VersionZip64) &&
                        (((uint)size == uint.MaxValue) || ((uint)compressedSize == uint.MaxValue)))
                    {
                        throw new ZipException("Required Zip64 extended information missing");
                    }
                }

                if (testData)
                {
                    if (entry.IsFile)
                    {
                        if (!entry.IsCompressionMethodSupported())
                        {
                            throw new ZipException("Compression method not supported");
                        }

                        if ((extractVersion > ZipConstants.VersionMadeBy)
                            || ((extractVersion > 20) && (extractVersion < ZipConstants.VersionZip64)))
                        {
                            throw new ZipException(string.Format("Version required to extract this entry not supported ({0})", extractVersion));
                        }

                        if ((localFlags & (int)(GeneralBitFlags.Patched | GeneralBitFlags.StrongEncryption | GeneralBitFlags.EnhancedCompress | GeneralBitFlags.HeaderMasked)) != 0)
                        {
                            throw new ZipException("The library does not support the zip version required to extract this entry");
                        }
                    }
                }

                if (testHeader)
                {
                    if ((extractVersion <= 63) &&	// Ignore later versions as we dont know about them..
                        (extractVersion != 10) &&
                        (extractVersion != 11) &&
                        (extractVersion != 20) &&
                        (extractVersion != 21) &&
                        (extractVersion != 25) &&
                        (extractVersion != 27) &&
                        (extractVersion != 45) &&
                        (extractVersion != 46) &&
                        (extractVersion != 50) &&
                        (extractVersion != 51) &&
                        (extractVersion != 52) &&
                        (extractVersion != 61) &&
                        (extractVersion != 62) &&
                        (extractVersion != 63)
                        )
                    {
                        throw new ZipException(string.Format("Version required to extract this entry is invalid ({0})", extractVersion));
                    }

                    // Local entry flags dont have reserved bit set on.
                    if ((localFlags & (int)(GeneralBitFlags.ReservedPKware4 | GeneralBitFlags.ReservedPkware14 | GeneralBitFlags.ReservedPkware15)) != 0)
                    {
                        throw new ZipException("Reserved bit flags cannot be set.");
                    }

                    // Encryption requires extract version >= 20
                    if (((localFlags & (int)GeneralBitFlags.Encrypted) != 0) && (extractVersion < 20))
                    {
                        throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion));
                    }

                    // Strong encryption requires encryption flag to be set and extract version >= 50.
                    if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0)
                    {
                        if ((localFlags & (int)GeneralBitFlags.Encrypted) == 0)
                        {
                            throw new ZipException("Strong encryption flag set but encryption flag is not set");
                        }

                        if (extractVersion < 50)
                        {
                            throw new ZipException(string.Format("Version required to extract this entry is too low for encryption ({0})", extractVersion));
                        }
                    }

                    // Patched entries require extract version >= 27
                    if (((localFlags & (int)GeneralBitFlags.Patched) != 0) && (extractVersion < 27))
                    {
                        throw new ZipException(string.Format("Patched data requires higher version than ({0})", extractVersion));
                    }

                    // Central header flags match local entry flags.
                    if (localFlags != entry.Flags)
                    {
                        throw new ZipException("Central header/local header flags mismatch");
                    }

                    // Central header compression method matches local entry
                    if (entry.CompressionMethod != (CompressionMethod)compressionMethod)
                    {
                        throw new ZipException("Central header/local header compression method mismatch");
                    }

                    if (entry.Version != extractVersion)
                    {
                        throw new ZipException("Extract version mismatch");
                    }

                    // Strong encryption and extract version match
                    if ((localFlags & (int)GeneralBitFlags.StrongEncryption) != 0)
                    {
                        if (extractVersion < 62)
                        {
                            throw new ZipException("Strong encryption flag set but version not high enough");
                        }
                    }

                    if ((localFlags & (int)GeneralBitFlags.HeaderMasked) != 0)
                    {
                        if ((fileTime != 0) || (fileDate != 0))
                        {
                            throw new ZipException("Header masked set but date/time values non-zero");
                        }
                    }

                    if ((localFlags & (int)GeneralBitFlags.Descriptor) == 0)
                    {
                        if (crcValue != (uint)entry.Crc)
                        {
                            throw new ZipException("Central header/local header crc mismatch");
                        }
                    }

                    // Crc valid for empty entry.
                    // This will also apply to streamed entries where size isnt known and the header cant be patched
                    if ((size == 0) && (compressedSize == 0))
                    {
                        if (crcValue != 0)
                        {
                            throw new ZipException("Invalid CRC for empty entry");
                        }
                    }

                    // TODO: make test more correct...  can't compare lengths as was done originally as this can fail for MBCS strings
                    // Assuming a code page at this point is not valid?  Best is to store the name length in the ZipEntry probably
                    if (entry.Name.Length > storedNameLength)
                    {
                        throw new ZipException("File name length mismatch");
                    }

                    // Name data has already been read convert it and compare.
                    string localName = ZipConstants.ConvertToStringExt(localFlags, nameData);

                    // Central directory and local entry name match
                    if (localName != entry.Name)
                    {
                        throw new ZipException("Central header and local header file name mismatch");
                    }

                    // Directories have zero actual size but can have compressed size
                    if (entry.IsDirectory)
                    {
                        if (size > 0)
                        {
                            throw new ZipException("Directory cannot have size");
                        }

                        // There may be other cases where the compressed size can be greater than this?
                        // If so until details are known we will be strict.
                        if (entry.IsCrypted)
                        {
                            if (compressedSize > ZipConstants.CryptoHeaderSize + 2)
                            {
                                throw new ZipException("Directory compressed size invalid");
                            }
                        }
                        else if (compressedSize > 2)
                        {
                            // When not compressed the directory size can validly be 2 bytes
                            // if the true size wasnt known when data was originally being written.
                            // NOTE: Versions of the library 0.85.4 and earlier always added 2 bytes
                            throw new ZipException("Directory compressed size invalid");
                        }
                    }

                    if (!ZipNameTransform.IsValidName(localName, true))
                    {
                        throw new ZipException("Name is invalid");
                    }
                }

                // Tests that apply to both data and header.

                // Size can be verified only if it is known in the local header.
                // it will always be known in the central header.
                if (((localFlags & (int)GeneralBitFlags.Descriptor) == 0) ||
                    ((size > 0) || (compressedSize > 0)))
                {

                    if (size != entry.Size)
                    {
                        throw new ZipException(
                            string.Format("Size mismatch between central header({0}) and local header({1})",
                                entry.Size, size));
                    }

                    if (compressedSize != entry.CompressedSize &&
                        compressedSize != 0xFFFFFFFF && compressedSize != -1)
                    {
                        throw new ZipException(
                            string.Format("Compressed size mismatch between central header({0}) and local header({1})",
                            entry.CompressedSize, compressedSize));
                    }
                }

                int extraLength = storedNameLength + extraDataLength;
                return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength;
            }
        }
        // For AES the method in the entry is 99, and the real compression method is in the extradata
        //
        private void ProcessAESExtraData(ZipExtraData extraData)
        {

#if !NET_1_1 && !NETCF_2_0
            if (extraData.Find(0x9901))
            {
                // Set version and flag for Zipfile.CreateAndInitDecryptionStream
                versionToExtract = ZipConstants.VERSION_AES;			// Ver 5.1 = AES see "Version" getter
                // Set StrongEncryption flag for ZipFile.CreateAndInitDecryptionStream
                Flags = Flags | (int)GeneralBitFlags.StrongEncryption;
                //
                // Unpack AES extra data field see http://www.winzip.com/aes_info.htm
                int length = extraData.ValueLength;			// Data size currently 7
                if (length < 7)
                    throw new ZipException("AES Extra Data Length " + length + " invalid.");
                int ver = extraData.ReadShort();			// Version number (1=AE-1 2=AE-2)
                int vendorId = extraData.ReadShort();		// 2-character vendor ID 0x4541 = "AE"
                int encrStrength = extraData.ReadByte();	// encryption strength 1 = 128 2 = 192 3 = 256
                int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file
                _aesVer = ver;
                _aesEncryptionStrength = encrStrength;
                method = (CompressionMethod)actualCompress;
            }
            else
                throw new ZipException("AES Extra Data missing");
#else
				throw new ZipException("AES unsupported");
#endif
        }
        /// <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))
            {
                // Version required to extract is ignored here as some archivers dont set it correctly
                // in theory it should be version 45 or higher

                // 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();
                }

                // Disk number on which file starts is ignored
            }
            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 < 4)
                {
                    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();
                }
            }
            if (method == CompressionMethod.WinZipAES)
            {
                ProcessAESExtraData(extraData);
            }
        }
        // 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);
            }
        }
        // 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);
            }
        }