Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipEntry.Read.cs @ 18172

Last change on this file since 18172 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

File size: 35.6 KB
Line 
1// ZipEntry.Read.cs
2// ------------------------------------------------------------------
3//
4// Copyright (c) 2009-2011 Dino Chiesa
5// All rights reserved.
6//
7// This code module is part of DotNetZip, a zipfile class library.
8//
9// ------------------------------------------------------------------
10//
11// This code is licensed under the Microsoft Public License.
12// See the file License.txt for the license details.
13// More info on: http://dotnetzip.codeplex.com
14//
15// ------------------------------------------------------------------
16//
17// last saved (in emacs):
18// Time-stamp: <2011-July-09 21:31:28>
19//
20// ------------------------------------------------------------------
21//
22// This module defines logic for Reading the ZipEntry from a
23// zip file.
24//
25// ------------------------------------------------------------------
26
27
28using System;
29using System.IO;
30
31namespace OfficeOpenXml.Packaging.Ionic.Zip
32{
33    internal partial class ZipEntry
34    {
35        private int _readExtraDepth;
36        private void ReadExtraField()
37        {
38            _readExtraDepth++;
39            // workitem 8098: ok (restore)
40            long posn = this.ArchiveStream.Position;
41            this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
42            // workitem 10178
43            Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
44
45            byte[] block = new byte[30];
46            this.ArchiveStream.Read(block, 0, block.Length);
47            int i = 26;
48            Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
49            Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
50
51            // workitem 8098: ok (relative)
52            this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current);
53            // workitem 10178
54            Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
55
56            ProcessExtraField(this.ArchiveStream, extraFieldLength);
57
58            // workitem 8098: ok (restore)
59            this.ArchiveStream.Seek(posn, SeekOrigin.Begin);
60            // workitem 10178
61            Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
62            _readExtraDepth--;
63        }
64
65
66        private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
67        {
68            int bytesRead = 0;
69
70            // change for workitem 8098
71            ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position;
72
73            int signature = Ionic.Zip.SharedUtilities.ReadEntrySignature(ze.ArchiveStream);
74            bytesRead += 4;
75
76            // Return false if this is not a local file header signature.
77            if (ZipEntry.IsNotValidSig(signature))
78            {
79                // Getting "not a ZipEntry signature" is not always wrong or an error.
80                // This will happen after the last entry in a zipfile.  In that case, we
81                // expect to read :
82                //    a ZipDirEntry signature (if a non-empty zip file) or
83                //    a ZipConstants.EndOfCentralDirectorySignature.
84                //
85                // Anything else is a surprise.
86
87                ze.ArchiveStream.Seek(-4, SeekOrigin.Current); // unread the signature
88                // workitem 10178
89                Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
90                if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
91                {
92                    throw new BadReadException(String.Format("  Bad signature (0x{0:X8}) at position  0x{1:X8}", signature, ze.ArchiveStream.Position));
93                }
94                return false;
95            }
96
97            byte[] block = new byte[26];
98            int n = ze.ArchiveStream.Read(block, 0, block.Length);
99            if (n != block.Length) return false;
100            bytesRead += n;
101
102            int i = 0;
103            ze._VersionNeeded = (Int16)(block[i++] + block[i++] * 256);
104            ze._BitField = (Int16)(block[i++] + block[i++] * 256);
105            ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
106            ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
107            // transform the time data into something usable (a DateTime)
108            ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);
109            ze._timestamp |= ZipEntryTimestamp.DOS;
110
111            if ((ze._BitField & 0x01) == 0x01)
112            {
113                ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field
114                ze._sourceIsEncrypted = true;
115            }
116
117            // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and
118            // CRC values are not true values; the true values will follow the entry data.
119            // But, regardless of the status of bit 3 in the bitfield, the slots for
120            // the three amigos may contain marker values for ZIP64.  So we must read them.
121            {
122                ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
123                ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
124                ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
125
126                if ((uint)ze._CompressedSize == 0xFFFFFFFF ||
127                    (uint)ze._UncompressedSize == 0xFFFFFFFF)
128
129                    ze._InputUsesZip64 = true;
130            }
131
132            Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
133            Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
134
135            block = new byte[filenameLength];
136            n = ze.ArchiveStream.Read(block, 0, block.Length);
137            bytesRead += n;
138
139            // if the UTF8 bit is set for this entry, override the
140            // encoding the application requested.
141
142            if ((ze._BitField & 0x0800) == 0x0800)
143            {
144                // workitem 12744
145                ze.AlternateEncoding = System.Text.Encoding.UTF8;
146                ze.AlternateEncodingUsage = ZipOption.Always;
147            }
148
149            // need to use this form of GetString() for .NET CF
150            ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length);
151
152            // workitem 6898
153            if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory();
154
155            bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength);
156
157            ze._LengthOfTrailer = 0;
158
159            // workitem 6607 - don't read for directories
160            // actually get the compressed size and CRC if necessary
161            if (!ze._FileNameInArchive.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008)
162            {
163                // This descriptor exists only if bit 3 of the general
164                // purpose bit flag is set (see below).  It is byte aligned
165                // and immediately follows the last byte of compressed data,
166                // as well as any encryption trailer, as with AES.
167                // This descriptor is used only when it was not possible to
168                // seek in the output .ZIP file, e.g., when the output .ZIP file
169                // was standard output or a non-seekable device.  For ZIP64(tm) format
170                // archives, the compressed and uncompressed sizes are 8 bytes each.
171
172                // workitem 8098: ok (restore)
173                long posn = ze.ArchiveStream.Position;
174
175                // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and
176                // a consistent data record after that.   To be consistent, the data record must
177                // indicate the length of the entry data.
178                bool wantMore = true;
179                long SizeOfDataRead = 0;
180                int tries = 0;
181                while (wantMore)
182                {
183                    tries++;
184                    // We call the FindSignature shared routine to find the specified signature
185                    // in the already-opened zip archive, starting from the current cursor
186                    // position in that filestream.  If we cannot find the signature, then the
187                    // routine returns -1, and the ReadHeader() method returns false,
188                    // indicating we cannot read a legal entry header.  If we have found it,
189                    // then the FindSignature() method returns the number of bytes in the
190                    // stream we had to seek forward, to find the sig.  We need this to
191                    // determine if the zip entry is valid, later.
192
193                    if (ze._container.ZipFile != null)
194                        ze._container.ZipFile.OnReadBytes(ze);
195
196                    long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature);
197                    if (d == -1) return false;
198
199                    // total size of data read (through all loops of this).
200                    SizeOfDataRead += d;
201
202                    if (ze._InputUsesZip64)
203                    {
204                        // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size)
205                        block = new byte[20];
206                        n = ze.ArchiveStream.Read(block, 0, block.Length);
207                        if (n != 20) return false;
208
209                        // do not increment bytesRead - it is for entry header only.
210                        // the data we have just read is a footer (falls after the file data)
211                        //bytesRead += n;
212
213                        i = 0;
214                        ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
215                        ze._CompressedSize = BitConverter.ToInt64(block, i);
216                        i += 8;
217                        ze._UncompressedSize = BitConverter.ToInt64(block, i);
218                        i += 8;
219
220                        ze._LengthOfTrailer += 24;  // bytes including sig, CRC, Comp and Uncomp sizes
221                    }
222                    else
223                    {
224                        // read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size)
225                        block = new byte[12];
226                        n = ze.ArchiveStream.Read(block, 0, block.Length);
227                        if (n != 12) return false;
228
229                        // do not increment bytesRead - it is for entry header only.
230                        // the data we have just read is a footer (falls after the file data)
231                        //bytesRead += n;
232
233                        i = 0;
234                        ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
235                        ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
236                        ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
237
238                        ze._LengthOfTrailer += 16;  // bytes including sig, CRC, Comp and Uncomp sizes
239
240                    }
241
242                    wantMore = (SizeOfDataRead != ze._CompressedSize);
243
244                    if (wantMore)
245                    {
246                        // Seek back to un-read the last 12 bytes  - maybe THEY contain
247                        // the ZipEntryDataDescriptorSignature.
248                        // (12 bytes for the CRC, Comp and Uncomp size.)
249                        ze.ArchiveStream.Seek(-12, SeekOrigin.Current);
250                        // workitem 10178
251                        Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
252
253                        // Adjust the size to account for the false signature read in
254                        // FindSignature().
255                        SizeOfDataRead += 4;
256                    }
257                }
258
259                // seek back to previous position, to prepare to read file data
260                // workitem 8098: ok (restore)
261                ze.ArchiveStream.Seek(posn, SeekOrigin.Begin);
262                // workitem 10178
263                Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
264            }
265
266            ze._CompressedFileDataSize = ze._CompressedSize;
267
268
269            // bit 0 set indicates that some kind of encryption is in use
270            if ((ze._BitField & 0x01) == 0x01)
271            {
272#if AESCRYPTO
273                if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 ||
274                    ze.Encryption == EncryptionAlgorithm.WinZipAes256)
275                {
276                    int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile);
277                    // read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128.
278                    ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream);
279                    bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes)
280                    // according to WinZip, the CompressedSize includes the AES Crypto framing data.
281                    ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata;
282                    ze._LengthOfTrailer += 10;  // MAC
283                }
284                else
285#endif
286                {
287                    // read in the header data for "weak" encryption
288                    ze._WeakEncryptionHeader = new byte[12];
289                    bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader);
290                    // decrease the filedata size by 12 bytes
291                    ze._CompressedFileDataSize -= 12;
292                }
293            }
294
295            // Remember the size of the blob for this entry.
296            // We also have the starting position in the stream for this entry.
297            ze._LengthOfHeader = bytesRead;
298            ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer;
299
300
301            // We've read in the regular entry header, the extra field, and any
302            // encryption header.  The pointer in the file is now at the start of the
303            // filedata, which is potentially compressed and encrypted.  Just ahead in
304            // the file, there are _CompressedFileDataSize bytes of data, followed by
305            // potentially a non-zero length trailer, consisting of optionally, some
306            // encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24
307            // bytes).
308
309            return true;
310        }
311
312
313
314        internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer)
315        {
316            // PKZIP encrypts the compressed data stream.  Encrypted files must
317            // be decrypted before they can be extracted.
318
319            // Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data
320            // area defining the encryption header for that file.  The encryption header is
321            // originally set to random values, and then itself encrypted, using three, 32-bit
322            // keys.  The key values are initialized using the supplied encryption password.
323            // After each byte is encrypted, the keys are then updated using pseudo-random
324            // number generation techniques in combination with the same CRC-32 algorithm used
325            // in PKZIP and implemented in the CRC32.cs module in this project.
326
327            // read the 12-byte encryption header
328            int additionalBytesRead = s.Read(buffer, 0, 12);
329            if (additionalBytesRead != 12)
330                throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position));
331
332            return additionalBytesRead;
333        }
334
335
336
337        private static bool IsNotValidSig(int signature)
338        {
339            return (signature != ZipConstants.ZipEntrySignature);
340        }
341
342
343        /// <summary>
344        ///   Reads one <c>ZipEntry</c> from the given stream.  The content for
345        ///   the entry does not get decompressed or decrypted.  This method
346        ///   basically reads metadata, and seeks.
347        /// </summary>
348        /// <param name="zc">the ZipContainer this entry belongs to.</param>
349        /// <param name="first">
350        ///   true of this is the first entry being read from the stream.
351        /// </param>
352        /// <returns>the <c>ZipEntry</c> read from the stream.</returns>
353        internal static ZipEntry ReadEntry(ZipContainer zc, bool first)
354        {
355            ZipFile zf = zc.ZipFile;
356            Stream s = zc.ReadStream;
357            System.Text.Encoding defaultEncoding = zc.AlternateEncoding;
358            ZipEntry entry = new ZipEntry();
359            entry._Source = ZipEntrySource.ZipFile;
360            entry._container = zc;
361            entry._archiveStream = s;
362            if (zf != null)
363                zf.OnReadEntry(true, null);
364
365            if (first) HandlePK00Prefix(s);
366
367            // Read entry header, including any encryption header
368            if (!ReadHeader(entry, defaultEncoding)) return null;
369
370            // Store the position in the stream for this entry
371            // change for workitem 8098
372            entry.__FileDataPosition = entry.ArchiveStream.Position;
373
374            // seek past the data without reading it. We will read on Extract()
375            s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current);
376            // workitem 10178
377            Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
378
379            // ReadHeader moves the file pointer to the end of the entry header,
380            // as well as any encryption header.
381
382            // CompressedFileDataSize includes:
383            //   the maybe compressed, maybe encrypted file data
384            //   the encryption trailer, if any
385            //   the bit 3 descriptor, if any
386
387            // workitem 5306
388            // http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306
389            HandleUnexpectedDataDescriptor(entry);
390
391            if (zf != null)
392            {
393                zf.OnReadBytes(entry);
394                zf.OnReadEntry(false, entry);
395            }
396
397            return entry;
398        }
399
400
401        internal static void HandlePK00Prefix(Stream s)
402        {
403            // in some cases, the zip file begins with "PK00".  This is a throwback and is rare,
404            // but we handle it anyway. We do not change behavior based on it.
405            uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
406            if (datum != ZipConstants.PackedToRemovableMedia)
407            {
408                s.Seek(-4, SeekOrigin.Current); // unread the block
409                // workitem 10178
410                Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
411            }
412        }
413
414
415
416        private static void HandleUnexpectedDataDescriptor(ZipEntry entry)
417        {
418            Stream s = entry.ArchiveStream;
419
420            // In some cases, the "data descriptor" is present, without a signature, even when
421            // bit 3 of the BitField is NOT SET.  This is the CRC, followed
422            //    by the compressed length and the uncompressed length (4 bytes for each
423            //    of those three elements).  Need to check that here.
424            //
425            uint datum = (uint)Ionic.Zip.SharedUtilities.ReadInt(s);
426            if (datum == entry._Crc32)
427            {
428                int sz = Ionic.Zip.SharedUtilities.ReadInt(s);
429                if (sz == entry._CompressedSize)
430                {
431                    sz = Ionic.Zip.SharedUtilities.ReadInt(s);
432                    if (sz == entry._UncompressedSize)
433                    {
434                        // ignore everything and discard it.
435                    }
436                    else
437                    {
438                        s.Seek(-12, SeekOrigin.Current); // unread the three blocks
439
440                        // workitem 10178
441                        Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
442                    }
443                }
444                else
445                {
446                    s.Seek(-8, SeekOrigin.Current); // unread the two blocks
447
448                    // workitem 10178
449                    Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
450                }
451            }
452            else
453            {
454                s.Seek(-4, SeekOrigin.Current); // unread the block
455
456                // workitem 10178
457                Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
458            }
459        }
460
461
462        /// <summary>
463        ///   Finds a particular segment in the given extra field.
464        ///   This is used when modifying a previously-generated
465        ///   extra field, in particular when removing the AES crypto
466        ///   segment in the extra field.
467        /// </summary>
468        static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId)
469        {
470            int j = offx;
471            while (j + 3 < extra.Length)
472            {
473                UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256);
474                if (headerId == targetHeaderId) return j-2;
475
476                // else advance to next segment
477                Int16 dataSize = (short)(extra[j++] + extra[j++] * 256);
478                j+= dataSize;
479            }
480
481            return -1;
482        }
483
484
485        /// <summary>
486        ///   At current cursor position in the stream, read the extra
487        ///   field, and set the properties on the ZipEntry instance
488        ///   appropriately.  This can be called when processing the
489        ///   Extra field in the Central Directory, or in the local
490        ///   header.
491        /// </summary>
492        internal int ProcessExtraField(Stream s, Int16 extraFieldLength)
493        {
494            int additionalBytesRead = 0;
495            if (extraFieldLength > 0)
496            {
497                byte[] buffer = this._Extra = new byte[extraFieldLength];
498                additionalBytesRead = s.Read(buffer, 0, buffer.Length);
499                long posn = s.Position - additionalBytesRead;
500                int j = 0;
501                while (j + 3 < buffer.Length)
502                {
503                    int start = j;
504                    UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256);
505                    Int16 dataSize = (short)(buffer[j++] + buffer[j++] * 256);
506
507                    switch (headerId)
508                    {
509                        case 0x000a:  // NTFS ctime, atime, mtime
510                            j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn);
511                            break;
512
513                        case 0x5455:  // Unix ctime, atime, mtime
514                            j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn);
515                            break;
516
517                        case 0x5855:  // Info-zip Extra field (outdated)
518                            // This is outdated, so the field is supported on
519                            // read only.
520                            j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn);
521                            break;
522
523                        case 0x7855:  // Unix uid/gid
524                            // ignored. DotNetZip does not handle this field.
525                            break;
526
527                        case 0x7875:  // ??
528                            // ignored.  I could not find documentation on this field,
529                            // though it appears in some zip files.
530                            break;
531
532                        case 0x0001: // ZIP64
533                            j = ProcessExtraFieldZip64(buffer, j, dataSize, posn);
534                            break;
535
536#if AESCRYPTO
537                        case 0x9901: // WinZip AES encryption is in use.  (workitem 6834)
538                            // we will handle this extra field only  if compressionmethod is 0x63
539                            j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn);
540                            break;
541#endif
542                        case 0x0017: // workitem 7968: handle PKWare Strong encryption header
543                            j = ProcessExtraFieldPkwareStrongEncryption(buffer, j);
544                            break;
545                    }
546
547                    // move to the next Header in the extra field
548                    j = start + dataSize + 4;
549                }
550            }
551            return additionalBytesRead;
552        }
553
554        private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j)
555        {
556            //           Value     Size     Description
557            //           -----     ----     -----------
558            //           0x0017    2 bytes  Tag for this "extra" block type
559            //           TSize     2 bytes  Size of data that follows
560            //           Format    2 bytes  Format definition for this record
561            //           AlgID     2 bytes  Encryption algorithm identifier
562            //           Bitlen    2 bytes  Bit length of encryption key
563            //           Flags     2 bytes  Processing flags
564            //           CertData  TSize-8  Certificate decryption extra field data
565            //                              (refer to the explanation for CertData
566            //                               in the section describing the
567            //                               Certificate Processing Method under
568            //                               the Strong Encryption Specification)
569
570            j += 2;
571            _UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256);
572            _Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported;
573
574            // DotNetZip doesn't support this algorithm, but we don't need to throw
575            // here.  we might just be reading the archive, which is fine.  We'll
576            // need to throw if Extract() is called.
577
578            return j;
579        }
580
581
582#if AESCRYPTO
583        private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, Int16 dataSize, long posn)
584        {
585            if (this._CompressionMethod == 0x0063)
586            {
587                if ((this._BitField & 0x01) != 0x01)
588                    throw new BadReadException(String.Format("  Inconsistent metadata at position 0x{0:X16}", posn));
589
590                this._sourceIsEncrypted = true;
591
592                //this._aesCrypto = new WinZipAesCrypto(this);
593                // see spec at http://www.winzip.com/aes_info.htm
594                if (dataSize != 7)
595                    throw new BadReadException(String.Format("  Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn));
596
597                this._WinZipAesMethod = BitConverter.ToInt16(buffer, j);
598                j += 2;
599                if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02)
600                    throw new BadReadException(String.Format("  Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}",
601                        this._WinZipAesMethod, posn));
602
603                Int16 vendorId = BitConverter.ToInt16(buffer, j);
604                j += 2;
605                if (vendorId != 0x4541)
606                    throw new BadReadException(String.Format("  Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn));
607
608                int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1;
609                if (keystrength < 0)
610                    throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength));
611
612                _Encryption_FromZipFile = this._Encryption = (keystrength == 128)
613                    ? EncryptionAlgorithm.WinZipAes128
614                    : EncryptionAlgorithm.WinZipAes256;
615
616                j++;
617
618                // set the actual compression method
619                this._CompressionMethod_FromZipFile =
620                this._CompressionMethod = BitConverter.ToInt16(buffer, j);
621                j += 2; // for the next segment of the extra field
622            }
623            return j;
624        }
625
626#endif
627
628        private delegate T Func<T>();
629
630        private int ProcessExtraFieldZip64(byte[] buffer, int j, Int16 dataSize, long posn)
631        {
632            // The PKWare spec says that any of {UncompressedSize, CompressedSize,
633            // RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header,
634            // and the ZIP64 header may contain one or more of those.  If the
635            // values are present, they will be found in the prescribed order.
636            // There may also be a 4-byte "disk start number."
637            // This means that the DataSize must be 28 bytes or less.
638
639            this._InputUsesZip64 = true;
640
641            // workitem 7941: check datasize before reading.
642            if (dataSize > 28)
643                throw new BadReadException(String.Format("  Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}",
644                                                         dataSize, posn));
645            int remainingData = dataSize;
646
647            var slurp = new Func<Int64>( () => {
648                    if (remainingData < 8)
649                        throw new BadReadException(String.Format("  Missing data for ZIP64 extra field, position 0x{0:X16}", posn));
650                    var x = BitConverter.ToInt64(buffer, j);
651                    j+= 8;
652                    remainingData -= 8;
653                    return x;
654                });
655
656            if (this._UncompressedSize == 0xFFFFFFFF)
657                this._UncompressedSize = slurp();
658
659            if (this._CompressedSize == 0xFFFFFFFF)
660                this._CompressedSize = slurp();
661
662            if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF)
663                this._RelativeOffsetOfLocalHeader = slurp();
664
665            // Ignore anything else. Potentially there are 4 more bytes for the
666            // disk start number.  DotNetZip currently doesn't handle multi-disk
667            // archives.
668            return j;
669        }
670
671
672        private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, Int16 dataSize, long posn)
673        {
674            if (dataSize != 12 && dataSize != 8)
675                throw new BadReadException(String.Format("  Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn));
676
677            Int32 timet = BitConverter.ToInt32(buffer, j);
678            this._Mtime = _unixEpoch.AddSeconds(timet);
679            j += 4;
680
681            timet = BitConverter.ToInt32(buffer, j);
682            this._Atime = _unixEpoch.AddSeconds(timet);
683            j += 4;
684
685            this._Ctime = DateTime.UtcNow;
686
687            _ntfsTimesAreSet = true;
688            _timestamp |= ZipEntryTimestamp.InfoZip1; return j;
689        }
690
691
692
693        private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, Int16 dataSize, long posn)
694        {
695            // The Unix filetimes are 32-bit unsigned integers,
696            // storing seconds since Unix epoch.
697
698            if (dataSize != 13 && dataSize != 9 && dataSize != 5)
699                throw new BadReadException(String.Format("  Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn));
700
701            int remainingData = dataSize;
702
703            var slurp = new Func<DateTime>( () => {
704                    Int32 timet = BitConverter.ToInt32(buffer, j);
705                    j += 4;
706                    remainingData -= 4;
707                    return _unixEpoch.AddSeconds(timet);
708                });
709
710            if (dataSize == 13 || _readExtraDepth > 0)
711            {
712                byte flag = buffer[j++];
713                remainingData--;
714
715                if ((flag & 0x0001) != 0 && remainingData >= 4)
716                    this._Mtime = slurp();
717
718                 this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4)
719                     ? slurp()
720                     : DateTime.UtcNow;
721
722                 this._Ctime =  ((flag & 0x0004) != 0 && remainingData >= 4)
723                     ? slurp()
724                     :DateTime.UtcNow;
725
726                _timestamp |= ZipEntryTimestamp.Unix;
727                _ntfsTimesAreSet = true;
728                _emitUnixTimes = true;
729            }
730            else
731                ReadExtraField(); // will recurse
732
733            return j;
734        }
735
736
737        private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, Int16 dataSize, long posn)
738        {
739            // The NTFS filetimes are 64-bit unsigned integers, stored in Intel
740            // (least significant byte first) byte order. They are expressed as the
741            // number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
742            // which is "01-Jan-1601 00:00:00 UTC".
743            //
744            // HeaderId   2 bytes    0x000a == NTFS stuff
745            // Datasize   2 bytes    ?? (usually 32)
746            // reserved   4 bytes    ??
747            // timetag    2 bytes    0x0001 == time
748            // size       2 bytes    24 == 8 bytes each for ctime, mtime, atime
749            // mtime      8 bytes    win32 ticks since win32epoch
750            // atime      8 bytes    win32 ticks since win32epoch
751            // ctime      8 bytes    win32 ticks since win32epoch
752
753            if (dataSize != 32)
754                throw new BadReadException(String.Format("  Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn));
755
756            j += 4;  // reserved
757            Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256);
758            Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256);
759            j += 4;  // tag and size
760
761            if (timetag == 0x0001 && addlsize == 24)
762            {
763                Int64 z = BitConverter.ToInt64(buffer, j);
764                this._Mtime = DateTime.FromFileTimeUtc(z);
765                j += 8;
766
767                // At this point the library *could* set the LastModified value
768                // to coincide with the Mtime value.  In theory, they refer to
769                // the same property of the file, and should be the same anyway,
770                // allowing for differences in precision.  But they are
771                // independent quantities in the zip archive, and this library
772                // will keep them separate in the object model. There is no ill
773                // effect from this, because as files are extracted, the
774                // higher-precision value (Mtime) is used if it is present.
775                // Apps may wish to compare the Mtime versus LastModified
776                // values, but any difference when both are present is not
777                // germaine to the correctness of the library. but note: when
778                // explicitly setting either value, both are set. See the setter
779                // for LastModified or the SetNtfsTimes() method.
780
781                z = BitConverter.ToInt64(buffer, j);
782                this._Atime = DateTime.FromFileTimeUtc(z);
783                j += 8;
784
785                z = BitConverter.ToInt64(buffer, j);
786                this._Ctime = DateTime.FromFileTimeUtc(z);
787                j += 8;
788
789                _ntfsTimesAreSet = true;
790                _timestamp |= ZipEntryTimestamp.Windows;
791                _emitNtfsTimes = true;
792            }
793            return j;
794        }
795
796
797    }
798}
Note: See TracBrowser for help on using the repository browser.