ZipInputStream.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // ZipInputStream.cs
  2. // Copyright (C) 2001 Mike Krueger
  3. //
  4. // This file was translated from java, it was part of the GNU Classpath
  5. // Copyright (C) 2001 Free Software Foundation, Inc.
  6. //
  7. // This program is free software; you can redistribute it and/or
  8. // modify it under the terms of the GNU General Public License
  9. // as published by the Free Software Foundation; either version 2
  10. // of the License, or (at your option) any later version.
  11. //
  12. // This program is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public License
  18. // along with this program; if not, write to the Free Software
  19. // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. //
  21. // Linking this library statically or dynamically with other modules is
  22. // making a combined work based on this library. Thus, the terms and
  23. // conditions of the GNU General Public License cover the whole
  24. // combination.
  25. //
  26. // As a special exception, the copyright holders of this library give you
  27. // permission to link this library with independent modules to produce an
  28. // executable, regardless of the license terms of these independent
  29. // modules, and to copy and distribute the resulting executable under
  30. // terms of your choice, provided that you also meet, for each linked
  31. // independent module, the terms and conditions of the license of that
  32. // module. An independent module is a module which is not derived from
  33. // or based on this library. If you modify this library, you may extend
  34. // this exception to your version of the library, but you are not
  35. // obligated to do so. If you do not wish to do so, delete this
  36. // exception statement from your version.
  37. using System;
  38. using System.Text;
  39. using System.IO;
  40. using ICSharpCode.SharpZipLib.Checksums;
  41. using ICSharpCode.SharpZipLib.Zip.Compression;
  42. using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
  43. namespace ICSharpCode.SharpZipLib.Zip
  44. {
  45. /// <summary>
  46. /// This is a FilterInputStream that reads the files baseInputStream an zip archive
  47. /// one after another. It has a special method to get the zip entry of
  48. /// the next file. The zip entry contains information about the file name
  49. /// size, compressed size, CRC, etc.
  50. /// It includes support for STORED and DEFLATED entries.
  51. ///
  52. /// author of the original java version : Jochen Hoenicke
  53. /// </summary>
  54. /// <example> This sample shows how to read a zip file
  55. /// <code lang="C#">
  56. /// using System;
  57. /// using System.Text;
  58. /// using System.IO;
  59. ///
  60. /// using NZlib.Zip;
  61. ///
  62. /// class MainClass
  63. /// {
  64. /// public static void Main(string[] args)
  65. /// {
  66. /// ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]));
  67. ///
  68. /// ZipEntry theEntry;
  69. /// while ((theEntry = s.GetNextEntry()) != null) {
  70. /// int size = 2048;
  71. /// byte[] data = new byte[2048];
  72. ///
  73. /// Console.Write("Show contents (y/n) ?");
  74. /// if (Console.ReadLine() == "y") {
  75. /// while (true) {
  76. /// size = s.Read(data, 0, data.Length);
  77. /// if (size > 0) {
  78. /// Console.Write(new ASCIIEncoding().GetString(data, 0, size));
  79. /// } else {
  80. /// break;
  81. /// }
  82. /// }
  83. /// }
  84. /// }
  85. /// s.Close();
  86. /// }
  87. /// }
  88. /// </code>
  89. /// </example>
  90. public class ZipInputStream : InflaterInputStream
  91. {
  92. Crc32 crc = new Crc32();
  93. ZipEntry entry = null;
  94. long size;
  95. int method;
  96. int flags;
  97. long avail;
  98. string password = null;
  99. public string Password {
  100. get {
  101. return password;
  102. }
  103. set {
  104. password = value;
  105. }
  106. }
  107. /// <summary>
  108. /// Creates a new Zip input stream, reading a zip archive.
  109. /// </summary>
  110. public ZipInputStream(Stream baseInputStream) : base(baseInputStream, new Inflater(true))
  111. {
  112. }
  113. void FillBuf()
  114. {
  115. avail = len = baseInputStream.Read(buf, 0, buf.Length);
  116. }
  117. int ReadBuf(byte[] outBuf, int offset, int length)
  118. {
  119. if (avail <= 0) {
  120. FillBuf();
  121. if (avail <= 0) {
  122. return 0;
  123. }
  124. }
  125. if (length > avail) {
  126. length = (int)avail;
  127. }
  128. System.Array.Copy(buf, len - (int)avail, outBuf, offset, length);
  129. avail -= length;
  130. return length;
  131. }
  132. void ReadFully(byte[] outBuf)
  133. {
  134. int off = 0;
  135. int len = outBuf.Length;
  136. while (len > 0) {
  137. int count = ReadBuf(outBuf, off, len);
  138. if (count == -1) {
  139. throw new Exception();
  140. }
  141. off += count;
  142. len -= count;
  143. }
  144. }
  145. int ReadLeByte()
  146. {
  147. if (avail <= 0) {
  148. FillBuf();
  149. if (avail <= 0) {
  150. throw new ZipException("EOF in header");
  151. }
  152. }
  153. return buf[len - avail--] & 0xff;
  154. }
  155. /// <summary>
  156. /// Read an unsigned short baseInputStream little endian byte order.
  157. /// </summary>
  158. int ReadLeShort()
  159. {
  160. return ReadLeByte() | (ReadLeByte() << 8);
  161. }
  162. /// <summary>
  163. /// Read an int baseInputStream little endian byte order.
  164. /// </summary>
  165. int ReadLeInt()
  166. {
  167. return ReadLeShort() | (ReadLeShort() << 16);
  168. }
  169. /// <summary>
  170. /// Read an int baseInputStream little endian byte order.
  171. /// </summary>
  172. long ReadLeLong()
  173. {
  174. return ReadLeInt() | (ReadLeInt() << 32);
  175. }
  176. /// <summary>
  177. /// Open the next entry from the zip archive, and return its description.
  178. /// If the previous entry wasn't closed, this method will close it.
  179. /// </summary>
  180. public ZipEntry GetNextEntry()
  181. {
  182. if (crc == null) {
  183. throw new InvalidOperationException("Closed.");
  184. }
  185. if (entry != null) {
  186. CloseEntry();
  187. }
  188. if (this.cryptbuffer != null) {
  189. if (avail == 0 && inf.RemainingInput != 0) {
  190. avail = inf.RemainingInput - 16;
  191. inf.Reset();
  192. }
  193. baseInputStream.Position -= this.len;
  194. baseInputStream.Read(this.buf, 0, this.len);
  195. }
  196. int header = ReadLeInt();
  197. // -jr- added end sig for empty zip files, Zip64 end sig and digital sig for files that have them...
  198. if (header == ZipConstants.CENSIG ||
  199. header == ZipConstants.ENDSIG ||
  200. header == ZipConstants.CENDIGITALSIG ||
  201. header == ZipConstants.CENSIG64) {
  202. // Central Header reached or end of empty zip file
  203. Close();
  204. return null;
  205. }
  206. // -jr- 07-Dec-2003 ignore spanning temporary signatures if found
  207. // SPANNINGSIG is same as descriptor signature and is untested as yet.
  208. if (header == ZipConstants.SPANTEMPSIG || header == ZipConstants.SPANNINGSIG) {
  209. header = ReadLeInt();
  210. }
  211. if (header != ZipConstants.LOCSIG) {
  212. throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header));
  213. }
  214. short version = (short)ReadLeShort();
  215. flags = ReadLeShort();
  216. method = ReadLeShort();
  217. uint dostime = (uint)ReadLeInt();
  218. int crc2 = ReadLeInt();
  219. csize = ReadLeInt();
  220. size = ReadLeInt();
  221. int nameLen = ReadLeShort();
  222. int extraLen = ReadLeShort();
  223. bool isCrypted = (flags & 1) == 1;
  224. if (method == ZipOutputStream.STORED && (!isCrypted && csize != size || (isCrypted && csize - 12 != size))) {
  225. throw new ZipException("Stored, but compressed != uncompressed");
  226. }
  227. byte[] buffer = new byte[nameLen];
  228. ReadFully(buffer);
  229. string name = ZipConstants.ConvertToString(buffer);
  230. entry = new ZipEntry(name);
  231. entry.IsCrypted = isCrypted;
  232. entry.Version = (ushort)version;
  233. if (method != 0 && method != 8) {
  234. throw new ZipException("unknown compression method " + method);
  235. }
  236. entry.CompressionMethod = (CompressionMethod)method;
  237. if ((flags & 8) == 0) {
  238. entry.Crc = crc2 & 0xFFFFFFFFL;
  239. entry.Size = size & 0xFFFFFFFFL;
  240. entry.CompressedSize = csize & 0xFFFFFFFFL;
  241. }
  242. entry.DosTime = dostime;
  243. if (extraLen > 0) {
  244. byte[] extra = new byte[extraLen];
  245. ReadFully(extra);
  246. entry.ExtraData = extra;
  247. }
  248. // test for encryption
  249. if (isCrypted) {
  250. if (password == null) {
  251. throw new ZipException("No password set.");
  252. }
  253. InitializePassword(password);
  254. cryptbuffer = new byte[12];
  255. ReadFully(cryptbuffer);
  256. DecryptBlock(cryptbuffer, 0, cryptbuffer.Length);
  257. if ((flags & 8) == 0) {// -jr- 10-Feb-2004 Dont yet know correct size here....
  258. csize -= 12;
  259. }
  260. } else {
  261. cryptbuffer = null;
  262. }
  263. if (method == ZipOutputStream.DEFLATED && avail > 0) {
  264. System.Array.Copy(buf, len - (int)avail, buf, 0, (int)avail);
  265. len = (int)avail;
  266. avail = 0;
  267. if (isCrypted) {
  268. DecryptBlock(buf, 0, Math.Min((int)csize, len));
  269. }
  270. inf.SetInput(buf, 0, len);
  271. }
  272. return entry;
  273. }
  274. private void ReadDataDescr()
  275. {
  276. if (ReadLeInt() != ZipConstants.EXTSIG) {
  277. throw new ZipException("Data descriptor signature not found");
  278. }
  279. entry.Crc = ReadLeInt() & 0xFFFFFFFFL;
  280. csize = ReadLeInt();
  281. size = ReadLeInt();
  282. entry.Size = size & 0xFFFFFFFFL;
  283. entry.CompressedSize = csize & 0xFFFFFFFFL;
  284. }
  285. /// <summary>
  286. /// Closes the current zip entry and moves to the next one.
  287. /// </summary>
  288. public void CloseEntry()
  289. {
  290. if (crc == null) {
  291. throw new InvalidOperationException("Closed.");
  292. }
  293. if (entry == null) {
  294. return;
  295. }
  296. if (method == ZipOutputStream.DEFLATED) {
  297. if ((flags & 8) != 0) {
  298. /* We don't know how much we must skip, read until end. */
  299. byte[] tmp = new byte[2048];
  300. while (Read(tmp, 0, tmp.Length) > 0)
  301. ;
  302. /* read will close this entry */
  303. return;
  304. }
  305. csize -= inf.TotalIn;
  306. avail = inf.RemainingInput;
  307. }
  308. if (avail > csize && csize >= 0) {
  309. avail -= csize;
  310. } else {
  311. csize -= avail;
  312. avail = 0;
  313. while (csize != 0) {
  314. int skipped = (int)base.Skip(csize & 0xFFFFFFFFL);
  315. if (skipped <= 0) {
  316. throw new ZipException("zip archive ends early.");
  317. }
  318. csize -= skipped;
  319. }
  320. }
  321. size = 0;
  322. crc.Reset();
  323. if (method == ZipOutputStream.DEFLATED) {
  324. inf.Reset();
  325. }
  326. entry = null;
  327. }
  328. public override int Available {
  329. get {
  330. return entry != null ? 1 : 0;
  331. }
  332. }
  333. /// <summary>
  334. /// Reads a byte from the current zip entry.
  335. /// </summary>
  336. /// <returns>
  337. /// the byte or -1 on EOF.
  338. /// </returns>
  339. /// <exception name="System.IO.IOException">
  340. /// IOException if a i/o error occured.
  341. /// </exception>
  342. /// <exception name="ICSharpCode.SharpZipLib.ZipException">
  343. /// ZipException if the deflated stream is corrupted.
  344. /// </exception>
  345. public override int ReadByte()
  346. {
  347. byte[] b = new byte[1];
  348. if (Read(b, 0, 1) <= 0) {
  349. return -1; // ok
  350. }
  351. return b[0] & 0xff;
  352. }
  353. /// <summary>
  354. /// Reads a block of bytes from the current zip entry.
  355. /// </summary>
  356. /// <returns>
  357. /// the number of bytes read (may be smaller, even before EOF), or -1 on EOF.
  358. /// </returns>
  359. /// <exception name="Exception">
  360. /// IOException if a i/o error occured.
  361. /// ZipException if the deflated stream is corrupted.
  362. /// </exception>
  363. public override int Read(byte[] b, int off, int len)
  364. {
  365. if (crc == null) {
  366. throw new InvalidOperationException("Closed.");
  367. }
  368. if (entry == null) {
  369. return 0;
  370. }
  371. bool finished = false;
  372. switch (method) {
  373. case ZipOutputStream.DEFLATED:
  374. len = base.Read(b, off, len);
  375. if (len <= 0) { // TODO BUG1 -jr- Check this was < 0 but avail was not adjusted causing failure in later calls
  376. if (!inf.IsFinished) {
  377. throw new ZipException("Inflater not finished!?");
  378. }
  379. avail = inf.RemainingInput;
  380. // BUG1 -jr- With bit 3 set you dont yet know the size
  381. if ((flags & 8) == 0 && (inf.TotalIn != csize || inf.TotalOut != size)) {
  382. throw new ZipException("size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut);
  383. }
  384. inf.Reset();
  385. finished = true;
  386. }
  387. break;
  388. case ZipOutputStream.STORED:
  389. if (len > csize && csize >= 0) {
  390. len = (int)csize;
  391. }
  392. len = ReadBuf(b, off, len);
  393. if (len > 0) {
  394. csize -= len;
  395. size -= len;
  396. }
  397. if (csize == 0) {
  398. finished = true;
  399. } else {
  400. if (len < 0) {
  401. throw new ZipException("EOF in stored block");
  402. }
  403. }
  404. // decrypting crypted data
  405. if (cryptbuffer != null) {
  406. DecryptBlock(b, off, len);
  407. }
  408. break;
  409. }
  410. if (len > 0) {
  411. crc.Update(b, off, len);
  412. }
  413. if (finished) {
  414. if ((flags & 8) != 0) {
  415. ReadDataDescr();
  416. }
  417. if ((crc.Value & 0xFFFFFFFFL) != entry.Crc && entry.Crc != -1) {
  418. throw new ZipException("CRC mismatch");
  419. }
  420. crc.Reset();
  421. entry = null;
  422. }
  423. return len;
  424. }
  425. /// <summary>
  426. /// Closes the zip file.
  427. /// </summary>
  428. /// <exception name="Exception">
  429. /// if a i/o error occured.
  430. /// </exception>
  431. public override void Close()
  432. {
  433. base.Close();
  434. crc = null;
  435. entry = null;
  436. }
  437. }
  438. }