| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349 |
- 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]
- private 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 = name;
- this.versionMadeBy = (ushort)madeByInfo;
- this.versionToExtract = (ushort)versionRequiredToExtract;
- this.method = method;
- IsUnicodeText = ZipStrings.UseUnicode;
- }
- /// <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 Constructors
- /// <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>
- 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;
- }
- /// <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 &= 0x00ff;
- 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 for Zipfile.CreateAndInitDecryptionStream
- versionToExtract = ZipConstants.VERSION_AES; // Ver 5.1 = AES see "Version" getter
- //
- // 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");
- }
- /// <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 ICloneable Members
- /// <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
- 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
- }
- }
|