Add this for backwards compatibility
[mono.git] / mcs / class / Compat.ICSharpCode.SharpZipLib / ICSharpCode.SharpZipLib / Zip / ZipInputStream.cs
diff --git a/mcs/class/Compat.ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs b/mcs/class/Compat.ICSharpCode.SharpZipLib/ICSharpCode.SharpZipLib/Zip/ZipInputStream.cs
new file mode 100644 (file)
index 0000000..ceae244
--- /dev/null
@@ -0,0 +1,488 @@
+// 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