mirror of
https://github.com/Ne-Lexa/php-zip.git
synced 2025-08-24 07:52:48 +02:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
251ce11bdc | ||
|
f969e59319 | ||
|
6808e4ffdc | ||
|
91f08b9f55 | ||
|
d0cf7f7d1d | ||
|
9f0d151f5e | ||
|
171d4a8e4c | ||
|
aa09b24d02 | ||
|
c34f90ac18 |
@@ -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"
|
||||
}
|
||||
|
@@ -255,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -446,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:
|
||||
|
@@ -18,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');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -223,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())) {
|
||||
@@ -233,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -337,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);
|
||||
@@ -375,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'],
|
||||
];
|
||||
}
|
||||
|
||||
|
@@ -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