ZipEntry.cs 37 KB

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