| // ZipEntry.Extract.cs | |
| // ------------------------------------------------------------------ | |
| // | |
| // Copyright (c) 2009-2011 Dino Chiesa | |
| // All rights reserved. | |
| // | |
| // This code module is part of DotNetZip, a zipfile class library. | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This code is licensed under the Microsoft Public License. | |
| // See the file License.txt for the license details. | |
| // More info on: http://dotnetzip.codeplex.com | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // last saved (in emacs): | |
| // Time-stamp: <2011-August-06 18:08:21> | |
| // | |
| // ------------------------------------------------------------------ | |
| // | |
| // This module defines logic for Extract methods on the ZipEntry class. | |
| // | |
| // ------------------------------------------------------------------ | |
| using System; | |
| using System.IO; | |
| namespace OfficeOpenXml.Packaging.Ionic.Zip | |
| { | |
| internal partial class ZipEntry | |
| { | |
| /// <summary> | |
| /// Extract the entry to the filesystem, starting at the current | |
| /// working directory. | |
| /// </summary> | |
| /// | |
| /// <overloads> | |
| /// This method has a bunch of overloads! One of them is sure to | |
| /// be the right one for you... If you don't like these, check | |
| /// out the <c>ExtractWithPassword()</c> methods. | |
| /// </overloads> | |
| /// | |
| /// <seealso cref="ZipEntry.ExtractExistingFile"/> | |
| /// <seealso cref="ZipEntry.Extract(ExtractExistingFileAction)"/> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// This method extracts an entry from a zip file into the current | |
| /// working directory. The path of the entry as extracted is the full | |
| /// path as specified in the zip archive, relative to the current | |
| /// working directory. After the file is extracted successfully, the | |
| /// file attributes and timestamps are set. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// The action taken when extraction an entry would overwrite an | |
| /// existing file is determined by the <see cref="ExtractExistingFile" | |
| /// /> property. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Within the call to <c>Extract()</c>, the content for the entry is | |
| /// written into a filesystem file, and then the last modified time of the | |
| /// file is set according to the <see cref="LastModified"/> property on | |
| /// the entry. See the remarks the <see cref="LastModified"/> property for | |
| /// some details about the last modified time. | |
| /// </para> | |
| /// | |
| /// </remarks> | |
| internal void Extract() | |
| { | |
| InternalExtract(".", null, null); | |
| } | |
| /// <summary> | |
| /// Extract the entry to a file in the filesystem, using the specified | |
| /// behavior when extraction would overwrite an existing file. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// See the remarks on the <see cref="LastModified"/> property, for some | |
| /// details about how the last modified time of the file is set after | |
| /// extraction. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <param name="extractExistingFile"> | |
| /// The action to take if extraction would overwrite an existing file. | |
| /// </param> | |
| internal void Extract(ExtractExistingFileAction extractExistingFile) | |
| { | |
| ExtractExistingFile = extractExistingFile; | |
| InternalExtract(".", null, null); | |
| } | |
| /// <summary> | |
| /// Extracts the entry to the specified stream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// The caller can specify any write-able stream, for example a <see | |
| /// cref="System.IO.FileStream"/>, a <see | |
| /// cref="System.IO.MemoryStream"/>, or ASP.NET's | |
| /// <c>Response.OutputStream</c>. The content will be decrypted and | |
| /// decompressed as necessary. If the entry is encrypted and no password | |
| /// is provided, this method will throw. | |
| /// </para> | |
| /// <para> | |
| /// The position on the stream is not reset by this method before it extracts. | |
| /// You may want to call stream.Seek() before calling ZipEntry.Extract(). | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <param name="stream"> | |
| /// the stream to which the entry should be extracted. | |
| /// </param> | |
| /// | |
| public void Extract(Stream stream) | |
| { | |
| InternalExtract(null, stream, null); | |
| } | |
| /// <summary> | |
| /// Extract the entry to the filesystem, starting at the specified base | |
| /// directory. | |
| /// </summary> | |
| /// | |
| /// <param name="baseDirectory">the pathname of the base directory</param> | |
| /// | |
| /// <seealso cref="ZipEntry.ExtractExistingFile"/> | |
| /// <seealso cref="ZipEntry.Extract(string, ExtractExistingFileAction)"/> | |
| /// | |
| /// <example> | |
| /// This example extracts only the entries in a zip file that are .txt files, | |
| /// into a directory called "textfiles". | |
| /// <code lang="C#"> | |
| /// using (ZipFile zip = ZipFile.Read("PackedDocuments.zip")) | |
| /// { | |
| /// foreach (string s1 in zip.EntryFilenames) | |
| /// { | |
| /// if (s1.EndsWith(".txt")) | |
| /// { | |
| /// zip[s1].Extract("textfiles"); | |
| /// } | |
| /// } | |
| /// } | |
| /// </code> | |
| /// <code lang="VB"> | |
| /// Using zip As ZipFile = ZipFile.Read("PackedDocuments.zip") | |
| /// Dim s1 As String | |
| /// For Each s1 In zip.EntryFilenames | |
| /// If s1.EndsWith(".txt") Then | |
| /// zip(s1).Extract("textfiles") | |
| /// End If | |
| /// Next | |
| /// End Using | |
| /// </code> | |
| /// </example> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// Using this method, existing entries in the filesystem will not be | |
| /// overwritten. If you would like to force the overwrite of existing | |
| /// files, see the <see cref="ExtractExistingFile"/> property, or call | |
| /// <see cref="Extract(string, ExtractExistingFileAction)"/>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// See the remarks on the <see cref="LastModified"/> property, for some | |
| /// details about how the last modified time of the created file is set. | |
| /// </para> | |
| /// </remarks> | |
| public void Extract(string baseDirectory) | |
| { | |
| InternalExtract(baseDirectory, null, null); | |
| } | |
| /// <summary> | |
| /// Extract the entry to the filesystem, starting at the specified base | |
| /// directory, and using the specified behavior when extraction would | |
| /// overwrite an existing file. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// See the remarks on the <see cref="LastModified"/> property, for some | |
| /// details about how the last modified time of the created file is set. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// <code lang="C#"> | |
| /// String sZipPath = "Airborne.zip"; | |
| /// String sFilePath = "Readme.txt"; | |
| /// String sRootFolder = "Digado"; | |
| /// using (ZipFile zip = ZipFile.Read(sZipPath)) | |
| /// { | |
| /// if (zip.EntryFileNames.Contains(sFilePath)) | |
| /// { | |
| /// // use the string indexer on the zip file | |
| /// zip[sFileName].Extract(sRootFolder, | |
| /// ExtractExistingFileAction.OverwriteSilently); | |
| /// } | |
| /// } | |
| /// </code> | |
| /// | |
| /// <code lang="VB"> | |
| /// Dim sZipPath as String = "Airborne.zip" | |
| /// Dim sFilePath As String = "Readme.txt" | |
| /// Dim sRootFolder As String = "Digado" | |
| /// Using zip As ZipFile = ZipFile.Read(sZipPath) | |
| /// If zip.EntryFileNames.Contains(sFilePath) | |
| /// ' use the string indexer on the zip file | |
| /// zip(sFilePath).Extract(sRootFolder, _ | |
| /// ExtractExistingFileAction.OverwriteSilently) | |
| /// End If | |
| /// End Using | |
| /// </code> | |
| /// </example> | |
| /// | |
| /// <param name="baseDirectory">the pathname of the base directory</param> | |
| /// <param name="extractExistingFile"> | |
| /// The action to take if extraction would overwrite an existing file. | |
| /// </param> | |
| internal void Extract(string baseDirectory, ExtractExistingFileAction extractExistingFile) | |
| { | |
| ExtractExistingFile = extractExistingFile; | |
| InternalExtract(baseDirectory, null, null); | |
| } | |
| /// <summary> | |
| /// Extract the entry to the filesystem, using the current working directory | |
| /// and the specified password. | |
| /// </summary> | |
| /// | |
| /// <overloads> | |
| /// This method has a bunch of overloads! One of them is sure to be | |
| /// the right one for you... | |
| /// </overloads> | |
| /// | |
| /// <seealso cref="ZipEntry.ExtractExistingFile"/> | |
| /// <seealso cref="ZipEntry.ExtractWithPassword(ExtractExistingFileAction, string)"/> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// Existing entries in the filesystem will not be overwritten. If you | |
| /// would like to force the overwrite of existing files, see the <see | |
| /// cref="ZipEntry.ExtractExistingFile"/>property, or call | |
| /// <see | |
| /// cref="ExtractWithPassword(ExtractExistingFileAction,string)"/>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// See the remarks on the <see cref="LastModified"/> property for some | |
| /// details about how the "last modified" time of the created file is | |
| /// set. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// In this example, entries that use encryption are extracted using a | |
| /// particular password. | |
| /// <code> | |
| /// using (var zip = ZipFile.Read(FilePath)) | |
| /// { | |
| /// foreach (ZipEntry e in zip) | |
| /// { | |
| /// if (e.UsesEncryption) | |
| /// e.ExtractWithPassword("Secret!"); | |
| /// else | |
| /// e.Extract(); | |
| /// } | |
| /// } | |
| /// </code> | |
| /// <code lang="VB"> | |
| /// Using zip As ZipFile = ZipFile.Read(FilePath) | |
| /// Dim e As ZipEntry | |
| /// For Each e In zip | |
| /// If (e.UsesEncryption) | |
| /// e.ExtractWithPassword("Secret!") | |
| /// Else | |
| /// e.Extract | |
| /// End If | |
| /// Next | |
| /// End Using | |
| /// </code> | |
| /// </example> | |
| /// <param name="password">The Password to use for decrypting the entry.</param> | |
| public void ExtractWithPassword(string password) | |
| { | |
| InternalExtract(".", null, password); | |
| } | |
| /// <summary> | |
| /// Extract the entry to the filesystem, starting at the specified base | |
| /// directory, and using the specified password. | |
| /// </summary> | |
| /// | |
| /// <seealso cref="ZipEntry.ExtractExistingFile"/> | |
| /// <seealso cref="ZipEntry.ExtractWithPassword(string, ExtractExistingFileAction, string)"/> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// Existing entries in the filesystem will not be overwritten. If you | |
| /// would like to force the overwrite of existing files, see the <see | |
| /// cref="ZipEntry.ExtractExistingFile"/>property, or call | |
| /// <see | |
| /// cref="ExtractWithPassword(ExtractExistingFileAction,string)"/>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// See the remarks on the <see cref="LastModified"/> property, for some | |
| /// details about how the last modified time of the created file is set. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <param name="baseDirectory">The pathname of the base directory.</param> | |
| /// <param name="password">The Password to use for decrypting the entry.</param> | |
| public void ExtractWithPassword(string baseDirectory, string password) | |
| { | |
| InternalExtract(baseDirectory, null, password); | |
| } | |
| /// <summary> | |
| /// Extract the entry to a file in the filesystem, relative to the | |
| /// current directory, using the specified behavior when extraction | |
| /// would overwrite an existing file. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// See the remarks on the <see cref="LastModified"/> property, for some | |
| /// details about how the last modified time of the created file is set. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <param name="password">The Password to use for decrypting the entry.</param> | |
| /// | |
| /// <param name="extractExistingFile"> | |
| /// The action to take if extraction would overwrite an existing file. | |
| /// </param> | |
| internal void ExtractWithPassword(ExtractExistingFileAction extractExistingFile, string password) | |
| { | |
| ExtractExistingFile = extractExistingFile; | |
| InternalExtract(".", null, password); | |
| } | |
| /// <summary> | |
| /// Extract the entry to the filesystem, starting at the specified base | |
| /// directory, and using the specified behavior when extraction would | |
| /// overwrite an existing file. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// See the remarks on the <see cref="LastModified"/> property, for some | |
| /// details about how the last modified time of the created file is set. | |
| /// </remarks> | |
| /// | |
| /// <param name="baseDirectory">the pathname of the base directory</param> | |
| /// | |
| /// <param name="extractExistingFile">The action to take if extraction would | |
| /// overwrite an existing file.</param> | |
| /// | |
| /// <param name="password">The Password to use for decrypting the entry.</param> | |
| internal void ExtractWithPassword(string baseDirectory, ExtractExistingFileAction extractExistingFile, string password) | |
| { | |
| ExtractExistingFile = extractExistingFile; | |
| InternalExtract(baseDirectory, null, password); | |
| } | |
| /// <summary> | |
| /// Extracts the entry to the specified stream, using the specified | |
| /// Password. For example, the caller could extract to Console.Out, or | |
| /// to a MemoryStream. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// The caller can specify any write-able stream, for example a <see | |
| /// cref="System.IO.FileStream"/>, a <see | |
| /// cref="System.IO.MemoryStream"/>, or ASP.NET's | |
| /// <c>Response.OutputStream</c>. The content will be decrypted and | |
| /// decompressed as necessary. If the entry is encrypted and no password | |
| /// is provided, this method will throw. | |
| /// </para> | |
| /// <para> | |
| /// The position on the stream is not reset by this method before it extracts. | |
| /// You may want to call stream.Seek() before calling ZipEntry.Extract(). | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// | |
| /// <param name="stream"> | |
| /// the stream to which the entry should be extracted. | |
| /// </param> | |
| /// <param name="password"> | |
| /// The password to use for decrypting the entry. | |
| /// </param> | |
| public void ExtractWithPassword(Stream stream, string password) | |
| { | |
| InternalExtract(null, stream, password); | |
| } | |
| /// <summary> | |
| /// Opens a readable stream corresponding to the zip entry in the | |
| /// archive. The stream decompresses and decrypts as necessary, as it | |
| /// is read. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// | |
| /// <para> | |
| /// DotNetZip offers a variety of ways to extract entries from a zip | |
| /// file. This method allows an application to extract an entry by | |
| /// reading a <see cref="System.IO.Stream"/>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// The return value is of type <see | |
| /// cref="Ionic.Crc.CrcCalculatorStream"/>. Use it as you would any | |
| /// stream for reading. When an application calls <see | |
| /// cref="Stream.Read(byte[], int, int)"/> on that stream, it will | |
| /// receive data from the zip entry that is decrypted and decompressed | |
| /// as necessary. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// <c>CrcCalculatorStream</c> adds one additional feature: it keeps a | |
| /// CRC32 checksum on the bytes of the stream as it is read. The CRC | |
| /// value is available in the <see | |
| /// cref="Ionic.Crc.CrcCalculatorStream.Crc"/> property on the | |
| /// <c>CrcCalculatorStream</c>. When the read is complete, your | |
| /// application | |
| /// <em>should</em> check this CRC against the <see cref="ZipEntry.Crc"/> | |
| /// property on the <c>ZipEntry</c> to validate the content of the | |
| /// ZipEntry. You don't have to validate the entry using the CRC, but | |
| /// you should, to verify integrity. Check the example for how to do | |
| /// this. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If the entry is protected with a password, then you need to provide | |
| /// a password prior to calling <see cref="OpenReader()"/>, either by | |
| /// setting the <see cref="Password"/> property on the entry, or the | |
| /// <see cref="ZipFile.Password"/> property on the <c>ZipFile</c> | |
| /// itself. Or, you can use <see cref="OpenReader(String)" />, the | |
| /// overload of OpenReader that accepts a password parameter. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// If you want to extract entry data into a write-able stream that is | |
| /// already opened, like a <see cref="System.IO.FileStream"/>, do not | |
| /// use this method. Instead, use <see cref="Extract(Stream)"/>. | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// Your application may use only one stream created by OpenReader() at | |
| /// a time, and you should not call other Extract methods before | |
| /// completing your reads on a stream obtained from OpenReader(). This | |
| /// is because there is really only one source stream for the compressed | |
| /// content. A call to OpenReader() seeks in the source stream, to the | |
| /// beginning of the compressed content. A subsequent call to | |
| /// OpenReader() on a different entry will seek to a different position | |
| /// in the source stream, as will a call to Extract() or one of its | |
| /// overloads. This will corrupt the state for the decompressing stream | |
| /// from the original call to OpenReader(). | |
| /// </para> | |
| /// | |
| /// <para> | |
| /// The <c>OpenReader()</c> method works only when the ZipEntry is | |
| /// obtained from an instance of <c>ZipFile</c>. This method will throw | |
| /// an exception if the ZipEntry is obtained from a ZipInputStream. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <example> | |
| /// This example shows how to open a zip archive, then read in a named | |
| /// entry via a stream. After the read loop is complete, the code | |
| /// compares the calculated during the read loop with the expected CRC | |
| /// on the <c>ZipEntry</c>, to verify the extraction. | |
| /// <code> | |
| /// using (ZipFile zip = new ZipFile(ZipFileToRead)) | |
| /// { | |
| /// ZipEntry e1= zip["Elevation.mp3"]; | |
| /// using (Ionic.Zlib.CrcCalculatorStream s = e1.OpenReader()) | |
| /// { | |
| /// byte[] buffer = new byte[4096]; | |
| /// int n, totalBytesRead= 0; | |
| /// do { | |
| /// n = s.Read(buffer,0, buffer.Length); | |
| /// totalBytesRead+=n; | |
| /// } while (n>0); | |
| /// if (s.Crc32 != e1.Crc32) | |
| /// throw new Exception(string.Format("The Zip Entry failed the CRC Check. (0x{0:X8}!=0x{1:X8})", s.Crc32, e1.Crc32)); | |
| /// if (totalBytesRead != e1.UncompressedSize) | |
| /// throw new Exception(string.Format("We read an unexpected number of bytes. ({0}!={1})", totalBytesRead, e1.UncompressedSize)); | |
| /// } | |
| /// } | |
| /// </code> | |
| /// <code lang="VB"> | |
| /// Using zip As New ZipFile(ZipFileToRead) | |
| /// Dim e1 As ZipEntry = zip.Item("Elevation.mp3") | |
| /// Using s As Ionic.Zlib.CrcCalculatorStream = e1.OpenReader | |
| /// Dim n As Integer | |
| /// Dim buffer As Byte() = New Byte(4096) {} | |
| /// Dim totalBytesRead As Integer = 0 | |
| /// Do | |
| /// n = s.Read(buffer, 0, buffer.Length) | |
| /// totalBytesRead = (totalBytesRead + n) | |
| /// Loop While (n > 0) | |
| /// If (s.Crc32 <> e1.Crc32) Then | |
| /// Throw New Exception(String.Format("The Zip Entry failed the CRC Check. (0x{0:X8}!=0x{1:X8})", s.Crc32, e1.Crc32)) | |
| /// End If | |
| /// If (totalBytesRead <> e1.UncompressedSize) Then | |
| /// Throw New Exception(String.Format("We read an unexpected number of bytes. ({0}!={1})", totalBytesRead, e1.UncompressedSize)) | |
| /// End If | |
| /// End Using | |
| /// End Using | |
| /// </code> | |
| /// </example> | |
| /// <seealso cref="ZipEntry.Extract(System.IO.Stream)"/> | |
| /// <returns>The Stream for reading.</returns> | |
| internal Ionic.Crc.CrcCalculatorStream OpenReader() | |
| { | |
| // workitem 10923 | |
| if (_container.ZipFile == null) | |
| throw new InvalidOperationException("Use OpenReader() only with ZipFile."); | |
| // use the entry password if it is non-null, | |
| // else use the zipfile password, which is possibly null | |
| return InternalOpenReader(this._Password ?? this._container.Password); | |
| } | |
| /// <summary> | |
| /// Opens a readable stream for an encrypted zip entry in the archive. | |
| /// The stream decompresses and decrypts as necessary, as it is read. | |
| /// </summary> | |
| /// | |
| /// <remarks> | |
| /// <para> | |
| /// See the documentation on the <see cref="OpenReader()"/> method for | |
| /// full details. This overload allows the application to specify a | |
| /// password for the <c>ZipEntry</c> to be read. | |
| /// </para> | |
| /// </remarks> | |
| /// | |
| /// <param name="password">The password to use for decrypting the entry.</param> | |
| /// <returns>The Stream for reading.</returns> | |
| internal Ionic.Crc.CrcCalculatorStream OpenReader(string password) | |
| { | |
| // workitem 10923 | |
| if (_container.ZipFile == null) | |
| throw new InvalidOperationException("Use OpenReader() only with ZipFile."); | |
| return InternalOpenReader(password); | |
| } | |
| internal Ionic.Crc.CrcCalculatorStream InternalOpenReader(string password) | |
| { | |
| ValidateCompression(); | |
| ValidateEncryption(); | |
| SetupCryptoForExtract(password); | |
| // workitem 7958 | |
| if (this._Source != ZipEntrySource.ZipFile) | |
| throw new BadStateException("You must call ZipFile.Save before calling OpenReader"); | |
| // LeftToRead is a count of bytes remaining to be read (out) | |
| // from the stream AFTER decompression and decryption. | |
| // It is the uncompressed size, unless ... there is no compression in which | |
| // case ...? :< I'm not sure why it's not always UncompressedSize | |
| Int64 LeftToRead = (_CompressionMethod_FromZipFile == (short)CompressionMethod.None) | |
| ? this._CompressedFileDataSize | |
| : this.UncompressedSize; | |
| Stream input = this.ArchiveStream; | |
| this.ArchiveStream.Seek(this.FileDataPosition, SeekOrigin.Begin); | |
| // workitem 10178 | |
| Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); | |
| _inputDecryptorStream = GetExtractDecryptor(input); | |
| Stream input3 = GetExtractDecompressor(_inputDecryptorStream); | |
| return new Ionic.Crc.CrcCalculatorStream(input3, LeftToRead); | |
| } | |
| private void OnExtractProgress(Int64 bytesWritten, Int64 totalBytesToWrite) | |
| { | |
| if (_container.ZipFile != null) | |
| _ioOperationCanceled = _container.ZipFile.OnExtractBlock(this, bytesWritten, totalBytesToWrite); | |
| } | |
| private void OnBeforeExtract(string path) | |
| { | |
| // When in the context of a ZipFile.ExtractAll, the events are generated from | |
| // the ZipFile method, not from within the ZipEntry instance. (why?) | |
| // Therefore we suppress the events originating from the ZipEntry method. | |
| if (_container.ZipFile != null) | |
| { | |
| if (!_container.ZipFile._inExtractAll) | |
| { | |
| _ioOperationCanceled = _container.ZipFile.OnSingleEntryExtract(this, path, true); | |
| } | |
| } | |
| } | |
| private void OnAfterExtract(string path) | |
| { | |
| // When in the context of a ZipFile.ExtractAll, the events are generated from | |
| // the ZipFile method, not from within the ZipEntry instance. (why?) | |
| // Therefore we suppress the events originating from the ZipEntry method. | |
| if (_container.ZipFile != null) | |
| { | |
| if (!_container.ZipFile._inExtractAll) | |
| { | |
| _container.ZipFile.OnSingleEntryExtract(this, path, false); | |
| } | |
| } | |
| } | |
| private void OnExtractExisting(string path) | |
| { | |
| if (_container.ZipFile != null) | |
| _ioOperationCanceled = _container.ZipFile.OnExtractExisting(this, path); | |
| } | |
| private static void ReallyDelete(string fileName) | |
| { | |
| // workitem 7881 | |
| // reset ReadOnly bit if necessary | |
| #if NETCF | |
| if ( (NetCfFile.GetAttributes(fileName) & (uint)FileAttributes.ReadOnly) == (uint)FileAttributes.ReadOnly) | |
| NetCfFile.SetAttributes(fileName, (uint)FileAttributes.Normal); | |
| #elif SILVERLIGHT | |
| #else | |
| if ((File.GetAttributes(fileName) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) | |
| File.SetAttributes(fileName, FileAttributes.Normal); | |
| #endif | |
| File.Delete(fileName); | |
| } | |
| private void WriteStatus(string format, params Object[] args) | |
| { | |
| if (_container.ZipFile != null && _container.ZipFile.Verbose) _container.ZipFile.StatusMessageTextWriter.WriteLine(format, args); | |
| } | |
| // Pass in either basedir or s, but not both. | |
| // In other words, you can extract to a stream or to a directory (filesystem), but not both! | |
| // The Password param is required for encrypted entries. | |
| private void InternalExtract(string baseDir, Stream outstream, string password) | |
| { | |
| // workitem 7958 | |
| if (_container == null) | |
| throw new BadStateException("This entry is an orphan"); | |
| // workitem 10355 | |
| if (_container.ZipFile == null) | |
| throw new InvalidOperationException("Use Extract() only with ZipFile."); | |
| _container.ZipFile.Reset(false); | |
| if (this._Source != ZipEntrySource.ZipFile) | |
| throw new BadStateException("You must call ZipFile.Save before calling any Extract method"); | |
| OnBeforeExtract(baseDir); | |
| _ioOperationCanceled = false; | |
| string targetFileName = null; | |
| Stream output = null; | |
| bool fileExistsBeforeExtraction = false; | |
| bool checkLaterForResetDirTimes = false; | |
| try | |
| { | |
| ValidateCompression(); | |
| ValidateEncryption(); | |
| if (ValidateOutput(baseDir, outstream, out targetFileName)) | |
| { | |
| WriteStatus("extract dir {0}...", targetFileName); | |
| // if true, then the entry was a directory and has been created. | |
| // We need to fire the Extract Event. | |
| OnAfterExtract(baseDir); | |
| return; | |
| } | |
| // workitem 10639 | |
| // do we want to extract to a regular filesystem file? | |
| if (targetFileName != null) | |
| { | |
| // Check for extracting to a previously extant file. The user | |
| // can specify bejavior for that case: overwrite, don't | |
| // overwrite, and throw. Also, if the file exists prior to | |
| // extraction, it affects exception handling: whether to delete | |
| // the target of extraction or not. This check needs to be done | |
| // before the password check is done, because password check may | |
| // throw a BadPasswordException, which triggers the catch, | |
| // wherein the extant file may be deleted if not flagged as | |
| // pre-existing. | |
| if (File.Exists(targetFileName)) | |
| { | |
| fileExistsBeforeExtraction = true; | |
| int rc = CheckExtractExistingFile(baseDir, targetFileName); | |
| if (rc == 2) goto ExitTry; // cancel | |
| if (rc == 1) return; // do not overwrite | |
| } | |
| } | |
| // If no password explicitly specified, use the password on the entry itself, | |
| // or on the zipfile itself. | |
| string p = password ?? this._Password ?? this._container.Password; | |
| if (_Encryption_FromZipFile != EncryptionAlgorithm.None) | |
| { | |
| if (p == null) | |
| throw new BadPasswordException(); | |
| SetupCryptoForExtract(p); | |
| } | |
| // set up the output stream | |
| if (targetFileName != null) | |
| { | |
| WriteStatus("extract file {0}...", targetFileName); | |
| targetFileName += ".tmp"; | |
| var dirName = Path.GetDirectoryName(targetFileName); | |
| // ensure the target path exists | |
| if (!Directory.Exists(dirName)) | |
| { | |
| // we create the directory here, but we do not set the | |
| // create/modified/accessed times on it because it is being | |
| // created implicitly, not explcitly. There's no entry in the | |
| // zip archive for the directory. | |
| Directory.CreateDirectory(dirName); | |
| } | |
| else | |
| { | |
| // workitem 8264 | |
| if (_container.ZipFile != null) | |
| checkLaterForResetDirTimes = _container.ZipFile._inExtractAll; | |
| } | |
| // File.Create(CreateNew) will overwrite any existing file. | |
| output = new FileStream(targetFileName, FileMode.CreateNew); | |
| } | |
| else | |
| { | |
| WriteStatus("extract entry {0} to stream...", FileName); | |
| output = outstream; | |
| } | |
| if (_ioOperationCanceled) | |
| goto ExitTry; | |
| Int32 ActualCrc32 = ExtractOne(output); | |
| if (_ioOperationCanceled) | |
| goto ExitTry; | |
| VerifyCrcAfterExtract(ActualCrc32); | |
| if (targetFileName != null) | |
| { | |
| output.Close(); | |
| output = null; | |
| // workitem 10639 | |
| // move file to permanent home | |
| string tmpName = targetFileName; | |
| string zombie = null; | |
| targetFileName = tmpName.Substring(0,tmpName.Length-4); | |
| if (fileExistsBeforeExtraction) | |
| { | |
| // An AV program may hold the target file open, which means | |
| // File.Delete() will succeed, though the actual deletion | |
| // remains pending. This will prevent a subsequent | |
| // File.Move() from succeeding. To avoid this, when the file | |
| // already exists, we need to replace it in 3 steps: | |
| // | |
| // 1. rename the existing file to a zombie name; | |
| // 2. rename the extracted file from the temp name to | |
| // the target file name; | |
| // 3. delete the zombie. | |
| // | |
| zombie = targetFileName + ".PendingOverwrite"; | |
| File.Move(targetFileName, zombie); | |
| } | |
| File.Move(tmpName, targetFileName); | |
| _SetTimes(targetFileName, true); | |
| if (zombie != null && File.Exists(zombie)) | |
| ReallyDelete(zombie); | |
| // workitem 8264 | |
| if (checkLaterForResetDirTimes) | |
| { | |
| // This is sort of a hack. What I do here is set the time on | |
| // the parent directory, every time a file is extracted into | |
| // it. If there is a directory with 1000 files, then I set | |
| // the time on the dir, 1000 times. This allows the directory | |
| // to have times that reflect the actual time on the entry in | |
| // the zip archive. | |
| // String.Contains is not available on .NET CF 2.0 | |
| if (this.FileName.IndexOf('/') != -1) | |
| { | |
| string dirname = Path.GetDirectoryName(this.FileName); | |
| if (this._container.ZipFile[dirname] == null) | |
| { | |
| _SetTimes(Path.GetDirectoryName(targetFileName), false); | |
| } | |
| } | |
| } | |
| #if NETCF | |
| // workitem 7926 - version made by OS can be zero or 10 | |
| if ((_VersionMadeBy & 0xFF00) == 0x0a00 || (_VersionMadeBy & 0xFF00) == 0x0000) | |
| NetCfFile.SetAttributes(targetFileName, (uint)_ExternalFileAttrs); | |
| #else | |
| // workitem 7071 | |
| // | |
| // We can only apply attributes if they are relevant to the NTFS | |
| // OS. Must do this LAST because it may involve a ReadOnly bit, | |
| // which would prevent us from setting the time, etc. | |
| // | |
| // workitem 7926 - version made by OS can be zero (FAT) or 10 | |
| // (NTFS) | |
| if ((_VersionMadeBy & 0xFF00) == 0x0a00 || (_VersionMadeBy & 0xFF00) == 0x0000) | |
| File.SetAttributes(targetFileName, (FileAttributes)_ExternalFileAttrs); | |
| #endif | |
| } | |
| OnAfterExtract(baseDir); | |
| ExitTry: ; | |
| } | |
| catch (Exception) | |
| { | |
| _ioOperationCanceled = true; | |
| throw; | |
| } | |
| finally | |
| { | |
| if (_ioOperationCanceled) | |
| { | |
| if (targetFileName != null) | |
| { | |
| try | |
| { | |
| if (output != null) output.Close(); | |
| // An exception has occurred. If the file exists, check | |
| // to see if it existed before we tried extracting. If | |
| // it did not, attempt to remove the target file. There | |
| // is a small possibility that the existing file has | |
| // been extracted successfully, overwriting a previously | |
| // existing file, and an exception was thrown after that | |
| // but before final completion (setting times, etc). In | |
| // that case the file will remain, even though some | |
| // error occurred. Nothing to be done about it. | |
| if (File.Exists(targetFileName) && !fileExistsBeforeExtraction) | |
| File.Delete(targetFileName); | |
| } | |
| finally { } | |
| } | |
| } | |
| } | |
| } | |
| #if NOT | |
| internal void CalcWinZipAesMac(Stream input) | |
| { | |
| if (Encryption == EncryptionAlgorithm.WinZipAes128 || | |
| Encryption == EncryptionAlgorithm.WinZipAes256) | |
| { | |
| if (input is WinZipAesCipherStream) | |
| wzs = input as WinZipAesCipherStream; | |
| else if (input is Ionic.Zlib.CrcCalculatorStream) | |
| { | |
| xxx; | |
| } | |
| } | |
| } | |
| #endif | |
| internal void VerifyCrcAfterExtract(Int32 actualCrc32) | |
| { | |
| #if AESCRYPTO | |
| // After extracting, Validate the CRC32 | |
| if (actualCrc32 != _Crc32) | |
| { | |
| // CRC is not meaningful with WinZipAES and AES method 2 (AE-2) | |
| if ((Encryption != EncryptionAlgorithm.WinZipAes128 && | |
| Encryption != EncryptionAlgorithm.WinZipAes256) | |
| || _WinZipAesMethod != 0x02) | |
| throw new BadCrcException("CRC error: the file being extracted appears to be corrupted. " + | |
| String.Format("Expected 0x{0:X8}, Actual 0x{1:X8}", _Crc32, actualCrc32)); | |
| } | |
| // ignore MAC if the size of the file is zero | |
| if (this.UncompressedSize == 0) | |
| return; | |
| // calculate the MAC | |
| if (Encryption == EncryptionAlgorithm.WinZipAes128 || | |
| Encryption == EncryptionAlgorithm.WinZipAes256) | |
| { | |
| WinZipAesCipherStream wzs = _inputDecryptorStream as WinZipAesCipherStream; | |
| _aesCrypto_forExtract.CalculatedMac = wzs.FinalAuthentication; | |
| _aesCrypto_forExtract.ReadAndVerifyMac(this.ArchiveStream); // throws if MAC is bad | |
| // side effect: advances file position. | |
| } | |
| #else | |
| if (actualCrc32 != _Crc32) | |
| throw new BadCrcException("CRC error: the file being extracted appears to be corrupted. " + | |
| String.Format("Expected 0x{0:X8}, Actual 0x{1:X8}", _Crc32, actualCrc32)); | |
| #endif | |
| } | |
| private int CheckExtractExistingFile(string baseDir, string targetFileName) | |
| { | |
| int loop = 0; | |
| // returns: 0 == extract, 1 = don't, 2 = cancel | |
| do | |
| { | |
| switch (ExtractExistingFile) | |
| { | |
| case ExtractExistingFileAction.OverwriteSilently: | |
| WriteStatus("the file {0} exists; will overwrite it...", targetFileName); | |
| return 0; | |
| case ExtractExistingFileAction.DoNotOverwrite: | |
| WriteStatus("the file {0} exists; not extracting entry...", FileName); | |
| OnAfterExtract(baseDir); | |
| return 1; | |
| case ExtractExistingFileAction.InvokeExtractProgressEvent: | |
| if (loop>0) | |
| throw new ZipException(String.Format("The file {0} already exists.", targetFileName)); | |
| OnExtractExisting(baseDir); | |
| if (_ioOperationCanceled) | |
| return 2; | |
| // loop around | |
| break; | |
| case ExtractExistingFileAction.Throw: | |
| default: | |
| throw new ZipException(String.Format("The file {0} already exists.", targetFileName)); | |
| } | |
| loop++; | |
| } | |
| while (true); | |
| } | |
| private void _CheckRead(int nbytes) | |
| { | |
| if (nbytes == 0) | |
| throw new BadReadException(String.Format("bad read of entry {0} from compressed archive.", | |
| this.FileName)); | |
| } | |
| private Stream _inputDecryptorStream; | |
| private Int32 ExtractOne(Stream output) | |
| { | |
| Int32 CrcResult = 0; | |
| Stream input = this.ArchiveStream; | |
| try | |
| { | |
| // change for workitem 8098 | |
| input.Seek(this.FileDataPosition, SeekOrigin.Begin); | |
| // workitem 10178 | |
| Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(input); | |
| byte[] bytes = new byte[BufferSize]; | |
| // The extraction process varies depending on how the entry was | |
| // stored. It could have been encrypted, and it coould have | |
| // been compressed, or both, or neither. So we need to check | |
| // both the encryption flag and the compression flag, and take | |
| // the proper action in all cases. | |
| Int64 LeftToRead = (_CompressionMethod_FromZipFile != (short)CompressionMethod.None) | |
| ? this.UncompressedSize | |
| : this._CompressedFileDataSize; | |
| // Get a stream that either decrypts or not. | |
| _inputDecryptorStream = GetExtractDecryptor(input); | |
| Stream input3 = GetExtractDecompressor( _inputDecryptorStream ); | |
| Int64 bytesWritten = 0; | |
| // As we read, we maybe decrypt, and then we maybe decompress. Then we write. | |
| using (var s1 = new Ionic.Crc.CrcCalculatorStream(input3)) | |
| { | |
| while (LeftToRead > 0) | |
| { | |
| //Console.WriteLine("ExtractOne: LeftToRead {0}", LeftToRead); | |
| // Casting LeftToRead down to an int is ok here in the else clause, | |
| // because that only happens when it is less than bytes.Length, | |
| // which is much less than MAX_INT. | |
| int len = (LeftToRead > bytes.Length) ? bytes.Length : (int)LeftToRead; | |
| int n = s1.Read(bytes, 0, len); | |
| // must check data read - essential for detecting corrupt zip files | |
| _CheckRead(n); | |
| output.Write(bytes, 0, n); | |
| LeftToRead -= n; | |
| bytesWritten += n; | |
| // fire the progress event, check for cancels | |
| OnExtractProgress(bytesWritten, UncompressedSize); | |
| if (_ioOperationCanceled) | |
| { | |
| break; | |
| } | |
| } | |
| CrcResult = s1.Crc; | |
| } | |
| } | |
| finally | |
| { | |
| var zss = input as ZipSegmentedStream; | |
| if (zss != null) | |
| { | |
| #if NETCF | |
| zss.Close(); | |
| #else | |
| // need to dispose it | |
| zss.Dispose(); | |
| #endif | |
| _archiveStream = null; | |
| } | |
| } | |
| return CrcResult; | |
| } | |
| internal Stream GetExtractDecompressor(Stream input2) | |
| { | |
| // get a stream that either decompresses or not. | |
| switch (_CompressionMethod_FromZipFile) | |
| { | |
| case (short)CompressionMethod.None: | |
| return input2; | |
| case (short)CompressionMethod.Deflate: | |
| return new Ionic.Zlib.DeflateStream(input2, Ionic.Zlib.CompressionMode.Decompress, true); | |
| #if BZIP | |
| case (short)CompressionMethod.BZip2: | |
| return new Ionic.BZip2.BZip2InputStream(input2, true); | |
| #endif | |
| } | |
| return null; | |
| } | |
| internal Stream GetExtractDecryptor(Stream input) | |
| { | |
| Stream input2 = null; | |
| if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak) | |
| input2 = new ZipCipherStream(input, _zipCrypto_forExtract, CryptoMode.Decrypt); | |
| #if AESCRYPTO | |
| else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 || | |
| _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256) | |
| input2 = new WinZipAesCipherStream(input, _aesCrypto_forExtract, _CompressedFileDataSize, CryptoMode.Decrypt); | |
| #endif | |
| else | |
| input2 = input; | |
| return input2; | |
| } | |
| internal void _SetTimes(string fileOrDirectory, bool isFile) | |
| { | |
| #if SILVERLIGHT | |
| // punt on setting file times | |
| #else | |
| // workitem 8807: | |
| // Because setting the time is not considered to be a fatal error, | |
| // and because other applications can interfere with the setting | |
| // of a time on a directory, we're going to swallow IO exceptions | |
| // in this method. | |
| try | |
| { | |
| if (_ntfsTimesAreSet) | |
| { | |
| #if NETCF | |
| // workitem 7944: set time should not be a fatal error on CF | |
| int rc = NetCfFile.SetTimes(fileOrDirectory, _Ctime, _Atime, _Mtime); | |
| if ( rc != 0) | |
| { | |
| WriteStatus("Warning: SetTimes failed. entry({0}) file({1}) rc({2})", | |
| FileName, fileOrDirectory, rc); | |
| } | |
| #else | |
| if (isFile) | |
| { | |
| // It's possible that the extract was cancelled, in which case, | |
| // the file does not exist. | |
| if (File.Exists(fileOrDirectory)) | |
| { | |
| File.SetCreationTimeUtc(fileOrDirectory, _Ctime); | |
| File.SetLastAccessTimeUtc(fileOrDirectory, _Atime); | |
| File.SetLastWriteTimeUtc(fileOrDirectory, _Mtime); | |
| } | |
| } | |
| else | |
| { | |
| // It's possible that the extract was cancelled, in which case, | |
| // the directory does not exist. | |
| if (Directory.Exists(fileOrDirectory)) | |
| { | |
| Directory.SetCreationTimeUtc(fileOrDirectory, _Ctime); | |
| Directory.SetLastAccessTimeUtc(fileOrDirectory, _Atime); | |
| Directory.SetLastWriteTimeUtc(fileOrDirectory, _Mtime); | |
| } | |
| } | |
| #endif | |
| } | |
| else | |
| { | |
| // workitem 6191 | |
| DateTime AdjustedLastModified = Ionic.Zip.SharedUtilities.AdjustTime_Reverse(LastModified); | |
| #if NETCF | |
| int rc = NetCfFile.SetLastWriteTime(fileOrDirectory, AdjustedLastModified); | |
| if ( rc != 0) | |
| { | |
| WriteStatus("Warning: SetLastWriteTime failed. entry({0}) file({1}) rc({2})", | |
| FileName, fileOrDirectory, rc); | |
| } | |
| #else | |
| if (isFile) | |
| File.SetLastWriteTime(fileOrDirectory, AdjustedLastModified); | |
| else | |
| Directory.SetLastWriteTime(fileOrDirectory, AdjustedLastModified); | |
| #endif | |
| } | |
| } | |
| catch (System.IO.IOException ioexc1) | |
| { | |
| WriteStatus("failed to set time on {0}: {1}", fileOrDirectory, ioexc1.Message); | |
| } | |
| #endif | |
| } | |
| #region Support methods | |
| // workitem 7968 | |
| private string UnsupportedAlgorithm | |
| { | |
| get | |
| { | |
| string alg = String.Empty; | |
| switch (_UnsupportedAlgorithmId) | |
| { | |
| case 0: | |
| alg = "--"; | |
| break; | |
| case 0x6601: | |
| alg = "DES"; | |
| break; | |
| case 0x6602: // - RC2 (version needed to extract < 5.2) | |
| alg = "RC2"; | |
| break; | |
| case 0x6603: // - 3DES 168 | |
| alg = "3DES-168"; | |
| break; | |
| case 0x6609: // - 3DES 112 | |
| alg = "3DES-112"; | |
| break; | |
| case 0x660E: // - AES 128 | |
| alg = "PKWare AES128"; | |
| break; | |
| case 0x660F: // - AES 192 | |
| alg = "PKWare AES192"; | |
| break; | |
| case 0x6610: // - AES 256 | |
| alg = "PKWare AES256"; | |
| break; | |
| case 0x6702: // - RC2 (version needed to extract >= 5.2) | |
| alg = "RC2"; | |
| break; | |
| case 0x6720: // - Blowfish | |
| alg = "Blowfish"; | |
| break; | |
| case 0x6721: // - Twofish | |
| alg = "Twofish"; | |
| break; | |
| case 0x6801: // - RC4 | |
| alg = "RC4"; | |
| break; | |
| case 0xFFFF: // - Unknown algorithm | |
| default: | |
| alg = String.Format("Unknown (0x{0:X4})", _UnsupportedAlgorithmId); | |
| break; | |
| } | |
| return alg; | |
| } | |
| } | |
| // workitem 7968 | |
| private string UnsupportedCompressionMethod | |
| { | |
| get | |
| { | |
| string meth = String.Empty; | |
| switch ((int)_CompressionMethod) | |
| { | |
| case 0: | |
| meth = "Store"; | |
| break; | |
| case 1: | |
| meth = "Shrink"; | |
| break; | |
| case 8: | |
| meth = "DEFLATE"; | |
| break; | |
| case 9: | |
| meth = "Deflate64"; | |
| break; | |
| case 12: | |
| meth = "BZIP2"; // only if BZIP not compiled in | |
| break; | |
| case 14: | |
| meth = "LZMA"; | |
| break; | |
| case 19: | |
| meth = "LZ77"; | |
| break; | |
| case 98: | |
| meth = "PPMd"; | |
| break; | |
| default: | |
| meth = String.Format("Unknown (0x{0:X4})", _CompressionMethod); | |
| break; | |
| } | |
| return meth; | |
| } | |
| } | |
| internal void ValidateEncryption() | |
| { | |
| if (Encryption != EncryptionAlgorithm.PkzipWeak && | |
| #if AESCRYPTO | |
| Encryption != EncryptionAlgorithm.WinZipAes128 && | |
| Encryption != EncryptionAlgorithm.WinZipAes256 && | |
| #endif | |
| Encryption != EncryptionAlgorithm.None) | |
| { | |
| // workitem 7968 | |
| if (_UnsupportedAlgorithmId != 0) | |
| throw new ZipException(String.Format("Cannot extract: Entry {0} is encrypted with an algorithm not supported by DotNetZip: {1}", | |
| FileName, UnsupportedAlgorithm)); | |
| else | |
| throw new ZipException(String.Format("Cannot extract: Entry {0} uses an unsupported encryption algorithm ({1:X2})", | |
| FileName, (int)Encryption)); | |
| } | |
| } | |
| private void ValidateCompression() | |
| { | |
| if ((_CompressionMethod_FromZipFile != (short)CompressionMethod.None) && | |
| (_CompressionMethod_FromZipFile != (short)CompressionMethod.Deflate) | |
| #if BZIP | |
| && (_CompressionMethod_FromZipFile != (short)CompressionMethod.BZip2) | |
| #endif | |
| ) | |
| throw new ZipException(String.Format("Entry {0} uses an unsupported compression method (0x{1:X2}, {2})", | |
| FileName, _CompressionMethod_FromZipFile, UnsupportedCompressionMethod)); | |
| } | |
| private void SetupCryptoForExtract(string password) | |
| { | |
| //if (password == null) return; | |
| if (_Encryption_FromZipFile == EncryptionAlgorithm.None) return; | |
| if (_Encryption_FromZipFile == EncryptionAlgorithm.PkzipWeak) | |
| { | |
| if (password == null) | |
| throw new ZipException("Missing password."); | |
| this.ArchiveStream.Seek(this.FileDataPosition - 12, SeekOrigin.Begin); | |
| // workitem 10178 | |
| Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); | |
| _zipCrypto_forExtract = ZipCrypto.ForRead(password, this); | |
| } | |
| #if AESCRYPTO | |
| else if (_Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes128 || | |
| _Encryption_FromZipFile == EncryptionAlgorithm.WinZipAes256) | |
| { | |
| if (password == null) | |
| throw new ZipException("Missing password."); | |
| // If we already have a WinZipAesCrypto object in place, use it. | |
| // It can be set up in the ReadDirEntry(), or during a previous Extract. | |
| if (_aesCrypto_forExtract != null) | |
| { | |
| _aesCrypto_forExtract.Password = password; | |
| } | |
| else | |
| { | |
| int sizeOfSaltAndPv = GetLengthOfCryptoHeaderBytes(_Encryption_FromZipFile); | |
| this.ArchiveStream.Seek(this.FileDataPosition - sizeOfSaltAndPv, SeekOrigin.Begin); | |
| // workitem 10178 | |
| Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream); | |
| int keystrength = GetKeyStrengthInBits(_Encryption_FromZipFile); | |
| _aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(password, keystrength, this.ArchiveStream); | |
| } | |
| } | |
| #endif | |
| } | |
| /// <summary> | |
| /// Validates that the args are consistent. | |
| /// </summary> | |
| /// <remarks> | |
| /// Only one of {baseDir, outStream} can be non-null. | |
| /// If baseDir is non-null, then the outputFile is created. | |
| /// </remarks> | |
| private bool ValidateOutput(string basedir, Stream outstream, out string outFileName) | |
| { | |
| if (basedir != null) | |
| { | |
| // Sometimes the name on the entry starts with a slash. | |
| // Rather than unpack to the root of the volume, we're going to | |
| // drop the slash and unpack to the specified base directory. | |
| string f = this.FileName.Replace("\\","/"); | |
| // workitem 11772: remove drive letter with separator | |
| if (f.IndexOf(':') == 1) | |
| f= f.Substring(2); | |
| if (f.StartsWith("/")) | |
| f= f.Substring(1); | |
| // String.Contains is not available on .NET CF 2.0 | |
| if (_container.ZipFile.FlattenFoldersOnExtract) | |
| outFileName = Path.Combine(basedir, | |
| (f.IndexOf('/') != -1) ? Path.GetFileName(f) : f); | |
| else | |
| outFileName = Path.Combine(basedir, f); | |
| // workitem 10639 | |
| outFileName = outFileName.Replace("/","\\"); | |
| // check if it is a directory | |
| if ((IsDirectory) || (FileName.EndsWith("/"))) | |
| { | |
| if (!Directory.Exists(outFileName)) | |
| { | |
| Directory.CreateDirectory(outFileName); | |
| _SetTimes(outFileName, false); | |
| } | |
| else | |
| { | |
| // the dir exists, maybe we want to overwrite times. | |
| if (ExtractExistingFile == ExtractExistingFileAction.OverwriteSilently) | |
| _SetTimes(outFileName, false); | |
| } | |
| return true; // true == all done, caller will return | |
| } | |
| return false; // false == work to do by caller. | |
| } | |
| if (outstream != null) | |
| { | |
| outFileName = null; | |
| if ((IsDirectory) || (FileName.EndsWith("/"))) | |
| { | |
| // extract a directory to streamwriter? nothing to do! | |
| return true; // true == all done! caller can return | |
| } | |
| return false; | |
| } | |
| throw new ArgumentNullException("outstream"); | |
| } | |
| #endregion | |
| } | |
| } |