[12074] | 1 | // ZipDirEntry.cs
|
---|
| 2 | // ------------------------------------------------------------------
|
---|
| 3 | //
|
---|
| 4 | // Copyright (c) 2006-2011 Dino Chiesa .
|
---|
| 5 | // All rights reserved.
|
---|
| 6 | //
|
---|
| 7 | // This code module is part of DotNetZip, a zipfile class library.
|
---|
| 8 | //
|
---|
| 9 | // ------------------------------------------------------------------
|
---|
| 10 | //
|
---|
| 11 | // This code is licensed under the Microsoft Public License.
|
---|
| 12 | // See the file License.txt for the license details.
|
---|
| 13 | // More info on: http://dotnetzip.codeplex.com
|
---|
| 14 | //
|
---|
| 15 | // ------------------------------------------------------------------
|
---|
| 16 | //
|
---|
| 17 | // last saved (in emacs):
|
---|
| 18 | // Time-stamp: <2011-July-11 12:03:03>
|
---|
| 19 | //
|
---|
| 20 | // ------------------------------------------------------------------
|
---|
| 21 | //
|
---|
| 22 | // This module defines members of the ZipEntry class for reading the
|
---|
| 23 | // Zip file central directory.
|
---|
| 24 | //
|
---|
| 25 | // Created: Tue, 27 Mar 2007 15:30
|
---|
| 26 | //
|
---|
| 27 | // ------------------------------------------------------------------
|
---|
| 28 |
|
---|
| 29 |
|
---|
| 30 | using System;
|
---|
| 31 | using System.Collections.Generic;
|
---|
| 32 |
|
---|
| 33 | namespace OfficeOpenXml.Packaging.Ionic.Zip
|
---|
| 34 | {
|
---|
| 35 |
|
---|
| 36 | partial class ZipEntry
|
---|
| 37 | {
|
---|
| 38 | /// <summary>
|
---|
| 39 | /// True if the referenced entry is a directory.
|
---|
| 40 | /// </summary>
|
---|
| 41 | internal bool AttributesIndicateDirectory
|
---|
| 42 | {
|
---|
| 43 | get { return ((_InternalFileAttrs == 0) && ((_ExternalFileAttrs & 0x0010) == 0x0010)); }
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 |
|
---|
| 47 | internal void ResetDirEntry()
|
---|
| 48 | {
|
---|
| 49 | // __FileDataPosition is the position of the file data for an entry.
|
---|
| 50 | // It is _RelativeOffsetOfLocalHeader + size of local header.
|
---|
| 51 |
|
---|
| 52 | // We cannot know the __FileDataPosition until we read the local
|
---|
| 53 | // header.
|
---|
| 54 |
|
---|
| 55 | // The local header is not necessarily the same length as the record
|
---|
| 56 | // in the central directory.
|
---|
| 57 |
|
---|
| 58 | // Set to -1, to indicate we need to read this later.
|
---|
| 59 | this.__FileDataPosition = -1;
|
---|
| 60 |
|
---|
| 61 | // set _LengthOfHeader to 0, to indicate we need to read later.
|
---|
| 62 | this._LengthOfHeader = 0;
|
---|
| 63 | }
|
---|
| 64 |
|
---|
| 65 | /// <summary>
|
---|
| 66 | /// Provides a human-readable string with information about the ZipEntry.
|
---|
| 67 | /// </summary>
|
---|
| 68 | public string Info
|
---|
| 69 | {
|
---|
| 70 | get
|
---|
| 71 | {
|
---|
| 72 | var builder = new System.Text.StringBuilder();
|
---|
| 73 | builder
|
---|
| 74 | .Append(string.Format(" ZipEntry: {0}\n", this.FileName))
|
---|
| 75 | .Append(string.Format(" Version Made By: {0}\n", this._VersionMadeBy))
|
---|
| 76 | .Append(string.Format(" Needed to extract: {0}\n", this.VersionNeeded));
|
---|
| 77 |
|
---|
| 78 | if (this._IsDirectory)
|
---|
| 79 | builder.Append(" Entry type: directory\n");
|
---|
| 80 | else
|
---|
| 81 | {
|
---|
| 82 | builder.Append(string.Format(" File type: {0}\n", this._IsText? "text":"binary"))
|
---|
| 83 | .Append(string.Format(" Compression: {0}\n", this.CompressionMethod))
|
---|
| 84 | .Append(string.Format(" Compressed: 0x{0:X}\n", this.CompressedSize))
|
---|
| 85 | .Append(string.Format(" Uncompressed: 0x{0:X}\n", this.UncompressedSize))
|
---|
| 86 | .Append(string.Format(" CRC32: 0x{0:X8}\n", this._Crc32));
|
---|
| 87 | }
|
---|
| 88 | builder.Append(string.Format(" Disk Number: {0}\n", this._diskNumber));
|
---|
| 89 | if (this._RelativeOffsetOfLocalHeader > 0xFFFFFFFF)
|
---|
| 90 | builder
|
---|
| 91 | .Append(string.Format(" Relative Offset: 0x{0:X16}\n", this._RelativeOffsetOfLocalHeader));
|
---|
| 92 | else
|
---|
| 93 | builder
|
---|
| 94 | .Append(string.Format(" Relative Offset: 0x{0:X8}\n", this._RelativeOffsetOfLocalHeader));
|
---|
| 95 |
|
---|
| 96 | builder
|
---|
| 97 | .Append(string.Format(" Bit Field: 0x{0:X4}\n", this._BitField))
|
---|
| 98 | .Append(string.Format(" Encrypted?: {0}\n", this._sourceIsEncrypted))
|
---|
| 99 | .Append(string.Format(" Timeblob: 0x{0:X8}\n", this._TimeBlob))
|
---|
| 100 | .Append(string.Format(" Time: {0}\n", Ionic.Zip.SharedUtilities.PackedToDateTime(this._TimeBlob)));
|
---|
| 101 |
|
---|
| 102 | builder.Append(string.Format(" Is Zip64?: {0}\n", this._InputUsesZip64));
|
---|
| 103 | if (!string.IsNullOrEmpty(this._Comment))
|
---|
| 104 | {
|
---|
| 105 | builder.Append(string.Format(" Comment: {0}\n", this._Comment));
|
---|
| 106 | }
|
---|
| 107 | builder.Append("\n");
|
---|
| 108 | return builder.ToString();
|
---|
| 109 | }
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 |
|
---|
| 113 | // workitem 10330
|
---|
| 114 | private class CopyHelper
|
---|
| 115 | {
|
---|
| 116 | private static System.Text.RegularExpressions.Regex re =
|
---|
| 117 | new System.Text.RegularExpressions.Regex(" \\(copy (\\d+)\\)$");
|
---|
| 118 |
|
---|
| 119 | private static int callCount = 0;
|
---|
| 120 |
|
---|
| 121 | internal static string AppendCopyToFileName(string f)
|
---|
| 122 | {
|
---|
| 123 | callCount++;
|
---|
| 124 | if (callCount > 25)
|
---|
| 125 | throw new OverflowException("overflow while creating filename");
|
---|
| 126 |
|
---|
| 127 | int n = 1;
|
---|
| 128 | int r = f.LastIndexOf(".");
|
---|
| 129 |
|
---|
| 130 | if (r == -1)
|
---|
| 131 | {
|
---|
| 132 | // there is no extension
|
---|
| 133 | System.Text.RegularExpressions.Match m = re.Match(f);
|
---|
| 134 | if (m.Success)
|
---|
| 135 | {
|
---|
| 136 | n = Int32.Parse(m.Groups[1].Value) + 1;
|
---|
| 137 | string copy = String.Format(" (copy {0})", n);
|
---|
| 138 | f = f.Substring(0, m.Index) + copy;
|
---|
| 139 | }
|
---|
| 140 | else
|
---|
| 141 | {
|
---|
| 142 | string copy = String.Format(" (copy {0})", n);
|
---|
| 143 | f = f + copy;
|
---|
| 144 | }
|
---|
| 145 | }
|
---|
| 146 | else
|
---|
| 147 | {
|
---|
| 148 | //System.Console.WriteLine("HasExtension");
|
---|
| 149 | System.Text.RegularExpressions.Match m = re.Match(f.Substring(0, r));
|
---|
| 150 | if (m.Success)
|
---|
| 151 | {
|
---|
| 152 | n = Int32.Parse(m.Groups[1].Value) + 1;
|
---|
| 153 | string copy = String.Format(" (copy {0})", n);
|
---|
| 154 | f = f.Substring(0, m.Index) + copy + f.Substring(r);
|
---|
| 155 | }
|
---|
| 156 | else
|
---|
| 157 | {
|
---|
| 158 | string copy = String.Format(" (copy {0})", n);
|
---|
| 159 | f = f.Substring(0, r) + copy + f.Substring(r);
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | //System.Console.WriteLine("returning f({0})", f);
|
---|
| 163 | }
|
---|
| 164 | return f;
|
---|
| 165 | }
|
---|
| 166 | }
|
---|
| 167 |
|
---|
| 168 |
|
---|
| 169 |
|
---|
| 170 | /// <summary>
|
---|
| 171 | /// Reads one entry from the zip directory structure in the zip file.
|
---|
| 172 | /// </summary>
|
---|
| 173 | ///
|
---|
| 174 | /// <param name="zf">
|
---|
| 175 | /// The zipfile for which a directory entry will be read. From this param, the
|
---|
| 176 | /// method gets the ReadStream and the expected text encoding
|
---|
| 177 | /// (ProvisionalAlternateEncoding) which is used if the entry is not marked
|
---|
| 178 | /// UTF-8.
|
---|
| 179 | /// </param>
|
---|
| 180 | ///
|
---|
| 181 | /// <param name="previouslySeen">
|
---|
| 182 | /// a list of previously seen entry names; used to prevent duplicates.
|
---|
| 183 | /// </param>
|
---|
| 184 | ///
|
---|
| 185 | /// <returns>the entry read from the archive.</returns>
|
---|
| 186 | internal static ZipEntry ReadDirEntry(ZipFile zf,
|
---|
| 187 | Dictionary<String,Object> previouslySeen)
|
---|
| 188 | {
|
---|
| 189 | System.IO.Stream s = zf.ReadStream;
|
---|
| 190 | System.Text.Encoding expectedEncoding = (zf.AlternateEncodingUsage == ZipOption.Always)
|
---|
| 191 | ? zf.AlternateEncoding
|
---|
| 192 | : ZipFile.DefaultEncoding;
|
---|
| 193 |
|
---|
| 194 | int signature = Ionic.Zip.SharedUtilities.ReadSignature(s);
|
---|
| 195 | // return null if this is not a local file header signature
|
---|
| 196 | if (IsNotValidZipDirEntrySig(signature))
|
---|
| 197 | {
|
---|
| 198 | s.Seek(-4, System.IO.SeekOrigin.Current);
|
---|
| 199 | // workitem 10178
|
---|
| 200 | Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
---|
| 201 |
|
---|
| 202 | // Getting "not a ZipDirEntry signature" here is not always wrong or an
|
---|
| 203 | // error. This can happen when walking through a zipfile. After the
|
---|
| 204 | // last ZipDirEntry, we expect to read an
|
---|
| 205 | // EndOfCentralDirectorySignature. When we get this is how we know
|
---|
| 206 | // we've reached the end of the central directory.
|
---|
| 207 | if (signature != ZipConstants.EndOfCentralDirectorySignature &&
|
---|
| 208 | signature != ZipConstants.Zip64EndOfCentralDirectoryRecordSignature &&
|
---|
| 209 | signature != ZipConstants.ZipEntrySignature // workitem 8299
|
---|
| 210 | )
|
---|
| 211 | {
|
---|
| 212 | throw new BadReadException(String.Format(" Bad signature (0x{0:X8}) at position 0x{1:X8}", signature, s.Position));
|
---|
| 213 | }
|
---|
| 214 | return null;
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | int bytesRead = 42 + 4;
|
---|
| 218 | byte[] block = new byte[42];
|
---|
| 219 | int n = s.Read(block, 0, block.Length);
|
---|
| 220 | if (n != block.Length) return null;
|
---|
| 221 |
|
---|
| 222 | int i = 0;
|
---|
| 223 | ZipEntry zde = new ZipEntry();
|
---|
| 224 | zde.AlternateEncoding = expectedEncoding;
|
---|
| 225 | zde._Source = ZipEntrySource.ZipFile;
|
---|
| 226 | zde._container = new ZipContainer(zf);
|
---|
| 227 |
|
---|
| 228 | unchecked
|
---|
| 229 | {
|
---|
| 230 | zde._VersionMadeBy = (short)(block[i++] + block[i++] * 256);
|
---|
| 231 | zde._VersionNeeded = (short)(block[i++] + block[i++] * 256);
|
---|
| 232 | zde._BitField = (short)(block[i++] + block[i++] * 256);
|
---|
| 233 | zde._CompressionMethod = (Int16)(block[i++] + block[i++] * 256);
|
---|
| 234 | zde._TimeBlob = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
---|
| 235 | zde._LastModified = Ionic.Zip.SharedUtilities.PackedToDateTime(zde._TimeBlob);
|
---|
| 236 | zde._timestamp |= ZipEntryTimestamp.DOS;
|
---|
| 237 |
|
---|
| 238 | zde._Crc32 = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
---|
| 239 | zde._CompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
---|
| 240 | zde._UncompressedSize = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
---|
| 241 | }
|
---|
| 242 |
|
---|
| 243 | // preserve
|
---|
| 244 | zde._CompressionMethod_FromZipFile = zde._CompressionMethod;
|
---|
| 245 |
|
---|
| 246 | zde._filenameLength = (short)(block[i++] + block[i++] * 256);
|
---|
| 247 | zde._extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
---|
| 248 | zde._commentLength = (short)(block[i++] + block[i++] * 256);
|
---|
| 249 | zde._diskNumber = (UInt32)(block[i++] + block[i++] * 256);
|
---|
| 250 |
|
---|
| 251 | zde._InternalFileAttrs = (short)(block[i++] + block[i++] * 256);
|
---|
| 252 | zde._ExternalFileAttrs = block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256;
|
---|
| 253 |
|
---|
| 254 | zde._RelativeOffsetOfLocalHeader = (uint)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
---|
| 255 |
|
---|
| 256 | // workitem 7801
|
---|
| 257 | zde.IsText = ((zde._InternalFileAttrs & 0x01) == 0x01);
|
---|
| 258 |
|
---|
| 259 | block = new byte[zde._filenameLength];
|
---|
| 260 | n = s.Read(block, 0, block.Length);
|
---|
| 261 | bytesRead += n;
|
---|
| 262 | if ((zde._BitField & 0x0800) == 0x0800)
|
---|
| 263 | {
|
---|
| 264 | // UTF-8 is in use
|
---|
| 265 | zde._FileNameInArchive = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
|
---|
| 266 | }
|
---|
| 267 | else
|
---|
| 268 | {
|
---|
| 269 | zde._FileNameInArchive = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
|
---|
| 270 | }
|
---|
| 271 |
|
---|
| 272 | // workitem 10330
|
---|
| 273 | // insure unique entry names
|
---|
| 274 | while (previouslySeen.ContainsKey(zde._FileNameInArchive))
|
---|
| 275 | {
|
---|
| 276 | zde._FileNameInArchive = CopyHelper.AppendCopyToFileName(zde._FileNameInArchive);
|
---|
| 277 | zde._metadataChanged = true;
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | if (zde.AttributesIndicateDirectory)
|
---|
| 281 | zde.MarkAsDirectory(); // may append a slash to filename if nec.
|
---|
| 282 | // workitem 6898
|
---|
| 283 | else if (zde._FileNameInArchive.EndsWith("/")) zde.MarkAsDirectory();
|
---|
| 284 |
|
---|
| 285 | zde._CompressedFileDataSize = zde._CompressedSize;
|
---|
| 286 | if ((zde._BitField & 0x01) == 0x01)
|
---|
| 287 | {
|
---|
| 288 | // this may change after processing the Extra field
|
---|
| 289 | zde._Encryption_FromZipFile = zde._Encryption =
|
---|
| 290 | EncryptionAlgorithm.PkzipWeak;
|
---|
| 291 | zde._sourceIsEncrypted = true;
|
---|
| 292 | }
|
---|
| 293 |
|
---|
| 294 | if (zde._extraFieldLength > 0)
|
---|
| 295 | {
|
---|
| 296 | zde._InputUsesZip64 = (zde._CompressedSize == 0xFFFFFFFF ||
|
---|
| 297 | zde._UncompressedSize == 0xFFFFFFFF ||
|
---|
| 298 | zde._RelativeOffsetOfLocalHeader == 0xFFFFFFFF);
|
---|
| 299 |
|
---|
| 300 | // Console.WriteLine(" Input uses Z64?: {0}", zde._InputUsesZip64);
|
---|
| 301 |
|
---|
| 302 | bytesRead += zde.ProcessExtraField(s, zde._extraFieldLength);
|
---|
| 303 | zde._CompressedFileDataSize = zde._CompressedSize;
|
---|
| 304 | }
|
---|
| 305 |
|
---|
| 306 | // we've processed the extra field, so we know the encryption method is set now.
|
---|
| 307 | if (zde._Encryption == EncryptionAlgorithm.PkzipWeak)
|
---|
| 308 | {
|
---|
| 309 | // the "encryption header" of 12 bytes precedes the file data
|
---|
| 310 | zde._CompressedFileDataSize -= 12;
|
---|
| 311 | }
|
---|
| 312 | #if AESCRYPTO
|
---|
| 313 | else if (zde.Encryption == EncryptionAlgorithm.WinZipAes128 ||
|
---|
| 314 | zde.Encryption == EncryptionAlgorithm.WinZipAes256)
|
---|
| 315 | {
|
---|
| 316 | zde._CompressedFileDataSize = zde.CompressedSize -
|
---|
| 317 | (ZipEntry.GetLengthOfCryptoHeaderBytes(zde.Encryption) + 10);
|
---|
| 318 | zde._LengthOfTrailer = 10;
|
---|
| 319 | }
|
---|
| 320 | #endif
|
---|
| 321 |
|
---|
| 322 | // tally the trailing descriptor
|
---|
| 323 | if ((zde._BitField & 0x0008) == 0x0008)
|
---|
| 324 | {
|
---|
| 325 | // sig, CRC, Comp and Uncomp sizes
|
---|
| 326 | if (zde._InputUsesZip64)
|
---|
| 327 | zde._LengthOfTrailer += 24;
|
---|
| 328 | else
|
---|
| 329 | zde._LengthOfTrailer += 16;
|
---|
| 330 | }
|
---|
| 331 |
|
---|
| 332 | // workitem 12744
|
---|
| 333 | zde.AlternateEncoding = ((zde._BitField & 0x0800) == 0x0800)
|
---|
| 334 | ? System.Text.Encoding.UTF8
|
---|
| 335 | :expectedEncoding;
|
---|
| 336 |
|
---|
| 337 | zde.AlternateEncodingUsage = ZipOption.Always;
|
---|
| 338 |
|
---|
| 339 | if (zde._commentLength > 0)
|
---|
| 340 | {
|
---|
| 341 | block = new byte[zde._commentLength];
|
---|
| 342 | n = s.Read(block, 0, block.Length);
|
---|
| 343 | bytesRead += n;
|
---|
| 344 | if ((zde._BitField & 0x0800) == 0x0800)
|
---|
| 345 | {
|
---|
| 346 | // UTF-8 is in use
|
---|
| 347 | zde._Comment = Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
|
---|
| 348 | }
|
---|
| 349 | else
|
---|
| 350 | {
|
---|
| 351 | zde._Comment = Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
|
---|
| 352 | }
|
---|
| 353 | }
|
---|
| 354 | //zde._LengthOfDirEntry = bytesRead;
|
---|
| 355 | return zde;
|
---|
| 356 | }
|
---|
| 357 |
|
---|
| 358 |
|
---|
| 359 | /// <summary>
|
---|
| 360 | /// Returns true if the passed-in value is a valid signature for a ZipDirEntry.
|
---|
| 361 | /// </summary>
|
---|
| 362 | /// <param name="signature">the candidate 4-byte signature value.</param>
|
---|
| 363 | /// <returns>true, if the signature is valid according to the PKWare spec.</returns>
|
---|
| 364 | internal static bool IsNotValidZipDirEntrySig(int signature)
|
---|
| 365 | {
|
---|
| 366 | return (signature != ZipConstants.ZipDirEntrySignature);
|
---|
| 367 | }
|
---|
| 368 |
|
---|
| 369 |
|
---|
| 370 | private Int16 _VersionMadeBy;
|
---|
| 371 | private Int16 _InternalFileAttrs;
|
---|
| 372 | private Int32 _ExternalFileAttrs;
|
---|
| 373 |
|
---|
| 374 | //private Int32 _LengthOfDirEntry;
|
---|
| 375 | private Int16 _filenameLength;
|
---|
| 376 | private Int16 _extraFieldLength;
|
---|
| 377 | private Int16 _commentLength;
|
---|
| 378 | }
|
---|
| 379 |
|
---|
| 380 |
|
---|
| 381 | }
|
---|