diff --git a/src/ICSharpCode.SharpZipLib/AssemblyInfo.cs b/src/ICSharpCode.SharpZipLib/AssemblyInfo.cs
new file mode 100644
index 000000000..8f8e62016
--- /dev/null
+++ b/src/ICSharpCode.SharpZipLib/AssemblyInfo.cs
@@ -0,0 +1,3 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("ICSharpCode.SharpZipLib.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b9a14ea8fc9d7599e0e82a1292a23103f0210e2f928a0f466963af23fffadba59dcc8c9e26ecd114d7c0b4179e4bc93b1656b7ee2d4a67dd7c1992653e0d9cc534f7914b6f583b022e0a7aa8a430f407932f9a6806f0fc64d61e78d5ae01aa8f8233196719d44da2c50a2d1cfa3f7abb7487b3567a4f0456aa6667154c6749b1")]
diff --git a/src/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs b/src/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs
new file mode 100644
index 000000000..a2e30da7f
--- /dev/null
+++ b/src/ICSharpCode.SharpZipLib/Core/ByteOrderUtils.cs
@@ -0,0 +1,130 @@
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Threading.Tasks;
+using CT = System.Threading.CancellationToken;
+
+// ReSharper disable MemberCanBePrivate.Global
+// ReSharper disable InconsistentNaming
+
+namespace ICSharpCode.SharpZipLib.Core
+{
+ internal static class ByteOrderStreamExtensions
+ {
+ internal static byte[] SwappedBytes(ushort value) => new[] {(byte)value, (byte)(value >> 8)};
+ internal static byte[] SwappedBytes(short value) => new[] {(byte)value, (byte)(value >> 8)};
+ internal static byte[] SwappedBytes(uint value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)};
+ internal static byte[] SwappedBytes(int value) => new[] {(byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24)};
+
+ internal static byte[] SwappedBytes(long value) => new[] {
+ (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24),
+ (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56)
+ };
+
+ internal static byte[] SwappedBytes(ulong value) => new[] {
+ (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24),
+ (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56)
+ };
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static long SwappedS64(byte[] bytes) => (
+ (long)bytes[0] << 0 | (long)bytes[1] << 8 | (long)bytes[2] << 16 | (long)bytes[3] << 24 |
+ (long)bytes[4] << 32 | (long)bytes[5] << 40 | (long)bytes[6] << 48 | (long)bytes[7] << 56);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ulong SwappedU64(byte[] bytes) => (
+ (ulong)bytes[0] << 0 | (ulong)bytes[1] << 8 | (ulong)bytes[2] << 16 | (ulong)bytes[3] << 24 |
+ (ulong)bytes[4] << 32 | (ulong)bytes[5] << 40 | (ulong)bytes[6] << 48 | (ulong)bytes[7] << 56);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static int SwappedS32(byte[] bytes) => bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static uint SwappedU32(byte[] bytes) => (uint) SwappedS32(bytes);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static short SwappedS16(byte[] bytes) => (short)(bytes[0] | bytes[1] << 8);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static ushort SwappedU16(byte[] bytes) => (ushort) SwappedS16(bytes);
+
+ internal static byte[] ReadBytes(this Stream stream, int count)
+ {
+ var bytes = new byte[count];
+ var remaining = count;
+ while (remaining > 0)
+ {
+ var bytesRead = stream.Read(bytes, count - remaining, remaining);
+ if (bytesRead < 1) throw new EndOfStreamException();
+ remaining -= bytesRead;
+ }
+
+ return bytes;
+ }
+
+ /// Read an unsigned short in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ReadLEShort(this Stream stream) => SwappedS16(ReadBytes(stream, 2));
+
+ /// Read an int in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ReadLEInt(this Stream stream) => SwappedS32(ReadBytes(stream, 4));
+
+ /// Read a long in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static long ReadLELong(this Stream stream) => SwappedS64(ReadBytes(stream, 8));
+
+ /// Write an unsigned short in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLEShort(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 2);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static async Task WriteLEShortAsync(this Stream stream, int value, CT ct)
+ => await stream.WriteAsync(SwappedBytes(value), 0, 2, ct);
+
+ /// Write a ushort in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLEUshort(this Stream stream, ushort value) => stream.Write(SwappedBytes(value), 0, 2);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static async Task WriteLEUshortAsync(this Stream stream, ushort value, CT ct)
+ => await stream.WriteAsync(SwappedBytes(value), 0, 2, ct);
+
+ /// Write an int in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLEInt(this Stream stream, int value) => stream.Write(SwappedBytes(value), 0, 4);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static async Task WriteLEIntAsync(this Stream stream, int value, CT ct)
+ => await stream.WriteAsync(SwappedBytes(value), 0, 4, ct);
+
+ /// Write a uint in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLEUint(this Stream stream, uint value) => stream.Write(SwappedBytes(value), 0, 4);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static async Task WriteLEUintAsync(this Stream stream, uint value, CT ct)
+ => await stream.WriteAsync(SwappedBytes(value), 0, 4, ct);
+
+ /// Write a long in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLELong(this Stream stream, long value) => stream.Write(SwappedBytes(value), 0, 8);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static async Task WriteLELongAsync(this Stream stream, long value, CT ct)
+ => await stream.WriteAsync(SwappedBytes(value), 0, 8, ct);
+
+ /// Write a ulong in little endian byte order.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void WriteLEUlong(this Stream stream, ulong value) => stream.Write(SwappedBytes(value), 0, 8);
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static async Task WriteLEUlongAsync(this Stream stream, ulong value, CT ct)
+ => await stream.WriteAsync(SwappedBytes(value), 0, 8, ct);
+ }
+}
diff --git a/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs b/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs
index 6d0d9b304..47de6e26e 100644
--- a/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs
+++ b/src/ICSharpCode.SharpZipLib/Core/StreamUtils.cs
@@ -1,12 +1,14 @@
using System;
using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.Core
{
///
/// Provides simple " utilities.
///
- public sealed class StreamUtils
+ public static class StreamUtils
{
///
/// Read from a ensuring all the required data is read.
@@ -14,7 +16,7 @@ public sealed class StreamUtils
/// The stream to read.
/// The buffer to fill.
///
- static public void ReadFully(Stream stream, byte[] buffer)
+ public static void ReadFully(Stream stream, byte[] buffer)
{
ReadFully(stream, buffer, 0, buffer.Length);
}
@@ -29,7 +31,7 @@ static public void ReadFully(Stream stream, byte[] buffer)
/// Required parameter is null
/// and or are invalid.
/// End of stream is encountered before all the data has been read.
- static public void ReadFully(Stream stream, byte[] buffer, int offset, int count)
+ public static void ReadFully(Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
{
@@ -73,7 +75,7 @@ static public void ReadFully(Stream stream, byte[] buffer, int offset, int count
/// The number of bytes of data to store.
/// Required parameter is null
/// and or are invalid.
- static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count)
+ public static int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
{
@@ -118,7 +120,7 @@ static public int ReadRequestedBytes(Stream stream, byte[] buffer, int offset, i
/// The stream to source data from.
/// The stream to write data to.
/// The buffer to use during copying.
- static public void Copy(Stream source, Stream destination, byte[] buffer)
+ public static void Copy(Stream source, Stream destination, byte[] buffer)
{
if (source == null)
{
@@ -169,7 +171,7 @@ static public void Copy(Stream source, Stream destination, byte[] buffer)
/// The source for this event.
/// The name to use with the event.
/// This form is specialised for use within #Zip to support events during archive operations.
- static public void Copy(Stream source, Stream destination,
+ public static void Copy(Stream source, Stream destination,
byte[] buffer, ProgressHandler progressHandler, TimeSpan updateInterval, object sender, string name)
{
Copy(source, destination, buffer, progressHandler, updateInterval, sender, name, -1);
@@ -188,7 +190,7 @@ static public void Copy(Stream source, Stream destination,
/// A predetermined fixed target value to use with progress updates.
/// If the value is negative the target is calculated by looking at the stream.
/// This form is specialised for use within #Zip to support events during archive operations.
- static public void Copy(Stream source, Stream destination,
+ public static void Copy(Stream source, Stream destination,
byte[] buffer,
ProgressHandler progressHandler, TimeSpan updateInterval,
object sender, string name, long fixedTarget)
@@ -272,13 +274,22 @@ static public void Copy(Stream source, Stream destination,
progressHandler(sender, args);
}
}
-
- ///
- /// Initialise an instance of
- ///
- private StreamUtils()
+
+ internal static async Task WriteProcToStreamAsync(this Stream targetStream, MemoryStream bufferStream, Action writeProc, CancellationToken ct)
{
- // Do nothing.
+ bufferStream.SetLength(0);
+ writeProc(bufferStream);
+ bufferStream.Position = 0;
+ await bufferStream.CopyToAsync(targetStream, 81920, ct);
+ bufferStream.SetLength(0);
+ }
+
+ internal static async Task WriteProcToStreamAsync(this Stream targetStream, Action writeProc, CancellationToken ct)
+ {
+ using (var ms = new MemoryStream())
+ {
+ await WriteProcToStreamAsync(targetStream, ms, writeProc, ct);
+ }
}
}
}
diff --git a/src/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj b/src/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj
index ca37ba1ae..066c4fb43 100644
--- a/src/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj
+++ b/src/ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib.csproj
@@ -40,5 +40,5 @@ Please see https://github.com/icsharpcode/SharpZipLib/wiki/Release-1.3.3 for mor
images
-
+
diff --git a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
index b6d4025d1..fd4bb47af 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/Compression/Streams/DeflaterOutputStream.cs
@@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
{
@@ -105,10 +107,7 @@ public virtual void Finish()
break;
}
- if (cryptoTransform_ != null)
- {
- EncryptBlock(buffer_, 0, len);
- }
+ EncryptBlock(buffer_, 0, len);
baseOutputStream_.Write(buffer_, 0, len);
}
@@ -131,6 +130,47 @@ public virtual void Finish()
}
}
+ ///
+ /// Finishes the stream by calling finish() on the deflater.
+ ///
+ /// The that can be used to cancel the operation.
+ ///
+ /// Not all input is deflated
+ ///
+ public virtual async Task FinishAsync(CancellationToken ct)
+ {
+ deflater_.Finish();
+ while (!deflater_.IsFinished)
+ {
+ int len = deflater_.Deflate(buffer_, 0, buffer_.Length);
+ if (len <= 0)
+ {
+ break;
+ }
+
+ EncryptBlock(buffer_, 0, len);
+
+ await baseOutputStream_.WriteAsync(buffer_, 0, len, ct);
+ }
+
+ if (!deflater_.IsFinished)
+ {
+ throw new SharpZipBaseException("Can't deflate all input?");
+ }
+
+ await baseOutputStream_.FlushAsync(ct);
+
+ if (cryptoTransform_ != null)
+ {
+ if (cryptoTransform_ is ZipAESTransform)
+ {
+ AESAuthCode = ((ZipAESTransform)cryptoTransform_).GetAuthCode();
+ }
+ cryptoTransform_.Dispose();
+ cryptoTransform_ = null;
+ }
+ }
+
///
/// Gets or sets a flag indicating ownership of underlying stream.
/// When the flag is true will close the underlying stream also.
@@ -177,6 +217,7 @@ public bool CanPatchEntries
///
protected void EncryptBlock(byte[] buffer, int offset, int length)
{
+ if(cryptoTransform_ is null) return;
cryptoTransform_.TransformBlock(buffer, 0, length, buffer, 0);
}
@@ -204,10 +245,8 @@ private void Deflate(bool flushing)
{
break;
}
- if (cryptoTransform_ != null)
- {
- EncryptBlock(buffer_, 0, deflateCount);
- }
+
+ EncryptBlock(buffer_, 0, deflateCount);
baseOutputStream_.Write(buffer_, 0, deflateCount);
}
@@ -369,6 +408,38 @@ protected override void Dispose(bool disposing)
}
}
+#if NETSTANDARD2_1
+ ///
+ /// Calls and closes the underlying
+ /// stream when is true.
+ ///
+ public override async ValueTask DisposeAsync()
+ {
+ if (!isClosed_)
+ {
+ isClosed_ = true;
+
+ try
+ {
+ await FinishAsync(CancellationToken.None);
+ if (cryptoTransform_ != null)
+ {
+ GetAuthCodeIfAES();
+ cryptoTransform_.Dispose();
+ cryptoTransform_ = null;
+ }
+ }
+ finally
+ {
+ if (IsStreamOwner)
+ {
+ await baseOutputStream_.DisposeAsync();
+ }
+ }
+ }
+ }
+#endif
+
///
/// Get the Auth code for AES encrypted entries
///
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs
index 12e29bb3e..cc2e74490 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipExtraData.cs
@@ -153,16 +153,15 @@ public ushort TagID
public void SetData(byte[] data, int index, int count)
{
using (MemoryStream ms = new MemoryStream(data, index, count, false))
- using (ZipHelperStream helperStream = new ZipHelperStream(ms))
{
// bit 0 if set, modification time is present
// bit 1 if set, access time is present
// bit 2 if set, creation time is present
- _flags = (Flags)helperStream.ReadByte();
+ _flags = (Flags)ms.ReadByte();
if (((_flags & Flags.ModificationTime) != 0))
{
- int iTime = helperStream.ReadLEInt();
+ int iTime = ms.ReadLEInt();
_modificationTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) +
new TimeSpan(0, 0, 0, iTime, 0);
@@ -173,7 +172,7 @@ public void SetData(byte[] data, int index, int count)
if ((_flags & Flags.AccessTime) != 0)
{
- int iTime = helperStream.ReadLEInt();
+ int iTime = ms.ReadLEInt();
_lastAccessTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) +
new TimeSpan(0, 0, 0, iTime, 0);
@@ -181,7 +180,7 @@ public void SetData(byte[] data, int index, int count)
if ((_flags & Flags.CreateTime) != 0)
{
- int iTime = helperStream.ReadLEInt();
+ int iTime = ms.ReadLEInt();
_createTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc) +
new TimeSpan(0, 0, 0, iTime, 0);
@@ -196,27 +195,25 @@ public void SetData(byte[] data, int index, int count)
public byte[] GetData()
{
using (MemoryStream ms = new MemoryStream())
- using (ZipHelperStream helperStream = new ZipHelperStream(ms))
{
- helperStream.IsStreamOwner = false;
- helperStream.WriteByte((byte)_flags); // Flags
+ ms.WriteByte((byte)_flags); // Flags
if ((_flags & Flags.ModificationTime) != 0)
{
TimeSpan span = _modificationTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var seconds = (int)span.TotalSeconds;
- helperStream.WriteLEInt(seconds);
+ ms.WriteLEInt(seconds);
}
if ((_flags & Flags.AccessTime) != 0)
{
TimeSpan span = _lastAccessTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var seconds = (int)span.TotalSeconds;
- helperStream.WriteLEInt(seconds);
+ ms.WriteLEInt(seconds);
}
if ((_flags & Flags.CreateTime) != 0)
{
TimeSpan span = _createTime - new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
var seconds = (int)span.TotalSeconds;
- helperStream.WriteLEInt(seconds);
+ ms.WriteLEInt(seconds);
}
return ms.ToArray();
}
@@ -342,24 +339,23 @@ public ushort TagID
public void SetData(byte[] data, int index, int count)
{
using (MemoryStream ms = new MemoryStream(data, index, count, false))
- using (ZipHelperStream helperStream = new ZipHelperStream(ms))
{
- helperStream.ReadLEInt(); // Reserved
- while (helperStream.Position < helperStream.Length)
+ ms.ReadLEInt(); // Reserved
+ while (ms.Position < ms.Length)
{
- int ntfsTag = helperStream.ReadLEShort();
- int ntfsLength = helperStream.ReadLEShort();
+ int ntfsTag = ms.ReadLEShort();
+ int ntfsLength = ms.ReadLEShort();
if (ntfsTag == 1)
{
if (ntfsLength >= 24)
{
- long lastModificationTicks = helperStream.ReadLELong();
+ long lastModificationTicks = ms.ReadLELong();
_lastModificationTime = DateTime.FromFileTimeUtc(lastModificationTicks);
- long lastAccessTicks = helperStream.ReadLELong();
+ long lastAccessTicks = ms.ReadLELong();
_lastAccessTime = DateTime.FromFileTimeUtc(lastAccessTicks);
- long createTimeTicks = helperStream.ReadLELong();
+ long createTimeTicks = ms.ReadLELong();
_createTime = DateTime.FromFileTimeUtc(createTimeTicks);
}
break;
@@ -367,7 +363,7 @@ public void SetData(byte[] data, int index, int count)
else
{
// An unknown NTFS tag so simply skip it.
- helperStream.Seek(ntfsLength, SeekOrigin.Current);
+ ms.Seek(ntfsLength, SeekOrigin.Current);
}
}
}
@@ -380,15 +376,13 @@ public void SetData(byte[] data, int index, int count)
public byte[] GetData()
{
using (MemoryStream ms = new MemoryStream())
- using (ZipHelperStream helperStream = new ZipHelperStream(ms))
- {
- helperStream.IsStreamOwner = false;
- helperStream.WriteLEInt(0); // Reserved
- helperStream.WriteLEShort(1); // Tag
- helperStream.WriteLEShort(24); // Length = 3 x 8.
- helperStream.WriteLELong(_lastModificationTime.ToFileTimeUtc());
- helperStream.WriteLELong(_lastAccessTime.ToFileTimeUtc());
- helperStream.WriteLELong(_createTime.ToFileTimeUtc());
+ {
+ ms.WriteLEInt(0); // Reserved
+ ms.WriteLEShort(1); // Tag
+ ms.WriteLEShort(24); // Length = 3 x 8.
+ ms.WriteLELong(_lastModificationTime.ToFileTimeUtc());
+ ms.WriteLELong(_lastAccessTime.ToFileTimeUtc());
+ ms.WriteLELong(_createTime.ToFileTimeUtc());
return ms.ToArray();
}
}
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
index 3bd66ffeb..a07b19f0c 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
@@ -1003,10 +1003,8 @@ public bool TestArchive(bool testData, TestStrategy strategy, ZipTestResultHandl
if ((this[entryIndex].Flags & (int)GeneralBitFlags.Descriptor) != 0)
{
- var helper = new ZipHelperStream(baseStream_);
var data = new DescriptorData();
- helper.ReadDataDescriptor(this[entryIndex].LocalHeaderRequiresZip64, data);
-
+ ZipFormat.ReadDataDescriptor(baseStream_, this[entryIndex].LocalHeaderRequiresZip64, data);
if (checkCRC && this[entryIndex].Crc != data.Crc)
{
status.AddError();
@@ -1582,10 +1580,7 @@ public void CommitUpdate()
if (entries_.Length == 0)
{
byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipStrings.ConvertToArray(comment_);
- using (ZipHelperStream zhs = new ZipHelperStream(baseStream_))
- {
- zhs.WriteEndOfCentralDirectory(0, 0, 0, theComment);
- }
+ ZipFormat.WriteEndOfCentralDirectory(baseStream_, 0, 0, 0, theComment);
}
}
}
@@ -2728,8 +2723,7 @@ private void AddEntry(ZipFile workFile, ZipUpdate update)
if ((update.OutEntry.Flags & (int)GeneralBitFlags.Descriptor) == (int)GeneralBitFlags.Descriptor)
{
- var helper = new ZipHelperStream(workFile.baseStream_);
- helper.WriteDataDescriptor(update.OutEntry);
+ ZipFormat.WriteDataDescriptor(workFile.baseStream_, update.OutEntry);
}
}
}
@@ -2866,15 +2860,11 @@ private void UpdateCommentOnly()
{
long baseLength = baseStream_.Length;
- ZipHelperStream updateFile = null;
+ Stream updateFile;
if (archiveStorage_.UpdateMode == FileUpdateMode.Safe)
{
- Stream copyStream = archiveStorage_.MakeTemporaryCopy(baseStream_);
- updateFile = new ZipHelperStream(copyStream)
- {
- IsStreamOwner = true
- };
+ updateFile = archiveStorage_.MakeTemporaryCopy(baseStream_);
baseStream_.Dispose();
baseStream_ = null;
@@ -2891,21 +2881,21 @@ private void UpdateCommentOnly()
// Need to tidy up the archive storage interface and contract basically.
baseStream_ = archiveStorage_.OpenForDirectUpdate(baseStream_);
- updateFile = new ZipHelperStream(baseStream_);
+ updateFile = baseStream_;
}
else
{
baseStream_.Dispose();
baseStream_ = null;
- updateFile = new ZipHelperStream(Name);
+ updateFile = new FileStream(Name, FileMode.Open, FileAccess.ReadWrite);
}
}
- using (updateFile)
+ try
{
long locatedCentralDirOffset =
- updateFile.LocateBlockWithSignature(ZipConstants.EndOfCentralDirectorySignature,
- baseLength, ZipConstants.EndOfCentralRecordBaseSize, 0xffff);
+ ZipFormat.LocateBlockWithSignature(updateFile, ZipConstants.EndOfCentralDirectorySignature,
+ baseLength, ZipConstants.EndOfCentralRecordBaseSize, 0xffff);
if (locatedCentralDirOffset < 0)
{
throw new ZipException("Cannot find central directory");
@@ -2920,6 +2910,11 @@ private void UpdateCommentOnly()
updateFile.Write(rawComment, 0, rawComment.Length);
updateFile.SetLength(updateFile.Position);
}
+ finally
+ {
+ if(updateFile != baseStream_)
+ updateFile.Dispose();
+ }
if (archiveStorage_.UpdateMode == FileUpdateMode.Safe)
{
@@ -3082,10 +3077,8 @@ private void RunUpdates()
}
byte[] theComment = (newComment_ != null) ? newComment_.RawComment : ZipStrings.ConvertToArray(comment_);
- using (ZipHelperStream zhs = new ZipHelperStream(workFile.baseStream_))
- {
- zhs.WriteEndOfCentralDirectory(updateCount_, sizeEntries, centralDirOffset, theComment);
- }
+ ZipFormat.WriteEndOfCentralDirectory(workFile.baseStream_, updateCount_,
+ sizeEntries, centralDirOffset, theComment);
endOfStream = workFile.baseStream_.Position;
@@ -3426,13 +3419,8 @@ private ulong ReadLEUlong()
#endregion Reading
// NOTE this returns the offset of the first byte after the signature.
- private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
- {
- using (ZipHelperStream les = new ZipHelperStream(baseStream_))
- {
- return les.LocateBlockWithSignature(signature, endLocation, minimumBlockSize, maximumVariableData);
- }
- }
+ private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
+ => ZipFormat.LocateBlockWithSignature(baseStream_, signature, endLocation, minimumBlockSize, maximumVariableData);
///
/// Search for and read the central directory of a zip file filling the entries array.
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs
new file mode 100644
index 000000000..75f6b72d7
--- /dev/null
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFormat.cs
@@ -0,0 +1,597 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using ICSharpCode.SharpZipLib.Core;
+
+namespace ICSharpCode.SharpZipLib.Zip
+{
+ ///
+ /// Holds data pertinent to a data descriptor.
+ ///
+ public class DescriptorData
+ {
+ private long _crc;
+
+ ///
+ /// Get /set the compressed size of data.
+ ///
+ public long CompressedSize { get; set; }
+
+ ///
+ /// Get / set the uncompressed size of data
+ ///
+ public long Size { get; set; }
+
+ ///
+ /// Get /set the crc value.
+ ///
+ public long Crc
+ {
+ get => _crc;
+ set => _crc = (value & 0xffffffff);
+ }
+ }
+
+ internal struct EntryPatchData
+ {
+ public long SizePatchOffset { get; set; }
+
+ public long CrcPatchOffset { get; set; }
+ }
+
+ ///
+ /// This class assists with writing/reading from Zip files.
+ ///
+ internal static class ZipFormat
+ {
+ // Write the local file header
+ // TODO: ZipFormat.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
+ internal static int WriteLocalHeader(Stream stream, ZipEntry entry, out EntryPatchData patchData,
+ bool headerInfoAvailable, bool patchEntryHeader, long streamOffset)
+ {
+ patchData = new EntryPatchData();
+
+ stream.WriteLEInt(ZipConstants.LocalHeaderSignature);
+ stream.WriteLEShort(entry.Version);
+ stream.WriteLEShort(entry.Flags);
+ stream.WriteLEShort((byte)entry.CompressionMethodForHeader);
+ stream.WriteLEInt((int)entry.DosTime);
+
+ if (headerInfoAvailable)
+ {
+ stream.WriteLEInt((int)entry.Crc);
+ if (entry.LocalHeaderRequiresZip64)
+ {
+ stream.WriteLEInt(-1);
+ stream.WriteLEInt(-1);
+ }
+ else
+ {
+ stream.WriteLEInt((int)entry.CompressedSize + entry.EncryptionOverheadSize);
+ stream.WriteLEInt((int)entry.Size);
+ }
+ }
+ else
+ {
+ if (patchEntryHeader)
+ patchData.CrcPatchOffset = streamOffset + stream.Position;
+
+ stream.WriteLEInt(0); // Crc
+
+ if (patchEntryHeader)
+ patchData.SizePatchOffset = streamOffset + stream.Position;
+
+ // For local header both sizes appear in Zip64 Extended Information
+ if (entry.LocalHeaderRequiresZip64 && patchEntryHeader)
+ {
+ stream.WriteLEInt(-1);
+ stream.WriteLEInt(-1);
+ }
+ else
+ {
+ stream.WriteLEInt(0); // Compressed size
+ stream.WriteLEInt(0); // Uncompressed size
+ }
+ }
+
+ byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
+
+ if (name.Length > 0xFFFF)
+ {
+ throw new ZipException("Entry name too long.");
+ }
+
+ var ed = new ZipExtraData(entry.ExtraData);
+
+ if (entry.LocalHeaderRequiresZip64)
+ {
+ ed.StartNewEntry();
+ if (headerInfoAvailable)
+ {
+ ed.AddLeLong(entry.Size);
+ ed.AddLeLong(entry.CompressedSize + entry.EncryptionOverheadSize);
+ }
+ else
+ {
+ ed.AddLeLong(-1);
+ ed.AddLeLong(-1);
+ }
+ ed.AddNewEntry(1);
+
+ if (!ed.Find(1))
+ {
+ throw new ZipException("Internal error cant find extra data");
+ }
+
+ patchData.SizePatchOffset = ed.CurrentReadIndex;
+ }
+ else
+ {
+ ed.Delete(1);
+ }
+
+ if (entry.AESKeySize > 0)
+ {
+ AddExtraDataAES(entry, ed);
+ }
+ byte[] extra = ed.GetEntryData();
+
+ stream.WriteLEShort(name.Length);
+ stream.WriteLEShort(extra.Length);
+
+ if (name.Length > 0)
+ {
+ stream.Write(name, 0, name.Length);
+ }
+
+ if (entry.LocalHeaderRequiresZip64 && patchEntryHeader)
+ {
+ patchData.SizePatchOffset += streamOffset + stream.Position;
+ }
+
+ if (extra.Length > 0)
+ {
+ stream.Write(extra, 0, extra.Length);
+ }
+
+ return ZipConstants.LocalHeaderBaseSize + name.Length + extra.Length;
+ }
+
+ ///
+ /// Locates a block with the desired .
+ ///
+ ///
+ /// The signature to find.
+ /// Location, marking the end of block.
+ /// Minimum size of the block.
+ /// The maximum variable data.
+ /// Returns the offset of the first byte after the signature; -1 if not found
+ internal static long LocateBlockWithSignature(Stream stream, int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
+ {
+ long pos = endLocation - minimumBlockSize;
+ if (pos < 0)
+ {
+ return -1;
+ }
+
+ long giveUpMarker = Math.Max(pos - maximumVariableData, 0);
+
+ // TODO: This loop could be optimized for speed.
+ do
+ {
+ if (pos < giveUpMarker)
+ {
+ return -1;
+ }
+ stream.Seek(pos--, SeekOrigin.Begin);
+ } while (stream.ReadLEInt() != signature);
+
+ return stream.Position;
+ }
+
+ ///
+ public static async Task WriteZip64EndOfCentralDirectoryAsync(Stream stream, long noOfEntries,
+ long sizeEntries, long centralDirOffset, CancellationToken cancellationToken)
+ {
+ await stream.WriteProcToStreamAsync(s => WriteZip64EndOfCentralDirectory(s, noOfEntries, sizeEntries, centralDirOffset), cancellationToken);
+ }
+
+ ///
+ /// Write Zip64 end of central directory records (File header and locator).
+ ///
+ ///
+ /// The number of entries in the central directory.
+ /// The size of entries in the central directory.
+ /// The offset of the central directory.
+ internal static void WriteZip64EndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long centralDirOffset)
+ {
+ long centralSignatureOffset = centralDirOffset + sizeEntries;
+ stream.WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature);
+ stream.WriteLELong(44); // Size of this record (total size of remaining fields in header or full size - 12)
+ stream.WriteLEShort(ZipConstants.VersionMadeBy); // Version made by
+ stream.WriteLEShort(ZipConstants.VersionZip64); // Version to extract
+ stream.WriteLEInt(0); // Number of this disk
+ stream.WriteLEInt(0); // number of the disk with the start of the central directory
+ stream.WriteLELong(noOfEntries); // No of entries on this disk
+ stream.WriteLELong(noOfEntries); // Total No of entries in central directory
+ stream.WriteLELong(sizeEntries); // Size of the central directory
+ stream.WriteLELong(centralDirOffset); // offset of start of central directory
+ // zip64 extensible data sector not catered for here (variable size)
+
+ // Write the Zip64 end of central directory locator
+ stream.WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature);
+
+ // no of the disk with the start of the zip64 end of central directory
+ stream.WriteLEInt(0);
+
+ // relative offset of the zip64 end of central directory record
+ stream.WriteLELong(centralSignatureOffset);
+
+ // total number of disks
+ stream.WriteLEInt(1);
+ }
+
+ ///
+ public static async Task WriteEndOfCentralDirectoryAsync(Stream stream, long noOfEntries, long sizeEntries,
+ long start, byte[] comment, CancellationToken cancellationToken)
+ => await stream.WriteProcToStreamAsync(s
+ => WriteEndOfCentralDirectory(s, noOfEntries, sizeEntries, start, comment), cancellationToken);
+
+ ///
+ /// Write the required records to end the central directory.
+ ///
+ ///
+ /// The number of entries in the directory.
+ /// The size of the entries in the directory.
+ /// The start of the central directory.
+ /// The archive comment. (This can be null).
+
+ internal static void WriteEndOfCentralDirectory(Stream stream, long noOfEntries, long sizeEntries, long start, byte[] comment)
+ {
+ if (noOfEntries >= 0xffff ||
+ start >= 0xffffffff ||
+ sizeEntries >= 0xffffffff)
+ {
+ WriteZip64EndOfCentralDirectory(stream, noOfEntries, sizeEntries, start);
+ }
+
+ stream.WriteLEInt(ZipConstants.EndOfCentralDirectorySignature);
+
+ // TODO: ZipFile Multi disk handling not done
+ stream.WriteLEShort(0); // number of this disk
+ stream.WriteLEShort(0); // no of disk with start of central dir
+
+ // Number of entries
+ if (noOfEntries >= 0xffff)
+ {
+ stream.WriteLEUshort(0xffff); // Zip64 marker
+ stream.WriteLEUshort(0xffff);
+ }
+ else
+ {
+ stream.WriteLEShort((short)noOfEntries); // entries in central dir for this disk
+ stream.WriteLEShort((short)noOfEntries); // total entries in central directory
+ }
+
+ // Size of the central directory
+ if (sizeEntries >= 0xffffffff)
+ {
+ stream.WriteLEUint(0xffffffff); // Zip64 marker
+ }
+ else
+ {
+ stream.WriteLEInt((int)sizeEntries);
+ }
+
+ // offset of start of central directory
+ if (start >= 0xffffffff)
+ {
+ stream.WriteLEUint(0xffffffff); // Zip64 marker
+ }
+ else
+ {
+ stream.WriteLEInt((int)start);
+ }
+
+ var commentLength = comment?.Length ?? 0;
+
+ if (commentLength > 0xffff)
+ {
+ throw new ZipException($"Comment length ({commentLength}) is larger than 64K");
+ }
+
+ stream.WriteLEShort(commentLength);
+
+ if (commentLength > 0)
+ {
+ stream.Write(comment, 0, commentLength);
+ }
+ }
+
+
+
+ ///
+ /// Write a data descriptor.
+ ///
+ ///
+ /// The entry to write a descriptor for.
+ /// Returns the number of descriptor bytes written.
+ internal static int WriteDataDescriptor(Stream stream, ZipEntry entry)
+ {
+ if (entry == null)
+ {
+ throw new ArgumentNullException(nameof(entry));
+ }
+
+ int result = 0;
+
+ // Add data descriptor if flagged as required
+ if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0)
+ {
+ // The signature is not PKZIP originally but is now described as optional
+ // in the PKZIP Appnote documenting the format.
+ stream.WriteLEInt(ZipConstants.DataDescriptorSignature);
+ stream.WriteLEInt(unchecked((int)(entry.Crc)));
+
+ result += 8;
+
+ if (entry.LocalHeaderRequiresZip64)
+ {
+ stream.WriteLELong(entry.CompressedSize);
+ stream.WriteLELong(entry.Size);
+ result += 16;
+ }
+ else
+ {
+ stream.WriteLEInt((int)entry.CompressedSize);
+ stream.WriteLEInt((int)entry.Size);
+ result += 8;
+ }
+ }
+
+ return result;
+ }
+
+ ///
+ /// Read data descriptor at the end of compressed data.
+ ///
+ ///
+ /// if set to true [zip64].
+ /// The data to fill in.
+ /// Returns the number of bytes read in the descriptor.
+ internal static void ReadDataDescriptor(Stream stream, bool zip64, DescriptorData data)
+ {
+ int intValue = stream.ReadLEInt();
+
+ // In theory this may not be a descriptor according to PKZIP appnote.
+ // In practice its always there.
+ if (intValue != ZipConstants.DataDescriptorSignature)
+ {
+ throw new ZipException("Data descriptor signature not found");
+ }
+
+ data.Crc = stream.ReadLEInt();
+
+ if (zip64)
+ {
+ data.CompressedSize = stream.ReadLELong();
+ data.Size = stream.ReadLELong();
+ }
+ else
+ {
+ data.CompressedSize = stream.ReadLEInt();
+ data.Size = stream.ReadLEInt();
+ }
+ }
+
+ internal static int WriteEndEntry(Stream stream, ZipEntry entry)
+ {
+ stream.WriteLEInt(ZipConstants.CentralHeaderSignature);
+ stream.WriteLEShort((entry.HostSystem << 8) | entry.VersionMadeBy);
+ stream.WriteLEShort(entry.Version);
+ stream.WriteLEShort(entry.Flags);
+ stream.WriteLEShort((short)entry.CompressionMethodForHeader);
+ stream.WriteLEInt((int)entry.DosTime);
+ stream.WriteLEInt((int)entry.Crc);
+
+ if (entry.IsZip64Forced() ||
+ (entry.CompressedSize >= uint.MaxValue))
+ {
+ stream.WriteLEInt(-1);
+ }
+ else
+ {
+ stream.WriteLEInt((int)entry.CompressedSize);
+ }
+
+ if (entry.IsZip64Forced() ||
+ (entry.Size >= uint.MaxValue))
+ {
+ stream.WriteLEInt(-1);
+ }
+ else
+ {
+ stream.WriteLEInt((int)entry.Size);
+ }
+
+ byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
+
+ if (name.Length > 0xffff)
+ {
+ throw new ZipException("Name too long.");
+ }
+
+ var 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 (entry.AESKeySize > 0)
+ {
+ AddExtraDataAES(entry, ed);
+ }
+ byte[] extra = ed.GetEntryData();
+
+ byte[] entryComment = !(entry.Comment is null)
+ ? ZipStrings.ConvertToArray(entry.Flags, entry.Comment)
+ : Empty.Array();
+
+ if (entryComment.Length > 0xffff)
+ {
+ throw new ZipException("Comment too long.");
+ }
+
+ stream.WriteLEShort(name.Length);
+ stream.WriteLEShort(extra.Length);
+ stream.WriteLEShort(entryComment.Length);
+ stream.WriteLEShort(0); // disk number
+ stream.WriteLEShort(0); // internal file attributes
+ // external file attributes
+
+ if (entry.ExternalFileAttributes != -1)
+ {
+ stream.WriteLEInt(entry.ExternalFileAttributes);
+ }
+ else
+ {
+ if (entry.IsDirectory)
+ { // mark entry as directory (from nikolam.AT.perfectinfo.com)
+ stream.WriteLEInt(16);
+ }
+ else
+ {
+ stream.WriteLEInt(0);
+ }
+ }
+
+ if (entry.Offset >= uint.MaxValue)
+ {
+ stream.WriteLEInt(-1);
+ }
+ else
+ {
+ stream.WriteLEInt((int)entry.Offset);
+ }
+
+ if (name.Length > 0)
+ {
+ stream.Write(name, 0, name.Length);
+ }
+
+ if (extra.Length > 0)
+ {
+ stream.Write(extra, 0, extra.Length);
+ }
+
+ if (entryComment.Length > 0)
+ {
+ stream.Write(entryComment, 0, entryComment.Length);
+ }
+
+ return ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
+ }
+
+ internal 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);
+ }
+
+ internal static async Task PatchLocalHeaderAsync(Stream stream, ZipEntry entry,
+ EntryPatchData patchData, CancellationToken ct)
+ {
+ var initialPos = stream.Position;
+
+ // Update CRC
+ stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin);
+ await stream.WriteLEIntAsync((int)entry.Crc, ct);
+
+ // Update Sizes
+ if (entry.LocalHeaderRequiresZip64)
+ {
+ if (patchData.SizePatchOffset == -1)
+ {
+ throw new ZipException("Entry requires zip64 but this has been turned off");
+ }
+ // Seek to the Zip64 Extra Data
+ stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin);
+
+ // Note: The order of the size fields is reversed when compared to the local header!
+ await stream.WriteLELongAsync(entry.Size, ct);
+ await stream.WriteLELongAsync(entry.CompressedSize, ct);
+ }
+ else
+ {
+ await stream.WriteLEIntAsync((int)entry.CompressedSize, ct);
+ await stream.WriteLEIntAsync((int)entry.Size, ct);
+ }
+
+ stream.Seek(initialPos, SeekOrigin.Begin);
+ }
+
+ internal static void PatchLocalHeaderSync(Stream stream, ZipEntry entry,
+ EntryPatchData patchData)
+ {
+ var initialPos = stream.Position;
+ stream.Seek(patchData.CrcPatchOffset, SeekOrigin.Begin);
+ stream.WriteLEInt((int)entry.Crc);
+
+ if (entry.LocalHeaderRequiresZip64)
+ {
+ if (patchData.SizePatchOffset == -1)
+ {
+ throw new ZipException("Entry requires zip64 but this has been turned off");
+ }
+
+ // Seek to the Zip64 Extra Data
+ stream.Seek(patchData.SizePatchOffset, SeekOrigin.Begin);
+
+ // Note: The order of the size fields is reversed when compared to the local header!
+ stream.WriteLELong(entry.Size);
+ stream.WriteLELong(entry.CompressedSize);
+ }
+ else
+ {
+ stream.WriteLEInt((int)entry.CompressedSize);
+ stream.WriteLEInt((int)entry.Size);
+ }
+
+ stream.Seek(initialPos, SeekOrigin.Begin);
+ }
+ }
+}
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs
index da65630c6..e69de29bb 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipHelperStream.cs
@@ -1,629 +0,0 @@
-using System;
-using System.IO;
-
-namespace ICSharpCode.SharpZipLib.Zip
-{
- ///
- /// Holds data pertinent to a data descriptor.
- ///
- public class DescriptorData
- {
- ///
- /// Get /set the compressed size of data.
- ///
- public long CompressedSize
- {
- get { return compressedSize; }
- set { compressedSize = value; }
- }
-
- ///
- /// Get / set the uncompressed size of data
- ///
- public long Size
- {
- get { return size; }
- set { size = value; }
- }
-
- ///
- /// Get /set the crc value.
- ///
- public long Crc
- {
- get { return crc; }
- set { crc = (value & 0xffffffff); }
- }
-
- #region Instance Fields
-
- private long size;
- private long compressedSize;
- private long crc;
-
- #endregion Instance Fields
- }
-
- internal class EntryPatchData
- {
- public long SizePatchOffset
- {
- get { return sizePatchOffset_; }
- set { sizePatchOffset_ = value; }
- }
-
- public long CrcPatchOffset
- {
- get { return crcPatchOffset_; }
- set { crcPatchOffset_ = value; }
- }
-
- #region Instance Fields
-
- private long sizePatchOffset_;
- private long crcPatchOffset_;
-
- #endregion Instance Fields
- }
-
- ///
- /// This class assists with writing/reading from Zip files.
- ///
- internal class ZipHelperStream : Stream
- {
- #region Constructors
-
- ///
- /// Initialise an instance of this class.
- ///
- /// The name of the file to open.
- public ZipHelperStream(string name)
- {
- stream_ = new FileStream(name, FileMode.Open, FileAccess.ReadWrite);
- isOwner_ = true;
- }
-
- ///
- /// Initialise a new instance of .
- ///
- /// The stream to use.
- public ZipHelperStream(Stream stream)
- {
- stream_ = stream;
- }
-
- #endregion Constructors
-
- ///
- /// Get / set a value indicating whether the underlying stream is owned or not.
- ///
- /// If the stream is owned it is closed when this instance is closed.
- public bool IsStreamOwner
- {
- get { return isOwner_; }
- set { isOwner_ = value; }
- }
-
- #region Base Stream Methods
-
- public override bool CanRead
- {
- get { return stream_.CanRead; }
- }
-
- public override bool CanSeek
- {
- get { return stream_.CanSeek; }
- }
-
- public override bool CanTimeout
- {
- get { return stream_.CanTimeout; }
- }
-
- public override long Length
- {
- get { return stream_.Length; }
- }
-
- public override long Position
- {
- get { return stream_.Position; }
- set { stream_.Position = value; }
- }
-
- public override bool CanWrite
- {
- get { return stream_.CanWrite; }
- }
-
- public override void Flush()
- {
- stream_.Flush();
- }
-
- public override long Seek(long offset, SeekOrigin origin)
- {
- return stream_.Seek(offset, origin);
- }
-
- public override void SetLength(long value)
- {
- stream_.SetLength(value);
- }
-
- public override int Read(byte[] buffer, int offset, int count)
- {
- return stream_.Read(buffer, offset, count);
- }
-
- public override void Write(byte[] buffer, int offset, int count)
- {
- stream_.Write(buffer, offset, count);
- }
-
- ///
- /// Close the stream.
- ///
- ///
- /// The underlying stream is closed only if is true.
- ///
- protected override void Dispose(bool disposing)
- {
- Stream toClose = stream_;
- stream_ = null;
- if (isOwner_ && (toClose != null))
- {
- isOwner_ = false;
- toClose.Dispose();
- }
- }
-
- #endregion Base Stream Methods
-
- // Write the local file header
- // TODO: ZipHelperStream.WriteLocalHeader is not yet used and needs checking for ZipFile and ZipOuptutStream usage
- private 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 = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
-
- if (name.Length > 0xFFFF)
- {
- throw new ZipException("Entry name too long.");
- }
-
- var 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);
- }
- }
-
- ///
- /// Locates a block with the desired .
- ///
- /// The signature to find.
- /// Location, marking the end of block.
- /// Minimum size of the block.
- /// The maximum variable data.
- /// Returns the offset of the first byte after the signature; -1 if not found
- public long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
- {
- long pos = endLocation - minimumBlockSize;
- if (pos < 0)
- {
- return -1;
- }
-
- long giveUpMarker = Math.Max(pos - maximumVariableData, 0);
-
- // TODO: This loop could be optimised for speed.
- do
- {
- if (pos < giveUpMarker)
- {
- return -1;
- }
- Seek(pos--, SeekOrigin.Begin);
- } while (ReadLEInt() != signature);
-
- return Position;
- }
-
- ///
- /// Write Zip64 end of central directory records (File header and locator).
- ///
- /// The number of entries in the central directory.
- /// The size of entries in the central directory.
- /// The offset of the central directory.
- public void WriteZip64EndOfCentralDirectory(long noOfEntries, long sizeEntries, long centralDirOffset)
- {
- long centralSignatureOffset = centralDirOffset + sizeEntries;
- WriteLEInt(ZipConstants.Zip64CentralFileHeaderSignature);
- WriteLELong(44); // Size of this record (total size of remaining fields in header or full size - 12)
- WriteLEShort(ZipConstants.VersionMadeBy); // Version made by
- WriteLEShort(ZipConstants.VersionZip64); // Version to extract
- WriteLEInt(0); // Number of this disk
- WriteLEInt(0); // number of the disk with the start of the central directory
- WriteLELong(noOfEntries); // No of entries on this disk
- WriteLELong(noOfEntries); // Total No of entries in central directory
- WriteLELong(sizeEntries); // Size of the central directory
- WriteLELong(centralDirOffset); // offset of start of central directory
- // zip64 extensible data sector not catered for here (variable size)
-
- // Write the Zip64 end of central directory locator
- WriteLEInt(ZipConstants.Zip64CentralDirLocatorSignature);
-
- // no of the disk with the start of the zip64 end of central directory
- WriteLEInt(0);
-
- // relative offset of the zip64 end of central directory record
- WriteLELong(centralSignatureOffset);
-
- // total number of disks
- WriteLEInt(1);
- }
-
- ///
- /// Write the required records to end the central directory.
- ///
- /// The number of entries in the directory.
- /// The size of the entries in the directory.
- /// The start of the central directory.
- /// The archive comment. (This can be null).
- public void WriteEndOfCentralDirectory(long noOfEntries, long sizeEntries,
- long startOfCentralDirectory, byte[] comment)
- {
- if ((noOfEntries >= 0xffff) ||
- (startOfCentralDirectory >= 0xffffffff) ||
- (sizeEntries >= 0xffffffff))
- {
- WriteZip64EndOfCentralDirectory(noOfEntries, sizeEntries, startOfCentralDirectory);
- }
-
- WriteLEInt(ZipConstants.EndOfCentralDirectorySignature);
-
- // TODO: ZipFile Multi disk handling not done
- WriteLEShort(0); // number of this disk
- WriteLEShort(0); // no of disk with start of central dir
-
- // Number of entries
- if (noOfEntries >= 0xffff)
- {
- WriteLEUshort(0xffff); // Zip64 marker
- WriteLEUshort(0xffff);
- }
- else
- {
- WriteLEShort((short)noOfEntries); // entries in central dir for this disk
- WriteLEShort((short)noOfEntries); // total entries in central directory
- }
-
- // Size of the central directory
- if (sizeEntries >= 0xffffffff)
- {
- WriteLEUint(0xffffffff); // Zip64 marker
- }
- else
- {
- WriteLEInt((int)sizeEntries);
- }
-
- // offset of start of central directory
- if (startOfCentralDirectory >= 0xffffffff)
- {
- WriteLEUint(0xffffffff); // Zip64 marker
- }
- else
- {
- WriteLEInt((int)startOfCentralDirectory);
- }
-
- int commentLength = (comment != null) ? comment.Length : 0;
-
- if (commentLength > 0xffff)
- {
- throw new ZipException(string.Format("Comment length({0}) is too long can only be 64K", commentLength));
- }
-
- WriteLEShort(commentLength);
-
- if (commentLength > 0)
- {
- Write(comment, 0, comment.Length);
- }
- }
-
- #region LE value reading/writing
-
- ///
- /// Read an unsigned short in little endian byte order.
- ///
- /// Returns the value read.
- ///
- /// An i/o error occurs.
- ///
- ///
- /// The file ends prematurely
- ///
- public int ReadLEShort()
- {
- int byteValue1 = stream_.ReadByte();
-
- if (byteValue1 < 0)
- {
- throw new EndOfStreamException();
- }
-
- int byteValue2 = stream_.ReadByte();
- if (byteValue2 < 0)
- {
- throw new EndOfStreamException();
- }
-
- return byteValue1 | (byteValue2 << 8);
- }
-
- ///
- /// Read an int in little endian byte order.
- ///
- /// Returns the value read.
- ///
- /// An i/o error occurs.
- ///
- ///
- /// The file ends prematurely
- ///
- public int ReadLEInt()
- {
- return ReadLEShort() | (ReadLEShort() << 16);
- }
-
- ///
- /// Read a long in little endian byte order.
- ///
- /// The value read.
- public long ReadLELong()
- {
- return (uint)ReadLEInt() | ((long)ReadLEInt() << 32);
- }
-
- ///
- /// Write an unsigned short in little endian byte order.
- ///
- /// The value to write.
- public void WriteLEShort(int value)
- {
- stream_.WriteByte((byte)(value & 0xff));
- stream_.WriteByte((byte)((value >> 8) & 0xff));
- }
-
- ///
- /// Write a ushort in little endian byte order.
- ///
- /// The value to write.
- public void WriteLEUshort(ushort value)
- {
- stream_.WriteByte((byte)(value & 0xff));
- stream_.WriteByte((byte)(value >> 8));
- }
-
- ///
- /// Write an int in little endian byte order.
- ///
- /// The value to write.
- public void WriteLEInt(int value)
- {
- WriteLEShort(value);
- WriteLEShort(value >> 16);
- }
-
- ///
- /// Write a uint in little endian byte order.
- ///
- /// The value to write.
- public void WriteLEUint(uint value)
- {
- WriteLEUshort((ushort)(value & 0xffff));
- WriteLEUshort((ushort)(value >> 16));
- }
-
- ///
- /// Write a long in little endian byte order.
- ///
- /// The value to write.
- public void WriteLELong(long value)
- {
- WriteLEInt((int)value);
- WriteLEInt((int)(value >> 32));
- }
-
- ///
- /// Write a ulong in little endian byte order.
- ///
- /// The value to write.
- public void WriteLEUlong(ulong value)
- {
- WriteLEUint((uint)(value & 0xffffffff));
- WriteLEUint((uint)(value >> 32));
- }
-
- #endregion LE value reading/writing
-
- ///
- /// Write a data descriptor.
- ///
- /// The entry to write a descriptor for.
- /// Returns the number of descriptor bytes written.
- public int WriteDataDescriptor(ZipEntry entry)
- {
- if (entry == null)
- {
- throw new ArgumentNullException(nameof(entry));
- }
-
- int result = 0;
-
- // Add data descriptor if flagged as required
- if ((entry.Flags & (int)GeneralBitFlags.Descriptor) != 0)
- {
- // The signature is not PKZIP originally but is now described as optional
- // in the PKZIP Appnote documenting the format.
- WriteLEInt(ZipConstants.DataDescriptorSignature);
- WriteLEInt(unchecked((int)(entry.Crc)));
-
- result += 8;
-
- if (entry.LocalHeaderRequiresZip64)
- {
- WriteLELong(entry.CompressedSize);
- WriteLELong(entry.Size);
- result += 16;
- }
- else
- {
- WriteLEInt((int)entry.CompressedSize);
- WriteLEInt((int)entry.Size);
- result += 8;
- }
- }
-
- return result;
- }
-
- ///
- /// Read data descriptor at the end of compressed data.
- ///
- /// if set to true [zip64].
- /// The data to fill in.
- /// Returns the number of bytes read in the descriptor.
- public void ReadDataDescriptor(bool zip64, DescriptorData data)
- {
- int intValue = ReadLEInt();
-
- // In theory this may not be a descriptor according to PKZIP appnote.
- // In practice its always there.
- if (intValue != ZipConstants.DataDescriptorSignature)
- {
- throw new ZipException("Data descriptor signature not found");
- }
-
- data.Crc = ReadLEInt();
-
- if (zip64)
- {
- data.CompressedSize = ReadLELong();
- data.Size = ReadLELong();
- }
- else
- {
- data.CompressedSize = ReadLEInt();
- data.Size = ReadLEInt();
- }
- }
-
- #region Instance Fields
-
- private bool isOwner_;
- private Stream stream_;
-
- #endregion Instance Fields
- }
-}
diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
index 79d65f560..7aa3295fe 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipOutputStream.cs
@@ -7,6 +7,8 @@
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.Zip
{
@@ -154,7 +156,7 @@ public UseZip64 UseZip64
/// Defaults to , set to null to disable transforms and use names as supplied.
///
public INameTransform NameTransform { get; set; } = new PathTransformer();
-
+
///
/// Get/set the password used for encryption.
///
@@ -217,17 +219,10 @@ private void WriteLeLong(long value)
// Apply any configured transforms/cleaning to the name of the supplied entry.
private void TransformEntryName(ZipEntry entry)
{
- if (this.NameTransform != null)
- {
- if (entry.IsDirectory)
- {
- entry.Name = this.NameTransform.TransformDirectory(entry.Name);
- }
- else
- {
- entry.Name = this.NameTransform.TransformFile(entry.Name);
- }
- }
+ if (NameTransform == null) return;
+ entry.Name = entry.IsDirectory
+ ? NameTransform.TransformDirectory(entry.Name)
+ : NameTransform.TransformFile(entry.Name);
}
///
@@ -244,7 +239,7 @@ private void TransformEntryName(ZipEntry entry)
/// if entry passed is null.
///
///
- /// if an I/O error occured.
+ /// if an I/O error occurred.
///
///
/// if stream was finished
@@ -258,6 +253,32 @@ private void TransformEntryName(ZipEntry entry)
/// The Compression method specified for the entry is unsupported.
///
public void PutNextEntry(ZipEntry entry)
+ {
+ if (curEntry != null)
+ {
+ CloseEntry();
+ }
+
+ PutNextEntry(baseOutputStream_, entry);
+
+ if (entry.IsCrypted)
+ {
+ WriteOutput(GetEntryEncryptionHeader(entry));
+ }
+ }
+
+ private void WriteOutput(byte[] bytes)
+ => baseOutputStream_.Write(bytes, 0, bytes.Length);
+
+ private Task WriteOutputAsync(byte[] bytes)
+ => baseOutputStream_.WriteAsync(bytes, 0, bytes.Length);
+
+ private byte[] GetEntryEncryptionHeader(ZipEntry entry) =>
+ entry.AESKeySize > 0
+ ? InitializeAESPassword(entry, Password)
+ : CreateZipCryptoHeader(entry.Crc < 0 ? entry.DosTime << 16 : entry.Crc);
+
+ internal void PutNextEntry(Stream stream, ZipEntry entry, long streamOffset = 0)
{
if (entry == null)
{
@@ -269,11 +290,6 @@ public void PutNextEntry(ZipEntry entry)
throw new InvalidOperationException("ZipOutputStream was finished");
}
- if (curEntry != null)
- {
- CloseEntry();
- }
-
if (entries.Count == int.MaxValue)
{
throw new ZipException("Too many entries for Zip file");
@@ -365,128 +381,21 @@ public void PutNextEntry(ZipEntry entry)
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)
- {
- WriteLeInt((int)entry.Crc);
- if (entry.LocalHeaderRequiresZip64)
- {
- WriteLeInt(-1);
- WriteLeInt(-1);
- }
- else
- {
- WriteLeInt((int)entry.CompressedSize + entry.EncryptionOverheadSize);
- 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
- }
- }
-
- // Apply any required transforms to the entry name, and then convert to byte array format.
+ // Apply any required transforms to the entry name
TransformEntryName(entry);
- byte[] name = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
- if (name.Length > 0xFFFF)
- {
- throw new ZipException("Entry name too long.");
- }
-
- var ed = new ZipExtraData(entry.ExtraData);
-
- if (entry.LocalHeaderRequiresZip64)
- {
- ed.StartNewEntry();
- if (headerInfoAvailable)
- {
- ed.AddLeLong(entry.Size);
- ed.AddLeLong(entry.CompressedSize + entry.EncryptionOverheadSize);
- }
- 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 (entry.AESKeySize > 0)
- {
- AddExtraDataAES(entry, ed);
- }
- 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;
- }
+ // Write the local file header
+ offset += ZipFormat.WriteLocalHeader(stream, entry, out var entryPatchData,
+ headerInfoAvailable, patchEntryHeader, streamOffset);
- if (extra.Length > 0)
- {
- baseOutputStream_.Write(extra, 0, extra.Length);
- }
+ patchData = entryPatchData;
- offset += ZipConstants.LocalHeaderBaseSize + name.Length + extra.Length;
// Fix offsetOfCentraldir for AES
if (entry.AESKeySize > 0)
offset += entry.AESOverheadSize;
@@ -500,25 +409,47 @@ public void PutNextEntry(ZipEntry entry)
deflater_.SetLevel(compressionLevel);
}
size = 0;
+
+ }
- if (entry.IsCrypted)
- {
- if (entry.AESKeySize > 0)
- {
- WriteAESHeader(entry);
- }
- else
- {
- if (entry.Crc < 0)
- { // so testing Zip will says its ok
- WriteEncryptionHeader(entry.DosTime << 16);
- }
- else
- {
- WriteEncryptionHeader(entry.Crc);
- }
- }
- }
+ ///
+ /// 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
+ ///
+ ///
+ /// the entry.
+ ///
+ /// The that can be used to cancel the operation.
+ ///
+ /// if entry passed is null.
+ ///
+ ///
+ /// if an I/O error occured.
+ ///
+ ///
+ /// if stream was finished
+ ///
+ ///
+ /// Too many entries in the Zip file
+ /// Entry name is too long
+ /// Finish has already been called
+ ///
+ ///
+ /// The Compression method specified for the entry is unsupported.
+ ///
+ public async Task PutNextEntryAsync(ZipEntry entry, CancellationToken ct = default)
+ {
+ if (curEntry != null) await CloseEntryAsync(ct);
+ await baseOutputStream_.WriteProcToStreamAsync(s =>
+ {
+ PutNextEntry(s, entry, baseOutputStream_.Position);
+ }, ct);
+
+ if (!entry.IsCrypted) return;
+ await WriteOutputAsync(GetEntryEncryptionHeader(entry));
}
///
@@ -534,6 +465,37 @@ public void PutNextEntry(ZipEntry entry)
/// No entry is active.
///
public void CloseEntry()
+ {
+ WriteEntryFooter(baseOutputStream_);
+
+ // Patch the header if possible
+ if (patchEntryHeader)
+ {
+ patchEntryHeader = false;
+ ZipFormat.PatchLocalHeaderSync(baseOutputStream_, curEntry, patchData);
+ }
+
+ entries.Add(curEntry);
+ curEntry = null;
+ }
+
+ ///
+ public async Task CloseEntryAsync(CancellationToken ct)
+ {
+ await baseOutputStream_.WriteProcToStreamAsync(WriteEntryFooter, ct);
+
+ // Patch the header if possible
+ if (patchEntryHeader)
+ {
+ patchEntryHeader = false;
+ await ZipFormat.PatchLocalHeaderAsync(baseOutputStream_, curEntry, patchData, ct);
+ }
+
+ entries.Add(curEntry);
+ curEntry = null;
+ }
+
+ internal void WriteEntryFooter(Stream stream)
{
if (curEntry == null)
{
@@ -565,7 +527,7 @@ public void CloseEntry()
// Write the AES Authentication Code (a hash of the compressed and encrypted data)
if (curEntry.AESKeySize > 0)
{
- baseOutputStream_.Write(AESAuthCode, 0, 10);
+ stream.Write(AESAuthCode, 0, 10);
// Always use 0 as CRC for AE-2 format
curEntry.Crc = 0;
}
@@ -606,91 +568,69 @@ public void CloseEntry()
curEntry.CompressedSize += curEntry.EncryptionOverheadSize;
}
- // Patch the header if possible
- if (patchEntryHeader)
- {
- patchEntryHeader = false;
-
- long curPos = baseOutputStream_.Position;
- baseOutputStream_.Seek(crcPatchPos, SeekOrigin.Begin);
- WriteLeInt((int)curEntry.Crc);
-
- if (curEntry.LocalHeaderRequiresZip64)
- {
- if (sizePatchPos == -1)
- {
- throw new ZipException("Entry requires zip64 but this has been turned off");
- }
-
- baseOutputStream_.Seek(sizePatchPos, SeekOrigin.Begin);
- WriteLeLong(curEntry.Size);
- WriteLeLong(curEntry.CompressedSize);
- }
- else
- {
- WriteLeInt((int)curEntry.CompressedSize);
- WriteLeInt((int)curEntry.Size);
- }
- baseOutputStream_.Seek(curPos, SeekOrigin.Begin);
- }
-
// Add data descriptor if flagged as required
if ((curEntry.Flags & 8) != 0)
{
- WriteLeInt(ZipConstants.DataDescriptorSignature);
- WriteLeInt(unchecked((int)curEntry.Crc));
+ stream.WriteLEInt(ZipConstants.DataDescriptorSignature);
+ stream.WriteLEInt(unchecked((int)curEntry.Crc));
if (curEntry.LocalHeaderRequiresZip64)
{
- WriteLeLong(curEntry.CompressedSize);
- WriteLeLong(curEntry.Size);
+ stream.WriteLELong(curEntry.CompressedSize);
+ stream.WriteLELong(curEntry.Size);
offset += ZipConstants.Zip64DataDescriptorSize;
}
else
{
- WriteLeInt((int)curEntry.CompressedSize);
- WriteLeInt((int)curEntry.Size);
+ stream.WriteLEInt((int)curEntry.CompressedSize);
+ stream.WriteLEInt((int)curEntry.Size);
offset += ZipConstants.DataDescriptorSize;
}
}
-
- entries.Add(curEntry);
- curEntry = null;
}
- ///
- /// Initializes encryption keys based on given .
- ///
- /// The password.
- private void InitializePassword(string password)
- {
- var pkManaged = new PkzipClassicManaged();
- byte[] key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(password));
- cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
- }
+
+ // File format for AES:
+ // Size (bytes) Content
+ // ------------ -------
+ // Variable Salt value
+ // 2 Password verification value
+ // Variable Encrypted file data
+ // 10 Authentication code
+ //
+ // Value in the "compressed size" fields of the local file header and the central directory entry
+ // is the total size of all the items listed above. In other words, it is the total size of the
+ // salt value, password verification value, encrypted data, and authentication code.
+
///
/// Initializes encryption keys based on given password.
///
- private void InitializeAESPassword(ZipEntry entry, string rawPassword,
- out byte[] salt, out byte[] pwdVerifier)
+ protected byte[] InitializeAESPassword(ZipEntry entry, string rawPassword)
{
- salt = new byte[entry.AESSaltLen];
-
+ var salt = new byte[entry.AESSaltLen];
// Salt needs to be cryptographically random, and unique per file
+ if (_aesRnd == null)
+ _aesRnd = RandomNumberGenerator.Create();
_aesRnd.GetBytes(salt);
-
int blockSize = entry.AESKeySize / 8; // bits to bytes
cryptoTransform_ = new ZipAESTransform(rawPassword, salt, blockSize, true);
- pwdVerifier = ((ZipAESTransform)cryptoTransform_).PwdVerifier;
- }
- private void WriteEncryptionHeader(long crcValue)
+ var headBytes = new byte[salt.Length + 2];
+
+ Array.Copy(salt, headBytes, salt.Length);
+ Array.Copy(((ZipAESTransform)cryptoTransform_).PwdVerifier, 0,
+ headBytes, headBytes.Length - 2, 2);
+
+ return headBytes;
+ }
+
+ private byte[] CreateZipCryptoHeader(long crcValue)
{
offset += ZipConstants.CryptoHeaderSize;
- InitializePassword(Password);
+ InitializeZipCryptoPassword(Password);
byte[] cryptBuffer = new byte[ZipConstants.CryptoHeaderSize];
using (var rng = new RNGCryptoServiceProvider())
@@ -701,47 +641,21 @@ private void WriteEncryptionHeader(long crcValue)
cryptBuffer[11] = (byte)(crcValue >> 24);
EncryptBlock(cryptBuffer, 0, cryptBuffer.Length);
- baseOutputStream_.Write(cryptBuffer, 0, cryptBuffer.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);
+ return cryptBuffer;
}
-
- // Replaces WriteEncryptionHeader for AES
- //
- private void WriteAESHeader(ZipEntry entry)
+
+ ///
+ /// Initializes encryption keys based on given .
+ ///
+ /// The password.
+ private void InitializeZipCryptoPassword(string password)
{
- byte[] salt;
- byte[] pwdVerifier;
- InitializeAESPassword(entry, Password, out salt, out pwdVerifier);
- // File format for AES:
- // Size (bytes) Content
- // ------------ -------
- // Variable Salt value
- // 2 Password verification value
- // Variable Encrypted file data
- // 10 Authentication code
- //
- // Value in the "compressed size" fields of the local file header and the central directory entry
- // is the total size of all the items listed above. In other words, it is the total size of the
- // salt value, password verification value, encrypted data, and authentication code.
- baseOutputStream_.Write(salt, 0, salt.Length);
- baseOutputStream_.Write(pwdVerifier, 0, pwdVerifier.Length);
+ var pkManaged = new PkzipClassicManaged();
+ byte[] key = PkzipClassic.GenerateKeys(ZipStrings.ConvertToArray(password));
+ cryptoTransform_ = pkManaged.CreateEncryptor(key, null);
}
-
+
///
/// Writes the given buffer to the current entry.
///
@@ -849,144 +763,48 @@ public override void Finish()
long numEntries = entries.Count;
long sizeEntries = 0;
- foreach (ZipEntry entry in entries)
+ foreach (var entry in entries)
{
- WriteLeInt(ZipConstants.CentralHeaderSignature);
- WriteLeShort((entry.HostSystem << 8) | entry.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 = ZipStrings.ConvertToArray(entry.Flags, entry.Name);
-
- if (name.Length > 0xffff)
- {
- throw new ZipException("Name too long.");
- }
-
- var 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);
- }
+ sizeEntries += ZipFormat.WriteEndEntry(baseOutputStream_, entry);
+ }
- if (entry.Offset >= 0xffffffff)
- {
- ed.AddLeLong(entry.Offset);
- }
+ ZipFormat.WriteEndOfCentralDirectory(baseOutputStream_, numEntries, sizeEntries, offset, zipComment);
- ed.AddNewEntry(1);
- }
- else
- {
- ed.Delete(1);
- }
+ entries = null;
+ }
- if (entry.AESKeySize > 0)
+ /// >
+ public override async Task FinishAsync(CancellationToken ct)
+ {
+ using (var ms = new MemoryStream())
+ {
+ if (entries == null)
{
- AddExtraDataAES(entry, ed);
+ return;
}
- byte[] extra = ed.GetEntryData();
-
- byte[] entryComment =
- (entry.Comment != null) ?
- ZipStrings.ConvertToArray(entry.Flags, entry.Comment) :
- Empty.Array();
- if (entryComment.Length > 0xffff)
+ if (curEntry != null)
{
- throw new ZipException("Comment too long.");
+ await CloseEntryAsync(ct);
}
- WriteLeShort(name.Length);
- WriteLeShort(extra.Length);
- WriteLeShort(entryComment.Length);
- WriteLeShort(0); // disk number
- WriteLeShort(0); // internal file attributes
- // external file attributes
+ long numEntries = entries.Count;
+ long sizeEntries = 0;
- if (entry.ExternalFileAttributes != -1)
+ foreach (var entry in entries)
{
- WriteLeInt(entry.ExternalFileAttributes);
- }
- else
- {
- if (entry.IsDirectory)
- { // mark entry as directory (from nikolam.AT.perfectinfo.com)
- WriteLeInt(16);
- }
- else
+ await baseOutputStream_.WriteProcToStreamAsync(ms, s =>
{
- WriteLeInt(0);
- }
- }
-
- if (entry.Offset >= uint.MaxValue)
- {
- WriteLeInt(-1);
- }
- else
- {
- WriteLeInt((int)entry.Offset);
+ sizeEntries += ZipFormat.WriteEndEntry(s, entry);
+ }, ct);
}
- 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);
- }
+ await baseOutputStream_.WriteProcToStreamAsync(ms, s
+ => ZipFormat.WriteEndOfCentralDirectory(s, numEntries, sizeEntries, offset, zipComment),
+ ct);
- sizeEntries += ZipConstants.CentralHeaderBaseSize + name.Length + extra.Length + entryComment.Length;
+ entries = null;
}
-
- using (ZipHelperStream zhs = new ZipHelperStream(baseOutputStream_))
- {
- zhs.WriteEndOfCentralDirectory(numEntries, sizeEntries, offset, zipComment);
- }
-
- entries = null;
}
///
@@ -1047,14 +865,9 @@ public override void Flush()
private bool patchEntryHeader;
///
- /// Position to patch crc
- ///
- private long crcPatchPos = -1;
-
- ///
- /// Position to patch size.
+ /// The values to patch in the entry local header
///
- private long sizePatchPos = -1;
+ private EntryPatchData patchData;
// Default is dynamic which is not backwards compatible and can cause problems
// with XP's built in compression which cant read Zip64 archives.
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Base/InflaterDeflaterTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Base/InflaterDeflaterTests.cs
index 9df9319b4..e9ba0ad77 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/Base/InflaterDeflaterTests.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/Base/InflaterDeflaterTests.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Security;
using System.Text;
+using System.Threading;
using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.Tests.Base
@@ -113,7 +114,7 @@ private async Task DeflateAsync(byte[] data, int level, bool zlib)
outStream.IsStreamOwner = false;
await outStream.WriteAsync(data, 0, data.Length);
await outStream.FlushAsync();
- outStream.Finish();
+ await outStream.FinishAsync(CancellationToken.None);
}
return memoryStream;
}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Core/ByteOrderUtilsTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Core/ByteOrderUtilsTests.cs
new file mode 100644
index 000000000..1a5d271ff
--- /dev/null
+++ b/test/ICSharpCode.SharpZipLib.Tests/Core/ByteOrderUtilsTests.cs
@@ -0,0 +1,137 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using BO = ICSharpCode.SharpZipLib.Core.ByteOrderStreamExtensions;
+using ICSharpCode.SharpZipLib.Core;
+
+// ReSharper disable InconsistentNaming
+
+namespace ICSharpCode.SharpZipLib.Tests.Core
+{
+ [TestFixture]
+ [Category("Core")]
+ public class ByteOrderUtilsTests
+ {
+ private const short native16 = 0x1234;
+ private static readonly byte[] swapped16 = { 0x34, 0x12 };
+
+ private const int native32 = 0x12345678;
+ private static readonly byte[] swapped32 = { 0x78, 0x56, 0x34, 0x12 };
+
+ private const long native64 = 0x123456789abcdef0;
+ private static readonly byte[] swapped64 = { 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12 };
+
+ [Test]
+ public void ToSwappedBytes()
+ {
+ Assert.AreEqual(swapped16, BO.SwappedBytes(native16));
+ Assert.AreEqual(swapped16, BO.SwappedBytes((ushort)native16));
+
+ Assert.AreEqual(swapped32, BO.SwappedBytes(native32));
+ Assert.AreEqual(swapped32, BO.SwappedBytes((uint)native32));
+
+ Assert.AreEqual(swapped64, BO.SwappedBytes(native64));
+ Assert.AreEqual(swapped64, BO.SwappedBytes((ulong)native64));
+ }
+
+ [Test]
+ public void FromSwappedBytes()
+ {
+ Assert.AreEqual(native16, BO.SwappedS16(swapped16));
+ Assert.AreEqual(native16, BO.SwappedU16(swapped16));
+
+ Assert.AreEqual(native32, BO.SwappedS32(swapped32));
+ Assert.AreEqual(native32, BO.SwappedU32(swapped32));
+
+ Assert.AreEqual(native64, BO.SwappedS64(swapped64));
+ Assert.AreEqual(native64, BO.SwappedU64(swapped64));
+ }
+
+ [Test]
+ public void ReadLESigned16()
+ => TestReadLE(native16, 2, BO.ReadLEShort);
+
+ [Test]
+ public void ReadLESigned32()
+ => TestReadLE(native32,4, BO.ReadLEInt);
+
+ [Test]
+ public void ReadLESigned64()
+ => TestReadLE(native64,8, BO.ReadLELong);
+
+ [Test]
+ public void WriteLESigned16()
+ => TestWriteLE(swapped16, s => s.WriteLEShort(native16));
+
+ [Test]
+ public void WriteLESigned32()
+ => TestWriteLE(swapped32, s => s.WriteLEInt(native32));
+
+ [Test]
+ public void WriteLESigned64()
+ => TestWriteLE(swapped64, s => s.WriteLELong(native64));
+
+ [Test]
+ public void WriteLEUnsigned16()
+ => TestWriteLE(swapped16, s => s.WriteLEUshort((ushort)native16));
+
+ [Test]
+ public void WriteLEUnsigned32()
+ => TestWriteLE(swapped32, s => s.WriteLEUint(native32));
+
+ [Test]
+ public void WriteLEUnsigned64()
+ => TestWriteLE(swapped64, s => s.WriteLEUlong(native64));
+
+ [Test]
+ public async Task WriteLEAsyncSigned16()
+ => await TestWriteLEAsync(swapped16, (int)native16, BO.WriteLEShortAsync);
+
+ [Test]
+ public async Task WriteLEAsyncUnsigned16()
+ => await TestWriteLEAsync(swapped16, (ushort)native16, BO.WriteLEUshortAsync);
+
+ [Test]
+ public async Task WriteLEAsyncSigned32()
+ => await TestWriteLEAsync(swapped32, native32, BO.WriteLEIntAsync);
+ [Test]
+ public async Task WriteLEAsyncUnsigned32()
+ => await TestWriteLEAsync(swapped32, (uint)native32, BO.WriteLEUintAsync);
+
+ [Test]
+ public async Task WriteLEAsyncSigned64()
+ => await TestWriteLEAsync(swapped64, native64, BO.WriteLELongAsync);
+ [Test]
+ public async Task WriteLEAsyncUnsigned64()
+ => await TestWriteLEAsync(swapped64, (ulong)native64, BO.WriteLEUlongAsync);
+
+
+ private static void TestReadLE(T expected, int bytes, Func read)
+ {
+ using (var ms = new MemoryStream(swapped64, 8 - bytes, bytes))
+ {
+ Assert.AreEqual(expected, read(ms));
+ }
+ }
+
+ private static void TestWriteLE(byte[] expected, Action write)
+ {
+ using (var ms = new MemoryStream())
+ {
+ write(ms);
+ Assert.AreEqual(expected, ms.ToArray());
+ }
+ }
+
+ private static async Task TestWriteLEAsync(byte[] expected, T input, Func write)
+ {
+ using (var ms = new MemoryStream())
+ {
+ await write(ms, input, CancellationToken.None);
+ Assert.AreEqual(expected, ms.ToArray());
+ }
+ }
+ }
+}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/ICSharpCode.SharpZipLib.Tests.csproj b/test/ICSharpCode.SharpZipLib.Tests/ICSharpCode.SharpZipLib.Tests.csproj
index 12183fcdd..4a46e84f2 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/ICSharpCode.SharpZipLib.Tests.csproj
+++ b/test/ICSharpCode.SharpZipLib.Tests/ICSharpCode.SharpZipLib.Tests.csproj
@@ -5,7 +5,10 @@
netcoreapp3.1;net46
- 8
+ true
+ ..\..\assets\ICSharpCode.SharpZipLib.snk
+ true
+ 8.0
@@ -25,4 +28,10 @@
+
+
+ ICSharpCode.SharpZipLib.snk
+
+
+
diff --git a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs
index 179202b44..d0d2f2175 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/Utils.cs
@@ -2,6 +2,8 @@
using System;
using System.IO;
using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
namespace ICSharpCode.SharpZipLib.Tests.TestSupport
{
@@ -76,6 +78,12 @@ public static byte[] GetDummyBytes(int size, int seed = DefaultSeed)
random.NextBytes(bytes);
return bytes;
}
+
+ public static async Task WriteDummyDataAsync(Stream stream, int size = -1)
+ {
+ var bytes = GetDummyBytes(size);
+ await stream.WriteAsync(bytes, 0, bytes.Length);
+ }
///
/// Returns a file reference with bytes of dummy data written to it
diff --git a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/ZipTesting.cs b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/ZipTesting.cs
index 688b91dc3..fcc4fe9b8 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/TestSupport/ZipTesting.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/TestSupport/ZipTesting.cs
@@ -1,5 +1,7 @@
+using System;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
+using NUnit.Framework;
namespace ICSharpCode.SharpZipLib.Tests.TestSupport
{
@@ -8,30 +10,57 @@ namespace ICSharpCode.SharpZipLib.Tests.TestSupport
///
internal static class ZipTesting
{
+ public static void AssertValidZip(Stream stream, string password = null, bool usesAes = true)
+ {
+ Assert.That(TestArchive(stream, password), "Archive did not pass ZipFile.TestArchive");
+
+ if (!string.IsNullOrEmpty(password) && usesAes)
+ {
+ Assert.Ignore("ZipInputStream does not support AES");
+ }
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ Assert.DoesNotThrow(() =>
+ {
+ using var zis = new ZipInputStream(stream){Password = password};
+ while (zis.GetNextEntry() != null)
+ {
+ new StreamReader(zis).ReadToEnd();
+ }
+ }, "Archive could not be read by ZipInputStream");
+ }
+
///
/// Tests the archive.
///
/// The data.
+ /// The password.
///
- public static bool TestArchive(byte[] data)
+ public static bool TestArchive(byte[] data, string password = null)
{
- return TestArchive(data, null);
+ using var ms = new MemoryStream(data);
+ return TestArchive(new MemoryStream(data), password);
}
///
/// Tests the archive.
///
- /// The data.
+ /// The data.
/// The password.
/// true if archive tests ok; false otherwise.
- public static bool TestArchive(byte[] data, string password)
+ public static bool TestArchive(Stream stream, string password = null)
{
- using (MemoryStream ms = new MemoryStream(data))
- using (ZipFile zipFile = new ZipFile(ms))
+ using var zipFile = new ZipFile(stream)
{
- zipFile.Password = password;
- return zipFile.TestArchive(true);
- }
+ IsStreamOwner = false,
+ Password = password,
+ };
+
+ return zipFile.TestArchive(true, TestStrategy.FindAllErrors, (status, message) =>
+ {
+ if (!string.IsNullOrWhiteSpace(message)) TestContext.Out.WriteLine(message);
+ });
}
}
}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs
index f2b3e7859..2d641370c 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs
@@ -913,7 +913,10 @@ private void TestEncryptedDirectoryEntry(MemoryStream s, int aesKeySize)
var ms2 = new MemoryStream(s.ToArray());
using (ZipFile zf = new ZipFile(ms2))
{
- Assert.IsTrue(zf.TestArchive(true));
+ Assert.IsTrue(zf.TestArchive(true, TestStrategy.FindAllErrors,
+ (status, message) => {
+ if (!string.IsNullOrWhiteSpace(message)) TestContext.Out.WriteLine(message);
+ }));
}
}
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs
new file mode 100644
index 000000000..b693f205d
--- /dev/null
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/ZipStreamAsyncTests.cs
@@ -0,0 +1,102 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using ICSharpCode.SharpZipLib.Tests.TestSupport;
+using ICSharpCode.SharpZipLib.Zip;
+using NUnit.Framework;
+
+namespace ICSharpCode.SharpZipLib.Tests.Zip
+{
+ [TestFixture]
+ public class ZipStreamAsyncTests
+ {
+
+ [Test]
+ [Category("Zip")]
+ [Category("Async")]
+ public async Task WriteZipStreamUsingAsync()
+ {
+#if NETCOREAPP3_1_OR_GREATER
+ await using var ms = new MemoryStream();
+
+ await using (var outStream = new ZipOutputStream(ms){IsStreamOwner = false})
+ {
+ await outStream.PutNextEntryAsync(new ZipEntry("FirstFile"));
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.PutNextEntryAsync(new ZipEntry("SecondFile"));
+ await Utils.WriteDummyDataAsync(outStream, 12);
+ }
+
+ ZipTesting.AssertValidZip(ms);
+#endif
+ }
+
+ [Test]
+ [Category("Zip")]
+ [Category("Async")]
+ public async Task WriteZipStreamAsync ()
+ {
+ using var ms = new MemoryStream();
+
+ using(var outStream = new ZipOutputStream(ms) { IsStreamOwner = false })
+ {
+ await outStream.PutNextEntryAsync(new ZipEntry("FirstFile"));
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.PutNextEntryAsync(new ZipEntry("SecondFile"));
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.FinishAsync(CancellationToken.None);
+ }
+
+ ZipTesting.AssertValidZip(ms);
+ }
+
+
+ [Test]
+ [Category("Zip")]
+ [Category("Async")]
+ public async Task WriteZipStreamWithAesAsync()
+ {
+ using var ms = new MemoryStream();
+ var password = "f4ls3p0s1t1v3";
+
+ using (var outStream = new ZipOutputStream(ms){IsStreamOwner = false, Password = password})
+ {
+ await outStream.PutNextEntryAsync(new ZipEntry("FirstFile"){AESKeySize = 256});
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.PutNextEntryAsync(new ZipEntry("SecondFile"){AESKeySize = 256});
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.FinishAsync(CancellationToken.None);
+ }
+
+ ZipTesting.AssertValidZip(ms, password);
+ }
+
+ [Test]
+ [Category("Zip")]
+ [Category("Async")]
+ public async Task WriteZipStreamWithZipCryptoAsync()
+ {
+ using var ms = new MemoryStream();
+ var password = "f4ls3p0s1t1v3";
+
+ using (var outStream = new ZipOutputStream(ms){IsStreamOwner = false, Password = password})
+ {
+ await outStream.PutNextEntryAsync(new ZipEntry("FirstFile"){AESKeySize = 0});
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.PutNextEntryAsync(new ZipEntry("SecondFile"){AESKeySize = 0});
+ await Utils.WriteDummyDataAsync(outStream, 12);
+
+ await outStream.FinishAsync(CancellationToken.None);
+ }
+
+ ZipTesting.AssertValidZip(ms, password, false);
+ }
+
+ }
+}