// 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 HH.WMS.Utils.Ionic.Zip
|
{
|
|
public 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="HH.WMS.Utils.Ionic.Zip.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>
|
public 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>
|
public 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="HH.WMS.Utils.Ionic.Zip.ZipEntry.ExtractExistingFile"/>
|
/// <seealso cref="HH.WMS.Utils.Ionic.Zip.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>
|
public 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="HH.WMS.Utils.Ionic.Zip.ZipEntry.ExtractExistingFile"/>
|
/// <seealso cref="HH.WMS.Utils.Ionic.Zip.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="HH.WMS.Utils.Ionic.Zip.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="HH.WMS.Utils.Ionic.Zip.ZipEntry.ExtractExistingFile"/>
|
/// <seealso cref="HH.WMS.Utils.Ionic.Zip.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="HH.WMS.Utils.Ionic.Zip.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>
|
public 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>
|
public 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="HH.WMS.Utils.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="HH.WMS.Utils.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 <see
|
/// cref="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 (HH.WMS.Utils.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 HH.WMS.Utils.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="HH.WMS.Utils.Ionic.Zip.ZipEntry.Extract(System.IO.Stream)"/>
|
/// <returns>The Stream for reading.</returns>
|
public HH.WMS.Utils.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>
|
public HH.WMS.Utils.Ionic.Crc.CrcCalculatorStream OpenReader(string password)
|
{
|
// workitem 10923
|
if (_container.ZipFile == null)
|
throw new InvalidOperationException("Use OpenReader() only with ZipFile.");
|
|
return InternalOpenReader(password);
|
}
|
|
|
|
internal HH.WMS.Utils.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
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
|
_inputDecryptorStream = GetExtractDecryptor(input);
|
Stream input3 = GetExtractDecompressor(_inputDecryptorStream);
|
|
return new HH.WMS.Utils.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 HH.WMS.Utils.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
|
HH.WMS.Utils.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 HH.WMS.Utils.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 HH.WMS.Utils.Ionic.Zlib.DeflateStream(input2, HH.WMS.Utils.Ionic.Zlib.CompressionMode.Decompress, true);
|
#if BZIP
|
case (short)CompressionMethod.BZip2:
|
return new HH.WMS.Utils.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 = HH.WMS.Utils.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
|
HH.WMS.Utils.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
|
HH.WMS.Utils.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
|
|
}
|
}
|