mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-07-09 02:26:47 +00:00
212 lines
6.7 KiB
C#
212 lines
6.7 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using SharpCompress;
|
|
using SharpCompress.Common.Zip.Headers;
|
|
using SharpCompress.IO;
|
|
|
|
namespace SharpCompress.Common.Zip;
|
|
|
|
internal partial class ZipHeaderFactory
|
|
{
|
|
internal const uint ENTRY_HEADER_BYTES = 0x04034b50;
|
|
internal const uint POST_DATA_DESCRIPTOR = 0x08074b50;
|
|
internal const uint DIRECTORY_START_HEADER_BYTES = 0x02014b50;
|
|
internal const uint DIRECTORY_END_HEADER_BYTES = 0x06054b50;
|
|
internal const uint DIGITAL_SIGNATURE = 0x05054b50;
|
|
internal const uint SPLIT_ARCHIVE_HEADER_BYTES = 0x30304b50;
|
|
|
|
internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50;
|
|
internal const uint ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR = 0x07064b50;
|
|
|
|
protected LocalEntryHeader? _lastEntryHeader;
|
|
private readonly string? _password;
|
|
private readonly StreamingMode _mode;
|
|
private readonly IArchiveEncoding _archiveEncoding;
|
|
|
|
protected ZipHeaderFactory(
|
|
StreamingMode mode,
|
|
string? password,
|
|
IArchiveEncoding archiveEncoding
|
|
)
|
|
{
|
|
_mode = mode;
|
|
_password = password;
|
|
_archiveEncoding = archiveEncoding;
|
|
}
|
|
|
|
protected ZipHeader? ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false)
|
|
{
|
|
switch (headerBytes)
|
|
{
|
|
case ENTRY_HEADER_BYTES:
|
|
{
|
|
var entryHeader = new LocalEntryHeader(_archiveEncoding);
|
|
entryHeader.Read(reader);
|
|
LoadHeader(entryHeader, reader.BaseStream);
|
|
|
|
_lastEntryHeader = entryHeader;
|
|
return entryHeader;
|
|
}
|
|
case DIRECTORY_START_HEADER_BYTES:
|
|
{
|
|
var entry = new DirectoryEntryHeader(_archiveEncoding);
|
|
entry.Read(reader);
|
|
return entry;
|
|
}
|
|
case POST_DATA_DESCRIPTOR:
|
|
{
|
|
if (
|
|
_lastEntryHeader != null
|
|
&& FlagUtility.HasFlag(
|
|
_lastEntryHeader.NotNull().Flags,
|
|
HeaderFlags.UsePostDataDescriptor
|
|
)
|
|
)
|
|
{
|
|
_lastEntryHeader.Crc = reader.ReadUInt32();
|
|
_lastEntryHeader.IsCrcAvailable = true;
|
|
_lastEntryHeader.CompressedSize = zip64
|
|
? (long)reader.ReadUInt64()
|
|
: reader.ReadUInt32();
|
|
_lastEntryHeader.UncompressedSize = zip64
|
|
? (long)reader.ReadUInt64()
|
|
: reader.ReadUInt32();
|
|
}
|
|
else
|
|
{
|
|
reader.ReadBytes(zip64 ? 20 : 12);
|
|
}
|
|
return null;
|
|
}
|
|
case DIGITAL_SIGNATURE:
|
|
return null;
|
|
case DIRECTORY_END_HEADER_BYTES:
|
|
{
|
|
var entry = new DirectoryEndHeader();
|
|
entry.Read(reader);
|
|
return entry;
|
|
}
|
|
case SPLIT_ARCHIVE_HEADER_BYTES:
|
|
{
|
|
return new SplitHeader();
|
|
}
|
|
case ZIP64_END_OF_CENTRAL_DIRECTORY:
|
|
{
|
|
var entry = new Zip64DirectoryEndHeader();
|
|
entry.Read(reader);
|
|
return entry;
|
|
}
|
|
case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR:
|
|
{
|
|
var entry = new Zip64DirectoryEndLocatorHeader();
|
|
entry.Read(reader);
|
|
return entry;
|
|
}
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
internal static bool IsHeader(uint headerBytes)
|
|
{
|
|
switch (headerBytes)
|
|
{
|
|
case ENTRY_HEADER_BYTES:
|
|
case DIRECTORY_START_HEADER_BYTES:
|
|
case POST_DATA_DESCRIPTOR:
|
|
case DIGITAL_SIGNATURE:
|
|
case DIRECTORY_END_HEADER_BYTES:
|
|
case SPLIT_ARCHIVE_HEADER_BYTES:
|
|
case ZIP64_END_OF_CENTRAL_DIRECTORY:
|
|
case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void LoadHeader(ZipFileEntry entryHeader, Stream stream)
|
|
{
|
|
if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.Encrypted))
|
|
{
|
|
if (
|
|
!entryHeader.IsDirectory
|
|
&& entryHeader.CompressedSize == 0
|
|
&& FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor)
|
|
)
|
|
{
|
|
throw new NotSupportedException(
|
|
"SharpCompress cannot currently read non-seekable Zip Streams with encrypted data that has been written in a non-seekable manner."
|
|
);
|
|
}
|
|
|
|
if (_password is null)
|
|
{
|
|
throw new CryptographicException("No password supplied for encrypted zip.");
|
|
}
|
|
|
|
entryHeader.Password = _password;
|
|
|
|
if (entryHeader.CompressionMethod == ZipCompressionMethod.WinzipAes)
|
|
{
|
|
var data = entryHeader.Extra.SingleOrDefault(x =>
|
|
x.Type == ExtraDataType.WinZipAes
|
|
);
|
|
if (data != null)
|
|
{
|
|
var keySize = (WinzipAesKeySize)data.DataBytes[4];
|
|
|
|
var salt = new byte[WinzipAesEncryptionData.KeyLengthInBytes(keySize) / 2];
|
|
var passwordVerifyValue = new byte[2];
|
|
stream.Read(salt, 0, salt.Length);
|
|
stream.Read(passwordVerifyValue, 0, 2);
|
|
entryHeader.WinzipAesEncryptionData = new WinzipAesEncryptionData(
|
|
keySize,
|
|
salt,
|
|
passwordVerifyValue,
|
|
_password
|
|
);
|
|
|
|
entryHeader.CompressedSize -= (uint)(salt.Length + 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (entryHeader.IsDirectory)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//if (FlagUtility.HasFlag(entryHeader.Flags, HeaderFlags.UsePostDataDescriptor))
|
|
//{
|
|
// entryHeader.PackedStream = new ReadOnlySubStream(stream);
|
|
//}
|
|
//else
|
|
//{
|
|
switch (_mode)
|
|
{
|
|
case StreamingMode.Seekable:
|
|
{
|
|
entryHeader.DataStartPosition = stream.Position;
|
|
stream.Position += entryHeader.CompressedSize;
|
|
break;
|
|
}
|
|
|
|
case StreamingMode.Streaming:
|
|
{
|
|
entryHeader.PackedStream = stream;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
throw new InvalidFormatException("Invalid StreamingMode");
|
|
}
|
|
}
|
|
|
|
//}
|
|
}
|
|
}
|