private static void AddExtraDataAes(ZipEntry entry, ZipExtraData extraData) { extraData.StartNewEntry(); extraData.AddLeShort(2); extraData.AddLeShort(0x4541); extraData.AddData(entry.AesEncryptionStrength); extraData.AddLeShort((int)entry.CompressionMethod); extraData.AddNewEntry(0x9901); }
internal void ProcessExtraData(bool localHeader) { ZipExtraData zipExtraData = new ZipExtraData(this.extra); if (zipExtraData.Find(1)) { if ((this.versionToExtract & 255) < 45) { throw new ZipException("Zip64 Extended information found but version is not valid"); } this.forceZip64_ = true; if (zipExtraData.ValueLength < 4) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if (localHeader || this.size == unchecked ((ulong)-1)) { this.size = (ulong)zipExtraData.ReadLong(); } if (localHeader || this.compressedSize == unchecked ((ulong)-1)) { this.compressedSize = (ulong)zipExtraData.ReadLong(); } } else if ((this.versionToExtract & 255) >= 45 && (this.size == unchecked ((ulong)-1) || this.compressedSize == unchecked ((ulong)-1))) { throw new ZipException("Zip64 Extended information required but is missing."); } if (zipExtraData.Find(21589)) { int valueLength = zipExtraData.ValueLength; int num = zipExtraData.ReadByte(); if ((num & 1) != 0 && valueLength >= 5) { int seconds = zipExtraData.ReadInt(); this.DateTime = (new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime() + new TimeSpan(0, 0, 0, seconds, 0)).ToLocalTime(); } } }
private void ProcessAesExtraData(ZipExtraData extraData) { if (!extraData.Find(0x9901)) { throw new ZipException("AES Extra Data missing"); } _versionToExtract = 0x33; Flags |= 0x40; int valueLength = extraData.ValueLength; if (valueLength < 7) { throw new ZipException("AES Extra Data Length " + valueLength + " invalid."); } extraData.ReadShort(); extraData.ReadShort(); int num3 = extraData.ReadByte(); int num4 = extraData.ReadShort(); _aesEncryptionStrength = num3; _method = (CompressionMethod)num4; }
public override void Finish() { if (_entries != null) { if (_curEntry != null) { CloseEntry(); } long count = _entries.Count; long sizeEntries = 0L; foreach (ZipEntry entry in _entries) { WriteLeInt(ZipConstants.CentralHeaderSignature); WriteLeShort(0x33); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((short)entry.CompressionMethodForHeader); WriteLeInt((int)entry.DosTime); WriteLeInt((int)entry.Crc); if (entry.IsZip64Forced() || (entry.CompressedSize >= 0xffffffffL)) { WriteLeInt(-1); } else { WriteLeInt((int)entry.CompressedSize); } if (entry.IsZip64Forced() || (entry.Size >= 0xffffffffL)) { WriteLeInt(-1); } else { WriteLeInt((int)entry.Size); } byte[] buffer = ZipConstants.ConvertToArray(entry.Flags, entry.Name); if (buffer.Length > 0xffff) { throw new ZipException("Name too long."); } ZipExtraData extraData = new ZipExtraData(entry.ExtraData); if (entry.CentralHeaderRequiresZip64) { extraData.StartNewEntry(); if (entry.IsZip64Forced() || (entry.Size >= 0xffffffffL)) { extraData.AddLeLong(entry.Size); } if (entry.IsZip64Forced() || (entry.CompressedSize >= 0xffffffffL)) { extraData.AddLeLong(entry.CompressedSize); } if (entry.Offset >= 0xffffffffL) { extraData.AddLeLong(entry.Offset); } extraData.AddNewEntry(1); } else { extraData.Delete(1); } if (entry.AesKeySize > 0) { AddExtraDataAes(entry, extraData); } byte[] entryData = extraData.GetEntryData(); byte[] buffer3 = (entry.Comment != null) ? ZipConstants.ConvertToArray(entry.Flags, entry.Comment) : new byte[0]; if (buffer3.Length > 0xffff) { throw new ZipException("Comment too long."); } WriteLeShort(buffer.Length); WriteLeShort(entryData.Length); WriteLeShort(buffer3.Length); WriteLeShort(0); WriteLeShort(0); if (entry.ExternalFileAttributes != -1) { WriteLeInt(entry.ExternalFileAttributes); } else if (entry.IsDirectory) { WriteLeInt(0x10); } else { WriteLeInt(0); } if (entry.Offset >= 0xffffffffL) { WriteLeInt(-1); } else { WriteLeInt((int)entry.Offset); } if (buffer.Length > 0) { BaseOutputStream.Write(buffer, 0, buffer.Length); } if (entryData.Length > 0) { BaseOutputStream.Write(entryData, 0, entryData.Length); } if (buffer3.Length > 0) { BaseOutputStream.Write(buffer3, 0, buffer3.Length); } sizeEntries += ((ZipConstants.CentralHeaderBaseSize + buffer.Length) + entryData.Length) + buffer3.Length; } using (ZipHelperStream stream = new ZipHelperStream(BaseOutputStream)) { stream.WriteEndOfCentralDirectory(count, sizeEntries, _offset, _zipComment); } _entries = null; } }
private int WriteCentralDirectoryHeader(ZipEntry entry) { if (entry.CompressedSize < 0L) { throw new ZipException("Attempt to write central directory entry with unknown csize"); } if (entry.Size < 0L) { throw new ZipException("Attempt to write central directory entry with unknown size"); } if (entry.Crc < 0L) { throw new ZipException("Attempt to write central directory entry with unknown crc"); } WriteLeInt(ZipConstants.CentralHeaderSignature); WriteLeShort(0x33); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((byte)entry.CompressionMethod); WriteLeInt((int)entry.DosTime); WriteLeInt((int)entry.Crc); if (entry.IsZip64Forced() || (entry.CompressedSize >= 0xffffffffL)) { WriteLeInt(-1); } else { WriteLeInt((int)(((ulong)entry.CompressedSize) & 0xffffffffL)); } if (entry.IsZip64Forced() || (entry.Size >= 0xffffffffL)) { WriteLeInt(-1); } else { WriteLeInt((int)entry.Size); } var buffer = ZipConstants.ConvertToArray(entry.Flags, entry.Name); if (buffer.Length > 0xffff) { throw new ZipException("Entry name is too long."); } WriteLeShort(buffer.Length); var data = new ZipExtraData(entry.ExtraData); if (entry.CentralHeaderRequiresZip64) { data.StartNewEntry(); if ((entry.Size >= 0xffffffffL) || (_useZip64 == UseZip64.On)) { data.AddLeLong(entry.Size); } if ((entry.CompressedSize >= 0xffffffffL) || (_useZip64 == UseZip64.On)) { data.AddLeLong(entry.CompressedSize); } if (entry.Offset >= 0xffffffffL) { data.AddLeLong(entry.Offset); } data.AddNewEntry(1); } else { data.Delete(1); } var entryData = data.GetEntryData(); WriteLeShort(entryData.Length); WriteLeShort(entry.Comment?.Length ?? 0); WriteLeShort(0); WriteLeShort(0); if (entry.ExternalFileAttributes != -1) { WriteLeInt(entry.ExternalFileAttributes); } else if (entry.IsDirectory) { WriteLeUint(0x10); } else { WriteLeUint(0); } if (entry.Offset >= 0xffffffffL) { WriteLeUint(uint.MaxValue); } else { WriteLeUint((uint)((int)entry.Offset)); } if (buffer.Length > 0) { _baseStream.Write(buffer, 0, buffer.Length); } if (entryData.Length > 0) { _baseStream.Write(entryData, 0, entryData.Length); } var buffer3 = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : new byte[0]; if (buffer3.Length > 0) { _baseStream.Write(buffer3, 0, buffer3.Length); } return (((0x2e + buffer.Length) + entryData.Length) + buffer3.Length); }
public override void Finish() { if (this.entries == null) { return; } if (this.curEntry != null) { this.CloseEntry(); } long noOfEntries = (long)this.entries.Count; long num = 0L; foreach (object obj in this.entries) { ZipEntry zipEntry = (ZipEntry)obj; this.WriteLeInt(33639248); this.WriteLeShort(45); this.WriteLeShort(zipEntry.Version); this.WriteLeShort(zipEntry.Flags); this.WriteLeShort((int)((short)zipEntry.CompressionMethod)); this.WriteLeInt((int)zipEntry.DosTime); this.WriteLeInt((int)zipEntry.Crc); if (zipEntry.IsZip64Forced() || zipEntry.CompressedSize >= unchecked ((long)((ulong)-1))) { this.WriteLeInt(-1); } else { this.WriteLeInt((int)zipEntry.CompressedSize); } if (zipEntry.IsZip64Forced() || zipEntry.Size >= unchecked ((long)((ulong)-1))) { this.WriteLeInt(-1); } else { this.WriteLeInt((int)zipEntry.Size); } byte[] array = ZipConstants.ConvertToArray(zipEntry.Flags, zipEntry.Name); if (array.Length > 65535) { throw new ZipException("Name too long."); } ZipExtraData zipExtraData = new ZipExtraData(zipEntry.ExtraData); if (zipEntry.CentralHeaderRequiresZip64) { zipExtraData.StartNewEntry(); if (zipEntry.IsZip64Forced() || zipEntry.Size >= unchecked ((long)((ulong)-1))) { zipExtraData.AddLeLong(zipEntry.Size); } if (zipEntry.IsZip64Forced() || zipEntry.CompressedSize >= unchecked ((long)((ulong)-1))) { zipExtraData.AddLeLong(zipEntry.CompressedSize); } if (zipEntry.Offset >= unchecked ((long)((ulong)-1))) { zipExtraData.AddLeLong(zipEntry.Offset); } zipExtraData.AddNewEntry(1); } else { zipExtraData.Delete(1); } byte[] entryData = zipExtraData.GetEntryData(); byte[] array2 = (zipEntry.Comment != null) ? ZipConstants.ConvertToArray(zipEntry.Flags, zipEntry.Comment) : new byte[0]; if (array2.Length > 65535) { throw new ZipException("Comment too long."); } this.WriteLeShort(array.Length); this.WriteLeShort(entryData.Length); this.WriteLeShort(array2.Length); this.WriteLeShort(0); this.WriteLeShort(0); if (zipEntry.ExternalFileAttributes != -1) { this.WriteLeInt(zipEntry.ExternalFileAttributes); } else if (zipEntry.IsDirectory) { this.WriteLeInt(16); } else { this.WriteLeInt(0); } if (zipEntry.Offset >= unchecked ((long)((ulong)-1))) { this.WriteLeInt(-1); } else { this.WriteLeInt((int)zipEntry.Offset); } if (array.Length > 0) { this.baseOutputStream.Write(array, 0, array.Length); } if (entryData.Length > 0) { this.baseOutputStream.Write(entryData, 0, entryData.Length); } if (array2.Length > 0) { this.baseOutputStream.Write(array2, 0, array2.Length); } num += (long)(46 + array.Length + entryData.Length + array2.Length); } using (ZipHelperStream zipHelperStream = new ZipHelperStream(this.baseOutputStream)) { zipHelperStream.WriteEndOfCentralDirectory(noOfEntries, num, this.offset, this.zipComment); } this.entries = null; }
int WriteCentralDirectoryHeader(ZipEntry entry) { // Write the central file header WriteLEInt(ZipConstants.CentralHeaderSignature); // Version made by WriteLEShort(ZipConstants.VersionMadeBy); // Version required to extract WriteLEShort(entry.Version); WriteLEShort(entry.Flags); WriteLEShort((byte)entry.CompressionMethod); WriteLEInt((int)entry.DosTime); WriteLEInt((int)entry.Crc); if ( entry.CompressedSize >= 0xffffffff ) { WriteLEInt(-1); } else { WriteLEInt((int)(entry.CompressedSize & 0xffffffff)); } if ( entry.Size >= 0xffffffff ) { WriteLEInt(-1); } else { WriteLEInt((int)entry.Size); } byte[] name = ZipConstants.ConvertToArray(entry.Flags, entry.Name); if ( name.Length > 0xFFFF ) { throw new ZipException("Entry name is too long."); } WriteLEShort(name.Length); // Central header extra data is different to local header version so regenerate. ZipExtraData ed = new ZipExtraData(entry.ExtraData); if ( entry.CentralHeaderRequiresZip64 ) { ed.StartNewEntry(); if ( (entry.Size >= 0xffffffff) || (useZip64_ == UseZip64.On) ) { ed.AddLeLong(entry.Size); } if ( (entry.CompressedSize >= 0xffffffff) || (useZip64_ == UseZip64.On) ) { ed.AddLeLong(entry.CompressedSize); } if ( entry.Offset >= 0xffffffff ) { ed.AddLeLong(entry.Offset); } // Number of disk on which this file starts isnt supported and is never written here. ed.AddNewEntry(1); } else { // Should have already be done when local header was added. ed.Delete(1); } byte[] centralExtraData = ed.GetEntryData(); WriteLEShort(centralExtraData.Length); WriteLEShort(entry.Comment != null ? entry.Comment.Length : 0); WriteLEShort(0); // disk number WriteLEShort(0); // internal file attributes // External file attributes... if ( entry.ExternalFileAttributes != -1 ) { WriteLEInt(entry.ExternalFileAttributes); } else { if ( entry.IsDirectory ) { WriteLEUint(16); } else { WriteLEUint(0); } } if ( entry.Offset >= 0xffffffff ) { WriteLEUint(0xffffffff); } else { WriteLEUint((uint)(int)entry.Offset); } if ( name.Length > 0 ) { baseStream_.Write(name, 0, name.Length); } if ( centralExtraData.Length > 0 ) { baseStream_.Write(centralExtraData, 0, centralExtraData.Length); } byte[] rawComment = (entry.Comment != null) ? Encoding.ASCII.GetBytes(entry.Comment) : new byte[0]; if ( rawComment.Length > 0 ) { baseStream_.Write(rawComment, 0, rawComment.Length); } return ZipConstants.CentralHeaderBaseSize + name.Length + centralExtraData.Length + rawComment.Length; }
/// <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(); } } else { if ( ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))) { throw new ZipException("Zip64 Extended information required but is missing."); } } /* TODO: Testing for handling of windows extra data * 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(); } } }
/// <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); } } }
internal void ProcessExtraData(bool localHeader) { ZipExtraData extraData = new ZipExtraData(_extra); if (extraData.Find(1)) { _forceZip64 = true; if (extraData.ValueLength < 4) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if (localHeader || (_size == 0xffffffffL)) { _size = (ulong)extraData.ReadLong(); } if (localHeader || (_compressedSize == 0xffffffffL)) { _compressedSize = (ulong)extraData.ReadLong(); } if (!localHeader && (Offset == 0xffffffffL)) { Offset = extraData.ReadLong(); } } else if (((_versionToExtract & 0xff) >= 0x2d) && ((_size == 0xffffffffL) || (_compressedSize == 0xffffffffL))) { throw new ZipException("Zip64 Extended information required but is missing."); } if (extraData.Find(10)) { if (extraData.ValueLength < 4) { throw new ZipException("NTFS Extra data invalid"); } extraData.ReadInt(); while (extraData.UnreadCount >= 4) { int num = extraData.ReadShort(); int amount = extraData.ReadShort(); if (num == 1) { if (amount >= 0x18) { long fileTime = extraData.ReadLong(); extraData.ReadLong(); extraData.ReadLong(); DateTime = DateTime.FromFileTime(fileTime); } break; } extraData.Skip(amount); } } else if (extraData.Find(0x5455)) { int valueLength = extraData.ValueLength; if (((extraData.ReadByte() & 1) != 0) && (valueLength >= 5)) { int seconds = extraData.ReadInt(); DateTime time = new DateTime(0x7b2, 1, 1, 0, 0, 0); DateTime = (time.ToUniversalTime() + new TimeSpan(0, 0, 0, seconds, 0)).ToLocalTime(); } } if (_method == CompressionMethod.WinZipAes) { ProcessAesExtraData(extraData); } }
private void WriteLocalEntryHeader(ZipUpdate update) { var outEntry = update.OutEntry; outEntry.Offset = _baseStream.Position; if (update.Command != UpdateCommand.Copy) { if (outEntry.CompressionMethod == CompressionMethod.Deflated) { if (outEntry.Size == 0L) { outEntry.CompressedSize = outEntry.Size; outEntry.Crc = 0L; outEntry.CompressionMethod = CompressionMethod.Stored; } } else if (outEntry.CompressionMethod == CompressionMethod.Stored) { outEntry.Flags &= -9; } if (HaveKeys) { outEntry.IsCrypted = true; if (outEntry.Crc < 0L) { outEntry.Flags |= 8; } } else { outEntry.IsCrypted = false; } switch (_useZip64) { case UseZip64.On: outEntry.ForceZip64(); break; case UseZip64.Dynamic: if (outEntry.Size < 0L) { outEntry.ForceZip64(); } break; } } WriteLeInt(ZipConstants.LocalHeaderSignature); WriteLeShort(outEntry.Version); WriteLeShort(outEntry.Flags); WriteLeShort((byte)outEntry.CompressionMethod); WriteLeInt((int)outEntry.DosTime); if (!outEntry.HasCrc) { update.CrcPatchOffset = _baseStream.Position; WriteLeInt(0); } else { WriteLeInt((int)outEntry.Crc); } if (outEntry.LocalHeaderRequiresZip64) { WriteLeInt(-1); WriteLeInt(-1); } else { if ((outEntry.CompressedSize < 0L) || (outEntry.Size < 0L)) { update.SizePatchOffset = _baseStream.Position; } WriteLeInt((int)outEntry.CompressedSize); WriteLeInt((int)outEntry.Size); } var buffer = ZipConstants.ConvertToArray(outEntry.Flags, outEntry.Name); if (buffer.Length > 0xffff) { throw new ZipException("Entry name too long."); } var data = new ZipExtraData(outEntry.ExtraData); if (outEntry.LocalHeaderRequiresZip64) { data.StartNewEntry(); data.AddLeLong(outEntry.Size); data.AddLeLong(outEntry.CompressedSize); data.AddNewEntry(1); } else { data.Delete(1); } outEntry.ExtraData = data.GetEntryData(); WriteLeShort(buffer.Length); WriteLeShort(outEntry.ExtraData.Length); if (buffer.Length > 0) { _baseStream.Write(buffer, 0, buffer.Length); } if (outEntry.LocalHeaderRequiresZip64) { if (!data.Find(1)) { throw new ZipException("Internal error cannot find extra data"); } update.SizePatchOffset = _baseStream.Position + data.CurrentReadIndex; } if (outEntry.ExtraData.Length > 0) { _baseStream.Write(outEntry.ExtraData, 0, outEntry.ExtraData.Length); } }
public void PutNextEntry(ZipEntry entry) { bool hasCrc; if (entry == null) { throw new ArgumentNullException(nameof(entry)); } if (_entries == null) { throw new InvalidOperationException("ZipOutputStream was finished"); } if (_curEntry != null) { CloseEntry(); } if (_entries.Count == 0x7fffffff) { throw new ZipException("Too many entries for Zip file"); } CompressionMethod compressionMethod = entry.CompressionMethod; int compressionLevel = _defaultCompressionLevel; entry.Flags &= 0x800; _patchEntryHeader = false; if (entry.Size == 0L) { entry.CompressedSize = entry.Size; entry.Crc = 0L; compressionMethod = CompressionMethod.Stored; hasCrc = true; } else { hasCrc = (entry.Size >= 0L) && entry.HasCrc; if (compressionMethod == CompressionMethod.Stored) { if (!hasCrc) { if (!CanPatchEntries) { compressionMethod = CompressionMethod.Deflated; compressionLevel = 0; } } else { entry.CompressedSize = entry.Size; hasCrc = entry.HasCrc; } } } if (!hasCrc) { if (!CanPatchEntries) { entry.Flags |= 8; } else { _patchEntryHeader = true; } } if (Password != null) { entry.IsCrypted = true; if (entry.Crc < 0L) { entry.Flags |= 8; } } entry.Offset = _offset; entry.CompressionMethod = compressionMethod; _curMethod = compressionMethod; _sizePatchPos = -1L; if ((_useZip64 == UseZip64.On) || ((entry.Size < 0L) && (_useZip64 == UseZip64.Dynamic))) { entry.ForceZip64(); } WriteLeInt(ZipConstants.LocalHeaderSignature); WriteLeShort(entry.Version); WriteLeShort(entry.Flags); WriteLeShort((byte)entry.CompressionMethodForHeader); WriteLeInt((int)entry.DosTime); if (hasCrc) { WriteLeInt((int)entry.Crc); if (entry.LocalHeaderRequiresZip64) { WriteLeInt(-1); WriteLeInt(-1); } else { WriteLeInt(entry.IsCrypted ? (((int)entry.CompressedSize) + 12) : ((int)entry.CompressedSize)); WriteLeInt((int)entry.Size); } } else { if (_patchEntryHeader) { _crcPatchPos = BaseOutputStream.Position; } WriteLeInt(0); if (_patchEntryHeader) { _sizePatchPos = BaseOutputStream.Position; } if (entry.LocalHeaderRequiresZip64 || _patchEntryHeader) { WriteLeInt(-1); WriteLeInt(-1); } else { WriteLeInt(0); WriteLeInt(0); } } byte[] buffer = ZipConstants.ConvertToArray(entry.Flags, entry.Name); if (buffer.Length > 0xffff) { throw new ZipException("Entry name too long."); } ZipExtraData extraData = new ZipExtraData(entry.ExtraData); if (entry.LocalHeaderRequiresZip64) { extraData.StartNewEntry(); if (hasCrc) { extraData.AddLeLong(entry.Size); extraData.AddLeLong(entry.CompressedSize); } else { extraData.AddLeLong(-1L); extraData.AddLeLong(-1L); } extraData.AddNewEntry(1); if (!extraData.Find(1)) { throw new ZipException("Internal error cant find extra data"); } if (_patchEntryHeader) { _sizePatchPos = extraData.CurrentReadIndex; } } else { extraData.Delete(1); } if (entry.AesKeySize > 0) { AddExtraDataAes(entry, extraData); } byte[] entryData = extraData.GetEntryData(); WriteLeShort(buffer.Length); WriteLeShort(entryData.Length); if (buffer.Length > 0) { BaseOutputStream.Write(buffer, 0, buffer.Length); } if (entry.LocalHeaderRequiresZip64 && _patchEntryHeader) { _sizePatchPos += BaseOutputStream.Position; } if (entryData.Length > 0) { BaseOutputStream.Write(entryData, 0, entryData.Length); } _offset += (30 + buffer.Length) + entryData.Length; if (entry.AesKeySize > 0) { _offset += entry.AesOverheadSize; } _curEntry = entry; _crc.Reset(); if (compressionMethod == CompressionMethod.Deflated) { Deflater.Reset(); Deflater.SetLevel(compressionLevel); } _size = 0L; if (entry.IsCrypted) { if (entry.AesKeySize > 0) { WriteAesHeader(entry); } else if (entry.Crc < 0L) { WriteEncryptionHeader(entry.DosTime << 0x10); } else { WriteEncryptionHeader(entry.Crc); } } }
/// <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; 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) { 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(); 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) { 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> /// 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; }
/// <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; }
/// <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 size = ReadLEUint(); long compressedSize = ReadLEUint(); int storedNameLength = ReadLEUshort(); int extraDataLength = ReadLEUshort(); // eliminate system id from version (use version only) extractVersion &= 0x00FF; 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"); } // 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. 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"); } byte[] nameData = new byte[storedNameLength]; StreamUtils.ReadFully(baseStream_, nameData); 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 size. if ( entry.IsDirectory ) { if ( (compressedSize != 0) || (size != 0) ) { throw new ZipException("Directory cannot have size"); } } if ( !ZipNameTransform.IsValidName(localName, true) ) { throw new ZipException("Name is invalid"); } byte[] data = new byte[extraDataLength]; StreamUtils.ReadFully(baseStream_, data); ZipExtraData ed = new ZipExtraData(data); // Extra data / zip64 checks if ( ed.Find(1) ) { // Zip64 extra data but 'extract version' is too low if ( extractVersion < ZipConstants.VersionZip64 ) { throw new ZipException( string.Format("Extra data contains Zip64 information but version {0}.{1} is not high enough", extractVersion / 10, extractVersion % 10)); } // Zip64 extra data but size fields dont indicate its required. if ( (( uint )size != uint.MaxValue) && (( uint )compressedSize != uint.MaxValue) ) { throw new ZipException("Entry sizes not correct for Zip64"); } size = ed.ReadLong(); compressedSize = ed.ReadLong(); } 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"); } } } int extraLength = storedNameLength + extraDataLength; return offsetOfFirstEntry + entry.Offset + ZipConstants.LocalHeaderBaseSize + extraLength; } }
/// <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(); } } }
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 later. update.CrcPatchOffset = baseStream_.Position; WriteLEInt(( int )0); } else { WriteLEInt(( 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 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); } }
// 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); } }
public void PutNextEntry(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException("entry"); } if (this.entries == null) { throw new InvalidOperationException("ZipOutputStream was finished"); } if (this.curEntry != null) { this.CloseEntry(); } if (this.entries.Count == 2147483647) { throw new ZipException("Too many entries for Zip file"); } CompressionMethod compressionMethod = entry.CompressionMethod; int level = this.defaultCompressionLevel; entry.Flags &= 2048; this.patchEntryHeader = false; bool flag = true; if (compressionMethod == CompressionMethod.Stored) { entry.Flags &= -9; if (entry.CompressedSize >= 0L) { if (entry.Size < 0L) { entry.Size = entry.CompressedSize; } else if (entry.Size != entry.CompressedSize) { throw new ZipException("Method STORED, but compressed size != size"); } } else if (entry.Size >= 0L) { entry.CompressedSize = entry.Size; } if (entry.Size < 0L || entry.Crc < 0L) { if (base.CanPatchEntries) { flag = false; } else { compressionMethod = CompressionMethod.Deflated; level = 0; } } } if (compressionMethod == CompressionMethod.Deflated) { if (entry.Size == 0L) { entry.CompressedSize = entry.Size; entry.Crc = 0L; compressionMethod = CompressionMethod.Stored; } else if (entry.CompressedSize < 0L || entry.Size < 0L || entry.Crc < 0L) { flag = false; } } if (!flag) { if (!base.CanPatchEntries) { entry.Flags |= 8; } else { this.patchEntryHeader = true; } } if (base.Password != null) { entry.IsCrypted = true; if (entry.Crc < 0L) { entry.Flags |= 8; } } entry.Offset = this.offset; entry.CompressionMethod = compressionMethod; this.curMethod = compressionMethod; if (this.useZip64_ == UseZip64.On || (entry.Size < 0L && this.useZip64_ == UseZip64.Dynamic)) { entry.ForceZip64(); } this.WriteLeInt(67324752); this.WriteLeShort(entry.Version); this.WriteLeShort(entry.Flags); this.WriteLeShort((int)((byte)compressionMethod)); this.WriteLeInt((int)entry.DosTime); if (flag) { this.WriteLeInt((int)entry.Crc); if (entry.LocalHeaderRequiresZip64) { this.WriteLeInt(-1); this.WriteLeInt(-1); } else { this.WriteLeInt(entry.IsCrypted ? ((int)entry.CompressedSize + 12) : ((int)entry.CompressedSize)); this.WriteLeInt((int)entry.Size); } } else { if (this.patchEntryHeader) { this.crcPatchPos = this.baseOutputStream.Position; } this.WriteLeInt(0); if (this.patchEntryHeader) { this.sizePatchPos = this.baseOutputStream.Position; } if (entry.LocalHeaderRequiresZip64) { this.WriteLeInt(-1); this.WriteLeInt(-1); } else { this.WriteLeInt(0); this.WriteLeInt(0); } } byte[] array = ZipConstants.ConvertToArray(entry.Flags, entry.Name); if (array.Length > 65535) { throw new ZipException("Entry name too long."); } ZipExtraData zipExtraData = new ZipExtraData(entry.ExtraData); if (entry.LocalHeaderRequiresZip64) { zipExtraData.StartNewEntry(); zipExtraData.AddLeLong(-1L); zipExtraData.AddLeLong(-1L); zipExtraData.AddNewEntry(1); if (!zipExtraData.Find(1)) { throw new ZipException("Internal error cant find extra data"); } if (this.patchEntryHeader) { this.sizePatchPos = (long)zipExtraData.CurrentReadIndex; } } else { zipExtraData.Delete(1); } byte[] entryData = zipExtraData.GetEntryData(); this.WriteLeShort(array.Length); this.WriteLeShort(entryData.Length); if (array.Length > 0) { this.baseOutputStream.Write(array, 0, array.Length); } if (entry.LocalHeaderRequiresZip64 && this.patchEntryHeader) { this.sizePatchPos += this.baseOutputStream.Position; } if (entryData.Length > 0) { this.baseOutputStream.Write(entryData, 0, entryData.Length); } this.offset += (long)(30 + array.Length + entryData.Length); this.curEntry = entry; this.crc.Reset(); if (compressionMethod == CompressionMethod.Deflated) { this.def.Reset(); this.def.SetLevel(level); } this.size = 0L; if (entry.IsCrypted) { if (entry.Crc < 0L) { this.WriteEncryptionHeader(entry.DosTime << 16); return; } this.WriteEncryptionHeader(entry.Crc); } }
private long TestLocalHeader(ZipEntry entry, HeaderTest tests) { lock (_baseStream) { var flag = (tests & HeaderTest.Header) != 0; var flag2 = (tests & HeaderTest.Extract) != 0; _baseStream.Seek(_offsetOfFirstEntry + entry.Offset, SeekOrigin.Begin); if (ReadLeUint() != ZipConstants.LocalHeaderSignature) { throw new ZipException($"Wrong local header signature @{_offsetOfFirstEntry + entry.Offset:X}"); } var num = (short)ReadLeUshort(); var flags = (short)ReadLeUshort(); var num3 = (short)ReadLeUshort(); var num4 = (short)ReadLeUshort(); var num5 = (short)ReadLeUshort(); var num6 = ReadLeUint(); long num7 = ReadLeUint(); long num8 = ReadLeUint(); int num9 = ReadLeUshort(); int num10 = ReadLeUshort(); var buffer = new byte[num9]; StreamUtils.ReadFully(_baseStream, buffer); var buffer2 = new byte[num10]; StreamUtils.ReadFully(_baseStream, buffer2); var data = new ZipExtraData(buffer2); if (data.Find(1)) { num8 = data.ReadLong(); num7 = data.ReadLong(); if ((flags & 8) != 0) { if ((num8 != -1L) && (num8 != entry.Size)) { throw new ZipException("Size invalid for descriptor"); } if ((num7 != -1L) && (num7 != entry.CompressedSize)) { throw new ZipException("Compressed size invalid for descriptor"); } } } else if ((num >= 0x2d) && ((((uint)num8) == uint.MaxValue) || (((uint)num7) == uint.MaxValue))) { throw new ZipException("Required Zip64 extended information missing"); } if (flag2 && entry.IsFile) { if (!entry.IsCompressionMethodSupported()) { throw new ZipException("Compression method not supported"); } if ((num > 0x33) || ((num > 20) && (num < 0x2d))) { throw new ZipException($"Version required to extract this entry not supported ({num})"); } if ((flags & 0x3060) != 0) { throw new ZipException("The library does not support the zip version required to extract this entry"); } } if (flag) { if (((((num <= 0x3f) && (num != 10)) && ((num != 11) && (num != 20))) && (((num != 0x15) && (num != 0x19)) && ((num != 0x1b) && (num != 0x2d)))) && ((((num != ZipConstants.CentralHeaderBaseSize) && (num != 50)) && ((num != 0x33) && (num != 0x34))) && (((num != 0x3d) && (num != 0x3e)) && (num != 0x3f)))) { throw new ZipException($"Version required to extract this entry is invalid ({num})"); } if ((flags & 0xc010) != 0) { throw new ZipException("Reserved bit flags cannot be set."); } if (((flags & 1) != 0) && (num < 20)) { throw new ZipException( $"Version required to extract this entry is too low for encryption ({num})"); } if ((flags & 0x40) != 0) { if ((flags & 1) == 0) { throw new ZipException("Strong encryption flag set but encryption flag is not set"); } if (num < 50) { throw new ZipException( $"Version required to extract this entry is too low for encryption ({num})"); } } if (((flags & 0x20) != 0) && (num < 0x1b)) { throw new ZipException($"Patched data requires higher version than ({num})"); } if (flags != entry.Flags) { throw new ZipException("Central header/local header flags mismatch"); } if (entry.CompressionMethod != ((CompressionMethod)num3)) { throw new ZipException("Central header/local header compression method mismatch"); } if (entry.Version != num) { throw new ZipException("Extract version mismatch"); } if (((flags & 0x40) != 0) && (num < 0x3e)) { throw new ZipException("Strong encryption flag set but version not high enough"); } if (((flags & 0x2000) != 0) && ((num4 != 0) || (num5 != 0))) { throw new ZipException("Header masked set but date/time values non-zero"); } if (((flags & 8) == 0) && (num6 != ((uint)entry.Crc))) { throw new ZipException("Central header/local header crc mismatch"); } if (((num8 == 0L) && (num7 == 0L)) && (num6 != 0)) { throw new ZipException("Invalid CRC for empty entry"); } if (entry.Name.Length > num9) { throw new ZipException("File name length mismatch"); } var name = ZipConstants.ConvertToStringExt(flags, buffer); if (name != entry.Name) { throw new ZipException("Central header and local header file name mismatch"); } if (entry.IsDirectory) { if (num8 > 0L) { throw new ZipException("Directory cannot have size"); } if (entry.IsCrypted) { if (num7 > 14L) { throw new ZipException("Directory compressed size invalid"); } } else if (num7 > 2L) { throw new ZipException("Directory compressed size invalid"); } } if (!ZipNameTransform.IsValidName(name, true)) { throw new ZipException("Name is invalid"); } } if ((((flags & 8) == 0) || (num8 > 0L)) || (num7 > 0L)) { if (num8 != entry.Size) { throw new ZipException( $"Size mismatch between central header({entry.Size}) and local header({num8})"); } if (((num7 != entry.CompressedSize) && (num7 != 0xffffffffL)) && (num7 != -1L)) { throw new ZipException( $"Compressed size mismatch between central header({entry.CompressedSize}) and local header({num7})"); } } var num11 = num9 + num10; return (((_offsetOfFirstEntry + entry.Offset) + 30L) + num11); } }