1
0
mirror of https://github.com/Ne-Lexa/php-zip.git synced 2025-01-17 07:18:14 +01:00

Merge branch 'hotfix/3.2.1'

This commit is contained in:
Ne-Lexa 2020-01-14 16:44:56 +03:00
commit d305ab68bc
13 changed files with 246 additions and 122 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
*.iml
/.idea
/composer.lock
/.php_cs.cache
/*.cache

View File

@ -107,4 +107,22 @@ namespace PHPSTORM_META {
\PhpZip\Constants\GeneralPurposeBitFlag::UTF8
);
expectedArguments(\PhpZip\Model\ZipEntry::setGeneralPurposeBitFlags(), 0, argumentsSet('zip_gpbf'));
registerArgumentsSet(
"winzip_aes_vendor_version",
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::VERSION_AE1,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::VERSION_AE2
);
registerArgumentsSet(
"winzip_aes_key_strength",
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_256BIT,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_128BIT,
\PhpZip\Model\Extra\Fields\WinZipAesExtraField::KEY_STRENGTH_192BIT
);
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 0, argumentsSet('winzip_aes_vendor_version'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 1, argumentsSet('winzip_aes_key_strength'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::__construct(), 2, argumentsSet('compression_methods'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setVendorVersion(), 0, argumentsSet('winzip_aes_vendor_version'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setKeyStrength(), 0, argumentsSet('winzip_aes_key_strength'));
expectedArguments(\PhpZip\Model\Extra\Fields\WinZipAesExtraField::setCompressionMethod(), 0, argumentsSet('compression_methods'));
}

View File

@ -366,7 +366,7 @@ class ZipReader
/** @var UnicodePathExtraField|null $unicodePathExtraField */
$unicodePathExtraField = $zipEntry->getExtraField(UnicodePathExtraField::HEADER_ID);
if ($unicodePathExtraField !== null) {
if ($unicodePathExtraField !== null && $unicodePathExtraField->getCrc32() === crc32($entryName)) {
$unicodePath = $unicodePathExtraField->getUnicodeValue();
if ($unicodePath !== null) {
@ -543,25 +543,23 @@ class ZipReader
/** @var string|ZipExtraField|null $className */
$className = ZipExtraDriver::getClassNameOrNull($headerId);
if ($className !== null) {
try {
$extraField = $local ?
\call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry) :
\call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) {
throw new \RuntimeException(
sprintf(
'Error parse %s extra field 0x%04X',
$local ? 'local' : 'central directory',
$headerId
)
);
try {
if ($className !== null) {
try {
$extraField = $local ?
\call_user_func([$className, 'unpackLocalFileData'], $bufferData, $zipEntry) :
\call_user_func([$className, 'unpackCentralDirData'], $bufferData, $zipEntry);
} catch (\Throwable $e) {
// skip errors while parsing invalid data
continue;
}
} else {
$extraField = new UnrecognizedExtraField($headerId, $bufferData);
}
} else {
$extraField = new UnrecognizedExtraField($headerId, $bufferData);
$collection->add($extraField);
} finally {
$pos += $data['dataSize'];
}
$collection->add($extraField);
$pos += $data['dataSize'];
}
}

View File

@ -29,16 +29,6 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
$this->unicodeValue = (string) $unicodeValue;
}
/**
* @param string $unicodeValue
*
* @return static
*/
public static function create($unicodeValue)
{
return new static(crc32($unicodeValue), $unicodeValue);
}
/**
* @return int the CRC32 checksum of the filename or comment as
* encoded in the central directory of the zip file
@ -48,6 +38,14 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
return $this->crc32;
}
/**
* @param int $crc32
*/
public function setCrc32($crc32)
{
$this->crc32 = (int) $crc32;
}
/**
* @return string
*/
@ -62,7 +60,6 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
public function setUnicodeValue($unicodeValue)
{
$this->unicodeValue = $unicodeValue;
$this->crc32 = crc32($unicodeValue);
}
/**
@ -78,19 +75,18 @@ abstract class AbstractUnicodeExtraField implements ZipExtraField
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
if (\strlen($buffer) < 5) {
throw new ZipException('UniCode path extra data must have at least 5 bytes.');
throw new ZipException('Unicode path extra data must have at least 5 bytes.');
}
$version = unpack('C', $buffer)[1];
$data = unpack('Cversion/Vcrc32', $buffer);
if ($version !== self::DEFAULT_VERSION) {
throw new ZipException(sprintf('Unsupported version [%d] for UniCode path extra data.', $version));
if ($data['version'] !== self::DEFAULT_VERSION) {
throw new ZipException(sprintf('Unsupported version [%d] for Unicode path extra data.', $data['version']));
}
$crc32 = unpack('V', substr($buffer, 1))[1];
$unicodeValue = substr($buffer, 5);
return new static($crc32, $unicodeValue);
return new static($data['crc32'], $unicodeValue);
}
/**

View File

@ -79,10 +79,10 @@ class AsiExtraField implements ZipExtraField
*/
public function __construct($mode, $uid = self::USER_GID_PID, $gid = self::USER_GID_PID, $link = '')
{
$this->mode = (int) $mode;
$this->uid = (int) $uid;
$this->gid = (int) $gid;
$this->link = (string) $link;
$this->mode = $mode;
$this->uid = $uid;
$this->gid = $gid;
$this->link = $link;
}
/**
@ -121,7 +121,7 @@ class AsiExtraField implements ZipExtraField
$link = '';
if ($data['linkSize'] > 0) {
$link = substr($buffer, 8);
$link = substr($buffer, 10);
}
return new self($data['mode'], $data['uid'], $data['gid'], $link);
@ -191,7 +191,7 @@ class AsiExtraField implements ZipExtraField
*/
public function setLink($link)
{
$this->link = $link;
$this->link = (string) $link;
$this->mode = $this->getPermissionsMode($this->mode);
}
@ -214,11 +214,13 @@ class AsiExtraField implements ZipExtraField
*/
protected function getPermissionsMode($mode)
{
$type = UnixStat::UNX_IFMT;
$type = 0;
if ($this->isLink()) {
$type = UnixStat::UNX_IFLNK;
} elseif ($this->isDirectory()) {
} elseif (($mode & UnixStat::UNX_IFREG) !== 0) {
$type = UnixStat::UNX_IFREG;
} elseif (($mode & UnixStat::UNX_IFDIR) !== 0) {
$type = UnixStat::UNX_IFDIR;
}
@ -264,7 +266,7 @@ class AsiExtraField implements ZipExtraField
*/
public function setUserId($uid)
{
$this->uid = $uid;
$this->uid = (int) $uid;
}
/**
@ -280,7 +282,7 @@ class AsiExtraField implements ZipExtraField
*/
public function setGroupId($gid)
{
$this->gid = $gid;
$this->gid = (int) $gid;
}
/**

View File

@ -359,6 +359,25 @@ class ExtendedTimestampExtraField implements ZipExtraField
public function setModifyTime($unixTime)
{
$this->modifyTime = $unixTime;
$this->updateFlags();
}
private function updateFlags()
{
$flags = 0;
if ($this->modifyTime !== null) {
$flags |= self::MODIFY_TIME_BIT;
}
if ($this->accessTime !== null) {
$flags |= self::ACCESS_TIME_BIT;
}
if ($this->createTime !== null) {
$flags |= self::CREATE_TIME_BIT;
}
$this->flags = $flags;
}
/**
@ -370,6 +389,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
public function setAccessTime($unixTime)
{
$this->accessTime = $unixTime;
$this->updateFlags();
}
/**
@ -381,6 +401,7 @@ class ExtendedTimestampExtraField implements ZipExtraField
public function setCreateTime($unixTime)
{
$this->createTime = $unixTime;
$this->updateFlags();
}
/**

View File

@ -115,25 +115,6 @@ class NewUnixExtraField implements ZipExtraField
return new self($data['version'], $gid, $uid);
}
/**
* Converts a signed byte into an unsigned integer representation
* (e.g., -1 becomes 255).
*
* @param int $b byte to convert to int
*
* @return int representation of the provided byte
*
* @since 1.5
*/
public static function signedByteToUnsignedInt($b)
{
if ($b >= 0) {
return $b;
}
return 256 + $b;
}
/**
* Populate data from this array as if it was in central directory data.
*
@ -160,10 +141,10 @@ class NewUnixExtraField implements ZipExtraField
return pack(
'CCVCV',
$this->version,
4, // GIDSize
$this->gid,
4, // UIDSize
$this->uid
$this->uid,
4, // GIDSize
$this->gid
);
}
@ -233,6 +214,14 @@ class NewUnixExtraField implements ZipExtraField
$this->gid = $gid & 0xffffffff;
}
/**
* @return int
*/
public function getVersion()
{
return $this->version;
}
/**
* @return string
*/

View File

@ -3,6 +3,7 @@
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Exception\ZipException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
use PhpZip\Util\PackUtil;
@ -10,7 +11,7 @@ use PhpZip\Util\PackUtil;
/**
* NTFS Extra Field.
*
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
* @see https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT .ZIP File Format Specification
*
* @license MIT
*/
@ -31,42 +32,45 @@ class NtfsExtraField implements ZipExtraField
* A.M. January 1, 1601 Coordinated Universal Time (UTC).
* this is the offset of Windows time 0 to Unix epoch in 100-nanosecond intervals.
*/
const EPOCH_OFFSET = -11644473600;
const EPOCH_OFFSET = -116444736000000000;
/** @var int Modify ntfs time */
private $modifyTime;
private $modifyNtfsTime;
/** @var int Access ntfs time */
private $accessTime;
private $accessNtfsTime;
/** @var int Create ntfs time */
private $createTime;
private $createNtfsTime;
/**
* @param int $modifyTime
* @param int $accessTime
* @param int $createTime
* @param int $modifyNtfsTime
* @param int $accessNtfsTime
* @param int $createNtfsTime
*/
public function __construct($modifyTime, $accessTime, $createTime)
public function __construct($modifyNtfsTime, $accessNtfsTime, $createNtfsTime)
{
$this->modifyTime = (int) $modifyTime;
$this->accessTime = (int) $accessTime;
$this->createTime = (int) $createTime;
$this->modifyNtfsTime = (int) $modifyNtfsTime;
$this->accessNtfsTime = (int) $accessNtfsTime;
$this->createNtfsTime = (int) $createNtfsTime;
}
/**
* @param \DateTimeInterface $mtime
* @param \DateTimeInterface $atime
* @param \DateTimeInterface $ctime
* @param \DateTimeInterface $modifyDateTime
* @param \DateTimeInterface $accessDateTime
* @param \DateTimeInterface $createNtfsTime
*
* @return NtfsExtraField
*/
public static function create(\DateTimeInterface $mtime, \DateTimeInterface $atime, \DateTimeInterface $ctime)
{
public static function create(
\DateTimeInterface $modifyDateTime,
\DateTimeInterface $accessDateTime,
\DateTimeInterface $createNtfsTime
) {
return new self(
self::dateTimeToNtfsTime($mtime),
self::dateTimeToNtfsTime($atime),
self::dateTimeToNtfsTime($ctime)
self::dateTimeToNtfsTime($modifyDateTime),
self::dateTimeToNtfsTime($accessDateTime),
self::dateTimeToNtfsTime($createNtfsTime)
);
}
@ -88,10 +92,16 @@ class NtfsExtraField implements ZipExtraField
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
if (\PHP_INT_SIZE === 4) {
throw new ZipException('not supported for php-32bit');
}
$buffer = substr($buffer, 4);
$modifyTime = 0;
@ -121,6 +131,8 @@ class NtfsExtraField implements ZipExtraField
* @param string $buffer the buffer to read data from
* @param ZipEntry|null $entry
*
* @throws ZipException
*
* @return NtfsExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
@ -138,13 +150,61 @@ class NtfsExtraField implements ZipExtraField
{
$data = pack('Vvv', 0, self::TIME_ATTR_TAG, self::TIME_ATTR_SIZE);
// refactoring will be needed when php 5.5 support ends
$data .= PackUtil::packLongLE($this->modifyTime);
$data .= PackUtil::packLongLE($this->accessTime);
$data .= PackUtil::packLongLE($this->createTime);
$data .= PackUtil::packLongLE($this->modifyNtfsTime);
$data .= PackUtil::packLongLE($this->accessNtfsTime);
$data .= PackUtil::packLongLE($this->createNtfsTime);
return $data;
}
/**
* @return int
*/
public function getModifyNtfsTime()
{
return $this->modifyNtfsTime;
}
/**
* @param int $modifyNtfsTime
*/
public function setModifyNtfsTime($modifyNtfsTime)
{
$this->modifyNtfsTime = (int) $modifyNtfsTime;
}
/**
* @return int
*/
public function getAccessNtfsTime()
{
return $this->accessNtfsTime;
}
/**
* @param int $accessNtfsTime
*/
public function setAccessNtfsTime($accessNtfsTime)
{
$this->accessNtfsTime = (int) $accessNtfsTime;
}
/**
* @return int
*/
public function getCreateNtfsTime()
{
return $this->createNtfsTime;
}
/**
* @param int $createNtfsTime
*/
public function setCreateNtfsTime($createNtfsTime)
{
$this->createNtfsTime = (int) $createNtfsTime;
}
/**
* The actual data to put into central directory - without Header-ID or
* length specifier.
@ -161,7 +221,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function getModifyDateTime()
{
return self::ntfsTimeToDateTime($this->modifyTime);
return self::ntfsTimeToDateTime($this->modifyNtfsTime);
}
/**
@ -169,7 +229,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function setModifyDateTime(\DateTimeInterface $modifyTime)
{
$this->modifyTime = self::dateTimeToNtfsTime($modifyTime);
$this->modifyNtfsTime = self::dateTimeToNtfsTime($modifyTime);
}
/**
@ -177,7 +237,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function getAccessDateTime()
{
return self::ntfsTimeToDateTime($this->accessTime);
return self::ntfsTimeToDateTime($this->accessNtfsTime);
}
/**
@ -185,7 +245,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function setAccessDateTime(\DateTimeInterface $accessTime)
{
$this->accessTime = self::dateTimeToNtfsTime($accessTime);
$this->accessNtfsTime = self::dateTimeToNtfsTime($accessTime);
}
/**
@ -193,7 +253,7 @@ class NtfsExtraField implements ZipExtraField
*/
public function getCreateDateTime()
{
return self::ntfsTimeToDateTime($this->createTime);
return self::ntfsTimeToDateTime($this->createNtfsTime);
}
/**
@ -201,7 +261,17 @@ class NtfsExtraField implements ZipExtraField
*/
public function setCreateDateTime(\DateTimeInterface $createTime)
{
$this->createTime = self::dateTimeToNtfsTime($createTime);
$this->createNtfsTime = self::dateTimeToNtfsTime($createTime);
}
/**
* @param float $timestamp Float timestamp
*
* @return int
*/
public static function timestampToNtfsTime($timestamp)
{
return (int) (((float) $timestamp * 10000000) - self::EPOCH_OFFSET);
}
/**
@ -211,23 +281,34 @@ class NtfsExtraField implements ZipExtraField
*/
public static function dateTimeToNtfsTime(\DateTimeInterface $dateTime)
{
return $dateTime->getTimestamp() * 10000000 + self::EPOCH_OFFSET;
return self::timestampToNtfsTime((float) $dateTime->format('U.u'));
}
/**
* @param int $time
* @param int $ntfsTime
*
* @return float Float unix timestamp
*/
public static function ntfsTimeToTimestamp($ntfsTime)
{
return (float) (($ntfsTime + self::EPOCH_OFFSET) / 10000000);
}
/**
* @param int $ntfsTime
*
* @return \DateTimeInterface
*/
public static function ntfsTimeToDateTime($time)
public static function ntfsTimeToDateTime($ntfsTime)
{
$timestamp = (int) ($time / 10000000 + self::EPOCH_OFFSET);
$timestamp = self::ntfsTimeToTimestamp($ntfsTime);
$dateTime = \DateTimeImmutable::createFromFormat('U.u', sprintf('%.6f', $timestamp));
try {
return new \DateTimeImmutable('@' . $timestamp);
} catch (\Exception $e) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp, 1, $e);
if ($dateTime === false) {
throw new InvalidArgumentException('Cannot create date/time object for timestamp ' . $timestamp);
}
return $dateTime;
}
/**
@ -238,17 +319,17 @@ class NtfsExtraField implements ZipExtraField
$args = [self::HEADER_ID];
$format = '0x%04x NtfsExtra:';
if ($this->modifyTime !== 0) {
if ($this->modifyNtfsTime !== 0) {
$format .= ' Modify:[%s]';
$args[] = $this->getModifyDateTime()->format(\DATE_ATOM);
}
if ($this->accessTime !== 0) {
if ($this->accessNtfsTime !== 0) {
$format .= ' Access:[%s]';
$args[] = $this->getAccessDateTime()->format(\DATE_ATOM);
}
if ($this->createTime !== 0) {
if ($this->createNtfsTime !== 0) {
$format .= ' Create:[%s]';
$args[] = $this->getCreateDateTime()->format(\DATE_ATOM);
}

View File

@ -2,6 +2,7 @@
namespace PhpZip\Model\Extra\Fields;
use PhpZip\Exception\RuntimeException;
use PhpZip\Model\Extra\ZipExtraField;
use PhpZip\Model\ZipEntry;
@ -56,7 +57,7 @@ class UnrecognizedExtraField implements ZipExtraField
*/
public static function unpackLocalFileData($buffer, ZipEntry $entry = null)
{
throw new \RuntimeException('Unsupport parse');
throw new RuntimeException('Unsupport parse');
}
/**
@ -67,7 +68,7 @@ class UnrecognizedExtraField implements ZipExtraField
*/
public static function unpackCentralDirData($buffer, ZipEntry $entry = null)
{
throw new \RuntimeException('Unsupport parse');
throw new RuntimeException('Unsupport parse');
}
/**

View File

@ -256,7 +256,7 @@ class WinZipAesExtraField implements ZipExtraField
$vendorVersion = (int) $vendorVersion;
if (!\in_array($vendorVersion, self::$allowVendorVersions, true)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf(
'Unsupport WinZip AES vendor version: %d',
$vendorVersion
@ -294,7 +294,7 @@ class WinZipAesExtraField implements ZipExtraField
$keyStrength = (int) $keyStrength;
if (!isset(self::$encryptionStrengths[$keyStrength])) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf(
'Key strength %d not support value. Allow values: %s',
$keyStrength,

View File

@ -963,9 +963,12 @@ class ZipEntry
{
$dosTime = (int) $dosTime;
if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
throw new InvalidArgumentException('DosTime out of range');
if (\PHP_INT_SIZE === 8) {
if ($dosTime < 0x00000000 || $dosTime > 0xffffffff) {
throw new InvalidArgumentException('DosTime out of range');
}
}
$this->dosTime = $dosTime;
return $this;
@ -1010,9 +1013,12 @@ class ZipEntry
{
$this->externalAttributes = (int) $externalAttributes;
if ($externalAttributes < 0x00000000 || $externalAttributes > 0xffffffff) {
throw new InvalidArgumentException('external attributes out of range: ' . $externalAttributes);
if (\PHP_INT_SIZE === 8) {
if ($externalAttributes < 0x00000000 || $externalAttributes > 0xffffffff) {
throw new InvalidArgumentException('external attributes out of range: ' . $externalAttributes);
}
}
$this->externalAttributes = $externalAttributes;
return $this;

View File

@ -89,7 +89,10 @@ class ZipEntryMatcher implements \Countable
*/
public function all()
{
$this->matches = array_keys($this->zipContainer->getEntries());
$this->matches = array_map(
'strval',
array_keys($this->zipContainer->getEntries())
);
return $this;
}
@ -192,10 +195,7 @@ class ZipEntryMatcher implements \Countable
*
* @see http://php.net/manual/en/countable.count.php
*
* @return int The custom count as an integer.
* </p>
* <p>
* The return value is cast to an integer.
* @return int the custom count as an integer
*
* @since 5.1.0
*/

View File

@ -266,7 +266,7 @@ class ZipEntryTest extends TestCase
$dosEntryName = DosCodePage::fromUTF8($entryName, $charset);
static::assertSame(DosCodePage::toUTF8($dosEntryName, $charset), $entryName);
$unicodePathExtraField = UnicodePathExtraField::create($entryName);
$unicodePathExtraField = new UnicodePathExtraField(crc32($dosEntryName), $entryName);
$zipEntry = new ZipEntry($dosEntryName, $charset);
static::assertSame($zipEntry->getName(), $dosEntryName);
@ -800,6 +800,12 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidDosTime($dosTime)
{
if (\PHP_INT_SIZE === 4) {
static::markTestSkipped('only 64 bit test');
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'DosTime out of range');
$zipEntry = new ZipEntry('entry');
@ -951,6 +957,12 @@ class ZipEntryTest extends TestCase
*/
public function testInvalidExternalAttributes($externalAttributes)
{
if (\PHP_INT_SIZE === 4) {
static::markTestSkipped('only 64 bit test');
return;
}
$this->setExpectedException(InvalidArgumentException::class, 'external attributes out of range');
$zipEntry = new ZipEntry('entry');