Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net35 / ICSharpCode.SharpZipLib.Silverlight / Zip / ZipEntry.cs
1 // ZipEntry.cs
2 //
3 // Copyright (C) 2001 Mike Krueger
4 // Copyright (C) 2004 John Reilly
5 //
6 // This file was translated from java, it was part of the GNU Classpath
7 // Copyright (C) 2001 Free Software Foundation, Inc.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22 //
23 // Linking this library statically or dynamically with other modules is
24 // making a combined work based on this library.  Thus, the terms and
25 // conditions of the GNU General Public License cover the whole
26 // combination.
27 // 
28 // As a special exception, the copyright holders of this library give you
29 // permission to link this library with independent modules to produce an
30 // executable, regardless of the license terms of these independent
31 // modules, and to copy and distribute the resulting executable under
32 // terms of your choice, provided that you also meet, for each linked
33 // independent module, the terms and conditions of the license of that
34 // module.  An independent module is a module which is not derived from
35 // or based on this library.  If you modify this library, you may extend
36 // this exception to your version of the library, but you are not
37 // obligated to do so.  If you do not wish to do so, delete this
38 // exception statement from your version.
39
40 using System;
41 using System.IO;
42 using ICSharpCode.SharpZipLib.Silverlight.Zip;
43 using ICSharpCode.SharpZipLib.Zip;
44
45 namespace ICSharpCode.SharpZipLib.Silverlight.Zip
46 {
47     /// <summary>
48     /// Defines known values for the <see cref="HostSystemID"/> property.
49     /// </summary>
50     public enum HostSystemID
51     {
52         /// <summary>
53         /// Host system = MSDOS
54         /// </summary>
55         Msdos = 0,
56         /// <summary>
57         /// Host system = Amiga
58         /// </summary>
59         Amiga = 1,
60         /// <summary>
61         /// Host system = Open VMS
62         /// </summary>
63         OpenVms = 2,
64         /// <summary>
65         /// Host system = Unix
66         /// </summary>
67         Unix = 3,
68         /// <summary>
69         /// Host system = VMCms
70         /// </summary>
71         VMCms = 4,
72         /// <summary>
73         /// Host system = Atari ST
74         /// </summary>
75         AtariST = 5,
76         /// <summary>
77         /// Host system = OS2
78         /// </summary>
79         OS2 = 6,
80         /// <summary>
81         /// Host system = Macintosh
82         /// </summary>
83         Macintosh = 7,
84         /// <summary>
85         /// Host system = ZSystem
86         /// </summary>
87         ZSystem = 8,
88         /// <summary>
89         /// Host system = Cpm
90         /// </summary>
91         Cpm = 9,
92         /// <summary>
93         /// Host system = Windows NT
94         /// </summary>
95         WindowsNT = 10,
96         /// <summary>
97         /// Host system = MVS
98         /// </summary>
99         MVS = 11,
100         /// <summary>
101         /// Host system = VSE
102         /// </summary>
103         Vse = 12,
104         /// <summary>
105         /// Host system = Acorn RISC
106         /// </summary>
107         AcornRisc = 13,
108         /// <summary>
109         /// Host system = VFAT
110         /// </summary>
111         Vfat = 14,
112         /// <summary>
113         /// Host system = Alternate MVS
114         /// </summary>
115         AlternateMvs = 15,
116         /// <summary>
117         /// Host system = BEOS
118         /// </summary>
119         BeOS = 16,
120         /// <summary>
121         /// Host system = Tandem
122         /// </summary>
123         Tandem = 17,
124         /// <summary>
125         /// Host system = OS400
126         /// </summary>
127         OS400 = 18,
128         /// <summary>
129         /// Host system = OSX
130         /// </summary>
131         OSX = 19,
132         /// <summary>
133         /// Host system = WinZIP AES
134         /// </summary>
135         WinZipAES = 99,
136     }
137
138     /// <summary>
139     /// This class represents an entry in a zip archive.  This can be a file
140     /// or a directory
141     /// ZipFile and ZipInputStream will give you instances of this class as 
142     /// information about the members in an archive.  ZipOutputStream
143     /// uses an instance of this class when creating an entry in a Zip file.
144     /// <br/>
145     /// <br/>Author of the original java version : Jochen Hoenicke
146     /// </summary>
147     public class ZipEntry
148     {
149         [Flags]
150         enum Known : byte
151         {
152             None = 0,
153             Size = 0x01,
154             CompressedSize = 0x02,
155             Crc = 0x04,
156             Time = 0x08,
157             ExternalAttributes = 0x10,
158         }
159                 
160         #region Constructors
161         /// <summary>
162         /// Creates a zip entry with the given name.
163         /// </summary>
164         /// <param name="name">
165         /// The name for this entry. Can include directory components.
166         /// The convention for names is 'unix' style paths with relative names only.
167         /// There are with no device names and path elements are separated by '/' characters.
168         /// </param>
169         /// <exception cref="ArgumentNullException">
170         /// The name passed is null
171         /// </exception>
172         public ZipEntry(string name)
173             : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated)
174         {
175         }
176
177         /// <summary>
178         /// Creates a zip entry with the given name and version required to extract
179         /// </summary>
180         /// <param name="name">
181         /// The name for this entry. Can include directory components.
182         /// The convention for names is 'unix'  style paths with no device names and 
183         /// path elements separated by '/' characters.  This is not enforced see <see cref="CleanName(string)">CleanName</see>
184         /// on how to ensure names are valid if this is desired.
185         /// </param>
186         /// <param name="versionRequiredToExtract">
187         /// The minimum 'feature version' required this entry
188         /// </param>
189         /// <exception cref="ArgumentNullException">
190         /// The name passed is null
191         /// </exception>
192         internal ZipEntry(string name, int versionRequiredToExtract)
193             : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy,
194                    CompressionMethod.Deflated)
195         {
196         }
197                 
198         /// <summary>
199         /// Initializes an entry with the given name and made by information
200         /// </summary>
201         /// <param name="name">Name for this entry</param>
202         /// <param name="madeByInfo">Version and HostSystem Information</param>
203         /// <param name="versionRequiredToExtract">Minimum required zip feature version required to extract this entry</param>
204         /// <param name="method">Compression method for this entry.</param>
205         /// <exception cref="ArgumentNullException">
206         /// The name passed is null
207         /// </exception>
208         /// <exception cref="ArgumentOutOfRangeException">
209         /// versionRequiredToExtract should be 0 (auto-calculate) or > 10
210         /// </exception>
211         /// <remarks>
212         /// This constructor is used by the ZipFile class when reading from the central header
213         /// It is not generally useful, use the constructor specifying the name only.
214         /// </remarks>
215         internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo,
216                           CompressionMethod method)
217         {
218             if (name == null) {
219                 throw new System.ArgumentNullException("ZipEntry name");
220             }
221
222             if ( name.Length > 0xffff ) {
223                 throw new ArgumentException("Name is too long", "name");
224             }
225
226             if ( (versionRequiredToExtract != 0) && (versionRequiredToExtract < 10) ) {
227                 throw new ArgumentOutOfRangeException("versionRequiredToExtract");
228             }
229                         
230             this.DateTime = System.DateTime.Now;
231             this.name = name;
232             this.versionMadeBy = (ushort)madeByInfo;
233             this.versionToExtract = (ushort)versionRequiredToExtract;
234             this.method = method;
235         }
236                 
237         /// <summary>
238         /// Creates a deep copy of the given zip entry.
239         /// </summary>
240         /// <param name="entry">
241         /// The entry to copy.
242         /// </param>
243         [Obsolete("Use Clone instead")]
244         public ZipEntry(ZipEntry entry)
245         {
246             if ( entry == null ) {
247                 throw new ArgumentNullException("entry");
248             }
249
250             known                  = entry.known;
251             name                   = entry.name;
252             size                   = entry.size;
253             compressedSize         = entry.compressedSize;
254             crc                    = entry.crc;
255             dosTime                = entry.dosTime;
256             method                 = entry.method;
257             comment                = entry.comment;
258             versionToExtract       = entry.versionToExtract;
259             versionMadeBy          = entry.versionMadeBy;
260             externalFileAttributes = entry.externalFileAttributes;
261             flags                  = entry.flags;
262
263             zipFileIndex           = entry.zipFileIndex;
264             offset                 = entry.offset;
265
266             forceZip64_                    = entry.forceZip64_;
267
268             if ( entry.extra != null ) {
269                 extra = new byte[entry.extra.Length];
270                 Array.Copy(entry.extra, 0, extra, 0, entry.extra.Length);
271             }
272         }
273
274         #endregion
275                 
276         /// <summary>
277         /// Get a value indicating wether the entry has a CRC value available.
278         /// </summary>
279         public bool HasCrc 
280         {
281             get {
282                 return (known & Known.Crc) != 0;
283             }
284         }
285
286         /// <summary>
287         /// Get/Set flag indicating if entry is encrypted.
288         /// A simple helper routine to aid interpretation of <see cref="Flags">flags</see>
289         /// </summary>
290         /// <remarks>This is an assistant that interprets the <see cref="Flags">flags</see> property.</remarks>
291         public bool IsCrypted 
292         {
293             get {
294                 return (flags & 1) != 0; 
295             }
296             set {
297                 if (value) {
298                     flags |= 1;
299                 } 
300                 else {
301                     flags &= ~1;
302                 }
303             }
304         }
305
306         /// <summary>
307         /// Get / set a flag indicating wether entry name and comment text are
308         /// encoded in <a href="http://www.unicode.org">unicode UTF8</a>.
309         /// </summary>
310         /// <remarks>This is an assistant that interprets the <see cref="Flags">flags</see> property.</remarks>
311         public bool IsUnicodeText
312         {
313             get {
314                 return ( flags & (int)GeneralBitFlags.UnicodeText ) != 0;
315             }
316             set {
317                 if ( value ) {
318                     flags |= (int)GeneralBitFlags.UnicodeText;
319                 }
320                 else {
321                     flags &= ~(int)GeneralBitFlags.UnicodeText;
322                 }
323             }
324         }
325                 
326         /// <summary>
327         /// Value used during password checking for PKZIP 2.0 / 'classic' encryption.
328         /// </summary>
329         internal byte CryptoCheckValue
330         {
331             get {
332                 return cryptoCheckValue_;
333             }
334
335             set {
336                 cryptoCheckValue_ = value;
337             }
338         }
339
340         /// <summary>
341         /// Get/Set general purpose bit flag for entry
342         /// </summary>
343         /// <remarks>
344         /// General purpose bit flag<br/>
345         /// <br/>
346         /// Bit 0: If set, indicates the file is encrypted<br/>
347         /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating<br/>
348         /// Imploding:<br/>
349         /// Bit 1 if set indicates an 8K sliding dictionary was used.  If clear a 4k dictionary was used<br/>
350         /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise<br/>
351         /// <br/>
352         /// Deflating:<br/>
353         ///   Bit 2    Bit 1<br/>
354         ///     0        0       Normal compression was used<br/>
355         ///     0        1       Maximum compression was used<br/>
356         ///     1        0       Fast compression was used<br/>
357         ///     1        1       Super fast compression was used<br/>
358         /// <br/>
359         /// Bit 3: If set, the fields crc-32, compressed size
360         /// and uncompressed size are were not able to be written during zip file creation
361         /// The correct values are held in a data descriptor immediately following the compressed data. <br/>
362         /// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/>
363         /// Bit 5: If set indicates the file contains compressed patch data<br/>
364         /// Bit 6: If set indicates strong encryption was used.<br/>
365         /// Bit 7-10: Unused or reserved<br/>
366         /// Bit 11: If set the name and comments for this entry are in <a href="http://www.unicode.org">unicode</a>.<br/>
367         /// Bit 12-15: Unused or reserved<br/>
368         /// </remarks>
369         /// <seealso cref="IsUnicodeText"></seealso>
370         /// <seealso cref="IsCrypted"></seealso>
371         public int Flags 
372         {
373             get { 
374                 return flags; 
375             }
376             set {
377                 flags = value; 
378             }
379         }
380
381         /// <summary>
382         /// Get/Set index of this entry in Zip file
383         /// </summary>
384         /// <remarks>This is only valid when the entry is part of a <see cref="ZipFile"></see></remarks>
385         public long ZipFileIndex 
386         {
387             get {
388                 return zipFileIndex;
389             }
390             set {
391                 zipFileIndex = value;
392             }
393         }
394                 
395         /// <summary>
396         /// Get/set offset for use in central header
397         /// </summary>
398         public long Offset 
399         {
400             get {
401                 return offset;
402             }
403             set {
404                 offset = value;
405             }
406         }
407
408         /// <summary>
409         /// Get/Set external file attributes as an integer.
410         /// The values of this are operating system dependant see
411         /// <see cref="HostSystem">HostSystem</see> for details
412         /// </summary>
413         public int ExternalFileAttributes 
414         {
415             get {
416                 if ((known & Known.ExternalAttributes) == 0) {
417                     return -1;
418                 } 
419                 else {
420                     return externalFileAttributes;
421                 }
422             }
423                         
424             set {
425                 externalFileAttributes = value;
426                 known |= Known.ExternalAttributes;
427             }
428         }
429
430         /// <summary>
431         /// Get the version made by for this entry or zero if unknown.
432         /// The value / 10 indicates the major version number, and 
433         /// the value mod 10 is the minor version number
434         /// </summary>
435         public int VersionMadeBy 
436         {
437             get { 
438                 return (versionMadeBy & 0xff);
439             }
440         }
441
442         /// <summary>
443         /// Get a value indicating this entry is for a DOS/Windows system.
444         /// </summary>
445         public bool IsDOSEntry
446         {
447             get {
448                 return ((HostSystem == ( int )HostSystemID.Msdos) ||
449                         (HostSystem == ( int )HostSystemID.WindowsNT));
450             }
451         }
452
453         /// <summary>
454         /// Test the external attributes for this <see cref="ZipEntry"/> to
455         /// see if the external attributes are Dos based (including WINNT and variants)
456         /// and match the values
457         /// </summary>
458         /// <param name="attributes">The attributes to test.</param>
459         /// <returns>Returns true if the external attributes are known to be DOS/Windows 
460         /// based and have the same attributes set as the value passed.</returns>
461         bool HasDosAttributes(int attributes)
462         {
463             bool result = false;
464             if ( (known & Known.ExternalAttributes) != 0 ) {
465                 if ( ((HostSystem == (int)HostSystemID.Msdos) || 
466                       (HostSystem == (int)HostSystemID.WindowsNT)) && 
467                      (ExternalFileAttributes & attributes) == attributes) {
468                          result = true;
469                      }
470             }
471             return result;
472         }
473
474         /// <summary>
475         /// Gets the compatability information for the <see cref="ExternalFileAttributes">external file attribute</see>
476         /// If the external file attributes are compatible with MS-DOS and can be read
477         /// by PKZIP for DOS version 2.04g then this value will be zero.  Otherwise the value
478         /// will be non-zero and identify the host system on which the attributes are compatible.
479         /// </summary>
480         ///             
481         /// <remarks>
482         /// The values for this as defined in the Zip File format and by others are shown below.  The values are somewhat
483         /// misleading in some cases as they are not all used as shown.  You should consult the relevant documentation
484         /// to obtain up to date and correct information.  The modified appnote by the infozip group is
485         /// particularly helpful as it documents a lot of peculiarities.  The document is however a little dated.
486         /// <list type="table">
487         /// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item>
488         /// <item>1 - Amiga</item>
489         /// <item>2 - OpenVMS</item>
490         /// <item>3 - Unix</item>
491         /// <item>4 - VM/CMS</item>
492         /// <item>5 - Atari ST</item>
493         /// <item>6 - OS/2 HPFS</item>
494         /// <item>7 - Macintosh</item>
495         /// <item>8 - Z-System</item>
496         /// <item>9 - CP/M</item>
497         /// <item>10 - Windows NTFS</item>
498         /// <item>11 - MVS (OS/390 - Z/OS)</item>
499         /// <item>12 - VSE</item>
500         /// <item>13 - Acorn Risc</item>
501         /// <item>14 - VFAT</item>
502         /// <item>15 - Alternate MVS</item>
503         /// <item>16 - BeOS</item>
504         /// <item>17 - Tandem</item>
505         /// <item>18 - OS/400</item>
506         /// <item>19 - OS/X (Darwin)</item>
507         /// <item>99 - WinZip AES</item>
508         /// <item>remainder - unused</item>
509         /// </list>
510         /// </remarks>
511         public int HostSystem 
512         {
513             get {
514                 return (versionMadeBy >> 8) & 0xff; 
515             }
516
517             set {
518                 versionMadeBy &= 0xff;
519                 versionMadeBy |= (ushort)((value & 0xff) << 8);
520             }
521         }
522                 
523         /// <summary>
524         /// Get minimum Zip feature version required to extract this entry
525         /// </summary>          
526         /// <remarks>
527         /// Minimum features are defined as:<br/>
528         /// 1.0 - Default value<br/>
529         /// 1.1 - File is a volume label<br/>
530         /// 2.0 - File is a folder/directory<br/>
531         /// 2.0 - File is compressed using Deflate compression<br/>
532         /// 2.0 - File is encrypted using traditional encryption<br/>
533         /// 2.1 - File is compressed using Deflate64<br/>
534         /// 2.5 - File is compressed using PKWARE DCL Implode<br/>
535         /// 2.7 - File is a patch data set<br/>
536         /// 4.5 - File uses Zip64 format extensions<br/>
537         /// 4.6 - File is compressed using BZIP2 compression<br/>
538         /// 5.0 - File is encrypted using DES<br/>
539         /// 5.0 - File is encrypted using 3DES<br/>
540         /// 5.0 - File is encrypted using original RC2 encryption<br/>
541         /// 5.0 - File is encrypted using RC4 encryption<br/>
542         /// 5.1 - File is encrypted using AES encryption<br/>
543         /// 5.1 - File is encrypted using corrected RC2 encryption<br/>
544         /// 5.1 - File is encrypted using corrected RC2-64 encryption<br/>
545         /// 6.1 - File is encrypted using non-OAEP key wrapping<br/>
546         /// 6.2 - Central directory encryption (not confirmed yet)<br/>
547         /// 6.3 - File is compressed using LZMA<br/>
548         /// 6.3 - File is compressed using PPMD+<br/>
549         /// 6.3 - File is encrypted using Blowfish<br/>
550         /// 6.3 - File is encrypted using Twofish<br/>
551         /// </remarks>
552         /// <seealso cref="CanDecompress"></seealso>
553         public int Version 
554         {
555             get {
556                 // Return recorded version if known.
557                 if (versionToExtract != 0) {
558                     return versionToExtract;
559                 } 
560                 else {
561                     int result = 10;
562                     if ( CentralHeaderRequiresZip64 ) {
563                         result = ZipConstants.VersionZip64;     
564                     }
565                     else if (CompressionMethod.Deflated == method) {
566                         result = 20;
567                     } 
568                     else if (IsDirectory == true) {
569                         result = 20;
570                     } 
571                     else if (IsCrypted == true) {
572                         result = 20;
573                     } 
574                     else if (HasDosAttributes(0x08) ) {
575                         result = 11;
576                     }
577                     return result;
578                 }
579             }
580         }
581
582         /// <summary>
583         /// Get a value indicating wether this entry can be decompressed by the library.
584         /// </summary>
585         /// <remarks>This is based on the <see cref="Version"></see> and 
586         /// wether the <see cref="IsCompressionMethodSupported()">compression method</see> is supported.</remarks>
587         public bool CanDecompress
588         {
589             get {
590                 return (Version <= ZipConstants.VersionMadeBy) &&
591                        ((Version == 10) ||
592                         (Version == 11) ||
593                         (Version == 20) ||
594                         (Version == 45)) &&
595                        IsCompressionMethodSupported();
596             }
597         }
598
599         /// <summary>
600         /// Force this entry to be recorded using Zip64 extensions.
601         /// </summary>
602         public void ForceZip64()
603         {
604             forceZip64_ = true;
605         }
606                 
607         /// <summary>
608         /// Get a value indicating wether Zip64 extensions were forced.
609         /// </summary>
610         /// <returns>A <see cref="bool"/> value of true if Zip64 extensions have been forced on; false if not.</returns>
611         public bool IsZip64Forced()
612         {
613             return forceZip64_;
614         }
615
616         /// <summary>
617         /// Gets a value indicating if the entry requires Zip64 extensions 
618         /// to store the full entry values.
619         /// </summary>
620         /// <value>A <see cref="bool"/> value of true if a local header requires Zip64 extensions; false if not.</value>
621         public bool LocalHeaderRequiresZip64 
622         {
623             get {
624                 bool result = forceZip64_;
625
626                 if ( !result ) {
627                     ulong trueCompressedSize = compressedSize;
628
629                     if ( (versionToExtract == 0) && IsCrypted ) {
630                         trueCompressedSize += ZipConstants.CryptoHeaderSize;
631                     }
632
633                     // TODO: A better estimation of the true limit based on compression overhead should be used
634                     // to determine when an entry should use Zip64.
635                     result =
636                         ((this.size >= uint.MaxValue) || (trueCompressedSize >= uint.MaxValue)) &&
637                         ((versionToExtract == 0) || (versionToExtract >= ZipConstants.VersionZip64));
638                 }
639
640                 return result;
641             }
642         }
643                 
644         /// <summary>
645         /// Get a value indicating wether the central directory entry requires Zip64 extensions to be stored.
646         /// </summary>
647         public bool CentralHeaderRequiresZip64
648         {
649             get {
650                 return LocalHeaderRequiresZip64 || (offset >= uint.MaxValue);
651             }
652         }
653                 
654         /// <summary>
655         /// Get/Set DosTime value.
656         /// </summary>
657         /// <remarks>
658         /// The MS-DOS date format can only represent dates between 1/1/1980 and 12/31/2107.
659         /// </remarks>
660         public long DosTime 
661         {
662             get {
663                 if ((known & Known.Time) == 0) {
664                     return 0;
665                 } 
666                 else {
667                     return dosTime;
668                 }
669             }
670                         
671             set {
672                 unchecked {
673                     dosTime = (uint)value;
674                 }
675
676                 known |= Known.Time;
677             }
678         }
679                         
680         /// <summary>
681         /// Gets/Sets the time of last modification of the entry.
682         /// </summary>
683         /// <remarks>
684         /// The <see cref="DosTime"></see> property is updated to match this as far as possible.
685         /// </remarks>
686         public DateTime DateTime 
687         {
688             get {
689                 var sec  = Math.Min(59, (int) (2 * (dosTime & 0x1f)));
690                 var min  = Math.Min(59, (int) ((dosTime >> 5) & 0x3f));
691                 var hrs  = Math.Min(23, (int) ((dosTime >> 11) & 0x1f));
692                 var mon  = Math.Max(1, Math.Min(12, (int) ((dosTime >> 21) & 0xf)));
693                 var year = ((dosTime >> 25) & 0x7f) + 1980;
694                 var day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((dosTime >> 16) & 0x1f)));
695                 return new DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec);
696             }
697             set {
698                 var year = (uint) value.Year;
699                 var month = (uint) value.Month;
700                 var day = (uint) value.Day;
701                 var hour = (uint) value.Hour;
702                 var minute = (uint) value.Minute;
703                 var second = (uint) value.Second;
704                                 
705                 if ( year < 1980 ) {
706                     year = 1980;
707                     month = 1;
708                     day = 1;
709                     hour = 0;
710                     minute = 0;
711                     second = 0;
712                 }
713                 else if ( year > 2107 ) {
714                     year = 2107;
715                     month = 12;
716                     day = 31;
717                     hour = 23;
718                     minute = 59;
719                     second = 59;
720                 }
721                                 
722                 DosTime = ((year - 1980) & 0x7f) << 25 | 
723                           (month << 21) |
724                           (day << 16) |
725                           (hour << 11) |
726                           (minute << 5) |
727                           (second >> 1);
728             }
729         }
730                 
731         /// <summary>
732         /// Returns the entry name.
733         /// </summary>
734         /// <remarks>
735         /// The unix naming convention is followed.
736         /// Path components in the entry should always separated by forward slashes ('/').
737         /// Dos device names like C: should also be removed.
738         /// See the <see cref="ZipNameTransform"/> class, or <see cref="CleanName(string)"/>
739         ///</remarks>
740         public string Name 
741         {
742             get {
743                 return name;
744             }
745         }
746                 
747         /// <summary>
748         /// Gets/Sets the size of the uncompressed data.
749         /// </summary>
750         /// <returns>
751         /// The size or -1 if unknown.
752         /// </returns>
753         /// <remarks>Setting the size before adding an entry to an archive can help
754         /// avoid compatability problems with some archivers which dont understand Zip64 extensions.</remarks>
755         public long Size 
756         {
757             get {
758                 return (known & Known.Size) != 0 ? (long)size : -1L;
759             }
760             set {
761                 this.size  = (ulong)value;
762                 this.known |= Known.Size;
763             }
764         }
765                 
766         /// <summary>
767         /// Gets/Sets the size of the compressed data.
768         /// </summary>
769         /// <returns>
770         /// The compressed entry size or -1 if unknown.
771         /// </returns>
772         public long CompressedSize 
773         {
774             get {
775                 return (known & Known.CompressedSize) != 0 ? (long)compressedSize : -1L;
776             }
777             set {
778                 this.compressedSize = (ulong)value;
779                 this.known |= Known.CompressedSize;
780             }
781         }
782
783         /// <summary>
784         /// Gets/Sets the crc of the uncompressed data.
785         /// </summary>
786         /// <exception cref="System.ArgumentOutOfRangeException">
787         /// Crc is not in the range 0..0xffffffffL
788         /// </exception>
789         /// <returns>
790         /// The crc value or -1 if unknown.
791         /// </returns>
792         public long Crc 
793         {
794             get {
795                 return (known & Known.Crc) != 0 ? crc & 0xffffffffL : -1L;
796             }
797             set {
798                 if (((ulong)crc & 0xffffffff00000000L) != 0) {
799                     throw new ArgumentOutOfRangeException("value");
800                 }
801                 this.crc = (uint)value;
802                 this.known |= Known.Crc;
803             }
804         }
805                 
806         /// <summary>
807         /// Gets/Sets the compression method. Only Deflated and Stored are supported.
808         /// </summary>
809         /// <returns>
810         /// The compression method for this entry
811         /// </returns>
812         /// <see cref="Zip.CompressionMethod.Deflated"/>
813         /// <see cref="Zip.CompressionMethod.Stored"/>
814         public CompressionMethod CompressionMethod {
815             get {
816                 return method;
817             }
818
819             set {
820                 if ( !IsCompressionMethodSupported(value) ) {
821                     throw new NotSupportedException("Compression method not supported");
822                 }
823                 this.method = value;
824             }
825         }
826                 
827         /// <summary>
828         /// Gets/Sets the extra data.
829         /// </summary>
830         /// <exception cref="System.ArgumentOutOfRangeException">
831         /// Extra data is longer than 64KB (0xffff) bytes.
832         /// </exception>
833         /// <returns>
834         /// Extra data or null if not set.
835         /// </returns>
836         public byte[] ExtraData {
837                         
838             get {
839 // TODO: This is slightly safer but less efficient.  Think about wether it should change.
840 //                              return (byte[]) extra.Clone();
841                 return extra;
842             }
843
844             set {
845                 if (value == null) {
846                     extra = null;
847                 }
848                 else {
849                     if (value.Length > 0xffff) {
850                         throw new System.ArgumentOutOfRangeException("value");
851                     }
852                                 
853                     extra = new byte[value.Length];
854                     Array.Copy(value, 0, extra, 0, value.Length);
855                 }
856             }
857         }
858                 
859         /// <summary>
860         /// Process extra data fields updating the entry based on the contents.
861         /// </summary>
862         /// <param name="localHeader">True if the extra data fields should be handled
863         /// for a local header, rather than for a central header.
864         /// </param>
865         internal void ProcessExtraData(bool localHeader)
866         {
867             ZipExtraData extraData = new ZipExtraData(this.extra);
868
869             if ( extraData.Find(0x0001) ) {
870                 if ( (versionToExtract & 0xff) < ZipConstants.VersionZip64 ) {
871                     throw new ZipException("Zip64 Extended information found but version is not valid");
872                 }
873
874                 // The recorded size will change but remember that this is zip64.
875                 forceZip64_ = true;
876
877                 if ( extraData.ValueLength < 4 ) {
878                     throw new ZipException("Extra data extended Zip64 information length is invalid");
879                 }
880
881                 if ( localHeader || (size == uint.MaxValue) ) {
882                     size = (ulong)extraData.ReadLong();
883                 }
884
885                 if ( localHeader || (compressedSize == uint.MaxValue) ) {
886                     compressedSize = (ulong)extraData.ReadLong();
887                 }
888
889                 if ( !localHeader && (offset == uint.MaxValue) ) {
890                     offset = extraData.ReadLong();
891                 }
892             }
893             else {
894                 if ( 
895                     ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) &&
896                     ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))
897                     ) {
898                         throw new ZipException("Zip64 Extended information required but is missing.");
899                     }
900             }
901
902             if ( extraData.Find(10) ) {
903                 // No room for any tags.
904                 if ( extraData.ValueLength < 8 ) {
905                     throw new ZipException("NTFS Extra data invalid");
906                 }
907
908                 extraData.ReadInt(); // Reserved
909
910                 while ( extraData.UnreadCount >= 4 ) {
911                     int ntfsTag = extraData.ReadShort();
912                     int ntfsLength = extraData.ReadShort();
913                     if ( ntfsTag == 1 ) {
914                         if ( ntfsLength >= 24 ) {
915                             long lastModification = extraData.ReadLong();
916                             long lastAccess = extraData.ReadLong();
917                             long createTime = extraData.ReadLong();
918
919                             DateTime = System.DateTime.FromFileTime(lastModification);
920                         }
921                         break;
922                     }
923                     else {
924                         // An unknown NTFS tag so simply skip it.
925                         extraData.Skip(ntfsLength);
926                     }
927                 }
928             }
929             else if ( extraData.Find(0x5455) ) {
930                 int length = extraData.ValueLength;     
931                 int flags = extraData.ReadByte();
932                                         
933                 // Can include other times but these are ignored.  Length of data should
934                 // actually be 1 + 4 * no of bits in flags.
935                 if ( ((flags & 1) != 0) && (length >= 5) ) {
936                     int iTime = extraData.ReadInt();
937
938                     DateTime = (new System.DateTime ( 1970, 1, 1, 0, 0, 0 ).ToUniversalTime() +
939                                 new TimeSpan ( 0, 0, 0, iTime, 0 )).ToLocalTime();
940                 }
941             }
942         }
943
944         /// <summary>
945         /// Gets/Sets the entry comment.
946         /// </summary>
947         /// <exception cref="System.ArgumentOutOfRangeException">
948         /// If comment is longer than 0xffff.
949         /// </exception>
950         /// <returns>
951         /// The comment or null if not set.
952         /// </returns>
953         /// <remarks>
954         /// A comment is only available for entries when read via the <see cref="ZipFile"/> class.
955         /// The <see cref="ZipInputStream"/> class doesnt have the comment data available.
956         /// </remarks>
957         public string Comment {
958             get {
959                 return comment;
960             }
961             set {
962                 // This test is strictly incorrect as the length is in characters
963                 // while the storage limit is in bytes.
964                 // While the test is partially correct in that a comment of this length or greater 
965                 // is definitely invalid, shorter comments may also have an invalid length
966                 // where there are multi-byte characters
967                 // The full test is not possible here however as the code page to apply conversions with
968                 // isnt available.
969                 if ( (value != null) && (value.Length > 0xffff) ) {
970                     throw new ArgumentOutOfRangeException("value", "cannot exceed 65535");
971                 }
972                                 
973                 comment = value;
974             }
975         }
976                 
977         /// <summary>
978         /// Gets a value indicating if the entry is a directory.
979         /// however.
980         /// </summary>
981         /// <remarks>
982         /// A directory is determined by an entry name with a trailing slash '/'.
983         /// The external file attributes can also indicate an entry is for a directory.
984         /// Currently only dos/windows attributes are tested in this manner.
985         /// The trailing slash convention should always be followed.
986         /// </remarks>
987         public bool IsDirectory 
988         {
989             get {
990                 int nameLength = name.Length;
991                 bool result = 
992                     ((nameLength > 0) && 
993                      ((name[nameLength - 1] == '/') || (name[nameLength - 1] == '\\'))) ||
994                     HasDosAttributes(16)
995                     ;
996                 return result;
997             }
998         }
999                 
1000         /// <summary>
1001         /// Get a value of true if the entry appears to be a file; false otherwise
1002         /// </summary>
1003         /// <remarks>
1004         /// This only takes account of DOS/Windows attributes.  Other operating systems are ignored.
1005         /// For linux and others the result may be incorrect.
1006         /// </remarks>
1007         public bool IsFile
1008         {
1009             get {
1010                 return !IsDirectory && !HasDosAttributes(8);
1011             }
1012         }
1013                 
1014         /// <summary>
1015         /// Test entry to see if data can be extracted.
1016         /// </summary>
1017         /// <returns>Returns true if data can be extracted for this entry; false otherwise.</returns>
1018         public bool IsCompressionMethodSupported()
1019         {
1020             return IsCompressionMethodSupported(CompressionMethod);
1021         }
1022                 
1023         #region ICloneable Members
1024         /// <summary>
1025         /// Creates a copy of this zip entry.
1026         /// </summary>
1027         /// <returns>An <see cref="Object"/> that is a copy of the current instance.</returns>
1028         public object Clone()
1029         {
1030             ZipEntry result = (ZipEntry)this.MemberwiseClone();
1031
1032             // Ensure extra data is unique if it exists.
1033             if ( extra != null ) {
1034                 result.extra = new byte[extra.Length];
1035                 Array.Copy(extra, 0, result.extra, 0, extra.Length);
1036             }
1037
1038             return result;
1039         }
1040                 
1041         #endregion
1042
1043         /// <summary>
1044         /// Gets a string representation of this ZipEntry.
1045         /// </summary>
1046         /// <returns>A readable textual representation of this <see cref="ZipEntry"/></returns>
1047         public override string ToString()
1048         {
1049             return name;
1050         }
1051
1052         /// <summary>
1053         /// Test a <see cref="CompressionMethod">compression method</see> to see if this library
1054         /// supports extracting data compressed with that method
1055         /// </summary>
1056         /// <param name="method">The compression method to test.</param>
1057         /// <returns>Returns true if the compression method is supported; false otherwise</returns>
1058         public static bool IsCompressionMethodSupported(CompressionMethod method)
1059         {
1060             return
1061                 ( method == CompressionMethod.Deflated ) ||
1062                 ( method == CompressionMethod.Stored );
1063         }
1064                 
1065         /// <summary>
1066         /// Cleans a name making it conform to Zip file conventions.
1067         /// Devices names ('c:\') and UNC share names ('\\server\share') are removed
1068         /// and forward slashes ('\') are converted to back slashes ('/').
1069         /// Names are made relative by trimming leading slashes which is compatible
1070         /// with the ZIP naming convention.
1071         /// </summary>
1072         /// <param name="name">The name to clean</param>
1073         /// <returns>The 'cleaned' name.</returns>
1074         /// <remarks>
1075         /// The <seealso cref="ZipNameTransform">Zip name transform</seealso> class is more flexible.
1076         /// </remarks>
1077         public static string CleanName(string name)
1078         {
1079             if (name == null) {
1080                 return string.Empty;
1081             }
1082                         
1083             if (Path.IsPathRooted(name) == true) {
1084                 // NOTE:
1085                 // for UNC names...  \\machine\share\zoom\beet.txt gives \zoom\beet.txt
1086                 name = name.Substring(Path.GetPathRoot(name).Length);
1087             }
1088
1089             name = name.Replace(@"\", "/");
1090                         
1091             while ( (name.Length > 0) && (name[0] == '/')) {
1092                 name = name.Remove(0, 1);
1093             }
1094             return name;
1095         }
1096
1097         #region Instance Fields
1098         Known known;
1099         int    externalFileAttributes = -1;     // contains external attributes (O/S dependant)
1100                 
1101         ushort versionMadeBy;                                   // Contains host system and version information
1102         // only relevant for central header entries
1103                 
1104         string name;
1105         ulong  size;
1106         ulong  compressedSize;
1107         ushort versionToExtract;                // Version required to extract (library handles <= 2.0)
1108         uint   crc;
1109         uint   dosTime;
1110                 
1111         CompressionMethod  method = CompressionMethod.Deflated;
1112         byte[] extra;
1113         string comment;
1114                 
1115         int flags;                             // general purpose bit flags
1116
1117         long zipFileIndex = -1;                // used by ZipFile
1118         long offset;                           // used by ZipFile and ZipOutputStream
1119                 
1120         bool forceZip64_;
1121         byte cryptoCheckValue_;
1122         #endregion
1123     }
1124 }