/dotnet/common-windsor-net/windsor-commons-compression/DotNetZip/Library/ZipEntry.cs
http://opennode2.googlecode.com/ · C# · 3960 lines · 1861 code · 586 blank · 1513 comment · 494 complexity · 0490151077d9c42f7c2318602c17223c MD5 · raw file
Large files are truncated click here to view the full file
- #region License
- /*
- Copyright (c) 2009, The Environmental Council of the States (ECOS)
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of the ECOS nor the names of its contributors may
- be used to endorse or promote products derived from this software
- without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
- #endregion
-
- #define OPTIMIZE_WI6612
-
- // ZipEntry.cs
- //
- // Copyright (c) 2006, 2007, 2008, 2009 Microsoft Corporation. All rights reserved.
- //
- // Part of an implementation of a zipfile class library.
- // See the file ZipFile.cs for the license and for further information.
- //
- // Created: Tue, 27 Mar 2007 15:30
- //
-
- using System;
- using System.IO;
- using RE = System.Text.RegularExpressions;
-
- namespace Ionic.Zip
- {
- /// <summary>
- /// An enum that provides the various encryption algorithms supported by this library.
- /// </summary>
- /// <remarks>
- /// <para>
- /// PkzipWeak implies the use of Zip 2.0 encryption, which is known to be weak and subvertible.
- /// </para>
- /// <para>
- /// A note on interoperability: Values of PkzipWeak and None are specified in the PKWare AppNote.txt document, are
- /// considered to be "standard". Zip archives produced using these options will be interoperable with many other
- /// zip tools and libraries, including Windows Explorer.
- /// </para>
- /// <para>
- /// Values of WinZipAes128 and WinZipAes256 are not part of the Zip specification, but rather imply the use of a
- /// vendor-specific extension from WinZip. If you want to produce interoperable Zip archives, do not use these values.
- /// For example, if you
- /// produce a zip archive using WinZipAes256, you will be able to open it in Windows Explorer on Windows XP and Vista,
- /// but you will not be able to extract entries; trying this will lead to an "unspecified error". For this reason,
- /// some people have said that a zip archive that uses WinZip's AES encryption is not actually a zip archive at all.
- /// A zip archive produced this way will be readable with the WinZip tool
- /// (Version 11 and beyond).
- /// </para>
- /// <para>
- /// There are other third-party tools and libraries, both commercial and otherwise, that support WinZip's
- /// AES encryption. These will be able to read AES-encrypted zip archives produced by DotNetZip, and conversely applications
- /// that use DotNetZip to read zip archives will be able to read AES-encrypted archives produced by those tools
- /// or libraries. Consult the documentation for those other tools and libraries to find out if WinZip's AES
- /// encryption is supported.
- /// </para>
- /// <para>
- /// In case you care: According to the WinZip specification, the actual key used is derived from the
- /// <see cref="ZipEntry.Password"/> via an algorithm that complies with RFC 2898, using an iteration count of 1000.
- /// I am no security expert, but I think you should use a long-ish password if you employ 256-bit AES
- /// encryption. Make it 16 characters or more.
- /// </para>
- /// <para>
- /// The WinZip AES algorithms are not supported with the version of DotNetZip that runs on the .NET Compact Framework.
- /// This is because .NET CF lacks the HMACSHA1 class that is required for producing the archive.
- /// </para>
- /// </remarks>
- public enum EncryptionAlgorithm
- {
- /// <summary>
- /// No encryption at all.
- /// </summary>
- None = 0,
-
- /// <summary>
- /// Traditional or Classic pkzip encryption.
- /// </summary>
- PkzipWeak,
-
- #if AESCRYPTO
- /// <summary>
- /// WinZip AES encryption (128 key bits).
- /// </summary>
- WinZipAes128,
-
- /// <summary>
- /// WinZip AES encryption (256 key bits).
- /// </summary>
- WinZipAes256,
- #endif
-
- // others... not implemented (yet?)
- }
-
- /// <summary>
- /// An enum that specifies the source of the ZipEntry.
- /// </summary>
- internal enum EntrySource
- {
- /// <summary>
- /// Default value. Invalid on a bonafide ZipEntry.
- /// </summary>
- None = 0,
-
- /// <summary>
- /// Entry was instantiated by Adding an entry from the filesystem.
- /// </summary>
- Filesystem,
-
- /// <summary>
- /// Entry was instantiated by reading a zipfile.
- /// </summary>
- Zipfile,
-
- /// <summary>
- /// Entry was instantiated via a stream or string.
- /// </summary>
- Stream,
- }
-
-
- /// <summary>
- /// Represents a single entry in a ZipFile. Typically, applications
- /// get a ZipEntry by enumerating the entries within a ZipFile,
- /// or by adding an entry to a ZipFile.
- /// </summary>
- public partial class ZipEntry
- {
- internal ZipEntry() { }
-
- /// <summary>
- /// The time and date at which the file indicated by the ZipEntry was last modified.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// The DotNetZip library sets the LastModified value for an entry, equal to the
- /// Last Modified time of the file in the filesystem. If an entry is added from a stream,
- /// in which case no Last Modified attribute is available, the library uses
- /// <c>System.DateTime.Now</c> for this value, for the given entry.
- /// </para>
- ///
- /// <para>
- /// It is also possible to set the LastModified value on an entry, to an arbitrary
- /// value. Be aware that because of the way the PKZip specification describes how
- /// times are stored in the zip file, the full precision of the
- /// <c>System.DateTime</c> datatype is not stored in LastModified when saving zip
- /// files. For more information on how times are formatted, see the PKZip
- /// specification.
- /// </para>
- ///
- /// <para>
- /// The last modified time of the file created upon a call to <c>ZipEntry.Extract()</c>
- /// may be adjusted during extraction to compensate
- /// for differences in how the .NET Base Class Library deals
- /// with daylight saving time (DST) versus how the Windows
- /// filesystem deals with daylight saving time.
- /// See http://blogs.msdn.com/oldnewthing/archive/2003/10/24/55413.aspx for more context.
- /// </para>
- /// <para>
- /// In a nutshell: Daylight savings time rules change regularly. In
- /// 2007, for example, the inception week of DST changed. In 1977,
- /// DST was in place all year round. In 1945, likewise. And so on.
- /// Win32 does not attempt to guess which time zone rules were in
- /// effect at the time in question. It will render a time as
- /// "standard time" and allow the app to change to DST as necessary.
- /// .NET makes a different choice.
- /// </para>
- /// <para>
- /// Compare the output of FileInfo.LastWriteTime.ToString("f") with
- /// what you see in the Windows Explorer property sheet for a file that was last
- /// written to on the other side of the DST transition. For example,
- /// suppose the file was last modified on October 17, 2003, during DST but
- /// DST is not currently in effect. Explorer's file properties
- /// reports Thursday, October 17, 2003, 8:45:38 AM, but .NETs
- /// FileInfo reports Thursday, October 17, 2003, 9:45 AM.
- /// </para>
- /// <para>
- /// Win32 says, "Thursday, October 17, 2002 8:45:38 AM PST". Note:
- /// Pacific STANDARD Time. Even though October 17 of that year
- /// occurred during Pacific Daylight Time, Win32 displays the time as
- /// standard time because that's what time it is NOW.
- /// </para>
- /// <para>
- /// .NET BCL assumes that the current DST rules were in place at the
- /// time in question. So, .NET says, "Well, if the rules in effect
- /// now were also in effect on October 17, 2003, then that would be
- /// daylight time" so it displays "Thursday, October 17, 2003, 9:45
- /// AM PDT" - daylight time.
- /// </para>
- /// <para>
- /// So .NET gives a value which is more intuitively correct, but is
- /// also potentially incorrect, and which is not invertible. Win32
- /// gives a value which is intuitively incorrect, but is strictly
- /// correct.
- /// </para>
- /// <para>
- /// Because of this funkiness, this library adds one hour to the LastModified time
- /// on the extracted file, if necessary. That is to say, if the time in question
- /// had occurred in what the .NET Base Class Library assumed to be DST (an
- /// assumption that may be wrong given the constantly changing DST rules).
- /// </para>
- /// </remarks>
- ///
- public DateTime LastModified
- {
- get { return _LastModified; }
- set
- {
- _LastModified = value;
- //SetLastModDateTimeWithAdjustment(this);
- _metadataChanged = true;
- }
- }
-
- #if LEGACY
- /// <summary>
- /// When this is set, this class trims the volume (eg C:\) from any
- /// fully-qualified pathname on the ZipEntry, before writing the ZipEntry into
- /// the ZipFile. This flag affects only zip creation. By default, this flag is TRUE,
- /// which means volume names will not be included in the filenames on entries in
- /// the archive. Your best bet is to just leave this alone.
- /// </summary>
- internal bool TrimVolumeFromFullyQualifiedPaths
- {
- get { return _TrimVolumeFromFullyQualifiedPaths; }
- set { _TrimVolumeFromFullyQualifiedPaths = value; }
- }
- #endif
-
- /// <summary>
- /// When this is set, the entry is not compressed when written to
- /// the archive. For example, the application might want to set flag to <c>true</c>
- /// this when zipping up JPG or MP3 files, which are already compressed.
- /// </summary>
- /// <seealso cref="Ionic.Zip.ZipFile.ForceNoCompression"/>
- ///
- public bool ForceNoCompression
- {
- get { return _ForceNoCompression; }
- set
- {
- if (value == _ForceNoCompression) return; // nothing to do.
-
- // If the source is a zip archive and there was encryption on the
- // entry, changing the compression method is not supported.
- if (this._Source == EntrySource.Zipfile && _sourceIsEncrypted)
- throw new InvalidOperationException("Cannot change compression method on encrypted entries read from archives.");
-
- _ForceNoCompression = value;
- }
- }
-
-
- /// <summary>
- /// The name of the filesystem file, referred to by the ZipEntry.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// This may be different than the path used in the archive itself. What I mean is,
- /// if you call <c>Zip.AddFile("fooo.txt", AlternativeDirectory)</c>, then the
- /// path used for the ZipEntry within the zip archive will be different than this path.
- /// This path is used to locate the thing-to-be-zipped on disk.
- /// </para>
- /// <para>
- /// If the entry is being added from a stream, then this is null (Nothing in VB).
- /// </para>
- ///
- /// </remarks>
- /// <seealso cref="FileName"/>
- public string LocalFileName
- {
- get { return _LocalFileName; }
- }
-
- /// <summary>
- /// The name of the file contained in the ZipEntry.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// When writing a zip, this path has backslashes replaced with
- /// forward slashes, according to the zip spec, for compatibility
- /// with Unix(tm) and ... get this.... Amiga!
- /// </para>
- ///
- /// <para>
- /// This is the name of the entry in the ZipFile itself. This name may be different
- /// than the name of the filesystem file used to create the entry (LocalFileName). In fact, there
- /// may be no filesystem file at all, if the entry is created from a stream or a string.
- /// </para>
- ///
- /// <para>
- /// When setting this property, the value is made permanent only after a call to one of the ZipFile.Save() methods
- /// on the ZipFile that contains the ZipEntry. By reading in a ZipFile, then explicitly setting the FileName on an
- /// entry contained within the ZipFile, and then calling Save(), you will effectively rename the entry within
- /// the zip archive.
- /// </para>
- /// </remarks>
- /// <seealso cref="LocalFileName"/>
- public string FileName
- {
- get { return _FileNameInArchive; }
- set
- {
- // rename the entry!
- if (value == null || value == "") throw new ZipException("The FileName must be non empty and non-null.");
-
- var filename = ZipEntry.NameInArchive(value, null);
- _FileNameInArchive = value;
- if (this._zipfile != null) this._zipfile.NotifyEntryChanged();
- _metadataChanged = true;
- }
- }
-
- /// <summary>
- /// The version of the zip engine needed to read the ZipEntry.
- /// </summary>
- /// <remarks>
- /// This is usually 0x14.
- /// (Decimal 20). If ZIP64 is in use, the version will be decimal 45.
- /// </remarks>
- public Int16 VersionNeeded
- {
- get { return _VersionNeeded; }
- }
-
- /// <summary>
- /// The comment attached to the ZipEntry.
- /// </summary>
- ///
- /// <remarks>
- /// By default, the Comment is encoded in IBM437 code page. You can specify
- /// an alternative with <see cref="ProvisionalAlternateEncoding"/>
- /// </remarks>
- /// <seealso cref="ProvisionalAlternateEncoding">ProvisionalAlternateEncoding</seealso>
- public string Comment
- {
- get { return _Comment; }
- set
- {
- _Comment = value;
- _metadataChanged = true;
- }
- }
-
-
- /// <summary>
- /// Indicates whether the entry requires ZIP64 extensions.
- /// </summary>
- /// <remarks>
- /// <para>
- /// This property is null (Nothing in VB) until a Save() method on the containing
- /// <see cref="ZipFile"/> instance has been called. The property is non-null (HasValue is true)
- /// only after a Save() method has been called.
- /// </para>
- /// <para>
- /// After the containing ZipFile has been saved. the Value of this property is true if any of the following
- /// three conditions holds: the uncompressed size of the entry is larger than 0xFFFFFFFF;
- /// the compressed size of the entry is larger than 0xFFFFFFFF; the relative offset of the entry within the
- /// zip archive is larger than 0xFFFFFFFF. These quantities are not known until a Save() is attempted on the
- /// zip archive and the compression is applied.
- /// </para>
- /// <para>If none of the three conditions holds, then the Value is false.</para>
- /// <para>
- /// A value of false does not indicate that the entry, as saved in the zip archive, does not use
- /// ZIP64. It merely indicates that ZIP64 is not required. An entry may use ZIP64 even when not
- /// required if the <see cref="ZipFile.UseZip64WhenSaving"/> property on the containing ZipFile
- /// instance is set to <see cref="Zip64Option.Always"/>, or if
- /// the <see cref="ZipFile.UseZip64WhenSaving"/> property on the containing ZipFile
- /// instance is set to <see cref="Zip64Option.AsNecessary"/> and the output stream was not seekable.
- /// </para>
- /// </remarks>
- /// <seealso cref="OutputUsedZip64"/>
- public Nullable<bool> RequiresZip64
- {
- get
- {
- return _entryRequiresZip64;
- }
- }
-
- /// <summary>
- /// Indicates whether the entry actually used ZIP64 extensions, as it was most recently written
- /// to the output file or stream.
- /// </summary>
- /// <remarks>
- /// <para>
- /// This Nullable property is null (Nothing in VB) until a Save() method on the containing
- /// <see cref="ZipFile"/> instance has been called. HasValue is true only after a Save() method
- /// has been called.
- /// </para>
- /// <para>
- /// The value of this property for a particular ZipEntry may change over successive calls to
- /// Save() methods on the containing ZipFile, even if the file that corresponds to the ZipEntry does
- /// not. This may happen if other entries contained in the ZipFile expand, causing the offset
- /// for this particular entry to exceed 0xFFFFFFFF.
- /// </para>
- /// </remarks>
- /// <seealso cref="RequiresZip64"/>
- public Nullable<bool> OutputUsedZip64
- {
- get { return _OutputUsesZip64; }
- }
-
-
- /// <summary>
- /// The bitfield as defined in the zip spec. You probably never need to look at this.
- /// </summary>
- ///
- /// <remarks>
- /// <code>
- /// bit 0 - set if encryption is used.
- /// b. 1-2 - set to determine whether normal, max, fast deflation.
- /// This library always leaves these bits unset when writing (indicating
- /// "normal" deflation").
- ///
- /// bit 3 - indicates crc32, compressed and uncompressed sizes are zero in
- /// local header. We always leave this as zero on writing, but can read
- /// a zip with it nonzero.
- ///
- /// bit 4 - reserved for "enhanced deflating". This library doesn't do enhanced deflating.
- /// bit 5 - set to indicate the zip is compressed patched data. This library doesn't do that.
- /// bit 6 - set if strong encryption is used (must also set bit 1 if bit 6 is set)
- /// bit 7 - unused
- /// bit 8 - unused
- /// bit 9 - unused
- /// bit 10 - unused
- /// Bit 11 - Language encoding flag (EFS). If this bit is set,
- /// the filename and comment fields for this file
- /// must be encoded using UTF-8. This library currently does not support UTF-8.
- /// Bit 12 - Reserved by PKWARE for enhanced compression.
- /// Bit 13 - Used when encrypting the Central Directory to indicate
- /// selected data values in the Local Header are masked to
- /// hide their actual values. See the section describing
- /// the Strong Encryption Specification for details.
- /// Bit 14 - Reserved by PKWARE.
- /// Bit 15 - Reserved by PKWARE.
- /// </code>
- /// </remarks>
-
- public Int16 BitField
- {
- get { return _BitField; }
- }
-
- /// <summary>
- /// The compression method employed for this ZipEntry.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// The ZIP specification allows a variety of compression methods. This library
- /// supports just two: 0x08 = Deflate. 0x00 = Store (no compression).
- /// </para>
- ///
- /// <para>
- /// When reading an entry from an existing zipfile, the value you retrieve here
- /// indicates the compression method used on the entry by the original creator of the zip.
- /// When writing a zipfile, you can specify either 0x08 (Deflate) or 0x00 (None). If you
- /// try setting something else, you will get an exception.
- /// </para>
- ///
- /// <para>
- /// You may wish to set CompressionMethod to 0 (None) when zipping previously compressed
- /// data like a jpg, png, or mp3 file. This can save time and cpu cycles.
- /// Setting CompressionMethod to 0 is equivalent to setting ForceNoCompression to true.
- /// </para>
- ///
- /// <para>
- /// When updating a ZipFile, you may not modify the CompressionMethod on an entry that has been encrypted.
- /// In other words, if you read an existing ZipFile with one of the ZipFile.Read() methods, and then
- /// change the CompressionMethod on an entry that has Encryption not equal to None, you will receive an exception.
- /// There is no way to modify the compression on an encrypted entry, without extracting it and re-adding it
- /// into the ZipFile.
- /// </para>
- /// </remarks>
- ///
- /// <example>
- /// In this example, the first entry added to the zip archive uses
- /// the default behavior - compression is used where it makes sense.
- /// The second entry, the MP3 file, is added to the archive without being compressed.
- /// <code>
- /// using (ZipFile zip = new ZipFile(ZipFileToCreate))
- /// {
- /// ZipEntry e1= zip.AddFile(@"c:\temp\Readme.txt");
- /// ZipEntry e2= zip.AddFile(@"c:\temp\StopThisTrain.mp3");
- /// e2.CompressionMethod = 0;
- /// zip.Save();
- /// }
- /// </code>
- ///
- /// <code lang="VB">
- /// Using zip as new ZipFile(ZipFileToCreate)
- /// zip.AddFile("c:\temp\Readme.txt")
- /// Dim e2 as ZipEntry = zip.AddFile("c:\temp\StopThisTrain.mp3")
- /// e2.CompressionMethod = 0
- /// zip.Save
- /// End Using
- /// </code>
- /// </example>
- public Int16 CompressionMethod
- {
- get { return _CompressionMethod; }
- set
- {
- if (value == _CompressionMethod) return; // nothing to do.
-
- if (value != 0x00 && value != 0x08)
- throw new InvalidOperationException("Unsupported compression method. Specify 8 or 0.");
-
- // If the source is a zip archive and there was encryption on the
- // entry, changing the compression method is not supported.
- if (this._Source == EntrySource.Zipfile && _sourceIsEncrypted)
- throw new InvalidOperationException("Cannot change compression method on encrypted entries read from archives.");
-
- _CompressionMethod = value;
-
- _ForceNoCompression = (_CompressionMethod == 0x0);
-
- _restreamRequiredOnSave = true;
- }
- }
-
-
- /// <summary>
- /// The compressed size of the file, in bytes, within the zip archive.
- /// </summary>
- /// <remarks>
- /// The compressed size is computed during compression. This means that it is only
- /// valid to read this AFTER reading in an existing zip file, or AFTER saving a
- /// zipfile you are creating.
- /// </remarks>
- public Int64 CompressedSize
- {
- get { return _CompressedSize; }
- }
-
- /// <summary>
- /// The size of the file, in bytes, before compression, or after extraction.
- /// </summary>
- /// <remarks>
- /// This property is valid AFTER reading in an existing zip file, or AFTER saving the
- /// ZipFile that contains the ZipEntry.
- /// </remarks>
- public Int64 UncompressedSize
- {
- get { return _UncompressedSize; }
- }
-
- /// <summary>
- /// The ratio of compressed size to uncompressed size of the ZipEntry.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// This is a ratio of the compressed size to the uncompressed size of the entry,
- /// expressed as a double in the range of 0 to 100+. A value of 100 indicates no
- /// compression at all. It could be higher than 100 when the compression algorithm
- /// actually inflates the data.
- /// </para>
- ///
- /// <para>
- /// You could format it for presentation to a user via a format string of "{3,5:F0}%"
- /// to see it as a percentage.
- /// </para>
- ///
- /// <para>
- /// If the size of the original uncompressed file is 0, (indicating a denominator of 0)
- /// the return value will be zero.
- /// </para>
- ///
- /// <para>
- /// This property is valid AFTER reading in an existing zip file, or AFTER saving the
- /// ZipFile that contains the ZipEntry.
- /// </para>
- ///
- /// </remarks>
- public Double CompressionRatio
- {
- get
- {
- if (UncompressedSize == 0) return 0;
- return 100 * (1.0 - (1.0 * CompressedSize) / (1.0 * UncompressedSize));
- }
- }
-
- /// <summary>
- /// The CRC (Cyclic Redundancy Check) on the contents of the ZipEntry.
- /// </summary>
- ///
- /// <remarks>
- /// You probably don't need to concern yourself with this. The CRC is generated according
- /// to the algorithm described in the Pkzip specification. It is a read-only property;
- /// when creating a Zip archive, the CRC for each entry is set only after a call to
- /// Save() on the containing ZipFile.
- /// </remarks>
- public Int32 Crc32
- {
- get { return _Crc32; }
- }
-
- /// <summary>
- /// True if the entry is a directory (not a file).
- /// This is a readonly property on the entry.
- /// </summary>
- public bool IsDirectory
- {
- get { return _IsDirectory; }
- }
-
- /// <summary>
- /// A derived property that is <c>true</c> if the entry uses encryption.
- /// </summary>
- /// <remarks>
- /// This is a readonly property on the entry.
- /// Upon reading an entry, this bool is determined by
- /// the data read. When writing an entry, this bool is
- /// determined by whether the Encryption property is set to something other than
- /// EncryptionAlgorithm.None.
- /// </remarks>
- public bool UsesEncryption
- {
- get { return (Encryption != EncryptionAlgorithm.None); }
- }
-
- /// <summary>
- /// Set this to specify which encryption algorithm to use for the entry.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// When setting this property, you must also set a Password on the entry. The set of
- /// algorithms supported is determined by the authors of this library. The PKZIP
- /// specification from PKWare defines a set of encryption algorithms, and the data formats
- /// for the zip archive that support them. Other vendors of tools and libraries, such as
- /// WinZip or Xceed, also specify and support different encryption algorithms and data
- /// formats.
- /// </para>
- ///
- /// <para>
- /// There is no common, ubiquitous multi-vendor standard for strong encryption. There is
- /// broad support for "traditional" Zip encryption, sometimes called Zip 2.0 encryption,
- /// as specified by PKWare, but this encryption is considered weak. This library currently
- /// supports AES 128 and 256 in addition to the Zip 2.0 "weak" encryption.
- /// </para>
- ///
- /// <para>
- /// The WinZip AES encryption algorithms are not supported on the .NET Compact Framework.
- /// </para>
- /// </remarks>
- public EncryptionAlgorithm Encryption
- {
- get
- {
- return _Encryption;
- }
- set
- {
- if (value == _Encryption) return;
- _Encryption = value;
-
- // If the source is a zip archive and there was encryption
- // on the entry, this will not work.
- if (this._Source == EntrySource.Zipfile && _sourceIsEncrypted)
- throw new InvalidOperationException("You cannot change the encryption method on encrypted entries read from archives.");
-
- _restreamRequiredOnSave = true;
-
- #if AESCRYPTO
- if (value == EncryptionAlgorithm.WinZipAes256) this._KeyStrengthInBits = 256;
- else if (value == EncryptionAlgorithm.WinZipAes128) this._KeyStrengthInBits = 128;
- #endif
- }
- }
-
-
- /// <summary>
- /// The Password to be used when encrypting a ZipEntry upon ZipFile.Save(), or
- /// when decrypting an entry upon Extract().
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// This is a write-only property on the entry.
- /// Set this to request that the entry be encrypted when writing the zip
- /// archive, or set it to specify the password to be used when extracting an
- /// existing entry that is encrypted.
- /// </para>
- ///
- /// <para>
- /// The password set here is implicitly
- /// used to encrypt the entry during the Save() operation, or to decrypt during
- /// the <see cref="Extract()"/> or <see cref="OpenReader()"/> operation.
- /// </para>
- ///
- /// <para>
- /// Some comments on Updating archives: Suppose you read a zipfile, and there is an
- /// encrypted entry. Setting the password on that entry and then saving the zipfile
- /// does not update the password on that entry in the archive. Instead, what happens
- /// is the existing entry is copied through to the new zip archive, in its original
- /// encrypted form. Upon re-reading that archive, the entry can be decrypted with its
- /// original password.
- /// </para>
- ///
- /// <para>
- /// If you read a zipfile, you cannot modify the password on any encrypted entry,
- /// except by extracting the entry with the first password (if any), removing the original
- /// entry via <see cref="ZipFile.RemoveEntry(ZipEntry)"/>, and then adding
- /// a new entry with a new password.
- /// </para>
- ///
- /// <para>
- /// If you read a zipfile, and there is an un-encrypted entry, you can set the password
- /// on the entry and then call Save() on the ZipFile, and get encryption on that entry.
- /// </para>
- ///
- /// </remarks>
- public string Password
- {
- set
- {
- _Password = value;
- if (_Password == null)
- {
- _Encryption = EncryptionAlgorithm.None;
- }
- else
- {
- // We're setting a non-null password.
-
- // For entries obtained from a zip file that are encrypted, we cannot
- // simply restream (recompress, re-encrypt) the file data, because we
- // need the old password in order to decrypt the data, and then we
- // need the new password to encrypt. So, setting the password is
- // never going to work on an entry that is stored encrypted in a zipfile.
-
- // But it is not en error to set the password, obviously: callers will
- // set the password in order to Extract encrypted archives.
-
- // If the source is a zip archive and there was previously no encryption
- // on the entry, then we must re-stream the entry in order to encrypt it.
- if (this._Source == EntrySource.Zipfile && !_sourceIsEncrypted)
- _restreamRequiredOnSave = true;
-
- if (Encryption == EncryptionAlgorithm.None)
- {
- _Encryption = EncryptionAlgorithm.PkzipWeak;
- }
- }
-
- }
- }
-
-
-
- /// <summary>
- /// Specifies that the extraction should overwrite any existing files.
- /// </summary>
- /// <remarks>
- /// This applies only when calling an Extract method. By default this
- /// property is false. Generally you will get overwrite behavior by calling
- /// one of the overloads of the Extract() method that accepts a boolean flag
- /// to indicate explicitly whether you want overwrite.
- /// </remarks>
- /// <seealso cref="Ionic.Zip.ZipEntry.Extract(bool)"/>
- public bool OverwriteOnExtract
- {
- get { return _OverwriteOnExtract; }
- set { _OverwriteOnExtract = value; }
- }
-
-
- /// <summary>
- /// A callback that allows the application to specify whether multiple reads of the
- /// stream should be performed, in the case that a compression operation actually
- /// inflates the size of the file data.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// In some cases, applying the Deflate compression algorithm in DeflateStream can
- /// result an increase in the size of the data. This "inflation" can happen with
- /// previously compressed files, such as a zip, jpg, png, mp3, and so on. In a few
- /// tests, inflation on zip files can be as large as 60%! Inflation can also happen
- /// with very small files. In these cases, by default, the DotNetZip library
- /// discards the compressed bytes, and stores the uncompressed file data into the
- /// zip archive. This is an optimization where smaller size is preferred over
- /// longer run times.
- /// </para>
- ///
- /// <para>
- /// The application can specify that compression is not even tried, by setting the
- /// ForceNoCompression flag. In this case, the compress-and-check-sizes process as
- /// decribed above, is not done.
- /// </para>
- ///
- /// <para>
- /// In some cases, neither choice is optimal. The application wants compression,
- /// but in some cases also wants to avoid reading the stream more than once. This
- /// may happen when the stream is very large, or when the read is very expensive, or
- /// when the difference between the compressed and uncompressed sizes is not
- /// significant.
- /// </para>
- ///
- /// <para>
- /// To satisfy these applications, this delegate allows the DotNetZip library to ask
- /// the application to for approval for re-reading the stream. As with other
- /// properties (like Password and ForceNoCompression), setting the corresponding
- /// delegate on the ZipFile class itself will set it on all ZipEntry items that are
- /// subsequently added to the ZipFile instance.
- /// </para>
- ///
- /// </remarks>
- /// <seealso cref="Ionic.Zip.ZipFile.WillReadTwiceOnInflation"/>
- /// <seealso cref="Ionic.Zip.ReReadApprovalCallback"/>
- public ReReadApprovalCallback WillReadTwiceOnInflation
- {
- get;
- set;
- }
-
-
- /// <summary>
- /// A callback that allows the application to specify whether compression should
- /// be used for a given entry that is about to be added to the zip archive.
- /// </summary>
- ///
- /// <remarks>
- /// See <see cref="ZipFile.WantCompression" />
- /// </remarks>
- public WantCompressionCallback WantCompression
- {
- get;
- set;
- }
-
-
-
- /// <summary>
- /// Set to indicate whether to use UTF-8 encoding on filenames and
- /// comments, according to the PKWare specification.
- /// </summary>
- /// <remarks>
- /// If this flag is set, the entry will be marked as encoded with UTF-8,
- /// according to the PWare spec, if necessary. Necessary means, if the filename or
- /// entry comment (if any) cannot be reflexively encoded with the default (IBM437) code page.
- /// </remarks>
- /// <remarks>
- /// Setting this flag to true is equivalent to setting <see cref="ProvisionalAlternateEncoding"/> to <c>System.Text.Encoding.UTF8</c>
- /// </remarks>
- public bool UseUnicodeAsNecessary
- {
- get
- {
- return _provisionalAlternateEncoding == System.Text.Encoding.GetEncoding("UTF-8");
- }
- set
- {
- _provisionalAlternateEncoding = (value) ? System.Text.Encoding.GetEncoding("UTF-8") : Ionic.Zip.ZipFile.DefaultEncoding;
- }
- }
-
- /// <summary>
- /// The text encoding to use for this ZipEntry, when the default
- /// encoding is insufficient.
- /// </summary>
- ///
- /// <remarks>
- /// <para>
- /// According to the zip specification from PKWare, filenames and comments for a
- /// ZipEntry are encoded either with IBM437 or with UTF8. But, some archivers do not
- /// follow the specification, and instead encode characters using the system default
- /// code page, or an arbitrary code page. For example, WinRAR when run on a machine in
- /// Shanghai may encode filenames with the Chinese (Big-5) code page. This behavior is
- /// contrary to the Zip specification, but it occurs anyway. This property exists to
- /// support that non-compliant behavior when reading or writing zip files.
- /// </para>
- /// <para>
- /// When writing zip archives that will be read by one of these other archivers, use this property to
- /// specify the code page to use when encoding filenames and comments into the zip
- /// file, when the IBM437 code page will not suffice.
- /// </para>
- /// <para>
- /// Be aware that a zip file created after you've explicitly specified the code page will not
- /// be compliant to the PKWare specification, and may not be readable by compliant archivers.
- /// On the other hand, many archivers are non-compliant and can read zip files created in
- /// arbitrary code pages.
- /// </para>
- /// <para>
- /// When using an arbitrary, non-UTF8 code page for encoding, there is no standard way for the
- /// creator (DotNetZip) to specify in the zip file which code page has been used. DotNetZip is not
- /// able to inspect the zip file and determine the codepage used for the entries within it. Therefore,
- /// you, the application author, must determine that. If you use a codepage which results in filenames
- /// that are not legal in Windows, you will get exceptions upon extract. Caveat Emptor.
- /// </para>
- /// </remarks>
- public System.Text.Encoding ProvisionalAlternateEncoding
- {
- get
- {
- return _provisionalAlternateEncoding;
- }
- set
- {
- _provisionalAlternateEncoding = value;
- }
- }
-
- /// <summary>
- /// The text encoding actually used for this ZipEntry.
- /// </summary>
- public System.Text.Encoding ActualEncoding
- {
- get
- {
- return _actualEncoding;
- }
- }
-
-
-
- private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
- {
- int bytesRead = 0;
-
- ze._RelativeOffsetOfLocalHeader = (int)ze.ArchiveStream.Position;
-
- int signature = Ionic.Zip.SharedUtilities.ReadSignature(ze.ArchiveStream);
- bytesRead += 4;
-
- // Return false if this is not a local file header signature.
- if (ZipEntry.IsNotValidSig(signature))
- {
- // Getting "not a ZipEntry signature" is not always wrong or an error.
- // This will happen after the last entry in a zipfile. In that case, we
- // expect to read :
- // a ZipDirEntry signature (if a non-empty zip file) or
- // a ZipConstants.EndOfCentralDirectorySignature.
- //
- // Anything else is a surprise.
-
- ze.ArchiveStream.Seek(-4, System.IO.SeekOrigin.Current); // unread the signature
- if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
- {
- throw new BadReadException(String.Format(" ZipEntry::ReadHeader(): Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, ze.ArchiveStream.Position));
- }
- return false;
- }
-
- byte[] block = new byte[26];
- int n = ze.ArchiveStream.Read(block, 0, block.Length);
- if (n != block.Length) return false;
- bytesRead += n;
-
- int i = 0;
- ze._VersionNeeded = (short)(block[i++] + block[i++] * 256);
- ze._BitField = (short)(block[i++] + block[i++] * 256);
- ze._CompressionMethod = (short)(block[i++] + block[i++] * 256);
- ze._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
- // transform the time data into something usable (a DateTime)
- ze._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);
-
- // NB: if ((ze._BitField & 0x0008) != 0x0008), then the Compressed, uncompressed and
- // CRC values are not true values; the true values will follow the entry data.
- // Nevertheless, regardless of the statis of bit 3 in the bitfield, the slots for
- // the three amigos may contain marker values for ZIP64. So we must read them.
- {
- ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
- ze._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
- ze._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
-
- // validate ZIP64? No. We don't need to be pedantic about it.
- //if (((uint)ze._CompressedSize == 0xFFFFFFFF &&
- // (uint)ze._UncompressedSize != 0xFFFFFFFF) ||
- // ((uint)ze._CompressedSize != 0xFFFFFFFF &&
- // (uint)ze._UncompressedSize == 0xFFFFFFFF))
- // throw new BadReadException(String.Format(" ZipEntry::Read(): Inconsistent uncompressed size (0x{0:X8}) for zip64, at position 0x{1:X16}", ze._UncompressedSize, ze.ArchiveStream.Position));
-
- if ((uint)ze._CompressedSize == 0xFFFFFFFF ||
- (uint)ze._UncompressedSize == 0xFFFFFFFF)
-
- ze._InputUsesZip64 = true;
-
-
- //throw new BadReadException(" DotNetZip does not currently support reading the ZIP64 format.");
- }
- // else
- // {
- // // The CRC, compressed size, and uncompressed size stored here are not valid.
- // // The actual values are stored later in the stream.
- // // Here, we advance the pointer to skip the dummy data.
- // i += 12;
- // }
-
- Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
- Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
-
- block = new byte[filenameLength];
- n = ze.ArchiveStream.Read(block, 0, block.Length);
- bytesRead += n;
-
- // if the UTF8 bit is set for this entry, we override the encoding the application requested.
- ze._actualEncoding = ((ze._BitField & 0x0800) == 0x0800)
- ? System.Text.Encoding.UTF8
- : defaultEncoding;
-
- // need to use this form of GetString() for .NET CF
- ze._FileNameInArchive = ze._actualEncoding.GetString(block, 0, block.Length);
-
- // when creating an entry by reading, the LocalFileName is the same as the FileNameInArchive
- ze._LocalFileName = ze._FileNameInArchive;
-
- // workitem 6898
- if (ze._LocalFileName.EndsWith("/")) ze.MarkAsDirectory();
-
- bytesRead += ze.ProcessExtraField(extraFieldLength);
-
- ze._LengthOfTrailer = 0;
-
- // workitem 6607 - don't read for directories
- // actually get the compressed size and CRC if necessary
- if (!ze._LocalFileName.EndsWith("/") && (ze._BitField & 0x0008) == 0x0008)
- {
- // This descriptor exists only if bit 3 of the general
- // purpose bit flag is set (see below). It is byte aligned
- // and immediately follows the last byte of compressed data.
- // This descriptor is used only when it was not possible to
- // seek in the output .ZIP file, e.g., when the output .ZIP file
- // was standard output or a non-seekable device. For ZIP64(tm) format
- // archives, the compressed and uncompressed sizes are 8 bytes each.
-
- long posn = ze.ArchiveStream.Position;
-
- // Here, we're going to loop until we find a ZipEntryDataDescriptorSignature and
- // a consistent data record after that. To be consistent, the data record must
- // indicate the length of the entry data.
- bool wantMore = true;
- long SizeOfDataRead = 0;
- int tries = 0;
- while (wantMore)
- {
- tries++;
- // We call the FindSignature shared routine to find the specified signature
- // in the already-opened zip archive, starting from the current cursor
- // position in that filestream. There are two possibilities: either we
- // find the signature or we don't. If we cannot find it, then the routine
- // returns -1, and the ReadHeader() method returns false, indicating we
- // cannot read a legal entry header. If we have found it, then the
- // FindSignature() method returns the number of bytes in the stream we had
- // to seek forward, to find the sig. We need this to determine if the zip
- // entry is valid, later.
-
- ze._zipfile.OnReadBytes(ze);
-
- long d = Ionic.Zip.SharedUtilities.FindSignature(ze.ArchiveStream, ZipConstants.ZipEntryDataDescriptorSignature);
- if (d == -1) return false;
-
- // total size of data read (through all loops of this).
- SizeOfDataRead += d;
-
- if (ze._InputUsesZip64 == true)
- {
- // read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size)
- block = new byte[20];
- n = ze.ArchiveStream.Read(…