// 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
{
///
/// 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 : ICloneable
{
[Flags]
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 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;
}
///
/// 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("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
///
/// 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.
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;
}
///
/// 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;
}
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 {
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);
}
}
///
/// 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("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("value");
}
extra = new byte[value.Length];
Array.Copy(value, 0, extra, 0, value.Length);
}
}
}
#if !NET_1_1 && !NETCF_2_0
///
/// 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;
}
}
#else
///
/// AES unsupported prior to .NET 2.0
///
internal int AESKeySize;
#endif
///
/// 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)
{
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
}
///
/// 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) ) {
#if NETCF_1_0
throw new ArgumentOutOfRangeException("value");
#else
throw new ArgumentOutOfRangeException("value", "cannot exceed 65535");
#endif
}
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()
{
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
///
/// 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) == 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
}
}