Free cookie consent management tool by TermsFeed Policy Generator

source: branches/3027-NormalDistribution/HeuristicLab.ExtLibs/HeuristicLab.EPPlus/4.0.3/EPPlus-4.0.3/Packaging/DotNetZip/ZipEntry.Extract.cs @ 17243

Last change on this file since 17243 was 12074, checked in by sraggl, 10 years ago

#2341: Added EPPlus-4.0.3 to ExtLibs

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