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 
43 namespace ICSharpCode.SharpZipLib.Zip
44 {
45 
46 	/// <summary>
47 	/// This class represents an entry in a zip archive.  This can be a file
48 	/// or a directory
49 	/// ZipFile and ZipInputStream will give you instances of this class as
50 	/// information about the members in an archive.  ZipOutputStream
51 	/// uses an instance of this class when creating an entry in a Zip file.
52 	/// <br/>
53 	/// <br/>Author of the original java version : Jochen Hoenicke
54 	/// </summary>
55 	[System.ObsoleteAttribute("This assembly has been deprecated. Please use https://www.nuget.org/packages/SharpZipLib/ instead.")]
56 	public class ZipEntry : ICloneable
57 	{
58 		static int KNOWN_SIZE               = 1;
59 		static int KNOWN_CSIZE              = 2;
60 		static int KNOWN_CRC                = 4;
61 		static int KNOWN_TIME               = 8;
62 		static int KNOWN_EXTERN_ATTRIBUTES 	= 16;
63 
64 		ushort known = 0;                       // Bit flags made up of above bits
65 		int    externalFileAttributes = -1;     // contains external attributes (os dependant)
66 
67 		ushort versionMadeBy;                   // Contains host system and version information
68 		                                        // only relevant for central header entries
69 
70 		string name;
71 		ulong  size;
72 		ulong  compressedSize;
73 		ushort versionToExtract;                // Version required to extract (library handles <= 2.0)
74 		uint   crc;
75 		uint   dosTime;
76 
77 		CompressionMethod  method = CompressionMethod.Deflated;
78 		byte[] extra = null;
79 		string comment = null;
80 
81 		int flags;                             // general purpose bit flags
82 
83 		int zipFileIndex = -1;                 // used by ZipFile
84 		int offset;                            // used by ZipFile and ZipOutputStream
85 
86 		/// <summary>
87 		/// Get/Set flag indicating if entry is encrypted.
88 		/// A simple helper routine to aid interpretation of <see cref="Flags">flags</see>
89 		/// </summary>
90 		public bool IsCrypted {
91 			get {
92 				return (flags & 1) != 0;
93 			}
94 			set {
95 				if (value) {
96 					flags |= 1;
97 				} else {
98 					flags &= ~1;
99 				}
100 			}
101 		}
102 
103 		/// <summary>
104 		/// Get/Set general purpose bit flag for entry
105 		/// </summary>
106 		/// <remarks>
107 		/// General purpose bit flag<br/>
108 		/// Bit 0: If set, indicates the file is encrypted<br/>
109 		/// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating<br/>
110 		/// Imploding:<br/>
111 		/// Bit 1 if set indicates an 8K sliding dictionary was used.  If clear a 4k dictionary was used<br/>
112 		/// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise<br/>
113 		/// <br/>
114 		/// Deflating:<br/>
115 		///   Bit 2    Bit 1<br/>
116 		///     0        0       Normal compression was used<br/>
117 		///     0        1       Maximum compression was used<br/>
118 		///     1        0       Fast compression was used<br/>
119 		///     1        1       Super fast compression was used<br/>
120 		/// <br/>
121 		/// Bit 3: If set, the fields crc-32, compressed size
122 		/// and uncompressed size are were not able to be written during zip file creation
123 		/// The correct values are held in a data descriptor immediately following the compressed data. <br/>
124 		/// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/>
125 		/// Bit 5: If set indicates the file contains compressed patch data<br/>
126 		/// Bit 6: If set indicates strong encryption was used.<br/>
127 		/// Bit 7-15: Unused or reserved<br/>
128 		/// </remarks>
129 		public int Flags {
130 			get {
131 				return flags;
132 			}
133 			set {
134 				flags = value;
135 			}
136 		}
137 
138 
139 		/// <summary>
140 		/// Get/Set index of this entry in Zip file
141 		/// </summary>
142 		public int ZipFileIndex {
143 			get {
144 				return zipFileIndex;
145 			}
146 			set {
147 				zipFileIndex = value;
148 			}
149 		}
150 
151 		/// <summary>
152 		/// Get/set offset for use in central header
153 		/// </summary>
154 		public int Offset {
155 			get {
156 				return offset;
157 			}
158 			set {
159 				if (((ulong)value & 0xFFFFFFFF00000000L) != 0) {
160 					throw new ArgumentOutOfRangeException("Offset");
161 				}
162 				offset = value;
163 			}
164 		}
165 
166 
167 		/// <summary>
168 		/// Get/Set external file attributes as an integer.
169 		/// The values of this are operating system dependant see
170 		/// <see cref="HostSystem">HostSystem</see> for details
171 		/// </summary>
172 		public int ExternalFileAttributes {
173 			get {
174 				if ((known & KNOWN_EXTERN_ATTRIBUTES) == 0) {
175 					return -1;
176 				} else {
177 					return externalFileAttributes;
178 				}
179 			}
180 
181 			set {
182 				externalFileAttributes = value;
183 				known |= (ushort)KNOWN_EXTERN_ATTRIBUTES;
184 			}
185 		}
186 
187 		/// <summary>
188 		/// Get the version made by for this entry or zero if unknown.
189 		/// The value / 10 indicates the major version number, and
190 		/// the value mod 10 is the minor version number
191 		/// </summary>
192 		public int VersionMadeBy {
193 			get {
194 				return versionMadeBy & 0xff;
195 			}
196 		}
197 
198 		/// <summary>
199 		/// Gets the compatability information for the <see cref="ExternalFileAttributes">external file attribute</see>
200 		/// If the external file attributes are compatible with MS-DOS and can be read
201 		/// by PKZIP for DOS version 2.04g then this value will be zero.  Otherwise the value
202 		/// will be non-zero and identify the host system on which the attributes are compatible.
203 		/// </summary>
204 		///
205 		/// <remarks>
206 		/// The values for this as defined in the Zip File format and by others are shown below.  The values are somewhat
207 		/// misleading in some cases as they are not all used as shown.  You should consult the relevant documentation
208 		/// to obtain up to date and correct information.  The modified appnote by the infozip group is
209 		/// particularly helpful as it documents a lot of peculiarities.  The document is however a little dated.
210 		/// <list type="table">
211 		/// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item>
212 		/// <item>1 - Amiga</item>
213 		/// <item>2 - OpenVMS</item>
214 		/// <item>3 - Unix</item>
215 		/// <item>4 - VM/CMS</item>
216 		/// <item>5 - Atari ST</item>
217 		/// <item>6 - OS/2 HPFS</item>
218 		/// <item>7 - Macintosh</item>
219 		/// <item>8 - Z-System</item>
220 		/// <item>9 - CP/M</item>
221 		/// <item>10 - Windows NTFS</item>
222 		/// <item>11 - MVS (OS/390 - Z/OS)</item>
223 		/// <item>12 - VSE</item>
224 		/// <item>13 - Acorn Risc</item>
225 		/// <item>14 - VFAT</item>
226 		/// <item>15 - Alternate MVS</item>
227 		/// <item>16 - BeOS</item>
228 		/// <item>17 - Tandem</item>
229 		/// <item>18 - OS/400</item>
230 		/// <item>19 - OS/X (Darwin)</item>
231 		/// <item>99 - WinZip AES</item>
232 		/// <item>remainder - unused</item>
233 		/// </list>
234 		/// </remarks>
235 
236 		public int HostSystem {
237 			get { return (versionMadeBy >> 8) & 0xff; }
238 		}
239 
240 		/// <summary>
241 		/// Creates a zip entry with the given name.
242 		/// </summary>
243 		/// <param name="name">
244 		/// The name for this entry. Can include directory components.
245 		/// The convention for names is 'unix'  style paths with no device names and
246 		/// path elements separated by '/' characters.  This is not enforced see <see cref="CleanName">CleanName</see>
247 		/// on how to ensure names are valid if this is desired.
248 		/// </param>
249 		/// <exception cref="ArgumentNullException">
250 		/// The name passed is null
251 		/// </exception>
ZipEntry(string name)252 		public ZipEntry(string name) : this(name, 0, ZipConstants.VERSION_MADE_BY)
253 		{
254 		}
255 
256 		/// <summary>
257 		/// Creates a zip entry with the given name and version required to extract
258 		/// </summary>
259 		/// <param name="name">
260 		/// The name for this entry. Can include directory components.
261 		/// The convention for names is 'unix'  style paths with no device names and
262 		/// path elements separated by '/' characters.  This is not enforced see <see cref="CleanName">CleanName</see>
263 		/// on how to ensure names are valid if this is desired.
264 		/// </param>
265 		/// <param name="versionRequiredToExtract">
266 		/// The minimum 'feature version' required this entry
267 		/// </param>
268 		/// <exception cref="ArgumentNullException">
269 		/// The name passed is null
270 		/// </exception>
ZipEntry(string name, int versionRequiredToExtract)271 		internal ZipEntry(string name, int versionRequiredToExtract) : this(name, versionRequiredToExtract, ZipConstants.VERSION_MADE_BY)
272 		{
273 		}
274 
275 		/// <summary>
276 		/// Initializes an entry with the given name and made by information
277 		/// </summary>
278 		/// <param name="name">Name for this entry</param>
279 		/// <param name="madeByInfo">Version and HostSystem Information</param>
280 		/// <param name="versionRequiredToExtract">Minimum required zip feature version required to extract this entry</param>
281 		/// <exception cref="ArgumentNullException">
282 		/// The name passed is null
283 		/// </exception>
284 		/// <exception cref="ArgumentOutOfRangeException">
285 		/// versionRequiredToExtract should be 0 (auto-calculate) or > 10
286 		/// </exception>
287 		/// <remarks>
288 		/// This constructor is used by the ZipFile class when reading from the central header
289 		/// It is not generally useful, use the constructor specifying the name only.
290 		/// </remarks>
ZipEntry(string name, int versionRequiredToExtract, int madeByInfo)291 		internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo)
292 		{
293 			if (name == null)  {
294 				throw new System.ArgumentNullException("ZipEntry name");
295 			}
296 
297 			if ( name.Length == 0 ) {
298 				throw new ArgumentException("ZipEntry name is empty");
299 			}
300 
301 			if (versionRequiredToExtract != 0 && versionRequiredToExtract < 10) {
302 				throw new ArgumentOutOfRangeException("versionRequiredToExtract");
303 			}
304 
305 			this.DateTime         = System.DateTime.Now;
306 			this.name             = name;
307 			this.versionMadeBy    = (ushort)madeByInfo;
308 			this.versionToExtract = (ushort)versionRequiredToExtract;
309 		}
310 
311 		/// <summary>
312 		/// Creates a copy of the given zip entry.
313 		/// </summary>
314 		/// <param name="e">
315 		/// The entry to copy.
316 		/// </param>
ZipEntry(ZipEntry e)317 		public ZipEntry(ZipEntry e)
318 		{
319 			known                  = e.known;
320 			name                   = e.name;
321 			size                   = e.size;
322 			compressedSize         = e.compressedSize;
323 			crc                    = e.crc;
324 			dosTime                = e.dosTime;
325 			method                 = e.method;
326 			ExtraData              = e.ExtraData;     // Note use of property ensuring data is unique
327 			comment                = e.comment;
328 			versionToExtract       = e.versionToExtract;
329 			versionMadeBy          = e.versionMadeBy;
330 			externalFileAttributes = e.externalFileAttributes;
331 			flags                  = e.flags;
332 
333 			zipFileIndex           = -1;
334 			offset                 = 0;
335 		}
336 
337 		/// <summary>
338 		/// Get minimum Zip feature version required to extract this entry
339 		/// </summary>
340 		/// <remarks>
341 		/// Minimum features are defined as:<br/>
342 		/// 1.0 - Default value<br/>
343 		/// 1.1 - File is a volume label<br/>
344 		/// 2.0 - File is a folder/directory<br/>
345 		/// 2.0 - File is compressed using Deflate compression<br/>
346 		/// 2.0 - File is encrypted using traditional encryption<br/>
347 		/// 2.1 - File is compressed using Deflate64<br/>
348 		/// 2.5 - File is compressed using PKWARE DCL Implode<br/>
349 		/// 2.7 - File is a patch data set<br/>
350 		/// 4.5 - File uses Zip64 format extensions<br/>
351 		/// 4.6 - File is compressed using BZIP2 compression<br/>
352 		/// 5.0 - File is encrypted using DES<br/>
353 		/// 5.0 - File is encrypted using 3DES<br/>
354 		/// 5.0 - File is encrypted using original RC2 encryption<br/>
355 		/// 5.0 - File is encrypted using RC4 encryption<br/>
356 		/// 5.1 - File is encrypted using AES encryption<br/>
357 		/// 5.1 - File is encrypted using corrected RC2 encryption<br/>
358 		/// 5.1 - File is encrypted using corrected RC2-64 encryption<br/>
359 		/// 6.1 - File is encrypted using non-OAEP key wrapping<br/>
360 		/// 6.2 - Central directory encryption (not confirmed yet)<br/>
361 		/// </remarks>
362 		public int Version {
363 			get {
364 				if (versionToExtract != 0) {
365 					return versionToExtract;
366 				} else {
367 					int result = 10;
368 					if (CompressionMethod.Deflated == method) {
369 						result = 20;
370 					} else if (IsDirectory == true) {
371 						result = 20;
372 					} else if (IsCrypted == true) {
373 						result = 20;
374 					} else if ((known & KNOWN_EXTERN_ATTRIBUTES) != 0 && (externalFileAttributes & 0x08) != 0) {
375 						result = 11;
376 					}
377 					return result;
378 				}
379 			}
380 		}
381 
382 		/// <summary>
383 		/// Gets a value indicating if the entry requires Zip64 extensions to be stored
384 		/// </summary>
385 		public bool RequiresZip64 {
386 			get {
387 				return (this.size > uint.MaxValue) || (this.compressedSize > uint.MaxValue);
388 			}
389 		}
390 
391 		/// <summary>
392 		/// Get/Set DosTime
393 		/// </summary>
394 		public long DosTime {
395 			get {
396 				if ((known & KNOWN_TIME) == 0) {
397 					return 0;
398 				} else {
399 					return dosTime;
400 				}
401 			}
402 			set {
403 				this.dosTime = (uint)value;
404 				known |= (ushort)KNOWN_TIME;
405 			}
406 		}
407 
408 
409 		/// <summary>
410 		/// Gets/Sets the time of last modification of the entry.
411 		/// </summary>
412 		public DateTime DateTime {
413 			get {
414 				// Although technically not valid some archives have dates set to zero.
415 				// This mimics some archivers handling and is a good a cludge as any probably.
416 				if ( dosTime == 0 ) {
417 					return DateTime.Now;
418 				}
419 				else {
420 					uint sec  = 2 * (dosTime & 0x1f);
421 					uint min  = (dosTime >> 5) & 0x3f;
422 					uint hrs  = (dosTime >> 11) & 0x1f;
423 					uint day  = (dosTime >> 16) & 0x1f;
424 					uint mon  = ((dosTime >> 21) & 0xf);
425 					uint year = ((dosTime >> 25) & 0x7f) + 1980;
426 					return new System.DateTime((int)year, (int)mon, (int)day, (int)hrs, (int)min, (int)sec);
427 				}
428 			}
429 			set {
430 				DosTime = ((uint)value.Year - 1980 & 0x7f) << 25 |
431 				          ((uint)value.Month) << 21 |
432 				          ((uint)value.Day) << 16 |
433 				          ((uint)value.Hour) << 11 |
434 				          ((uint)value.Minute) << 5 |
435 				          ((uint)value.Second) >> 1;
436 			}
437 		}
438 
439 		/// <summary>
440 		/// Returns the entry name.  The path components in the entry should
441 		/// always separated by slashes ('/').  Dos device names like C: should also
442 		/// be removed.  See <see cref="CleanName">CleanName</see>.
443 		/// </summary>
444 		public string Name {
445 			get {
446 				return name;
447 			}
448 		}
449 
450 		/// <summary>
451 		/// Cleans a name making it conform to Zip file conventions.
452 		/// Devices names ('c:\') and UNC share names ('\\server\share') are removed
453 		/// and forward slashes ('\') are converted to back slashes ('/').
454 		/// </summary>
455 		/// <param name="name">Name to clean</param>
456 		/// <param name="relativePath">Make names relative if true or absolute if false</param>
CleanName(string name, bool relativePath)457 		static public string CleanName(string name, bool relativePath)
458 		{
459 			if (name == null) {
460 				return "";
461 			}
462 
463 			if (Path.IsPathRooted(name) == true) {
464 				// NOTE:
465 				// for UNC names...  \\machine\share\zoom\beet.txt gives \zoom\beet.txt
466 				name = name.Substring(Path.GetPathRoot(name).Length);
467 			}
468 
469 			name = name.Replace(@"\", "/");
470 
471 			if (relativePath == true) {
472 				if (name.Length > 0 && (name[0] == Path.AltDirectorySeparatorChar || name[0] == Path.DirectorySeparatorChar)) {
473 					name = name.Remove(0, 1);
474 				}
475 			} else {
476 				if (name.Length > 0 && name[0] != Path.AltDirectorySeparatorChar && name[0] != Path.DirectorySeparatorChar) {
477 					name = name.Insert(0, "/");
478 				}
479 			}
480 			return name;
481 		}
482 
483 		/// <summary>
484 		/// Cleans a name making it conform to Zip file conventions.
485 		/// Devices names ('c:\') and UNC share names ('\\server\share') are removed
486 		/// and forward slashes ('\') are converted to back slashes ('/').
487 		/// Names are made relative by trimming leading slashes which is compatible
488 		/// with Windows-XPs built in Zip file handling.
489 		/// </summary>
490 		/// <param name="name">Name to clean</param>
CleanName(string name)491 		static public string CleanName(string name)
492 		{
493 			return CleanName(name, true);
494 		}
495 
496 		/// <summary>
497 		/// Gets/Sets the size of the uncompressed data.
498 		/// </summary>
499 		/// <exception cref="System.ArgumentOutOfRangeException">
500 		/// If the size is not in the range 0..0xffffffffL
501 		/// </exception>
502 		/// <returns>
503 		/// The size or -1 if unknown.
504 		/// </returns>
505 		public long Size {
506 			get {
507 				return (known & KNOWN_SIZE) != 0 ? (long)size : -1L;
508 			}
509 			set {
510 				if (((ulong)value & 0xFFFFFFFF00000000L) != 0) {
511 					throw new ArgumentOutOfRangeException("size");
512 				}
513 				this.size  = (ulong)value;
514 				this.known |= (ushort)KNOWN_SIZE;
515 			}
516 		}
517 
518 		/// <summary>
519 		/// Gets/Sets the size of the compressed data.
520 		/// </summary>
521 		/// <exception cref="System.ArgumentOutOfRangeException">
522 		/// Size is not in the range 0..0xffffffff
523 		/// </exception>
524 		/// <returns>
525 		/// The size or -1 if unknown.
526 		/// </returns>
527 		public long CompressedSize {
528 			get {
529 				return (known & KNOWN_CSIZE) != 0 ? (long)compressedSize : -1L;
530 			}
531 			set {
532 				if (((ulong)value & 0xffffffff00000000L) != 0) {
533 					throw new ArgumentOutOfRangeException();
534 				}
535 				this.compressedSize = (ulong)value;
536 				this.known |= (ushort)KNOWN_CSIZE;
537 			}
538 		}
539 
540 		/// <summary>
541 		/// Gets/Sets the crc of the uncompressed data.
542 		/// </summary>
543 		/// <exception cref="System.ArgumentOutOfRangeException">
544 		/// Crc is not in the range 0..0xffffffffL
545 		/// </exception>
546 		/// <returns>
547 		/// The crc value or -1 if unknown.
548 		/// </returns>
549 		public long Crc {
550 			get {
551 				return (known & KNOWN_CRC) != 0 ? crc & 0xffffffffL : -1L;
552 			}
553 			set {
554 				if (((ulong)crc & 0xffffffff00000000L) != 0) {
555 					throw new ArgumentOutOfRangeException();
556 				}
557 				this.crc = (uint)value;
558 				this.known |= (ushort)KNOWN_CRC;
559 			}
560 		}
561 
562 		/// <summary>
563 		/// Gets/Sets the compression method. Only Deflated and Stored are supported.
564 		/// </summary>
565 		/// <returns>
566 		/// The compression method for this entry
567 		/// </returns>
568 		/// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Deflated"/>
569 		/// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Stored"/>
570 		public CompressionMethod CompressionMethod {
571 			get {
572 				return method;
573 			}
574 			set {
575 				this.method = value;
576 			}
577 		}
578 
579 		/// <summary>
580 		/// Gets/Sets the extra data.
581 		/// </summary>
582 		/// <exception cref="System.ArgumentOutOfRangeException">
583 		/// Extra data is longer than 0xffff bytes.
584 		/// </exception>
585 		/// <returns>
586 		/// Extra data or null if not set.
587 		/// </returns>
588 		public byte[] ExtraData {
589 			get {
590 				return extra;
591 			}
592 			set {
593 				if (value == null) {
594 					this.extra = null;
595 					return;
596 				}
597 
598 				if (value.Length > 0xffff) {
599 					throw new System.ArgumentOutOfRangeException();
600 				}
601 
602 				this.extra = new byte[value.Length];
603 				Array.Copy(value, 0, this.extra, 0, value.Length);
604 
605 				try {
606 					int pos = 0;
607 					while (pos < extra.Length) {
608 						int sig = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8;
609 						int len = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8;
610 
611 						if (len < 0 || pos + len > extra.Length) {
612 							// This is still lenient but the extra data is corrupt
613 							// TODO: drop the extra data? or somehow indicate to user
614 							// there is a problem...
615 							break;
616 						}
617 
618 						if (sig == 0x5455) {
619 							// extended time stamp, unix format by Rainer Prem <Rainer@Prem.de>
620 							int flags = extra[pos];
621 							// Can include other times but these are ignored.  Length of data should
622 							// actually be 1 + 4 * no of bits in flags.
623 							if ((flags & 1) != 0 && len >= 5) {
624 								int iTime = ((extra[pos+1] & 0xff) |
625 									(extra[pos + 2] & 0xff) << 8 |
626 									(extra[pos + 3] & 0xff) << 16 |
627 									(extra[pos + 4] & 0xff) << 24);
628 
629 								DateTime = (new DateTime ( 1970, 1, 1, 0, 0, 0 ) + new TimeSpan ( 0, 0, 0, iTime, 0 )).ToLocalTime ();
630 								known |= (ushort)KNOWN_TIME;
631 							}
632 						} else if (sig == 0x0001) {
633 							// ZIP64 extended information extra field
634 							// Of variable size depending on which fields in header are too small
635 							// fields appear here if the corresponding local or central directory record field
636 							// is set to 0xFFFF or 0xFFFFFFFF and the entry is in Zip64 format.
637 							//
638 							// Original Size          8 bytes
639 							// Compressed size        8 bytes
640 							// Relative header offset 8 bytes
641 							// Disk start number      4 bytes
642 						}
643 						pos += len;
644 					}
645 				} catch (Exception) {
646 					/* be lenient */
647 					return;
648 				}
649 			}
650 		}
651 
652 
653 		/// <summary>
654 		/// Gets/Sets the entry comment.
655 		/// </summary>
656 		/// <exception cref="System.ArgumentOutOfRangeException">
657 		/// If comment is longer than 0xffff.
658 		/// </exception>
659 		/// <returns>
660 		/// The comment or null if not set.
661 		/// </returns>
662 		public string Comment {
663 			get {
664 				return comment;
665 			}
666 			set {
667 				// TODO: this test is strictly incorrect as the length is in characters
668 				// While the test is correct in that a comment of this length or greater
669 				// is definitely invalid, shorter comments may also have an invalid length.
670 				if (value != null && value.Length > 0xffff) {
671 					throw new ArgumentOutOfRangeException();
672 				}
673 				this.comment = value;
674 			}
675 		}
676 
677 		/// <summary>
678 		/// Gets a value indicating of the if the entry is a directory.  A directory is determined by
679 		/// an entry name with a trailing slash '/'.  The external file attributes
680 		/// can also mark a file as a directory.  The trailing slash convention should always be followed
681 		/// however.
682 		/// </summary>
683 		public bool IsDirectory {
684 			get {
685 				int nlen = name.Length;
686 				bool result = nlen > 0 && name[nlen - 1] == '/';
687 
688 				if (result == false && (known & KNOWN_EXTERN_ATTRIBUTES) != 0) {
689 					if (HostSystem == 0 && (ExternalFileAttributes & 16) != 0) {
690 						result = true;
691 					}
692 				}
693 				return result;
694 			}
695 		}
696 
697 		/// <summary>
698 		/// Get a value of true if the entry appears to be a file; false otherwise
699 		/// </summary>
700 		/// <remarks>
701 		/// This only takes account Windows attributes.  Other operating systems are ignored.
702 		/// For linux and others the result may be incorrect.
703 		/// </remarks>
704 		public bool IsFile {
705 			get {
706 				bool result = !IsDirectory;
707 
708 				// Exclude volume labels
709 				if ( result && (known & KNOWN_EXTERN_ATTRIBUTES) != 0) {
710 					if (HostSystem == 0 && (ExternalFileAttributes & 8) != 0) {
711 						result = false;
712 					}
713 				}
714 				return result;
715 			}
716 		}
717 
718 		/// <summary>
719 		/// Creates a copy of this zip entry.
720 		/// </summary>
Clone()721 		public object Clone()
722 		{
723 			return this.MemberwiseClone();
724 		}
725 
726 		/// <summary>
727 		/// Gets the string representation of this ZipEntry.
728 		/// </summary>
ToString()729 		public override string ToString()
730 		{
731 			return name;
732 		}
733 	}
734 }
735