ZipEntry.cs 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349
  1. using System;
  2. using System.IO;
  3. namespace ICSharpCode.SharpZipLib.Zip
  4. {
  5. /// <summary>
  6. /// Defines known values for the <see cref="HostSystemID"/> property.
  7. /// </summary>
  8. public enum HostSystemID
  9. {
  10. /// <summary>
  11. /// Host system = MSDOS
  12. /// </summary>
  13. Msdos = 0,
  14. /// <summary>
  15. /// Host system = Amiga
  16. /// </summary>
  17. Amiga = 1,
  18. /// <summary>
  19. /// Host system = Open VMS
  20. /// </summary>
  21. OpenVms = 2,
  22. /// <summary>
  23. /// Host system = Unix
  24. /// </summary>
  25. Unix = 3,
  26. /// <summary>
  27. /// Host system = VMCms
  28. /// </summary>
  29. VMCms = 4,
  30. /// <summary>
  31. /// Host system = Atari ST
  32. /// </summary>
  33. AtariST = 5,
  34. /// <summary>
  35. /// Host system = OS2
  36. /// </summary>
  37. OS2 = 6,
  38. /// <summary>
  39. /// Host system = Macintosh
  40. /// </summary>
  41. Macintosh = 7,
  42. /// <summary>
  43. /// Host system = ZSystem
  44. /// </summary>
  45. ZSystem = 8,
  46. /// <summary>
  47. /// Host system = Cpm
  48. /// </summary>
  49. Cpm = 9,
  50. /// <summary>
  51. /// Host system = Windows NT
  52. /// </summary>
  53. WindowsNT = 10,
  54. /// <summary>
  55. /// Host system = MVS
  56. /// </summary>
  57. MVS = 11,
  58. /// <summary>
  59. /// Host system = VSE
  60. /// </summary>
  61. Vse = 12,
  62. /// <summary>
  63. /// Host system = Acorn RISC
  64. /// </summary>
  65. AcornRisc = 13,
  66. /// <summary>
  67. /// Host system = VFAT
  68. /// </summary>
  69. Vfat = 14,
  70. /// <summary>
  71. /// Host system = Alternate MVS
  72. /// </summary>
  73. AlternateMvs = 15,
  74. /// <summary>
  75. /// Host system = BEOS
  76. /// </summary>
  77. BeOS = 16,
  78. /// <summary>
  79. /// Host system = Tandem
  80. /// </summary>
  81. Tandem = 17,
  82. /// <summary>
  83. /// Host system = OS400
  84. /// </summary>
  85. OS400 = 18,
  86. /// <summary>
  87. /// Host system = OSX
  88. /// </summary>
  89. OSX = 19,
  90. /// <summary>
  91. /// Host system = WinZIP AES
  92. /// </summary>
  93. WinZipAES = 99,
  94. }
  95. /// <summary>
  96. /// This class represents an entry in a zip archive. This can be a file
  97. /// or a directory
  98. /// ZipFile and ZipInputStream will give you instances of this class as
  99. /// information about the members in an archive. ZipOutputStream
  100. /// uses an instance of this class when creating an entry in a Zip file.
  101. /// <br/>
  102. /// <br/>Author of the original java version : Jochen Hoenicke
  103. /// </summary>
  104. public class ZipEntry
  105. {
  106. [Flags]
  107. private enum Known : byte
  108. {
  109. None = 0,
  110. Size = 0x01,
  111. CompressedSize = 0x02,
  112. Crc = 0x04,
  113. Time = 0x08,
  114. ExternalAttributes = 0x10,
  115. }
  116. #region Constructors
  117. /// <summary>
  118. /// Creates a zip entry with the given name.
  119. /// </summary>
  120. /// <param name="name">
  121. /// The name for this entry. Can include directory components.
  122. /// The convention for names is 'unix' style paths with relative names only.
  123. /// There are with no device names and path elements are separated by '/' characters.
  124. /// </param>
  125. /// <exception cref="ArgumentNullException">
  126. /// The name passed is null
  127. /// </exception>
  128. public ZipEntry(string name)
  129. : this(name, 0, ZipConstants.VersionMadeBy, CompressionMethod.Deflated)
  130. {
  131. }
  132. /// <summary>
  133. /// Creates a zip entry with the given name and version required to extract
  134. /// </summary>
  135. /// <param name="name">
  136. /// The name for this entry. Can include directory components.
  137. /// The convention for names is 'unix' style paths with no device names and
  138. /// path elements separated by '/' characters. This is not enforced see <see cref="CleanName(string)">CleanName</see>
  139. /// on how to ensure names are valid if this is desired.
  140. /// </param>
  141. /// <param name="versionRequiredToExtract">
  142. /// The minimum 'feature version' required this entry
  143. /// </param>
  144. /// <exception cref="ArgumentNullException">
  145. /// The name passed is null
  146. /// </exception>
  147. internal ZipEntry(string name, int versionRequiredToExtract)
  148. : this(name, versionRequiredToExtract, ZipConstants.VersionMadeBy,
  149. CompressionMethod.Deflated)
  150. {
  151. }
  152. /// <summary>
  153. /// Initializes an entry with the given name and made by information
  154. /// </summary>
  155. /// <param name="name">Name for this entry</param>
  156. /// <param name="madeByInfo">Version and HostSystem Information</param>
  157. /// <param name="versionRequiredToExtract">Minimum required zip feature version required to extract this entry</param>
  158. /// <param name="method">Compression method for this entry.</param>
  159. /// <exception cref="ArgumentNullException">
  160. /// The name passed is null
  161. /// </exception>
  162. /// <exception cref="ArgumentOutOfRangeException">
  163. /// versionRequiredToExtract should be 0 (auto-calculate) or > 10
  164. /// </exception>
  165. /// <remarks>
  166. /// This constructor is used by the ZipFile class when reading from the central header
  167. /// It is not generally useful, use the constructor specifying the name only.
  168. /// </remarks>
  169. internal ZipEntry(string name, int versionRequiredToExtract, int madeByInfo,
  170. CompressionMethod method)
  171. {
  172. if (name == null)
  173. {
  174. throw new ArgumentNullException(nameof(name));
  175. }
  176. if (name.Length > 0xffff)
  177. {
  178. throw new ArgumentException("Name is too long", nameof(name));
  179. }
  180. if ((versionRequiredToExtract != 0) && (versionRequiredToExtract < 10))
  181. {
  182. throw new ArgumentOutOfRangeException(nameof(versionRequiredToExtract));
  183. }
  184. this.DateTime = DateTime.Now;
  185. this.name = name;
  186. this.versionMadeBy = (ushort)madeByInfo;
  187. this.versionToExtract = (ushort)versionRequiredToExtract;
  188. this.method = method;
  189. IsUnicodeText = ZipStrings.UseUnicode;
  190. }
  191. /// <summary>
  192. /// Creates a deep copy of the given zip entry.
  193. /// </summary>
  194. /// <param name="entry">
  195. /// The entry to copy.
  196. /// </param>
  197. [Obsolete("Use Clone instead")]
  198. public ZipEntry(ZipEntry entry)
  199. {
  200. if (entry == null)
  201. {
  202. throw new ArgumentNullException(nameof(entry));
  203. }
  204. known = entry.known;
  205. name = entry.name;
  206. size = entry.size;
  207. compressedSize = entry.compressedSize;
  208. crc = entry.crc;
  209. dosTime = entry.dosTime;
  210. method = entry.method;
  211. comment = entry.comment;
  212. versionToExtract = entry.versionToExtract;
  213. versionMadeBy = entry.versionMadeBy;
  214. externalFileAttributes = entry.externalFileAttributes;
  215. flags = entry.flags;
  216. zipFileIndex = entry.zipFileIndex;
  217. offset = entry.offset;
  218. forceZip64_ = entry.forceZip64_;
  219. if (entry.extra != null)
  220. {
  221. extra = new byte[entry.extra.Length];
  222. Array.Copy(entry.extra, 0, extra, 0, entry.extra.Length);
  223. }
  224. }
  225. #endregion Constructors
  226. /// <summary>
  227. /// Get a value indicating wether the entry has a CRC value available.
  228. /// </summary>
  229. public bool HasCrc
  230. {
  231. get
  232. {
  233. return (known & Known.Crc) != 0;
  234. }
  235. }
  236. /// <summary>
  237. /// Get/Set flag indicating if entry is encrypted.
  238. /// A simple helper routine to aid interpretation of <see cref="Flags">flags</see>
  239. /// </summary>
  240. /// <remarks>This is an assistant that interprets the <see cref="Flags">flags</see> property.</remarks>
  241. public bool IsCrypted
  242. {
  243. get
  244. {
  245. return (flags & 1) != 0;
  246. }
  247. set
  248. {
  249. if (value)
  250. {
  251. flags |= 1;
  252. }
  253. else
  254. {
  255. flags &= ~1;
  256. }
  257. }
  258. }
  259. /// <summary>
  260. /// Get / set a flag indicating wether entry name and comment text are
  261. /// encoded in <a href="http://www.unicode.org">unicode UTF8</a>.
  262. /// </summary>
  263. /// <remarks>This is an assistant that interprets the <see cref="Flags">flags</see> property.</remarks>
  264. public bool IsUnicodeText
  265. {
  266. get
  267. {
  268. return (flags & (int)GeneralBitFlags.UnicodeText) != 0;
  269. }
  270. set
  271. {
  272. if (value)
  273. {
  274. flags |= (int)GeneralBitFlags.UnicodeText;
  275. }
  276. else
  277. {
  278. flags &= ~(int)GeneralBitFlags.UnicodeText;
  279. }
  280. }
  281. }
  282. /// <summary>
  283. /// Value used during password checking for PKZIP 2.0 / 'classic' encryption.
  284. /// </summary>
  285. internal byte CryptoCheckValue
  286. {
  287. get
  288. {
  289. return cryptoCheckValue_;
  290. }
  291. set
  292. {
  293. cryptoCheckValue_ = value;
  294. }
  295. }
  296. /// <summary>
  297. /// Get/Set general purpose bit flag for entry
  298. /// </summary>
  299. /// <remarks>
  300. /// General purpose bit flag<br/>
  301. /// <br/>
  302. /// Bit 0: If set, indicates the file is encrypted<br/>
  303. /// Bit 1-2 Only used for compression type 6 Imploding, and 8, 9 deflating<br/>
  304. /// Imploding:<br/>
  305. /// Bit 1 if set indicates an 8K sliding dictionary was used. If clear a 4k dictionary was used<br/>
  306. /// Bit 2 if set indicates 3 Shannon-Fanno trees were used to encode the sliding dictionary, 2 otherwise<br/>
  307. /// <br/>
  308. /// Deflating:<br/>
  309. /// Bit 2 Bit 1<br/>
  310. /// 0 0 Normal compression was used<br/>
  311. /// 0 1 Maximum compression was used<br/>
  312. /// 1 0 Fast compression was used<br/>
  313. /// 1 1 Super fast compression was used<br/>
  314. /// <br/>
  315. /// Bit 3: If set, the fields crc-32, compressed size
  316. /// and uncompressed size are were not able to be written during zip file creation
  317. /// The correct values are held in a data descriptor immediately following the compressed data. <br/>
  318. /// Bit 4: Reserved for use by PKZIP for enhanced deflating<br/>
  319. /// Bit 5: If set indicates the file contains compressed patch data<br/>
  320. /// Bit 6: If set indicates strong encryption was used.<br/>
  321. /// Bit 7-10: Unused or reserved<br/>
  322. /// Bit 11: If set the name and comments for this entry are in <a href="http://www.unicode.org">unicode</a>.<br/>
  323. /// Bit 12-15: Unused or reserved<br/>
  324. /// </remarks>
  325. /// <seealso cref="IsUnicodeText"></seealso>
  326. /// <seealso cref="IsCrypted"></seealso>
  327. public int Flags
  328. {
  329. get
  330. {
  331. return flags;
  332. }
  333. set
  334. {
  335. flags = value;
  336. }
  337. }
  338. /// <summary>
  339. /// Get/Set index of this entry in Zip file
  340. /// </summary>
  341. /// <remarks>This is only valid when the entry is part of a <see cref="ZipFile"></see></remarks>
  342. public long ZipFileIndex
  343. {
  344. get
  345. {
  346. return zipFileIndex;
  347. }
  348. set
  349. {
  350. zipFileIndex = value;
  351. }
  352. }
  353. /// <summary>
  354. /// Get/set offset for use in central header
  355. /// </summary>
  356. public long Offset
  357. {
  358. get
  359. {
  360. return offset;
  361. }
  362. set
  363. {
  364. offset = value;
  365. }
  366. }
  367. /// <summary>
  368. /// Get/Set external file attributes as an integer.
  369. /// The values of this are operating system dependant see
  370. /// <see cref="HostSystem">HostSystem</see> for details
  371. /// </summary>
  372. public int ExternalFileAttributes
  373. {
  374. get
  375. {
  376. if ((known & Known.ExternalAttributes) == 0)
  377. {
  378. return -1;
  379. }
  380. else
  381. {
  382. return externalFileAttributes;
  383. }
  384. }
  385. set
  386. {
  387. externalFileAttributes = value;
  388. known |= Known.ExternalAttributes;
  389. }
  390. }
  391. /// <summary>
  392. /// Get the version made by for this entry or zero if unknown.
  393. /// The value / 10 indicates the major version number, and
  394. /// the value mod 10 is the minor version number
  395. /// </summary>
  396. public int VersionMadeBy
  397. {
  398. get
  399. {
  400. return (versionMadeBy & 0xff);
  401. }
  402. }
  403. /// <summary>
  404. /// Get a value indicating this entry is for a DOS/Windows system.
  405. /// </summary>
  406. public bool IsDOSEntry
  407. {
  408. get
  409. {
  410. return ((HostSystem == (int)HostSystemID.Msdos) ||
  411. (HostSystem == (int)HostSystemID.WindowsNT));
  412. }
  413. }
  414. /// <summary>
  415. /// Test the external attributes for this <see cref="ZipEntry"/> to
  416. /// see if the external attributes are Dos based (including WINNT and variants)
  417. /// and match the values
  418. /// </summary>
  419. /// <param name="attributes">The attributes to test.</param>
  420. /// <returns>Returns true if the external attributes are known to be DOS/Windows
  421. /// based and have the same attributes set as the value passed.</returns>
  422. private bool HasDosAttributes(int attributes)
  423. {
  424. bool result = false;
  425. if ((known & Known.ExternalAttributes) != 0)
  426. {
  427. result |= (((HostSystem == (int)HostSystemID.Msdos) ||
  428. (HostSystem == (int)HostSystemID.WindowsNT)) &&
  429. (ExternalFileAttributes & attributes) == attributes);
  430. }
  431. return result;
  432. }
  433. /// <summary>
  434. /// Gets the compatability information for the <see cref="ExternalFileAttributes">external file attribute</see>
  435. /// If the external file attributes are compatible with MS-DOS and can be read
  436. /// by PKZIP for DOS version 2.04g then this value will be zero. Otherwise the value
  437. /// will be non-zero and identify the host system on which the attributes are compatible.
  438. /// </summary>
  439. ///
  440. /// <remarks>
  441. /// The values for this as defined in the Zip File format and by others are shown below. The values are somewhat
  442. /// misleading in some cases as they are not all used as shown. You should consult the relevant documentation
  443. /// to obtain up to date and correct information. The modified appnote by the infozip group is
  444. /// particularly helpful as it documents a lot of peculiarities. The document is however a little dated.
  445. /// <list type="table">
  446. /// <item>0 - MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)</item>
  447. /// <item>1 - Amiga</item>
  448. /// <item>2 - OpenVMS</item>
  449. /// <item>3 - Unix</item>
  450. /// <item>4 - VM/CMS</item>
  451. /// <item>5 - Atari ST</item>
  452. /// <item>6 - OS/2 HPFS</item>
  453. /// <item>7 - Macintosh</item>
  454. /// <item>8 - Z-System</item>
  455. /// <item>9 - CP/M</item>
  456. /// <item>10 - Windows NTFS</item>
  457. /// <item>11 - MVS (OS/390 - Z/OS)</item>
  458. /// <item>12 - VSE</item>
  459. /// <item>13 - Acorn Risc</item>
  460. /// <item>14 - VFAT</item>
  461. /// <item>15 - Alternate MVS</item>
  462. /// <item>16 - BeOS</item>
  463. /// <item>17 - Tandem</item>
  464. /// <item>18 - OS/400</item>
  465. /// <item>19 - OS/X (Darwin)</item>
  466. /// <item>99 - WinZip AES</item>
  467. /// <item>remainder - unused</item>
  468. /// </list>
  469. /// </remarks>
  470. public int HostSystem
  471. {
  472. get
  473. {
  474. return (versionMadeBy >> 8) & 0xff;
  475. }
  476. set
  477. {
  478. versionMadeBy &= 0x00ff;
  479. versionMadeBy |= (ushort)((value & 0xff) << 8);
  480. }
  481. }
  482. /// <summary>
  483. /// Get minimum Zip feature version required to extract this entry
  484. /// </summary>
  485. /// <remarks>
  486. /// Minimum features are defined as:<br/>
  487. /// 1.0 - Default value<br/>
  488. /// 1.1 - File is a volume label<br/>
  489. /// 2.0 - File is a folder/directory<br/>
  490. /// 2.0 - File is compressed using Deflate compression<br/>
  491. /// 2.0 - File is encrypted using traditional encryption<br/>
  492. /// 2.1 - File is compressed using Deflate64<br/>
  493. /// 2.5 - File is compressed using PKWARE DCL Implode<br/>
  494. /// 2.7 - File is a patch data set<br/>
  495. /// 4.5 - File uses Zip64 format extensions<br/>
  496. /// 4.6 - File is compressed using BZIP2 compression<br/>
  497. /// 5.0 - File is encrypted using DES<br/>
  498. /// 5.0 - File is encrypted using 3DES<br/>
  499. /// 5.0 - File is encrypted using original RC2 encryption<br/>
  500. /// 5.0 - File is encrypted using RC4 encryption<br/>
  501. /// 5.1 - File is encrypted using AES encryption<br/>
  502. /// 5.1 - File is encrypted using corrected RC2 encryption<br/>
  503. /// 5.1 - File is encrypted using corrected RC2-64 encryption<br/>
  504. /// 6.1 - File is encrypted using non-OAEP key wrapping<br/>
  505. /// 6.2 - Central directory encryption (not confirmed yet)<br/>
  506. /// 6.3 - File is compressed using LZMA<br/>
  507. /// 6.3 - File is compressed using PPMD+<br/>
  508. /// 6.3 - File is encrypted using Blowfish<br/>
  509. /// 6.3 - File is encrypted using Twofish<br/>
  510. /// </remarks>
  511. /// <seealso cref="CanDecompress"></seealso>
  512. public int Version
  513. {
  514. get
  515. {
  516. // Return recorded version if known.
  517. if (versionToExtract != 0)
  518. {
  519. return versionToExtract & 0x00ff; // Only lower order byte. High order is O/S file system.
  520. }
  521. else
  522. {
  523. int result = 10;
  524. if (AESKeySize > 0)
  525. {
  526. result = ZipConstants.VERSION_AES; // Ver 5.1 = AES
  527. }
  528. else if (CentralHeaderRequiresZip64)
  529. {
  530. result = ZipConstants.VersionZip64;
  531. }
  532. else if (CompressionMethod.Deflated == method)
  533. {
  534. result = 20;
  535. }
  536. else if (IsDirectory == true)
  537. {
  538. result = 20;
  539. }
  540. else if (IsCrypted == true)
  541. {
  542. result = 20;
  543. }
  544. else if (HasDosAttributes(0x08))
  545. {
  546. result = 11;
  547. }
  548. return result;
  549. }
  550. }
  551. }
  552. /// <summary>
  553. /// Get a value indicating whether this entry can be decompressed by the library.
  554. /// </summary>
  555. /// <remarks>This is based on the <see cref="Version"></see> and
  556. /// wether the <see cref="IsCompressionMethodSupported()">compression method</see> is supported.</remarks>
  557. public bool CanDecompress
  558. {
  559. get
  560. {
  561. return (Version <= ZipConstants.VersionMadeBy) &&
  562. ((Version == 10) ||
  563. (Version == 11) ||
  564. (Version == 20) ||
  565. (Version == 45) ||
  566. (Version == 51)) &&
  567. IsCompressionMethodSupported();
  568. }
  569. }
  570. /// <summary>
  571. /// Force this entry to be recorded using Zip64 extensions.
  572. /// </summary>
  573. public void ForceZip64()
  574. {
  575. forceZip64_ = true;
  576. }
  577. /// <summary>
  578. /// Get a value indicating wether Zip64 extensions were forced.
  579. /// </summary>
  580. /// <returns>A <see cref="bool"/> value of true if Zip64 extensions have been forced on; false if not.</returns>
  581. public bool IsZip64Forced()
  582. {
  583. return forceZip64_;
  584. }
  585. /// <summary>
  586. /// Gets a value indicating if the entry requires Zip64 extensions
  587. /// to store the full entry values.
  588. /// </summary>
  589. /// <value>A <see cref="bool"/> value of true if a local header requires Zip64 extensions; false if not.</value>
  590. public bool LocalHeaderRequiresZip64
  591. {
  592. get
  593. {
  594. bool result = forceZip64_;
  595. if (!result)
  596. {
  597. ulong trueCompressedSize = compressedSize;
  598. if ((versionToExtract == 0) && IsCrypted)
  599. {
  600. trueCompressedSize += ZipConstants.CryptoHeaderSize;
  601. }
  602. // TODO: A better estimation of the true limit based on compression overhead should be used
  603. // to determine when an entry should use Zip64.
  604. result =
  605. ((this.size >= uint.MaxValue) || (trueCompressedSize >= uint.MaxValue)) &&
  606. ((versionToExtract == 0) || (versionToExtract >= ZipConstants.VersionZip64));
  607. }
  608. return result;
  609. }
  610. }
  611. /// <summary>
  612. /// Get a value indicating wether the central directory entry requires Zip64 extensions to be stored.
  613. /// </summary>
  614. public bool CentralHeaderRequiresZip64
  615. {
  616. get
  617. {
  618. return LocalHeaderRequiresZip64 || (offset >= uint.MaxValue);
  619. }
  620. }
  621. /// <summary>
  622. /// Get/Set DosTime value.
  623. /// </summary>
  624. /// <remarks>
  625. /// The MS-DOS date format can only represent dates between 1/1/1980 and 12/31/2107.
  626. /// </remarks>
  627. public long DosTime
  628. {
  629. get
  630. {
  631. if ((known & Known.Time) == 0)
  632. {
  633. return 0;
  634. }
  635. else
  636. {
  637. return dosTime;
  638. }
  639. }
  640. set
  641. {
  642. unchecked
  643. {
  644. dosTime = (uint)value;
  645. }
  646. known |= Known.Time;
  647. }
  648. }
  649. /// <summary>
  650. /// Gets/Sets the time of last modification of the entry.
  651. /// </summary>
  652. /// <remarks>
  653. /// The <see cref="DosTime"></see> property is updated to match this as far as possible.
  654. /// </remarks>
  655. public DateTime DateTime
  656. {
  657. get
  658. {
  659. uint sec = Math.Min(59, 2 * (dosTime & 0x1f));
  660. uint min = Math.Min(59, (dosTime >> 5) & 0x3f);
  661. uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f);
  662. uint mon = Math.Max(1, Math.Min(12, ((dosTime >> 21) & 0xf)));
  663. uint year = ((dosTime >> 25) & 0x7f) + 1980;
  664. int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((dosTime >> 16) & 0x1f)));
  665. return new System.DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec);
  666. }
  667. set
  668. {
  669. var year = (uint)value.Year;
  670. var month = (uint)value.Month;
  671. var day = (uint)value.Day;
  672. var hour = (uint)value.Hour;
  673. var minute = (uint)value.Minute;
  674. var second = (uint)value.Second;
  675. if (year < 1980)
  676. {
  677. year = 1980;
  678. month = 1;
  679. day = 1;
  680. hour = 0;
  681. minute = 0;
  682. second = 0;
  683. }
  684. else if (year > 2107)
  685. {
  686. year = 2107;
  687. month = 12;
  688. day = 31;
  689. hour = 23;
  690. minute = 59;
  691. second = 59;
  692. }
  693. DosTime = ((year - 1980) & 0x7f) << 25 |
  694. (month << 21) |
  695. (day << 16) |
  696. (hour << 11) |
  697. (minute << 5) |
  698. (second >> 1);
  699. }
  700. }
  701. /// <summary>
  702. /// Returns the entry name.
  703. /// </summary>
  704. /// <remarks>
  705. /// The unix naming convention is followed.
  706. /// Path components in the entry should always separated by forward slashes ('/').
  707. /// Dos device names like C: should also be removed.
  708. /// See the <see cref="ZipNameTransform"/> class, or <see cref="CleanName(string)"/>
  709. ///</remarks>
  710. public string Name
  711. {
  712. get
  713. {
  714. return name;
  715. }
  716. }
  717. /// <summary>
  718. /// Gets/Sets the size of the uncompressed data.
  719. /// </summary>
  720. /// <returns>
  721. /// The size or -1 if unknown.
  722. /// </returns>
  723. /// <remarks>Setting the size before adding an entry to an archive can help
  724. /// avoid compatability problems with some archivers which dont understand Zip64 extensions.</remarks>
  725. public long Size
  726. {
  727. get
  728. {
  729. return (known & Known.Size) != 0 ? (long)size : -1L;
  730. }
  731. set
  732. {
  733. this.size = (ulong)value;
  734. this.known |= Known.Size;
  735. }
  736. }
  737. /// <summary>
  738. /// Gets/Sets the size of the compressed data.
  739. /// </summary>
  740. /// <returns>
  741. /// The compressed entry size or -1 if unknown.
  742. /// </returns>
  743. public long CompressedSize
  744. {
  745. get
  746. {
  747. return (known & Known.CompressedSize) != 0 ? (long)compressedSize : -1L;
  748. }
  749. set
  750. {
  751. this.compressedSize = (ulong)value;
  752. this.known |= Known.CompressedSize;
  753. }
  754. }
  755. /// <summary>
  756. /// Gets/Sets the crc of the uncompressed data.
  757. /// </summary>
  758. /// <exception cref="System.ArgumentOutOfRangeException">
  759. /// Crc is not in the range 0..0xffffffffL
  760. /// </exception>
  761. /// <returns>
  762. /// The crc value or -1 if unknown.
  763. /// </returns>
  764. public long Crc
  765. {
  766. get
  767. {
  768. return (known & Known.Crc) != 0 ? crc & 0xffffffffL : -1L;
  769. }
  770. set
  771. {
  772. if (((ulong)crc & 0xffffffff00000000L) != 0)
  773. {
  774. throw new ArgumentOutOfRangeException(nameof(value));
  775. }
  776. this.crc = (uint)value;
  777. this.known |= Known.Crc;
  778. }
  779. }
  780. /// <summary>
  781. /// Gets/Sets the compression method. Only Deflated and Stored are supported.
  782. /// </summary>
  783. /// <returns>
  784. /// The compression method for this entry
  785. /// </returns>
  786. /// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Deflated"/>
  787. /// <see cref="ICSharpCode.SharpZipLib.Zip.CompressionMethod.Stored"/>
  788. public CompressionMethod CompressionMethod
  789. {
  790. get
  791. {
  792. return method;
  793. }
  794. set
  795. {
  796. if (!IsCompressionMethodSupported(value))
  797. {
  798. throw new NotSupportedException("Compression method not supported");
  799. }
  800. this.method = value;
  801. }
  802. }
  803. /// <summary>
  804. /// Gets the compression method for outputting to the local or central header.
  805. /// Returns same value as CompressionMethod except when AES encrypting, which
  806. /// places 99 in the method and places the real method in the extra data.
  807. /// </summary>
  808. internal CompressionMethod CompressionMethodForHeader
  809. {
  810. get
  811. {
  812. return (AESKeySize > 0) ? CompressionMethod.WinZipAES : method;
  813. }
  814. }
  815. /// <summary>
  816. /// Gets/Sets the extra data.
  817. /// </summary>
  818. /// <exception cref="System.ArgumentOutOfRangeException">
  819. /// Extra data is longer than 64KB (0xffff) bytes.
  820. /// </exception>
  821. /// <returns>
  822. /// Extra data or null if not set.
  823. /// </returns>
  824. public byte[] ExtraData
  825. {
  826. get
  827. {
  828. // TODO: This is slightly safer but less efficient. Think about wether it should change.
  829. // return (byte[]) extra.Clone();
  830. return extra;
  831. }
  832. set
  833. {
  834. if (value == null)
  835. {
  836. extra = null;
  837. }
  838. else
  839. {
  840. if (value.Length > 0xffff)
  841. {
  842. throw new System.ArgumentOutOfRangeException(nameof(value));
  843. }
  844. extra = new byte[value.Length];
  845. Array.Copy(value, 0, extra, 0, value.Length);
  846. }
  847. }
  848. }
  849. /// <summary>
  850. /// For AES encrypted files returns or sets the number of bits of encryption (128, 192 or 256).
  851. /// When setting, only 0 (off), 128 or 256 is supported.
  852. /// </summary>
  853. public int AESKeySize
  854. {
  855. get
  856. {
  857. // the strength (1 or 3) is in the entry header
  858. switch (_aesEncryptionStrength)
  859. {
  860. case 0:
  861. return 0; // Not AES
  862. case 1:
  863. return 128;
  864. case 2:
  865. return 192; // Not used by WinZip
  866. case 3:
  867. return 256;
  868. default:
  869. throw new ZipException("Invalid AESEncryptionStrength " + _aesEncryptionStrength);
  870. }
  871. }
  872. set
  873. {
  874. switch (value)
  875. {
  876. case 0:
  877. _aesEncryptionStrength = 0;
  878. break;
  879. case 128:
  880. _aesEncryptionStrength = 1;
  881. break;
  882. case 256:
  883. _aesEncryptionStrength = 3;
  884. break;
  885. default:
  886. throw new ZipException("AESKeySize must be 0, 128 or 256: " + value);
  887. }
  888. }
  889. }
  890. /// <summary>
  891. /// AES Encryption strength for storage in extra data in entry header.
  892. /// 1 is 128 bit, 2 is 192 bit, 3 is 256 bit.
  893. /// </summary>
  894. internal byte AESEncryptionStrength
  895. {
  896. get
  897. {
  898. return (byte)_aesEncryptionStrength;
  899. }
  900. }
  901. /// <summary>
  902. /// Returns the length of the salt, in bytes
  903. /// </summary>
  904. internal int AESSaltLen
  905. {
  906. get
  907. {
  908. // Key size -> Salt length: 128 bits = 8 bytes, 192 bits = 12 bytes, 256 bits = 16 bytes.
  909. return AESKeySize / 16;
  910. }
  911. }
  912. /// <summary>
  913. /// Number of extra bytes required to hold the AES Header fields (Salt, Pwd verify, AuthCode)
  914. /// </summary>
  915. internal int AESOverheadSize
  916. {
  917. get
  918. {
  919. // File format:
  920. // Bytes Content
  921. // Variable Salt value
  922. // 2 Password verification value
  923. // Variable Encrypted file data
  924. // 10 Authentication code
  925. return 12 + AESSaltLen;
  926. }
  927. }
  928. /// <summary>
  929. /// Process extra data fields updating the entry based on the contents.
  930. /// </summary>
  931. /// <param name="localHeader">True if the extra data fields should be handled
  932. /// for a local header, rather than for a central header.
  933. /// </param>
  934. internal void ProcessExtraData(bool localHeader)
  935. {
  936. var extraData = new ZipExtraData(this.extra);
  937. if (extraData.Find(0x0001))
  938. {
  939. // Version required to extract is ignored here as some archivers dont set it correctly
  940. // in theory it should be version 45 or higher
  941. // The recorded size will change but remember that this is zip64.
  942. forceZip64_ = true;
  943. if (extraData.ValueLength < 4)
  944. {
  945. throw new ZipException("Extra data extended Zip64 information length is invalid");
  946. }
  947. // (localHeader ||) was deleted, because actually there is no specific difference with reading sizes between local header & central directory
  948. // https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
  949. // ...
  950. // 4.4 Explanation of fields
  951. // ...
  952. // 4.4.8 compressed size: (4 bytes)
  953. // 4.4.9 uncompressed size: (4 bytes)
  954. //
  955. // The size of the file compressed (4.4.8) and uncompressed,
  956. // (4.4.9) respectively. When a decryption header is present it
  957. // will be placed in front of the file data and the value of the
  958. // compressed file size will include the bytes of the decryption
  959. // header. If bit 3 of the general purpose bit flag is set,
  960. // these fields are set to zero in the local header and the
  961. // correct values are put in the data descriptor and
  962. // in the central directory. If an archive is in ZIP64 format
  963. // and the value in this field is 0xFFFFFFFF, the size will be
  964. // in the corresponding 8 byte ZIP64 extended information
  965. // extra field. When encrypting the central directory, if the
  966. // local header is not in ZIP64 format and general purpose bit
  967. // flag 13 is set indicating masking, the value stored for the
  968. // uncompressed size in the Local Header will be zero.
  969. //
  970. // Othewise there is problem with minizip implementation
  971. if (size == uint.MaxValue)
  972. {
  973. size = (ulong)extraData.ReadLong();
  974. }
  975. if (compressedSize == uint.MaxValue)
  976. {
  977. compressedSize = (ulong)extraData.ReadLong();
  978. }
  979. if (!localHeader && (offset == uint.MaxValue))
  980. {
  981. offset = extraData.ReadLong();
  982. }
  983. // Disk number on which file starts is ignored
  984. }
  985. else
  986. {
  987. if (
  988. ((versionToExtract & 0xff) >= ZipConstants.VersionZip64) &&
  989. ((size == uint.MaxValue) || (compressedSize == uint.MaxValue))
  990. )
  991. {
  992. throw new ZipException("Zip64 Extended information required but is missing.");
  993. }
  994. }
  995. DateTime = GetDateTime(extraData);
  996. if (method == CompressionMethod.WinZipAES)
  997. {
  998. ProcessAESExtraData(extraData);
  999. }
  1000. }
  1001. private DateTime GetDateTime(ZipExtraData extraData)
  1002. {
  1003. // Check for NT timestamp
  1004. // NOTE: Disable by default to match behavior of InfoZIP
  1005. #if RESPECT_NT_TIMESTAMP
  1006. NTTaggedData ntData = extraData.GetData<NTTaggedData>();
  1007. if (ntData != null)
  1008. return ntData.LastModificationTime;
  1009. #endif
  1010. // Check for Unix timestamp
  1011. ExtendedUnixData unixData = extraData.GetData<ExtendedUnixData>();
  1012. if (unixData != null &&
  1013. // Only apply modification time, but require all other values to be present
  1014. // This is done to match InfoZIP's behaviour
  1015. ((unixData.Include & ExtendedUnixData.Flags.ModificationTime) != 0) &&
  1016. ((unixData.Include & ExtendedUnixData.Flags.AccessTime) != 0) &&
  1017. ((unixData.Include & ExtendedUnixData.Flags.CreateTime) != 0))
  1018. return unixData.ModificationTime;
  1019. // Fall back to DOS time
  1020. uint sec = Math.Min(59, 2 * (dosTime & 0x1f));
  1021. uint min = Math.Min(59, (dosTime >> 5) & 0x3f);
  1022. uint hrs = Math.Min(23, (dosTime >> 11) & 0x1f);
  1023. uint mon = Math.Max(1, Math.Min(12, ((dosTime >> 21) & 0xf)));
  1024. uint year = ((dosTime >> 25) & 0x7f) + 1980;
  1025. int day = Math.Max(1, Math.Min(DateTime.DaysInMonth((int)year, (int)mon), (int)((dosTime >> 16) & 0x1f)));
  1026. return new DateTime((int)year, (int)mon, day, (int)hrs, (int)min, (int)sec, DateTimeKind.Utc);
  1027. }
  1028. // For AES the method in the entry is 99, and the real compression method is in the extradata
  1029. //
  1030. private void ProcessAESExtraData(ZipExtraData extraData)
  1031. {
  1032. if (extraData.Find(0x9901))
  1033. {
  1034. // Set version for Zipfile.CreateAndInitDecryptionStream
  1035. versionToExtract = ZipConstants.VERSION_AES; // Ver 5.1 = AES see "Version" getter
  1036. //
  1037. // Unpack AES extra data field see http://www.winzip.com/aes_info.htm
  1038. int length = extraData.ValueLength; // Data size currently 7
  1039. if (length < 7)
  1040. throw new ZipException("AES Extra Data Length " + length + " invalid.");
  1041. int ver = extraData.ReadShort(); // Version number (1=AE-1 2=AE-2)
  1042. int vendorId = extraData.ReadShort(); // 2-character vendor ID 0x4541 = "AE"
  1043. int encrStrength = extraData.ReadByte(); // encryption strength 1 = 128 2 = 192 3 = 256
  1044. int actualCompress = extraData.ReadShort(); // The actual compression method used to compress the file
  1045. _aesVer = ver;
  1046. _aesEncryptionStrength = encrStrength;
  1047. method = (CompressionMethod)actualCompress;
  1048. }
  1049. else
  1050. throw new ZipException("AES Extra Data missing");
  1051. }
  1052. /// <summary>
  1053. /// Gets/Sets the entry comment.
  1054. /// </summary>
  1055. /// <exception cref="System.ArgumentOutOfRangeException">
  1056. /// If comment is longer than 0xffff.
  1057. /// </exception>
  1058. /// <returns>
  1059. /// The comment or null if not set.
  1060. /// </returns>
  1061. /// <remarks>
  1062. /// A comment is only available for entries when read via the <see cref="ZipFile"/> class.
  1063. /// The <see cref="ZipInputStream"/> class doesnt have the comment data available.
  1064. /// </remarks>
  1065. public string Comment
  1066. {
  1067. get
  1068. {
  1069. return comment;
  1070. }
  1071. set
  1072. {
  1073. // This test is strictly incorrect as the length is in characters
  1074. // while the storage limit is in bytes.
  1075. // While the test is partially correct in that a comment of this length or greater
  1076. // is definitely invalid, shorter comments may also have an invalid length
  1077. // where there are multi-byte characters
  1078. // The full test is not possible here however as the code page to apply conversions with
  1079. // isnt available.
  1080. if ((value != null) && (value.Length > 0xffff))
  1081. {
  1082. throw new ArgumentOutOfRangeException(nameof(value), "cannot exceed 65535");
  1083. }
  1084. comment = value;
  1085. }
  1086. }
  1087. /// <summary>
  1088. /// Gets a value indicating if the entry is a directory.
  1089. /// however.
  1090. /// </summary>
  1091. /// <remarks>
  1092. /// A directory is determined by an entry name with a trailing slash '/'.
  1093. /// The external file attributes can also indicate an entry is for a directory.
  1094. /// Currently only dos/windows attributes are tested in this manner.
  1095. /// The trailing slash convention should always be followed.
  1096. /// </remarks>
  1097. public bool IsDirectory
  1098. {
  1099. get
  1100. {
  1101. int nameLength = name.Length;
  1102. bool result =
  1103. ((nameLength > 0) &&
  1104. ((name[nameLength - 1] == '/') || (name[nameLength - 1] == '\\'))) ||
  1105. HasDosAttributes(16)
  1106. ;
  1107. return result;
  1108. }
  1109. }
  1110. /// <summary>
  1111. /// Get a value of true if the entry appears to be a file; false otherwise
  1112. /// </summary>
  1113. /// <remarks>
  1114. /// This only takes account of DOS/Windows attributes. Other operating systems are ignored.
  1115. /// For linux and others the result may be incorrect.
  1116. /// </remarks>
  1117. public bool IsFile
  1118. {
  1119. get
  1120. {
  1121. return !IsDirectory && !HasDosAttributes(8);
  1122. }
  1123. }
  1124. /// <summary>
  1125. /// Test entry to see if data can be extracted.
  1126. /// </summary>
  1127. /// <returns>Returns true if data can be extracted for this entry; false otherwise.</returns>
  1128. public bool IsCompressionMethodSupported()
  1129. {
  1130. return IsCompressionMethodSupported(CompressionMethod);
  1131. }
  1132. #region ICloneable Members
  1133. /// <summary>
  1134. /// Creates a copy of this zip entry.
  1135. /// </summary>
  1136. /// <returns>An <see cref="Object"/> that is a copy of the current instance.</returns>
  1137. public object Clone()
  1138. {
  1139. var result = (ZipEntry)this.MemberwiseClone();
  1140. // Ensure extra data is unique if it exists.
  1141. if (extra != null)
  1142. {
  1143. result.extra = new byte[extra.Length];
  1144. Array.Copy(extra, 0, result.extra, 0, extra.Length);
  1145. }
  1146. return result;
  1147. }
  1148. #endregion ICloneable Members
  1149. /// <summary>
  1150. /// Gets a string representation of this ZipEntry.
  1151. /// </summary>
  1152. /// <returns>A readable textual representation of this <see cref="ZipEntry"/></returns>
  1153. public override string ToString()
  1154. {
  1155. return name;
  1156. }
  1157. /// <summary>
  1158. /// Test a <see cref="CompressionMethod">compression method</see> to see if this library
  1159. /// supports extracting data compressed with that method
  1160. /// </summary>
  1161. /// <param name="method">The compression method to test.</param>
  1162. /// <returns>Returns true if the compression method is supported; false otherwise</returns>
  1163. public static bool IsCompressionMethodSupported(CompressionMethod method)
  1164. {
  1165. return
  1166. (method == CompressionMethod.Deflated) ||
  1167. (method == CompressionMethod.Stored);
  1168. }
  1169. /// <summary>
  1170. /// Cleans a name making it conform to Zip file conventions.
  1171. /// Devices names ('c:\') and UNC share names ('\\server\share') are removed
  1172. /// and forward slashes ('\') are converted to back slashes ('/').
  1173. /// Names are made relative by trimming leading slashes which is compatible
  1174. /// with the ZIP naming convention.
  1175. /// </summary>
  1176. /// <param name="name">The name to clean</param>
  1177. /// <returns>The 'cleaned' name.</returns>
  1178. /// <remarks>
  1179. /// The <seealso cref="ZipNameTransform">Zip name transform</seealso> class is more flexible.
  1180. /// </remarks>
  1181. public static string CleanName(string name)
  1182. {
  1183. if (name == null)
  1184. {
  1185. return string.Empty;
  1186. }
  1187. if (Path.IsPathRooted(name))
  1188. {
  1189. // NOTE:
  1190. // for UNC names... \\machine\share\zoom\beet.txt gives \zoom\beet.txt
  1191. name = name.Substring(Path.GetPathRoot(name).Length);
  1192. }
  1193. name = name.Replace(@"\", "/");
  1194. while ((name.Length > 0) && (name[0] == '/'))
  1195. {
  1196. name = name.Remove(0, 1);
  1197. }
  1198. return name;
  1199. }
  1200. #region Instance Fields
  1201. private Known known;
  1202. private int externalFileAttributes = -1; // contains external attributes (O/S dependant)
  1203. private ushort versionMadeBy; // Contains host system and version information
  1204. // only relevant for central header entries
  1205. private string name;
  1206. private ulong size;
  1207. private ulong compressedSize;
  1208. private ushort versionToExtract; // Version required to extract (library handles <= 2.0)
  1209. private uint crc;
  1210. private uint dosTime;
  1211. private CompressionMethod method = CompressionMethod.Deflated;
  1212. private byte[] extra;
  1213. private string comment;
  1214. private int flags; // general purpose bit flags
  1215. private long zipFileIndex = -1; // used by ZipFile
  1216. private long offset; // used by ZipFile and ZipOutputStream
  1217. private bool forceZip64_;
  1218. private byte cryptoCheckValue_;
  1219. private int _aesVer; // Version number (2 = AE-2 ?). Assigned but not used.
  1220. private int _aesEncryptionStrength; // Encryption strength 1 = 128 2 = 192 3 = 256
  1221. #endregion Instance Fields
  1222. }
  1223. }