// ZipEntry.Read.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-July-09 21:31:28>
|
//
|
// ------------------------------------------------------------------
|
//
|
// This module defines logic for Reading the ZipEntry from a
|
// zip file.
|
//
|
// ------------------------------------------------------------------
|
|
|
using System;
|
using System.IO;
|
|
namespace HH.WMS.Utils.Ionic.Zip
|
{
|
public partial class ZipEntry
|
{
|
private int _readExtraDepth;
|
private void ReadExtraField()
|
{
|
_readExtraDepth++;
|
// workitem 8098: ok (restore)
|
long posn = this.ArchiveStream.Position;
|
this.ArchiveStream.Seek(this._RelativeOffsetOfLocalHeader, SeekOrigin.Begin);
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
|
byte[] block = new byte[30];
|
this.ArchiveStream.Read(block, 0, block.Length);
|
int i = 26;
|
Int16 filenameLength = (short)(block[i++] + block[i++] * 256);
|
Int16 extraFieldLength = (short)(block[i++] + block[i++] * 256);
|
|
// workitem 8098: ok (relative)
|
this.ArchiveStream.Seek(filenameLength, SeekOrigin.Current);
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
|
ProcessExtraField(this.ArchiveStream, extraFieldLength);
|
|
// workitem 8098: ok (restore)
|
this.ArchiveStream.Seek(posn, SeekOrigin.Begin);
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(this.ArchiveStream);
|
_readExtraDepth--;
|
}
|
|
|
private static bool ReadHeader(ZipEntry ze, System.Text.Encoding defaultEncoding)
|
{
|
int bytesRead = 0;
|
|
// change for workitem 8098
|
ze._RelativeOffsetOfLocalHeader = ze.ArchiveStream.Position;
|
|
int signature = HH.WMS.Utils.Ionic.Zip.SharedUtilities.ReadEntrySignature(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, SeekOrigin.Current); // unread the signature
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
if (ZipEntry.IsNotValidZipDirEntrySig(signature) && (signature != ZipConstants.EndOfCentralDirectorySignature))
|
{
|
throw new BadReadException(String.Format(" 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 = (Int16)(block[i++] + block[i++] * 256);
|
ze._BitField = (Int16)(block[i++] + block[i++] * 256);
|
ze._CompressionMethod_FromZipFile = ze._CompressionMethod = (Int16)(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 = HH.WMS.Utils.Ionic.Zip.SharedUtilities.PackedToDateTime(ze._TimeBlob);
|
ze._timestamp |= ZipEntryTimestamp.DOS;
|
|
if ((ze._BitField & 0x01) == 0x01)
|
{
|
ze._Encryption_FromZipFile = ze._Encryption = EncryptionAlgorithm.PkzipWeak; // this *may* change after processing the Extra field
|
ze._sourceIsEncrypted = true;
|
}
|
|
// 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.
|
// But, regardless of the status 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);
|
|
if ((uint)ze._CompressedSize == 0xFFFFFFFF ||
|
(uint)ze._UncompressedSize == 0xFFFFFFFF)
|
|
ze._InputUsesZip64 = true;
|
}
|
|
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, override the
|
// encoding the application requested.
|
|
if ((ze._BitField & 0x0800) == 0x0800)
|
{
|
// workitem 12744
|
ze.AlternateEncoding = System.Text.Encoding.UTF8;
|
ze.AlternateEncodingUsage = ZipOption.Always;
|
}
|
|
// need to use this form of GetString() for .NET CF
|
ze._FileNameInArchive = ze.AlternateEncoding.GetString(block, 0, block.Length);
|
|
// workitem 6898
|
if (ze._FileNameInArchive.EndsWith("/")) ze.MarkAsDirectory();
|
|
bytesRead += ze.ProcessExtraField(ze.ArchiveStream, extraFieldLength);
|
|
ze._LengthOfTrailer = 0;
|
|
// workitem 6607 - don't read for directories
|
// actually get the compressed size and CRC if necessary
|
if (!ze._FileNameInArchive.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,
|
// as well as any encryption trailer, as with AES.
|
// 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.
|
|
// workitem 8098: ok (restore)
|
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. If we cannot find the signature, 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.
|
|
if (ze._container.ZipFile != null)
|
ze._container.ZipFile.OnReadBytes(ze);
|
|
long d = HH.WMS.Utils.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)
|
{
|
// read 1x 4-byte (CRC) and 2x 8-bytes (Compressed Size, Uncompressed Size)
|
block = new byte[20];
|
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
if (n != 20) return false;
|
|
// do not increment bytesRead - it is for entry header only.
|
// the data we have just read is a footer (falls after the file data)
|
//bytesRead += n;
|
|
i = 0;
|
ze._Crc32 = (Int32)(block[i++] + block[i++] * 256 + block[i++] * 256 * 256 + block[i++] * 256 * 256 * 256);
|
ze._CompressedSize = BitConverter.ToInt64(block, i);
|
i += 8;
|
ze._UncompressedSize = BitConverter.ToInt64(block, i);
|
i += 8;
|
|
ze._LengthOfTrailer += 24; // bytes including sig, CRC, Comp and Uncomp sizes
|
}
|
else
|
{
|
// read 3x 4-byte fields (CRC, Compressed Size, Uncompressed Size)
|
block = new byte[12];
|
n = ze.ArchiveStream.Read(block, 0, block.Length);
|
if (n != 12) return false;
|
|
// do not increment bytesRead - it is for entry header only.
|
// the data we have just read is a footer (falls after the file data)
|
//bytesRead += n;
|
|
i = 0;
|
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);
|
|
ze._LengthOfTrailer += 16; // bytes including sig, CRC, Comp and Uncomp sizes
|
|
}
|
|
wantMore = (SizeOfDataRead != ze._CompressedSize);
|
|
if (wantMore)
|
{
|
// Seek back to un-read the last 12 bytes - maybe THEY contain
|
// the ZipEntryDataDescriptorSignature.
|
// (12 bytes for the CRC, Comp and Uncomp size.)
|
ze.ArchiveStream.Seek(-12, SeekOrigin.Current);
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
|
// Adjust the size to account for the false signature read in
|
// FindSignature().
|
SizeOfDataRead += 4;
|
}
|
}
|
|
// seek back to previous position, to prepare to read file data
|
// workitem 8098: ok (restore)
|
ze.ArchiveStream.Seek(posn, SeekOrigin.Begin);
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(ze.ArchiveStream);
|
}
|
|
ze._CompressedFileDataSize = ze._CompressedSize;
|
|
|
// bit 0 set indicates that some kind of encryption is in use
|
if ((ze._BitField & 0x01) == 0x01)
|
{
|
#if AESCRYPTO
|
if (ze.Encryption == EncryptionAlgorithm.WinZipAes128 ||
|
ze.Encryption == EncryptionAlgorithm.WinZipAes256)
|
{
|
int bits = ZipEntry.GetKeyStrengthInBits(ze._Encryption_FromZipFile);
|
// read in the WinZip AES metadata: salt + PV. 18 bytes for AES256. 10 bytes for AES128.
|
ze._aesCrypto_forExtract = WinZipAesCrypto.ReadFromStream(null, bits, ze.ArchiveStream);
|
bytesRead += ze._aesCrypto_forExtract.SizeOfEncryptionMetadata - 10; // MAC (follows crypto bytes)
|
// according to WinZip, the CompressedSize includes the AES Crypto framing data.
|
ze._CompressedFileDataSize -= ze._aesCrypto_forExtract.SizeOfEncryptionMetadata;
|
ze._LengthOfTrailer += 10; // MAC
|
}
|
else
|
#endif
|
{
|
// read in the header data for "weak" encryption
|
ze._WeakEncryptionHeader = new byte[12];
|
bytesRead += ZipEntry.ReadWeakEncryptionHeader(ze._archiveStream, ze._WeakEncryptionHeader);
|
// decrease the filedata size by 12 bytes
|
ze._CompressedFileDataSize -= 12;
|
}
|
}
|
|
// Remember the size of the blob for this entry.
|
// We also have the starting position in the stream for this entry.
|
ze._LengthOfHeader = bytesRead;
|
ze._TotalEntrySize = ze._LengthOfHeader + ze._CompressedFileDataSize + ze._LengthOfTrailer;
|
|
|
// We've read in the regular entry header, the extra field, and any
|
// encryption header. The pointer in the file is now at the start of the
|
// filedata, which is potentially compressed and encrypted. Just ahead in
|
// the file, there are _CompressedFileDataSize bytes of data, followed by
|
// potentially a non-zero length trailer, consisting of optionally, some
|
// encryption stuff (10 byte MAC for AES), and the bit-3 trailer (16 or 24
|
// bytes).
|
|
return true;
|
}
|
|
|
|
internal static int ReadWeakEncryptionHeader(Stream s, byte[] buffer)
|
{
|
// PKZIP encrypts the compressed data stream. Encrypted files must
|
// be decrypted before they can be extracted.
|
|
// Each PKZIP-encrypted file has an extra 12 bytes stored at the start of the data
|
// area defining the encryption header for that file. The encryption header is
|
// originally set to random values, and then itself encrypted, using three, 32-bit
|
// keys. The key values are initialized using the supplied encryption password.
|
// After each byte is encrypted, the keys are then updated using pseudo-random
|
// number generation techniques in combination with the same CRC-32 algorithm used
|
// in PKZIP and implemented in the CRC32.cs module in this project.
|
|
// read the 12-byte encryption header
|
int additionalBytesRead = s.Read(buffer, 0, 12);
|
if (additionalBytesRead != 12)
|
throw new ZipException(String.Format("Unexpected end of data at position 0x{0:X8}", s.Position));
|
|
return additionalBytesRead;
|
}
|
|
|
|
private static bool IsNotValidSig(int signature)
|
{
|
return (signature != ZipConstants.ZipEntrySignature);
|
}
|
|
|
/// <summary>
|
/// Reads one <c>ZipEntry</c> from the given stream. The content for
|
/// the entry does not get decompressed or decrypted. This method
|
/// basically reads metadata, and seeks.
|
/// </summary>
|
/// <param name="zc">the ZipContainer this entry belongs to.</param>
|
/// <param name="first">
|
/// true of this is the first entry being read from the stream.
|
/// </param>
|
/// <returns>the <c>ZipEntry</c> read from the stream.</returns>
|
internal static ZipEntry ReadEntry(ZipContainer zc, bool first)
|
{
|
ZipFile zf = zc.ZipFile;
|
Stream s = zc.ReadStream;
|
System.Text.Encoding defaultEncoding = zc.AlternateEncoding;
|
ZipEntry entry = new ZipEntry();
|
entry._Source = ZipEntrySource.ZipFile;
|
entry._container = zc;
|
entry._archiveStream = s;
|
if (zf != null)
|
zf.OnReadEntry(true, null);
|
|
if (first) HandlePK00Prefix(s);
|
|
// Read entry header, including any encryption header
|
if (!ReadHeader(entry, defaultEncoding)) return null;
|
|
// Store the position in the stream for this entry
|
// change for workitem 8098
|
entry.__FileDataPosition = entry.ArchiveStream.Position;
|
|
// seek past the data without reading it. We will read on Extract()
|
s.Seek(entry._CompressedFileDataSize + entry._LengthOfTrailer, SeekOrigin.Current);
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
|
// ReadHeader moves the file pointer to the end of the entry header,
|
// as well as any encryption header.
|
|
// CompressedFileDataSize includes:
|
// the maybe compressed, maybe encrypted file data
|
// the encryption trailer, if any
|
// the bit 3 descriptor, if any
|
|
// workitem 5306
|
// http://www.codeplex.com/DotNetZip/WorkItem/View.aspx?WorkItemId=5306
|
HandleUnexpectedDataDescriptor(entry);
|
|
if (zf != null)
|
{
|
zf.OnReadBytes(entry);
|
zf.OnReadEntry(false, entry);
|
}
|
|
return entry;
|
}
|
|
|
internal static void HandlePK00Prefix(Stream s)
|
{
|
// in some cases, the zip file begins with "PK00". This is a throwback and is rare,
|
// but we handle it anyway. We do not change behavior based on it.
|
uint datum = (uint)HH.WMS.Utils.Ionic.Zip.SharedUtilities.ReadInt(s);
|
if (datum != ZipConstants.PackedToRemovableMedia)
|
{
|
s.Seek(-4, SeekOrigin.Current); // unread the block
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
}
|
}
|
|
|
|
private static void HandleUnexpectedDataDescriptor(ZipEntry entry)
|
{
|
Stream s = entry.ArchiveStream;
|
|
// In some cases, the "data descriptor" is present, without a signature, even when
|
// bit 3 of the BitField is NOT SET. This is the CRC, followed
|
// by the compressed length and the uncompressed length (4 bytes for each
|
// of those three elements). Need to check that here.
|
//
|
uint datum = (uint)HH.WMS.Utils.Ionic.Zip.SharedUtilities.ReadInt(s);
|
if (datum == entry._Crc32)
|
{
|
int sz = HH.WMS.Utils.Ionic.Zip.SharedUtilities.ReadInt(s);
|
if (sz == entry._CompressedSize)
|
{
|
sz = HH.WMS.Utils.Ionic.Zip.SharedUtilities.ReadInt(s);
|
if (sz == entry._UncompressedSize)
|
{
|
// ignore everything and discard it.
|
}
|
else
|
{
|
s.Seek(-12, SeekOrigin.Current); // unread the three blocks
|
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
}
|
}
|
else
|
{
|
s.Seek(-8, SeekOrigin.Current); // unread the two blocks
|
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
}
|
}
|
else
|
{
|
s.Seek(-4, SeekOrigin.Current); // unread the block
|
|
// workitem 10178
|
HH.WMS.Utils.Ionic.Zip.SharedUtilities.Workaround_Ladybug318918(s);
|
}
|
}
|
|
|
/// <summary>
|
/// Finds a particular segment in the given extra field.
|
/// This is used when modifying a previously-generated
|
/// extra field, in particular when removing the AES crypto
|
/// segment in the extra field.
|
/// </summary>
|
static internal int FindExtraFieldSegment(byte[] extra, int offx, UInt16 targetHeaderId)
|
{
|
int j = offx;
|
while (j + 3 < extra.Length)
|
{
|
UInt16 headerId = (UInt16)(extra[j++] + extra[j++] * 256);
|
if (headerId == targetHeaderId) return j-2;
|
|
// else advance to next segment
|
Int16 dataSize = (short)(extra[j++] + extra[j++] * 256);
|
j+= dataSize;
|
}
|
|
return -1;
|
}
|
|
|
/// <summary>
|
/// At current cursor position in the stream, read the extra
|
/// field, and set the properties on the ZipEntry instance
|
/// appropriately. This can be called when processing the
|
/// Extra field in the Central Directory, or in the local
|
/// header.
|
/// </summary>
|
internal int ProcessExtraField(Stream s, Int16 extraFieldLength)
|
{
|
int additionalBytesRead = 0;
|
if (extraFieldLength > 0)
|
{
|
byte[] buffer = this._Extra = new byte[extraFieldLength];
|
additionalBytesRead = s.Read(buffer, 0, buffer.Length);
|
long posn = s.Position - additionalBytesRead;
|
int j = 0;
|
while (j + 3 < buffer.Length)
|
{
|
int start = j;
|
UInt16 headerId = (UInt16)(buffer[j++] + buffer[j++] * 256);
|
Int16 dataSize = (short)(buffer[j++] + buffer[j++] * 256);
|
|
switch (headerId)
|
{
|
case 0x000a: // NTFS ctime, atime, mtime
|
j = ProcessExtraFieldWindowsTimes(buffer, j, dataSize, posn);
|
break;
|
|
case 0x5455: // Unix ctime, atime, mtime
|
j = ProcessExtraFieldUnixTimes(buffer, j, dataSize, posn);
|
break;
|
|
case 0x5855: // Info-zip Extra field (outdated)
|
// This is outdated, so the field is supported on
|
// read only.
|
j = ProcessExtraFieldInfoZipTimes(buffer, j, dataSize, posn);
|
break;
|
|
case 0x7855: // Unix uid/gid
|
// ignored. DotNetZip does not handle this field.
|
break;
|
|
case 0x7875: // ??
|
// ignored. I could not find documentation on this field,
|
// though it appears in some zip files.
|
break;
|
|
case 0x0001: // ZIP64
|
j = ProcessExtraFieldZip64(buffer, j, dataSize, posn);
|
break;
|
|
#if AESCRYPTO
|
case 0x9901: // WinZip AES encryption is in use. (workitem 6834)
|
// we will handle this extra field only if compressionmethod is 0x63
|
j = ProcessExtraFieldWinZipAes(buffer, j, dataSize, posn);
|
break;
|
#endif
|
case 0x0017: // workitem 7968: handle PKWare Strong encryption header
|
j = ProcessExtraFieldPkwareStrongEncryption(buffer, j);
|
break;
|
}
|
|
// move to the next Header in the extra field
|
j = start + dataSize + 4;
|
}
|
}
|
return additionalBytesRead;
|
}
|
|
private int ProcessExtraFieldPkwareStrongEncryption(byte[] Buffer, int j)
|
{
|
// Value Size Description
|
// ----- ---- -----------
|
// 0x0017 2 bytes Tag for this "extra" block type
|
// TSize 2 bytes Size of data that follows
|
// Format 2 bytes Format definition for this record
|
// AlgID 2 bytes Encryption algorithm identifier
|
// Bitlen 2 bytes Bit length of encryption key
|
// Flags 2 bytes Processing flags
|
// CertData TSize-8 Certificate decryption extra field data
|
// (refer to the explanation for CertData
|
// in the section describing the
|
// Certificate Processing Method under
|
// the Strong Encryption Specification)
|
|
j += 2;
|
_UnsupportedAlgorithmId = (UInt16)(Buffer[j++] + Buffer[j++] * 256);
|
_Encryption_FromZipFile = _Encryption = EncryptionAlgorithm.Unsupported;
|
|
// DotNetZip doesn't support this algorithm, but we don't need to throw
|
// here. we might just be reading the archive, which is fine. We'll
|
// need to throw if Extract() is called.
|
|
return j;
|
}
|
|
|
#if AESCRYPTO
|
private int ProcessExtraFieldWinZipAes(byte[] buffer, int j, Int16 dataSize, long posn)
|
{
|
if (this._CompressionMethod == 0x0063)
|
{
|
if ((this._BitField & 0x01) != 0x01)
|
throw new BadReadException(String.Format(" Inconsistent metadata at position 0x{0:X16}", posn));
|
|
this._sourceIsEncrypted = true;
|
|
//this._aesCrypto = new WinZipAesCrypto(this);
|
// see spec at http://www.winzip.com/aes_info.htm
|
if (dataSize != 7)
|
throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) in WinZip AES field at position 0x{1:X16}", dataSize, posn));
|
|
this._WinZipAesMethod = BitConverter.ToInt16(buffer, j);
|
j += 2;
|
if (this._WinZipAesMethod != 0x01 && this._WinZipAesMethod != 0x02)
|
throw new BadReadException(String.Format(" Unexpected vendor version number (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}",
|
this._WinZipAesMethod, posn));
|
|
Int16 vendorId = BitConverter.ToInt16(buffer, j);
|
j += 2;
|
if (vendorId != 0x4541)
|
throw new BadReadException(String.Format(" Unexpected vendor ID (0x{0:X4}) for WinZip AES metadata at position 0x{1:X16}", vendorId, posn));
|
|
int keystrength = (buffer[j] == 1) ? 128 : (buffer[j] == 3) ? 256 : -1;
|
if (keystrength < 0)
|
throw new BadReadException(String.Format("Invalid key strength ({0})", keystrength));
|
|
_Encryption_FromZipFile = this._Encryption = (keystrength == 128)
|
? EncryptionAlgorithm.WinZipAes128
|
: EncryptionAlgorithm.WinZipAes256;
|
|
j++;
|
|
// set the actual compression method
|
this._CompressionMethod_FromZipFile =
|
this._CompressionMethod = BitConverter.ToInt16(buffer, j);
|
j += 2; // for the next segment of the extra field
|
}
|
return j;
|
}
|
|
#endif
|
|
private delegate T Func<T>();
|
|
private int ProcessExtraFieldZip64(byte[] buffer, int j, Int16 dataSize, long posn)
|
{
|
// The PKWare spec says that any of {UncompressedSize, CompressedSize,
|
// RelativeOffset} exceeding 0xFFFFFFFF can lead to the ZIP64 header,
|
// and the ZIP64 header may contain one or more of those. If the
|
// values are present, they will be found in the prescribed order.
|
// There may also be a 4-byte "disk start number."
|
// This means that the DataSize must be 28 bytes or less.
|
|
this._InputUsesZip64 = true;
|
|
// workitem 7941: check datasize before reading.
|
if (dataSize > 28)
|
throw new BadReadException(String.Format(" Inconsistent size (0x{0:X4}) for ZIP64 extra field at position 0x{1:X16}",
|
dataSize, posn));
|
int remainingData = dataSize;
|
|
var slurp = new Func<Int64>( () => {
|
if (remainingData < 8)
|
throw new BadReadException(String.Format(" Missing data for ZIP64 extra field, position 0x{0:X16}", posn));
|
var x = BitConverter.ToInt64(buffer, j);
|
j+= 8;
|
remainingData -= 8;
|
return x;
|
});
|
|
if (this._UncompressedSize == 0xFFFFFFFF)
|
this._UncompressedSize = slurp();
|
|
if (this._CompressedSize == 0xFFFFFFFF)
|
this._CompressedSize = slurp();
|
|
if (this._RelativeOffsetOfLocalHeader == 0xFFFFFFFF)
|
this._RelativeOffsetOfLocalHeader = slurp();
|
|
// Ignore anything else. Potentially there are 4 more bytes for the
|
// disk start number. DotNetZip currently doesn't handle multi-disk
|
// archives.
|
return j;
|
}
|
|
|
private int ProcessExtraFieldInfoZipTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
{
|
if (dataSize != 12 && dataSize != 8)
|
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for InfoZip v1 extra field at position 0x{1:X16}", dataSize, posn));
|
|
Int32 timet = BitConverter.ToInt32(buffer, j);
|
this._Mtime = _unixEpoch.AddSeconds(timet);
|
j += 4;
|
|
timet = BitConverter.ToInt32(buffer, j);
|
this._Atime = _unixEpoch.AddSeconds(timet);
|
j += 4;
|
|
this._Ctime = DateTime.UtcNow;
|
|
_ntfsTimesAreSet = true;
|
_timestamp |= ZipEntryTimestamp.InfoZip1; return j;
|
}
|
|
|
|
private int ProcessExtraFieldUnixTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
{
|
// The Unix filetimes are 32-bit unsigned integers,
|
// storing seconds since Unix epoch.
|
|
if (dataSize != 13 && dataSize != 9 && dataSize != 5)
|
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for Extended Timestamp extra field at position 0x{1:X16}", dataSize, posn));
|
|
int remainingData = dataSize;
|
|
var slurp = new Func<DateTime>( () => {
|
Int32 timet = BitConverter.ToInt32(buffer, j);
|
j += 4;
|
remainingData -= 4;
|
return _unixEpoch.AddSeconds(timet);
|
});
|
|
if (dataSize == 13 || _readExtraDepth > 0)
|
{
|
byte flag = buffer[j++];
|
remainingData--;
|
|
if ((flag & 0x0001) != 0 && remainingData >= 4)
|
this._Mtime = slurp();
|
|
this._Atime = ((flag & 0x0002) != 0 && remainingData >= 4)
|
? slurp()
|
: DateTime.UtcNow;
|
|
this._Ctime = ((flag & 0x0004) != 0 && remainingData >= 4)
|
? slurp()
|
:DateTime.UtcNow;
|
|
_timestamp |= ZipEntryTimestamp.Unix;
|
_ntfsTimesAreSet = true;
|
_emitUnixTimes = true;
|
}
|
else
|
ReadExtraField(); // will recurse
|
|
return j;
|
}
|
|
|
private int ProcessExtraFieldWindowsTimes(byte[] buffer, int j, Int16 dataSize, long posn)
|
{
|
// The NTFS filetimes are 64-bit unsigned integers, stored in Intel
|
// (least significant byte first) byte order. They are expressed as the
|
// number of 1.0E-07 seconds (1/10th microseconds!) past WinNT "epoch",
|
// which is "01-Jan-1601 00:00:00 UTC".
|
//
|
// HeaderId 2 bytes 0x000a == NTFS stuff
|
// Datasize 2 bytes ?? (usually 32)
|
// reserved 4 bytes ??
|
// timetag 2 bytes 0x0001 == time
|
// size 2 bytes 24 == 8 bytes each for ctime, mtime, atime
|
// mtime 8 bytes win32 ticks since win32epoch
|
// atime 8 bytes win32 ticks since win32epoch
|
// ctime 8 bytes win32 ticks since win32epoch
|
|
if (dataSize != 32)
|
throw new BadReadException(String.Format(" Unexpected size (0x{0:X4}) for NTFS times extra field at position 0x{1:X16}", dataSize, posn));
|
|
j += 4; // reserved
|
Int16 timetag = (Int16)(buffer[j] + buffer[j + 1] * 256);
|
Int16 addlsize = (Int16)(buffer[j + 2] + buffer[j + 3] * 256);
|
j += 4; // tag and size
|
|
if (timetag == 0x0001 && addlsize == 24)
|
{
|
Int64 z = BitConverter.ToInt64(buffer, j);
|
this._Mtime = DateTime.FromFileTimeUtc(z);
|
j += 8;
|
|
// At this point the library *could* set the LastModified value
|
// to coincide with the Mtime value. In theory, they refer to
|
// the same property of the file, and should be the same anyway,
|
// allowing for differences in precision. But they are
|
// independent quantities in the zip archive, and this library
|
// will keep them separate in the object model. There is no ill
|
// effect from this, because as files are extracted, the
|
// higher-precision value (Mtime) is used if it is present.
|
// Apps may wish to compare the Mtime versus LastModified
|
// values, but any difference when both are present is not
|
// germaine to the correctness of the library. but note: when
|
// explicitly setting either value, both are set. See the setter
|
// for LastModified or the SetNtfsTimes() method.
|
|
z = BitConverter.ToInt64(buffer, j);
|
this._Atime = DateTime.FromFileTimeUtc(z);
|
j += 8;
|
|
z = BitConverter.ToInt64(buffer, j);
|
this._Ctime = DateTime.FromFileTimeUtc(z);
|
j += 8;
|
|
_ntfsTimesAreSet = true;
|
_timestamp |= ZipEntryTimestamp.Windows;
|
_emitNtfsTimes = true;
|
}
|
return j;
|
}
|
|
|
}
|
}
|