Files
sharpcompress/src/SharpCompress/Common/Zip/ZipFilePart.cs

218 lines
7.9 KiB
C#
Raw Normal View History

2022-06-19 22:05:52 +02:00
using System;
using System.Buffers.Binary;
2015-12-30 11:19:42 +00:00
using System.IO;
using System.Linq;
using SharpCompress.Common.Zip.Headers;
2016-09-26 11:49:49 +01:00
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
2018-02-26 23:49:09 +01:00
using SharpCompress.Compressors.Deflate64;
2016-09-26 11:49:49 +01:00
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.PPMd;
2015-12-30 11:19:42 +00:00
using SharpCompress.IO;
namespace SharpCompress.Common.Zip
{
internal abstract class ZipFilePart : FilePart
{
2022-12-20 13:45:47 +00:00
internal ZipFilePart(ZipFileEntry header, Stream stream) : base(header.ArchiveEncoding)
2015-12-30 11:19:42 +00:00
{
Header = header;
header.Part = this;
BaseStream = stream;
2015-12-30 11:19:42 +00:00
}
2017-05-23 16:15:47 +01:00
internal Stream BaseStream { get; }
2015-12-30 11:19:42 +00:00
internal ZipFileEntry Header { get; set; }
2017-05-19 10:52:49 +01:00
internal override string FilePartName => Header.Name;
2015-12-30 11:19:42 +00:00
internal override Stream GetCompressedStream()
{
if (!Header.HasData)
{
return Stream.Null;
}
2022-12-20 13:45:47 +00:00
Stream decompressionStream = CreateDecompressionStream(
GetCryptoStream(CreateBaseStream()),
Header.CompressionMethod
);
2015-12-30 11:19:42 +00:00
if (LeaveStreamOpen)
{
2022-06-19 22:05:52 +02:00
return NonDisposingStream.Create(decompressionStream);
2015-12-30 11:19:42 +00:00
}
return decompressionStream;
}
internal override Stream GetRawStream()
{
if (!Header.HasData)
{
return Stream.Null;
}
return CreateBaseStream();
}
protected abstract Stream CreateBaseStream();
2022-12-20 13:45:47 +00:00
protected bool LeaveStreamOpen =>
FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64;
2015-12-30 11:19:42 +00:00
protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method)
2015-12-30 11:19:42 +00:00
{
switch (method)
2015-12-30 11:19:42 +00:00
{
case ZipCompressionMethod.None:
2022-12-20 13:45:47 +00:00
{
return stream;
}
2015-12-30 11:19:42 +00:00
case ZipCompressionMethod.Deflate:
2022-12-20 13:45:47 +00:00
{
return new DeflateStream(stream, CompressionMode.Decompress);
}
2018-02-26 23:49:09 +01:00
case ZipCompressionMethod.Deflate64:
2022-12-20 13:45:47 +00:00
{
return new Deflate64Stream(stream, CompressionMode.Decompress);
}
2015-12-30 11:19:42 +00:00
case ZipCompressionMethod.BZip2:
2022-12-20 13:45:47 +00:00
{
return new BZip2Stream(stream, CompressionMode.Decompress, false);
}
2015-12-30 11:19:42 +00:00
case ZipCompressionMethod.LZMA:
2022-12-20 13:45:47 +00:00
{
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
2015-12-30 11:19:42 +00:00
{
2022-12-20 13:45:47 +00:00
throw new NotSupportedException("LZMA with pkware encryption.");
2015-12-30 11:19:42 +00:00
}
2022-12-20 13:45:47 +00:00
var reader = new BinaryReader(stream);
reader.ReadUInt16(); //LZMA version
var props = new byte[reader.ReadUInt16()];
reader.Read(props, 0, props.Length);
return new LzmaStream(
props,
stream,
Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
? -1
: (long)Header.UncompressedSize
);
}
2015-12-30 11:19:42 +00:00
case ZipCompressionMethod.PPMd:
2022-12-20 13:45:47 +00:00
{
Span<byte> props = stackalloc byte[2];
stream.ReadFully(props);
return new PpmdStream(new PpmdProperties(props), stream, false);
}
case ZipCompressionMethod.WinzipAes:
{
ExtraData? data = Header.Extra.SingleOrDefault(
x => x.Type == ExtraDataType.WinZipAes
);
if (data is null)
2015-12-30 11:19:42 +00:00
{
2022-12-20 13:45:47 +00:00
throw new InvalidFormatException("No Winzip AES extra data found.");
2015-12-30 11:19:42 +00:00
}
2022-12-20 13:45:47 +00:00
if (data.Length != 7)
2015-12-30 11:19:42 +00:00
{
2022-12-20 13:45:47 +00:00
throw new InvalidFormatException("Winzip data length is not 7.");
}
ushort compressedMethod = BinaryPrimitives.ReadUInt16LittleEndian(
data.DataBytes
);
2022-12-20 13:45:47 +00:00
if (compressedMethod != 0x01 && compressedMethod != 0x02)
{
throw new InvalidFormatException(
"Unexpected vendor version number for WinZip AES metadata"
);
2015-12-30 11:19:42 +00:00
}
2022-12-20 13:45:47 +00:00
ushort vendorId = BinaryPrimitives.ReadUInt16LittleEndian(
data.DataBytes.AsSpan(2)
);
if (vendorId != 0x4541)
2021-01-09 13:33:34 +00:00
{
2022-12-20 13:45:47 +00:00
throw new InvalidFormatException(
"Unexpected vendor ID for WinZip AES metadata"
);
2021-01-09 13:33:34 +00:00
}
2022-12-20 13:45:47 +00:00
return CreateDecompressionStream(
stream,
(ZipCompressionMethod)
BinaryPrimitives.ReadUInt16LittleEndian(data.DataBytes.AsSpan(5))
);
}
default:
{
throw new NotSupportedException(
"CompressionMethod: " + Header.CompressionMethod
);
}
2015-12-30 11:19:42 +00:00
}
}
2016-11-24 17:11:43 +03:00
protected Stream GetCryptoStream(Stream plainStream)
{
bool isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted);
if (Header.CompressedSize == 0 && isFileEncrypted)
2016-11-24 17:11:43 +03:00
{
throw new NotSupportedException("Cannot encrypt file with unknown size at start.");
}
2022-12-20 13:45:47 +00:00
if (
(
Header.CompressedSize == 0
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor)
) || Header.IsZip64
)
2016-11-24 17:11:43 +03:00
{
2022-06-19 22:05:52 +02:00
plainStream = NonDisposingStream.Create(plainStream); //make sure AES doesn't close
2016-11-24 17:11:43 +03:00
}
else
{
plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close
}
if (isFileEncrypted)
2016-11-24 17:11:43 +03:00
{
switch (Header.CompressionMethod)
{
case ZipCompressionMethod.None:
case ZipCompressionMethod.Deflate:
case ZipCompressionMethod.Deflate64:
case ZipCompressionMethod.BZip2:
case ZipCompressionMethod.LZMA:
case ZipCompressionMethod.PPMd:
2022-12-20 13:45:47 +00:00
{
return new PkwareTraditionalCryptoStream(
plainStream,
Header.ComposeEncryptionData(plainStream),
CryptoMode.Decrypt
);
}
2016-11-24 17:11:43 +03:00
case ZipCompressionMethod.WinzipAes:
2022-12-20 13:45:47 +00:00
{
if (Header.WinzipAesEncryptionData != null)
2016-11-24 17:11:43 +03:00
{
2022-12-20 13:45:47 +00:00
return new WinzipAesCryptoStream(
plainStream,
Header.WinzipAesEncryptionData,
Header.CompressedSize - 10
);
2016-11-24 17:11:43 +03:00
}
2022-12-20 13:45:47 +00:00
return plainStream;
}
2016-11-24 17:11:43 +03:00
default:
2022-12-20 13:45:47 +00:00
{
throw new InvalidOperationException("Header.CompressionMethod is invalid");
}
2016-11-24 17:11:43 +03:00
}
}
return plainStream;
}
}
}