Mercurial > repos > SharpZipLib
view Zip/ZipEntry.cs @ 3:686f96c8b694
Open with VS2k8 and force .Net 2.0 compatibility
no-open-ticket
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 30 Oct 2010 14:26:04 +0000 |
parents | 94e25b786321 |
children |
line wrap: on
line source
// ZipEntry.cs // // Copyright (C) 2001 Mike Krueger // Copyright (C) 2004 John Reilly // // This file was translated from java, it was part of the GNU Classpath // Copyright (C) 2001 Free Software Foundation, Inc. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // Linking this library statically or dynamically with other modules is // making a combined work based on this library. Thus, the terms and // conditions of the GNU General Public License cover the whole // combination. // // As a special exception, the copyright holders of this library give you // permission to link this library with independent modules to produce an // executable, regardless of the license terms of these independent // modules, and to copy and distribute the resulting executable under // terms of your choice, provided that you also meet, for each linked // independent module, the terms and conditions of the license of that // module. An independent module is a module which is not derived from // or based on this library. If you modify this library, you may extend // this exception to your version of the library, but you are not // obligated to do so. If you do not wish to do so, delete this // exception statement from your version. // HISTORY // 22-12-2009 DavidPierson Added AES support // 02-02-2010 DavidPierson Changed NTFS Extra Data min length to 4 using System; using System.IO; namespace ICSharpCode.SharpZipLib.Zip { /// <summary> /// Defines known values for the <see cref="HostSystemID"/> property. /// </summary> public enum HostSystemID { /// <summary> /// Host system = MSDOS /// </summary> Msdos = 0, /// <summary> /// Host system = Amiga /// </summary> Amiga = 1, /// <summary> /// Host system = Open VMS /// </summary> OpenVms = 2, /// <summary> /// Host system = Unix /// </summary> Unix = 3, /// <summary> /// Host system = VMCms /// </summary> VMCms = 4, /// <summary> /// Host system = Atari ST /// </summary> AtariST = 5, /// <summary> /// Host system = OS2 /// </summary> OS2 = 6, /// <summary> /// Host system = Macintosh /// </summary> Macintosh = 7, /// <summary> /// Host system = ZSystem /// </summary> ZSystem = 8, /// <summary> /// Host system = Cpm /// </summary> Cpm = 9, /// <summary> /// Host system = Windows NT /// </summary> WindowsNT = 10, /// <summary> /// Host system = MVS /// </summary> MVS = 11, /// <summary> /// Host system = VSE /// </summary> Vse = 12, /// <summary> /// Host system = Acorn RISC /// </summary> AcornRisc = 13, /// <summary> /// Host system = VFAT /// </summary> Vfat = 14, /// <summary> /// Host system = Alternate MVS /// </summary> AlternateMvs = 15, /// <summary> /// Host system = BEOS /// </summary> BeOS = 16, /// <summary> /// Host system = Tandem /// </summary> Tandem = 17, /// <summary> /// Host system = OS400 /// </summary> OS400 = 18, /// <summary> /// Host system = OSX /// </summary> OSX = 19, /// <summary> /// Host system = WinZIP AES /// </summary> WinZipAES = 99, } /// <summary> /// This class represents an entry in a zip archive. This can be a file /// or a directory /// ZipFile and ZipInputStream will give you instances of this class as /// information about the members in an archive. ZipOutputStream /// uses an instance of this class when creating an entry in a Zip file. /// <br/> /// <br/>Author of the original java version : Jochen Hoenicke /// </summary> public class ZipEntry : ICloneable { [Flags] enum Known : byte { None = 0, Size = 0x01, CompressedSize = 0x02, Crc = 0x04, Time = 0x08, ExternalAttributes = 0x10, } #region Constructors /// <summary> /// Creates a zip entry with the given name. /// </summary> /// <param name="name"> /// The name for this entry. Can include directory components. /// The convention for names is 'unix' style paths with relative names only. /// There are with no device names and path elements are separated by '/' characters. /// </param> /// <exception cref="ArgumentNullException"> /// The name passed is null /// </exception> public ZipEntry(string name) : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated) { } /// <summary> /// Creates a zip entry with the given name and version required to extract /// </summary> /// <param name="name"> /// The name for this entry. Can include directory components. /// The convention for names is 'unix' style paths with no device names and /// path elements separated by '/' characters. This is not enforced see <see cref="CleanName(string)">CleanName</see> /// on how to ensure names are valid if this is desired. /// </param> /// <param name="versionRequiredToExtract"> /// The minimum 'feature version' required this entry /// </param> /// <exception cref="ArgumentNullException"> /// The name passed is null /// </exception> internal ZipEntry(string name, int versionRequiredToExtract) : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, CompressionMethod.Deflated) { } /// <summary> /// Initializes an entry with the given name and made by information /// </summary> /// <param name="name">Name for this entry</param> /// <param name="madeByInfo">Version and HostSystem Information</param> /// <param name="versionRequiredToExtract">Minimum required zip feature version required to extract this entry</param> /// <param name="method">Compression method for this entry.</param> /// <exception cref="ArgumentNullException"> /// The name passed is null /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// versionRequiredToExtract should be 0 (auto-calculate) or > 10 /// </exception> /// <remarks> /// This constructor is used by the ZipFile class when reading from the central header /// It is not generally useful, use the constructor specifying the name only. /// </remarks> internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, CompressionMethod method) { if (name == null) { throw new System.ArgumentNullException("name"); } if ( name.Length > 0xffff ) { throw new ArgumentException("Name is too long", "name"); } if ( (versionRequiredToExtract != 0) && (versionRequiredToExtract < 10) ) { throw new ArgumentOutOfRangeException("versionRequiredToExtract"); } this.DateTime = System.DateTime.Now; this.name = name; this.versionMadeBy = (ushort)madeByInfo; this.versionToExtract = (ushort)versionRequiredToExtract; this.method = method; } /// <summary> /// Creates a deep copy of the given zip entry. /// </summary> /// <param name="entry"> /// The entry to copy. /// </param> [Obsolete("Use Clone instead")] public ZipEntry(ZipEntry entry) { if ( entry == null ) { throw new ArgumentNullException("entry"); } known = entry.known; name = entry.name; size = entry.size; compressedSize = entry.compressedSize; crc = entry.crc; dosTime = entry.dosTime; method = entry.method; comment = entry.comment; versionToExtract = entry.versionToExtract; versionMadeBy = entry.versionMadeBy; externalFileAttributes = entry.externalFileAttributes; flags = entry.flags; zipFileIndex = entry.zipFileIndex; offset = entry.offset; forceZip64_ = entry.forceZip64_; if ( entry.extra != null ) { extra = new byte[entry.extra.Length]; Array.Copy(entry.extra, 0, extra, 0, entry.extra.Length); } } #endregion /// <summary> /// Get a value indicating wether the entry has a CRC value available. /// </summary> public bool HasCrc { get { return (known & Known.Crc) != 0; } } /// <summary> /// Get/Set flag indicating if entry is encrypted. /// A simple helper routine to aid interpretation of <see cref="Flags">flags</see> /// </summary> /// <remarks>This is an assistant that interprets the <see cref="Flags">flags</see> property.</remarks> public bool IsCrypted { get { return (flags & 1) != 0; } set { if (value) { flags |= 1; } else { flags &= ~1; } } } /// <summary> /// Get / set a flag indicating wether entry name and comment text are /// encoded in <a href="http://www.unicode.org">unicode UTF8</a>. /// </summary> /// <remarks>This is an assistant that interprets the <see cref="Flags">flags</see> property.</remarks> public bool IsUnicodeText { get { return ( flags & (int)GeneralBitFlags.UnicodeText ) != 0; } set { if ( value ) { flags |= (int)GeneralBitFlags.UnicodeText; } else { flags &= ~(int)GeneralBitFlags.UnicodeText; } } } /// <summary> /// Value used during password checking for PKZIP 2.0 / 'classic' encryption. /// </summary> internal byte CryptoCheckValue { get { return cryptoCheckValue_; } set { cryptoCheckValue_ = value; } } /// <summary> /// Get/Set general purpose bit flag for entry /// </summary> /// <remarks> /// General purpose bit flag<br/> /// <br/> /// Bit 0: If set, indicates the file is encrypted<br/> /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating<br/> /// Imploding:<br/> /// Bit 1 if set indicates an 8K sliding dictionary was used. If clear a 4k dictionary was used<br/> /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise<br/> /// <br/> /// Deflating:<br/> /// Bit 2 Bit 1<br/> /// 0 0 Normal compression was used<br/> /// 0 1 Maximum compression was used<br/> /// 1 0 Fast compression was used<br/> /// 1 1 Super fast compression was used<br/> /// <br/> /// Bit 3: If set, the fields crc-32, compressed size /// and uncompressed size are were not able to be written during zip file creation /// The correct values are held in a data descriptor immediately following the compressed data. <br/> /// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/> /// Bit 5: If set indicates the file contains compressed patch data<br/> /// Bit 6: If set indicates strong encryption was used.<br/> /// Bit 7-10: Unused or reserved<br/> /// Bit 11: If set the name and comments for this entry are in <a href="http://www.unicode.org">unicode</a>.<br/> /// Bit 12-15: Unused or reserved<br/> /// </remarks> /// <seealso cref="IsUnicodeText"></seealso> /// <seealso cref="IsCrypted"></seealso> public int Flags { get { return flags; } set { flags = value; } } /// <summary> /// Get/Set index of this entry in Zip file /// </summary> /// <remarks>This is only valid when the entry is part of a <see cref="ZipFile"></see></remarks> public long ZipFileIndex { get { return zipFileIndex; } set { zipFileIndex = value; } } /// <summary> /// Get/set offset for use in central header /// </summary> public long Offset { get { return offset; } set { offset = value; } } /// <summary> /// Get/Set external file attributes as an integer. /// The values of this are operating system dependant see /// <see cref="HostSystem">HostSystem</see> for details /// </summary> public int ExternalFileAttributes { get { if ((known & Known.ExternalAttributes) == 0) { return -1; } else { return externalFileAttributes; } } set { externalFileAttributes = value; known |= Known.ExternalAttributes; } } /// <summary> /// Get the version made by for this entry or zero if unknown. /// The value / 10 indicates the major version number, and /// the value mod 10 is the minor version number /// </summary> public int VersionMadeBy { get { return (versionMadeBy & 0xff); } } /// <summary> /// Get a value indicating this entry is for a DOS/Windows system. /// </summary> public bool IsDOSEntry { get { return ((HostSystem == ( int )HostSystemID.Msdos) || (HostSystem == ( int )HostSystemID.WindowsNT)); } } /// <summary> /// Test the external attributes for this <see cref="ZipEntry"/> to /// see if the external attributes are Dos based (including WINNT and variants) /// and match the values /// </summary> /// <param name="attributes">The attributes to test.</param> /// <returns>Returns true if the external attributes are known to be DOS/Windows /// based and have the same attributes set as the value passed.</returns> bool HasDosAttributes(int attributes) { bool result = false; if ( (known & Known.ExternalAttributes) != 0 ) { if ( ((HostSystem == (int)HostSystemID.Msdos) || (HostSystem == (int)HostSystemID.WindowsNT)) && (ExternalFileAttributes & attributes) == attributes) { result = true; } } return result; } /// <summary> /// Gets the compatability information for the <see cref="ExternalFileAttributes">external file attribute</see> /// If the external file attributes are compatible with MS-DOS and can be read /// by PKZIP for DOS version 2.04g then this value will be zero. Otherwise the value /// will be non-zero and identify the host system on which the attributes are compatible. /// </summary> /// /// <remarks> /// The values for this as defined in the Zip File format and by others are shown below. The values are somewhat /// misleading in some cases as they are not all used as shown. You should consult the relevant documentation /// to obtain up to date and correct information. The modified appnote by the infozip group is /// particularly helpful as it documents a lot of peculiarities. The document is however a little dated. /// <list type="table"> /// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item> /// <item>1 - Amiga</item> /// <item>2 - OpenVMS</item> /// <item>3 - Unix</item> /// <item>4 - VM/CMS</item> /// <item>5 - Atari ST</item> /// <item>6 - OS/2 HPFS</item> /// <item>7 - Macintosh</item> /// <item>8 - Z-System</item> /// <item>9 - CP/M</item> /// <item>10 - Windows NTFS</item> /// <item>11 - MVS (OS/390 - Z/OS)</item> /// <item>12 - VSE</item> /// <item>13 - Acorn Risc</item> /// <item>14 - VFAT</item> /// <item>15 - Alternate MVS</item> /// <item>16 - BeOS</item> /// <item>17 - Tandem</item> /// <item>18 - OS/400</item> /// <item>19 - OS/X (Darwin)</item> /// <item>99 - WinZip AES</item> /// <item>remainder - unused</item> /// </list> /// </remarks> public int HostSystem { get { return (versionMadeBy >> 8) & 0xff; } set { versionMadeBy &= 0xff; versionMadeBy |= (ushort)((value & 0xff) << 8); } } /// <summary> /// Get minimum Zip feature version required to extract this entry /// </summary> /// <remarks> /// Minimum features are defined as:<br/> /// 1.0 - Default value<br/> /// 1.1 - File is a volume label<br/> /// 2.0 - File is a folder/directory<br/> /// 2.0 - File is compressed using Deflate compression<br/> /// 2.0 - File is encrypted using traditional encryption<br/> /// 2.1 - File is compressed using Deflate64<br/> /// 2.5 - File is compressed using PKWARE DCL Implode<br/> /// 2.7 - File is a patch data set<br/> /// 4.5 - File uses Zip64 format extensions<br/> /// 4.6 - File is compressed using BZIP2 compression<br/> /// 5.0 - File is encrypted using DES<br/> /// 5.0 - File is encrypted using 3DES<br/> /// 5.0 - File is encrypted using original RC2 encryption<br/> /// 5.0 - File is encrypted using RC4 encryption<br/> /// 5.1 - File is encrypted using AES encryption<br/> /// 5.1 - File is encrypted using corrected RC2 encryption<br/> /// 5.1 - File is encrypted using corrected RC2-64 encryption<br/> /// 6.1 - File is encrypted using non-OAEP key wrapping<br/> /// 6.2 - Central directory encryption (not confirmed yet)<br/> /// 6.3 - File is compressed using LZMA<br/> /// 6.3 - File is compressed using PPMD+<br/> /// 6.3 - File is encrypted using Blowfish<br/> /// 6.3 - File is encrypted using Twofish<br/> /// </remarks> /// <seealso cref="CanDecompress"></seealso> public int Version { get { // Return recorded version if known. if (versionToExtract != 0) { return versionToExtract; } else { int result = 10; if (AESKeySize > 0) { result = ZipConstants.VERSION_AES; // Ver 5.1 = AES } else if (CentralHeaderRequiresZip64) { result = ZipConstants.VersionZip64; } else if (CompressionMethod.Deflated == method) { result = 20; } else if (IsDirectory == true) { result = 20; } else if (IsCrypted == true) { result = 20; } else if (HasDosAttributes(0x08) ) { result = 11; } return result; } } } /// <summary> /// Get a value indicating whether this entry can be decompressed by the library. /// </summary> /// <remarks>This is based on the <see cref="Version"></see> and /// wether the <see cref="IsCompressionMethodSupported()">compression method</see> is supported.</remarks> public bool CanDecompress { get { return (Version <= ZipConstants.VersionMadeBy) && ((Version == 10) || (Version == 11) || (Version == 20) || (Version == 45) || (Version == 51)) && IsCompressionMethodSupported(); } } /// <summary> /// Force this entry to be recorded using Zip64 extensions. /// </summary> public void ForceZip64() { forceZip64_ = true; } /// <summary> /// Get a value indicating wether Zip64 extensions were forced. /// </summary> /// <returns>A <see cref="bool"/> value of true if Zip64 extensions have been forced on; false if not.</returns> public bool IsZip64Forced() { return forceZip64_; } /// <summary> /// Gets a value indicating if the entry requires Zip64 extensions /// to store the full entry values. /// </summary> /// <value>A <see cref="bool"/> value of true if a local header requires Zip64 extensions; false if not.</value> public bool LocalHeaderRequiresZip64 { get { bool result = forceZip64_; if ( !result ) { ulong trueCompressedSize = compressedSize; if ( (versionToExtract == 0) && IsCrypted ) { trueCompressedSize += ZipConstants.CryptoHeaderSize; } // TODO: A better estimation of the true limit based on compression overhead should be used // to determine when an entry should use Zip64. result = ((this.size >= uint.MaxValue) || (trueCompressedSize >= uint.MaxValue)) && ((versionToExtract == 0) || (versionToExtract >= ZipConstants.VersionZip64)); } return result; } } /// <summary> /// Get a value indicating wether the central directory entry requires Zip64 extensions to be stored. /// </summary> public bool CentralHeaderRequiresZip64 { get { return LocalHeaderRequiresZip64 || (offset >= uint.MaxValue); } } /// <summary> /// Get/Set DosTime value. /// </summary> /// <remarks> /// The MS-DOS date format can only represent dates between 1/1/1980 and 12/31/2107. /// </remarks> public long DosTime { get { if ((known & Known.Time) == 0) { return 0; } else { return dosTime; } } set { unchecked { dosTime = (uint)value; } known |= Known.Time; } } /// <summary> /// Gets/Sets the time of last modification of the entry. /// </summary> /// <remarks> /// The <see cref="DosTime"></see> property is updated to match this as far as possible. /// </remarks> public DateTime DateTime { get { uint sec = Math.Min(59, 2 * (dosTime & 0x1f)); uint min = Math.Min(59, (dosTime >> 5) & 0x3f); uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f); uint mon = Math.Max(1, Math.Min(12, ((dosTime >> 21) & 0xf))); uint year = ((dosTime >> 25) & 0x7f) + 1980; int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((dosTime >> 16) & 0x1f))); return new System.DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec); } set { uint year = (uint) value.Year; uint month = (uint) value.Month; uint day = (uint) value.Day; uint hour = (uint) value.Hour; uint minute = (uint) value.Minute; uint second = (uint) value.Second; if ( year < 1980 ) { year = 1980; month = 1; day = 1; hour = 0; minute = 0; second = 0; } else if ( year > 2107 ) { year = 2107; month = 12; day = 31; hour = 23; minute = 59; second = 59; } DosTime = ((year - 1980) & 0x7f) << 25 | (month << 21) | (day << 16) | (hour << 11) | (minute << 5) | (second >> 1); } } /// <summary> /// Returns the entry name. /// </summary> /// <remarks> /// The unix naming convention is followed. /// Path components in the entry should always separated by forward slashes ('/'). /// Dos device names like C: should also be removed. /// See the <see cref="ZipNameTransform"/> class, or <see cref="CleanName(string)"/> ///</remarks> public string Name { get { return name; } } /// <summary> /// Gets/Sets the size of the uncompressed data. /// </summary> /// <returns> /// The size or -1 if unknown. /// </returns> /// <remarks>Setting the size before adding an entry to an archive can help /// avoid compatability problems with some archivers which dont understand Zip64 extensions.</remarks> public long Size { get { return (known & Known.Size) != 0 ? (long)size : -1L; } set { this.size = (ulong)value; this.known |= Known.Size; } } /// <summary> /// Gets/Sets the size of the compressed data. /// </summary> /// <returns> /// The compressed entry size or -1 if unknown. /// </returns> public long CompressedSize { get { return (known & Known.CompressedSize) != 0 ? (long)compressedSize : -1L; } set { this.compressedSize = (ulong)value; this.known |= Known.CompressedSize; } } /// <summary> /// Gets/Sets the crc of the uncompressed data. /// </summary> /// <exception cref="System.ArgumentOutOfRangeException"> /// Crc is not in the range 0..0xffffffffL /// </exception> /// <returns> /// The crc value or -1 if unknown. /// </returns> public long Crc { get { return (known & Known.Crc) != 0 ? crc & 0xffffffffL : -1L; } set { if (((ulong)crc & 0xffffffff00000000L) != 0) { throw new ArgumentOutOfRangeException("value"); } this.crc = (uint)value; this.known |= Known.Crc; } } /// <summary> /// Gets/Sets the compression method. Only Deflated and Stored are supported. /// </summary> /// <returns> /// The compression method for this entry /// </returns> /// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Deflated"/> /// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Stored"/> public CompressionMethod CompressionMethod { get { return method; } set { if ( !IsCompressionMethodSupported(value) ) { throw new NotSupportedException("Compression method not supported"); } this.method = value; } } /// <summary> /// Gets the compression method for outputting to the local or central header. /// Returns same value as CompressionMethod except when AES encrypting, which /// places 99 in the method and places the real method in the extra data. /// </summary> internal CompressionMethod CompressionMethodForHeader { get { return (AESKeySize > 0) ? CompressionMethod.WinZipAES : method; } } /// <summary> /// Gets/Sets the extra data. /// </summary> /// <exception cref="System.ArgumentOutOfRangeException"> /// Extra data is longer than 64KB (0xffff) bytes. /// </exception> /// <returns> /// Extra data or null if not set. /// </returns> public byte[] ExtraData { get { // TODO: This is slightly safer but less efficient. Think about wether it should change. // return (byte[]) extra.Clone(); return extra; } set { if (value == null) { extra = null; } else { if (value.Length > 0xffff) { throw new System.ArgumentOutOfRangeException("value"); } extra = new byte[value.Length]; Array.Copy(value, 0, extra, 0, value.Length); } } } #if !NET_1_1 && !NETCF_2_0 /// <summary> /// For AES encrypted files returns or sets the number of bits of encryption (128, 192 or 256). /// When setting, only 0 (off), 128 or 256 is supported. /// </summary> public int AESKeySize { get { // the strength (1 or 3) is in the entry header switch (_aesEncryptionStrength) { case 0: return 0; // Not AES case 1: return 128; case 2: return 192; // Not used by WinZip case 3: return 256; default: throw new ZipException("Invalid AESEncryptionStrength " + _aesEncryptionStrength); } } set { switch (value) { case 0: _aesEncryptionStrength = 0; break; case 128: _aesEncryptionStrength = 1; break; case 256: _aesEncryptionStrength = 3; break; default: throw new ZipException("AESKeySize must be 0, 128 or 256: " + value); } } } /// <summary> /// AES Encryption strength for storage in extra data in entry header. /// 1 is 128 bit, 2 is 192 bit, 3 is 256 bit. /// </summary> internal byte AESEncryptionStrength { get { return (byte)_aesEncryptionStrength; } } #else /// <summary> /// AES unsupported prior to .NET 2.0 /// </summary> internal int AESKeySize; #endif /// <summary> /// Returns the length of the salt, in bytes /// </summary> internal int AESSaltLen { get { // Key size -> Salt length: 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes. return AESKeySize / 16; } } /// <summary> /// Number of extra bytes required to hold the AES Header fields (Salt, Pwd verify, AuthCode) /// </summary> internal int AESOverheadSize { get { // File format: // Bytes Content // Variable Salt value // 2 Password verification value // Variable Encrypted file data // 10 Authentication code return 12 + AESSaltLen; } } /// <summary> /// Process extra data fields updating the entry based on the contents. /// </summary> /// <param name="localHeader">True if the extra data fields should be handled /// for a local header, rather than for a central header. /// </param> internal void ProcessExtraData(bool localHeader) { ZipExtraData extraData = new ZipExtraData(this.extra); if ( extraData.Find(0x0001) ) { // Version required to extract is ignored here as some archivers dont set it correctly // in theory it should be version 45 or higher // The recorded size will change but remember that this is zip64. forceZip64_ = true; if ( extraData.ValueLength < 4 ) { throw new ZipException("Extra data extended Zip64 information length is invalid"); } if ( localHeader || (size == uint.MaxValue) ) { size = (ulong)extraData.ReadLong(); } if ( localHeader || (compressedSize == uint.MaxValue) ) { compressedSize = (ulong)extraData.ReadLong(); } if ( !localHeader && (offset == uint.MaxValue) ) { offset = extraData.ReadLong(); } // Disk number on which file starts is ignored } else { if ( ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) && ((size == uint.MaxValue) || (compressedSize == uint.MaxValue)) ) { throw new ZipException("Zip64 Extended information required but is missing."); } } if ( extraData.Find(10) ) { // No room for any tags. if ( extraData.ValueLength < 4 ) { throw new ZipException("NTFS Extra data invalid"); } extraData.ReadInt(); // Reserved while ( extraData.UnreadCount >= 4 ) { int ntfsTag = extraData.ReadShort(); int ntfsLength = extraData.ReadShort(); if ( ntfsTag == 1 ) { if ( ntfsLength >= 24 ) { long lastModification = extraData.ReadLong(); long lastAccess = extraData.ReadLong(); long createTime = extraData.ReadLong(); DateTime = System.DateTime.FromFileTime(lastModification); } break; } else { // An unknown NTFS tag so simply skip it. extraData.Skip(ntfsLength); } } } else if ( extraData.Find(0x5455) ) { int length = extraData.ValueLength; int flags = extraData.ReadByte(); // Can include other times but these are ignored. Length of data should // actually be 1 + 4 * no of bits in flags. if ( ((flags & 1) != 0) && (length >= 5) ) { int iTime = extraData.ReadInt(); DateTime = (new System.DateTime ( 1970, 1, 1, 0, 0, 0 ).ToUniversalTime() + new TimeSpan ( 0, 0, 0, iTime, 0 )).ToLocalTime(); } } if (method == CompressionMethod.WinZipAES) { ProcessAESExtraData(extraData); } } // For AES the method in the entry is 99, and the real compression method is in the extradata // private void ProcessAESExtraData(ZipExtraData extraData) { #if !NET_1_1 && !NETCF_2_0 if (extraData.Find(0x9901)) { // Set version and flag for Zipfile.CreateAndInitDecryptionStream versionToExtract = ZipConstants.VERSION_AES; // Ver 5.1 = AES see "Version" getter // Set StrongEncryption flag for ZipFile.CreateAndInitDecryptionStream Flags = Flags | (int)GeneralBitFlags.StrongEncryption; // // Unpack AES extra data field see http://www.winzip.com/aes_info.htm int length = extraData.ValueLength; // Data size currently 7 if (length < 7) throw new ZipException("AES Extra Data Length " + length + " invalid."); int ver = extraData.ReadShort(); // Version number (1=AE-1 2=AE-2) int vendorId = extraData.ReadShort(); // 2-character vendor ID 0x4541 = "AE" int encrStrength = extraData.ReadByte(); // encryption strength 1 = 128 2 = 192 3 = 256 int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file _aesVer = ver; _aesEncryptionStrength = encrStrength; method = (CompressionMethod)actualCompress; } else throw new ZipException("AES Extra Data missing"); #else throw new ZipException("AES unsupported"); #endif } /// <summary> /// Gets/Sets the entry comment. /// </summary> /// <exception cref="System.ArgumentOutOfRangeException"> /// If comment is longer than 0xffff. /// </exception> /// <returns> /// The comment or null if not set. /// </returns> /// <remarks> /// A comment is only available for entries when read via the <see cref="ZipFile"/> class. /// The <see cref="ZipInputStream"/> class doesnt have the comment data available. /// </remarks> public string Comment { get { return comment; } set { // This test is strictly incorrect as the length is in characters // while the storage limit is in bytes. // While the test is partially correct in that a comment of this length or greater // is definitely invalid, shorter comments may also have an invalid length // where there are multi-byte characters // The full test is not possible here however as the code page to apply conversions with // isnt available. if ( (value != null) && (value.Length > 0xffff) ) { #if NETCF_1_0 throw new ArgumentOutOfRangeException("value"); #else throw new ArgumentOutOfRangeException("value", "cannot exceed 65535"); #endif } comment = value; } } /// <summary> /// Gets a value indicating if the entry is a directory. /// however. /// </summary> /// <remarks> /// A directory is determined by an entry name with a trailing slash '/'. /// The external file attributes can also indicate an entry is for a directory. /// Currently only dos/windows attributes are tested in this manner. /// The trailing slash convention should always be followed. /// </remarks> public bool IsDirectory { get { int nameLength = name.Length; bool result = ((nameLength > 0) && ((name[nameLength - 1] == '/') || (name[nameLength - 1] == '\\'))) || HasDosAttributes(16) ; return result; } } /// <summary> /// Get a value of true if the entry appears to be a file; false otherwise /// </summary> /// <remarks> /// This only takes account of DOS/Windows attributes. Other operating systems are ignored. /// For linux and others the result may be incorrect. /// </remarks> public bool IsFile { get { return !IsDirectory && !HasDosAttributes(8); } } /// <summary> /// Test entry to see if data can be extracted. /// </summary> /// <returns>Returns true if data can be extracted for this entry; false otherwise.</returns> public bool IsCompressionMethodSupported() { return IsCompressionMethodSupported(CompressionMethod); } #region ICloneable Members /// <summary> /// Creates a copy of this zip entry. /// </summary> /// <returns>An <see cref="Object"/> that is a copy of the current instance.</returns> public object Clone() { ZipEntry result = (ZipEntry)this.MemberwiseClone(); // Ensure extra data is unique if it exists. if ( extra != null ) { result.extra = new byte[extra.Length]; Array.Copy(extra, 0, result.extra, 0, extra.Length); } return result; } #endregion /// <summary> /// Gets a string representation of this ZipEntry. /// </summary> /// <returns>A readable textual representation of this <see cref="ZipEntry"/></returns> public override string ToString() { return name; } /// <summary> /// Test a <see cref="CompressionMethod">compression method</see> to see if this library /// supports extracting data compressed with that method /// </summary> /// <param name="method">The compression method to test.</param> /// <returns>Returns true if the compression method is supported; false otherwise</returns> public static bool IsCompressionMethodSupported(CompressionMethod method) { return ( method == CompressionMethod.Deflated ) || ( method == CompressionMethod.Stored ); } /// <summary> /// Cleans a name making it conform to Zip file conventions. /// Devices names ('c:\') and UNC share names ('\\server\share') are removed /// and forward slashes ('\') are converted to back slashes ('/'). /// Names are made relative by trimming leading slashes which is compatible /// with the ZIP naming convention. /// </summary> /// <param name="name">The name to clean</param> /// <returns>The 'cleaned' name.</returns> /// <remarks> /// The <seealso cref="ZipNameTransform">Zip name transform</seealso> class is more flexible. /// </remarks> public static string CleanName(string name) { if (name == null) { return string.Empty; } if (Path.IsPathRooted(name) == true) { // NOTE: // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt name = name.Substring(Path.GetPathRoot(name).Length); } name = name.Replace(@"\", "/"); while ( (name.Length > 0) && (name[0] == '/')) { name = name.Remove(0, 1); } return name; } #region Instance Fields Known known; int externalFileAttributes = -1; // contains external attributes (O/S dependant) ushort versionMadeBy; // Contains host system and version information // only relevant for central header entries string name; ulong size; ulong compressedSize; ushort versionToExtract; // Version required to extract (library handles <= 2.0) uint crc; uint dosTime; CompressionMethod method = CompressionMethod.Deflated; byte[] extra; string comment; int flags; // general purpose bit flags long zipFileIndex = -1; // used by ZipFile long offset; // used by ZipFile and ZipOutputStream bool forceZip64_; byte cryptoCheckValue_; #if !NET_1_1 && !NETCF_2_0 int _aesVer; // Version number (2 = AE-2 ?). Assigned but not used. int _aesEncryptionStrength; // Encryption strength 1 = 128 2 = 192 3 = 256 #endif #endregion } }