ZipFilePart.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using SharpCompress.Common.Zip.Headers;
  5. using SharpCompress.Compressors;
  6. using SharpCompress.Compressors.BZip2;
  7. using SharpCompress.Compressors.Deflate;
  8. using SharpCompress.Compressors.Deflate64;
  9. using SharpCompress.Compressors.LZMA;
  10. using SharpCompress.Compressors.PPMd;
  11. using SharpCompress.Converters;
  12. using SharpCompress.IO;
  13. namespace SharpCompress.Common.Zip
  14. {
  15. internal abstract class ZipFilePart : FilePart
  16. {
  17. internal ZipFilePart(ZipFileEntry header, Stream stream)
  18. : base(header.ArchiveEncoding)
  19. {
  20. Header = header;
  21. header.Part = this;
  22. BaseStream = stream;
  23. }
  24. internal Stream BaseStream { get; }
  25. internal ZipFileEntry Header { get; set; }
  26. internal override string FilePartName => Header.Name;
  27. internal override Stream GetCompressedStream()
  28. {
  29. if (!Header.HasData)
  30. {
  31. return Stream.Null;
  32. }
  33. Stream decompressionStream = CreateDecompressionStream(GetCryptoStream(CreateBaseStream()), Header.CompressionMethod);
  34. if (LeaveStreamOpen)
  35. {
  36. return new NonDisposingStream(decompressionStream);
  37. }
  38. return decompressionStream;
  39. }
  40. internal override Stream GetRawStream()
  41. {
  42. if (!Header.HasData)
  43. {
  44. return Stream.Null;
  45. }
  46. return CreateBaseStream();
  47. }
  48. protected abstract Stream CreateBaseStream();
  49. protected bool LeaveStreamOpen => FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor) || Header.IsZip64;
  50. protected Stream CreateDecompressionStream(Stream stream, ZipCompressionMethod method)
  51. {
  52. switch (method)
  53. {
  54. case ZipCompressionMethod.None:
  55. {
  56. return stream;
  57. }
  58. case ZipCompressionMethod.Deflate:
  59. {
  60. return new DeflateStream(stream, CompressionMode.Decompress);
  61. }
  62. case ZipCompressionMethod.Deflate64:
  63. {
  64. return new Deflate64Stream(stream, CompressionMode.Decompress);
  65. }
  66. case ZipCompressionMethod.BZip2:
  67. {
  68. return new BZip2Stream(stream, CompressionMode.Decompress, false);
  69. }
  70. case ZipCompressionMethod.LZMA:
  71. {
  72. if (FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted))
  73. {
  74. throw new NotSupportedException("LZMA with pkware encryption.");
  75. }
  76. var reader = new BinaryReader(stream);
  77. reader.ReadUInt16(); //LZMA version
  78. var props = new byte[reader.ReadUInt16()];
  79. reader.Read(props, 0, props.Length);
  80. return new LzmaStream(props, stream,
  81. Header.CompressedSize > 0 ? Header.CompressedSize - 4 - props.Length : -1,
  82. FlagUtility.HasFlag(Header.Flags, HeaderFlags.Bit1)
  83. ? -1
  84. : (long)Header.UncompressedSize);
  85. }
  86. case ZipCompressionMethod.PPMd:
  87. {
  88. var props = new byte[2];
  89. stream.ReadFully(props);
  90. return new PpmdStream(new PpmdProperties(props), stream, false);
  91. }
  92. case ZipCompressionMethod.WinzipAes:
  93. {
  94. ExtraData data = Header.Extra.Where(x => x.Type == ExtraDataType.WinZipAes).SingleOrDefault();
  95. if (data == null)
  96. {
  97. throw new InvalidFormatException("No Winzip AES extra data found.");
  98. }
  99. if (data.Length != 7)
  100. {
  101. throw new InvalidFormatException("Winzip data length is not 7.");
  102. }
  103. ushort compressedMethod = DataConverter.LittleEndian.GetUInt16(data.DataBytes, 0);
  104. if (compressedMethod != 0x01 && compressedMethod != 0x02)
  105. {
  106. throw new InvalidFormatException("Unexpected vendor version number for WinZip AES metadata");
  107. }
  108. ushort vendorId = DataConverter.LittleEndian.GetUInt16(data.DataBytes, 2);
  109. if (vendorId != 0x4541)
  110. {
  111. throw new InvalidFormatException("Unexpected vendor ID for WinZip AES metadata");
  112. }
  113. return CreateDecompressionStream(stream, (ZipCompressionMethod)DataConverter.LittleEndian.GetUInt16(data.DataBytes, 5));
  114. }
  115. default:
  116. {
  117. throw new NotSupportedException("CompressionMethod: " + Header.CompressionMethod);
  118. }
  119. }
  120. }
  121. protected Stream GetCryptoStream(Stream plainStream)
  122. {
  123. bool isFileEncrypted = FlagUtility.HasFlag(Header.Flags, HeaderFlags.Encrypted);
  124. if (Header.CompressedSize == 0 && isFileEncrypted)
  125. {
  126. throw new NotSupportedException("Cannot encrypt file with unknown size at start.");
  127. }
  128. if ((Header.CompressedSize == 0
  129. && FlagUtility.HasFlag(Header.Flags, HeaderFlags.UsePostDataDescriptor))
  130. || Header.IsZip64)
  131. {
  132. plainStream = new NonDisposingStream(plainStream); //make sure AES doesn't close
  133. }
  134. else
  135. {
  136. plainStream = new ReadOnlySubStream(plainStream, Header.CompressedSize); //make sure AES doesn't close
  137. }
  138. if (isFileEncrypted)
  139. {
  140. switch (Header.CompressionMethod)
  141. {
  142. case ZipCompressionMethod.None:
  143. case ZipCompressionMethod.Deflate:
  144. case ZipCompressionMethod.Deflate64:
  145. case ZipCompressionMethod.BZip2:
  146. case ZipCompressionMethod.LZMA:
  147. case ZipCompressionMethod.PPMd:
  148. {
  149. return new PkwareTraditionalCryptoStream(plainStream, Header.ComposeEncryptionData(plainStream), CryptoMode.Decrypt);
  150. }
  151. case ZipCompressionMethod.WinzipAes:
  152. {
  153. #if !NO_FILE
  154. if (Header.WinzipAesEncryptionData != null)
  155. {
  156. return new WinzipAesCryptoStream(plainStream, Header.WinzipAesEncryptionData, Header.CompressedSize - 10);
  157. }
  158. #endif
  159. return plainStream;
  160. }
  161. default:
  162. {
  163. throw new ArgumentOutOfRangeException();
  164. }
  165. }
  166. }
  167. return plainStream;
  168. }
  169. }
  170. }