private static UnixTime GetTimestamp(ZipEntry entry) { var oldTimestamp = new ZipExtraData(entry.ExtraData).GetData <OldUnixExtraData>()?.ModificationTime; if (oldTimestamp.HasValue) { return(oldTimestamp.Value); } // Special-case handling for unset/default timestamps to match behavior of Info-ZIP if (entry.DateTime == new DateTime(1980, 1, 1, 0, 0, 0, DateTimeKind.Utc)) { return(entry.DateTime - TimeSpan.FromDays(1)); } return(entry.DateTime); }
public void ReadOverrunLong() { var zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 }); Assert.AreEqual(4, zed.Length, "Length should be 4"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); // Empty Tag bool exceptionCaught = false; try { zed.ReadLong(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); // seven bytes zed = new ZipExtraData(new byte[] { 1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7 }); Assert.IsTrue(zed.Find(1), "Should find tag 1"); exceptionCaught = false; try { zed.ReadLong(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); zed = new ZipExtraData(new byte[] { 1, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); Assert.IsTrue(zed.Find(1), "Should find tag 1"); zed.ReadLong(); exceptionCaught = false; try { zed.ReadLong(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); }
public void ReadOverrunShort() { var zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 }); Assert.AreEqual(4, zed.Length, "Length should be 4"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); // Empty Tag bool exceptionCaught = false; try { zed.ReadShort(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); // Single byte zed = new ZipExtraData(new byte[] { 1, 0, 1, 0, 1 }); Assert.IsTrue(zed.Find(1), "Should find tag 1"); exceptionCaught = false; try { zed.ReadShort(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); zed = new ZipExtraData(new byte[] { 1, 0, 2, 0, 1, 2 }); Assert.IsTrue(zed.Find(1), "Should find tag 1"); zed.ReadShort(); exceptionCaught = false; try { zed.ReadShort(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); }
public void UnreadCountValid() { var zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 }); Assert.AreEqual(4, zed.Length, "Length should be 4"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); Assert.AreEqual(0, zed.UnreadCount); // seven bytes zed = new ZipExtraData(new byte[] { 1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7 }); Assert.IsTrue(zed.Find(1), "Should find tag 1"); for (int i = 0; i < 7; ++i) { Assert.AreEqual(7 - i, zed.UnreadCount); zed.ReadByte(); } zed.ReadByte(); Assert.AreEqual(0, zed.UnreadCount); }
public void PasswordCheckingWithDateInExtraData() { var ms = new MemoryStream(); var checkTime = new DateTimeOffset(2010, 10, 16, 0, 3, 28, new TimeSpan(1, 0, 0)); using (ZipOutputStream zos = new ZipOutputStream(ms)) { zos.IsStreamOwner = false; zos.Password = "******"; var ze = new ZipEntry("uno"); ze.DateTime = new DateTime(1998, 6, 5, 4, 3, 2); var zed = new ZipExtraData(); zed.StartNewEntry(); zed.AddData(1); TimeSpan delta = checkTime.UtcDateTime - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime(); var seconds = (int)delta.TotalSeconds; zed.AddLeInt(seconds); zed.AddNewEntry(0x5455); ze.ExtraData = zed.GetEntryData(); zos.PutNextEntry(ze); zos.WriteByte(54); } ms.Position = 0; using (ZipInputStream zis = new ZipInputStream(ms)) { zis.Password = "******"; ZipEntry uno = zis.GetNextEntry(); var theByte = (byte)zis.ReadByte(); Assert.AreEqual(54, theByte); Assert.AreEqual(-1, zis.ReadByte()); // eof Assert.AreEqual(checkTime.DateTime, uno.DateTime); } }
public void ExceedSize() { var zed = new ZipExtraData(); byte[] buffer = new byte[65506]; zed.AddEntry(1, buffer); Assert.AreEqual(65510, zed.Length); zed.AddEntry(2, new byte[21]); Assert.AreEqual(65535, zed.Length); bool caught = false; try { zed.AddEntry(3, null); } catch { caught = true; } Assert.IsTrue(caught, "Expected an exception when max size exceeded"); Assert.AreEqual(65535, zed.Length); zed.Delete(2); Assert.AreEqual(65510, zed.Length); caught = false; try { zed.AddEntry(2, new byte[22]); } catch { caught = true; } Assert.IsTrue(caught, "Expected an exception when max size exceeded"); Assert.AreEqual(65510, zed.Length); }
public void BasicOperations() { var zed = new ZipExtraData(null); Assert.AreEqual(0, zed.Length); zed = new ZipExtraData(new byte[]{1, 0, 0, 0}); Assert.AreEqual(4, zed.Length, "A length should be 4"); var zed2 = new ZipExtraData(); Assert.AreEqual(0, zed2.Length); zed2.AddEntry(1, new byte[]{}); var data = zed.GetEntryData(); for (var i = 0; i < data.Length; ++i) { Assert.AreEqual(zed2.GetEntryData()[i], data[i]); } Assert.AreEqual(4, zed2.Length, "A1 length should be 4"); var findResult = zed.Find(2); Assert.IsFalse(findResult, "A - Shouldnt find tag 2"); findResult = zed.Find(1); Assert.IsTrue(findResult, "A - Should find tag 1"); Assert.AreEqual(0, zed.ValueLength, "A- Length of entry should be 0"); Assert.AreEqual(-1, zed.ReadByte()); Assert.AreEqual(0, zed.GetStreamForTag(1).Length, "A - Length of stream should be 0"); zed = new ZipExtraData(new byte[]{1, 0, 3, 0, 1, 2, 3}); Assert.AreEqual(7, zed.Length, "Expected a length of 7"); findResult = zed.Find(1); Assert.IsTrue(findResult, "B - Should find tag 1"); Assert.AreEqual(3, zed.ValueLength, "B - Length of entry should be 3"); for (var i = 1; i <= 3; ++i) { Assert.AreEqual(i, zed.ReadByte()); } Assert.AreEqual(-1, zed.ReadByte()); var s = zed.GetStreamForTag(1); Assert.AreEqual(3, s.Length, "B.1 Stream length should be 3"); for (var i = 1; i <= 3; ++i) { Assert.AreEqual(i, s.ReadByte()); } Assert.AreEqual(-1, s.ReadByte()); zed = new ZipExtraData(new byte[]{1, 0, 3, 0, 1, 2, 3, 2, 0, 1, 0, 56}); Assert.AreEqual(12, zed.Length, "Expected a length of 12"); findResult = zed.Find(1); Assert.IsTrue(findResult, "C.1 - Should find tag 1"); Assert.AreEqual(3, zed.ValueLength, "C.1 - Length of entry should be 3"); for (var i = 1; i <= 3; ++i) { Assert.AreEqual(i, zed.ReadByte()); } Assert.AreEqual(-1, zed.ReadByte()); findResult = zed.Find(2); Assert.IsTrue(findResult, "C.2 - Should find tag 2"); Assert.AreEqual(1, zed.ValueLength, "C.2 - Length of entry should be 1"); Assert.AreEqual(56, zed.ReadByte()); Assert.AreEqual(-1, zed.ReadByte()); s = zed.GetStreamForTag(2); Assert.AreEqual(1, s.Length); Assert.AreEqual(56, s.ReadByte()); Assert.AreEqual(-1, s.ReadByte()); zed = new ZipExtraData(); zed.AddEntry(7, new byte[]{33, 44, 55}); findResult = zed.Find(7); Assert.IsTrue(findResult, "Add.1 should find new tag"); Assert.AreEqual(3, zed.ValueLength, "Add.1 length should be 3"); Assert.AreEqual(33, zed.ReadByte()); Assert.AreEqual(44, zed.ReadByte()); Assert.AreEqual(55, zed.ReadByte()); Assert.AreEqual(-1, zed.ReadByte()); zed.AddEntry(7, null); findResult = zed.Find(7); Assert.IsTrue(findResult, "Add.2 should find new tag"); Assert.AreEqual(0, zed.ValueLength, "Add.2 length should be 0"); zed.StartNewEntry(); zed.AddData(0xae); zed.AddNewEntry(55); findResult = zed.Find(55); Assert.IsTrue(findResult, "Add.3 should find new tag"); Assert.AreEqual(1, zed.ValueLength, "Add.3 length should be 1"); Assert.AreEqual(0xae, zed.ReadByte()); Assert.AreEqual(-1, zed.ReadByte()); zed = new ZipExtraData(); zed.StartNewEntry(); zed.AddLeLong(0); zed.AddLeLong(-4); zed.AddLeLong(-1); zed.AddLeLong(long.MaxValue); zed.AddLeLong(long.MinValue); zed.AddLeLong(0x123456789ABCDEF0); zed.AddLeLong(unchecked((long) 0xFEDCBA9876543210)); zed.AddNewEntry(567); s = zed.GetStreamForTag(567); var longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(0, longValue, "Expected long value of zero"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(-4, longValue, "Expected long value of -4"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(-1, longValue, "Expected long value of -1"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(long.MaxValue, longValue, "Expected long value of MaxValue"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(long.MinValue, longValue, "Expected long value of MinValue"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(0x123456789abcdef0, longValue, "Expected long value of MinValue"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(unchecked((long) 0xFEDCBA9876543210), longValue, "Expected long value of MinValue"); }
// 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> /// 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.CompressionMethodForHeader); 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); } #if !NET_1_1 && !NETCF_2_0 if (entry.AESKeySize > 0) { AddExtraDataAES(entry, ed); } #endif 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> /// 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); } } } }
public void UnreadCountValid() { var zed = new ZipExtraData(new byte[]{1, 0, 0, 0}); Assert.AreEqual(4, zed.Length, "Length should be 4"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); Assert.AreEqual(0, zed.UnreadCount); // seven bytes zed = new ZipExtraData(new byte[]{1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7}); Assert.IsTrue(zed.Find(1), "Should find tag 1"); for (var i = 0; i < 7; ++i) { Assert.AreEqual(7 - i, zed.UnreadCount); zed.ReadByte(); } zed.ReadByte(); Assert.AreEqual(0, zed.UnreadCount); }
public void Skipping() { var zed = new ZipExtraData(new byte[]{1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7}); Assert.AreEqual(11, zed.Length, "Length should be 11"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); Assert.AreEqual(7, zed.UnreadCount); Assert.AreEqual(4, zed.CurrentReadIndex); zed.ReadByte(); Assert.AreEqual(6, zed.UnreadCount); Assert.AreEqual(5, zed.CurrentReadIndex); zed.Skip(1); Assert.AreEqual(5, zed.UnreadCount); Assert.AreEqual(6, zed.CurrentReadIndex); zed.Skip(-1); Assert.AreEqual(6, zed.UnreadCount); Assert.AreEqual(5, zed.CurrentReadIndex); zed.Skip(6); Assert.AreEqual(0, zed.UnreadCount); Assert.AreEqual(11, zed.CurrentReadIndex); var exceptionCaught = false; try { zed.Skip(1); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Should fail to skip past end"); Assert.AreEqual(0, zed.UnreadCount); Assert.AreEqual(11, zed.CurrentReadIndex); zed.Skip(-7); Assert.AreEqual(7, zed.UnreadCount); Assert.AreEqual(4, zed.CurrentReadIndex); try { zed.Skip(-1); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Should fail to skip before beginning"); }
public void ReadOverrunShort() { var zed = new ZipExtraData(new byte[]{1, 0, 0, 0}); Assert.AreEqual(4, zed.Length, "Length should be 4"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); // Empty Tag var exceptionCaught = false; try { zed.ReadShort(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); // Single byte zed = new ZipExtraData(new byte[]{1, 0, 1, 0, 1}); Assert.IsTrue(zed.Find(1), "Should find tag 1"); exceptionCaught = false; try { zed.ReadShort(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); zed = new ZipExtraData(new byte[]{1, 0, 2, 0, 1, 2}); Assert.IsTrue(zed.Find(1), "Should find tag 1"); zed.ReadShort(); exceptionCaught = false; try { zed.ReadShort(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); }
public void ReadOverrunLong() { var zed = new ZipExtraData(new byte[]{1, 0, 0, 0}); Assert.AreEqual(4, zed.Length, "Length should be 4"); Assert.IsTrue(zed.Find(1), "Should find tag 1"); // Empty Tag var exceptionCaught = false; try { zed.ReadLong(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); // seven bytes zed = new ZipExtraData(new byte[]{1, 0, 7, 0, 1, 2, 3, 4, 5, 6, 7}); Assert.IsTrue(zed.Find(1), "Should find tag 1"); exceptionCaught = false; try { zed.ReadLong(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); zed = new ZipExtraData(new byte[]{1, 0, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); Assert.IsTrue(zed.Find(1), "Should find tag 1"); zed.ReadLong(); exceptionCaught = false; try { zed.ReadLong(); } catch (ZipException) { exceptionCaught = true; } Assert.IsTrue(exceptionCaught, "Expected EOS exception"); }
public void ExceedSize() { var zed = new ZipExtraData(); var buffer = new byte[65506]; zed.AddEntry(1, buffer); Assert.AreEqual(65510, zed.Length); zed.AddEntry(2, new byte[21]); Assert.AreEqual(65535, zed.Length); var caught = false; try { zed.AddEntry(3, null); } catch { caught = true; } Assert.IsTrue(caught, "Expected an exception when max size exceeded"); Assert.AreEqual(65535, zed.Length); zed.Delete(2); Assert.AreEqual(65510, zed.Length); caught = false; try { zed.AddEntry(2, new byte[22]); } catch { caught = true; } Assert.IsTrue(caught, "Expected an exception when max size exceeded"); Assert.AreEqual(65510, zed.Length); }
public void Deleting() { var zed = new ZipExtraData(); Assert.AreEqual(0, zed.Length); // Tag 1 Totoal length 10 zed.AddEntry(1, new byte[]{10, 11, 12, 13, 14, 15}); Assert.AreEqual(10, zed.Length, "Length should be 10"); Assert.AreEqual(10, zed.GetEntryData().Length, "Data length should be 10"); // Tag 2 total length 9 zed.AddEntry(2, new byte[]{20, 21, 22, 23, 24}); Assert.AreEqual(19, zed.Length, "Length should be 19"); Assert.AreEqual(19, zed.GetEntryData().Length, "Data length should be 19"); // Tag 3 Total Length 6 zed.AddEntry(3, new byte[]{30, 31}); Assert.AreEqual(25, zed.Length, "Length should be 25"); Assert.AreEqual(25, zed.GetEntryData().Length, "Data length should be 25"); zed.Delete(2); Assert.AreEqual(16, zed.Length, "Length should be 16"); Assert.AreEqual(16, zed.GetEntryData().Length, "Data length should be 16"); // Tag 2 total length 9 zed.AddEntry(2, new byte[]{20, 21, 22, 23, 24}); Assert.AreEqual(25, zed.Length, "Length should be 25"); Assert.AreEqual(25, zed.GetEntryData().Length, "Data length should be 25"); zed.AddEntry(3, null); Assert.AreEqual(23, zed.Length, "Length should be 23"); Assert.AreEqual(23, zed.GetEntryData().Length, "Data length should be 23"); }
/// <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; } }
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); } }
private static void AddExtraDataAES(ZipEntry entry, ZipExtraData extraData) { // Vendor Version: AE-1 IS 1. AE-2 is 2. With AE-2 no CRC is required and 0 is stored. const int VENDOR_VERSION = 2; // Vendor ID is the two ASCII characters "AE". const int VENDOR_ID = 0x4541; //not 6965; extraData.StartNewEntry(); // Pack AES extra data field see http://www.winzip.com/aes_info.htm //extraData.AddLeShort(7); // Data size (currently 7) extraData.AddLeShort(VENDOR_VERSION); // 2 = AE-2 extraData.AddLeShort(VENDOR_ID); // "AE" extraData.AddData(entry.AESEncryptionStrength); // 1 = 128, 2 = 192, 3 = 256 extraData.AddLeShort((int)entry.CompressionMethod); // The actual compression method used to compress the file extraData.AddNewEntry(0x9901); }
int WriteCentralDirectoryHeader(ZipEntry entry) { if ( entry.CompressedSize < 0 ) { throw new ZipException("Attempt to write central directory entry with unknown csize"); } if ( entry.Size < 0 ) { throw new ZipException("Attempt to write central directory entry with unknown size"); } if ( entry.Crc < 0 ) { throw new ZipException("Attempt to write central directory entry with unknown crc"); } // Write the central file header WriteLEInt(ZipConstants.CentralHeaderSignature); // Version made by WriteLEShort(ZipConstants.VersionMadeBy); // Version required to extract WriteLEShort(entry.Version); WriteLEShort(entry.Flags); unchecked { WriteLEShort((byte)entry.CompressionMethod); WriteLEInt((int)entry.DosTime); WriteLEInt((int)entry.Crc); } if ( (entry.IsZip64Forced()) || (entry.CompressedSize >= 0xffffffff) ) { WriteLEInt(-1); } else { WriteLEInt((int)(entry.CompressedSize & 0xffffffff)); } if ( (entry.IsZip64Forced()) || (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)) { // 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); } }
/// <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(); } } }
/// <summary> /// Unzip a zip file, treating paths appropriately for the file system irrespective of path style in central directory. /// </summary> /// <param name="zipFile"> /// The zip file. /// </param> /// <param name="directory"> /// The directory. /// </param> /// <exception cref="System.ArgumentException"> /// Value cannot be null or empty - directory /// or /// Value cannot be null or empty - zipFile /// </exception> public static void Unzip(string zipFile, string directory) { if (!File.Exists(zipFile)) { throw new FileNotFoundException("Cannot find zip file", zipFile); } if (string.IsNullOrWhiteSpace(directory)) { throw new ArgumentException("Value cannot be null or empty", nameof(directory)); } if (string.IsNullOrWhiteSpace(zipFile)) { throw new ArgumentException("Value cannot be null or empty", nameof(zipFile)); } if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } using (var archive = new ZipInputStream(File.OpenRead(zipFile))) { const int BufSize = 4096; var buffer = new byte[BufSize]; ZipEntry entry; while ((entry = archive.GetNextEntry()) != null) { // Convert entry path to that expected by local file system. var targetPath = Path.Combine( directory, IsWindows ? entry.Name.Replace('/', '\\') : entry.Name.Replace('\\', '/')); if (entry.ExtraData != null && entry.ExtraData.Any()) { var ed = new ZipExtraData(entry.ExtraData); // TODO - Use this var unixData = ed.GetData <ExtendedUnixData>(); var unixUserData = ed.GetData <UnixExtraType3>(); } if (entry.IsDirectory) { if (Environment.UserInteractive) { Console.WriteLine($"Creating {Path.GetFullPath(targetPath)}"); } CreateDirectoryIfNotExists(targetPath); } else { // File entry - expand it. CreateDirectoryIfNotExists(Path.GetDirectoryName(targetPath)); if (Environment.UserInteractive) { Console.WriteLine($"Extracting {Path.GetFullPath(targetPath)}"); } using (var fs = File.OpenWrite(targetPath)) { while (true) { var bytesRead = archive.Read(buffer, 0, BufSize); if (bytesRead == 0) { break; } fs.Write(buffer, 0, bytesRead); } fs.Flush(); } } } } }
public void BasicOperations() { var zed = new ZipExtraData(null); Assert.AreEqual(0, zed.Length); zed = new ZipExtraData(new byte[] { 1, 0, 0, 0 }); Assert.AreEqual(4, zed.Length, "A length should be 4"); var zed2 = new ZipExtraData(); Assert.AreEqual(0, zed2.Length); zed2.AddEntry(1, new byte[] { }); byte[] data = zed.GetEntryData(); for (int i = 0; i < data.Length; ++i) { Assert.AreEqual(zed2.GetEntryData()[i], data[i]); } Assert.AreEqual(4, zed2.Length, "A1 length should be 4"); bool findResult = zed.Find(2); Assert.IsFalse(findResult, "A - Shouldnt find tag 2"); findResult = zed.Find(1); Assert.IsTrue(findResult, "A - Should find tag 1"); Assert.AreEqual(0, zed.ValueLength, "A- Length of entry should be 0"); Assert.AreEqual(-1, zed.ReadByte()); Assert.AreEqual(0, zed.GetStreamForTag(1).Length, "A - Length of stream should be 0"); zed = new ZipExtraData(new byte[] { 1, 0, 3, 0, 1, 2, 3 }); Assert.AreEqual(7, zed.Length, "Expected a length of 7"); findResult = zed.Find(1); Assert.IsTrue(findResult, "B - Should find tag 1"); Assert.AreEqual(3, zed.ValueLength, "B - Length of entry should be 3"); for (int i = 1; i <= 3; ++i) { Assert.AreEqual(i, zed.ReadByte()); } Assert.AreEqual(-1, zed.ReadByte()); Stream s = zed.GetStreamForTag(1); Assert.AreEqual(3, s.Length, "B.1 Stream length should be 3"); for (int i = 1; i <= 3; ++i) { Assert.AreEqual(i, s.ReadByte()); } Assert.AreEqual(-1, s.ReadByte()); zed = new ZipExtraData(new byte[] { 1, 0, 3, 0, 1, 2, 3, 2, 0, 1, 0, 56 }); Assert.AreEqual(12, zed.Length, "Expected a length of 12"); findResult = zed.Find(1); Assert.IsTrue(findResult, "C.1 - Should find tag 1"); Assert.AreEqual(3, zed.ValueLength, "C.1 - Length of entry should be 3"); for (int i = 1; i <= 3; ++i) { Assert.AreEqual(i, zed.ReadByte()); } Assert.AreEqual(-1, zed.ReadByte()); findResult = zed.Find(2); Assert.IsTrue(findResult, "C.2 - Should find tag 2"); Assert.AreEqual(1, zed.ValueLength, "C.2 - Length of entry should be 1"); Assert.AreEqual(56, zed.ReadByte()); Assert.AreEqual(-1, zed.ReadByte()); s = zed.GetStreamForTag(2); Assert.AreEqual(1, s.Length); Assert.AreEqual(56, s.ReadByte()); Assert.AreEqual(-1, s.ReadByte()); zed = new ZipExtraData(); zed.AddEntry(7, new byte[] { 33, 44, 55 }); findResult = zed.Find(7); Assert.IsTrue(findResult, "Add.1 should find new tag"); Assert.AreEqual(3, zed.ValueLength, "Add.1 length should be 3"); Assert.AreEqual(33, zed.ReadByte()); Assert.AreEqual(44, zed.ReadByte()); Assert.AreEqual(55, zed.ReadByte()); Assert.AreEqual(-1, zed.ReadByte()); zed.AddEntry(7, null); findResult = zed.Find(7); Assert.IsTrue(findResult, "Add.2 should find new tag"); Assert.AreEqual(0, zed.ValueLength, "Add.2 length should be 0"); zed.StartNewEntry(); zed.AddData(0xae); zed.AddNewEntry(55); findResult = zed.Find(55); Assert.IsTrue(findResult, "Add.3 should find new tag"); Assert.AreEqual(1, zed.ValueLength, "Add.3 length should be 1"); Assert.AreEqual(0xae, zed.ReadByte()); Assert.AreEqual(-1, zed.ReadByte()); zed = new ZipExtraData(); zed.StartNewEntry(); zed.AddLeLong(0); zed.AddLeLong(-4); zed.AddLeLong(-1); zed.AddLeLong(long.MaxValue); zed.AddLeLong(long.MinValue); zed.AddLeLong(0x123456789ABCDEF0); zed.AddLeLong(unchecked ((long)0xFEDCBA9876543210)); zed.AddNewEntry(567); s = zed.GetStreamForTag(567); long longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(0, longValue, "Expected long value of zero"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(-4, longValue, "Expected long value of -4"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(-1, longValue, "Expected long value of -1"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(long.MaxValue, longValue, "Expected long value of MaxValue"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(long.MinValue, longValue, "Expected long value of MinValue"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(0x123456789abcdef0, longValue, "Expected long value of MinValue"); longValue = ReadLong(s); Assert.AreEqual(longValue, zed.ReadLong(), "Read/stream mismatch"); Assert.AreEqual(unchecked ((long)0xFEDCBA9876543210), longValue, "Expected long value of MinValue"); }
// 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 PasswordCheckingWithDateInExtraData() { var ms = new MemoryStream(); var checkTime = new DateTime(2010, 10, 16, 0, 3, 28); using (var zos = new ZipOutputStream(ms)) { zos.IsStreamOwner = false; zos.Password = "******"; var ze = new ZipEntry("uno"); ze.DateTime = new DateTime(1998, 6, 5, 4, 3, 2); var zed = new ZipExtraData(); zed.StartNewEntry(); zed.AddData(1); var delta = checkTime.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0).ToUniversalTime(); var seconds = (int) delta.TotalSeconds; zed.AddLeInt(seconds); zed.AddNewEntry(0x5455); ze.ExtraData = zed.GetEntryData(); zos.PutNextEntry(ze); zos.WriteByte(54); } ms.Position = 0; using (var zis = new ZipInputStream(ms)) { zis.Password = "******"; var uno = zis.GetNextEntry(); var theByte = (byte) zis.ReadByte(); Assert.AreEqual(54, theByte); Assert.AreEqual(-1, zis.ReadByte()); Assert.AreEqual(checkTime, uno.DateTime); } }