184 lines
6.7 KiB
C#
184 lines
6.7 KiB
C#
|
using System;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using SharpCompress.Common.Zip.Headers;
|
|||
|
using SharpCompress.Compressor;
|
|||
|
#if BZIP2
|
|||
|
using SharpCompress.Compressor.BZip2;
|
|||
|
#endif
|
|||
|
#if DEFLATE
|
|||
|
using SharpCompress.Compressor.Deflate;
|
|||
|
#endif
|
|||
|
#if LZMA
|
|||
|
using SharpCompress.Compressor.LZMA;
|
|||
|
#endif
|
|||
|
#if PPMd
|
|||
|
using SharpCompress.Compressor.PPMd;
|
|||
|
#endif
|
|||
|
using SharpCompress.IO;
|
|||
|
|
|||
|
namespace SharpCompress.Common.Zip
|
|||
|
{
|
|||
|
internal abstract class ZipFilePart : FilePart
|
|||
|
{
|
|||
|
internal ZipFilePart(ZipFileEntry header, Stream stream)
|
|||
|
{
|
|||
|
Header = header;
|
|||
|
header.Part = this;
|
|||
|
this.BaseStream = stream;
|
|||
|
}
|
|||
|
|
|||
|
internal Stream BaseStream { get; private set; }
|
|||
|
internal ZipFileEntry Header { get; set; }
|
|||
|
|
|||
|
|
|||
|
internal override string FilePartName
|
|||
|
{
|
|||
|
get { return Header.Name; }
|
|||
|
}
|
|||
|
|
|||
|
internal override Stream GetCompressedStream()
|
|||
|
{
|
|||
|
if (!Header.HasData)
|
|||
|
{
|
|||
|
return Stream.Null;
|
|||
|
}
|
|||
|
Stream decompressionStream = CreateDecompressionStream(GetCryptoStream(CreateBaseStream()));
|
|||
|
if (LeaveStreamOpen)
|
|||
|
{
|
|||
|
return new NonDisposingStream(decompressionStream);
|
|||
|
}
|
|||
|
return decompressionStream;
|
|||
|
}
|
|||
|
|
|||
|
internal override Stream GetRawStream()
|
|||
|
{
|
|||
|
if (!Header.HasData)
|
|||
|
{
|
|||
|
return Stream.Null;
|
|||
|
}
|
|||
|
return CreateBaseStream();
|
|||
|
}
|
|||
|
|
|||
|
protected abstract Stream CreateBaseStream();
|
|||
|
|
|||
|
protected bool LeaveStreamOpen
|
|||
|
{
|
|||
|
get { return FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor); }
|
|||
|
}
|
|||
|
|
|||
|
protected Stream CreateDecompressionStream(Stream stream)
|
|||
|
{
|
|||
|
switch (Header.CompressionMethod)
|
|||
|
{
|
|||
|
case ZipCompressionMethod.None:
|
|||
|
{
|
|||
|
return stream;
|
|||
|
}
|
|||
|
case ZipCompressionMethod.Deflate:
|
|||
|
{
|
|||
|
return new System.IO.Compression.DeflateStream(stream,
|
|||
|
System.IO.Compression.CompressionMode.Decompress);
|
|||
|
}
|
|||
|
#if BZIP2
|
|||
|
case ZipCompressionMethod.BZip2:
|
|||
|
{
|
|||
|
return new BZip2Stream(stream, CompressionMode.Decompress);
|
|||
|
}
|
|||
|
#endif
|
|||
|
#if LZMA
|
|||
|
case ZipCompressionMethod.LZMA:
|
|||
|
{
|
|||
|
if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
|
|||
|
{
|
|||
|
throw new NotSupportedException("LZMA with pkware encryption.");
|
|||
|
}
|
|||
|
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);
|
|||
|
}
|
|||
|
#endif
|
|||
|
#if PPMd
|
|||
|
case ZipCompressionMethod.PPMd:
|
|||
|
{
|
|||
|
var props = new byte[2];
|
|||
|
stream.Read(props, 0, props.Length);
|
|||
|
return new PpmdStream(new PpmdProperties(props), stream, false);
|
|||
|
}
|
|||
|
#endif
|
|||
|
case ZipCompressionMethod.WinzipAes:
|
|||
|
{
|
|||
|
ExtraData data = Header.Extra.Where(x => x.Type == ExtraDataType.WinZipAes).SingleOrDefault();
|
|||
|
if (data == null)
|
|||
|
{
|
|||
|
throw new InvalidFormatException("No Winzip AES extra data found.");
|
|||
|
}
|
|||
|
if (data.Length != 7)
|
|||
|
{
|
|||
|
throw new InvalidFormatException("Winzip data length is not 7.");
|
|||
|
}
|
|||
|
ushort method = BitConverter.ToUInt16(data.DataBytes, 0);
|
|||
|
|
|||
|
if (method != 0x01 && method != 0x02)
|
|||
|
{
|
|||
|
throw new InvalidFormatException("Unexpected vendor version number for WinZip AES metadata");
|
|||
|
}
|
|||
|
|
|||
|
ushort vendorId = BitConverter.ToUInt16(data.DataBytes, 2);
|
|||
|
if (vendorId != 0x4541)
|
|||
|
{
|
|||
|
throw new InvalidFormatException("Unexpected vendor ID for WinZip AES metadata");
|
|||
|
}
|
|||
|
Header.CompressionMethod = (ZipCompressionMethod)BitConverter.ToUInt16(data.DataBytes, 5);
|
|||
|
return CreateDecompressionStream(stream);
|
|||
|
}
|
|||
|
default:
|
|||
|
{
|
|||
|
throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected Stream GetCryptoStream(Stream plainStream)
|
|||
|
{
|
|||
|
if ((Header.CompressedSize == 0)
|
|||
|
#if !PORTABLE && !NETFX_CORE
|
|||
|
&& ((Header.PkwareTraditionalEncryptionData != null)
|
|||
|
|| (Header.WinzipAesEncryptionData != null)))
|
|||
|
#else
|
|||
|
&& (Header.PkwareTraditionalEncryptionData != null))
|
|||
|
#endif
|
|||
|
{
|
|||
|
throw new NotSupportedException("Cannot encrypt file with unknown size at start.");
|
|||
|
}
|
|||
|
if ((Header.CompressedSize == 0)
|
|||
|
&& FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor))
|
|||
|
{
|
|||
|
plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close
|
|||
|
}
|
|||
|
if (Header.PkwareTraditionalEncryptionData != null)
|
|||
|
{
|
|||
|
return new PkwareTraditionalCryptoStream(plainStream, Header.PkwareTraditionalEncryptionData,
|
|||
|
CryptoMode.Decrypt);
|
|||
|
}
|
|||
|
#if !PORTABLE && !NETFX_CORE
|
|||
|
if (Header.WinzipAesEncryptionData != null)
|
|||
|
{
|
|||
|
//only read 10 less because the last ten are auth bytes
|
|||
|
return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10);
|
|||
|
}
|
|||
|
#endif
|
|||
|
return plainStream;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|