Skip to content

Commit

Permalink
Merge pull request #7201 from crocopaw/zip3
Browse files Browse the repository at this point in the history
std.zip: Replace magic constants by constants named after specs
merged-on-behalf-of: Nicholas Wilson <thewilsonator@users.noreply.github.com>
  • Loading branch information
dlang-bot committed Oct 5, 2019
2 parents 86eacb4 + e82b773 commit bfc34bc
Showing 1 changed file with 59 additions and 32 deletions.
91 changes: 59 additions & 32 deletions std/zip.d
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,27 @@ final class ZipArchive
import std.conv : to;
import std.datetime.systime : DosFileTime;

private:
// names are taken directly from the specification
// https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
static immutable ubyte[] centralFileHeaderSignature = [ 0x50, 0x4b, 0x01, 0x02 ];
static immutable ubyte[] localFileHeaderSignature = [ 0x50, 0x4b, 0x03, 0x04 ];
static immutable ubyte[] endOfCentralDirSignature = [ 0x50, 0x4b, 0x05, 0x06 ];
static immutable ubyte[] archiveExtraDataSignature = [ 0x50, 0x4b, 0x06, 0x08 ];
static immutable ubyte[] digitalSignatureSignature = [ 0x50, 0x4b, 0x05, 0x05 ];
static immutable ubyte[] zip64EndOfCentralDirSignature = [ 0x50, 0x4b, 0x06, 0x06 ];
static immutable ubyte[] zip64EndOfCentralDirLocatorSignature = [ 0x50, 0x4b, 0x06, 0x07 ];

enum centralFileHeaderLength = 46;
enum localFileHeaderLength = 30;
enum endOfCentralDirLength = 22;
enum archiveExtraDataLength = 8;
enum digitalSignatureLength = 6;
enum zip64EndOfCentralDirLength = 56;
enum zip64EndOfCentralDirLocatorLength = 20;
enum dataDescriptorLength = 12;

public:
string comment; /// Read/Write: the archive comment. Must be less than 65536 bytes in length.

private ubyte[] _data;
Expand All @@ -302,8 +323,12 @@ final class ZipArchive
private uint _totalEntries;
private bool _isZip64;
static const ushort zip64ExtractVersion = 45;

deprecated("Use digitalSignatureLength instead; will be removed in 2.098.0")
static const int digiSignLength = 6;
deprecated("Use zip64EndOfCentralDirLocatorLength instead; will be removed in 2.098.0")
static const int eocd64LocLength = 20;
deprecated("Use zip64EndOfCentralDirLength instead; will be removed in 2.098.0")
static const int eocd64Length = 56;

private Segment[] _segs;
Expand Down Expand Up @@ -430,24 +455,25 @@ final class ZipArchive
auto directory = _directory.byValue.array.sort!((x, y) => x.index < y.index).release;
foreach (ArchiveMember de; directory)
{
if (to!ulong(archiveSize) + 30 + de.name.length + de.extra.length + de.compressedSize
+ directorySize + 46 + de.name.length + de.extra.length + de.comment.length
+ 22 + comment.length + eocd64LocLength + eocd64Length > uint.max)
if (to!ulong(archiveSize) + localFileHeaderLength + de.name.length + de.extra.length + de.compressedSize
+ directorySize + centralFileHeaderLength + de.name.length + de.extra.length + de.comment.length
+ endOfCentralDirLength + comment.length + zip64EndOfCentralDirLocatorLength
+ zip64EndOfCentralDirLength > uint.max)
throw new ZipException("zip files bigger than 4 GB are unsupported");

archiveSize += 30 + de.name.length +
archiveSize += localFileHeaderLength + de.name.length +
de.extra.length +
de.compressedSize;
directorySize += 46 + de.name.length +
directorySize += centralFileHeaderLength + de.name.length +
de.extra.length +
de.comment.length;
}

if (!isZip64 && _directory.length > ushort.max)
_isZip64 = true;
uint dataSize = archiveSize + directorySize + 22 + cast(uint) comment.length;
uint dataSize = archiveSize + directorySize + endOfCentralDirLength + cast(uint) comment.length;
if (isZip64)
dataSize += eocd64LocLength + eocd64Length;
dataSize += zip64EndOfCentralDirLocatorLength + zip64EndOfCentralDirLength;

_data = uninitializedArray!(ubyte[])(dataSize);

Expand All @@ -458,7 +484,7 @@ final class ZipArchive
foreach (ArchiveMember de; directory)
{
de.offset = i;
_data[i .. i + 4] = "PK\x03\x04".representation;
_data[i .. i + 4] = localFileHeaderSignature;
putUshort(i + 4, de.extractVersion);
putUshort(i + 6, de.flags);
putUshort(i + 8, de._compressionMethod);
Expand All @@ -468,7 +494,7 @@ final class ZipArchive
putUint (i + 22, to!uint(de.expandedSize));
putUshort(i + 26, cast(ushort) de.name.length);
putUshort(i + 28, cast(ushort) de.extra.length);
i += 30;
i += localFileHeaderLength;

_data[i .. i + de.name.length] = (de.name.representation)[];
i += de.name.length;
Expand All @@ -483,7 +509,7 @@ final class ZipArchive
_numEntries = 0;
foreach (ArchiveMember de; directory)
{
_data[i .. i + 4] = "PK\x01\x02".representation;
_data[i .. i + 4] = centralFileHeaderSignature;
putUshort(i + 4, de._madeVersion);
putUshort(i + 6, de.extractVersion);
putUshort(i + 8, de.flags);
Expand All @@ -499,7 +525,7 @@ final class ZipArchive
putUshort(i + 36, de.internalAttributes);
putUint (i + 38, de._externalAttributes);
putUint (i + 42, de.offset);
i += 46;
i += centralFileHeaderLength;

_data[i .. i + de.name.length] = (de.name.representation)[];
i += de.name.length;
Expand All @@ -515,8 +541,8 @@ final class ZipArchive
{
// Write zip64 end of central directory record
uint eocd64Offset = i;
_data[i .. i + 4] = "PK\x06\x06".representation;
putUlong (i + 4, eocd64Length - 12);
_data[i .. i + 4] = zip64EndOfCentralDirSignature;
putUlong (i + 4, zip64EndOfCentralDirLength - 12);
putUshort(i + 12, zip64ExtractVersion);
putUshort(i + 14, zip64ExtractVersion);
putUint (i + 16, diskNumber);
Expand All @@ -525,27 +551,27 @@ final class ZipArchive
putUlong (i + 32, totalEntries);
putUlong (i + 40, directorySize);
putUlong (i + 48, directoryOffset);
i += eocd64Length;
i += zip64EndOfCentralDirLength;

// Write zip64 end of central directory record locator
_data[i .. i + 4] = "PK\x06\x07".representation;
_data[i .. i + 4] = zip64EndOfCentralDirLocatorSignature;
putUint (i + 4, diskNumber);
putUlong (i + 8, eocd64Offset);
putUint (i + 16, 1);
i += eocd64LocLength;
i += zip64EndOfCentralDirLocatorLength;
}

// Write end record
endrecOffset = i;
_data[i .. i + 4] = "PK\x05\x06".representation;
_data[i .. i + 4] = endOfCentralDirSignature;
putUshort(i + 4, cast(ushort) diskNumber);
putUshort(i + 6, cast(ushort) diskStartDir);
putUshort(i + 8, (numEntries > ushort.max ? ushort.max : cast(ushort) numEntries));
putUshort(i + 10, (totalEntries > ushort.max ? ushort.max : cast(ushort) totalEntries));
putUint (i + 12, directorySize);
putUint (i + 16, directoryOffset);
putUshort(i + 20, cast(ushort) comment.length);
i += 22;
i += endOfCentralDirLength;

// Write archive comment
assert(i + comment.length == data.length, "Writing the archive comment failed.");
Expand Down Expand Up @@ -585,22 +611,23 @@ final class ZipArchive

// Find 'end record index' by searching backwards for signature
iend = (data.length > 66_000 ? to!uint(data.length - 66_000) : 0);
for (i = to!uint(data.length) - 22; 1; i--)
for (i = to!uint(data.length) - endOfCentralDirLength; 1; i--)
{
if (i < iend || i >= data.length)
throw new ZipException("no end record");

if (_data[i .. i + 4] == cast(ubyte[])"PK\x05\x06")
if (_data[i .. i + 4] == endOfCentralDirSignature)
{
endcommentlength = getUshort(i + 20);
if (i + 22 + endcommentlength > data.length
|| i + 22 + endcommentlength < i)
if (i + endOfCentralDirLength + endcommentlength > data.length
|| i + endOfCentralDirLength + endcommentlength < i)
continue;
comment = cast(string)(_data[i + 22 .. i + 22 + endcommentlength]);
comment = cast(string)(_data[i + endOfCentralDirLength ..
i + endOfCentralDirLength + endcommentlength]);
endrecOffset = i;

uint k = i - eocd64LocLength;
if (k < i && _data[k .. k + 4] == cast(ubyte[])"PK\x06\x07")
uint k = i - zip64EndOfCentralDirLocatorLength;
if (k < i && _data[k .. k + 4] == zip64EndOfCentralDirLocatorSignature)
{
_isZip64 = true;
i = k;
Expand All @@ -614,11 +641,11 @@ final class ZipArchive
{
// Read Zip64 record data
ulong eocdOffset = getUlong(i + 8);
if (eocdOffset + eocd64Length > _data.length)
if (eocdOffset + zip64EndOfCentralDirLength > _data.length)
throw new ZipException("corrupted directory");

i = to!uint(eocdOffset);
if (_data[i .. i + 4] != cast(ubyte[])"PK\x06\x06")
if (_data[i .. i + 4] != zip64EndOfCentralDirSignature)
throw new ZipException("invalid Zip EOCD64 signature");

ulong eocd64Size = getUlong(i + 4);
Expand Down Expand Up @@ -682,7 +709,7 @@ final class ZipArchive
uint extralen;
uint commentlen;

if (_data[i .. i + 4] != cast(ubyte[])"PK\x01\x02")
if (_data[i .. i + 4] != centralFileHeaderSignature)
throw new ZipException("invalid directory entry 1");
ArchiveMember de = new ArchiveMember();
de._index = n;
Expand All @@ -701,7 +728,7 @@ final class ZipArchive
de.internalAttributes = getUshort(i + 36);
de._externalAttributes = getUint(i + 38);
de.offset = getUint(i + 42);
i += 46;
i += centralFileHeaderLength;

if (i + namelen + extralen + commentlen > directoryOffset + directorySize)
throw new ZipException("invalid directory entry 2");
Expand All @@ -713,7 +740,7 @@ final class ZipArchive
de.comment = cast(string)(_data[i .. i + commentlen]);
i += commentlen;

immutable uint dataOffset = de.offset + 30 + namelen + extralen;
immutable uint dataOffset = de.offset + localFileHeaderLength + namelen + extralen;
if (dataOffset + de.compressedSize > endrecOffset)
throw new ZipException("Invalid directory entry offset or size.");
de._compressedData = _data[dataOffset .. dataOffset + de.compressedSize];
Expand All @@ -739,7 +766,7 @@ final class ZipArchive
uint namelen;
uint extralen;

if (_data[de.offset .. de.offset + 4] != "PK\x03\x04".representation)
if (_data[de.offset .. de.offset + 4] != localFileHeaderSignature)
throw new ZipException("invalid directory entry 4");

// These values should match what is in the main zip archive directory
Expand All @@ -765,7 +792,7 @@ final class ZipArchive
throw new ZipException("encryption not supported");

uint i;
i = de.offset + 30 + namelen + extralen;
i = de.offset + localFileHeaderLength + namelen + extralen;
if (i + de.compressedSize > endrecOffset)
throw new ZipException("invalid directory entry 5");

Expand Down

0 comments on commit bfc34bc

Please sign in to comment.