| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186 | 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	{		[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 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;		}		/// <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("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		/// <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) {				result |= (((HostSystem == (int)HostSystemID.Msdos) ||					(HostSystem == (int)HostSystemID.WindowsNT)) &&					(ExternalFileAttributes & attributes) == attributes);			}			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 & 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;				}			}		}		/// <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 {				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);			}		}		/// <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("nameof(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("nameof(value)");					}					extra = new byte[value.Length];					Array.Copy(value, 0, extra, 0, value.Length);				}			}		}		/// <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;			}		}		/// <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)		{			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<NTTaggedData>();			if (ntData != null)				return ntData.LastModificationTime;#endif			// Check for Unix timestamp			ExtendedUnixData unixData = extraData.GetData<ExtendedUnixData>();			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)                #pragma warning disable 0219				int vendorId = extraData.ReadShort();       // 2-character vendor ID 0x4541 = "AE"                #pragma warning restore 0219				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");		}		/// <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)) {					throw new ArgumentOutOfRangeException("nameof(value)", "cannot exceed 65535");				}				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()		{			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		/// <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)) {				// 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_;		#pragma warning disable 0414		int _aesVer;                            // Version number (2 = AE-2 ?). Assigned but not used.		#pragma warning restore 0414		int _aesEncryptionStrength;             // Encryption strength 1 = 128 2 = 192 3 = 256		#endregion	}}
 |