--- /dev/null
+// ZipInputStream.cs\r
+// Copyright (C) 2001 Mike Krueger\r
+//\r
+// This file was translated from java, it was part of the GNU Classpath\r
+// Copyright (C) 2001 Free Software Foundation, Inc.\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.\r
+//\r
+// Linking this library statically or dynamically with other modules is\r
+// making a combined work based on this library. Thus, the terms and\r
+// conditions of the GNU General Public License cover the whole\r
+// combination.\r
+// \r
+// As a special exception, the copyright holders of this library give you\r
+// permission to link this library with independent modules to produce an\r
+// executable, regardless of the license terms of these independent\r
+// modules, and to copy and distribute the resulting executable under\r
+// terms of your choice, provided that you also meet, for each linked\r
+// independent module, the terms and conditions of the license of that\r
+// module. An independent module is a module which is not derived from\r
+// or based on this library. If you modify this library, you may extend\r
+// this exception to your version of the library, but you are not\r
+// obligated to do so. If you do not wish to do so, delete this\r
+// exception statement from your version.\r
+\r
+using System;\r
+using System.Text;\r
+using System.IO;\r
+\r
+using ICSharpCode.SharpZipLib.Checksums;\r
+using ICSharpCode.SharpZipLib.Zip.Compression;\r
+using ICSharpCode.SharpZipLib.Zip.Compression.Streams;\r
+\r
+namespace ICSharpCode.SharpZipLib.Zip \r
+{\r
+ /// <summary>\r
+ /// This is a FilterInputStream that reads the files baseInputStream an zip archive\r
+ /// one after another. It has a special method to get the zip entry of\r
+ /// the next file. The zip entry contains information about the file name\r
+ /// size, compressed size, CRC, etc.\r
+ /// It includes support for STORED and DEFLATED entries.\r
+ /// \r
+ /// author of the original java version : Jochen Hoenicke\r
+ /// </summary>\r
+ /// <example> This sample shows how to read a zip file\r
+ /// <code lang="C#">\r
+ /// using System;\r
+ /// using System.Text;\r
+ /// using System.IO;\r
+ /// \r
+ /// using NZlib.Zip;\r
+ /// \r
+ /// class MainClass\r
+ /// {\r
+ /// public static void Main(string[] args)\r
+ /// {\r
+ /// ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]));\r
+ /// \r
+ /// ZipEntry theEntry;\r
+ /// while ((theEntry = s.GetNextEntry()) != null) {\r
+ /// int size = 2048;\r
+ /// byte[] data = new byte[2048];\r
+ /// \r
+ /// Console.Write("Show contents (y/n) ?");\r
+ /// if (Console.ReadLine() == "y") {\r
+ /// while (true) {\r
+ /// size = s.Read(data, 0, data.Length);\r
+ /// if (size > 0) {\r
+ /// Console.Write(new ASCIIEncoding().GetString(data, 0, size));\r
+ /// } else {\r
+ /// break;\r
+ /// }\r
+ /// }\r
+ /// }\r
+ /// }\r
+ /// s.Close();\r
+ /// }\r
+ /// } \r
+ /// </code>\r
+ /// </example>\r
+ public class ZipInputStream : InflaterInputStream\r
+ {\r
+ Crc32 crc = new Crc32();\r
+ ZipEntry entry = null;\r
+ \r
+ long size;\r
+ int method;\r
+ int flags;\r
+ long avail;\r
+ string password = null;\r
+ \r
+ public string Password {\r
+ get {\r
+ return password;\r
+ }\r
+ set {\r
+ password = value;\r
+ }\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Creates a new Zip input stream, reading a zip archive.\r
+ /// </summary>\r
+ public ZipInputStream(Stream baseInputStream) : base(baseInputStream, new Inflater(true))\r
+ {\r
+ }\r
+ \r
+ void FillBuf()\r
+ {\r
+ avail = len = baseInputStream.Read(buf, 0, buf.Length);\r
+ }\r
+ \r
+ int ReadBuf(byte[] outBuf, int offset, int length)\r
+ {\r
+ if (avail <= 0) {\r
+ FillBuf();\r
+ if (avail <= 0) {\r
+ return 0;\r
+ }\r
+ }\r
+ if (length > avail) {\r
+ length = (int)avail;\r
+ }\r
+ System.Array.Copy(buf, len - (int)avail, outBuf, offset, length);\r
+ avail -= length;\r
+ return length;\r
+ }\r
+ \r
+ void ReadFully(byte[] outBuf)\r
+ {\r
+ int off = 0;\r
+ int len = outBuf.Length;\r
+ while (len > 0) {\r
+ int count = ReadBuf(outBuf, off, len);\r
+ if (count == -1) {\r
+ throw new Exception(); \r
+ }\r
+ off += count;\r
+ len -= count;\r
+ }\r
+ }\r
+ \r
+ int ReadLeByte()\r
+ {\r
+ if (avail <= 0) {\r
+ FillBuf();\r
+ if (avail <= 0) {\r
+ throw new ZipException("EOF in header");\r
+ }\r
+ }\r
+ return buf[len - avail--] & 0xff;\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Read an unsigned short baseInputStream little endian byte order.\r
+ /// </summary>\r
+ int ReadLeShort()\r
+ {\r
+ return ReadLeByte() | (ReadLeByte() << 8);\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Read an int baseInputStream little endian byte order.\r
+ /// </summary>\r
+ int ReadLeInt()\r
+ {\r
+ return ReadLeShort() | (ReadLeShort() << 16);\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Read an int baseInputStream little endian byte order.\r
+ /// </summary>\r
+ long ReadLeLong()\r
+ {\r
+ return ReadLeInt() | (ReadLeInt() << 32);\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Open the next entry from the zip archive, and return its description.\r
+ /// If the previous entry wasn't closed, this method will close it.\r
+ /// </summary>\r
+ public ZipEntry GetNextEntry()\r
+ {\r
+ if (crc == null) {\r
+ throw new InvalidOperationException("Closed.");\r
+ }\r
+ if (entry != null) {\r
+ CloseEntry();\r
+ }\r
+ \r
+ if (this.cryptbuffer != null) {\r
+ if (avail == 0 && inf.RemainingInput != 0) {\r
+ avail = inf.RemainingInput - 16;\r
+ inf.Reset();\r
+ }\r
+ baseInputStream.Position -= this.len;\r
+ baseInputStream.Read(this.buf, 0, this.len);\r
+ }\r
+ \r
+ int header = ReadLeInt();\r
+ \r
+ // -jr- added end sig for empty zip files, Zip64 end sig and digital sig for files that have them...\r
+ if (header == ZipConstants.CENSIG || \r
+ header == ZipConstants.ENDSIG || \r
+ header == ZipConstants.CENDIGITALSIG || \r
+ header == ZipConstants.CENSIG64) {\r
+ // Central Header reached or end of empty zip file\r
+ Close();\r
+ return null;\r
+ }\r
+ // -jr- 07-Dec-2003 ignore spanning temporary signatures if found\r
+ // SPANNINGSIG is same as descriptor signature and is untested as yet.\r
+ if (header == ZipConstants.SPANTEMPSIG || header == ZipConstants.SPANNINGSIG) {\r
+ header = ReadLeInt();\r
+ }\r
+ \r
+ if (header != ZipConstants.LOCSIG) {\r
+ throw new ZipException("Wrong Local header signature: 0x" + String.Format("{0:X}", header));\r
+ }\r
+ \r
+ short version = (short)ReadLeShort();\r
+ \r
+ flags = ReadLeShort();\r
+ method = ReadLeShort();\r
+ uint dostime = (uint)ReadLeInt();\r
+ int crc2 = ReadLeInt();\r
+ csize = ReadLeInt();\r
+ size = ReadLeInt();\r
+ int nameLen = ReadLeShort();\r
+ int extraLen = ReadLeShort();\r
+ bool isCrypted = (flags & 1) == 1;\r
+ if (method == ZipOutputStream.STORED && (!isCrypted && csize != size || (isCrypted && csize - 12 != size))) {\r
+ throw new ZipException("Stored, but compressed != uncompressed");\r
+ }\r
+ \r
+ byte[] buffer = new byte[nameLen];\r
+ ReadFully(buffer);\r
+ \r
+ string name = ZipConstants.ConvertToString(buffer);\r
+ \r
+ entry = new ZipEntry(name);\r
+ entry.IsCrypted = isCrypted;\r
+ entry.Version = (ushort)version;\r
+ if (method != 0 && method != 8) {\r
+ throw new ZipException("unknown compression method " + method);\r
+ }\r
+ entry.CompressionMethod = (CompressionMethod)method;\r
+ \r
+ if ((flags & 8) == 0) {\r
+ entry.Crc = crc2 & 0xFFFFFFFFL;\r
+ entry.Size = size & 0xFFFFFFFFL;\r
+ entry.CompressedSize = csize & 0xFFFFFFFFL;\r
+ }\r
+ \r
+ entry.DosTime = dostime;\r
+ \r
+ if (extraLen > 0) {\r
+ byte[] extra = new byte[extraLen];\r
+ ReadFully(extra);\r
+ entry.ExtraData = extra;\r
+ }\r
+ \r
+ // test for encryption\r
+ if (isCrypted) {\r
+ if (password == null) {\r
+ throw new ZipException("No password set.");\r
+ }\r
+ InitializePassword(password);\r
+ cryptbuffer = new byte[12];\r
+ ReadFully(cryptbuffer);\r
+ DecryptBlock(cryptbuffer, 0, cryptbuffer.Length);\r
+ if ((flags & 8) == 0) {// -jr- 10-Feb-2004 Dont yet know correct size here....\r
+ csize -= 12;\r
+ }\r
+ } else {\r
+ cryptbuffer = null;\r
+ }\r
+ \r
+ if (method == ZipOutputStream.DEFLATED && avail > 0) {\r
+ System.Array.Copy(buf, len - (int)avail, buf, 0, (int)avail);\r
+ len = (int)avail;\r
+ avail = 0;\r
+ if (isCrypted) {\r
+ DecryptBlock(buf, 0, Math.Min((int)csize, len));\r
+ }\r
+ inf.SetInput(buf, 0, len);\r
+ }\r
+ \r
+ return entry;\r
+ }\r
+ private void ReadDataDescr()\r
+ {\r
+ if (ReadLeInt() != ZipConstants.EXTSIG) {\r
+ throw new ZipException("Data descriptor signature not found");\r
+ }\r
+ entry.Crc = ReadLeInt() & 0xFFFFFFFFL;\r
+ csize = ReadLeInt();\r
+ size = ReadLeInt();\r
+ entry.Size = size & 0xFFFFFFFFL;\r
+ entry.CompressedSize = csize & 0xFFFFFFFFL;\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Closes the current zip entry and moves to the next one.\r
+ /// </summary>\r
+ public void CloseEntry()\r
+ {\r
+ if (crc == null) {\r
+ throw new InvalidOperationException("Closed.");\r
+ }\r
+ \r
+ if (entry == null) {\r
+ return;\r
+ }\r
+ \r
+ if (method == ZipOutputStream.DEFLATED) {\r
+ if ((flags & 8) != 0) {\r
+ /* We don't know how much we must skip, read until end. */\r
+ byte[] tmp = new byte[2048];\r
+ while (Read(tmp, 0, tmp.Length) > 0)\r
+ ;\r
+ /* read will close this entry */\r
+ return;\r
+ }\r
+ csize -= inf.TotalIn;\r
+ avail = inf.RemainingInput;\r
+ }\r
+ if (avail > csize && csize >= 0) {\r
+ avail -= csize;\r
+ } else {\r
+ csize -= avail;\r
+ avail = 0;\r
+ while (csize != 0) {\r
+ int skipped = (int)base.Skip(csize & 0xFFFFFFFFL);\r
+ \r
+ if (skipped <= 0) {\r
+ throw new ZipException("zip archive ends early.");\r
+ }\r
+ \r
+ csize -= skipped;\r
+ }\r
+ }\r
+ \r
+ size = 0;\r
+ crc.Reset();\r
+ if (method == ZipOutputStream.DEFLATED) {\r
+ inf.Reset();\r
+ }\r
+ entry = null;\r
+ }\r
+ \r
+ public override int Available {\r
+ get {\r
+ return entry != null ? 1 : 0;\r
+ }\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Reads a byte from the current zip entry.\r
+ /// </summary>\r
+ /// <returns>\r
+ /// the byte or -1 on EOF.\r
+ /// </returns>\r
+ /// <exception name="System.IO.IOException">\r
+ /// IOException if a i/o error occured.\r
+ /// </exception>\r
+ /// <exception name="ICSharpCode.SharpZipLib.ZipException">\r
+ /// ZipException if the deflated stream is corrupted.\r
+ /// </exception>\r
+ public override int ReadByte()\r
+ {\r
+ byte[] b = new byte[1];\r
+ if (Read(b, 0, 1) <= 0) {\r
+ return -1; // ok\r
+ }\r
+ return b[0] & 0xff;\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Reads a block of bytes from the current zip entry.\r
+ /// </summary>\r
+ /// <returns>\r
+ /// the number of bytes read (may be smaller, even before EOF), or -1 on EOF.\r
+ /// </returns>\r
+ /// <exception name="Exception">\r
+ /// IOException if a i/o error occured.\r
+ /// ZipException if the deflated stream is corrupted.\r
+ /// </exception>\r
+ public override int Read(byte[] b, int off, int len)\r
+ {\r
+ if (crc == null) {\r
+ throw new InvalidOperationException("Closed.");\r
+ }\r
+ \r
+ if (entry == null) {\r
+ return 0;\r
+ }\r
+ bool finished = false;\r
+ \r
+ switch (method) {\r
+ case ZipOutputStream.DEFLATED:\r
+ len = base.Read(b, off, len);\r
+ if (len <= 0) { // TODO BUG1 -jr- Check this was < 0 but avail was not adjusted causing failure in later calls\r
+ if (!inf.IsFinished) {\r
+ throw new ZipException("Inflater not finished!?");\r
+ }\r
+ avail = inf.RemainingInput;\r
+ \r
+ // BUG1 -jr- With bit 3 set you dont yet know the size\r
+ if ((flags & 8) == 0 && (inf.TotalIn != csize || inf.TotalOut != size)) {\r
+ throw new ZipException("size mismatch: " + csize + ";" + size + " <-> " + inf.TotalIn + ";" + inf.TotalOut);\r
+ }\r
+ inf.Reset();\r
+ finished = true;\r
+ }\r
+ break;\r
+ \r
+ case ZipOutputStream.STORED:\r
+ if (len > csize && csize >= 0) {\r
+ len = (int)csize;\r
+ }\r
+ len = ReadBuf(b, off, len);\r
+ if (len > 0) {\r
+ csize -= len;\r
+ size -= len;\r
+ }\r
+ \r
+ if (csize == 0) {\r
+ finished = true;\r
+ } else {\r
+ if (len < 0) {\r
+ throw new ZipException("EOF in stored block");\r
+ }\r
+ }\r
+ \r
+ // decrypting crypted data\r
+ if (cryptbuffer != null) {\r
+ DecryptBlock(b, off, len);\r
+ }\r
+ \r
+ break;\r
+ }\r
+ \r
+ if (len > 0) {\r
+ crc.Update(b, off, len);\r
+ }\r
+ \r
+ if (finished) {\r
+ if ((flags & 8) != 0) {\r
+ ReadDataDescr();\r
+ }\r
+ \r
+ if ((crc.Value & 0xFFFFFFFFL) != entry.Crc && entry.Crc != -1) {\r
+ throw new ZipException("CRC mismatch");\r
+ }\r
+ crc.Reset();\r
+ entry = null;\r
+ }\r
+ return len;\r
+ }\r
+ \r
+ /// <summary>\r
+ /// Closes the zip file.\r
+ /// </summary>\r
+ /// <exception name="Exception">\r
+ /// if a i/o error occured.\r
+ /// </exception>\r
+ public override void Close()\r
+ {\r
+ base.Close();\r
+ crc = null;\r
+ entry = null;\r
+ }\r
+ \r
+ }\r
+}\r