Skip to content

Commit

Permalink
Merge branch 'hotfix/3.1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
Ne-Lexa committed Nov 17, 2017
2 parents d67fc4d + 4979829 commit 7d73ac4
Show file tree
Hide file tree
Showing 13 changed files with 381 additions and 102 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
8 changes: 4 additions & 4 deletions README.RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`, когда это возможно.
+ Установка пароля для чтения архива глобально или для некоторых записей.
+ Изменение пароля архива, в том числе и для отдельных записей.
+ Удаление пароля архива глобально или для отдельных записей.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
73 changes: 73 additions & 0 deletions src/PhpZip/Extra/ExtraFieldsFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,6 +29,56 @@ private function __construct()
{
}

/**
* @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.
Expand Down Expand Up @@ -69,6 +122,8 @@ protected static function getRegistry()
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;
}
Expand Down Expand Up @@ -97,4 +152,22 @@ public static function createZip64Extra(ZipEntry $entry)
{
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 changes: 112 additions & 0 deletions src/PhpZip/Extra/Fields/ApkAlignmentExtraField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace PhpZip\Extra\Fields;

use PhpZip\Exception\InvalidArgumentException;
use PhpZip\Extra\ExtraField;

/**
* Apk Alignment Extra Field
*
* @author Ne-Lexa [email protected]
* @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 changes: 51 additions & 0 deletions src/PhpZip/Extra/Fields/JarMarkerExtraField.php
Original file line number Diff line number Diff line change
@@ -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 [email protected]
* @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");
}
}
}
37 changes: 2 additions & 35 deletions src/PhpZip/Model/Entry/ZipAbstractEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
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;
Expand Down Expand Up @@ -585,18 +584,7 @@ public function &getExtraFieldsCollection()
*/
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);
}

/**
Expand All @@ -612,28 +600,7 @@ public function getExtra()
*/
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);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/PhpZip/Model/Entry/ZipNewEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public function getVersionNeededToExtract()
$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)
)
);
Expand Down
1 change: 0 additions & 1 deletion src/PhpZip/Model/ZipEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace PhpZip\Model;

use PhpZip\Exception\ZipException;

use PhpZip\Extra\ExtraFieldsCollection;
use PhpZip\ZipFileInterface;

Expand Down
Loading

0 comments on commit 7d73ac4

Please sign in to comment.