ZipEntry.Extract.cs 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464
  1. // ZipEntry.Extract.cs
  2. // ------------------------------------------------------------------
  3. //
  4. // Copyright (c) 2009-2011 Dino Chiesa
  5. // All rights reserved.
  6. //
  7. // This code module is part of DotNetZip, a zipfile class library.
  8. //
  9. // ------------------------------------------------------------------
  10. //
  11. // This code is licensed under the Microsoft Public License.
  12. // See the file License.txt for the license details.
  13. // More info on: http://dotnetzip.codeplex.com
  14. //
  15. // ------------------------------------------------------------------
  16. //
  17. // last saved (in emacs):
  18. // Time-stamp: <2011-August-06 18:08:21>
  19. //
  20. // ------------------------------------------------------------------
  21. //
  22. // This module defines logic for Extract methods on the ZipEntry class.
  23. //
  24. // ------------------------------------------------------------------
  25. using System;
  26. using System.IO;
  27. namespace Ionic.Zip
  28. {
  29. public partial class ZipEntry
  30. {
  31. /// <summary>
  32. /// Extract the entry to the filesystem, starting at the current
  33. /// working directory.
  34. /// </summary>
  35. ///
  36. /// <overloads>
  37. /// This method has a bunch of overloads! One of them is sure to
  38. /// be the right one for you... If you don't like these, check
  39. /// out the <c>ExtractWithPassword()</c> methods.
  40. /// </overloads>
  41. ///
  42. /// <seealso cref="Ionic.Zip.ZipEntry.ExtractExistingFile"/>
  43. /// <seealso cref="ZipEntry.Extract(ExtractExistingFileAction)"/>
  44. ///
  45. /// <remarks>
  46. ///
  47. /// <para>
  48. /// This method extracts an entry from a zip file into the current
  49. /// working directory. The path of the entry as extracted is the full
  50. /// path as specified in the zip archive, relative to the current
  51. /// working directory. After the file is extracted successfully, the
  52. /// file attributes and timestamps are set.
  53. /// </para>
  54. ///
  55. /// <para>
  56. /// The action taken when extraction an entry would overwrite an
  57. /// existing file is determined by the <see cref="ExtractExistingFile"
  58. /// /> property.
  59. /// </para>
  60. ///
  61. /// <para>
  62. /// Within the call to <c>Extract()</c>, the content for the entry is
  63. /// written into a filesystem file, and then the last modified time of the
  64. /// file is set according to the <see cref="LastModified"/> property on
  65. /// the entry. See the remarks the <see cref="LastModified"/> property for
  66. /// some details about the last modified time.
  67. /// </para>
  68. ///
  69. /// </remarks>
  70. public void Extract()
  71. {
  72. InternalExtractToBaseDir(".", null, _container, _Source, FileName);
  73. }
  74. /// <summary>
  75. /// Extract the entry to a file in the filesystem, using the specified
  76. /// behavior when extraction would overwrite an existing file.
  77. /// </summary>
  78. ///
  79. /// <remarks>
  80. /// <para>
  81. /// See the remarks on the <see cref="LastModified"/> property, for some
  82. /// details about how the last modified time of the file is set after
  83. /// extraction.
  84. /// </para>
  85. /// </remarks>
  86. ///
  87. /// <param name="extractExistingFile">
  88. /// The action to take if extraction would overwrite an existing file.
  89. /// </param>
  90. public void Extract(ExtractExistingFileAction extractExistingFile)
  91. {
  92. ExtractExistingFile = extractExistingFile;
  93. InternalExtractToBaseDir(".", null, _container, _Source, FileName);
  94. }
  95. /// <summary>
  96. /// Extracts the entry to the specified stream.
  97. /// </summary>
  98. ///
  99. /// <remarks>
  100. /// <para>
  101. /// The caller can specify any write-able stream, for example a <see
  102. /// cref="System.IO.FileStream"/>, a <see
  103. /// cref="System.IO.MemoryStream"/>, or ASP.NET's
  104. /// <c>Response.OutputStream</c>. The content will be decrypted and
  105. /// decompressed as necessary. If the entry is encrypted and no password
  106. /// is provided, this method will throw.
  107. /// </para>
  108. /// <para>
  109. /// The position on the stream is not reset by this method before it extracts.
  110. /// You may want to call stream.Seek() before calling ZipEntry.Extract().
  111. /// </para>
  112. /// </remarks>
  113. ///
  114. /// <param name="stream">
  115. /// the stream to which the entry should be extracted.
  116. /// </param>
  117. ///
  118. public void Extract(Stream stream)
  119. {
  120. InternalExtractToStream(stream, null, _container, _Source, FileName);
  121. }
  122. /// <summary>
  123. /// Extract the entry to the filesystem, starting at the specified base
  124. /// directory.
  125. /// </summary>
  126. ///
  127. /// <param name="baseDirectory">the pathname of the base directory</param>
  128. ///
  129. /// <seealso cref="Ionic.Zip.ZipEntry.ExtractExistingFile"/>
  130. /// <seealso cref="Ionic.Zip.ZipEntry.Extract(string, ExtractExistingFileAction)"/>
  131. ///
  132. /// <example>
  133. /// This example extracts only the entries in a zip file that are .txt files,
  134. /// into a directory called "textfiles".
  135. /// <code lang="C#">
  136. /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip"))
  137. /// {
  138. /// foreach (string s1 in zip.EntryFilenames)
  139. /// {
  140. /// if (s1.EndsWith(".txt"))
  141. /// {
  142. /// zip[s1].Extract("textfiles");
  143. /// }
  144. /// }
  145. /// }
  146. /// </code>
  147. /// <code lang="VB">
  148. /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip")
  149. /// Dim s1 As String
  150. /// For Each s1 In zip.EntryFilenames
  151. /// If s1.EndsWith(".txt") Then
  152. /// zip(s1).Extract("textfiles")
  153. /// End If
  154. /// Next
  155. /// End Using
  156. /// </code>
  157. /// </example>
  158. ///
  159. /// <remarks>
  160. ///
  161. /// <para>
  162. /// Using this method, existing entries in the filesystem will not be
  163. /// overwritten. If you would like to force the overwrite of existing
  164. /// files, see the <see cref="ExtractExistingFile"/> property, or call
  165. /// <see cref="Extract(string, ExtractExistingFileAction)"/>.
  166. /// </para>
  167. ///
  168. /// <para>
  169. /// See the remarks on the <see cref="LastModified"/> property, for some
  170. /// details about how the last modified time of the created file is set.
  171. /// </para>
  172. /// </remarks>
  173. public void Extract(string baseDirectory)
  174. {
  175. InternalExtractToBaseDir(baseDirectory, null, _container, _Source, FileName);
  176. }
  177. /// <summary>
  178. /// Extract the entry to the filesystem, starting at the specified base
  179. /// directory, and using the specified behavior when extraction would
  180. /// overwrite an existing file.
  181. /// </summary>
  182. ///
  183. /// <remarks>
  184. /// <para>
  185. /// See the remarks on the <see cref="LastModified"/> property, for some
  186. /// details about how the last modified time of the created file is set.
  187. /// </para>
  188. /// </remarks>
  189. ///
  190. /// <example>
  191. /// <code lang="C#">
  192. /// String sZipPath = "Airborne.zip";
  193. /// String sFilePath = "Readme.txt";
  194. /// String sRootFolder = "Digado";
  195. /// using (ZipFile zip = ZipFile.Read(sZipPath))
  196. /// {
  197. /// if (zip.EntryFileNames.Contains(sFilePath))
  198. /// {
  199. /// // use the string indexer on the zip file
  200. /// zip[sFileName].Extract(sRootFolder,
  201. /// ExtractExistingFileAction.OverwriteSilently);
  202. /// }
  203. /// }
  204. /// </code>
  205. ///
  206. /// <code lang="VB">
  207. /// Dim sZipPath as String = "Airborne.zip"
  208. /// Dim sFilePath As String = "Readme.txt"
  209. /// Dim sRootFolder As String = "Digado"
  210. /// Using zip As ZipFile = ZipFile.Read(sZipPath)
  211. /// If zip.EntryFileNames.Contains(sFilePath)
  212. /// ' use the string indexer on the zip file
  213. /// zip(sFilePath).Extract(sRootFolder, _
  214. /// ExtractExistingFileAction.OverwriteSilently)
  215. /// End If
  216. /// End Using
  217. /// </code>
  218. /// </example>
  219. ///
  220. /// <param name="baseDirectory">the pathname of the base directory</param>
  221. /// <param name="extractExistingFile">
  222. /// The action to take if extraction would overwrite an existing file.
  223. /// </param>
  224. public void Extract(string baseDirectory, ExtractExistingFileAction extractExistingFile)
  225. {
  226. ExtractExistingFile = extractExistingFile;
  227. InternalExtractToBaseDir(baseDirectory, null, _container, _Source, FileName);
  228. }
  229. /// <summary>
  230. /// Extract the entry to the filesystem, using the current working directory
  231. /// and the specified password.
  232. /// </summary>
  233. ///
  234. /// <overloads>
  235. /// This method has a bunch of overloads! One of them is sure to be
  236. /// the right one for you...
  237. /// </overloads>
  238. ///
  239. /// <seealso cref="Ionic.Zip.ZipEntry.ExtractExistingFile"/>
  240. /// <seealso cref="Ionic.Zip.ZipEntry.ExtractWithPassword(ExtractExistingFileAction, string)"/>
  241. ///
  242. /// <remarks>
  243. ///
  244. /// <para>
  245. /// Existing entries in the filesystem will not be overwritten. If you
  246. /// would like to force the overwrite of existing files, see the <see
  247. /// cref="Ionic.Zip.ZipEntry.ExtractExistingFile"/>property, or call
  248. /// <see
  249. /// cref="ExtractWithPassword(ExtractExistingFileAction,string)"/>.
  250. /// </para>
  251. ///
  252. /// <para>
  253. /// See the remarks on the <see cref="LastModified"/> property for some
  254. /// details about how the "last modified" time of the created file is
  255. /// set.
  256. /// </para>
  257. /// </remarks>
  258. ///
  259. /// <example>
  260. /// In this example, entries that use encryption are extracted using a
  261. /// particular password.
  262. /// <code>
  263. /// using (var zip = ZipFile.Read(FilePath))
  264. /// {
  265. /// foreach (ZipEntry e in zip)
  266. /// {
  267. /// if (e.UsesEncryption)
  268. /// e.ExtractWithPassword("Secret!");
  269. /// else
  270. /// e.Extract();
  271. /// }
  272. /// }
  273. /// </code>
  274. /// <code lang="VB">
  275. /// Using zip As ZipFile = ZipFile.Read(FilePath)
  276. /// Dim e As ZipEntry
  277. /// For Each e In zip
  278. /// If (e.UsesEncryption)
  279. /// e.ExtractWithPassword("Secret!")
  280. /// Else
  281. /// e.Extract
  282. /// End If
  283. /// Next
  284. /// End Using
  285. /// </code>
  286. /// </example>
  287. /// <param name="password">The Password to use for decrypting the entry.</param>
  288. public void ExtractWithPassword(string password)
  289. {
  290. InternalExtractToBaseDir(".", password, _container, _Source, FileName);
  291. }
  292. /// <summary>
  293. /// Extract the entry to the filesystem, starting at the specified base
  294. /// directory, and using the specified password.
  295. /// </summary>
  296. ///
  297. /// <seealso cref="Ionic.Zip.ZipEntry.ExtractExistingFile"/>
  298. /// <seealso cref="Ionic.Zip.ZipEntry.ExtractWithPassword(string, ExtractExistingFileAction, string)"/>
  299. ///
  300. /// <remarks>
  301. /// <para>
  302. /// Existing entries in the filesystem will not be overwritten. If you
  303. /// would like to force the overwrite of existing files, see the <see
  304. /// cref="Ionic.Zip.ZipEntry.ExtractExistingFile"/>property, or call
  305. /// <see
  306. /// cref="ExtractWithPassword(ExtractExistingFileAction,string)"/>.
  307. /// </para>
  308. ///
  309. /// <para>
  310. /// See the remarks on the <see cref="LastModified"/> property, for some
  311. /// details about how the last modified time of the created file is set.
  312. /// </para>
  313. /// </remarks>
  314. ///
  315. /// <param name="baseDirectory">The pathname of the base directory.</param>
  316. /// <param name="password">The Password to use for decrypting the entry.</param>
  317. public void ExtractWithPassword(string baseDirectory, string password)
  318. {
  319. InternalExtractToBaseDir(baseDirectory, password, _container, _Source, FileName);
  320. }
  321. /// <summary>
  322. /// Extract the entry to a file in the filesystem, relative to the
  323. /// current directory, using the specified behavior when extraction
  324. /// would overwrite an existing file.
  325. /// </summary>
  326. ///
  327. /// <remarks>
  328. /// <para>
  329. /// See the remarks on the <see cref="LastModified"/> property, for some
  330. /// details about how the last modified time of the created file is set.
  331. /// </para>
  332. /// </remarks>
  333. ///
  334. /// <param name="password">The Password to use for decrypting the entry.</param>
  335. ///
  336. /// <param name="extractExistingFile">
  337. /// The action to take if extraction would overwrite an existing file.
  338. /// </param>
  339. public void ExtractWithPassword(ExtractExistingFileAction extractExistingFile, string password)
  340. {
  341. ExtractExistingFile = extractExistingFile;
  342. InternalExtractToBaseDir(".", password, _container, _Source, FileName);
  343. }
  344. /// <summary>
  345. /// Extract the entry to the filesystem, starting at the specified base
  346. /// directory, and using the specified behavior when extraction would
  347. /// overwrite an existing file.
  348. /// </summary>
  349. ///
  350. /// <remarks>
  351. /// See the remarks on the <see cref="LastModified"/> property, for some
  352. /// details about how the last modified time of the created file is set.
  353. /// </remarks>
  354. ///
  355. /// <param name="baseDirectory">the pathname of the base directory</param>
  356. ///
  357. /// <param name="extractExistingFile">The action to take if extraction would
  358. /// overwrite an existing file.</param>
  359. ///
  360. /// <param name="password">The Password to use for decrypting the entry.</param>
  361. public void ExtractWithPassword(string baseDirectory, ExtractExistingFileAction extractExistingFile, string password)
  362. {
  363. ExtractExistingFile = extractExistingFile;
  364. InternalExtractToBaseDir(baseDirectory, password, _container, _Source, FileName);
  365. }
  366. /// <summary>
  367. /// Extracts the entry to the specified stream, using the specified
  368. /// Password. For example, the caller could extract to Console.Out, or
  369. /// to a MemoryStream.
  370. /// </summary>
  371. ///
  372. /// <remarks>
  373. /// <para>
  374. /// The caller can specify any write-able stream, for example a <see
  375. /// cref="System.IO.FileStream"/>, a <see
  376. /// cref="System.IO.MemoryStream"/>, or ASP.NET's
  377. /// <c>Response.OutputStream</c>. The content will be decrypted and
  378. /// decompressed as necessary. If the entry is encrypted and no password
  379. /// is provided, this method will throw.
  380. /// </para>
  381. /// <para>
  382. /// The position on the stream is not reset by this method before it extracts.
  383. /// You may want to call stream.Seek() before calling ZipEntry.Extract().
  384. /// </para>
  385. /// </remarks>
  386. ///
  387. ///
  388. /// <param name="stream">
  389. /// the stream to which the entry should be extracted.
  390. /// </param>
  391. /// <param name="password">
  392. /// The password to use for decrypting the entry.
  393. /// </param>
  394. public void ExtractWithPassword(Stream stream, string password)
  395. {
  396. InternalExtractToStream(stream, password, _container, _Source, FileName);
  397. }
  398. /// <summary>
  399. /// Opens a readable stream corresponding to the zip entry in the
  400. /// archive. The stream decompresses and decrypts as necessary, as it
  401. /// is read.
  402. /// </summary>
  403. ///
  404. /// <remarks>
  405. ///
  406. /// <para>
  407. /// DotNetZip offers a variety of ways to extract entries from a zip
  408. /// file. This method allows an application to extract an entry by
  409. /// reading a <see cref="System.IO.Stream"/>.
  410. /// </para>
  411. ///
  412. /// <para>
  413. /// The return value is of type <see
  414. /// cref="Ionic.Crc.CrcCalculatorStream"/>. Use it as you would any
  415. /// stream for reading. When an application calls <see
  416. /// cref="Stream.Read(byte[], int, int)"/> on that stream, it will
  417. /// receive data from the zip entry that is decrypted and decompressed
  418. /// as necessary.
  419. /// </para>
  420. ///
  421. /// <para>
  422. /// <c>CrcCalculatorStream</c> adds one additional feature: it keeps a
  423. /// CRC32 checksum on the bytes of the stream as it is read. The CRC
  424. /// value is available in the <see
  425. /// cref="Ionic.Crc.CrcCalculatorStream.Crc"/> property on the
  426. /// <c>CrcCalculatorStream</c>. When the read is complete, your
  427. /// application
  428. /// <em>should</em> check this CRC against the <see cref="ZipEntry.Crc"/>
  429. /// property on the <c>ZipEntry</c> to validate the content of the
  430. /// ZipEntry. You don't have to validate the entry using the CRC, but
  431. /// you should, to verify integrity. Check the example for how to do
  432. /// this.
  433. /// </para>
  434. ///
  435. /// <para>
  436. /// If the entry is protected with a password, then you need to provide
  437. /// a password prior to calling <see cref="OpenReader()"/>, either by
  438. /// setting the <see cref="Password"/> property on the entry, or the
  439. /// <see cref="ZipFile.Password"/> property on the <c>ZipFile</c>
  440. /// itself. Or, you can use <see cref="OpenReader(String)" />, the
  441. /// overload of OpenReader that accepts a password parameter.
  442. /// </para>
  443. ///
  444. /// <para>
  445. /// If you want to extract entry data into a write-able stream that is
  446. /// already opened, like a <see cref="System.IO.FileStream"/>, do not
  447. /// use this method. Instead, use <see cref="Extract(Stream)"/>.
  448. /// </para>
  449. ///
  450. /// <para>
  451. /// Your application may use only one stream created by OpenReader() at
  452. /// a time, and you should not call other Extract methods before
  453. /// completing your reads on a stream obtained from OpenReader(). This
  454. /// is because there is really only one source stream for the compressed
  455. /// content. A call to OpenReader() seeks in the source stream, to the
  456. /// beginning of the compressed content. A subsequent call to
  457. /// OpenReader() on a different entry will seek to a different position
  458. /// in the source stream, as will a call to Extract() or one of its
  459. /// overloads. This will corrupt the state for the decompressing stream
  460. /// from the original call to OpenReader().
  461. /// </para>
  462. ///
  463. /// <para>
  464. /// The <c>OpenReader()</c> method works only when the ZipEntry is
  465. /// obtained from an instance of <c>ZipFile</c>. This method will throw
  466. /// an exception if the ZipEntry is obtained from a <see
  467. /// cref="ZipInputStream"/>.
  468. /// </para>
  469. /// </remarks>
  470. ///
  471. /// <example>
  472. /// This example shows how to open a zip archive, then read in a named
  473. /// entry via a stream. After the read loop is complete, the code
  474. /// compares the calculated during the read loop with the expected CRC
  475. /// on the <c>ZipEntry</c>, to verify the extraction.
  476. /// <code>
  477. /// using (ZipFile zip = new ZipFile(ZipFileToRead))
  478. /// {
  479. /// ZipEntry e1= zip["Elevation.mp3"];
  480. /// using (Ionic.Zlib.CrcCalculatorStream s = e1.OpenReader())
  481. /// {
  482. /// byte[] buffer = new byte[4096];
  483. /// int n, totalBytesRead= 0;
  484. /// do {
  485. /// n = s.Read(buffer,0, buffer.Length);
  486. /// totalBytesRead+=n;
  487. /// } while (n&gt;0);
  488. /// if (s.Crc32 != e1.Crc32)
  489. /// throw new Exception(string.Format("The Zip Entry failed the CRC Check. (0x{0:X8}!=0x{1:X8})", s.Crc32, e1.Crc32));
  490. /// if (totalBytesRead != e1.UncompressedSize)
  491. /// throw new Exception(string.Format("We read an unexpected number of bytes. ({0}!={1})", totalBytesRead, e1.UncompressedSize));
  492. /// }
  493. /// }
  494. /// </code>
  495. /// <code lang="VB">
  496. /// Using zip As New ZipFile(ZipFileToRead)
  497. /// Dim e1 As ZipEntry = zip.Item("Elevation.mp3")
  498. /// Using s As Ionic.Zlib.CrcCalculatorStream = e1.OpenReader
  499. /// Dim n As Integer
  500. /// Dim buffer As Byte() = New Byte(4096) {}
  501. /// Dim totalBytesRead As Integer = 0
  502. /// Do
  503. /// n = s.Read(buffer, 0, buffer.Length)
  504. /// totalBytesRead = (totalBytesRead + n)
  505. /// Loop While (n &gt; 0)
  506. /// If (s.Crc32 &lt;&gt; e1.Crc32) Then
  507. /// Throw New Exception(String.Format("The Zip Entry failed the CRC Check. (0x{0:X8}!=0x{1:X8})", s.Crc32, e1.Crc32))
  508. /// End If
  509. /// If (totalBytesRead &lt;&gt; e1.UncompressedSize) Then
  510. /// Throw New Exception(String.Format("We read an unexpected number of bytes. ({0}!={1})", totalBytesRead, e1.UncompressedSize))
  511. /// End If
  512. /// End Using
  513. /// End Using
  514. /// </code>
  515. /// </example>
  516. /// <seealso cref="Ionic.Zip.ZipEntry.Extract(System.IO.Stream)"/>
  517. /// <returns>The Stream for reading.</returns>
  518. public Crc.CrcCalculatorStream OpenReader()
  519. {
  520. // workitem 10923
  521. if (_container.ZipFile == null)
  522. throw new InvalidOperationException("Use OpenReader() only with ZipFile.");
  523. // use the entry password if it is non-null,
  524. // else use the zipfile password, which is possibly null
  525. return InternalOpenReader(_Password ?? _container.Password);
  526. }
  527. /// <summary>
  528. /// Opens a readable stream for an encrypted zip entry in the archive.
  529. /// The stream decompresses and decrypts as necessary, as it is read.
  530. /// </summary>
  531. ///
  532. /// <remarks>
  533. /// <para>
  534. /// See the documentation on the <see cref="OpenReader()"/> method for
  535. /// full details. This overload allows the application to specify a
  536. /// password for the <c>ZipEntry</c> to be read.
  537. /// </para>
  538. /// </remarks>
  539. ///
  540. /// <param name="password">The password to use for decrypting the entry.</param>
  541. /// <returns>The Stream for reading.</returns>
  542. public Ionic.Crc.CrcCalculatorStream OpenReader(string password)
  543. {
  544. // workitem 10923
  545. if (_container.ZipFile == null)
  546. throw new InvalidOperationException("Use OpenReader() only with ZipFile.");
  547. return InternalOpenReader(password);
  548. }
  549. internal Crc.CrcCalculatorStream InternalOpenReader(string password)
  550. {
  551. ValidateCompression(_CompressionMethod_FromZipFile, FileName, GetUnsupportedCompressionMethod(_CompressionMethod));
  552. ValidateEncryption(Encryption, FileName, _UnsupportedAlgorithmId);
  553. SetupCryptoForExtract(password);
  554. // workitem 7958
  555. if (this._Source != ZipEntrySource.ZipFile)
  556. throw new BadStateException("You must call ZipFile.Save before calling OpenReader");
  557. // LeftToRead is a count of bytes remaining to be read (out)
  558. // from the stream AFTER decompression and decryption.
  559. // It is the uncompressed size, unless ... there is no compression in which
  560. // case ...? :< I'm not sure why it's not always UncompressedSize
  561. var leftToRead = (_CompressionMethod_FromZipFile == (short)CompressionMethod.None)
  562. ? _CompressedFileDataSize
  563. : UncompressedSize;
  564. this.ArchiveStream.Seek(this.FileDataPosition, SeekOrigin.Begin);
  565. // workitem 10178
  566. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  567. _inputDecryptorStream = GetExtractDecryptor(ArchiveStream);
  568. var input3 = GetExtractDecompressor(_inputDecryptorStream);
  569. return new Crc.CrcCalculatorStream(input3, leftToRead);
  570. }
  571. void OnExtractProgress(Int64 bytesWritten, Int64 totalBytesToWrite)
  572. {
  573. if (_container.ZipFile != null)
  574. _ioOperationCanceled = _container.ZipFile.OnExtractBlock(this, bytesWritten, totalBytesToWrite);
  575. }
  576. static void OnBeforeExtract(ZipEntry zipEntryInstance, string path, ZipFile zipFile)
  577. {
  578. // When in the context of a ZipFile.ExtractAll, the events are generated from
  579. // the ZipFile method, not from within the ZipEntry instance. (why?)
  580. // Therefore we suppress the events originating from the ZipEntry method.
  581. if (zipFile == null) return;
  582. if (zipFile._inExtractAll) return;
  583. // returned boolean is always ignored for all callers of OnBeforeExtract
  584. zipFile.OnSingleEntryExtract(zipEntryInstance, path, true);
  585. }
  586. private void OnAfterExtract(string path)
  587. {
  588. // When in the context of a ZipFile.ExtractAll, the events are generated from
  589. // the ZipFile method, not from within the ZipEntry instance. (why?)
  590. // Therefore we suppress the events originating from the ZipEntry method.
  591. if (_container.ZipFile == null) return;
  592. if (_container.ZipFile._inExtractAll) return;
  593. _container.ZipFile.OnSingleEntryExtract(this, path, false);
  594. }
  595. private void OnExtractExisting(string path)
  596. {
  597. if (_container.ZipFile != null)
  598. _ioOperationCanceled = _container.ZipFile.OnExtractExisting(this, path);
  599. }
  600. private static void ReallyDelete(string fileName)
  601. {
  602. // workitem 7881
  603. // reset ReadOnly bit if necessary
  604. #if NETCF
  605. if ( (NetCfFile.GetAttributes(fileName) & (uint)FileAttributes.ReadOnly) == (uint)FileAttributes.ReadOnly)
  606. NetCfFile.SetAttributes(fileName, (uint)FileAttributes.Normal);
  607. #elif SILVERLIGHT
  608. #else
  609. if ((File.GetAttributes(fileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
  610. File.SetAttributes(fileName, FileAttributes.Normal);
  611. #endif
  612. File.Delete(fileName);
  613. }
  614. void WriteStatus(string format, params Object[] args)
  615. {
  616. if (_container.ZipFile != null && _container.ZipFile.Verbose)
  617. _container.ZipFile.StatusMessageTextWriter.WriteLine(format, args);
  618. }
  619. /// <summary>
  620. /// Pass in either basedir or s, but not both.
  621. /// In other words, you can extract to a stream or to a directory (filesystem), but not both!
  622. /// The Password param is required for encrypted entries.
  623. /// </summary>
  624. void InternalExtractToBaseDir(string baseDir, string password, ZipContainer zipContainer, ZipEntrySource zipEntrySource, string fileName)
  625. {
  626. if (baseDir == null)
  627. throw new ArgumentNullException("baseDir");
  628. // workitem 7958
  629. if (zipContainer == null)
  630. throw new BadStateException("This entry is an orphan");
  631. // workitem 10355
  632. if (zipContainer.ZipFile == null)
  633. throw new InvalidOperationException("Use Extract() only with ZipFile.");
  634. zipContainer.ZipFile.Reset(false);
  635. if (zipEntrySource != ZipEntrySource.ZipFile)
  636. throw new BadStateException("You must call ZipFile.Save before calling any Extract method");
  637. OnBeforeExtract(this, baseDir, zipContainer.ZipFile);
  638. _ioOperationCanceled = false;
  639. var fileExistsBeforeExtraction = false;
  640. var checkLaterForResetDirTimes = false;
  641. string targetFileName = null;
  642. try
  643. {
  644. ValidateCompression(_CompressionMethod_FromZipFile, fileName, GetUnsupportedCompressionMethod(_CompressionMethod));
  645. ValidateEncryption(Encryption, fileName, _UnsupportedAlgorithmId);
  646. if (IsDoneWithOutputToBaseDir(baseDir, out targetFileName))
  647. {
  648. WriteStatus("extract dir {0}...", targetFileName);
  649. // if true, then the entry was a directory and has been created.
  650. // We need to fire the Extract Event.
  651. OnAfterExtract(baseDir);
  652. return;
  653. }
  654. // workitem 10639
  655. // do we want to extract to a regular filesystem file?
  656. // Check for extracting to a previously existing file. The user
  657. // can specify bejavior for that case: overwrite, don't
  658. // overwrite, and throw. Also, if the file exists prior to
  659. // extraction, it affects exception handling: whether to delete
  660. // the target of extraction or not. This check needs to be done
  661. // before the password check is done, because password check may
  662. // throw a BadPasswordException, which triggers the catch,
  663. // wherein the existing file may be deleted if not flagged as
  664. // pre-existing.
  665. if (File.Exists(targetFileName))
  666. {
  667. fileExistsBeforeExtraction = true;
  668. int rc = CheckExtractExistingFile(baseDir, targetFileName);
  669. if (rc == 2) goto ExitTry; // cancel
  670. if (rc == 1) return; // do not overwrite
  671. }
  672. // If no password explicitly specified, use the password on the entry itself,
  673. // or on the zipfile itself.
  674. if (_Encryption_FromZipFile != EncryptionAlgorithm.None)
  675. EnsurePassword(password);
  676. // set up the output stream
  677. var tmpName = SharedUtilities.InternalGetTempFileName();
  678. var tmpPath = Path.Combine(Path.GetDirectoryName(targetFileName), tmpName);
  679. WriteStatus("extract file {0}...", targetFileName);
  680. using (var output = OpenFileStream(tmpPath, ref checkLaterForResetDirTimes))
  681. {
  682. if (ExtractToStream(ArchiveStream, output, Encryption, _Crc32))
  683. goto ExitTry;
  684. output.Close();
  685. }
  686. MoveFileInPlace(fileExistsBeforeExtraction, targetFileName, tmpPath, checkLaterForResetDirTimes);
  687. OnAfterExtract(baseDir);
  688. ExitTry: ;
  689. }
  690. catch (Exception)
  691. {
  692. _ioOperationCanceled = true;
  693. throw;
  694. }
  695. finally
  696. {
  697. if (_ioOperationCanceled && targetFileName != null)
  698. {
  699. // An exception has occurred. If the file exists, check
  700. // to see if it existed before we tried extracting. If
  701. // it did not, attempt to remove the target file. There
  702. // is a small possibility that the existing file has
  703. // been extracted successfully, overwriting a previously
  704. // existing file, and an exception was thrown after that
  705. // but before final completion (setting times, etc). In
  706. // that case the file will remain, even though some
  707. // error occurred. Nothing to be done about it.
  708. if (File.Exists(targetFileName) && !fileExistsBeforeExtraction)
  709. File.Delete(targetFileName);
  710. }
  711. }
  712. }
  713. /// <summary>
  714. /// Extract to a stream
  715. /// In other words, you can extract to a stream or to a directory (filesystem), but not both!
  716. /// The Password param is required for encrypted entries.
  717. /// </summary>
  718. void InternalExtractToStream(Stream outStream, string password, ZipContainer zipContainer, ZipEntrySource zipEntrySource, string fileName)
  719. {
  720. // workitem 7958
  721. if (zipContainer == null)
  722. throw new BadStateException("This entry is an orphan");
  723. // workitem 10355
  724. if (zipContainer.ZipFile == null)
  725. throw new InvalidOperationException("Use Extract() only with ZipFile.");
  726. zipContainer.ZipFile.Reset(false);
  727. if (zipEntrySource != ZipEntrySource.ZipFile)
  728. throw new BadStateException("You must call ZipFile.Save before calling any Extract method");
  729. OnBeforeExtract(this, null, zipContainer.ZipFile);
  730. _ioOperationCanceled = false;
  731. try
  732. {
  733. ValidateCompression(_CompressionMethod_FromZipFile, fileName, GetUnsupportedCompressionMethod(_CompressionMethod));
  734. ValidateEncryption(Encryption, fileName, _UnsupportedAlgorithmId);
  735. if (IsDoneWithOutputToStream())
  736. {
  737. WriteStatus("extract dir {0}...", null);
  738. // if true, then the entry was a directory and has been created.
  739. // We need to fire the Extract Event.
  740. OnAfterExtract(null);
  741. return;
  742. }
  743. // If no password explicitly specified, use the password on the entry itself,
  744. // or on the zipfile itself.
  745. if (_Encryption_FromZipFile != EncryptionAlgorithm.None)
  746. EnsurePassword(password);
  747. WriteStatus("extract entry {0} to stream...", fileName);
  748. var archiveStream = ArchiveStream;
  749. if (ExtractToStream(archiveStream, outStream, Encryption, _Crc32))
  750. goto ExitTry;
  751. OnAfterExtract(null);
  752. ExitTry: ;
  753. }
  754. catch (Exception)
  755. {
  756. _ioOperationCanceled = true;
  757. throw;
  758. }
  759. }
  760. bool ExtractToStream(Stream archiveStream, Stream output, EncryptionAlgorithm encryptionAlgorithm, int expectedCrc32)
  761. {
  762. if (_ioOperationCanceled)
  763. return true;
  764. try
  765. {
  766. var calculatedCrc32 = ExtractAndCrc(archiveStream, output,
  767. _CompressionMethod_FromZipFile, _CompressedFileDataSize,
  768. UncompressedSize);
  769. if (_ioOperationCanceled)
  770. return true;
  771. VerifyCrcAfterExtract(calculatedCrc32, encryptionAlgorithm, expectedCrc32, archiveStream, UncompressedSize);
  772. return false;
  773. }
  774. finally
  775. {
  776. var zss = archiveStream as ZipSegmentedStream;
  777. if (zss != null)
  778. {
  779. #if NETCF
  780. zss.Close();
  781. #else
  782. // need to dispose it
  783. zss.Dispose();
  784. #endif
  785. _archiveStream = null;
  786. }
  787. }
  788. }
  789. void MoveFileInPlace(
  790. bool fileExistsBeforeExtraction,
  791. string targetFileName,
  792. string tmpPath, bool checkLaterForResetDirTimes)
  793. {
  794. // workitem 10639
  795. // move file to permanent home
  796. string zombie = null;
  797. if (fileExistsBeforeExtraction)
  798. {
  799. // An AV program may hold the target file open, which means
  800. // File.Delete() will succeed, though the actual deletion
  801. // remains pending. This will prevent a subsequent
  802. // File.Move() from succeeding. To avoid this, when the file
  803. // already exists, we need to replace it in 3 steps:
  804. //
  805. // 1. rename the existing file to a zombie name;
  806. // 2. rename the extracted file from the temp name to
  807. // the target file name;
  808. // 3. delete the zombie.
  809. //
  810. zombie = targetFileName + Path.GetRandomFileName() + ".PendingOverwrite";
  811. File.Move(targetFileName, zombie);
  812. }
  813. File.Move(tmpPath, targetFileName);
  814. _SetTimes(targetFileName, true);
  815. if (zombie != null && File.Exists(zombie))
  816. ReallyDelete(zombie);
  817. // workitem 8264
  818. if (checkLaterForResetDirTimes)
  819. {
  820. // This is sort of a hack. What I do here is set the time on
  821. // the parent directory, every time a file is extracted into
  822. // it. If there is a directory with 1000 files, then I set
  823. // the time on the dir, 1000 times. This allows the directory
  824. // to have times that reflect the actual time on the entry in
  825. // the zip archive.
  826. // String.Contains is not available on .NET CF 2.0
  827. if (FileName.IndexOf('/') != -1)
  828. {
  829. var dirname = Path.GetDirectoryName(FileName);
  830. if (_container.ZipFile[dirname] == null)
  831. _SetTimes(Path.GetDirectoryName(targetFileName), false);
  832. }
  833. }
  834. #if NETCF
  835. // workitem 7926 - version made by OS can be zero or 10
  836. if ((_VersionMadeBy & 0xFF00) == 0x0a00 || (_VersionMadeBy & 0xFF00) == 0x0000)
  837. NetCfFile.SetAttributes(targetFileName, (uint)_ExternalFileAttrs);
  838. #else
  839. // workitem 7071
  840. //
  841. // We can only apply attributes if they are relevant to the NTFS
  842. // OS. Must do this LAST because it may involve a ReadOnly bit,
  843. // which would prevent us from setting the time, etc.
  844. //
  845. // workitem 7926 - version made by OS can be zero (FAT) or 10
  846. // (NTFS)
  847. if ((_VersionMadeBy & 0xFF00) == 0x0a00 || (_VersionMadeBy & 0xFF00) == 0x0000)
  848. File.SetAttributes(targetFileName, (FileAttributes) _ExternalFileAttrs);
  849. #endif
  850. }
  851. void EnsurePassword(string password)
  852. {
  853. var p = password ?? _Password ?? _container.Password;
  854. if (p == null) throw new BadPasswordException();
  855. SetupCryptoForExtract(p);
  856. }
  857. Stream OpenFileStream(string tmpPath, ref bool checkLaterForResetDirTimes)
  858. {
  859. var dirName = Path.GetDirectoryName(tmpPath);
  860. // ensure the target path exists
  861. if (!Directory.Exists(dirName))
  862. {
  863. // we create the directory here, but we do not set the
  864. // create/modified/accessed times on it because it is being
  865. // created implicitly, not explcitly. There's no entry in the
  866. // zip archive for the directory.
  867. Directory.CreateDirectory(dirName);
  868. }
  869. else
  870. {
  871. // workitem 8264
  872. if (_container.ZipFile != null)
  873. checkLaterForResetDirTimes = _container.ZipFile._inExtractAll;
  874. }
  875. // File.Create(CreateNew) will overwrite any existing file.
  876. return new FileStream(tmpPath, FileMode.CreateNew);
  877. }
  878. #if NOT
  879. internal void CalcWinZipAesMac(Stream input)
  880. {
  881. if (Encryption == EncryptionAlgorithm.WinZipAes128 ||
  882. Encryption == EncryptionAlgorithm.WinZipAes256)
  883. {
  884. if (input is WinZipAesCipherStream)
  885. wzs = input as WinZipAesCipherStream;
  886. else if (input is Ionic.Zlib.CrcCalculatorStream)
  887. {
  888. xxx;
  889. }
  890. }
  891. }
  892. #endif
  893. internal void VerifyCrcAfterExtract(Int32 calculatedCrc32, EncryptionAlgorithm encryptionAlgorithm, int expectedCrc32, Stream archiveStream, long uncompressedSize)
  894. {
  895. #if AESCRYPTO
  896. // After extracting, Validate the CRC32
  897. if (calculatedCrc32 != expectedCrc32)
  898. {
  899. // CRC is not meaningful with WinZipAES and AES method 2 (AE-2)
  900. if ((encryptionAlgorithm != EncryptionAlgorithm.WinZipAes128 &&
  901. encryptionAlgorithm != EncryptionAlgorithm.WinZipAes256)
  902. || _WinZipAesMethod != 0x02)
  903. throw new BadCrcException("CRC error: the file being extracted appears to be corrupted. " +
  904. String.Format("Expected 0x{0:X8}, Actual 0x{1:X8}", expectedCrc32, calculatedCrc32));
  905. }
  906. // ignore MAC if the size of the file is zero
  907. if (uncompressedSize == 0)
  908. return;
  909. // calculate the MAC
  910. if (encryptionAlgorithm == EncryptionAlgorithm.WinZipAes128 ||
  911. encryptionAlgorithm == EncryptionAlgorithm.WinZipAes256)
  912. {
  913. var wzs = _inputDecryptorStream as WinZipAesCipherStream;
  914. _aesCrypto_forExtract.CalculatedMac = wzs.FinalAuthentication;
  915. _aesCrypto_forExtract.ReadAndVerifyMac(archiveStream); // throws if MAC is bad
  916. // side effect: advances file position.
  917. }
  918. #else
  919. if (calculatedCrc32 != expectedCrc32)
  920. throw new BadCrcException("CRC error: the file being extracted appears to be corrupted. " +
  921. String.Format("Expected 0x{0:X8}, Actual 0x{1:X8}", expectedCrc32, calculatedCrc32));
  922. #endif
  923. }
  924. int CheckExtractExistingFile(string baseDir, string targetFileName)
  925. {
  926. int loop = 0;
  927. // returns: 0 == extract, 1 = don't, 2 = cancel
  928. do
  929. {
  930. switch (ExtractExistingFile)
  931. {
  932. case ExtractExistingFileAction.OverwriteSilently:
  933. WriteStatus("the file {0} exists; will overwrite it...", targetFileName);
  934. return 0;
  935. case ExtractExistingFileAction.DoNotOverwrite:
  936. WriteStatus("the file {0} exists; not extracting entry...", FileName);
  937. OnAfterExtract(baseDir);
  938. return 1;
  939. case ExtractExistingFileAction.InvokeExtractProgressEvent:
  940. if (loop>0)
  941. throw new ZipException(String.Format("The file {0} already exists.", targetFileName));
  942. OnExtractExisting(baseDir);
  943. if (_ioOperationCanceled)
  944. return 2;
  945. // loop around
  946. break;
  947. case ExtractExistingFileAction.Throw:
  948. default:
  949. throw new ZipException(String.Format("The file {0} already exists.", targetFileName));
  950. }
  951. loop++;
  952. }
  953. while (true);
  954. }
  955. void _CheckRead(int nbytes)
  956. {
  957. if (nbytes == 0)
  958. throw new BadReadException(String.Format("bad read of entry {0} from compressed archive.",
  959. FileName));
  960. }
  961. Stream _inputDecryptorStream;
  962. int ExtractAndCrc(Stream archiveStream, Stream targetOutput,
  963. short compressionMethod,
  964. long compressedFileDataSize,
  965. long uncompressedSize)
  966. {
  967. int crcResult;
  968. var input = archiveStream;
  969. // change for workitem 8098
  970. input.Seek(FileDataPosition, SeekOrigin.Begin);
  971. // workitem 10178
  972. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(input);
  973. var bytes = new byte[BufferSize];
  974. // The extraction process varies depending on how the entry was
  975. // stored. It could have been encrypted, and it coould have
  976. // been compressed, or both, or neither. So we need to check
  977. // both the encryption flag and the compression flag, and take
  978. // the proper action in all cases.
  979. var leftToRead = (compressionMethod != (short)CompressionMethod.None)
  980. ? uncompressedSize
  981. : compressedFileDataSize;
  982. // Get a stream that either decrypts or not.
  983. _inputDecryptorStream = GetExtractDecryptor(input);
  984. var input3 = GetExtractDecompressor(_inputDecryptorStream);
  985. var bytesWritten = 0L;
  986. // As we read, we maybe decrypt, and then we maybe decompress. Then we write.
  987. using (var s1 = new Crc.CrcCalculatorStream(input3))
  988. {
  989. while (leftToRead > 0)
  990. {
  991. //Console.WriteLine("ExtractOne: LeftToRead {0}", LeftToRead);
  992. // Casting LeftToRead down to an int is ok here in the else clause,
  993. // because that only happens when it is less than bytes.Length,
  994. // which is much less than MAX_INT.
  995. int len = (leftToRead > bytes.Length) ? bytes.Length : (int)leftToRead;
  996. int n = s1.Read(bytes, 0, len);
  997. // must check data read - essential for detecting corrupt zip files
  998. _CheckRead(n);
  999. targetOutput.Write(bytes, 0, n);
  1000. leftToRead -= n;
  1001. bytesWritten += n;
  1002. // fire the progress event, check for cancels
  1003. OnExtractProgress(bytesWritten, uncompressedSize);
  1004. if (_ioOperationCanceled)
  1005. break;
  1006. }
  1007. crcResult = s1.Crc;
  1008. }
  1009. return crcResult;
  1010. }
  1011. Stream GetExtractDecompressor(Stream input2)
  1012. {
  1013. if (input2 == null) throw new ArgumentNullException("input2");
  1014. // get a stream that either decompresses or not.
  1015. switch (_CompressionMethod_FromZipFile)
  1016. {
  1017. case (short)CompressionMethod.None:
  1018. return input2;
  1019. case (short)CompressionMethod.Deflate:
  1020. return new Zlib.DeflateStream(input2, Zlib.CompressionMode.Decompress, true);
  1021. #if BZIP
  1022. case (short)CompressionMethod.BZip2:
  1023. return new BZip2.BZip2InputStream(input2, true);
  1024. #endif
  1025. }
  1026. throw new Exception(string.Format("Failed to find decompressor matching {0}",
  1027. _CompressionMethod_FromZipFile));
  1028. }
  1029. Stream GetExtractDecryptor(Stream input)
  1030. {
  1031. if (input == null) throw new ArgumentNullException("input");
  1032. Stream input2;
  1033. if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak)
  1034. input2 = new ZipCipherStream(input, _zipCrypto_forExtract, CryptoMode.Decrypt);
  1035. #if AESCRYPTO
  1036. else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 ||
  1037. _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256)
  1038. input2 = new WinZipAesCipherStream(input, _aesCrypto_forExtract, _CompressedFileDataSize, CryptoMode.Decrypt);
  1039. #endif
  1040. else
  1041. input2 = input;
  1042. return input2;
  1043. }
  1044. internal void _SetTimes(string fileOrDirectory, bool isFile)
  1045. {
  1046. #if SILVERLIGHT
  1047. // punt on setting file times
  1048. #else
  1049. // workitem 8807:
  1050. // Because setting the time is not considered to be a fatal error,
  1051. // and because other applications can interfere with the setting
  1052. // of a time on a directory, we're going to swallow IO exceptions
  1053. // in this method.
  1054. try
  1055. {
  1056. if (_ntfsTimesAreSet)
  1057. {
  1058. #if NETCF
  1059. // workitem 7944: set time should not be a fatal error on CF
  1060. int rc = NetCfFile.SetTimes(fileOrDirectory, _Ctime, _Atime, _Mtime);
  1061. if ( rc != 0)
  1062. {
  1063. WriteStatus("Warning: SetTimes failed. entry({0}) file({1}) rc({2})",
  1064. FileName, fileOrDirectory, rc);
  1065. }
  1066. #else
  1067. if (isFile)
  1068. {
  1069. // It's possible that the extract was cancelled, in which case,
  1070. // the file does not exist.
  1071. if (File.Exists(fileOrDirectory))
  1072. {
  1073. File.SetCreationTimeUtc(fileOrDirectory, _Ctime);
  1074. File.SetLastAccessTimeUtc(fileOrDirectory, _Atime);
  1075. File.SetLastWriteTimeUtc(fileOrDirectory, _Mtime);
  1076. }
  1077. }
  1078. else
  1079. {
  1080. // It's possible that the extract was cancelled, in which case,
  1081. // the directory does not exist.
  1082. if (Directory.Exists(fileOrDirectory))
  1083. {
  1084. Directory.SetCreationTimeUtc(fileOrDirectory, _Ctime);
  1085. Directory.SetLastAccessTimeUtc(fileOrDirectory, _Atime);
  1086. Directory.SetLastWriteTimeUtc(fileOrDirectory, _Mtime);
  1087. }
  1088. }
  1089. #endif
  1090. }
  1091. else
  1092. {
  1093. // workitem 6191
  1094. DateTime AdjustedLastModified = Ionic.Zip.SharedUtilities.AdjustTime_Reverse(LastModified);
  1095. #if NETCF
  1096. int rc = NetCfFile.SetLastWriteTime(fileOrDirectory, AdjustedLastModified);
  1097. if ( rc != 0)
  1098. {
  1099. WriteStatus("Warning: SetLastWriteTime failed. entry({0}) file({1}) rc({2})",
  1100. FileName, fileOrDirectory, rc);
  1101. }
  1102. #else
  1103. if (isFile)
  1104. File.SetLastWriteTime(fileOrDirectory, AdjustedLastModified);
  1105. else
  1106. Directory.SetLastWriteTime(fileOrDirectory, AdjustedLastModified);
  1107. #endif
  1108. }
  1109. }
  1110. catch (System.IO.IOException ioexc1)
  1111. {
  1112. WriteStatus("failed to set time on {0}: {1}", fileOrDirectory, ioexc1.Message);
  1113. }
  1114. #endif
  1115. }
  1116. #region Support methods
  1117. // workitem 7968
  1118. static string GetUnsupportedAlgorithm(uint unsupportedAlgorithmId)
  1119. {
  1120. string alg;
  1121. switch (unsupportedAlgorithmId)
  1122. {
  1123. case 0:
  1124. alg = "--";
  1125. break;
  1126. case 0x6601:
  1127. alg = "DES";
  1128. break;
  1129. case 0x6602: // - RC2 (version needed to extract < 5.2)
  1130. alg = "RC2";
  1131. break;
  1132. case 0x6603: // - 3DES 168
  1133. alg = "3DES-168";
  1134. break;
  1135. case 0x6609: // - 3DES 112
  1136. alg = "3DES-112";
  1137. break;
  1138. case 0x660E: // - AES 128
  1139. alg = "PKWare AES128";
  1140. break;
  1141. case 0x660F: // - AES 192
  1142. alg = "PKWare AES192";
  1143. break;
  1144. case 0x6610: // - AES 256
  1145. alg = "PKWare AES256";
  1146. break;
  1147. case 0x6702: // - RC2 (version needed to extract >= 5.2)
  1148. alg = "RC2";
  1149. break;
  1150. case 0x6720: // - Blowfish
  1151. alg = "Blowfish";
  1152. break;
  1153. case 0x6721: // - Twofish
  1154. alg = "Twofish";
  1155. break;
  1156. case 0x6801: // - RC4
  1157. alg = "RC4";
  1158. break;
  1159. case 0xFFFF: // - Unknown algorithm
  1160. default:
  1161. alg = String.Format("Unknown (0x{0:X4})", unsupportedAlgorithmId);
  1162. break;
  1163. }
  1164. return alg;
  1165. }
  1166. // workitem 7968
  1167. static string GetUnsupportedCompressionMethod(short compressionMethod)
  1168. {
  1169. string meth;
  1170. switch ((int) compressionMethod)
  1171. {
  1172. case 0:
  1173. meth = "Store";
  1174. break;
  1175. case 1:
  1176. meth = "Shrink";
  1177. break;
  1178. case 8:
  1179. meth = "DEFLATE";
  1180. break;
  1181. case 9:
  1182. meth = "Deflate64";
  1183. break;
  1184. case 12:
  1185. meth = "BZIP2"; // only if BZIP not compiled in
  1186. break;
  1187. case 14:
  1188. meth = "LZMA";
  1189. break;
  1190. case 19:
  1191. meth = "LZ77";
  1192. break;
  1193. case 98:
  1194. meth = "PPMd";
  1195. break;
  1196. default:
  1197. meth = String.Format("Unknown (0x{0:X4})", compressionMethod);
  1198. break;
  1199. }
  1200. return meth;
  1201. }
  1202. static void ValidateEncryption(EncryptionAlgorithm encryptionAlgorithm, string fileName, uint unsupportedAlgorithmId)
  1203. {
  1204. if (encryptionAlgorithm != EncryptionAlgorithm.PkzipWeak &&
  1205. #if AESCRYPTO
  1206. encryptionAlgorithm != EncryptionAlgorithm.WinZipAes128 &&
  1207. encryptionAlgorithm != EncryptionAlgorithm.WinZipAes256 &&
  1208. #endif
  1209. encryptionAlgorithm != EncryptionAlgorithm.None)
  1210. {
  1211. // workitem 7968
  1212. if (unsupportedAlgorithmId != 0)
  1213. throw new ZipException(string.Format("Cannot extract: Entry {0} is encrypted with an algorithm not supported by DotNetZip: {1}",
  1214. fileName, GetUnsupportedAlgorithm(unsupportedAlgorithmId)));
  1215. throw new ZipException(string.Format("Cannot extract: Entry {0} uses an unsupported encryption algorithm ({1:X2})",
  1216. fileName, (int)encryptionAlgorithm));
  1217. }
  1218. }
  1219. static void ValidateCompression(short compressionMethod, string fileName, string compressionMethodName)
  1220. {
  1221. if ((compressionMethod != (short)CompressionMethod.None) &&
  1222. (compressionMethod != (short)CompressionMethod.Deflate)
  1223. #if BZIP
  1224. && (compressionMethod != (short)CompressionMethod.BZip2)
  1225. #endif
  1226. )
  1227. throw new ZipException(String.Format("Entry {0} uses an unsupported compression method (0x{1:X2}, {2})",
  1228. fileName, compressionMethod, compressionMethodName));
  1229. }
  1230. void SetupCryptoForExtract(string password)
  1231. {
  1232. //if (password == null) return;
  1233. if (_Encryption_FromZipFile == EncryptionAlgorithm.None) return;
  1234. if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak)
  1235. {
  1236. if (password == null)
  1237. throw new ZipException("Missing password.");
  1238. this.ArchiveStream.Seek(this.FileDataPosition - 12, SeekOrigin.Begin);
  1239. // workitem 10178
  1240. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  1241. _zipCrypto_forExtract = ZipCrypto.ForRead(password, this);
  1242. }
  1243. #if AESCRYPTO
  1244. else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 ||
  1245. _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256)
  1246. {
  1247. if (password == null)
  1248. throw new ZipException("Missing password.");
  1249. // If we already have a WinZipAesCrypto object in place, use it.
  1250. // It can be set up in the ReadDirEntry(), or during a previous Extract.
  1251. if (_aesCrypto_forExtract != null)
  1252. {
  1253. _aesCrypto_forExtract.Password = password;
  1254. }
  1255. else
  1256. {
  1257. int sizeOfSaltAndPv = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile);
  1258. this.ArchiveStream.Seek(this.FileDataPosition - sizeOfSaltAndPv, SeekOrigin.Begin);
  1259. // workitem 10178
  1260. Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
  1261. int keystrength = GetKeyStrengthInBits(_Encryption_FromZipFile);
  1262. _aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(password, keystrength, this.ArchiveStream);
  1263. }
  1264. }
  1265. #endif
  1266. }
  1267. /// <summary>
  1268. /// Validates that the args are consistent; returning whether the caller can return
  1269. /// because it's done, or not (caller should continue)
  1270. /// </summary>
  1271. bool IsDoneWithOutputToBaseDir(string baseDir, out string outFileName)
  1272. {
  1273. if (baseDir == null) throw new ArgumentNullException("baseDir");
  1274. // Sometimes the name on the entry starts with a slash.
  1275. // Rather than unpack to the root of the volume, we're going to
  1276. // drop the slash and unpack to the specified base directory.
  1277. var f = FileName.Replace(Path.DirectorySeparatorChar, '/');
  1278. // workitem 11772: remove drive letter with separator
  1279. if (f.IndexOf(':') == 1)
  1280. f = f.Substring(2);
  1281. if (f.StartsWith("/"))
  1282. f = f.Substring(1);
  1283. // String.Contains is not available on .NET CF 2.0
  1284. outFileName = _container.ZipFile.FlattenFoldersOnExtract
  1285. ? Path.Combine(baseDir, f.IndexOf('/') != -1 ? Path.GetFileName(f) : f)
  1286. : Path.Combine(baseDir, f);
  1287. // workitem 10639
  1288. outFileName = outFileName.Replace('/', Path.DirectorySeparatorChar);
  1289. // check if it is a directory
  1290. if (IsDirectory || FileName.EndsWith("/"))
  1291. {
  1292. if (!Directory.Exists(outFileName))
  1293. {
  1294. Directory.CreateDirectory(outFileName);
  1295. _SetTimes(outFileName, false);
  1296. }
  1297. else
  1298. {
  1299. // the dir exists, maybe we want to overwrite times.
  1300. if (ExtractExistingFile == ExtractExistingFileAction.OverwriteSilently)
  1301. _SetTimes(outFileName, false);
  1302. }
  1303. return true; // true == all done, caller will return
  1304. }
  1305. return false; // false == work to do by caller.
  1306. }
  1307. /// <summary>
  1308. /// Validates that the args are consistent; returning whether the caller can return
  1309. /// because it's done, or not (caller should continue)
  1310. /// </summary>
  1311. bool IsDoneWithOutputToStream()
  1312. {
  1313. return IsDirectory || FileName.EndsWith("/");
  1314. }
  1315. #endregion
  1316. }
  1317. }