using System; using System.IO; namespace ICSharpCode.SharpZipLib.Zip { /// /// Defines known values for the property. /// public enum HostSystemID { /// /// Host system = MSDOS /// Msdos = 0, /// /// Host system = Amiga /// Amiga = 1, /// /// Host system = Open VMS /// OpenVms = 2, /// /// Host system = Unix /// Unix = 3, /// /// Host system = VMCms /// VMCms = 4, /// /// Host system = Atari ST /// AtariST = 5, /// /// Host system = OS2 /// OS2 = 6, /// /// Host system = Macintosh /// Macintosh = 7, /// /// Host system = ZSystem /// ZSystem = 8, /// /// Host system = Cpm /// Cpm = 9, /// /// Host system = Windows NT /// WindowsNT = 10, /// /// Host system = MVS /// MVS = 11, /// /// Host system = VSE /// Vse = 12, /// /// Host system = Acorn RISC /// AcornRisc = 13, /// /// Host system = VFAT /// Vfat = 14, /// /// Host system = Alternate MVS /// AlternateMvs = 15, /// /// Host system = BEOS /// BeOS = 16, /// /// Host system = Tandem /// Tandem = 17, /// /// Host system = OS400 /// OS400 = 18, /// /// Host system = OSX /// OSX = 19, /// /// Host system = WinZIP AES /// WinZipAES = 99, } /// /// 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. ///
///
Author of the original java version : Jochen Hoenicke ///
public class ZipEntry { [Flags] private enum Known : byte { None = 0, Size = 0x01, CompressedSize = 0x02, Crc = 0x04, Time = 0x08, ExternalAttributes = 0x10, } #region Constructors /// /// Creates a zip entry with the given 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. /// /// /// The name passed is null /// public ZipEntry(string name) : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated) { } /// /// Creates a zip entry with the given name and version required to extract /// /// /// 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 CleanName /// on how to ensure names are valid if this is desired. /// /// /// The minimum 'feature version' required this entry /// /// /// The name passed is null /// internal ZipEntry(string name, int versionRequiredToExtract) : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy, CompressionMethod.Deflated) { } /// /// Initializes an entry with the given name and made by information /// /// Name for this entry /// Version and HostSystem Information /// Minimum required zip feature version required to extract this entry /// Compression method for this entry. /// /// The name passed is null /// /// /// versionRequiredToExtract should be 0 (auto-calculate) or > 10 /// /// /// 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. /// internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo, CompressionMethod method) { if (name == null) { throw new ArgumentNullException(nameof(name)); } if (name.Length > 0xffff) { throw new ArgumentException("Name is too long", nameof(name)); } if ((versionRequiredToExtract != 0) && (versionRequiredToExtract < 10)) { throw new ArgumentOutOfRangeException(nameof(versionRequiredToExtract)); } this.DateTime = DateTime.Now; this.name = CleanName(name); this.versionMadeBy = (ushort)madeByInfo; this.versionToExtract = (ushort)versionRequiredToExtract; this.method = method; IsUnicodeText = ZipStrings.UseUnicode; } /// /// Creates a deep copy of the given zip entry. /// /// /// The entry to copy. /// [Obsolete("Use Clone instead")] public ZipEntry(ZipEntry entry) { if (entry == null) { throw new ArgumentNullException(nameof(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 Constructors /// /// Get a value indicating wether the entry has a CRC value available. /// public bool HasCrc { get { return (known & Known.Crc) != 0; } } /// /// Get/Set flag indicating if entry is encrypted. /// A simple helper routine to aid interpretation of flags /// /// This is an assistant that interprets the flags property. public bool IsCrypted { get { return (flags & 1) != 0; } set { if (value) { flags |= 1; } else { flags &= ~1; } } } /// /// Get / set a flag indicating wether entry name and comment text are /// encoded in unicode UTF8. /// /// This is an assistant that interprets the flags property. public bool IsUnicodeText { get { return (flags & (int)GeneralBitFlags.UnicodeText) != 0; } set { if (value) { flags |= (int)GeneralBitFlags.UnicodeText; } else { flags &= ~(int)GeneralBitFlags.UnicodeText; } } } /// /// Value used during password checking for PKZIP 2.0 / 'classic' encryption. /// internal byte CryptoCheckValue { get { return cryptoCheckValue_; } set { cryptoCheckValue_ = value; } } /// /// Get/Set general purpose bit flag for entry /// /// /// General purpose bit flag
///
/// Bit 0: If set, indicates the file is encrypted
/// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating
/// Imploding:
/// Bit 1 if set indicates an 8K sliding dictionary was used. If clear a 4k dictionary was used
/// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise
///
/// Deflating:
/// Bit 2 Bit 1
/// 0 0 Normal compression was used
/// 0 1 Maximum compression was used
/// 1 0 Fast compression was used
/// 1 1 Super fast compression was used
///
/// 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.
/// Bit 4: Reserved for use by PKZIP for enhanced deflating
/// Bit 5: If set indicates the file contains compressed patch data
/// Bit 6: If set indicates strong encryption was used.
/// Bit 7-10: Unused or reserved
/// Bit 11: If set the name and comments for this entry are in unicode.
/// Bit 12-15: Unused or reserved
///
/// /// public int Flags { get { return flags; } set { flags = value; } } /// /// Get/Set index of this entry in Zip file /// /// This is only valid when the entry is part of a public long ZipFileIndex { get { return zipFileIndex; } set { zipFileIndex = value; } } /// /// Get/set offset for use in central header /// public long Offset { get { return offset; } set { offset = value; } } /// /// Get/Set external file attributes as an integer. /// The values of this are operating system dependant see /// HostSystem for details /// public int ExternalFileAttributes { get { if ((known & Known.ExternalAttributes) == 0) { return -1; } else { return externalFileAttributes; } } set { externalFileAttributes = value; known |= Known.ExternalAttributes; } } /// /// 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 /// public int VersionMadeBy { get { return (versionMadeBy & 0xff); } } /// /// Get a value indicating this entry is for a DOS/Windows system. /// public bool IsDOSEntry { get { return ((HostSystem == (int)HostSystemID.Msdos) || (HostSystem == (int)HostSystemID.WindowsNT)); } } /// /// Test the external attributes for this to /// see if the external attributes are Dos based (including WINNT and variants) /// and match the values /// /// The attributes to test. /// Returns true if the external attributes are known to be DOS/Windows /// based and have the same attributes set as the value passed. private bool HasDosAttributes(int attributes) { bool result = false; if ((known & Known.ExternalAttributes) != 0) { result |= (((HostSystem == (int)HostSystemID.Msdos) || (HostSystem == (int)HostSystemID.WindowsNT)) && (ExternalFileAttributes & attributes) == attributes); } return result; } /// /// Gets the compatability information for the external file attribute /// 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. /// /// /// /// 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. /// /// 0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems) /// 1 - Amiga /// 2 - OpenVMS /// 3 - Unix /// 4 - VM/CMS /// 5 - Atari ST /// 6 - OS/2 HPFS /// 7 - Macintosh /// 8 - Z-System /// 9 - CP/M /// 10 - Windows NTFS /// 11 - MVS (OS/390 - Z/OS) /// 12 - VSE /// 13 - Acorn Risc /// 14 - VFAT /// 15 - Alternate MVS /// 16 - BeOS /// 17 - Tandem /// 18 - OS/400 /// 19 - OS/X (Darwin) /// 99 - WinZip AES /// remainder - unused /// /// public int HostSystem { get { return (versionMadeBy >> 8) & 0xff; } set { versionMadeBy &= 0xff; versionMadeBy |= (ushort)((value & 0xff) << 8); } } /// /// Get minimum Zip feature version required to extract this entry /// /// /// Minimum features are defined as:
/// 1.0 - Default value
/// 1.1 - File is a volume label
/// 2.0 - File is a folder/directory
/// 2.0 - File is compressed using Deflate compression
/// 2.0 - File is encrypted using traditional encryption
/// 2.1 - File is compressed using Deflate64
/// 2.5 - File is compressed using PKWARE DCL Implode
/// 2.7 - File is a patch data set
/// 4.5 - File uses Zip64 format extensions
/// 4.6 - File is compressed using BZIP2 compression
/// 5.0 - File is encrypted using DES
/// 5.0 - File is encrypted using 3DES
/// 5.0 - File is encrypted using original RC2 encryption
/// 5.0 - File is encrypted using RC4 encryption
/// 5.1 - File is encrypted using AES encryption
/// 5.1 - File is encrypted using corrected RC2 encryption
/// 5.1 - File is encrypted using corrected RC2-64 encryption
/// 6.1 - File is encrypted using non-OAEP key wrapping
/// 6.2 - Central directory encryption (not confirmed yet)
/// 6.3 - File is compressed using LZMA
/// 6.3 - File is compressed using PPMD+
/// 6.3 - File is encrypted using Blowfish
/// 6.3 - File is encrypted using Twofish
///
/// public int Version { get { // Return recorded version if known. if (versionToExtract != 0) { return versionToExtract & 0x00ff; // Only lower order byte. High order is O/S file system. } 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; } } } /// /// Get a value indicating whether this entry can be decompressed by the library. /// /// This is based on the and /// wether the compression method is supported. public bool CanDecompress { get { return (Version <= ZipConstants.VersionMadeBy) && ((Version == 10) || (Version == 11) || (Version == 20) || (Version == 45) || (Version == 51)) && IsCompressionMethodSupported(); } } /// /// Force this entry to be recorded using Zip64 extensions. /// public void ForceZip64() { forceZip64_ = true; } /// /// Get a value indicating wether Zip64 extensions were forced. /// /// A value of true if Zip64 extensions have been forced on; false if not. public bool IsZip64Forced() { return forceZip64_; } /// /// Gets a value indicating if the entry requires Zip64 extensions /// to store the full entry values. /// /// A value of true if a local header requires Zip64 extensions; false if not. 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; } } /// /// Get a value indicating wether the central directory entry requires Zip64 extensions to be stored. /// public bool CentralHeaderRequiresZip64 { get { return LocalHeaderRequiresZip64 || (offset >= uint.MaxValue); } } /// /// Get/Set DosTime value. /// /// /// The MS-DOS date format can only represent dates between 1/1/1980 and 12/31/2107. /// public long DosTime { get { if ((known & Known.Time) == 0) { return 0; } else { return dosTime; } } set { unchecked { dosTime = (uint)value; } known |= Known.Time; } } /// /// Gets/Sets the time of last modification of the entry. /// /// /// The property is updated to match this as far as possible. /// 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 { var year = (uint)value.Year; var month = (uint)value.Month; var day = (uint)value.Day; var hour = (uint)value.Hour; var minute = (uint)value.Minute; var 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); } } /// /// Returns the entry name. /// /// /// 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 class, or /// public string Name { get { return name; } } /// /// Gets/Sets the size of the uncompressed data. /// /// /// The size or -1 if unknown. /// /// Setting the size before adding an entry to an archive can help /// avoid compatability problems with some archivers which dont understand Zip64 extensions. public long Size { get { return (known & Known.Size) != 0 ? (long)size : -1L; } set { this.size = (ulong)value; this.known |= Known.Size; } } /// /// Gets/Sets the size of the compressed data. /// /// /// The compressed entry size or -1 if unknown. /// public long CompressedSize { get { return (known & Known.CompressedSize) != 0 ? (long)compressedSize : -1L; } set { this.compressedSize = (ulong)value; this.known |= Known.CompressedSize; } } /// /// Gets/Sets the crc of the uncompressed data. /// /// /// Crc is not in the range 0..0xffffffffL /// /// /// The crc value or -1 if unknown. /// public long Crc { get { return (known & Known.Crc) != 0 ? crc & 0xffffffffL : -1L; } set { if (((ulong)crc & 0xffffffff00000000L) != 0) { throw new ArgumentOutOfRangeException(nameof(value)); } this.crc = (uint)value; this.known |= Known.Crc; } } /// /// Gets/Sets the compression method. Only Deflated and Stored are supported. /// /// /// The compression method for this entry /// /// /// public CompressionMethod CompressionMethod { get { return method; } set { if (!IsCompressionMethodSupported(value)) { throw new NotSupportedException("Compression method not supported"); } this.method = value; } } /// /// 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. /// internal CompressionMethod CompressionMethodForHeader { get { return (AESKeySize > 0) ? CompressionMethod.WinZipAES : method; } } /// /// Gets/Sets the extra data. /// /// /// Extra data is longer than 64KB (0xffff) bytes. /// /// /// Extra data or null if not set. /// 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(nameof(value)); } extra = new byte[value.Length]; Array.Copy(value, 0, extra, 0, value.Length); } } } /// /// 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. /// 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); } } } /// /// AES Encryption strength for storage in extra data in entry header. /// 1 is 128 bit, 2 is 192 bit, 3 is 256 bit. /// internal byte AESEncryptionStrength { get { return (byte)_aesEncryptionStrength; } } /// /// Returns the length of the salt, in bytes /// internal int AESSaltLen { get { // Key size -> Salt length: 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes. return AESKeySize / 16; } } /// /// Number of extra bytes required to hold the AES Header fields (Salt, Pwd verify, AuthCode) /// 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; } } /// /// Process extra data fields updating the entry based on the contents. /// /// True if the extra data fields should be handled /// for a local header, rather than for a central header. /// internal void ProcessExtraData(bool localHeader) { var 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"); } // (localHeader ||) was deleted, because actually there is no specific difference with reading sizes between local header & central directory // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT // ... // 4.4 Explanation of fields // ... // 4.4.8 compressed size: (4 bytes) // 4.4.9 uncompressed size: (4 bytes) // // The size of the file compressed (4.4.8) and uncompressed, // (4.4.9) respectively. When a decryption header is present it // will be placed in front of the file data and the value of the // compressed file size will include the bytes of the decryption // header. If bit 3 of the general purpose bit flag is set, // these fields are set to zero in the local header and the // correct values are put in the data descriptor and // in the central directory. If an archive is in ZIP64 format // and the value in this field is 0xFFFFFFFF, the size will be // in the corresponding 8 byte ZIP64 extended information // extra field. When encrypting the central directory, if the // local header is not in ZIP64 format and general purpose bit // flag 13 is set indicating masking, the value stored for the // uncompressed size in the Local Header will be zero. // // Othewise there is problem with minizip implementation if (size == uint.MaxValue) { size = (ulong)extraData.ReadLong(); } if (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."); } } DateTime = GetDateTime(extraData); if (method == CompressionMethod.WinZipAES) { ProcessAESExtraData(extraData); } } private DateTime GetDateTime(ZipExtraData extraData) { // Check for NT timestamp // NOTE: Disable by default to match behavior of InfoZIP #if RESPECT_NT_TIMESTAMP NTTaggedData ntData = extraData.GetData(); if (ntData != null) return ntData.LastModificationTime; #endif // Check for Unix timestamp ExtendedUnixData unixData = extraData.GetData(); if (unixData != null && // Only apply modification time, but require all other values to be present // This is done to match InfoZIP's behaviour ((unixData.Include & ExtendedUnixData.Flags.ModificationTime) != 0) && ((unixData.Include & ExtendedUnixData.Flags.AccessTime) != 0) && ((unixData.Include & ExtendedUnixData.Flags.CreateTime) != 0)) return unixData.ModificationTime; // Fall back to DOS time 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 DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec, DateTimeKind.Utc); } // For AES the method in the entry is 99, and the real compression method is in the extradata // private void ProcessAESExtraData(ZipExtraData extraData) { 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"); } /// /// Gets/Sets the entry comment. /// /// /// If comment is longer than 0xffff. /// /// /// The comment or null if not set. /// /// /// A comment is only available for entries when read via the class. /// The class doesnt have the comment data available. /// 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)) { throw new ArgumentOutOfRangeException(nameof(value), "cannot exceed 65535"); } comment = value; } } /// /// Gets a value indicating if the entry is a directory. /// however. /// /// /// 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. /// public bool IsDirectory { get { int nameLength = name.Length; bool result = ((nameLength > 0) && ((name[nameLength - 1] == '/') || (name[nameLength - 1] == '\\'))) || HasDosAttributes(16) ; return result; } } /// /// Get a value of true if the entry appears to be a file; false otherwise /// /// /// This only takes account of DOS/Windows attributes. Other operating systems are ignored. /// For linux and others the result may be incorrect. /// public bool IsFile { get { return !IsDirectory && !HasDosAttributes(8); } } /// /// Test entry to see if data can be extracted. /// /// Returns true if data can be extracted for this entry; false otherwise. public bool IsCompressionMethodSupported() { return IsCompressionMethodSupported(CompressionMethod); } #region ICloneable Members /// /// Creates a copy of this zip entry. /// /// An that is a copy of the current instance. public object Clone() { var 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 ICloneable Members /// /// Gets a string representation of this ZipEntry. /// /// A readable textual representation of this public override string ToString() { return name; } /// /// Test a compression method to see if this library /// supports extracting data compressed with that method /// /// The compression method to test. /// Returns true if the compression method is supported; false otherwise public static bool IsCompressionMethodSupported(CompressionMethod method) { return (method == CompressionMethod.Deflated) || (method == CompressionMethod.Stored); } /// /// 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. /// /// The name to clean /// The 'cleaned' name. /// /// The Zip name transform class is more flexible. /// public static string CleanName(string name) { if (name == null) { return string.Empty; } if (Path.IsPathRooted(name)) { // 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 private Known known; private int externalFileAttributes = -1; // contains external attributes (O/S dependant) private ushort versionMadeBy; // Contains host system and version information // only relevant for central header entries private string name; private ulong size; private ulong compressedSize; private ushort versionToExtract; // Version required to extract (library handles <= 2.0) private uint crc; private uint dosTime; private CompressionMethod method = CompressionMethod.Deflated; private byte[] extra; private string comment; private int flags; // general purpose bit flags private long zipFileIndex = -1; // used by ZipFile private long offset; // used by ZipFile and ZipOutputStream private bool forceZip64_; private byte cryptoCheckValue_; private int _aesVer; // Version number (2 = AE-2 ?). Assigned but not used. private int _aesEncryptionStrength; // Encryption strength 1 = 128 2 = 192 3 = 256 #endregion Instance Fields } }