diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
index 7fc1c5592..41b83bef5 100644
--- a/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
+++ b/src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs
@@ -398,9 +398,9 @@ private bool HaveKeys
///
/// The file doesn't contain a valid zip archive.
///
- public ZipFile(string name) :
- this(name, null)
- {
+ public ZipFile(string name) :
+ this(name, null)
+ {
}
@@ -534,10 +534,10 @@ public ZipFile(Stream stream) :
///
/// The stream argument is null.
///
- public ZipFile(Stream stream, bool leaveOpen) :
- this(stream, leaveOpen, null)
- {
-
+ public ZipFile(Stream stream, bool leaveOpen) :
+ this(stream, leaveOpen, null)
+ {
+
}
///
@@ -789,7 +789,8 @@ public Encoding ZipCryptoEncoding
///
public StringCodec StringCodec
{
- set {
+ set
+ {
_stringCodec = value;
if (!isNewArchive_)
{
@@ -1182,7 +1183,7 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
bool testData = (tests & HeaderTest.Extract) != 0;
var entryAbsOffset = offsetOfFirstEntry + entry.Offset;
-
+
baseStream_.Seek(entryAbsOffset, SeekOrigin.Begin);
var signature = (int)ReadLEUint();
@@ -1258,9 +1259,9 @@ private long TestLocalHeader(ZipEntry entry, HeaderTest tests)
throw new ZipException($"Version required to extract this entry not supported ({extractVersion})");
}
- const GeneralBitFlags notSupportedFlags = GeneralBitFlags.Patched
- | GeneralBitFlags.StrongEncryption
- | GeneralBitFlags.EnhancedCompress
+ const GeneralBitFlags notSupportedFlags = GeneralBitFlags.Patched
+ | GeneralBitFlags.StrongEncryption
+ | GeneralBitFlags.EnhancedCompress
| GeneralBitFlags.HeaderMasked;
if (localFlags.HasAny(notSupportedFlags))
{
@@ -1677,8 +1678,8 @@ public void CommitUpdate()
{
// Create an empty archive if none existed originally.
if (entries_.Length != 0) return;
- byte[] theComment = (newComment_ != null)
- ? newComment_.RawComment
+ byte[] theComment = (newComment_ != null)
+ ? newComment_.RawComment
: _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_);
ZipFormat.WriteEndOfCentralDirectory(baseStream_, 0, 0, 0, theComment);
}
@@ -2165,7 +2166,12 @@ private void WriteLocalEntryHeader(ZipUpdate update)
// No need to compress - no data.
entry.CompressedSize = entry.Size;
entry.Crc = 0;
- entry.CompressionMethod = CompressionMethod.Stored;
+
+ // Crypted files should be deflated, even with no data (such as directories)
+ if (!HaveKeys)
+ {
+ entry.CompressionMethod = CompressionMethod.Stored;
+ }
}
}
else if (entry.CompressionMethod == CompressionMethod.Stored)
@@ -2583,15 +2589,15 @@ private void CopyBytes(ZipUpdate update, Stream destination, Stream source,
/// The descriptor size, zero if there isn't one.
private static int GetDescriptorSize(ZipUpdate update, bool includingSignature)
{
- if (!((GeneralBitFlags)update.Entry.Flags).HasAny(GeneralBitFlags.Descriptor))
+ if (!((GeneralBitFlags)update.Entry.Flags).HasAny(GeneralBitFlags.Descriptor))
return 0;
-
- var descriptorWithSignature = update.Entry.LocalHeaderRequiresZip64
- ? ZipConstants.Zip64DataDescriptorSize
+
+ var descriptorWithSignature = update.Entry.LocalHeaderRequiresZip64
+ ? ZipConstants.Zip64DataDescriptorSize
: ZipConstants.DataDescriptorSize;
- return includingSignature
- ? descriptorWithSignature
+ return includingSignature
+ ? descriptorWithSignature
: descriptorWithSignature - sizeof(int);
}
@@ -2878,7 +2884,7 @@ private void CopyEntryDirect(ZipFile workFile, ZipUpdate update, ref long destin
// Clumsy way of handling retrieving the original name and extra data length for now.
// TODO: Stop re-reading name and data length in CopyEntryDirect.
-
+
uint nameLength = ReadLEUshort();
uint extraLength = ReadLEUshort();
@@ -3013,7 +3019,7 @@ private void UpdateCommentOnly()
}
finally
{
- if(updateFile != baseStream_)
+ if (updateFile != baseStream_)
updateFile.Dispose();
}
@@ -3178,7 +3184,7 @@ private void RunUpdates()
}
byte[] theComment = newComment_?.RawComment ?? _stringCodec.ZipArchiveCommentEncoding.GetBytes(comment_);
- ZipFormat.WriteEndOfCentralDirectory(workFile.baseStream_, updateCount_,
+ ZipFormat.WriteEndOfCentralDirectory(workFile.baseStream_, updateCount_,
sizeEntries, centralDirOffset, theComment);
endOfStream = workFile.baseStream_.Position;
@@ -3520,7 +3526,7 @@ 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)
+ private long LocateBlockWithSignature(int signature, long endLocation, int minimumBlockSize, int maximumVariableData)
=> ZipFormat.LocateBlockWithSignature(baseStream_, signature, endLocation, minimumBlockSize, maximumVariableData);
///
@@ -3578,14 +3584,14 @@ private void ReadEntries()
}
bool isZip64 = false;
-
+
// Check if zip64 header information is required.
bool requireZip64 = thisDiskNumber == 0xffff ||
- startCentralDirDisk == 0xffff ||
- entriesForThisDisk == 0xffff ||
- entriesForWholeCentralDir == 0xffff ||
- centralDirSize == 0xffffffff ||
- offsetOfCentralDir == 0xffffffff;
+ startCentralDirDisk == 0xffff ||
+ entriesForThisDisk == 0xffff ||
+ entriesForWholeCentralDir == 0xffff ||
+ centralDirSize == 0xffffffff ||
+ offsetOfCentralDir == 0xffffffff;
// #357 - always check for the existence of the Zip64 central directory.
// #403 - Take account of the fixed size of the locator when searching.
@@ -3676,7 +3682,7 @@ private void ReadEntries()
int extraLen = ReadLEUshort();
int commentLen = ReadLEUshort();
-
+
// ReSharper disable once UnusedVariable, Currently unused but needs to be read to offset the stream
int diskStartNo = ReadLEUshort();
// ReSharper disable once UnusedVariable, Currently unused but needs to be read to offset the stream
@@ -3773,9 +3779,9 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)
int saltLen = entry.AESSaltLen;
byte[] saltBytes = new byte[saltLen];
int saltIn = StreamUtils.ReadRequestedBytes(baseStream, saltBytes, offset: 0, saltLen);
-
+
if (saltIn != saltLen) throw new ZipException($"AES Salt expected {saltLen} git {saltIn}");
-
+
byte[] pwdVerifyRead = new byte[2];
StreamUtils.ReadFully(baseStream, pwdVerifyRead);
int blockSize = entry.AESKeySize / 8; // bits to bytes
@@ -3819,7 +3825,7 @@ private Stream CreateAndInitDecryptionStream(Stream baseStream, ZipEntry entry)
private Stream CreateAndInitEncryptionStream(Stream baseStream, ZipEntry entry)
{
if (entry.Version >= ZipConstants.VersionStrongEncryption &&
- entry.HasFlag(GeneralBitFlags.StrongEncryption)) return null;
+ entry.HasFlag(GeneralBitFlags.StrongEncryption)) return null;
var classicManaged = new PkzipClassicManaged();
diff --git a/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs b/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs
index f3bf9a995..bdc3af23b 100644
--- a/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs
+++ b/test/ICSharpCode.SharpZipLib.Tests/Zip/GeneralHandling.cs
@@ -832,5 +832,53 @@ public void PasswordCheckingWithDateInExtraData()
Assert.AreEqual(checkTime.DateTime, uno.DateTime);
}
}
+
+ [Test]
+ [Category("Zip")]
+ [Category("Encryption")]
+ public void ReadingEncryptedZipWithDirectories()
+ {
+ var ms = new MemoryStream();
+ using (ZipOutputStream outStream = new ZipOutputStream(ms))
+ {
+ outStream.IsStreamOwner = false;
+ outStream.Password = "testPassword123";
+ outStream.SetLevel(5);
+
+ var entry = new ZipEntry("Folder/");
+ outStream.PutNextEntry(entry);
+
+ entry = new ZipEntry("Folder/File.txt");
+ outStream.PutNextEntry(entry);
+ byte[] fileData = Encoding.UTF8.GetBytes("This is some test data");
+ outStream.Write(fileData, 0, fileData.Length);
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+ using (ZipFile zipFile = new ZipFile(ms))
+ {
+ zipFile.BeginUpdate();
+ zipFile.IsStreamOwner = false;
+ zipFile.Password = "testPassword123";
+ zipFile.AddDirectory("Folder2/");
+ zipFile.Add(new MemoryDataSource(Encoding.UTF8.GetBytes("Test content")), "Folder2/File2.txt");
+ zipFile.CommitUpdate();
+ }
+
+ ms.Seek(0, SeekOrigin.Begin);
+ using (var inStream = new ZipInputStream(ms))
+ {
+ inStream.IsStreamOwner = false;
+ inStream.Password = "testPassword123";
+ ZipEntry fileEntry = inStream.GetNextEntry();
+ Assert.AreEqual("Folder/", fileEntry.Name);
+ fileEntry = inStream.GetNextEntry();
+ Assert.AreEqual("Folder/File.txt", fileEntry.Name);
+ fileEntry = inStream.GetNextEntry();
+ Assert.AreEqual("Folder2/", fileEntry.Name);
+ fileEntry = inStream.GetNextEntry();
+ Assert.AreEqual("Folder2/File2.txt", fileEntry.Name);
+ }
+ }
}
}