/Utilities/Compression/ZipEntry.cs
C# | 984 lines | 568 code | 66 blank | 350 comment | 75 complexity | 1ee0479e05a2d4b1c9554cae8d8731a6 MD5 | raw file
1// Based on Mike Krueger's SharpZipLib, Copyright (C) 2001 (GNU license). 2// Authors of the original java version: Jochen Hoenicke, John Leuner 3// See http://www.ISeeSharpCode.com for more information. 4 5using System; 6using System.IO; 7using Delta.Utilities.Helpers; 8 9namespace Delta.Utilities.Compression 10{ 11 /// <summary> 12 /// This class represents an entry in a zip archive. This can be a file 13 /// or a directory ZipFile and ZipInputStream will give you instances of 14 /// this class as information about the members in an archive. 15 /// ZipOutputStream uses an instance of this class when creating an entry 16 /// in a Zip file. 17 /// <br/>Author of the original java version : Jochen Hoenicke 18 /// </summary> 19 public class ZipEntry //: ICloneable 20 { 21 #region Constants 22 private const int KnownSize = 1; 23 24 private const int KnownCSize = 2; 25 26 private const int KnownCrc = 4; 27 28 private const int KnownTime = 8; 29 30 private const int KnownExternAttributes = 16; 31 #endregion 32 33 #region CleanName (Static) 34 /// <summary> 35 /// Cleans a name making it conform to Zip file conventions. 36 /// Devices names ('c:\') and UNC share names ('\\server\share') are 37 /// removed and forward slashes ('\') are converted to back slashes ('/'). 38 /// </summary> 39 /// <param name="name">Name to clean</param> 40 /// <param name="relativePath">Make names relative if true or absolute if 41 /// false</param> 42 public static string CleanName(string name, bool relativePath) 43 { 44 if (name == null) 45 { 46 return ""; 47 } 48 49 if (Path.IsPathRooted(name)) 50 { 51 // NOTE: 52 // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt 53 name = name.Substring(Path.GetPathRoot(name).Length); 54 } 55 56 name = name.Replace(@"\", "/"); 57 58 if (relativePath) 59 { 60 if (name.Length > 0 && 61 (name[0] == Path.AltDirectorySeparatorChar || 62 name[0] == Path.DirectorySeparatorChar)) 63 { 64 name = name.Remove(0, 1); 65 } 66 } 67 else 68 { 69 if (name.Length > 0 && 70 name[0] != Path.AltDirectorySeparatorChar && 71 name[0] != Path.DirectorySeparatorChar) 72 { 73 name = name.Insert(0, "/"); 74 } 75 } 76 return name; 77 } 78 79 /// <summary> 80 /// Cleans a name making it conform to Zip file conventions. 81 /// Devices names ('c:\') and UNC share names ('\\server\share') are 82 /// removed and forward slashes ('\') are converted to back slashes ('/'). 83 /// Names are made relative by trimming leading slashes which is 84 /// compatible with Windows-XPs built in Zip file handling. 85 /// </summary> 86 /// <param name="name">Name to clean</param> 87 public static string CleanName(string name) 88 { 89 return CleanName(name, true); 90 } 91 #endregion 92 93 #region IsCrypted (Public) 94 /// <summary> 95 /// Get/Set flag indicating if entry is encrypted. 96 /// A simple helper routine to aid interpretation of 97 /// <see cref="Flags">flags</see> 98 /// </summary> 99 public bool IsCrypted 100 { 101 get 102 { 103 return (flags & 1) != 0; 104 } // get 105 set 106 { 107 if (value) 108 { 109 flags |= 1; 110 } // if 111 else 112 { 113 flags &= ~1; 114 } // else 115 } // set 116 } 117 #endregion 118 119 #region Flags (Public) 120 /// <summary> 121 /// Get/Set general purpose bit flag for entry 122 /// </summary> 123 /// <remarks> 124 /// General purpose bit flag<br/> 125 /// Bit 0: If set, indicates the file is encrypted<br/> 126 /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 127 /// deflating<br/> 128 /// Imploding:<br/> 129 /// Bit 1 if set indicates an 8K sliding dictionary was used. 130 /// If clear a 4k dictionary was used<br/> 131 /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the 132 /// sliding dictionary, 2 otherwise<br/> 133 /// <br/> 134 /// Deflating:<br/> 135 /// Bit 2 Bit 1<br/> 136 /// 0 0 Normal compression was used<br/> 137 /// 0 1 Maximum compression was used<br/> 138 /// 1 0 Fast compression was used<br/> 139 /// 1 1 Super fast compression was used<br/> 140 /// <br/> 141 /// Bit 3: If set, the fields crc-32, compressed size 142 /// and uncompressed size are were not able to be written during zip file 143 /// creation. The correct values are held in a data descriptor immediately 144 /// following the compressed data. <br/> 145 /// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/> 146 /// Bit 5: If set indicates the file contains compressed patch data<br/> 147 /// Bit 6: If set indicates strong encryption was used.<br/> 148 /// Bit 7-15: Unused or reserved<br/> 149 /// </remarks> 150 public int Flags 151 { 152 get 153 { 154 return flags; 155 } // get 156 set 157 { 158 flags = value; 159 } // set 160 } 161 #endregion 162 163 #region ZipFileIndex (Public) 164 /// <summary> 165 /// Get/Set index of this entry in Zip file 166 /// </summary> 167 public int ZipFileIndex 168 { 169 get 170 { 171 return zipFileIndex; 172 } // get 173 set 174 { 175 zipFileIndex = value; 176 } // set 177 } 178 #endregion 179 180 #region Offset (Public) 181 /// <summary> 182 /// Get/set offset for use in central header 183 /// </summary> 184 public int Offset 185 { 186 get 187 { 188 return offset; 189 } 190 set 191 { 192 if (((ulong)value & 0xFFFFFFFF00000000L) != 0) 193 { 194 throw new ArgumentOutOfRangeException("Offset"); 195 } // if 196 offset = value; 197 } // set 198 } 199 #endregion 200 201 #region ExternalFileAttributes (Public) 202 /// <summary> 203 /// Get/Set external file attributes as an integer. 204 /// The values of this are operating system dependant see 205 /// <see cref="HostSystemId">HostSystem</see> for details 206 /// </summary> 207 public int ExternalFileAttributes 208 { 209 get 210 { 211 if ((known & KnownExternAttributes) == 0) 212 { 213 return MathHelper.InvalidIndex; 214 } // if 215 else 216 { 217 return externalFileAttributes; 218 } // else 219 } // get 220 set 221 { 222 externalFileAttributes = value; 223 known |= KnownExternAttributes; 224 } // set 225 } 226 #endregion 227 228 #region VersionMadeBy (Public) 229 /// <summary> 230 /// Get the version made by for this entry or zero if unknown. 231 /// The value / 10 indicates the major version number, and 232 /// the value mod 10 is the minor version number 233 /// </summary> 234 public int VersionMadeBy 235 { 236 get 237 { 238 return versionMadeBy & 0xff; 239 } // get 240 } 241 #endregion 242 243 #region HostSystemId (Public) 244 /// <summary> 245 /// Gets the compatability information for the 246 /// <see cref="ExternalFileAttributes">external file attribute</see> 247 /// If the external file attributes are compatible with MS-DOS and can be 248 /// read by PKZIP for DOS version 2.04g then this value will be zero. 249 /// Otherwise the value will be non-zero and identify the host system on 250 /// which the attributes are compatible. 251 /// </summary> 252 /// <remarks> 253 /// The values for this as defined in the Zip File format and by others 254 /// are shown below. The values are somewhat misleading in some cases as 255 /// they are not all used as shown. You should consult the relevant 256 /// documentation to obtain up to date and correct information. The 257 /// modified appnote by the infozip group is particularly helpful as it 258 /// documents a lot of peculiarities. The document is however a little 259 /// dated. 260 /// <list type="table"> 261 /// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item> 262 /// <item>1 - Amiga</item> 263 /// <item>2 - OpenVMS</item> 264 /// <item>3 - Unix</item> 265 /// <item>4 - VM/CMS</item> 266 /// <item>5 - Atari ST</item> 267 /// <item>6 - OS/2 HPFS</item> 268 /// <item>7 - Macintosh</item> 269 /// <item>8 - Z-System</item> 270 /// <item>9 - CP/M</item> 271 /// <item>10 - Windows NTFS</item> 272 /// <item>11 - MVS (OS/390 - Z/OS)</item> 273 /// <item>12 - VSE</item> 274 /// <item>13 - Acorn Risc</item> 275 /// <item>14 - VFAT</item> 276 /// <item>15 - Alternate MVS</item> 277 /// <item>16 - BeOS</item> 278 /// <item>17 - Tandem</item> 279 /// <item>18 - OS/400</item> 280 /// <item>19 - OS/X (Darwin)</item> 281 /// <item>99 - WinZip AES</item> 282 /// <item>remainder - unused</item> 283 /// </list> 284 /// </remarks> 285 public int HostSystemId 286 { 287 get 288 { 289 return (versionMadeBy >> 8) & 0xff; 290 } // get 291 } 292 #endregion 293 294 #region Version (Public) 295 /// <summary> 296 /// Get minimum Zip feature version required to extract this 297 /// entry 298 /// </summary> 299 /// <remarks> 300 /// Minimum features are defined as:<br/> 301 /// 1.0 - Default value<br/> 302 /// 1.1 - File is a volume label<br/> 303 /// 2.0 - File is a folder/directory<br/> 304 /// 2.0 - File is compressed using Deflate compression<br/> 305 /// 2.0 - File is encrypted using traditional encryption<br/> 306 /// 2.1 - File is compressed using Deflate64<br/> 307 /// 2.5 - File is compressed using PKWARE DCL Implode<br/> 308 /// 2.7 - File is a patch data set<br/> 309 /// 4.5 - File uses Zip64 format extensions<br/> 310 /// 4.6 - File is compressed using BZIP2 compression<br/> 311 /// 5.0 - File is encrypted using DES<br/> 312 /// 5.0 - File is encrypted using 3DES<br/> 313 /// 5.0 - File is encrypted using original RC2 encryption<br/> 314 /// 5.0 - File is encrypted using RC4 encryption<br/> 315 /// 5.1 - File is encrypted using AES encryption<br/> 316 /// 5.1 - File is encrypted using corrected RC2 encryption<br/> 317 /// 5.1 - File is encrypted using corrected RC2-64 encryption<br/> 318 /// 6.1 - File is encrypted using non-OAEP key wrapping<br/> 319 /// 6.2 - Central directory encryption (not confirmed yet)<br/> 320 /// </remarks> 321 public int Version 322 { 323 get 324 { 325 if (versionToExtract != 0) 326 { 327 return versionToExtract; 328 } // if 329 else 330 { 331 int result = 10; 332 if (CompressionMethod.Deflated == method) 333 { 334 result = 20; 335 } // if 336 else if (IsDirectory) 337 { 338 result = 20; 339 } // else if 340 else if (IsCrypted) 341 { 342 result = 20; 343 } // else if 344 else if ((known & KnownExternAttributes) != 0 && 345 (externalFileAttributes & 0x08) != 0) 346 { 347 result = 11; 348 } // else if 349 return result; 350 } // else 351 } // get 352 } 353 #endregion 354 355 #region RequiresZip64 (Public) 356 /// <summary> 357 /// Gets a value indicating if the entry requires Zip64 extensions to be 358 /// stored. 359 /// </summary> 360 public bool RequiresZip64 361 { 362 get 363 { 364 return (size > uint.MaxValue) || 365 (compressedSize > uint.MaxValue); 366 } // get 367 } 368 #endregion 369 370 #region DosTime (Public) 371 /// <summary> 372 /// Gets/Sets DosTime 373 /// </summary> 374 public long DosTime 375 { 376 get 377 { 378 if ((known & KnownTime) == 0) 379 { 380 return 0; 381 } // if 382 else 383 { 384 return dosTime; 385 } // else 386 } // get 387 set 388 { 389 dosTime = (uint)value; 390 known |= KnownTime; 391 } // set 392 } 393 #endregion 394 395 #region DateTime (Public) 396 /// <summary> 397 /// Gets/Sets the time of last modification of the entry. 398 /// </summary> 399 public DateTime DateTime 400 { 401 get 402 { 403 // Although technically not valid some archives have dates set to zero. 404 // This mimics some archivers handling and is a good a cludge as any 405 // probably. 406 if (dosTime == 0) 407 { 408 return DateTime.Now; 409 } // if 410 else 411 { 412 uint sec = 2 * (dosTime & 0x1f); 413 uint min = (dosTime >> 5) & 0x3f; 414 uint hrs = (dosTime >> 11) & 0x1f; 415 uint day = (dosTime >> 16) & 0x1f; 416 uint mon = ((dosTime >> 21) & 0xf); 417 uint year = ((dosTime >> 25) & 0x7f) + 1980; 418 return new DateTime((int)year, (int)mon, (int)day, 419 (int)hrs, (int)min, (int)sec); 420 } // else 421 } // get 422 set 423 { 424 DosTime = 425 ((uint)value.Year - 1980 & 0x7f) << 25 | 426 ((uint)value.Month) << 21 | 427 ((uint)value.Day) << 16 | 428 ((uint)value.Hour) << 11 | 429 ((uint)value.Minute) << 5 | 430 ((uint)value.Second) >> 1; 431 } // set 432 } 433 #endregion 434 435 #region Name (Public) 436 /// <summary> 437 /// Returns the entry name. The path components in the entry should 438 /// always separated by slashes ('/'). Dos device names like C: should 439 /// also be removed. See <see cref="CleanName(string)">CleanName</see>. 440 /// </summary> 441 public string Name 442 { 443 get 444 { 445 return name; 446 } // get 447 } 448 #endregion 449 450 #region Comment (Public) 451 /// <summary> 452 /// Gets/Sets the entry comment. 453 /// </summary> 454 /// <exception cref="System.ArgumentOutOfRangeException"> 455 /// If comment is longer than 0xffff. 456 /// </exception> 457 /// <returns> 458 /// The comment or null if not set. 459 /// </returns> 460 public string Comment 461 { 462 get 463 { 464 return comment; 465 } 466 set 467 { 468 // While the test is correct in that a comment of this length or 469 // greater is definitely invalid, shorter comments may also have an 470 // invalid length. 471 if (value != null && 472 value.Length > 0xffff) 473 { 474 throw new ArgumentOutOfRangeException(); 475 } 476 comment = value; 477 } 478 } 479 #endregion 480 481 #region IsDirectory (Public) 482 /// <summary> 483 /// Gets a value indicating of the if the entry is a directory. 484 /// A directory is determined by an entry name with a trailing slash '/'. 485 /// The external file attributes can also mark a file as a directory. 486 /// The trailing slash convention should always be followed however. 487 /// </summary> 488 public bool IsDirectory 489 { 490 get 491 { 492 int nlen = name.Length; 493 bool result = nlen > 0 && name[nlen - 1] == '/'; 494 495 if (result == false && 496 (known & KnownExternAttributes) != 0) 497 { 498 if (HostSystemId == 0 && 499 (ExternalFileAttributes & 16) != 0) 500 { 501 result = true; 502 } 503 } 504 return result; 505 } 506 } 507 #endregion 508 509 #region IsFile (Public) 510 /// <summary> 511 /// Get a value of true if the entry appears to be a file; false otherwise 512 /// </summary> 513 /// <remarks> 514 /// This only takes account Windows attributes. Other operating systems 515 /// are ignored. For Linux and others the result may be incorrect. 516 /// </remarks> 517 public bool IsFile 518 { 519 get 520 { 521 bool result = !IsDirectory; 522 523 // Exclude volume labels 524 if (result && (known & KnownExternAttributes) != 0) 525 { 526 if (HostSystemId == 0 && 527 (ExternalFileAttributes & 8) != 0) 528 { 529 result = false; 530 } 531 } 532 return result; 533 } 534 } 535 #endregion 536 537 #region Size (Public) 538 /// <summary> 539 /// Gets/Sets the size of the uncompressed data. 540 /// </summary> 541 /// <exception cref="System.ArgumentOutOfRangeException"> 542 /// If the size is not in the range 0..0xffffffffL 543 /// </exception> 544 /// <returns> 545 /// The size or -1 if unknown. 546 /// </returns> 547 public long Size 548 { 549 get 550 { 551 return (known & KnownSize) != 0 552 ? (long)size 553 : -1L; 554 } 555 set 556 { 557 if (((ulong)value & 0xFFFFFFFF00000000L) != 0) 558 { 559 throw new ArgumentOutOfRangeException("size"); 560 } 561 size = (ulong)value; 562 known |= KnownSize; 563 } 564 } 565 #endregion 566 567 #region CompressedSize (Public) 568 /// <summary> 569 /// Gets/Sets the size of the compressed data. 570 /// </summary> 571 /// <exception cref="System.ArgumentOutOfRangeException"> 572 /// Size is not in the range 0..0xffffffff 573 /// </exception> 574 /// <returns> 575 /// The size or -1 if unknown. 576 /// </returns> 577 public long CompressedSize 578 { 579 get 580 { 581 return (known & KnownCSize) != 0 582 ? (long)compressedSize 583 : -1L; 584 } 585 set 586 { 587 if (((ulong)value & 0xffffffff00000000L) != 0) 588 { 589 throw new ArgumentOutOfRangeException(); 590 } 591 compressedSize = (ulong)value; 592 known |= KnownCSize; 593 } 594 } 595 #endregion 596 597 #region Crc (Public) 598 /// <summary> 599 /// Gets/Sets the crc of the uncompressed data. 600 /// </summary> 601 /// <exception cref="System.ArgumentOutOfRangeException"> 602 /// Crc is not in the range 0..0xffffffffL 603 /// </exception> 604 /// <returns> 605 /// The crc value or -1 if unknown. 606 /// </returns> 607 public long Crc 608 { 609 get 610 { 611 return (known & KnownCrc) != 0 612 ? crc & 0xffffffffL 613 : -1L; 614 } 615 set 616 { 617 if ((crc & 0xffffffff00000000L) != 0) 618 { 619 throw new ArgumentOutOfRangeException(); 620 } 621 crc = (uint)value; 622 known |= KnownCrc; 623 } 624 } 625 #endregion 626 627 #region CompressionMethod (Public) 628 /// <summary> 629 /// Gets/Sets the compression method. Only Deflated and Stored are 630 /// supported. 631 /// </summary> 632 /// <returns> 633 /// The compression method for this entry 634 /// </returns> 635 /// <see cref="Delta.Utilities.Compression.CompressionMethod.Deflated"/> 636 /// <see cref="Delta.Utilities.Compression.CompressionMethod.Stored"/> 637 public CompressionMethod CompressionMethod 638 { 639 get 640 { 641 return method; 642 } 643 set 644 { 645 method = value; 646 } 647 } 648 #endregion 649 650 #region Private 651 652 #region known (Private) 653 /// <summary> 654 /// Bit flags made up of above bits 655 /// </summary> 656 private ushort known; 657 #endregion 658 659 #region externalFileAttributes (Private) 660 /// <summary> 661 /// contains external attributes (os dependant) 662 /// </summary> 663 private int externalFileAttributes; 664 #endregion 665 666 #region versionMadeBy (Private) 667 /// <summary> 668 /// Contains host system and version information 669 /// only relevant for central header entries 670 /// </summary> 671 private ushort versionMadeBy; 672 #endregion 673 674 #region name (Private) 675 /// <summary> 676 /// Name 677 /// </summary> 678 private string name; 679 #endregion 680 681 #region size (Private) 682 /// <summary> 683 /// Size 684 /// </summary> 685 private ulong size; 686 #endregion 687 688 #region compressedSize (Private) 689 /// <summary> 690 /// Compressed size 691 /// </summary> 692 private ulong compressedSize; 693 #endregion 694 695 #region versionToExtract (Private) 696 /// <summary> 697 /// Version required to extract (library handles <= 2.0) 698 /// </summary> 699 private ushort versionToExtract; 700 #endregion 701 702 #region crc (Private) 703 /// <summary> 704 /// Crc 705 /// </summary> 706 private uint crc; 707 #endregion 708 709 #region dosTime (Private) 710 /// <summary> 711 /// Dos time 712 /// </summary> 713 private uint dosTime; 714 #endregion 715 716 #region method (Private) 717 /// <summary> 718 /// Method 719 /// </summary> 720 private CompressionMethod method; 721 #endregion 722 723 #region extra (Private) 724 /// <summary> 725 /// Extra 726 /// </summary> 727 private byte[] extra; 728 #endregion 729 730 #region comment (Private) 731 /// <summary> 732 /// Comment 733 /// </summary> 734 private string comment; 735 #endregion 736 737 #region flags (Private) 738 /// <summary> 739 /// general purpose bit flags 740 /// </summary> 741 private int flags; 742 #endregion 743 744 #region zipFileIndex (Private) 745 /// <summary> 746 /// used by ZipFile 747 /// </summary> 748 private int zipFileIndex; 749 #endregion 750 751 #region offset (Private) 752 /// <summary> 753 /// used by ZipFile and ZipOutputStream 754 /// </summary> 755 private int offset; 756 #endregion 757 758 #endregion 759 760 #region Constructors 761 /// <summary> 762 /// Creates a zip entry with the given name. 763 /// </summary> 764 /// <param name="name"> 765 /// The name for this entry. Can include directory components. 766 /// The convention for names is 'unix' style paths with no device names 767 /// and path elements separated by '/' characters. This is not enforced 768 /// see <see cref="CleanName(string)">CleanName</see> on how to ensure 769 /// names are valid if this is desired. 770 /// </param> 771 /// <exception cref="ArgumentNullException">The name passed is null 772 /// </exception> 773 public ZipEntry(string name) 774 : this(name, 0, ZipConstants.VersionMadeBy) 775 { 776 } 777 778 /// <summary> 779 /// Creates a zip entry with the given name and version required to extract 780 /// </summary> 781 /// <param name="name"> 782 /// The name for this entry. Can include directory components. 783 /// The convention for names is 'unix' style paths with no device names 784 /// and path elements separated by '/' characters. This is not enforced 785 /// see <see cref="CleanName(string)">CleanName</see> on how to ensure 786 /// names are valid if this is desired. 787 /// </param> 788 /// <param name="versionRequiredToExtract"> 789 /// The minimum 'feature version' required this entry 790 /// </param> 791 /// <exception cref="ArgumentNullException">The name passed is null 792 /// </exception> 793 internal ZipEntry(string name, int versionRequiredToExtract) 794 : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy) 795 { 796 } 797 798 /// <summary> 799 /// Initializes an entry with the given name and made by information 800 /// </summary> 801 /// <param name="setName">Name for this entry</param> 802 /// <param name="setMadeByInfo">Version and HostSystem Information</param> 803 /// <param name="setVersionRequiredToExtract">Minimum required zip feature 804 /// version required to extract this entry</param> 805 /// <exception cref="ArgumentNullException"> 806 /// The name passed is null 807 /// </exception> 808 /// <exception cref="ArgumentOutOfRangeException"> 809 /// versionRequiredToExtract should be 0 (auto-calculate) or > 10 810 /// </exception> 811 /// <remarks> 812 /// This constructor is used by the ZipFile class when reading from the 813 /// central header. It is not generally useful, use the constructor 814 /// specifying the name only. 815 /// </remarks> 816 internal ZipEntry(string setName, int setVersionRequiredToExtract, 817 int setMadeByInfo) 818 { 819 if (setName == null) 820 { 821 throw new ArgumentNullException("ZipEntry name"); 822 } 823 824 if (setName.Length == 0) 825 { 826 throw new ArgumentException("ZipEntry name is empty"); 827 } 828 829 if (setVersionRequiredToExtract != 0 && 830 setVersionRequiredToExtract < 10) 831 { 832 throw new ArgumentOutOfRangeException( 833 "versionRequiredToExtract"); 834 } 835 836 DateTime = DateTime.Now; 837 name = setName; 838 versionMadeBy = (ushort)setMadeByInfo; 839 versionToExtract = (ushort)setVersionRequiredToExtract; 840 method = CompressionMethod.Deflated; 841 zipFileIndex = MathHelper.InvalidIndex; 842 externalFileAttributes = MathHelper.InvalidIndex; 843 } 844 845 /// <summary> 846 /// Can be used for the "Load" method (after saving) or to create a clone. 847 /// </summary> 848 private ZipEntry() 849 { 850 } 851 #endregion 852 853 #region GetExtraData (Public) 854 /// <summary> 855 /// Get extra data 856 /// </summary> 857 public byte[] GetExtraData() 858 { 859 return extra; 860 } 861 #endregion 862 863 #region SetExtraData (Public) 864 /// <summary> 865 /// Set extra data 866 /// </summary> 867 /// <param name="value">Value</param> 868 public void SetExtraData(byte[] value) 869 { 870 if (value == null) 871 { 872 extra = null; 873 return; 874 } 875 876 if (value.Length > 0xffff) 877 { 878 throw new ArgumentOutOfRangeException(); 879 } 880 881 extra = new byte[value.Length]; 882 Array.Copy(value, 0, extra, 0, value.Length); 883 884 try 885 { 886 int pos = 0; 887 while (pos < extra.Length) 888 { 889 int sig = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8; 890 int len = (extra[pos++] & 0xff) | (extra[pos++] & 0xff) << 8; 891 892 if (len < 0 || 893 pos + len > extra.Length) 894 { 895 // This is still lenient but the extra data is corrupt. 896 // Note: Drops the extra data. Indicate to user there is a problem. 897 Log.Warning( 898 "Dropping extra data in zip entry '" + name + "': " + len); 899 break; 900 } 901 902 if (sig == 0x5455) 903 { 904 // extended time stamp, unix format by Rainer Prem 905 int extraFlags = extra[pos]; 906 // Can include other times but these are ignored. 907 // Length of data should actually be 1 + 4 * no of bits in flags. 908 if ((extraFlags & 1) != 0 && 909 len >= 5) 910 { 911 int iTime = 912 ((extra[pos + 1] & 0xff) | 913 (extra[pos + 2] & 0xff) << 8 | 914 (extra[pos + 3] & 0xff) << 16 | 915 (extra[pos + 4] & 0xff) << 24); 916 917 DateTime = 918 (new DateTime(1970, 1, 1, 0, 0, 0) + 919 new TimeSpan(0, 0, 0, iTime, 0)).ToLocalTime(); 920 known |= KnownTime; 921 } 922 } 923 else if (sig == 0x0001) 924 { 925 // ZIP64 extended information extra field 926 // Of variable size depending on which fields in header are too 927 // small fields appear here if the corresponding local or central 928 // directory record field is set to 0xFFFF or 0xFFFFFFFF and the 929 // entry is in Zip64 format. 930 // 931 // Original Size 8 bytes 932 // Compressed size 8 bytes 933 // Relative header offset 8 bytes 934 // Disk start number 4 bytes 935 } 936 pos += len; 937 } 938 } 939 catch (Exception) 940 { 941 // be lenient 942 return; 943 } 944 } 945 #endregion 946 947 #region Clone (Public) 948 /// <summary> 949 /// Creates a copy of this zip entry. 950 /// </summary> 951 public ZipEntry Clone() 952 { 953 return new ZipEntry 954 { 955 known = known, 956 name = name, 957 size = size, 958 compressedSize = compressedSize, 959 crc = crc, 960 dosTime = dosTime, 961 method = method, 962 extra = extra, 963 comment = comment, 964 versionToExtract = versionToExtract, 965 versionMadeBy = versionMadeBy, 966 externalFileAttributes = externalFileAttributes, 967 flags = flags, 968 zipFileIndex = zipFileIndex, 969 offset = offset, 970 }; 971 } 972 #endregion 973 974 #region ToString (Public) 975 /// <summary> 976 /// Gets the string representation of this ZipEntry. 977 /// </summary> 978 public override string ToString() 979 { 980 return name; 981 } 982 #endregion 983 } 984}