InflaterInputStream.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. using System;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. namespace ICSharpCode.SharpZipLib.Zip.Compression.Streams
  5. {
  6. /// <summary>
  7. /// An input buffer customised for use by <see cref="InflaterInputStream"/>
  8. /// </summary>
  9. /// <remarks>
  10. /// The buffer supports decryption of incoming data.
  11. /// </remarks>
  12. public class InflaterInputBuffer
  13. {
  14. #region Constructors
  15. /// <summary>
  16. /// Initialise a new instance of <see cref="InflaterInputBuffer"/> with a default buffer size
  17. /// </summary>
  18. /// <param name="stream">The stream to buffer.</param>
  19. public InflaterInputBuffer(Stream stream) : this(stream, 4096)
  20. {
  21. }
  22. /// <summary>
  23. /// Initialise a new instance of <see cref="InflaterInputBuffer"/>
  24. /// </summary>
  25. /// <param name="stream">The stream to buffer.</param>
  26. /// <param name="bufferSize">The size to use for the buffer</param>
  27. /// <remarks>A minimum buffer size of 1KB is permitted. Lower sizes are treated as 1KB.</remarks>
  28. public InflaterInputBuffer(Stream stream, int bufferSize)
  29. {
  30. inputStream = stream;
  31. if (bufferSize < 1024) {
  32. bufferSize = 1024;
  33. }
  34. rawData = new byte[bufferSize];
  35. clearText = rawData;
  36. }
  37. #endregion
  38. /// <summary>
  39. /// Get the length of bytes bytes in the <see cref="RawData"/>
  40. /// </summary>
  41. public int RawLength {
  42. get {
  43. return rawLength;
  44. }
  45. }
  46. /// <summary>
  47. /// Get the contents of the raw data buffer.
  48. /// </summary>
  49. /// <remarks>This may contain encrypted data.</remarks>
  50. public byte[] RawData {
  51. get {
  52. return rawData;
  53. }
  54. }
  55. /// <summary>
  56. /// Get the number of useable bytes in <see cref="ClearText"/>
  57. /// </summary>
  58. public int ClearTextLength {
  59. get {
  60. return clearTextLength;
  61. }
  62. }
  63. /// <summary>
  64. /// Get the contents of the clear text buffer.
  65. /// </summary>
  66. public byte[] ClearText {
  67. get {
  68. return clearText;
  69. }
  70. }
  71. /// <summary>
  72. /// Get/set the number of bytes available
  73. /// </summary>
  74. public int Available {
  75. get { return available; }
  76. set { available = value; }
  77. }
  78. /// <summary>
  79. /// Call <see cref="Inflater.SetInput(byte[], int, int)"/> passing the current clear text buffer contents.
  80. /// </summary>
  81. /// <param name="inflater">The inflater to set input for.</param>
  82. public void SetInflaterInput(Inflater inflater)
  83. {
  84. if (available > 0) {
  85. inflater.SetInput(clearText, clearTextLength - available, available);
  86. available = 0;
  87. }
  88. }
  89. /// <summary>
  90. /// Fill the buffer from the underlying input stream.
  91. /// </summary>
  92. public void Fill()
  93. {
  94. rawLength = 0;
  95. int toRead = rawData.Length;
  96. while (toRead > 0) {
  97. int count = inputStream.Read(rawData, rawLength, toRead);
  98. if (count <= 0) {
  99. break;
  100. }
  101. rawLength += count;
  102. toRead -= count;
  103. }
  104. if (cryptoTransform != null) {
  105. clearTextLength = cryptoTransform.TransformBlock(rawData, 0, rawLength, clearText, 0);
  106. } else {
  107. clearTextLength = rawLength;
  108. }
  109. available = clearTextLength;
  110. }
  111. /// <summary>
  112. /// Read a buffer directly from the input stream
  113. /// </summary>
  114. /// <param name="buffer">The buffer to fill</param>
  115. /// <returns>Returns the number of bytes read.</returns>
  116. public int ReadRawBuffer(byte[] buffer)
  117. {
  118. return ReadRawBuffer(buffer, 0, buffer.Length);
  119. }
  120. /// <summary>
  121. /// Read a buffer directly from the input stream
  122. /// </summary>
  123. /// <param name="outBuffer">The buffer to read into</param>
  124. /// <param name="offset">The offset to start reading data into.</param>
  125. /// <param name="length">The number of bytes to read.</param>
  126. /// <returns>Returns the number of bytes read.</returns>
  127. public int ReadRawBuffer(byte[] outBuffer, int offset, int length)
  128. {
  129. if (length < 0) {
  130. throw new ArgumentOutOfRangeException("nameof(length)");
  131. }
  132. int currentOffset = offset;
  133. int currentLength = length;
  134. while (currentLength > 0) {
  135. if (available <= 0) {
  136. Fill();
  137. if (available <= 0) {
  138. return 0;
  139. }
  140. }
  141. int toCopy = Math.Min(currentLength, available);
  142. System.Array.Copy(rawData, rawLength - (int)available, outBuffer, currentOffset, toCopy);
  143. currentOffset += toCopy;
  144. currentLength -= toCopy;
  145. available -= toCopy;
  146. }
  147. return length;
  148. }
  149. /// <summary>
  150. /// Read clear text data from the input stream.
  151. /// </summary>
  152. /// <param name="outBuffer">The buffer to add data to.</param>
  153. /// <param name="offset">The offset to start adding data at.</param>
  154. /// <param name="length">The number of bytes to read.</param>
  155. /// <returns>Returns the number of bytes actually read.</returns>
  156. public int ReadClearTextBuffer(byte[] outBuffer, int offset, int length)
  157. {
  158. if (length < 0) {
  159. throw new ArgumentOutOfRangeException("nameof(length)");
  160. }
  161. int currentOffset = offset;
  162. int currentLength = length;
  163. while (currentLength > 0) {
  164. if (available <= 0) {
  165. Fill();
  166. if (available <= 0) {
  167. return 0;
  168. }
  169. }
  170. int toCopy = Math.Min(currentLength, available);
  171. Array.Copy(clearText, clearTextLength - (int)available, outBuffer, currentOffset, toCopy);
  172. currentOffset += toCopy;
  173. currentLength -= toCopy;
  174. available -= toCopy;
  175. }
  176. return length;
  177. }
  178. /// <summary>
  179. /// Read a <see cref="byte"/> from the input stream.
  180. /// </summary>
  181. /// <returns>Returns the byte read.</returns>
  182. public int ReadLeByte()
  183. {
  184. if (available <= 0) {
  185. Fill();
  186. if (available <= 0) {
  187. throw new ZipException("EOF in header");
  188. }
  189. }
  190. byte result = rawData[rawLength - available];
  191. available -= 1;
  192. return result;
  193. }
  194. /// <summary>
  195. /// Read an <see cref="short"/> in little endian byte order.
  196. /// </summary>
  197. /// <returns>The short value read case to an int.</returns>
  198. public int ReadLeShort()
  199. {
  200. return ReadLeByte() | (ReadLeByte() << 8);
  201. }
  202. /// <summary>
  203. /// Read an <see cref="int"/> in little endian byte order.
  204. /// </summary>
  205. /// <returns>The int value read.</returns>
  206. public int ReadLeInt()
  207. {
  208. return ReadLeShort() | (ReadLeShort() << 16);
  209. }
  210. /// <summary>
  211. /// Read a <see cref="long"/> in little endian byte order.
  212. /// </summary>
  213. /// <returns>The long value read.</returns>
  214. public long ReadLeLong()
  215. {
  216. return (uint)ReadLeInt() | ((long)ReadLeInt() << 32);
  217. }
  218. /// <summary>
  219. /// Get/set the <see cref="ICryptoTransform"/> to apply to any data.
  220. /// </summary>
  221. /// <remarks>Set this value to null to have no transform applied.</remarks>
  222. public ICryptoTransform CryptoTransform {
  223. set {
  224. cryptoTransform = value;
  225. if (cryptoTransform != null) {
  226. if (rawData == clearText) {
  227. if (internalClearText == null) {
  228. internalClearText = new byte[rawData.Length];
  229. }
  230. clearText = internalClearText;
  231. }
  232. clearTextLength = rawLength;
  233. if (available > 0) {
  234. cryptoTransform.TransformBlock(rawData, rawLength - available, available, clearText, rawLength - available);
  235. }
  236. } else {
  237. clearText = rawData;
  238. clearTextLength = rawLength;
  239. }
  240. }
  241. }
  242. #region Instance Fields
  243. int rawLength;
  244. byte[] rawData;
  245. int clearTextLength;
  246. byte[] clearText;
  247. byte[] internalClearText;
  248. int available;
  249. ICryptoTransform cryptoTransform;
  250. Stream inputStream;
  251. #endregion
  252. }
  253. /// <summary>
  254. /// This filter stream is used to decompress data compressed using the "deflate"
  255. /// format. The "deflate" format is described in RFC 1951.
  256. ///
  257. /// This stream may form the basis for other decompression filters, such
  258. /// as the <see cref="ICSharpCode.SharpZipLib.GZip.GZipInputStream">GZipInputStream</see>.
  259. ///
  260. /// Author of the original java version : John Leuner.
  261. /// </summary>
  262. public class InflaterInputStream : Stream
  263. {
  264. #region Constructors
  265. /// <summary>
  266. /// Create an InflaterInputStream with the default decompressor
  267. /// and a default buffer size of 4KB.
  268. /// </summary>
  269. /// <param name = "baseInputStream">
  270. /// The InputStream to read bytes from
  271. /// </param>
  272. public InflaterInputStream(Stream baseInputStream)
  273. : this(baseInputStream, new Inflater(), 4096)
  274. {
  275. }
  276. /// <summary>
  277. /// Create an InflaterInputStream with the specified decompressor
  278. /// and a default buffer size of 4KB.
  279. /// </summary>
  280. /// <param name = "baseInputStream">
  281. /// The source of input data
  282. /// </param>
  283. /// <param name = "inf">
  284. /// The decompressor used to decompress data read from baseInputStream
  285. /// </param>
  286. public InflaterInputStream(Stream baseInputStream, Inflater inf)
  287. : this(baseInputStream, inf, 4096)
  288. {
  289. }
  290. /// <summary>
  291. /// Create an InflaterInputStream with the specified decompressor
  292. /// and the specified buffer size.
  293. /// </summary>
  294. /// <param name = "baseInputStream">
  295. /// The InputStream to read bytes from
  296. /// </param>
  297. /// <param name = "inflater">
  298. /// The decompressor to use
  299. /// </param>
  300. /// <param name = "bufferSize">
  301. /// Size of the buffer to use
  302. /// </param>
  303. public InflaterInputStream(Stream baseInputStream, Inflater inflater, int bufferSize)
  304. {
  305. if (baseInputStream == null) {
  306. throw new ArgumentNullException("nameof(baseInputStream)");
  307. }
  308. if (inflater == null) {
  309. throw new ArgumentNullException("nameof(inflater)");
  310. }
  311. if (bufferSize <= 0) {
  312. throw new ArgumentOutOfRangeException("nameof(bufferSize)");
  313. }
  314. this.baseInputStream = baseInputStream;
  315. this.inf = inflater;
  316. inputBuffer = new InflaterInputBuffer(baseInputStream, bufferSize);
  317. }
  318. #endregion
  319. /// <summary>
  320. /// Gets or sets a flag indicating ownership of underlying stream.
  321. /// When the flag is true <see cref="Stream.Dispose()" /> will close the underlying stream also.
  322. /// </summary>
  323. /// <remarks>The default value is true.</remarks>
  324. private bool isStreamOwner = true;
  325. public bool IsStreamOwner
  326. {
  327. get { return isStreamOwner;}
  328. set { isStreamOwner = value; }
  329. }
  330. /// <summary>
  331. /// Skip specified number of bytes of uncompressed data
  332. /// </summary>
  333. /// <param name ="count">
  334. /// Number of bytes to skip
  335. /// </param>
  336. /// <returns>
  337. /// The number of bytes skipped, zero if the end of
  338. /// stream has been reached
  339. /// </returns>
  340. /// <exception cref="ArgumentOutOfRangeException">
  341. /// <paramref name="count">The number of bytes</paramref> to skip is less than or equal to zero.
  342. /// </exception>
  343. public long Skip(long count)
  344. {
  345. if (count <= 0) {
  346. throw new ArgumentOutOfRangeException("nameof(count)");
  347. }
  348. // v0.80 Skip by seeking if underlying stream supports it...
  349. if (baseInputStream.CanSeek) {
  350. baseInputStream.Seek(count, SeekOrigin.Current);
  351. return count;
  352. } else {
  353. int length = 2048;
  354. if (count < length) {
  355. length = (int)count;
  356. }
  357. byte[] tmp = new byte[length];
  358. int readCount = 1;
  359. long toSkip = count;
  360. while ((toSkip > 0) && (readCount > 0)) {
  361. if (toSkip < length) {
  362. length = (int)toSkip;
  363. }
  364. readCount = baseInputStream.Read(tmp, 0, length);
  365. toSkip -= readCount;
  366. }
  367. return count - toSkip;
  368. }
  369. }
  370. /// <summary>
  371. /// Clear any cryptographic state.
  372. /// </summary>
  373. protected void StopDecrypting()
  374. {
  375. inputBuffer.CryptoTransform = null;
  376. }
  377. /// <summary>
  378. /// Returns 0 once the end of the stream (EOF) has been reached.
  379. /// Otherwise returns 1.
  380. /// </summary>
  381. public virtual int Available {
  382. get {
  383. return inf.IsFinished ? 0 : 1;
  384. }
  385. }
  386. /// <summary>
  387. /// Fills the buffer with more data to decompress.
  388. /// </summary>
  389. /// <exception cref="SharpZipBaseException">
  390. /// Stream ends early
  391. /// </exception>
  392. protected void Fill()
  393. {
  394. // Protect against redundant calls
  395. if (inputBuffer.Available <= 0) {
  396. inputBuffer.Fill();
  397. if (inputBuffer.Available <= 0) {
  398. throw new SharpZipBaseException("Unexpected EOF");
  399. }
  400. }
  401. inputBuffer.SetInflaterInput(inf);
  402. }
  403. #region Stream Overrides
  404. /// <summary>
  405. /// Gets a value indicating whether the current stream supports reading
  406. /// </summary>
  407. public override bool CanRead {
  408. get {
  409. return baseInputStream.CanRead;
  410. }
  411. }
  412. /// <summary>
  413. /// Gets a value of false indicating seeking is not supported for this stream.
  414. /// </summary>
  415. public override bool CanSeek {
  416. get {
  417. return false;
  418. }
  419. }
  420. /// <summary>
  421. /// Gets a value of false indicating that this stream is not writeable.
  422. /// </summary>
  423. public override bool CanWrite {
  424. get {
  425. return false;
  426. }
  427. }
  428. /// <summary>
  429. /// A value representing the length of the stream in bytes.
  430. /// </summary>
  431. public override long Length {
  432. get {
  433. //return inputBuffer.RawLength;
  434. throw new NotSupportedException("InflaterInputStream Length is not supported");
  435. }
  436. }
  437. /// <summary>
  438. /// The current position within the stream.
  439. /// Throws a NotSupportedException when attempting to set the position
  440. /// </summary>
  441. /// <exception cref="NotSupportedException">Attempting to set the position</exception>
  442. public override long Position {
  443. get {
  444. return baseInputStream.Position;
  445. }
  446. set {
  447. throw new NotSupportedException("InflaterInputStream Position not supported");
  448. }
  449. }
  450. /// <summary>
  451. /// Flushes the baseInputStream
  452. /// </summary>
  453. public override void Flush()
  454. {
  455. baseInputStream.Flush();
  456. }
  457. /// <summary>
  458. /// Sets the position within the current stream
  459. /// Always throws a NotSupportedException
  460. /// </summary>
  461. /// <param name="offset">The relative offset to seek to.</param>
  462. /// <param name="origin">The <see cref="SeekOrigin"/> defining where to seek from.</param>
  463. /// <returns>The new position in the stream.</returns>
  464. /// <exception cref="NotSupportedException">Any access</exception>
  465. public override long Seek(long offset, SeekOrigin origin)
  466. {
  467. throw new NotSupportedException("Seek not supported");
  468. }
  469. /// <summary>
  470. /// Set the length of the current stream
  471. /// Always throws a NotSupportedException
  472. /// </summary>
  473. /// <param name="value">The new length value for the stream.</param>
  474. /// <exception cref="NotSupportedException">Any access</exception>
  475. public override void SetLength(long value)
  476. {
  477. throw new NotSupportedException("InflaterInputStream SetLength not supported");
  478. }
  479. /// <summary>
  480. /// Writes a sequence of bytes to stream and advances the current position
  481. /// This method always throws a NotSupportedException
  482. /// </summary>
  483. /// <param name="buffer">Thew buffer containing data to write.</param>
  484. /// <param name="offset">The offset of the first byte to write.</param>
  485. /// <param name="count">The number of bytes to write.</param>
  486. /// <exception cref="NotSupportedException">Any access</exception>
  487. public override void Write(byte[] buffer, int offset, int count)
  488. {
  489. throw new NotSupportedException("InflaterInputStream Write not supported");
  490. }
  491. /// <summary>
  492. /// Writes one byte to the current stream and advances the current position
  493. /// Always throws a NotSupportedException
  494. /// </summary>
  495. /// <param name="value">The byte to write.</param>
  496. /// <exception cref="NotSupportedException">Any access</exception>
  497. public override void WriteByte(byte value)
  498. {
  499. throw new NotSupportedException("InflaterInputStream WriteByte not supported");
  500. }
  501. /// <summary>
  502. /// Closes the input stream. When <see cref="IsStreamOwner"></see>
  503. /// is true the underlying stream is also closed.
  504. /// </summary>
  505. protected override void Dispose(bool disposing)
  506. {
  507. if (!isClosed) {
  508. isClosed = true;
  509. if (IsStreamOwner) {
  510. baseInputStream.Dispose();
  511. }
  512. }
  513. }
  514. /// <summary>
  515. /// Reads decompressed data into the provided buffer byte array
  516. /// </summary>
  517. /// <param name ="buffer">
  518. /// The array to read and decompress data into
  519. /// </param>
  520. /// <param name ="offset">
  521. /// The offset indicating where the data should be placed
  522. /// </param>
  523. /// <param name ="count">
  524. /// The number of bytes to decompress
  525. /// </param>
  526. /// <returns>The number of bytes read. Zero signals the end of stream</returns>
  527. /// <exception cref="SharpZipBaseException">
  528. /// Inflater needs a dictionary
  529. /// </exception>
  530. public override int Read(byte[] buffer, int offset, int count)
  531. {
  532. if (inf.IsNeedingDictionary) {
  533. throw new SharpZipBaseException("Need a dictionary");
  534. }
  535. int remainingBytes = count;
  536. while (true) {
  537. int bytesRead = inf.Inflate(buffer, offset, remainingBytes);
  538. offset += bytesRead;
  539. remainingBytes -= bytesRead;
  540. if (remainingBytes == 0 || inf.IsFinished) {
  541. break;
  542. }
  543. if (inf.IsNeedingInput) {
  544. Fill();
  545. } else if (bytesRead == 0) {
  546. throw new ZipException("Dont know what to do");
  547. }
  548. }
  549. return count - remainingBytes;
  550. }
  551. #endregion
  552. #region Instance Fields
  553. /// <summary>
  554. /// Decompressor for this stream
  555. /// </summary>
  556. protected Inflater inf;
  557. /// <summary>
  558. /// <see cref="InflaterInputBuffer">Input buffer</see> for this stream.
  559. /// </summary>
  560. protected InflaterInputBuffer inputBuffer;
  561. /// <summary>
  562. /// Base stream the inflater reads from.
  563. /// </summary>
  564. private Stream baseInputStream;
  565. /// <summary>
  566. /// The compressed size
  567. /// </summary>
  568. protected long csize;
  569. /// <summary>
  570. /// Flag indicating wether this instance has been closed or not.
  571. /// </summary>
  572. bool isClosed;
  573. #endregion
  574. }
  575. }