/// <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;
int signature = Crisis.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
Crisis.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 = Crisis.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 = Crisis.Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
}
else
{
zde._FileNameInArchive = Crisis.Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
}
// workitem 10330
// insure unique entry names
while (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 = Crisis.Ionic.Zip.SharedUtilities.Utf8StringFromBuffer(block);
}
else
{
zde._Comment = Crisis.Ionic.Zip.SharedUtilities.StringFromBuffer(block, expectedEncoding);
}
}
//zde._LengthOfDirEntry = bytesRead;
return(zde);
}