123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- // ZipDirEntry.cs
- // ------------------------------------------------------------------
- //
- // Copyright (c) 2006-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-July-11 12:03:03>
- //
- // ------------------------------------------------------------------
- //
- // This module defines members of the ZipEntry class for reading the
- // Zip file central directory.
- //
- // Created: Tue, 27 Mar 2007 15:30
- //
- // ------------------------------------------------------------------
- using System;
- using System.Collections.Generic;
- namespace Ionic.Zip
- {
- partial class ZipEntry
- {
- /// <summary>
- /// True if the referenced entry is a directory.
- /// </summary>
- internal bool AttributesIndicateDirectory
- {
- get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); }
- }
- internal void ResetDirEntry()
- {
- // __FileDataPosition is the position of the file data for an entry.
- // It is _RelativeOffsetOfLocalHeader + size of local header.
- // We cannot know the __FileDataPosition until we read the local
- // header.
- // The local header is not necessarily the same length as the record
- // in the central directory.
- // Set to -1, to indicate we need to read this later.
- this.__FileDataPosition = -1;
- // set _LengthOfHeader to 0, to indicate we need to read later.
- this._LengthOfHeader = 0;
- // reset the copy counter because we've got a good entry now
- CopyHelper.Reset();
- }
- /// <summary>
- /// Provides a human-readable string with information about the ZipEntry.
- /// </summary>
- public string Info
- {
- get
- {
- var builder = new System.Text.StringBuilder();
- builder
- .Append(string.Format(" ZipEntry: {0}\n", this.FileName))
- .Append(string.Format(" Version Made By: {0}\n", this._VersionMadeBy))
- .Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded));
- if (this._IsDirectory)
- builder.Append(" Entry type: directory\n");
- else
- {
- builder.Append(string.Format(" File type: {0}\n", this._IsText? "text":"binary"))
- .Append(string.Format(" Compression: {0}\n", this.CompressionMethod))
- .Append(string.Format(" Compressed: 0x{0:X}\n", this.CompressedSize))
- .Append(string.Format(" Uncompressed: 0x{0:X}\n", this.UncompressedSize))
- .Append(string.Format(" CRC32: 0x{0:X8}\n", this._Crc32));
- }
- builder.Append(string.Format(" Disk Number: {0}\n", this._diskNumber));
- if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF)
- builder
- .Append(string.Format(" Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader));
- else
- builder
- .Append(string.Format(" Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader));
- builder
- .Append(string.Format(" Bit Field: 0x{0:X4}\n", this._BitField))
- .Append(string.Format(" Encrypted?: {0}\n", this._sourceIsEncrypted))
- .Append(string.Format(" Timeblob: 0x{0:X8}\n", this._TimeBlob))
- .Append(string.Format(" Time: {0}\n", Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob)));
- builder.Append(string.Format(" Is Zip64?: {0}\n", this._InputUsesZip64));
- if (!string.IsNullOrEmpty(this._Comment))
- {
- builder.Append(string.Format(" Comment: {0}\n", this._Comment));
- }
- builder.Append("\n");
- return builder.ToString();
- }
- }
- // workitem 10330
- private class CopyHelper
- {
- private static System.Text.RegularExpressions.Regex re =
- new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$");
- private static int callCount = 0;
- internal static void Reset()
- {
- callCount = 0;
- }
- internal static string AppendCopyToFileName(string f)
- {
- callCount++;
- if (callCount > 25)
- throw new OverflowException("overflow while creating filename");
- int n = 1;
- int r = f.LastIndexOf(".");
- if (r == -1)
- {
- // there is no extension
- System.Text.RegularExpressions.Match m = re.Match(f);
- if (m.Success)
- {
- n = Int32.Parse(m.Groups[1].Value) + 1;
- string copy = String.Format(" (copy {0})", n);
- f = f.Substring(0, m.Index) + copy;
- }
- else
- {
- string copy = String.Format(" (copy {0})", n);
- f = f + copy;
- }
- }
- else
- {
- //System.Console.WriteLine("HasExtension");
- System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r));
- if (m.Success)
- {
- n = Int32.Parse(m.Groups[1].Value) + 1;
- string copy = String.Format(" (copy {0})", n);
- f = f.Substring(0, m.Index) + copy + f.Substring(r);
- }
- else
- {
- string copy = String.Format(" (copy {0})", n);
- f = f.Substring(0, r) + copy + f.Substring(r);
- }
- //System.Console.WriteLine("returning f({0})", f);
- }
- return f;
- }
- }
- /// <summary>
- /// Reads one entry from the zip directory structure in the zip file.
- /// </summary>
- ///
- /// <param name="zf">
- /// The zipfile for which a directory entry will be read. From this param, the
- /// method gets the ReadStream and the expected text encoding
- /// (ProvisionalAlternateEncoding) which is used if the entry is not marked
- /// UTF-8.
- /// </param>
- ///
- /// <param name="previouslySeen">
- /// a list of previously seen entry names; used to prevent duplicates.
- /// </param>
- ///
- /// <returns>the entry read from the archive.</returns>
- internal static ZipEntry ReadDirEntry(ZipFile zf,
- Dictionary<String,Object> previouslySeen)
- {
- System.IO.Stream s = zf.ReadStream;
- System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always)
- ? zf.AlternateEncoding
- : ZipFile.DefaultEncoding;
- while (true)
- {
- int signature = Ionic.Zip.SharedUtilities.ReadSignature(s);
- // return null if this is not a local file header signature
- if (IsNotValidZipDirEntrySig(signature))
- {
- s.Seek(-4, System.IO.SeekOrigin.Current);
- // workitem 10178
- Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
- // Getting "not a ZipDirEntry signature" here is not always wrong or an
- // error. This can happen when walking through a zipfile. After the
- // last ZipDirEntry, we expect to read an
- // EndOfCentralDirectorySignature. When we get this is how we know
- // we've reached the end of the central directory.
- if (signature != ZipConstants.EndOfCentralDirectorySignature &&
- signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature &&
- signature != ZipConstants.ZipEntrySignature // workitem 8299
- )
- {
- throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position));
- }
- return null;
- }
- int bytesRead = 42 + 4;
- byte[] block = new byte[42];
- int n = s.Read(block, 0, block.Length);
- if (n != block.Length) return null;
- int i = 0;
- ZipEntry zde = new ZipEntry();
- zde.AlternateEncoding = expectedEncoding;
- zde._Source = ZipEntrySource.ZipFile;
- zde._container = new ZipContainer(zf);
- unchecked
- {
- zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256);
- zde._VersionNeeded = (short)(block[i++] + block[i++] * 256);
- zde._BitField = (short)(block[i++] + block[i++] * 256);
- zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
- zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
- zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob);
- zde._timestamp |= ZipEntryTimestamp.DOS;
- zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
- zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
- zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
- }
- // preserve
- zde._CompressionMethod_FromZipFile = zde._CompressionMethod;
- zde._filenameLength = (short)(block[i++] + block[i++] * 256);
- zde._extraFieldLength = (short)(block[i++] + block[i++] * 256);
- zde._commentLength = (short)(block[i++] + block[i++] * 256);
- zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256);
- zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256);
- zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
- zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
- // workitem 7801
- zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01);
- block = new byte[zde._filenameLength];
- n = s.Read(block, 0, block.Length);
- bytesRead += n;
- if ((zde._BitField & 0x0800) == 0x0800)
- {
- // UTF-8 is in use
- zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
- }
- else
- {
- zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
- }
- // workitem 10330
- // insure unique entry names
- while (!zf.IgnoreDuplicateFiles && previouslySeen.ContainsKey(zde._FileNameInArchive))
- {
- zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive);
- zde._metadataChanged = true;
- }
- if (zde.AttributesIndicateDirectory)
- zde.MarkAsDirectory(); // may append a slash to filename if nec.
- // workitem 6898
- else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory();
- zde._CompressedFileDataSize = zde._CompressedSize;
- if ((zde._BitField & 0x01) == 0x01)
- {
- // this may change after processing the Extra field
- zde._Encryption_FromZipFile = zde._Encryption =
- EncryptionAlgorithm.PkzipWeak;
- zde._sourceIsEncrypted = true;
- }
- if (zde._extraFieldLength > 0)
- {
- zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF ||
- zde._UncompressedSize == 0xFFFFFFFF ||
- zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF);
- // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64);
- bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength);
- zde._CompressedFileDataSize = zde._CompressedSize;
- }
- // we've processed the extra field, so we know the encryption method is set now.
- if (zde._Encryption == EncryptionAlgorithm.PkzipWeak)
- {
- // the "encryption header" of 12 bytes precedes the file data
- zde._CompressedFileDataSize -= 12;
- }
- #if AESCRYPTO
- else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 ||
- zde.Encryption == EncryptionAlgorithm.WinZipAes256)
- {
- zde._CompressedFileDataSize = zde.CompressedSize -
- (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10);
- zde._LengthOfTrailer = 10;
- }
- #endif
- // tally the trailing descriptor
- if ((zde._BitField & 0x0008) == 0x0008)
- {
- // sig, CRC, Comp and Uncomp sizes
- if (zde._InputUsesZip64)
- zde._LengthOfTrailer += 24;
- else
- zde._LengthOfTrailer += 16;
- }
- // workitem 12744
- zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800)
- ? System.Text.Encoding.UTF8
- :expectedEncoding;
- zde.AlternateEncodingUsage = ZipOption.Always;
- if (zde._commentLength > 0)
- {
- block = new byte[zde._commentLength];
- n = s.Read(block, 0, block.Length);
- bytesRead += n;
- if ((zde._BitField & 0x0800) == 0x0800)
- {
- // UTF-8 is in use
- zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
- }
- else
- {
- zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
- }
- }
- //zde._LengthOfDirEntry = bytesRead;
- if (zf.IgnoreDuplicateFiles && previouslySeen.ContainsKey(zde._FileNameInArchive))
- {
- continue;
- }
- return zde;
- }
- }
- /// <summary>
- /// Returns true if the passed-in value is a valid signature for a ZipDirEntry.
- /// </summary>
- /// <param name="signature">the candidate 4-byte signature value.</param>
- /// <returns>true, if the signature is valid according to the PKWare spec.</returns>
- internal static bool IsNotValidZipDirEntrySig(int signature)
- {
- return (signature != ZipConstants.ZipDirEntrySignature);
- }
- private Int16 _VersionMadeBy;
- private Int16 _InternalFileAttrs;
- private Int32 _ExternalFileAttrs;
- //private Int32 _LengthOfDirEntry;
- private Int16 _filenameLength;
- private Int16 _extraFieldLength;
- private Int16 _commentLength;
- }
- }
|