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:  
38:   using System;
39:   using System.Text;
40:   using System.IO;
41:  
42:   using ICSharpCode.SharpZipLib.Checksums;
43:   using ICSharpCode.SharpZipLib.Zip.Compression;
44:   using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
45:  
46:   namespace ICSharpCode.SharpZipLib.Zip {
47:       
48:       /// <summary>
49:       /// This is a FilterInputStream that reads the files baseInputStream an zip archive
50:       /// one after another.  It has a special method to get the zip entry of
51:       /// the next file.  The zip entry contains information about the file name
52:       /// size, compressed size, CRC, etc.
53:       /// It includes support for STORED and DEFLATED entries.
54:       /// 
55:       /// author of the original java version : Jochen Hoenicke
56:       /// </summary>
57:       /// <exampleThis sample shows how to read a zip file
58:       /// <code>
59:       /// using System;
60:       /// using System.Text;
61:       /// using System.IO;
62:       /// 
63:       /// using NZlib.Zip;
64:       /// 
65:       /// class MainClass
66:       /// {
67:       ///     public static void Main(string[] args)
68:       ///     {
69:       ///         ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]));
70:       ///         
71:       ///         ZipEntry theEntry;
72:       ///         while ((theEntry = s.GetNextEntry()) != null) {
73:       ///             Console.WriteLine("File " + theEntry.Name);
74:       ///             int size = 2048;
75:       ///             byte[] data = new byte[2048];
76:       ///             
77:       ///             Console.Write("Show contents (y/n) ?");
78:       ///             if (Console.ReadLine() == "y") {
79:       ///                 while (true) {
80:       ///                     size = s.Read(data, 0, data.Length);
81:       ///                     if (size > 0) {
82:       ///                         Console.Write(new ASCIIEncoding().GetString(data, 0, size));
83:       ///                     } else {
84:       ///                         break;
85:       ///                     }
86:       ///                 }
87:       ///             }
88:       ///             Console.WriteLine();
89:       ///         }
90:       ///         s.Close();
91:       ///     }
92:       /// }    
93:       /// </code>
94:       /// </example>
95:       public class ZipInputStream : InflaterInputStream
96:       {
97:           private Crc32 crc new Crc32();
98:           private ZipEntry entry null;
99:           
100:           private long csize;
101:           private long size;
102:           private int method;
103:           private int flags;
104:           private long avail;
105:           
106:           /// <summary>
107:           /// Creates a new Zip input stream, reading a zip archive.
108:           /// </summary>
109:           public ZipInputStream(Stream baseInputStreambase(baseInputStreamnew Inflater(true))
110:           {
111:               
112:           }
113:           
114:           private void FillBuf()
115:           {
116:               avail len baseInputStream.Read(buf0buf.Length);
117:           }
118:           
119:           private int ReadBuf(byte[] outBufint offsetint length)
120:           {
121:               if (avail <= 0) {
122:                   FillBuf();
123:                   if (avail <= 0) {
124:                       return -1;
125:                   }
126:               }
127:               if (length avail) {
128:                   length = (int)avail;
129:               }
130:               System.Array.Copy(buflen - (int)availoutBufoffsetlength);
131:               avail -= length;
132:               return length;
133:           }
134:           
135:           private void ReadFully(byte[] outBuf)
136:           {
137:               int off 0;
138:               int len outBuf.Length;
139:               while (len 0) {
140:                   int count ReadBuf(outBufofflen);
141:                   if (count == -1) {
142:                       throw new Exception(); 
143:                   }
144:                   off += count;
145:                   len -= count;
146:               }
147:           }
148:           
149:           private int ReadLeByte()
150:           {
151:               if (avail <= 0) {
152:                   FillBuf();
153:                   if (avail <= 0) {
154:                       throw new ZipException("EOF in header");
155:                   }
156:               }
157:               return buf[len avail--] & 0xff;
158:           }
159:           
160:           /// <summary>
161:           /// Read an unsigned short baseInputStream little endian byte order.
162:           /// </summary>
163:           private int ReadLeShort()
164:           {
165:               return ReadLeByte() | (ReadLeByte() << 8);
166:           }
167:           
168:           /// <summary>
169:           /// Read an int baseInputStream little endian byte order.
170:           /// </summary>
171:           private int ReadLeInt()
172:           {
173:               return ReadLeShort() | (ReadLeShort() << 16);
174:           }
175:           
176:           /// <summary>
177:           /// Read an int baseInputStream little endian byte order.
178:           /// </summary>
179:           private long ReadLeLong()
180:           {
181:               return ReadLeInt() | (ReadLeInt() << 32);
182:           }
183:           
184:           /// <summary>
185:           /// Open the next entry from the zip archive, and return its description.
186:           /// If the previous entry wasn't closed, this method will close it.
187:           /// </summary>
188:           public ZipEntry GetNextEntry()
189:           {
190:               if (crc == null) {
191:                   throw new InvalidOperationException("Closed.");
192:               }
193:               if (entry != null) {
194:                   CloseEntry();
195:               }
196:               
197:               int header ReadLeInt();
198:               if (header == ZipConstants.CENSIG) {
199:                   /* Central Header reached. */
200:                   Close();
201:                   return null;
202:               }
203:               if (header != ZipConstants.LOCSIG) {
204:                   throw new ZipException("Wrong Local header signature" header);
205:               }
206:               
207:               short version = (short)ReadLeShort();
208:               
209:               flags ReadLeShort();
210:               method ReadLeShort();
211:               int dostime ReadLeInt();
212:               int crc2 ReadLeInt();
213:               csize ReadLeInt();
214:               size ReadLeInt();
215:               int nameLen ReadLeShort();
216:               int extraLen ReadLeShort();
217:               
218:               if (method == ZipOutputStream.STORED && csize != size) {
219:                   throw new ZipException("Stored, but compressed != uncompressed");
220:               }
221:               
222:               byte[] buffer new byte[nameLen];
223:               ReadFully(buffer);
224:               
225:               string name ZipConstants.ConvertToString(buffer);
226:               
227:               entry new ZipEntry(name);
228:               
229:               entry.Version = (ushort)version;
230:               if (method != && method != 8) {
231:                   throw new ZipException("unknown compression method " method);
232:               }
233:               entry.CompressionMethod = (CompressionMethod)method;
234:               
235:               if ((flags 8) == 0) {
236:                   entry.Crc  crc2 0xFFFFFFFFL;
237:                   entry.Size size 0xFFFFFFFFL;
238:                   entry.CompressedSize csize 0xFFFFFFFFL;
239:               }
240:               entry.DosTime dostime;
241:               if (extraLen 0) {
242:                   byte[] extra new byte[extraLen];
243:                   ReadFully(extra);
244:                   entry.ExtraData extra;
245:               }
246:               
247:               if (method == ZipOutputStream.DEFLATED && avail 0) {
248:                   System.Array.Copy(buflen - (int)availbuf0, (int)avail);
249:                   len = (int)avail;
250:                   avail 0;
251:                   inf.SetInput(buf0len);
252:               }
253:               return entry;
254:           }
255:           
256:           private void ReadDataDescr()
257:           {
258:               if (ReadLeInt() != ZipConstants.EXTSIG) {
259:                   throw new ZipException("Data descriptor signature not found");
260:               }
261:               entry.Crc ReadLeInt() & 0xFFFFFFFFL;
262:               csize ReadLeInt();
263:               size ReadLeInt();
264:               entry.Size size 0xFFFFFFFFL;
265:               entry.CompressedSize csize 0xFFFFFFFFL;
266:           }
267:           
268:           /// <summary>
269:           /// Closes the current zip entry and moves to the next one.
270:           /// </summary>
271:           public void CloseEntry()
272:           {
273:               if (crc == null) {
274:                   throw new InvalidOperationException("Closed.");
275:               }
276:               if (entry == null) {
277:                   return;
278:               }
279:               
280:               if (method == ZipOutputStream.DEFLATED) {
281:                   if ((flags 8) != 0) {
282:                       /* We don't know how much we must skip, read until end. */
283:                       byte[] tmp new byte[2048];
284:                       while (Read(tmp0tmp.Length) > 0)
285:                           ;
286:                       /* read will close this entry */
287:                       return;
288:                   }
289:                   csize -= inf.TotalIn;
290:                   avail inf.RemainingInput;
291:               }
292:               if (avail csize && csize >= 0) {
293:                   avail -= csize;
294:               else {
295:                   csize -= avail;
296:                   avail 0;
297:                   while (csize != 0) {
298:                       int skipped = (int)base.Skip(csize 0xFFFFFFFFL);
299:                       
300:                       if (skipped <= 0) {
301:                           throw new ZipException("zip archive ends early.");
302:                       }
303:                       
304:                       csize -= skipped;
305:                   }
306:               }
307:               
308:               size 0;
309:               crc.Reset();
310:               if (method == ZipOutputStream.DEFLATED) {
311:                   inf.Reset();
312:               }
313:               entry null;
314:           }
315:           
316:           public override int Available {
317:               get {
318:                   return entry != null 0;
319:               }
320:           }
321:           
322:           /// <summary>
323:           /// Reads a byte from the current zip entry.
324:           /// </summary>
325:           /// <returns>
326:           /// the byte or -1 on EOF.
327:           /// </returns>
328:           /// <exception name="System.IO.IOException">
329:           /// IOException if a i/o error occured.
330:           /// </exception>
331:           /// <exception name="ICSharpCode.SharpZipLib.ZipException">
332:           /// ZipException if the deflated stream is corrupted.
333:           /// </exception>
334:           public override int ReadByte()
335:           {
336:               byte[] new byte[1];
337:               if (Read(b01) <= 0) {
338:                   return -1;
339:               }
340:               return b[0] & 0xff;
341:           }
342:           
343:           /// <summary>
344:           /// Reads a block of bytes from the current zip entry.
345:           /// </summary>
346:           /// <returns>
347:           /// the number of bytes read (may be smaller, even before EOF), or -1 on EOF.
348:           /// </returns>
349:           /// <exception name="Exception">
350:           /// IOException if a i/o error occured.
351:           /// ZipException if the deflated stream is corrupted.
352:           /// </exception>
353:           public override int Read(byte[] bint offint len)
354:           {
355:               if (crc == null) {
356:                   throw new InvalidOperationException("Closed.");
357:               }
358:               if (entry == null) {
359:                   return -1;
360:               }
361:               bool finished false;
362:               
363:               switch (method) {
364:                   case ZipOutputStream.DEFLATED:
365:                       len base.Read(bofflen);
366:                       if (len 0) {
367:                           if (!inf.IsFinished) {
368:                               throw new ZipException("Inflater not finished!?");
369:                           }
370:                           avail inf.RemainingInput;
371:                           if ((flags 8) != 0) {
372:                               ReadDataDescr();
373:                           }
374:                           
375:                           if (inf.TotalIn != csize || inf.TotalOut != size) {
376:                               throw new ZipException("size mismatch: " csize ";" size " <-> " inf.TotalIn ";" inf.TotalOut);
377:                           }
378:                           inf.Reset();
379:                           finished true;
380:                       }
381:                       break;
382:                   
383:                   case ZipOutputStream.STORED:
384:                       
385:                       if (len csize && csize >=