mirror of
https://github.com/Ne-Lexa/php-zip.git
synced 2025-08-23 15:32:50 +02:00
Compare commits
12 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
251ce11bdc | ||
|
f969e59319 | ||
|
6808e4ffdc | ||
|
91f08b9f55 | ||
|
d0cf7f7d1d | ||
|
9f0d151f5e | ||
|
171d4a8e4c | ||
|
aa09b24d02 | ||
|
c34f90ac18 | ||
|
7d73ac417f | ||
|
4979829fad | ||
|
a3083b821c |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,5 +1,16 @@
|
||||
# Changelog
|
||||
|
||||
# 3.1.2 (2017-11-17)
|
||||
- Changed the algorithm for adding paddings to zipalign.
|
||||
Now we will use the special field ExtraField c ID 0xD935,
|
||||
which was implemented by Google in the apksigner library.
|
||||
Now this field corresponds to the ZIP standard for storing
|
||||
ExtraField records, and not just filling with zero bytes,
|
||||
as in the zipalign console utility.
|
||||
|
||||
## 3.1.1 (2017-11-15)
|
||||
- Fix resave zip aligned archive
|
||||
|
||||
## 3.1.0 (2017-11-14)
|
||||
- Added class `ZipModel` for all changes.
|
||||
- All manipulations with incoming and outgoing streams are in separate files: `ZipInputStream` and `ZipOutputStream`.
|
||||
|
@@ -51,10 +51,10 @@
|
||||
- Поддержка `ZIP64` (размер файла более 4 GB или количество записей в архиве более 65535).
|
||||
- Встроенная поддержка выравнивания архива для оптимизации Android пакетов (APK) [`zipalign`](https://developer.android.com/studio/command-line/zipalign.html).
|
||||
- Работа с паролями для PHP 5.5
|
||||
> **Внимание!**
|
||||
>
|
||||
> Для 32-bit систем, в данный момент не поддерживается метод шифрование `Traditional PKWARE Encryption (ZipCrypto)`.
|
||||
> Используйте метод шифрования `WinZIP AES Encryption`, когда это возможно.
|
||||
> **Внимание!**
|
||||
>
|
||||
> Для 32-bit систем, в данный момент не поддерживается метод шифрование `Traditional PKWARE Encryption (ZipCrypto)`.
|
||||
> Используйте метод шифрования `WinZIP AES Encryption`, когда это возможно.
|
||||
+ Установка пароля для чтения архива глобально или для некоторых записей.
|
||||
+ Изменение пароля архива, в том числе и для отдельных записей.
|
||||
+ Удаление пароля архива глобально или для отдельных записей.
|
||||
|
@@ -51,10 +51,10 @@ Table of contents
|
||||
- Support for `ZIP64` (file size is more than 4 GB or the number of entries in the archive is more than 65535).
|
||||
- Built-in support for aligning the archive to optimize Android packages (APK) [`zipalign`](https://developer.android.com/studio/command-line/zipalign.html).
|
||||
- Working with passwords for PHP 5.5
|
||||
> **Attention!**
|
||||
>
|
||||
> For 32-bit systems, the `Traditional PKWARE Encryption (ZipCrypto)` encryption method is not currently supported.
|
||||
> Use the encryption method `WinZIP AES Encryption`, whenever possible.
|
||||
> **Attention!**
|
||||
>
|
||||
> For 32-bit systems, the `Traditional PKWARE Encryption (ZipCrypto)` encryption method is not currently supported.
|
||||
> Use the encryption method `WinZIP AES Encryption`, whenever possible.
|
||||
+ Set the password to read the archive for all entries or only for some.
|
||||
+ Change the password for the archive, including for individual entries.
|
||||
+ Delete the archive password for all or individual entries.
|
||||
|
@@ -1,45 +1,47 @@
|
||||
{
|
||||
"name": "nelexa/zip",
|
||||
"description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, ZipAlign tool, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"zip",
|
||||
"unzip",
|
||||
"archive",
|
||||
"extract",
|
||||
"winzip",
|
||||
"zipalign",
|
||||
"ziparchive"
|
||||
],
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "4.8"
|
||||
},
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ne-Lexa",
|
||||
"email": "alexey@nelexa.ru",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"minimum-stability": "stable",
|
||||
"require": {
|
||||
"php": "^5.5 || ^7.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpZip\\": "src/PhpZip"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpZip\\": "tests/PhpZip"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt",
|
||||
"ext-mcrypt": "Needed to support encrypt zip entries or use ext-openssl",
|
||||
"ext-bz2": "Needed to support BZIP2 compression"
|
||||
}
|
||||
"name": "nelexa/zip",
|
||||
"type": "library",
|
||||
"description": "PhpZip is a php-library for extended work with ZIP-archives. Open, create, update, delete, extract and get info tool. Supports appending to existing ZIP files, WinZip AES encryption, Traditional PKWARE Encryption, ZipAlign tool, BZIP2 compression, external file attributes and ZIP64 extensions. Alternative ZipArchive. It does not require php-zip extension.",
|
||||
"keywords": [
|
||||
"zip",
|
||||
"unzip",
|
||||
"archive",
|
||||
"extract",
|
||||
"winzip",
|
||||
"zipalign",
|
||||
"ziparchive"
|
||||
],
|
||||
"homepage": "https://github.com/Ne-Lexa/php-zip",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ne-Lexa",
|
||||
"email": "alexey@nelexa.ru",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.5 || ^7.0",
|
||||
"psr/http-message": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8|~5.7",
|
||||
"zendframework/zend-diactoros": "^1.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpZip\\": "src/PhpZip"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpZip\\": "tests/PhpZip"
|
||||
}
|
||||
},
|
||||
"suggest": {
|
||||
"ext-openssl": "Needed to support encrypt zip entries or use ext-mcrypt",
|
||||
"ext-mcrypt": "Needed to support encrypt zip entries or use ext-openssl",
|
||||
"ext-bz2": "Needed to support BZIP2 compression"
|
||||
},
|
||||
"minimum-stability": "stable"
|
||||
}
|
||||
|
@@ -3,11 +3,14 @@
|
||||
namespace PhpZip\Extra;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Extra\Fields\ApkAlignmentExtraField;
|
||||
use PhpZip\Extra\Fields\DefaultExtraField;
|
||||
use PhpZip\Extra\Fields\JarMarkerExtraField;
|
||||
use PhpZip\Extra\Fields\NtfsExtraField;
|
||||
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
|
||||
use PhpZip\Extra\Fields\Zip64ExtraField;
|
||||
use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Util\StringUtil;
|
||||
|
||||
/**
|
||||
* Extra Fields Factory
|
||||
@@ -26,6 +29,56 @@ class ExtraFieldsFactory
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $extra
|
||||
* @param ZipEntry|null $entry
|
||||
* @return ExtraFieldsCollection
|
||||
* @throws ZipException
|
||||
*/
|
||||
public static function createExtraFieldCollections($extra, ZipEntry $entry = null)
|
||||
{
|
||||
$extraFieldsCollection = new ExtraFieldsCollection();
|
||||
if (null !== $extra) {
|
||||
$extraLength = strlen($extra);
|
||||
if ($extraLength > 0xffff) {
|
||||
throw new ZipException("Extra Fields too large: " . $extraLength);
|
||||
}
|
||||
$pos = 0;
|
||||
$endPos = $extraLength;
|
||||
|
||||
while ($endPos - $pos >= 4) {
|
||||
$unpack = unpack('vheaderId/vdataSize', substr($extra, $pos, 4));
|
||||
$pos += 4;
|
||||
$headerId = (int)$unpack['headerId'];
|
||||
$dataSize = (int)$unpack['dataSize'];
|
||||
$extraField = ExtraFieldsFactory::create($headerId);
|
||||
if ($extraField instanceof Zip64ExtraField && $entry !== null) {
|
||||
$extraField->setEntry($entry);
|
||||
}
|
||||
$extraField->deserialize(substr($extra, $pos, $dataSize));
|
||||
$pos += $dataSize;
|
||||
$extraFieldsCollection[$headerId] = $extraField;
|
||||
}
|
||||
}
|
||||
return $extraFieldsCollection;
|
||||
}
|
||||
|
||||
public static function createSerializedData(ExtraFieldsCollection $extraFieldsCollection)
|
||||
{
|
||||
$extraData = '';
|
||||
foreach ($extraFieldsCollection as $extraField) {
|
||||
$data = $extraField->serialize();
|
||||
$extraData .= pack('vv', $extraField::getHeaderId(), strlen($data));
|
||||
$extraData .= $data;
|
||||
}
|
||||
|
||||
$size = strlen($extraData);
|
||||
if (0x0000 > $size || $size > 0xffff) {
|
||||
throw new ZipException('Size extra out of range: ' . $size . '. Extra data: ' . $extraData);
|
||||
}
|
||||
return $extraData;
|
||||
}
|
||||
|
||||
/**
|
||||
* A static factory method which creates a new Extra Field based on the
|
||||
* given Header ID.
|
||||
@@ -69,6 +122,8 @@ class ExtraFieldsFactory
|
||||
self::$registry[WinZipAesEntryExtraField::getHeaderId()] = WinZipAesEntryExtraField::class;
|
||||
self::$registry[NtfsExtraField::getHeaderId()] = NtfsExtraField::class;
|
||||
self::$registry[Zip64ExtraField::getHeaderId()] = Zip64ExtraField::class;
|
||||
self::$registry[ApkAlignmentExtraField::getHeaderId()] = ApkAlignmentExtraField::class;
|
||||
self::$registry[JarMarkerExtraField::getHeaderId()] = JarMarkerExtraField::class;
|
||||
}
|
||||
return self::$registry;
|
||||
}
|
||||
@@ -97,4 +152,22 @@ class ExtraFieldsFactory
|
||||
{
|
||||
return new Zip64ExtraField($entry);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ZipEntry $entry
|
||||
* @param int $padding
|
||||
* @return ApkAlignmentExtraField
|
||||
*/
|
||||
public static function createApkAlignExtra(ZipEntry $entry, $padding)
|
||||
{
|
||||
$padding = (int)$padding;
|
||||
$multiple = 4;
|
||||
if (StringUtil::endsWith($entry->getName(), '.so')) {
|
||||
$multiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
|
||||
}
|
||||
$extraField = new ApkAlignmentExtraField();
|
||||
$extraField->setMultiple($multiple);
|
||||
$extraField->setPadding($padding);
|
||||
return $extraField;
|
||||
}
|
||||
}
|
||||
|
112
src/PhpZip/Extra/Fields/ApkAlignmentExtraField.php
Normal file
112
src/PhpZip/Extra/Fields/ApkAlignmentExtraField.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Extra\Fields;
|
||||
|
||||
use PhpZip\Exception\InvalidArgumentException;
|
||||
use PhpZip\Extra\ExtraField;
|
||||
|
||||
/**
|
||||
* Apk Alignment Extra Field
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class ApkAlignmentExtraField implements ExtraField
|
||||
{
|
||||
/**
|
||||
* Minimum size (in bytes) of the extensible data block/field used
|
||||
* for alignment of uncompressed entries.
|
||||
*/
|
||||
const ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES = 6;
|
||||
|
||||
const ANDROID_COMMON_PAGE_ALIGNMENT_BYTES = 4096;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $multiple;
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $padding;
|
||||
|
||||
/**
|
||||
* Returns the Header ID (type) of this Extra Field.
|
||||
* The Header ID is an unsigned short integer (two bytes)
|
||||
* which must be constant during the life cycle of this object.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getHeaderId()
|
||||
{
|
||||
return 0xD935;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
if ($this->padding > 0) {
|
||||
$args = array_merge(
|
||||
['vc*', $this->multiple],
|
||||
array_fill(2, $this->padding, 0)
|
||||
);
|
||||
return call_user_func_array('pack', $args);
|
||||
}
|
||||
return pack('v', $this->multiple);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
* @param string $data
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
$length = strlen($data);
|
||||
if ($length < 2) {
|
||||
// This is APK alignment field.
|
||||
// FORMAT:
|
||||
// * uint16 alignment multiple (in bytes)
|
||||
// * remaining bytes -- padding to achieve alignment of data which starts after
|
||||
// the extra field
|
||||
throw new InvalidArgumentException("Minimum 6 bytes of the extensible data block/field used for alignment of uncompressed entries.");
|
||||
}
|
||||
$this->multiple = unpack('v', $data)[1];
|
||||
$this->padding = $length - 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getMultiple()
|
||||
{
|
||||
return $this->multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPadding()
|
||||
{
|
||||
return $this->padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $multiple
|
||||
*/
|
||||
public function setMultiple($multiple)
|
||||
{
|
||||
$this->multiple = $multiple;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $padding
|
||||
*/
|
||||
public function setPadding($padding)
|
||||
{
|
||||
$this->padding = $padding;
|
||||
}
|
||||
}
|
51
src/PhpZip/Extra/Fields/JarMarkerExtraField.php
Normal file
51
src/PhpZip/Extra/Fields/JarMarkerExtraField.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace PhpZip\Extra\Fields;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Extra\ExtraField;
|
||||
|
||||
/**
|
||||
* Jar Marker Extra Field
|
||||
* An executable Java program can be packaged in a JAR file with all the libraries it uses.
|
||||
* Executable JAR files can easily be distinguished from the files packed in the JAR file
|
||||
* by the extra field in the first file, which is hexadecimal in the 0xCAFE bytes series.
|
||||
*
|
||||
* @author Ne-Lexa alexey@nelexa.ru
|
||||
* @license MIT
|
||||
*/
|
||||
class JarMarkerExtraField implements ExtraField
|
||||
{
|
||||
/**
|
||||
* Returns the Header ID (type) of this Extra Field.
|
||||
* The Header ID is an unsigned short integer (two bytes)
|
||||
* which must be constant during the life cycle of this object.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function getHeaderId()
|
||||
{
|
||||
return 0xCAFE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a Data Block.
|
||||
* @return string
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this Extra Field by deserializing a Data Block.
|
||||
* @param string $data
|
||||
* @throws ZipException
|
||||
*/
|
||||
public function deserialize($data)
|
||||
{
|
||||
if (0 !== strlen($data)) {
|
||||
throw new ZipException("JarMarker doesn't expect any data");
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,7 +7,6 @@ use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Extra\ExtraFieldsCollection;
|
||||
use PhpZip\Extra\ExtraFieldsFactory;
|
||||
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
|
||||
use PhpZip\Extra\Fields\Zip64ExtraField;
|
||||
use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Util\DateTimeConverter;
|
||||
use PhpZip\Util\StringUtil;
|
||||
@@ -256,11 +255,8 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
*/
|
||||
public function isZip64ExtensionsRequired()
|
||||
{
|
||||
// Offset MUST be considered in decision about ZIP64 format - see
|
||||
// description of Data Descriptor in ZIP File Format Specification!
|
||||
return 0xffffffff <= $this->getCompressedSize()
|
||||
|| 0xffffffff <= $this->getSize()
|
||||
|| 0xffffffff <= sprintf('%u', $this->getOffset());
|
||||
|| 0xffffffff <= $this->getSize();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,7 +429,10 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
*/
|
||||
public function getMethod()
|
||||
{
|
||||
return $this->isInit(self::BIT_METHOD) ? $this->method & 0xffff : self::UNKNOWN;
|
||||
$isInit = $this->isInit(self::BIT_METHOD);
|
||||
return $isInit ?
|
||||
$this->method & 0xffff :
|
||||
self::UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,17 +446,14 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
{
|
||||
if (self::UNKNOWN === $method) {
|
||||
$this->method = $method;
|
||||
$this->setInit(self::BIT_METHOD, false);
|
||||
return $this;
|
||||
}
|
||||
if (0x0000 > $method || $method > 0xffff) {
|
||||
throw new ZipException('method out of range');
|
||||
throw new ZipException('method out of range: ' . $method);
|
||||
}
|
||||
switch ($method) {
|
||||
case self::METHOD_WINZIP_AES:
|
||||
$this->method = $method;
|
||||
$this->setInit(self::BIT_METHOD, true);
|
||||
break;
|
||||
|
||||
case ZipFileInterface::METHOD_STORED:
|
||||
case ZipFileInterface::METHOD_DEFLATED:
|
||||
case ZipFileInterface::METHOD_BZIP2:
|
||||
@@ -585,18 +581,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
*/
|
||||
public function getExtra()
|
||||
{
|
||||
$extraData = '';
|
||||
foreach ($this->getExtraFieldsCollection() as $extraField) {
|
||||
$data = $extraField->serialize();
|
||||
$extraData .= pack('vv', $extraField::getHeaderId(), strlen($data));
|
||||
$extraData .= $data;
|
||||
}
|
||||
|
||||
$size = strlen($extraData);
|
||||
if (0x0000 > $size || $size > 0xffff) {
|
||||
throw new ZipException('Size extra out of range: ' . $size . '. Extra data: ' . $extraData);
|
||||
}
|
||||
return $extraData;
|
||||
return ExtraFieldsFactory::createSerializedData($this->extraFieldsCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -612,28 +597,7 @@ abstract class ZipAbstractEntry implements ZipEntry
|
||||
*/
|
||||
public function setExtra($data)
|
||||
{
|
||||
$this->extraFieldsCollection = new ExtraFieldsCollection();
|
||||
if (null !== $data) {
|
||||
$extraLength = strlen($data);
|
||||
if (0x0000 > $extraLength || $extraLength > 0xffff) {
|
||||
throw new ZipException("Extra Fields too large: " . $extraLength);
|
||||
}
|
||||
$pos = 0;
|
||||
$endPos = $extraLength;
|
||||
while ($pos < $endPos) {
|
||||
$unpack = unpack('vheaderId/vdataSize', substr($data, $pos, 4));
|
||||
$pos += 4;
|
||||
$headerId = (int)$unpack['headerId'];
|
||||
$dataSize = (int)$unpack['dataSize'];
|
||||
$extraField = ExtraFieldsFactory::create($headerId);
|
||||
if ($extraField instanceof Zip64ExtraField) {
|
||||
$extraField->setEntry($this);
|
||||
}
|
||||
$extraField->deserialize(substr($data, $pos, $dataSize));
|
||||
$pos += $dataSize;
|
||||
$this->extraFieldsCollection[$headerId] = $extraField;
|
||||
}
|
||||
}
|
||||
$this->extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($data, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -62,9 +62,9 @@ class ZipNewEntry extends ZipAbstractEntry
|
||||
$method = $this->getMethod();
|
||||
return self::METHOD_WINZIP_AES === $method ? 51 :
|
||||
(
|
||||
ZipFileInterface::METHOD_BZIP2 === $method ? 46 :
|
||||
ZipFileInterface::METHOD_BZIP2 === $method ? 46 :
|
||||
(
|
||||
$this->isZip64ExtensionsRequired() ? 45 :
|
||||
$this->isZip64ExtensionsRequired() ? 45 :
|
||||
(ZipFileInterface::METHOD_DEFLATED === $method || $this->isDirectory() ? 20 : 10)
|
||||
)
|
||||
);
|
||||
|
@@ -3,7 +3,6 @@
|
||||
namespace PhpZip\Model;
|
||||
|
||||
use PhpZip\Exception\ZipException;
|
||||
|
||||
use PhpZip\Extra\ExtraFieldsCollection;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
@@ -19,7 +18,7 @@ interface ZipEntry
|
||||
// Bit masks for initialized fields.
|
||||
const BIT_PLATFORM = 1,
|
||||
BIT_METHOD = 2 /* 1 << 1 */,
|
||||
BIT_CRC = 2 /* 1 << 2 */,
|
||||
BIT_CRC = 4 /* 1 << 2 */,
|
||||
BIT_DATE_TIME = 64 /* 1 << 6 */,
|
||||
BIT_EXTERNAL_ATTR = 128 /* 1 << 7*/
|
||||
;
|
||||
|
@@ -224,7 +224,7 @@ class ZipModel implements \Countable
|
||||
if (isset($this->outEntries[$entryName])) {
|
||||
return $this->outEntries[$entryName];
|
||||
}
|
||||
throw new ZipNotFoundEntry('Zip entry ' . $entryName . ' not found');
|
||||
throw new ZipNotFoundEntry('Zip entry "' . $entryName . '" not found');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -10,6 +10,9 @@ use PhpZip\Exception\RuntimeException;
|
||||
use PhpZip\Exception\ZipCryptoException;
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Exception\ZipUnsupportMethod;
|
||||
use PhpZip\Extra\ExtraFieldsCollection;
|
||||
use PhpZip\Extra\ExtraFieldsFactory;
|
||||
use PhpZip\Extra\Fields\ApkAlignmentExtraField;
|
||||
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
|
||||
use PhpZip\Mapper\OffsetPositionMapper;
|
||||
use PhpZip\Mapper\PositionMapper;
|
||||
@@ -18,6 +21,7 @@ use PhpZip\Model\Entry\ZipSourceEntry;
|
||||
use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Model\ZipModel;
|
||||
use PhpZip\Util\PackUtil;
|
||||
use PhpZip\Util\StringUtil;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
/**
|
||||
@@ -471,6 +475,9 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the input stream of the LOC entry zip and the data into
|
||||
* the output stream and zip the alignment if necessary.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipOutputStreamInterface $out
|
||||
*/
|
||||
@@ -484,37 +491,82 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$nameLength = strlen($entry->getName());
|
||||
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2, SEEK_SET);
|
||||
$extraLength = unpack('v', fread($this->in, 2))[1];
|
||||
$sourceExtraLength = $destExtraLength = unpack('v', fread($this->in, 2))[1];
|
||||
|
||||
$length = ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $extraLength + $nameLength;
|
||||
|
||||
$padding = 0;
|
||||
if ($this->zipModel->isZipAlign() && !$entry->isEncrypted() && $entry->getMethod() === ZipFileInterface::METHOD_STORED) {
|
||||
$padding =
|
||||
(
|
||||
$this->zipModel->getZipAlign() -
|
||||
(ftell($out->getStream()) + $length) % $this->zipModel->getZipAlign()
|
||||
) % $this->zipModel->getZipAlign();
|
||||
if ($sourceExtraLength > 0) {
|
||||
// read Local File Header extra fields
|
||||
fseek($this->in, $pos + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength, SEEK_SET);
|
||||
$extra = fread($this->in, $sourceExtraLength);
|
||||
$extraFieldsCollection = ExtraFieldsFactory::createExtraFieldCollections($extra, $entry);
|
||||
if (isset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]) && $this->zipModel->isZipAlign()) {
|
||||
unset($extraFieldsCollection[ApkAlignmentExtraField::getHeaderId()]);
|
||||
$destExtraLength = strlen(ExtraFieldsFactory::createSerializedData($extraFieldsCollection));
|
||||
}
|
||||
} else {
|
||||
$extraFieldsCollection = new ExtraFieldsCollection();
|
||||
}
|
||||
|
||||
$dataAlignmentMultiple = $this->zipModel->getZipAlign();
|
||||
$copyInToOutLength = $entry->getCompressedSize();
|
||||
|
||||
fseek($this->in, $pos, SEEK_SET);
|
||||
if ($padding > 0) {
|
||||
stream_copy_to_stream($this->in, $out->getStream(), ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2);
|
||||
fwrite($out->getStream(), pack('v', $extraLength + $padding));
|
||||
fseek($this->in, 2, SEEK_CUR);
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $nameLength + $extraLength);
|
||||
fwrite($out->getStream(), str_repeat(chr(0), $padding));
|
||||
} else {
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $length);
|
||||
}
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $entry->getCompressedSize());
|
||||
if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
|
||||
$length = 12;
|
||||
if ($entry->isZip64ExtensionsRequired()) {
|
||||
$length += 8;
|
||||
|
||||
if (
|
||||
$this->zipModel->isZipAlign() &&
|
||||
!$entry->isEncrypted() &&
|
||||
$entry->getMethod() === ZipFileInterface::METHOD_STORED
|
||||
) {
|
||||
if (StringUtil::endsWith($entry->getName(), '.so')) {
|
||||
$dataAlignmentMultiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
|
||||
}
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $length);
|
||||
|
||||
$dataMinStartOffset =
|
||||
ftell($out->getStream()) +
|
||||
ZipEntry::LOCAL_FILE_HEADER_MIN_LEN +
|
||||
$destExtraLength +
|
||||
$nameLength +
|
||||
ApkAlignmentExtraField::ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES;
|
||||
$padding =
|
||||
($dataAlignmentMultiple - ($dataMinStartOffset % $dataAlignmentMultiple))
|
||||
% $dataAlignmentMultiple;
|
||||
|
||||
$alignExtra = new ApkAlignmentExtraField();
|
||||
$alignExtra->setMultiple($dataAlignmentMultiple);
|
||||
$alignExtra->setPadding($padding);
|
||||
$extraFieldsCollection->add($alignExtra);
|
||||
|
||||
$extra = ExtraFieldsFactory::createSerializedData($extraFieldsCollection);
|
||||
|
||||
// copy Local File Header without extra field length
|
||||
// from input stream to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), ZipEntry::LOCAL_FILE_HEADER_MIN_LEN - 2);
|
||||
// write new extra field length (2 bytes) to output stream
|
||||
fwrite($out->getStream(), pack('v', strlen($extra)));
|
||||
// skip 2 bytes to input stream
|
||||
fseek($this->in, 2, SEEK_CUR);
|
||||
// copy name from input stream to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $nameLength);
|
||||
// write extra field to output stream
|
||||
fwrite($out->getStream(), $extra);
|
||||
// skip source extraLength from input stream
|
||||
fseek($this->in, $sourceExtraLength, SEEK_CUR);
|
||||
} else {
|
||||
$copyInToOutLength += ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $sourceExtraLength + $nameLength;
|
||||
;
|
||||
}
|
||||
if ($entry->getGeneralPurposeBitFlag(ZipEntry::GPBF_DATA_DESCRIPTOR)) {
|
||||
// crc-32 4 bytes
|
||||
// compressed size 4 bytes
|
||||
// uncompressed size 4 bytes
|
||||
$copyInToOutLength += 12;
|
||||
if ($entry->isZip64ExtensionsRequired()) {
|
||||
// compressed size +4 bytes
|
||||
// uncompressed size +4 bytes
|
||||
$copyInToOutLength += 8;
|
||||
}
|
||||
}
|
||||
// copy loc, data, data descriptor from input to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $copyInToOutLength);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -532,6 +584,7 @@ class ZipInputStream implements ZipInputStreamInterface
|
||||
$extraLength = unpack('v', fread($this->in, 2))[1];
|
||||
|
||||
fseek($this->in, $offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength + $extraLength, SEEK_SET);
|
||||
// copy raw data from input stream to output stream
|
||||
stream_copy_to_stream($this->in, $out->getStream(), $entry->getCompressedSize());
|
||||
}
|
||||
|
||||
|
@@ -35,6 +35,9 @@ interface ZipInputStreamInterface
|
||||
public function getStream();
|
||||
|
||||
/**
|
||||
* Copy the input stream of the LOC entry zip and the data into
|
||||
* the output stream and zip the alignment if necessary.
|
||||
*
|
||||
* @param ZipEntry $entry
|
||||
* @param ZipOutputStreamInterface $out
|
||||
*/
|
||||
|
@@ -8,6 +8,7 @@ use PhpZip\Exception\InvalidArgumentException;
|
||||
use PhpZip\Exception\RuntimeException;
|
||||
use PhpZip\Exception\ZipException;
|
||||
use PhpZip\Extra\ExtraFieldsFactory;
|
||||
use PhpZip\Extra\Fields\ApkAlignmentExtraField;
|
||||
use PhpZip\Extra\Fields\WinZipAesEntryExtraField;
|
||||
use PhpZip\Extra\Fields\Zip64ExtraField;
|
||||
use PhpZip\Model\EndOfCentralDirectory;
|
||||
@@ -17,6 +18,7 @@ use PhpZip\Model\Entry\ZipSourceEntry;
|
||||
use PhpZip\Model\ZipEntry;
|
||||
use PhpZip\Model\ZipModel;
|
||||
use PhpZip\Util\PackUtil;
|
||||
use PhpZip\Util\StringUtil;
|
||||
use PhpZip\ZipFileInterface;
|
||||
|
||||
/**
|
||||
@@ -87,6 +89,39 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
|
||||
$nameLength = strlen($entry->getName());
|
||||
$extraLength = strlen($extra);
|
||||
|
||||
// zip align
|
||||
if (
|
||||
$this->zipModel->isZipAlign() &&
|
||||
!$entry->isEncrypted() &&
|
||||
$entry->getMethod() === ZipFileInterface::METHOD_STORED
|
||||
) {
|
||||
$dataAlignmentMultiple = $this->zipModel->getZipAlign();
|
||||
if (StringUtil::endsWith($entry->getName(), '.so')) {
|
||||
$dataAlignmentMultiple = ApkAlignmentExtraField::ANDROID_COMMON_PAGE_ALIGNMENT_BYTES;
|
||||
}
|
||||
$dataMinStartOffset =
|
||||
$offset +
|
||||
ZipEntry::LOCAL_FILE_HEADER_MIN_LEN +
|
||||
$extraLength +
|
||||
$nameLength +
|
||||
ApkAlignmentExtraField::ALIGNMENT_ZIP_EXTRA_MIN_SIZE_BYTES;
|
||||
|
||||
$padding =
|
||||
($dataAlignmentMultiple - ($dataMinStartOffset % $dataAlignmentMultiple))
|
||||
% $dataAlignmentMultiple;
|
||||
|
||||
$alignExtra = new ApkAlignmentExtraField();
|
||||
$alignExtra->setMultiple($dataAlignmentMultiple);
|
||||
$alignExtra->setPadding($padding);
|
||||
|
||||
$extraFieldsCollection = clone $entry->getExtraFieldsCollection();
|
||||
$extraFieldsCollection->add($alignExtra);
|
||||
|
||||
$extra = ExtraFieldsFactory::createSerializedData($extraFieldsCollection);
|
||||
$extraLength = strlen($extra);
|
||||
}
|
||||
|
||||
$size = $nameLength + $extraLength;
|
||||
if (0xffff < $size) {
|
||||
throw new ZipException(
|
||||
@@ -96,20 +131,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
);
|
||||
}
|
||||
|
||||
// zip align
|
||||
$padding = 0;
|
||||
if ($this->zipModel->isZipAlign() && !$entry->isEncrypted() && $entry->getMethod() === ZipFileInterface::METHOD_STORED) {
|
||||
$padding =
|
||||
(
|
||||
$this->zipModel->getZipAlign() -
|
||||
(
|
||||
$offset + ZipEntry::LOCAL_FILE_HEADER_MIN_LEN + $nameLength + $extraLength
|
||||
) % $this->zipModel->getZipAlign()
|
||||
) % $this->zipModel->getZipAlign();
|
||||
}
|
||||
|
||||
$dd = $entry->isDataDescriptorRequired();
|
||||
|
||||
fwrite(
|
||||
$this->out,
|
||||
pack(
|
||||
@@ -134,18 +156,16 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
// file name length 2 bytes
|
||||
$nameLength,
|
||||
// extra field length 2 bytes
|
||||
$extraLength + $padding
|
||||
$extraLength
|
||||
)
|
||||
);
|
||||
fwrite($this->out, $entry->getName());
|
||||
if ($nameLength > 0) {
|
||||
fwrite($this->out, $entry->getName());
|
||||
}
|
||||
if ($extraLength > 0) {
|
||||
fwrite($this->out, $extra);
|
||||
}
|
||||
|
||||
if ($padding > 0) {
|
||||
fwrite($this->out, str_repeat(chr(0), $padding));
|
||||
}
|
||||
|
||||
if ($entry instanceof ZipChangesEntry && !$entry->isChangedContent()) {
|
||||
$entry->getSourceEntry()->getInputStream()->copyEntryData($entry->getSourceEntry(), $this);
|
||||
} elseif (null !== $entryContent) {
|
||||
@@ -203,7 +223,6 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
| ($entry->isDataDescriptorRequired() ? ZipEntry::GPBF_DATA_DESCRIPTOR : 0)
|
||||
| ($utf8 ? ZipEntry::GPBF_UTF8 : 0);
|
||||
|
||||
$skipCrc = false;
|
||||
$entryContent = null;
|
||||
$extraFieldsCollection = $entry->getExtraFieldsCollection();
|
||||
if (!($entry instanceof ZipChangesEntry && !$entry->isChangedContent())) {
|
||||
@@ -213,57 +232,13 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
$entry->setSize(strlen($entryContent));
|
||||
$entry->setCrc(crc32($entryContent));
|
||||
|
||||
if (
|
||||
$encrypted &&
|
||||
(
|
||||
ZipEntry::METHOD_WINZIP_AES === $method ||
|
||||
$entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128 ||
|
||||
$entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192 ||
|
||||
$entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
)
|
||||
) {
|
||||
$field = null;
|
||||
$method = $entry->getMethod();
|
||||
$keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($entry->getEncryptionMethod()); // size bits
|
||||
|
||||
$compressedSize = $entry->getCompressedSize();
|
||||
|
||||
if (ZipEntry::METHOD_WINZIP_AES === $method) {
|
||||
/**
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
$field = $extraFieldsCollection->get(WinZipAesEntryExtraField::getHeaderId());
|
||||
if (null !== $field) {
|
||||
$method = $field->getMethod();
|
||||
if (ZipEntry::UNKNOWN !== $compressedSize) {
|
||||
$compressedSize -= $field->getKeyStrength() / 2 // salt value
|
||||
+ 2 // password verification value
|
||||
+ 10; // authentication code
|
||||
}
|
||||
$entry->setMethod($method);
|
||||
}
|
||||
}
|
||||
if (null === $field) {
|
||||
$field = ExtraFieldsFactory::createWinZipAesEntryExtra();
|
||||
}
|
||||
$field->setKeyStrength($keyStrength);
|
||||
$field->setMethod($method);
|
||||
$size = $entry->getSize();
|
||||
if (20 <= $size && ZipFileInterface::METHOD_BZIP2 !== $method) {
|
||||
$field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_1);
|
||||
} else {
|
||||
$field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_2);
|
||||
$skipCrc = true;
|
||||
}
|
||||
$extraFieldsCollection->add($field);
|
||||
if (ZipEntry::UNKNOWN !== $compressedSize) {
|
||||
$compressedSize += $field->getKeyStrength() / 2 // salt value
|
||||
+ 2 // password verification value
|
||||
+ 10; // authentication code
|
||||
$entry->setCompressedSize($compressedSize);
|
||||
}
|
||||
if ($skipCrc) {
|
||||
$entry->setCrc(0);
|
||||
if ($encrypted && ZipEntry::METHOD_WINZIP_AES === $method) {
|
||||
/**
|
||||
* @var WinZipAesEntryExtraField $field
|
||||
*/
|
||||
$field = $extraFieldsCollection->get(WinZipAesEntryExtraField::getHeaderId());
|
||||
if (null !== $field) {
|
||||
$method = $field->getMethod();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,14 +292,23 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
}
|
||||
|
||||
if ($encrypted) {
|
||||
if (
|
||||
$entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128 ||
|
||||
$entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192 ||
|
||||
$entry->getEncryptionMethod() === ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256
|
||||
) {
|
||||
if ($skipCrc) {
|
||||
if (in_array($entry->getEncryptionMethod(), [
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_128,
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_192,
|
||||
ZipFileInterface::ENCRYPTION_METHOD_WINZIP_AES_256,
|
||||
], true)) {
|
||||
$keyStrength = WinZipAesEntryExtraField::getKeyStrangeFromEncryptionMethod($entry->getEncryptionMethod()); // size bits
|
||||
$field = ExtraFieldsFactory::createWinZipAesEntryExtra();
|
||||
$field->setKeyStrength($keyStrength);
|
||||
$field->setMethod($method);
|
||||
$size = $entry->getSize();
|
||||
if (20 <= $size && ZipFileInterface::METHOD_BZIP2 !== $method) {
|
||||
$field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_1);
|
||||
} else {
|
||||
$field->setVendorVersion(WinZipAesEntryExtraField::VV_AE_2);
|
||||
$entry->setCrc(0);
|
||||
}
|
||||
$extraFieldsCollection->add($field);
|
||||
$entry->setMethod(ZipEntry::METHOD_WINZIP_AES);
|
||||
|
||||
$winZipAesEngine = new WinZipAesEngine($entry);
|
||||
@@ -355,6 +339,7 @@ class ZipOutputStream implements ZipOutputStreamInterface
|
||||
* @param ZipEntry $entry
|
||||
* @param string $content
|
||||
* @return string
|
||||
* @throws ZipException
|
||||
*/
|
||||
protected function determineBestCompressionMethod(ZipEntry $entry, $content)
|
||||
{
|
||||
|
@@ -1299,11 +1299,11 @@ class ZipFile implements ZipFileInterface
|
||||
}
|
||||
|
||||
$stream = new ResponseStream($handle);
|
||||
$response->withHeader('Content-Type', $mimeType);
|
||||
$response->withHeader('Content-Disposition', $contentDispositionValue);
|
||||
$response->withHeader('Content-Length', $stream->getSize());
|
||||
$response->withBody($stream);
|
||||
return $response;
|
||||
return $response
|
||||
->withHeader('Content-Type', $mimeType)
|
||||
->withHeader('Content-Disposition', $contentDispositionValue)
|
||||
->withHeader('Content-Length', $stream->getSize())
|
||||
->withBody($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -75,7 +75,6 @@ class PhpZipExtResourceTest extends ZipTestCase
|
||||
{
|
||||
return [
|
||||
[__DIR__ . '/php-zip-ext-test-resources/bug40228.zip'],
|
||||
[__DIR__ . '/php-zip-ext-test-resources/bug40228私はガラスを食べられます.zip'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -9,28 +9,15 @@ use PhpZip\Util\CryptoUtil;
|
||||
*/
|
||||
class ZipAlignTest extends ZipTestCase
|
||||
{
|
||||
public function testApkAlignedAndReSave()
|
||||
{
|
||||
$filename = __DIR__ . '/resources/test.apk';
|
||||
|
||||
self::assertCorrectZipArchive($filename);
|
||||
self::doZipAlignVerify($this->outputFilename);
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->openFile($filename);
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
$zipFile->close();
|
||||
|
||||
self::assertCorrectZipArchive($this->outputFilename);
|
||||
self::doZipAlignVerify($this->outputFilename);
|
||||
}
|
||||
|
||||
public function testApkAlignedAndSetZipAlignAndReSave()
|
||||
{
|
||||
$filename = __DIR__ . '/resources/test.apk';
|
||||
|
||||
self::assertCorrectZipArchive($filename);
|
||||
self::doZipAlignVerify($this->outputFilename);
|
||||
$result = self::doZipAlignVerify($filename);
|
||||
if (null !== $result) {
|
||||
self::assertTrue($result);
|
||||
}
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->openFile($filename);
|
||||
@@ -39,7 +26,10 @@ class ZipAlignTest extends ZipTestCase
|
||||
$zipFile->close();
|
||||
|
||||
self::assertCorrectZipArchive($this->outputFilename);
|
||||
self::doZipAlignVerify($this->outputFilename);
|
||||
$result = self::doZipAlignVerify($this->outputFilename, true);
|
||||
if (null !== $result) {
|
||||
self::assertTrue($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -7,6 +7,7 @@ use PhpZip\Model\ZipInfo;
|
||||
use PhpZip\Util\CryptoUtil;
|
||||
use PhpZip\Util\FilesUtil;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Zend\Diactoros\Response;
|
||||
|
||||
/**
|
||||
* ZipFile test
|
||||
@@ -1672,7 +1673,7 @@ class ZipFileTest extends ZipTestCase
|
||||
|
||||
/**
|
||||
* @expectedException \PhpZip\Exception\ZipNotFoundEntry
|
||||
* @expectedExceptionMessage Zip entry bad entry name not found
|
||||
* @expectedExceptionMessage Zip entry "bad entry name" not found
|
||||
*/
|
||||
public function testNotFoundEntry()
|
||||
{
|
||||
@@ -1786,9 +1787,10 @@ class ZipFileTest extends ZipTestCase
|
||||
$zipFile[$i] = $i;
|
||||
}
|
||||
$filename = 'file.jar';
|
||||
$response = $this->getMock(ResponseInterface::class);
|
||||
$response = $zipFile->outputAsResponse($response, $filename);
|
||||
$response = $zipFile->outputAsResponse(new Response(), $filename);
|
||||
$this->assertInstanceOf(ResponseInterface::class, $response);
|
||||
$this->assertEquals('application/java-archive', $response->getHeaderLine('content-type'));
|
||||
$this->assertEquals('attachment; filename="file.jar"', $response->getHeaderLine('content-disposition'));
|
||||
}
|
||||
|
||||
public function testCompressionLevel()
|
||||
|
@@ -346,4 +346,54 @@ class ZipPasswordTest extends ZipFileAddDirTest
|
||||
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/Ne-Lexa/php-zip/issues/9
|
||||
*/
|
||||
public function testIssues9()
|
||||
{
|
||||
$contents = str_pad('', 1000, 'test;test2;test3' . PHP_EOL, STR_PAD_RIGHT);
|
||||
$password = base64_encode(CryptoUtil::randomBytes(20));
|
||||
|
||||
$encryptMethod = ZipFile::ENCRYPTION_METHOD_WINZIP_AES_256;
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile
|
||||
->addFromString('codes.csv', $contents)
|
||||
->setPassword($password, $encryptMethod)
|
||||
->saveAsFile($this->outputFilename)
|
||||
->close();
|
||||
|
||||
$this->assertCorrectZipArchive($this->outputFilename, $password);
|
||||
|
||||
$zipFile->openFile($this->outputFilename);
|
||||
$zipFile->setReadPassword($password);
|
||||
$this->assertEquals($zipFile['codes.csv'], $contents);
|
||||
$zipFile->close();
|
||||
}
|
||||
|
||||
public function testReadAesEncryptedAndRewriteArchive()
|
||||
{
|
||||
$file = __DIR__ . '/resources/aes_password_archive.zip';
|
||||
$password = '1234567890';
|
||||
|
||||
$zipFile = new ZipFile();
|
||||
$zipFile->openFile($file);
|
||||
$zipFile->setReadPassword($password);
|
||||
$zipFile->setEntryComment('contents.txt', 'comment'); // change entry, but not changed contents
|
||||
$zipFile->saveAsFile($this->outputFilename);
|
||||
|
||||
$zipFile2 = new ZipFile();
|
||||
$zipFile2->openFile($this->outputFilename);
|
||||
$zipFile2->setReadPassword($password);
|
||||
$this->assertEquals($zipFile2->getListFiles(), $zipFile->getListFiles());
|
||||
foreach ($zipFile as $name => $contents) {
|
||||
$this->assertNotEmpty($name);
|
||||
$this->assertNotEmpty($contents);
|
||||
$this->assertContains('test contents', $contents);
|
||||
$this->assertEquals($zipFile2[$name], $contents);
|
||||
}
|
||||
$zipFile2->close();
|
||||
|
||||
$zipFile->close();
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
BIN
tests/PhpZip/resources/aes_password_archive.zip
Normal file
BIN
tests/PhpZip/resources/aes_password_archive.zip
Normal file
Binary file not shown.
Reference in New Issue
Block a user