diff --git a/composer.json b/composer.json index 279658eea..05ef4bba9 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,7 @@ "aws/aws-sdk-php": "^3.35", "bantu/ini-get-wrapper": "v1.0.1", "christophwurst/id3parser": "^0.1.1", - "deepdiver/zipstreamer": "1.1.1", + "deepdiver/zipstreamer": "2.0.0", "deepdiver1975/tarstreamer": "v2.0.0", "doctrine/dbal": "2.12.0", "guzzlehttp/guzzle": "6.5.2", diff --git a/composer.lock b/composer.lock index ae48b3e8c..0fe3fac2e 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9f6e2f20fbb836e19a3c1b58de400d30", + "content-hash": "fda20470dd90716b8a28eab45fe2b56a", "packages": [ { "name": "aws/aws-sdk-php", @@ -220,16 +220,16 @@ }, { "name": "deepdiver/zipstreamer", - "version": "1.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/DeepDiver1975/PHPZipStreamer.git", - "reference": "c8e73ca3204bd0e06abdb0bc533f073b616d6e47" + "reference": "b8c59647ff34fb97e8937aefb2a65de2bc4b4755" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DeepDiver1975/PHPZipStreamer/zipball/c8e73ca3204bd0e06abdb0bc533f073b616d6e47", - "reference": "c8e73ca3204bd0e06abdb0bc533f073b616d6e47", + "url": "https://api.github.com/repos/DeepDiver1975/PHPZipStreamer/zipball/b8c59647ff34fb97e8937aefb2a65de2bc4b4755", + "reference": "b8c59647ff34fb97e8937aefb2a65de2bc4b4755", "shasum": "" }, "require": { @@ -281,7 +281,11 @@ "stream", "zip" ], - "time": "2018-03-26T14:48:40+00:00" + "support": { + "issues": "https://github.com/DeepDiver1975/PHPZipStreamer/issues", + "source": "https://github.com/DeepDiver1975/PHPZipStreamer/tree/master" + }, + "time": "2020-07-21T07:45:14+00:00" }, { "name": "deepdiver1975/tarstreamer", diff --git a/composer/InstalledVersions.php b/composer/InstalledVersions.php index df406ee6b..4617d9511 100644 --- a/composer/InstalledVersions.php +++ b/composer/InstalledVersions.php @@ -29,7 +29,7 @@ class InstalledVersions 'aliases' => array ( ), - 'reference' => '81a9fcefae15dfe4fb7c581ff99708e176c7a88d', + 'reference' => '455f47df98504ef1336cd9e4c5b59c60903ebb22', 'name' => 'nextcloud/3rdparty', ), 'versions' => @@ -72,12 +72,12 @@ class InstalledVersions ), 'deepdiver/zipstreamer' => array ( - 'pretty_version' => '1.1.1', - 'version' => '1.1.1.0', + 'pretty_version' => '2.0.0', + 'version' => '2.0.0.0', 'aliases' => array ( ), - 'reference' => 'c8e73ca3204bd0e06abdb0bc533f073b616d6e47', + 'reference' => 'b8c59647ff34fb97e8937aefb2a65de2bc4b4755', ), 'deepdiver1975/tarstreamer' => array ( @@ -284,7 +284,7 @@ class InstalledVersions 'aliases' => array ( ), - 'reference' => '81a9fcefae15dfe4fb7c581ff99708e176c7a88d', + 'reference' => '455f47df98504ef1336cd9e4c5b59c60903ebb22', ), 'nextcloud/lognormalizer' => array ( diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 39453db69..0cff536ab 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -2826,6 +2826,11 @@ 'Webauthn\\TrustPath\\TrustPath' => $vendorDir . '/web-auth/webauthn-lib/src/TrustPath/TrustPath.php', 'Webauthn\\TrustPath\\TrustPathLoader' => $vendorDir . '/web-auth/webauthn-lib/src/TrustPath/TrustPathLoader.php', 'Webauthn\\Util\\CoseSignatureFixer' => $vendorDir . '/web-auth/webauthn-lib/src/Util/CoseSignatureFixer.php', + 'ZipStreamer\\COMPR' => $vendorDir . '/deepdiver/zipstreamer/src/COMPR.php', + 'ZipStreamer\\Count64' => $vendorDir . '/deepdiver/zipstreamer/src/Count64.php', + 'ZipStreamer\\Lib\\Count64Base' => $vendorDir . '/deepdiver/zipstreamer/src/Lib/Count64Base.php', + 'ZipStreamer\\Lib\\Count64_32' => $vendorDir . '/deepdiver/zipstreamer/src/Lib/Count64_32.php', + 'ZipStreamer\\Lib\\Count64_64' => $vendorDir . '/deepdiver/zipstreamer/src/Lib/Count64_64.php', 'ZipStreamer\\ZipStreamer' => $vendorDir . '/deepdiver/zipstreamer/src/ZipStreamer.php', 'bantu\\IniGetWrapper\\IniGetWrapper' => $vendorDir . '/bantu/ini-get-wrapper/src/IniGetWrapper.php', 'libphonenumber\\AlternateFormatsCountryCodeSet' => $vendorDir . '/giggsey/libphonenumber-for-php/src/AlternateFormatsCountryCodeSet.php', diff --git a/composer/autoload_static.php b/composer/autoload_static.php index 14ae0b7e8..154c4692c 100644 --- a/composer/autoload_static.php +++ b/composer/autoload_static.php @@ -3327,6 +3327,11 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652 'Webauthn\\TrustPath\\TrustPath' => __DIR__ . '/..' . '/web-auth/webauthn-lib/src/TrustPath/TrustPath.php', 'Webauthn\\TrustPath\\TrustPathLoader' => __DIR__ . '/..' . '/web-auth/webauthn-lib/src/TrustPath/TrustPathLoader.php', 'Webauthn\\Util\\CoseSignatureFixer' => __DIR__ . '/..' . '/web-auth/webauthn-lib/src/Util/CoseSignatureFixer.php', + 'ZipStreamer\\COMPR' => __DIR__ . '/..' . '/deepdiver/zipstreamer/src/COMPR.php', + 'ZipStreamer\\Count64' => __DIR__ . '/..' . '/deepdiver/zipstreamer/src/Count64.php', + 'ZipStreamer\\Lib\\Count64Base' => __DIR__ . '/..' . '/deepdiver/zipstreamer/src/Lib/Count64Base.php', + 'ZipStreamer\\Lib\\Count64_32' => __DIR__ . '/..' . '/deepdiver/zipstreamer/src/Lib/Count64_32.php', + 'ZipStreamer\\Lib\\Count64_64' => __DIR__ . '/..' . '/deepdiver/zipstreamer/src/Lib/Count64_64.php', 'ZipStreamer\\ZipStreamer' => __DIR__ . '/..' . '/deepdiver/zipstreamer/src/ZipStreamer.php', 'bantu\\IniGetWrapper\\IniGetWrapper' => __DIR__ . '/..' . '/bantu/ini-get-wrapper/src/IniGetWrapper.php', 'libphonenumber\\AlternateFormatsCountryCodeSet' => __DIR__ . '/..' . '/giggsey/libphonenumber-for-php/src/AlternateFormatsCountryCodeSet.php', diff --git a/composer/installed.json b/composer/installed.json index 45e03142d..5d4c271bd 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -226,17 +226,17 @@ }, { "name": "deepdiver/zipstreamer", - "version": "1.1.1", - "version_normalized": "1.1.1.0", + "version": "2.0.0", + "version_normalized": "2.0.0.0", "source": { "type": "git", "url": "https://github.com/DeepDiver1975/PHPZipStreamer.git", - "reference": "c8e73ca3204bd0e06abdb0bc533f073b616d6e47" + "reference": "b8c59647ff34fb97e8937aefb2a65de2bc4b4755" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/DeepDiver1975/PHPZipStreamer/zipball/c8e73ca3204bd0e06abdb0bc533f073b616d6e47", - "reference": "c8e73ca3204bd0e06abdb0bc533f073b616d6e47", + "url": "https://api.github.com/repos/DeepDiver1975/PHPZipStreamer/zipball/b8c59647ff34fb97e8937aefb2a65de2bc4b4755", + "reference": "b8c59647ff34fb97e8937aefb2a65de2bc4b4755", "shasum": "" }, "require": { @@ -245,7 +245,7 @@ "require-dev": { "phpunit/phpunit": "^5.7" }, - "time": "2018-03-26T14:48:40+00:00", + "time": "2020-07-21T07:45:14+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -290,6 +290,10 @@ "stream", "zip" ], + "support": { + "issues": "https://github.com/DeepDiver1975/PHPZipStreamer/issues", + "source": "https://github.com/DeepDiver1975/PHPZipStreamer/tree/master" + }, "install-path": "../deepdiver/zipstreamer" }, { @@ -5242,6 +5246,6 @@ "install-path": "../web-auth/webauthn-lib" } ], - "dev": true, + "dev": false, "dev-package-names": [] } diff --git a/composer/installed.php b/composer/installed.php index 806984f56..d7d34c8b7 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -6,7 +6,7 @@ 'aliases' => array ( ), - 'reference' => '81a9fcefae15dfe4fb7c581ff99708e176c7a88d', + 'reference' => '455f47df98504ef1336cd9e4c5b59c60903ebb22', 'name' => 'nextcloud/3rdparty', ), 'versions' => @@ -49,12 +49,12 @@ ), 'deepdiver/zipstreamer' => array ( - 'pretty_version' => '1.1.1', - 'version' => '1.1.1.0', + 'pretty_version' => '2.0.0', + 'version' => '2.0.0.0', 'aliases' => array ( ), - 'reference' => 'c8e73ca3204bd0e06abdb0bc533f073b616d6e47', + 'reference' => 'b8c59647ff34fb97e8937aefb2a65de2bc4b4755', ), 'deepdiver1975/tarstreamer' => array ( @@ -261,7 +261,7 @@ 'aliases' => array ( ), - 'reference' => '81a9fcefae15dfe4fb7c581ff99708e176c7a88d', + 'reference' => '455f47df98504ef1336cd9e4c5b59c60903ebb22', ), 'nextcloud/lognormalizer' => array ( diff --git a/deepdiver/zipstreamer/.gitignore b/deepdiver/zipstreamer/.gitignore new file mode 100644 index 000000000..44a9f6e10 --- /dev/null +++ b/deepdiver/zipstreamer/.gitignore @@ -0,0 +1,3 @@ +/composer.phar +/vendor/ +/composer.lock diff --git a/deepdiver/zipstreamer/composer.json b/deepdiver/zipstreamer/composer.json index 85992b974..bdab47283 100644 --- a/deepdiver/zipstreamer/composer.json +++ b/deepdiver/zipstreamer/composer.json @@ -5,7 +5,7 @@ "keywords": ["zip", "stream"], "homepage": "https://github.com/DeepDiver1975/PHPZipStreamer", "license": "GPL-3.0+", - "version": "1.1.1", + "version": "2.0.0", "authors": [ { "name": "Nicolai Ehemann", "email": "en@enlightened.de", diff --git a/deepdiver/zipstreamer/src/COMPR.php b/deepdiver/zipstreamer/src/COMPR.php new file mode 100644 index 000000000..664b679bf --- /dev/null +++ b/deepdiver/zipstreamer/src/COMPR.php @@ -0,0 +1,36 @@ +. + * + * @author Nicolai Ehemann + * @author André Rothe + * @copyright Copyright (C) 2013-2015 Nicolai Ehemann and contributors + * @license GNU GPL + * @version 1.0 + */ +namespace ZipStreamer; + +class COMPR { + // compression method + const STORE = 0x0000; // 0 - The file is stored (no compression) + const DEFLATE = 0x0008; // 8 - The file is deflated + + // compression level (for deflate compression) + const NONE = 0; + const NORMAL = 1; + const MAXIMUM = 2; + const SUPERFAST = 3; +} diff --git a/deepdiver/zipstreamer/src/Count64.php b/deepdiver/zipstreamer/src/Count64.php new file mode 100644 index 000000000..a7a0fef1d --- /dev/null +++ b/deepdiver/zipstreamer/src/Count64.php @@ -0,0 +1,156 @@ +. + * + * @author Nicolai Ehemann + * @copyright Copyright (C) 2013-2014 Nicolai Ehemann and contributors + * @license GNU GPL + */ +namespace ZipStreamer; + +use \ZipStreamer\Lib\Count64_32; +use \ZipStreamer\Lib\Count64_64; + +const INT64_HIGH_MAP = 0xffffffff00000000; +const INT64_LOW_MAP = 0x00000000ffffffff; +const INT_MAX_32 = 0xffffffff; + +/** + * Unsigned right shift + * + * @param int $bits integer to be shifted + * @param int $shift number of bits to be shifted + * @return int shifted integer + */ +function urShift($bits, $shift) { + if ($shift == 0) { + return $bits; + } + return ($bits >> $shift) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($shift - 1)); +} + +/** + * Convert binary data string to readable hex string + * + * @param string $data binary string + * @return string readable hex string + */ +function byte2hex($data) { + return unpack("h*", $data); +} + +/** + * Pack 1 byte data into binary string + * + * @param mixed $data data + * @return string 1 byte binary string + */ +function pack8($data) { + return pack('C', $data); +} + +/** + * Pack 2 byte data into binary string, little endian format + * + * @param mixed $data data + * @return string 2 byte binary string + */ +function pack16le($data) { + return pack('v', $data); +} + +/** + * Unpack 2 byte binary string, little endian format to 2 byte data + * + * @param string $data binary string + * @return integer 2 byte data + */ +function unpack16le($data) { + $result = unpack('v', $data); + return $result[1]; +} + +/** + * Pack 4 byte data into binary string, little endian format + * + * @param mixed $data data + * @return 4 byte binary string + */ +function pack32le($data) { + return pack('V', $data); +} + +/** + * Unpack 4 byte binary string, little endian format to 4 byte data + * + * @param string $data binary string + * @return integer 4 byte data + */ +function unpack32le($data) { + $result = unpack('V', $data); + return $result[1]; +} + +/** + * Pack 8 byte data into binary string, little endian format + * + * @param mixed $data data + * @return string 8 byte binary string + */ +function pack64le($data) { + if (is_object($data)) { + if ("Count64_32" == get_class($data)) { + $value = $data->_getValue(); + $hiBytess = $value[0]; + $loBytess = $value[1]; + } else { + $hiBytess = ($data->_getValue() & INT64_HIGH_MAP) >> 32; + $loBytess = $data->_getValue() & INT64_LOW_MAP; + } + } else if (4 == PHP_INT_SIZE) { + $hiBytess = 0; + $loBytess = $data; + } else { + $hiBytess = ($data & INT64_HIGH_MAP) >> 32; + $loBytess = $data & INT64_LOW_MAP; + } + return pack('VV', $loBytess, $hiBytess); +} + +/** + * Unpack 8 byte binary string, little endian format to 8 byte data + * + * @param string $data binary string + * @return Count64Base data + */ +function unpack64le($data) { + $bytes = unpack('V2', $data); + return Count64::construct(array( + $bytes[1], + $bytes[2] + )); +} + +abstract class Count64 { + public static function construct($value = 0, $limit32Bit = False) { + if (4 == PHP_INT_SIZE) { + return new Count64_32($value, $limit32Bit); + } else { + return new Count64_64($value, $limit32Bit); + } + } +} diff --git a/deepdiver/zipstreamer/src/Lib/Count64Base.php b/deepdiver/zipstreamer/src/Lib/Count64Base.php new file mode 100644 index 000000000..7ae5dcd69 --- /dev/null +++ b/deepdiver/zipstreamer/src/Lib/Count64Base.php @@ -0,0 +1,42 @@ +. + * + * @author Nicolai Ehemann + * @copyright Copyright (C) 2013-2014 Nicolai Ehemann and contributors + * @license GNU GPL + */ +namespace ZipStreamer\Lib; + +abstract class Count64Base { + protected $limit32Bit = False; + + function __construct($value = 0, $limit32Bit = False) { + $this->limit32Bit = $limit32Bit; + $this->set($value); + } + + abstract public function set($value); + abstract public function add($value); + abstract public function getHiBytes(); + abstract public function getLoBytes(); + abstract public function _getValue(); + + const EXCEPTION_SET_INVALID_ARGUMENT = "Count64 object can only be set() to integer or Count64 values"; + const EXCEPTION_ADD_INVALID_ARGUMENT = "Count64 object can only be add()ed integer or Count64 values"; + const EXCEPTION_32BIT_OVERFLOW = "Count64 object limited to 32 bit (overflow)"; +} diff --git a/deepdiver/zipstreamer/src/Lib/Count64_32.php b/deepdiver/zipstreamer/src/Lib/Count64_32.php new file mode 100644 index 000000000..e8c7ceb85 --- /dev/null +++ b/deepdiver/zipstreamer/src/Lib/Count64_32.php @@ -0,0 +1,96 @@ +. + * + * @author Nicolai Ehemann + * @copyright Copyright (C) 2013-2014 Nicolai Ehemann and contributors + * @license GNU GPL + */ +namespace ZipStreamer\Lib; + +class Count64_32 extends Count64Base { + private $loBytes; + private $hiBytes; + + public function getHiBytes() { + return $this->hiBytes; + } + + public function getLoBytes() { + return $this->loBytes; + } + + public function _getValue() { + return array($this->hiBytes, $this->loBytes); + } + + public function set($value) { + if (is_int($value)) { + $this->loBytes = $value; + $this->hiBytes = 0; + } else if (is_array($value) && 2 == sizeof($value)) { + $this->loBytes = $value[0]; + if ($this->limit32Bit && 0 !== $value[1]) { + throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->hiBytes = $value[1]; + } else if (is_object($value) && __CLASS__ == get_class($value)) { + $value = $value->_getValue(); + if ($this->limit32Bit && 0 !== $value[0]) { + throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->hiBytes = $value[0]; + $this->loBytes = $value[1]; + } else { + throw new \InvalidArgumentException(self::EXCEPTION_SET_INVALID_ARGUMENT); + } + return $this; + } + + public function add($value) { + if (is_int($value)) { + $sum = (int) ($this->loBytes + $value); + // overflow! + if (($this->loBytes > -1 && $sum < $this->loBytes && $sum > -1) + || ($this->loBytes < 0 && ($sum < $this->loBytes || $sum > -1))) { + if ($this->limit32Bit) { + throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->hiBytes = (int) ($this->hiBytes + 1); + } + $this->loBytes = $sum; + } else if (is_object($value) && __CLASS__ == get_class($value)) { + $value = $value->_getValue(); + $sum = (int) ($this->loBytes + $value[1]); + if (($this->loBytes > -1 && $sum < $this->loBytes && $sum > -1) + || ($this->loBytes < 0 && ($sum < $this->loBytes || $sum > -1))) { + if ($this->limit32Bit) { + throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->hiBytes = (int) ($this->hiBytes + 1); + } + $this->loBytes = $sum; + if ($this->limit32Bit && 0 !== $value[0]) { + throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->hiBytes = (int) ($this->hiBytes + $value[0]); + } else { + throw new \InvalidArgumentException(self::EXCEPTION_ADD_INVALID_ARGUMENT); + } + return $this; + } +} diff --git a/deepdiver/zipstreamer/src/Lib/Count64_64.php b/deepdiver/zipstreamer/src/Lib/Count64_64.php new file mode 100644 index 000000000..1a31741ca --- /dev/null +++ b/deepdiver/zipstreamer/src/Lib/Count64_64.php @@ -0,0 +1,83 @@ +. + * + * @author Nicolai Ehemann + * @copyright Copyright (C) 2013-2014 Nicolai Ehemann and contributors + * @license GNU GPL + */ +namespace ZipStreamer\Lib; + +use const \ZipStreamer\INT64_LOW_MAP; +use const \ZipStreamer\INT_MAX_32; + +class Count64_64 extends Count64Base { + private $value; + + public function getHiBytes() { + return urShift($this->value, 32); + } + + public function getLoBytes() { + return $this->value & INT64_LOW_MAP; + } + + public function _getValue() { + return $this->value; + } + + public function set($value) { + if (is_int($value)) { + if ($this->limit32Bit && INT_MAX_32 < $value) { + throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->value = $value; + } else if (is_array($value) && 2 == sizeof($value)) { + if ($this->limit32Bit && 0 !== $value[1]) { + throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->value = $value[1]; + $this->value = $this->value << 32; + $this->value = $this->value + $value[0]; + } else if (is_object($value) && __CLASS__ == get_class($value)) { + $value = $value->_getValue(); + if ($this->limit32Bit && INT_MAX_32 < $value) { + throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->value = $value; + + } else { + throw new \InvalidArgumentException(self::EXCEPTION_SET_INVALID_ARGUMENT); + } + return $this; + } + + public function add($value) { + if (is_int($value)) { + $sum = (int) ($this->value + $value); + } else if (is_object($value) && __CLASS__ == get_class($value)) { + $sum = (int) ($this->value + $value->_getValue()); + } else { + throw new \InvalidArgumentException(self::EXCEPTION_ADD_INVALID_ARGUMENT); + } + if ($this->limit32Bit && INT_MAX_32 < $sum) { + throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); + } + $this->value = $sum; + return $this; + } +} diff --git a/deepdiver/zipstreamer/src/ZipStreamer.php b/deepdiver/zipstreamer/src/ZipStreamer.php index 656413be6..4542c3fa9 100644 --- a/deepdiver/zipstreamer/src/ZipStreamer.php +++ b/deepdiver/zipstreamer/src/ZipStreamer.php @@ -31,20 +31,6 @@ */ namespace ZipStreamer; -require_once __DIR__ . "/lib/Count64.php"; - -class COMPR { - // compression method - const STORE = 0x0000; // 0 - The file is stored (no compression) - const DEFLATE = 0x0008; // 8 - The file is deflated - - // compression level (for deflate compression) - const NONE = 0; - const NORMAL = 1; - const MAXIMUM = 2; - const SUPERFAST = 3; -} - class ZipStreamer { const VERSION = "1.0"; diff --git a/deepdiver/zipstreamer/src/lib/Count64.php b/deepdiver/zipstreamer/src/lib/Count64.php deleted file mode 100644 index 4db8bae8c..000000000 --- a/deepdiver/zipstreamer/src/lib/Count64.php +++ /dev/null @@ -1,302 +0,0 @@ -. - * - * @author Nicolai Ehemann - * @copyright Copyright (C) 2013-2014 Nicolai Ehemann and contributors - * @license GNU GPL - */ -namespace ZipStreamer; - -const INT64_HIGH_MAP = 0xffffffff00000000; -const INT64_LOW_MAP = 0x00000000ffffffff; -const INT_MAX_32 = 0xffffffff; - -/** - * Unsigned right shift - * - * @param int $bits integer to be shifted - * @param int $shift number of bits to be shifted - * @return int shifted integer - */ -function urShift($bits, $shift) { - if ($shift == 0) { - return $bits; - } - return ($bits >> $shift) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($shift - 1)); -} - -/** - * Convert binary data string to readable hex string - * - * @param string $data binary string - * @return string readable hex string - */ -function byte2hex($data) { - return unpack("h*", $data); -} - -/** - * Pack 1 byte data into binary string - * - * @param mixed $data data - * @return string 1 byte binary string - */ -function pack8($data) { - return pack('C', $data); -} - -/** - * Pack 2 byte data into binary string, little endian format - * - * @param mixed $data data - * @return string 2 byte binary string - */ -function pack16le($data) { - return pack('v', $data); -} - -/** - * Unpack 2 byte binary string, little endian format to 2 byte data - * - * @param string $data binary string - * @return integer 2 byte data - */ -function unpack16le($data) { - $result = unpack('v', $data); - return $result[1]; -} - -/** - * Pack 4 byte data into binary string, little endian format - * - * @param mixed $data data - * @return 4 byte binary string - */ -function pack32le($data) { - return pack('V', $data); -} - -/** - * Unpack 4 byte binary string, little endian format to 4 byte data - * - * @param string $data binary string - * @return integer 4 byte data - */ -function unpack32le($data) { - $result = unpack('V', $data); - return $result[1]; -} - -/** - * Pack 8 byte data into binary string, little endian format - * - * @param mixed $data data - * @return string 8 byte binary string - */ -function pack64le($data) { - if (is_object($data)) { - if ("Count64_32" == get_class($data)) { - $value = $data->_getValue(); - $hiBytess = $value[0]; - $loBytess = $value[1]; - } else { - $hiBytess = ($data->_getValue() & INT64_HIGH_MAP) >> 32; - $loBytess = $data->_getValue() & INT64_LOW_MAP; - } - } else if (4 == PHP_INT_SIZE) { - $hiBytess = 0; - $loBytess = $data; - } else { - $hiBytess = ($data & INT64_HIGH_MAP) >> 32; - $loBytess = $data & INT64_LOW_MAP; - } - return pack('VV', $loBytess, $hiBytess); -} - -/** - * Unpack 8 byte binary string, little endian format to 8 byte data - * - * @param string $data binary string - * @return Count64Base data - */ -function unpack64le($data) { - $bytes = unpack('V2', $data); - return Count64::construct(array( - $bytes[1], - $bytes[2] - )); -} - -abstract class Count64Base { - protected $limit32Bit = False; - - function __construct($value = 0, $limit32Bit = False) { - $this->limit32Bit = $limit32Bit; - $this->set($value); - } - - abstract public function set($value); - abstract public function add($value); - abstract public function getHiBytes(); - abstract public function getLoBytes(); - abstract public function _getValue(); - - const EXCEPTION_SET_INVALID_ARGUMENT = "Count64 object can only be set() to integer or Count64 values"; - const EXCEPTION_ADD_INVALID_ARGUMENT = "Count64 object can only be add()ed integer or Count64 values"; - const EXCEPTION_32BIT_OVERFLOW = "Count64 object limited to 32 bit (overflow)"; -} - -class Count64_32 extends Count64Base { - private $loBytes; - private $hiBytes; - - public function getHiBytes() { - return $this->hiBytes; - } - - public function getLoBytes() { - return $this->loBytes; - } - - public function _getValue() { - return array($this->hiBytes, $this->loBytes); - } - - public function set($value) { - if (is_int($value)) { - $this->loBytes = $value; - $this->hiBytes = 0; - } else if (is_array($value) && 2 == sizeof($value)) { - $this->loBytes = $value[0]; - if ($this->limit32Bit && 0 !== $value[1]) { - throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->hiBytes = $value[1]; - } else if (is_object($value) && __CLASS__ == get_class($value)) { - $value = $value->_getValue(); - if ($this->limit32Bit && 0 !== $value[0]) { - throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->hiBytes = $value[0]; - $this->loBytes = $value[1]; - } else { - throw new \InvalidArgumentException(self::EXCEPTION_SET_INVALID_ARGUMENT); - } - return $this; - } - - public function add($value) { - if (is_int($value)) { - $sum = (int) ($this->loBytes + $value); - // overflow! - if (($this->loBytes > -1 && $sum < $this->loBytes && $sum > -1) - || ($this->loBytes < 0 && ($sum < $this->loBytes || $sum > -1))) { - if ($this->limit32Bit) { - throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->hiBytes = (int) ($this->hiBytes + 1); - } - $this->loBytes = $sum; - } else if (is_object($value) && __CLASS__ == get_class($value)) { - $value = $value->_getValue(); - $sum = (int) ($this->loBytes + $value[1]); - if (($this->loBytes > -1 && $sum < $this->loBytes && $sum > -1) - || ($this->loBytes < 0 && ($sum < $this->loBytes || $sum > -1))) { - if ($this->limit32Bit) { - throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->hiBytes = (int) ($this->hiBytes + 1); - } - $this->loBytes = $sum; - if ($this->limit32Bit && 0 !== $value[0]) { - throw new \OverflowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->hiBytes = (int) ($this->hiBytes + $value[0]); - } else { - throw new \InvalidArgumentException(self::EXCEPTION_ADD_INVALID_ARGUMENT); - } - return $this; - } -} - -class Count64_64 extends Count64Base { - private $value; - - public function getHiBytes() { - return urShift($this->value, 32); - } - - public function getLoBytes() { - return $this->value & INT64_LOW_MAP; - } - - public function _getValue() { - return $this->value; - } - - public function set($value) { - if (is_int($value)) { - if ($this->limit32Bit && INT_MAX_32 < $value) { - throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->value = $value; - } else if (is_array($value) && 2 == sizeof($value)) { - if ($this->limit32Bit && 0 !== $value[1]) { - throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->value = $value[1]; - $this->value = $this->value << 32; - $this->value = $this->value + $value[0]; - } else if (is_object($value) && __CLASS__ == get_class($value)) { - $value = $value->_getValue(); - if ($this->limit32Bit && INT_MAX_32 < $value) { - throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->value = $value; - - } else { - throw new \InvalidArgumentException(self::EXCEPTION_SET_INVALID_ARGUMENT); - } - return $this; - } - - public function add($value) { - if (is_int($value)) { - $sum = (int) ($this->value + $value); - } else if (is_object($value) && __CLASS__ == get_class($value)) { - $sum = (int) ($this->value + $value->_getValue()); - } else { - throw new \InvalidArgumentException(self::EXCEPTION_ADD_INVALID_ARGUMENT); - } - if ($this->limit32Bit && INT_MAX_32 < $sum) { - throw new \OverFlowException(self::EXCEPTION_32BIT_OVERFLOW); - } - $this->value = $sum; - return $this; - } -} - -abstract class Count64 { - public static function construct($value = 0, $limit32Bit = False) { - if (4 == PHP_INT_SIZE) { - return new Count64_32($value, $limit32Bit); - } else { - return new Count64_64($value, $limit32Bit); - } - } -} diff --git a/deepdiver/zipstreamer/test/Count64Test.php b/deepdiver/zipstreamer/test/Count64Test.php new file mode 100644 index 000000000..be36e1b67 --- /dev/null +++ b/deepdiver/zipstreamer/test/Count64Test.php @@ -0,0 +1,161 @@ + +* +* This file is licensed under the GNU GPL version 3 or later. +* See COPYING for details. +*/ + +use \ZipStreamer\Count64; + +class TestPack extends \PHPUnit\Framework\TestCase +{ + public function providerPack16leValues() { + # input value, description + return array( + array(0, "packing 0"), + array(1, "packing positive integer"), + array(-1, "packing negative integer"), + array(0x0f0f, "packing pattern (0x0f0f)"), + array(0xf0f0, "packing pattern (0xf0f0)"), + array(0x00ff, "packing pattern (0x00ff)"), + array(0xff00, "packing pattern (0xff00)"), + array(0xffff, "packing maximum 16 bit value (0xffff)") + ); + } + + /** + * @dataProvider providerPack16leValues + */ + public function testPack16le($value, $description) { + $this->assertEquals(ZipStreamer\pack16le($value), pack('v', $value), $description); + } + + public function providerPack32leValues() { + # input value, description + return array( + array(0, "packing 0"), + array(1, "packing positive integer"), + array(-1, "packing negative integer"), + array(0x0000ffff, "packing pattern (0x0000ffff)"), + array(0xffff0000, "packing pattern (0xffff0000"), + array(0x0f0f0f0f, "packing pattern (0x0f0f0f0f)"), + array(0xf0f0f0f0, "packing pattern (0xf0f0f0f0)"), + array(0xffffffff, "packing maximum 32 bit value (0xffffffff)") + ); + } + + /** + * @dataProvider providerPack32leValues + */ + public function testPack32le($value, $description) { + $this->assertEquals(ZipStreamer\pack32le($value), pack('V', $value), $description); + } + + public function providerPack64leValues() { + # input value, expected high bytes, expected low bytes, description + return array( + array(0, 0, 0, "packing 0"), + array(ZipStreamer\Count64::construct(array(0xffffffff, 0x00000000)), 0xffffffff, 0x00000000, "packing pattern 0x00000000ffffffff"), + array(ZipStreamer\Count64::construct(array(0x00000000, 0xffffffff)), 0x00000000, 0xffffffff, "packing pattern 0xffffffff00000000"), + array(ZipStreamer\Count64::construct(array(0x0f0f0f0f, 0x0f0f0f0f)), 0x0f0f0f0f, 0x0f0f0f0f, "packing pattern 0x0f0f0f0f0f0f0f0f"), + array(ZipStreamer\Count64::construct(array(0xf0f0f0f0, 0xf0f0f0f0)), 0xf0f0f0f0, 0xf0f0f0f0, "packing pattern 0x00f0f0f0f0f0f0f0"), + array(ZipStreamer\Count64::construct(array(0xffffffff, 0xffffffff)), 0xffffffff, 0xffffffff, "packing maximum 64 bit value (0xffffffffffffffff)") + ); + } + + /** + * @dataProvider providerPack64leValues + */ + public function testPack64le($inVal, $cmpVal1, $cmpVal2, $description) { + $this->assertEquals(ZipStreamer\pack64le($inVal), pack('VV', $cmpVal1, $cmpVal2), $description); + } + + public function providerGoodCount64InitializationValues() { + // expected low bytes, expected high bytes, input value, message + return array( + array(0x00000000, 0x00000000, 0, "integer 0"), + array(0x00000000, 0x00000000, array(0, 0), "integer array(0, 0)"), + array(0xffffffff, 0xffffffff, array(0xffffffff, 0xffffffff), "bit pattern array(0xffffffff, 0xffffffff)"), + array(0x00000000, 0xffffffff, array(0x00000000, 0xffffffff), "bit pattern array(0x00000000, 0xffffffff)"), + array(0xffffffff, 0x00000000, array(0xffffffff, 0x00000000), "bit pattern array(0xffffffff, 0x00000000)"), + array(0x0f0f0f0f, 0x0f0f0f0f, array(0x0f0f0f0f, 0x0f0f0f0f), "bit pattern array(0x0f0f0f0f, 0x0f0f0f0f)"), + array(0xf0f0f0f0, 0xf0f0f0f0, array(0xf0f0f0f0, 0xf0f0f0f0), "bit pattern array(0xf0f0f0f0, 0xf0f0f0f0)"), + array(0x00000000, 0x00000000, ZipStreamer\Count64::construct(0), "Count64Base object (value 0)") + ); + } + + /** + * @dataProvider providerGoodCount64InitializationValues + */ + public function testCount64Construct($loBytes, $hiBytes, $value, $description) { + $count64 = ZipStreamer\Count64::construct($value); + $this->assertInstanceOf('ZipStreamer\Count64Base', $count64, $description . ' (instanceof)'); + $this->assertEquals($loBytes, $count64->getLoBytes(), $description . " (loBytes)"); + $this->assertEquals($hiBytes, $count64->getHiBytes(), $description . " (hiBytes)"); + } + + public function providerBadCount64InitializationValues() { + # input value + return array( + array("a"), + array(0.0), + array(1.0), + array(array()) + ); + } + + /** + * @dataProvider providerBadCount64InitializationValues + * @expectedException InvalidArgumentException + */ + public function testCount64ConstructFail($badValue) { + $count64 = ZipStreamer\Count64::construct($badValue); + } + + /** + * @dataProvider providerGoodCount64InitializationValues + */ + public function testCount64Set($loBytes, $hiBytes, $value, $description) { + $count64 = ZipStreamer\Count64::construct(); + $count64->set($value); + $this->assertInstanceOf('ZipStreamer\Count64Base', $count64, $description . ' (instanceof)'); + $this->assertEquals($loBytes, $count64->getLoBytes(), $description . " (loBytes)"); + $this->assertEquals($hiBytes, $count64->getHiBytes(), $description . " (hiBytes)"); + } + + /** + * @dataProvider providerBadCount64InitializationValues + * @expectedException InvalidArgumentException + */ + public function testCount64SetFail($badValue) { + $count64 = ZipStreamer\Count64::construct(); + $count64->set($badValue); + } + + /** + * @dataProvider providerCount64AddValues + */ + public function providerCount64AddValues() { + # input start value, input add value, expected low bytes, expected high bytes, description + return array( + array(0, 0, 0x00000000, 0x00000000, "adding 0 to 0"), + array(0, 1, 0x00000001, 0x00000000, "adding 1 to 0"), + array(1, 0, 0x00000001, 0x00000000, "adding 0 to 1"), + array(0xffffffff, 1, 0x00000000, 0x00000001, "adding 1 to 0xffffffff"), + array(0x00000001, 0xffffffff, 0x00000000, 0x00000001, "adding 0xfffffff to 0x00000001"), + array(0xffffffff, 0xffffffff, 0xfffffffe, 0x00000001, "adding 0xfffffff to 0xffffffff") + ); + } + + /** + * @dataProvider providerCount64AddValues + */ + public function testCount64Add($value, $add, $loBytes, $hiBytes, $description) { + $count64 = ZipStreamer\Count64::construct($value); + $count64->add($add); + $this->assertEquals($loBytes, $count64->getLoBytes(), $description . " (loBytes)".sprintf("%x=%x", $loBytes, $count64->getLoBytes())); + $this->assertEquals($hiBytes, $count64->getHiBytes(), $description . " (hiBytes)"); + } +} +?> diff --git a/deepdiver/zipstreamer/test/ZipComponents.php b/deepdiver/zipstreamer/test/ZipComponents.php new file mode 100644 index 000000000..ac197d6f8 --- /dev/null +++ b/deepdiver/zipstreamer/test/ZipComponents.php @@ -0,0 +1,614 @@ + + * + * This file is licensed under the GNU GPL version 3 or later. + * See COPYING for details. + */ +namespace ZipStreamer; + +/** + * @codeCoverageIgnore + */ +class ParseException extends \Exception { +} + +function readstr($str, &$pos, $len) { + $str = substr($str, $pos, $len); + $pos += $len; + return $str; +} + +function hexIfFFFF($value) { + return $value == 0xffff ? '0x' . dechex($value) : $value; +} + +function hexIfFFFFFFFF($value) { + return $value == 0xffffffff ? '0x' . dechex($value) : $value; +} + +/** + * @codeCoverageIgnore + */ +abstract class zipRecord { + protected static $magicBytes = array(); + protected static $unitTest = null; + protected static $shortName = ""; + protected static $magicLength = 4; + public $begin; + public $end; + + public function getLength() { + return $this->end - $this->begin + 1; + } + + public static function setUnitTest($unitTest) { + self::$unitTest = $unitTest; + } + + public static function getMagicBytes() { + if (!array_key_exists(static::$MAGIC, self::$magicBytes)) { + if (2 == static::$magicLength) { + self::$magicBytes[static::$MAGIC] = pack16le(static::$MAGIC); + } else { + self::$magicBytes[static::$MAGIC] = pack32le(static::$MAGIC); + } + } + return self::$magicBytes[static::$MAGIC]; + } + + protected static function __constructFromString($str, $pos, $size = -1) { + $eocdrec = new static(); + try { + $eocdrec->readFromString($str, $pos, $size); + } catch (Exception $e) { + $this->fail("error parsing end of central directory record"); + } + + return $eocdrec; + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + return static::__constructFromString($str, $offset, $size); + } + + protected abstract function readFromString($str, $pos, $size = -1); + + public function assertValues($values) { + if (self::$unitTest) { + foreach ($values as $key => $value) { + self::$unitTest->assertEquals($value, $this->{$key}, static::$shortName . " " . $key); + } + } + } +} + +/** + * @codeCoverageIgnore + */ +class EndOfCentralDirectoryRecord extends zipRecord { + protected static $MAGIC = 0x06054b50; // end of central directory record + protected static $shortName = "EOCDR"; + public $numberDisk; + public $numberDiskStartCD; + public $numberEntriesDisk; + public $numberEntriesCD; + public $size; + public $offsetStart; + public $lengthComment; + public $comment; + + public function __toString() { + return sprintf( + "Number of this disk: %d\n" . + "Number of disk with start of eocd record: %d\n" . + "Number of cd record entries on this disk: %d\n" . + "Total number of cd record entries: %d\n" . + "Size of central directory: %d\n" . + "Offset of central directory: %d\n" . + "Zip file comment length: %d\n" . + "Zip file comment following (if any)\n%s\n", + $this->numberDisk, + $this->numberDiskStartCD, + $this->numberEntriesDisk, + $this->numberEntriesCD, + $this->size, + $this->offsetStart, + $this->lengthComment, + $this->comment); + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + $eocdrecPos = strrpos($str, static::getMagicBytes()); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $eocdrecPos, "end of central directory record missing"); + self::$unitTest->assertGreaterThanOrEqual(22, strlen($str) - $eocdrecPos, "end of central directory record incomplete (smaller than minimum length)"); + } + + return static::__constructFromString($str, $eocdrecPos); + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 4); + if (self::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->numberDisk = (int) unpack16le(readstr($str, $pos, 2)); + $this->numberDiskStartCD = (int) unpack16le(readstr($str, $pos, 2)); + $this->numberEntriesDisk = (int) unpack16le(readstr($str, $pos, 2)); + $this->numberEntriesCD = (int) unpack16le(readstr($str, $pos, 2)); + $this->size = (int) unpack32le(readstr($str, $pos, 4)); + $this->offsetStart = (int) unpack32le(readstr($str, $pos, 4)); + $this->lengthComment = unpack16le(readstr($str, $pos, 2)); + if (0 < $this->lengthComment) { + $this->comment = (string) readstr($str, $pos, $this->lengthComment); + } else { + $this->comment = ''; + } + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class Zip64EndOfCentralDirectoryLocator extends zipRecord { + protected static $MAGIC = 0x07064b50; // zip64 end of central directory locator + protected static $shortName = "Z64EOCDL"; + public $numberDiskStartZ64EOCDL; + public $offsetStart; + public $numberDisks; + + public function __toString() { + return sprintf( + "Number of disk with start of zip64 eocd locator: %d\n" . + "Offset of zip64 eocd record: %d\n" . + "Number of disks: %d\n" . + $this->numberDiskStartZ64EOCDL, + $this->offsetStart, + $this->numberDisks); + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + $z64eocdlPos = strrpos($str, static::getMagicBytes(), -$offset); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $z64eocdlPos, "zip64 end of central directory locator missing"); + } + + $z64eocdl = static::__constructFromString($str, $z64eocdlPos); + + if (self::$unitTest) { + self::$unitTest->assertGreaterThanOrEqual(20, $z64eocdl->getLength(), "zip64 end of central directory locator incomplete (to short)"); + self::$unitTest->assertLessThanOrEqual(20, $z64eocdl->getLength(), "garbage after end of zip64 end of central directory locator"); + } + + return $z64eocdl; + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 4); + if (static::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->numberDiskStartZ64EOCDL = (int) unpack32le(readstr($str, $pos, 4)); + $this->offsetStart = unpack64le(readstr($str, $pos, 8)); + $this->numberDisks = (int) unpack32le(readstr($str, $pos, 4)); + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class Zip64EndOfCentralDirectoryRecord extends zipRecord { + protected static $MAGIC = 0x06064b50; // zip64 end of central directory locator + protected static $shortName = "Z64EOCDR"; + public $size; + public $madeByVersion; + public $versionToExtract; + public $numberDisk; + public $numberDiskStartCDR; + public $numberEntriesDisk; + public $numberEntriesCD; + public $sizeCD; + public $offsetStart; + + public function __toString() { + return sprintf( + "Size of Zip64 EOCDR: %d\n" . + "Made by version: %s\n" . + "Version needed to extract: %s\n" . + "Number of this disk: %d\n" . + "Number of disk with start of cd: %d\n" . + "Number of cd record entries on this disk: %d\n" . + "Total number of cd record entries: %d\n" . + "Size of central directory: %d\n" . + "Offset of central directory: %d\n", + $this->size, + $this->madeByVersion, + $this->versionToExtract, + $this->numberDisk, + $this->numberDiskStartCDR, + $this->numberEntriesDisk, + $this->numberEntriesCD, + $this->sizeCD, + $this->offsetStart); + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + $z64eocdlPos = strrpos($str, static::getMagicBytes(), -$offset); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $z64eocdlPos, "zip64 end of central directory record missing"); + } + + $z64eocdl = static::__constructFromString($str, $z64eocdlPos); + + if (self::$unitTest) { + self::$unitTest->assertGreaterThanOrEqual(56, $z64eocdl->getLength(), "zip64 end of central directory record incomplete (to short)"); + self::$unitTest->assertLessThanOrEqual(56, $z64eocdl->getLength(), "garbage after end of zip64 end of central directory record"); + } + + return $z64eocdl; + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 4); + if (static::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->size = unpack64le(readstr($str, $pos, 8)); + $this->madeByVersion = readstr($str, $pos, 2); + $this->versionToExtract = readstr($str, $pos, 2); + $this->numberDisk = (int) unpack32le(readstr($str, $pos, 4)); + $this->numberDiskStartCDR = (int) unpack32le(readstr($str, $pos, 4)); + $this->numberEntriesDisk = unpack64le(readstr($str, $pos, 8)); + $this->numberEntriesCD = unpack64le(readstr($str, $pos, 8)); + $this->sizeCD = unpack64le(readstr($str, $pos, 8)); + $this->offsetStart = unpack64le(readstr($str, $pos, 8)); + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class CentralDirectoryHeader extends zipRecord { + protected static $MAGIC = 0x02014b50; // central file header signature + protected static $shortName = "CDH"; + public $madeByVersion; + public $versionToExtract; + public $gpFlags; + public $gzMethod; + public $dosTime; + public $dataCRC32; + public $sizeCompressed; + public $size; + public $lengthFilename; + public $lengthExtraField; + public $lengthComment; + public $diskNumberStart; + public $fileAttrInternal; + public $fileAttrExternal; + public $offsetStart; + public $filename; + public $z64Ext; + public $comment; + + public function __toString() { + return sprintf( + "Made by version: 0x%s\n" . + "Version needed to extract: 0x%s\n" . + "General purpose flags: 0x%s\n" . + "Compression method: 0x%s\n" . + "Dos time: %s\n" . + "Data CRC32: %s\n" . + "Compressed file size: %s\n" . + "Uncompressed file size: %s\n" . + "Filename length: %d\n" . + "Extra field length: %d\n" . + "Comment length: %d\n" . + "Number of disk with file start: %s\n" . + "Internal file attributes. %s\n" . + "External file attributes: %s\n" . + "Offset of start of local file header: %s\n" . + "Filename: %s\n" . + "Comment: %s\n", + bin2hex($this->madeByVersion), + bin2hex($this->versionToExtract), + bin2hex($this->gpFlags), + bin2hex($this->gzMethod), + $this->dosTime, + $this->dataCRC32, + hexIfFFFFFFFF($this->sizeCompressed), + hexIfFFFFFFFF($this->size), + $this->lengthFilename, + $this->lengthExtraField, + $this->lengthComment, + hexIfFFFF($this->diskNumberStart), + $this->fileAttrInternal, + $this->fileAttrExternal, + hexIfFFFFFFFF($this->offsetStart), + $this->filename, + $this->comment); + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + $cdheadPos = strpos($str, static::getMagicBytes(), $offset); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $cdheadPos, "central directory header missing"); + self::$unitTest->assertEquals($offset, $cdheadPos, "garbage before central directory header"); + } + + return static::__constructFromString($str, $cdheadPos); + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 4); + if (static::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->madeByVersion = readstr($str, $pos, 2); + $this->versionToExtract = readstr($str, $pos, 2); + $this->gpFlags = readstr($str, $pos, 2); + $this->gzMethod = readstr($str, $pos, 2); + $this->dosTime = readstr($str, $pos, 4); + $this->dataCRC32 = (int) unpack32le(readstr($str, $pos, 4)); + $this->sizeCompressed = (int) unpack32le(readstr($str, $pos, 4)); + $this->size = (int) unpack32le(readstr($str, $pos, 4)); + $this->lengthFilename = (int) unpack16le(readstr($str, $pos, 2)); + $this->lengthExtraField = (int) unpack16le(readstr($str, $pos, 2)); + $this->lengthComment = (int) unpack16le(readstr($str, $pos, 2)); + $this->diskNumberStart = (int) unpack16le(readstr($str, $pos, 2)); + $this->fileAttrInternal = readstr($str, $pos, 2); + $this->fileAttrExternal = readstr($str, $pos, 4); + $this->offsetStart = (int) unpack32le(readstr($str, $pos, 4)); + if (0 < $this->lengthFilename) { + $this->filename = (string) readstr($str, $pos, $this->lengthFilename); + } else { + $this->filename = ''; + } + if (0 < $this->lengthExtraField) { + $this->z64Ext = Zip64ExtendedInformationField::constructFromString($str, $pos); + if (self::$unitTest) { + self::$unitTest->assertEquals($this->lengthExtraField, $this->z64Ext->getLength(), "Z64EIF is only field and fits into propagated length"); + } + $pos = $this->z64Ext->end + 1; + } + if (0 < $this->lengthComment) { + $this->comment = (string) readstr($str, $pos, $this->lengthComment); + } else { + $this->comment = ''; + } + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class Zip64ExtendedInformationField extends zipRecord { + protected static $MAGIC = 0x0001; // central file header signature + protected static $magicLength = 2; + protected static $shortName = "Z64EIF"; + public $sizeField; + public $size; + public $sizeCompressed; + public $offsetStart; + public $diskNumberStart; + + public function __toString() { + return sprintf( + "Size of this 'extra' block: %d\n" . + "Uncompressed file size: %d\n" . + "Compressed file size: %d\n" . + "Offset of begin of local file header: %d\n" . + "Number of disk with file start: %d\n", + $this->sizeField, + $this->size, + $this->sizeCompressed, + $this->offsetStart, + $this->diskNumberStart); + } + + public static function constructFromString($str, $offsetStart = 0, $size = -1) { + $pos = strpos($str, static::getMagicBytes(), $offsetStart); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $pos, "extra field magic bytes missing"); + self::$unitTest->assertEquals($offsetStart, $pos, "garbage before extra field"); + } + + return static::__constructFromString($str, $pos); + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 2); + if (static::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->sizeField = (int) unpack16le(readstr($str, $pos, 2)); + $this->size = unpack64le(readstr($str, $pos, 8)); + $this->sizeCompressed = unpack64le(readstr($str, $pos, 8)); + $this->offsetStart = unpack64le(readstr($str, $pos, 8)); + $this->diskNumberStart = (int) unpack16le(readstr($str, $pos, 4)); + + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class FileEntry extends zipRecord { + protected static $shortName = "FILE"; + public $lfh; + public $dataCompressed; + public $data; + public $dd; + + public function __toString() { + return sprintf("File content:\n" . "%s", $this->data); + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $this->lfh = LocalFileHeader::constructFromString($str, $pos); + $pos = $this->lfh->end + 1; + if (self::$unitTest) { + $this->dataCompressed = readStr($str, $pos, $size); + if (0 < strlen($this->dataCompressed) && COMPR::DEFLATE & $this->lfh->gzMethod) { + $this->data = gzinflate($this->dataCompressed); + } else { + $this->data = $this->dataCompressed; + } + } + if (GPFLAGS::ADD & $this->lfh->gpFlags) { + if (is_null($this->lfh->z64Ext)) { + $ddLength = 16; + } else { + $ddLength = 24; + } + $this->dd = DataDescriptor::constructFromString($str, $pos, $ddLength); + $pos = $this->dd->end + 1; + } + + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class LocalFileHeader extends zipRecord { + protected static $MAGIC = 0x04034b50; // central file header signature + protected static $shortName = "LFH"; + public $versionToExtract; + public $gpFlags; + public $gzMethod; + public $dosTime; + public $dataCRC32; + public $sizeCompressed; + public $size; + public $lengthFilename; + public $lengthExtraField; + public $filename; + public $z64Ext; + + public function __toString() { + return sprintf( + "Version needed to extract: %s\n" . + "General purpose flags: %s\n" . + "Compression method: %s\n" . + "Dos time: %s\n" . + "Data CRC32: %s\n" . + "Compressed file size: %d\n" . + "Uncompressed file size: %d\n" . + "Filename length: %d\n" . + "Extra field length: %d\n" . + "Filename: %s\n" , + bin2hex($this->versionToExtract), + bin2hex($this->gpFlags), + bin2hex($this->gzMethod), + $this->dosTime, + $this->dataCRC32, + hexIfFFFFFFFF($this->sizeCompressed), + hexIfFFFFFFFF($this->size), + $this->lengthFilename, + $this->lengthExtraField, + $this->filename); + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + $cdheadPos = strpos($str, static::getMagicBytes(), $offset); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $cdheadPos, "local file header missing"); + self::$unitTest->assertEquals($offset, $cdheadPos, "garbage before local file header"); + } + + return static::__constructFromString($str, $cdheadPos, $size); + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 4); + if (static::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->versionToExtract = readstr($str, $pos, 2); + $this->gpFlags = (int) unpack16le(readstr($str, $pos, 2)); + $this->gzMethod = (int) unpack16le(readstr($str, $pos, 2)); + $this->dosTime = readstr($str, $pos, 4); + $this->dataCRC32 = (int) unpack32le(readstr($str, $pos, 4)); + $this->sizeCompressed = (int) unpack32le(readstr($str, $pos, 4)); + $this->size = (int) unpack32le(readstr($str, $pos, 4)); + $this->lengthFilename = (int) unpack16le(readstr($str, $pos, 2)); + $this->lengthExtraField = (int) unpack16le(readstr($str, $pos, 2)); + if (0 < $this->lengthFilename) { + $this->filename = (string) readstr($str, $pos, $this->lengthFilename); + } else { + $this->filename = ''; + } + if (0 < $this->lengthExtraField) { + $this->z64Ext = Zip64ExtendedInformationField::constructFromString($str, $pos); + if (self::$unitTest) { + self::$unitTest->assertEquals($this->lengthExtraField, $this->z64Ext->getLength(), "Z64EIF is only field and fits into propagated length"); + } + $pos = $this->z64Ext->end + 1; + } + $this->end = $pos - 1; + } +} + +/** + * @codeCoverageIgnore + */ +class DataDescriptor extends zipRecord { + protected static $MAGIC = 0x08074b50; // data descriptor header signature + protected static $shortName = "DD"; + public $dataCRC32; + public $sizeCompressed; + public $size; + + public function __toString() { + return sprintf( + "Data CRC32: %s\n" . + "Compressed file size: %d\n" . + "Uncompressed file size: %d\n" , + $this->dataCRC32, + hexIfFFFFFFFF($this->sizeCompressed->getLoBytes()), + hexIfFFFFFFFF($this->size->getLoBytes())); + } + + public static function constructFromString($str, $offset = 0, $size = -1) { + $ddheadPos = strpos($str, static::getMagicBytes(), $offset); + if (self::$unitTest) { + self::$unitTest->assertFalse(False === $ddheadPos, "data descriptor header missing"); + self::$unitTest->assertEquals($offset, $ddheadPos, "garbage before data descriptor header"); + } + + return static::__constructFromString($str, $offset, $size); + } + + public function readFromString($str, $pos, $size = -1) { + $this->begin = $pos; + $magic = readstr($str, $pos, 4); + if (static::getMagicBytes() != $magic) { + throw new ParseException("invalid magic"); + } + $this->dataCRC32 = (int) unpack32le(readstr($str, $pos, 4)); + if (24 == $size) { + $this->sizeCompressed = unpack64le(readstr($str, $pos, 8)); + $this->size = unpack64le(readstr($str, $pos, 8)); + } else { + $this->sizeCompressed = Count64::construct((int) unpack32le(readstr($str, $pos, 4))); + $this->size = Count64::construct((int) unpack32le(readstr($str, $pos, 4))); + } + $this->end = $pos - 1; + } +} +?> diff --git a/deepdiver/zipstreamer/test/ZipStreamerTest.php b/deepdiver/zipstreamer/test/ZipStreamerTest.php new file mode 100644 index 000000000..d0d3ef748 --- /dev/null +++ b/deepdiver/zipstreamer/test/ZipStreamerTest.php @@ -0,0 +1,422 @@ + + * + * This file is licensed under the GNU GPL version 3 or later. + * See COPYING for details. + */ +namespace ZipStreamer; + +require_once __DIR__ . "/ZipComponents.php"; + +class File { + const FILE = 1; + const DIR = 2; + public $filename; + public $date; + public $type; + public $data; + + public function __construct($filename, $type, $date, $data = "") { + $this->filename = $filename; + $this->type = $type; + $this->date = $date; + $this->data = $data; + } + + public function getSize() { + return strlen($this->data); + } +} + +class TestZipStreamer extends \PHPUnit\Framework\TestCase { + const ATTR_MADE_BY_VERSION = 0x032d; // made by version (upper byte: UNIX, lower byte v4.5) + const EXT_FILE_ATTR_DIR = 0x41ed0010; + const EXT_FILE_ATTR_FILE = 0x81a40000; + protected $outstream; + + protected function setUp() { + parent::setUp(); + $this->outstream = fopen('php://memory', 'rw'); + zipRecord::setUnitTest($this); + } + + protected function tearDown() { + fclose($this->outstream); + parent::tearDown(); + } + + protected function getOutput() { + rewind($this->outstream); + return stream_get_contents($this->outstream); + } + + protected static function getVersionToExtract($zip64, $isDir) { + if ($zip64) { + $version = 0x2d; // 4.5 - File uses ZIP64 format extensions + } else if ($isDir) { + $version = 0x14; // 2.0 - File is a folder (directory) + } else { + $version = 0x0a; // 1.0 - Default value + } + return $version; + } + + protected function assertOutputEqualsFile($filename) { + $this->assertEquals(file_get_contents($filename), $this->getOutput()); + } + + protected function assertContainsOneMatch($pattern, $input) { + $results = preg_grep($pattern, $input); + $this->assertEquals(1, sizeof($results)); + } + + protected function assertOutputZipfileOK($files, $options) { + if (0 < sizeof($files)) { // php5.3 does not combine empty arrays + $files = array_combine(array_map(function ($element) { + return $element->filename; + }, $files), $files); + } + $output = $this->getOutput(); + + $eocdrec = EndOfCentralDirectoryRecord::constructFromString($output); + $this->assertEquals(strlen($output) - 1, $eocdrec->end, "EOCDR last item in file"); + + if ($options['zip64']) { + $eocdrec->assertValues(array( + "numberDisk" => 0xffff, + "numberDiskStartCD" => 0xffff, + "numberEntriesDisk" => sizeof($files), + "numberEntriesCD" => sizeof($files), + "size" => 0xffffffff, + "offsetStart" => 0xffffffff, + "lengthComment" => 0, + "comment" => '' + )); + + $z64eocdloc = Zip64EndOfCentralDirectoryLocator::constructFromString($output, strlen($output) - ($eocdrec->begin + 1)); + + $this->assertEquals($z64eocdloc->end + 1, $eocdrec->begin, "Z64EOCDL directly before EOCDR"); + $z64eocdloc->assertValues(array( + "numberDiskStartZ64EOCDL" => 0, + "numberDisks" => 1 + )); + + $z64eocdrec = Zip64EndOfCentralDirectoryRecord::constructFromString($output, strlen($output) - ($z64eocdloc->begin + 1)); + + $this->assertEquals(Count64::construct($z64eocdrec->begin), $z64eocdloc->offsetStart, "Z64EOCDR begin"); + $this->assertEquals($z64eocdrec->end + 1, $z64eocdloc->begin, "Z64EOCDR directly before Z64EOCDL"); + $z64eocdrec->assertValues(array( + "size" => Count64::construct(44), + "madeByVersion" => pack16le(self::ATTR_MADE_BY_VERSION), + "versionToExtract" => pack16le($this->getVersionToExtract($options['zip64'], False)), + "numberDisk" => 0, + "numberDiskStartCDR" => 0, + "numberEntriesDisk" => Count64::construct(sizeof($files)), + "numberEntriesCD" => Count64::construct(sizeof($files)) + )); + $sizeCD = $z64eocdrec->sizeCD->getLoBytes(); + $offsetCD = $z64eocdrec->offsetStart->getLoBytes(); + $beginFollowingRecord = $z64eocdrec->begin; + } else { + $eocdrec->assertValues(array( + "numberDisk" => 0, + "numberDiskStartCD" => 0, + "numberEntriesDisk" => sizeof($files), + "numberEntriesCD" => sizeof($files), + "lengthComment" => 0, + "comment" => '' + )); + $sizeCD = $eocdrec->size; + $offsetCD = $eocdrec->offsetStart; + $beginFollowingRecord = $eocdrec->begin; + } + + $cdheaders = array(); + $pos = $offsetCD; + $cdhead = null; + + while ($pos < $beginFollowingRecord) { + $cdhead = CentralDirectoryHeader::constructFromString($output, $pos); + $filename = $cdhead->filename; + $pos = $cdhead->end + 1; + $cdheaders[$filename] = $cdhead; + + $this->assertArrayHasKey($filename, $files, "CDH entry has valid name"); + $cdhead->assertValues(array( + "madeByVersion" => pack16le(self::ATTR_MADE_BY_VERSION), + "versionToExtract" => pack16le($this->getVersionToExtract($options['zip64'], File::DIR == $files[$filename]->type)), + "gpFlags" => (File::FILE == $files[$filename]->type ? pack16le(GPFLAGS::ADD) : pack16le(GPFLAGS::NONE)), + "gzMethod" => (File::FILE == $files[$filename]->type ? pack16le($options['compress']) : pack16le(COMPR::STORE)), + "dosTime" => pack32le(ZipStreamer::getDosTime($files[$filename]->date)), + "lengthFilename" => strlen($filename), + "lengthComment" => 0, + "fileAttrInternal" => pack16le(0x0000), + "fileAttrExternal" => (File::FILE == $files[$filename]->type ? pack32le(self::EXT_FILE_ATTR_FILE) : pack32le(self::EXT_FILE_ATTR_DIR)) + )); + if ($options['zip64']) { + $cdhead->assertValues(array( + "sizeCompressed" => 0xffffffff, + "size" => 0xffffffff, + "lengthExtraField" => 32, + "diskNumberStart" => 0xffff, + "offsetStart" => 0xffffffff + )); + $cdhead->z64Ext->assertValues(array( + "sizeField" => 28, + "size" => Count64::construct($files[$filename]->getSize()), + "diskNumberStart" => 0 + )); + } else { + $cdhead->assertValues(array( + "size" => $files[$filename]->getSize(), + "lengthExtraField" => 0, + "diskNumberStart" => 0 + )); + } + } + if (0 < sizeof($files)) { + $this->assertEquals($cdhead->end + 1, $beginFollowingRecord, "CDH directly before following record"); + $this->assertEquals(sizeof($files), sizeof($cdheaders), "CDH has correct number of entries"); + $this->assertEquals($sizeCD, $beginFollowingRecord - $offsetCD, "CDH has correct size"); + } else { + $this->assertNull($cdhead); + } + + $first = True; + foreach ($cdheaders as $filename => $cdhead) { + if ($options['zip64']) { + $sizeCompressed = $cdhead->z64Ext->sizeCompressed->getLoBytes(); + $offsetStart = $cdhead->z64Ext->offsetStart->getLoBytes(); + } else { + $sizeCompressed = $cdhead->sizeCompressed; + $offsetStart = $cdhead->offsetStart; + } + if ($first) { + $this->assertEquals(0, $offsetStart, "first file directly at beginning of zipfile"); + } else { + $this->assertEquals($endLastFile + 1, $offsetStart, "file immediately after last file"); + } + $file = FileEntry::constructFromString($output, $offsetStart, $sizeCompressed); + $this->assertEquals($files[$filename]->data, $file->data); + $this->assertEquals(crc32($files[$filename]->data), $cdhead->dataCRC32); + if (GPFLAGS::ADD & $file->lfh->gpFlags) { + $this->assertNotNull($file->dd, "data descriptor present (flag ADD set)"); + } + if ($options['zip64']) { + $file->lfh->assertValues(array( + "sizeCompressed" => 0xffffffff, + "size" => 0xffffffff, + )); + $file->lfh->z64Ext->assertValues(array( + "sizeField" => 28, + "size" => Count64::construct(0), + "sizeCompressed" => Count64::construct(0), + "diskNumberStart" => 0 + )); + } else { + $file->lfh->assertValues(array( + "sizeCompressed" => 0, + "size" => 0, + )); + } + $file->lfh->assertValues(array( + "versionToExtract" => pack16le($this->getVersionToExtract($options['zip64'], File::DIR == $files[$filename]->type)), + "gpFlags" => (File::FILE == $files[$filename]->type ? GPFLAGS::ADD : GPFLAGS::NONE), + "gzMethod" => (File::FILE == $files[$filename]->type ? $options['compress'] : COMPR::STORE), + "dosTime" => pack32le(ZipStreamer::getDosTime($files[$filename]->date)), + "dataCRC32" => 0x0000, + "lengthFilename" => strlen($filename), + "filename" => $filename + )); + + $endLastFile = $file->end; + $first = False; + } + if (0 < sizeof($files)) { + $this->assertEquals($offsetCD, $endLastFile + 1, "last file directly before CDH"); + } else { + $this->assertEquals(0, $beginFollowingRecord, "empty zip file, CD records at beginning of file"); + } + } + + /** + * @return array array(filename, mimetype), expectedMimetype, expectedFilename, $description, $browser + */ + public function providerSendHeadersOK() { + return array( + // Regular browsers + array( + array(), + 'application/zip', + 'archive.zip', + 'default headers', + 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', + 'Content-Disposition: attachment; filename*=UTF-8\'\'archive.zip; filename="archive.zip"', + ), + array( + array( + 'file.zip', + 'application/octet-stream', + ), + 'application/octet-stream', + 'file.zip', + 'specific headers', + 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', + 'Content-Disposition: attachment; filename*=UTF-8\'\'file.zip; filename="file.zip"', + ), + // Internet Explorer + array( + array(), + 'application/zip', + 'archive.zip', + 'default headers', + 'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko', + 'Content-Disposition: attachment; filename="archive.zip"', + ), + array( + array( + 'file.zip', + 'application/octet-stream', + ), + 'application/octet-stream', + 'file.zip', + 'specific headers', + 'Mozilla/5.0 (compatible, MSIE 11, Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko', + 'Content-Disposition: attachment; filename="file.zip"', + ), + ); + } + + /** + * @dataProvider providerSendHeadersOK + * @preserveGlobalState disabled + * @runInSeparateProcess + * + * @param array $arguments + * @param string $expectedMimetype + * @param string $expectedFilename + * @param string $description + * @param string $browser + * @param string $expectedDisposition + */ + public function testSendHeadersOKWithRegularBrowser(array $arguments, + $expectedMimetype, + $expectedFilename, + $description, + $browser, + $expectedDisposition) { + $zip = new ZipStreamer(array( + 'outstream' => $this->outstream + )); + $zip->turnOffOutputBuffering = false; + $_SERVER['HTTP_USER_AGENT'] = $browser; + call_user_func_array(array($zip, "sendHeaders"), $arguments); + $headers = xdebug_get_headers(); + $this->assertContains('Pragma: public', $headers); + $this->assertContains('Expires: 0', $headers); + $this->assertContains('Accept-Ranges: bytes', $headers); + $this->assertContains('Connection: Keep-Alive', $headers); + $this->assertContains('Content-Transfer-Encoding: binary', $headers); + $this->assertContains('Content-Type: ' . $expectedMimetype, $headers); + $this->assertContains($expectedDisposition, $headers); + $this->assertContainsOneMatch('/^Last-Modified: /', $headers); + } + + public function providerZipfileOK() { + $zip64Options = array(array(True, 'True'), array(False, 'False')); + $defaultLevelOption = array(array(COMPR::NORMAL, 'COMPR::NORMAL')); + $compressOptions = array(array(COMPR::STORE, 'COMPR::STORE'), array(COMPR::DEFLATE, 'COMPR::DEFLATE')); + $levelOptions = array(array(COMPR::NONE, 'COMPR::NONE'), array(COMPR::SUPERFAST, 'COMPR::SUPERFAST'), array(COMPR::MAXIMUM, 'COMPR::MAXIMUM')); + $fileSets = array( + array( + array(), + "empty" + ), + array( + array( + new File('test/', File::DIR, 1) + ), + "one empty dir" + ), + array( + array( + new File('test1.txt', File::FILE, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed elit diam, posuere vel aliquet et, malesuada quis purus. Aliquam mattis aliquet massa, a semper sem porta in. Aliquam consectetur ligula a nulla vestibulum dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam luctus faucibus urna, accumsan cursus neque laoreet eu. Suspendisse potenti. Nulla ut feugiat neque. Maecenas molestie felis non purus tempor, in blandit ligula tincidunt. Ut in tortor sit amet nisi rutrum vestibulum vel quis tortor. Sed bibendum mauris sit amet gravida tristique. Ut hendrerit sapien vel tellus dapibus, eu pharetra nulla adipiscing. Donec in quam faucibus, cursus lacus sed, elementum ligula. Morbi volutpat vel lacus malesuada condimentum. Fusce consectetur nisl euismod justo volutpat sodales.') + ), + "one file" + ), + array( + array( + new File('test1.txt', File::FILE, 1, 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed elit diam, posuere vel aliquet et, malesuada quis purus. Aliquam mattis aliquet massa, a semper sem porta in. Aliquam consectetur ligula a nulla vestibulum dictum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nullam luctus faucibus urna, accumsan cursus neque laoreet eu. Suspendisse potenti. Nulla ut feugiat neque. Maecenas molestie felis non purus tempor, in blandit ligula tincidunt. Ut in tortor sit amet nisi rutrum vestibulum vel quis tortor. Sed bibendum mauris sit amet gravida tristique. Ut hendrerit sapien vel tellus dapibus, eu pharetra nulla adipiscing. Donec in quam faucibus, cursus lacus sed, elementum ligula. Morbi volutpat vel lacus malesuada condimentum. Fusce consectetur nisl euismod justo volutpat sodales.'), + new File('test/', File::DIR, 1), + new File('test/test12.txt', File::FILE, 1, 'Duis malesuada lorem lorem, id sodales sapien sagittis ac. Donec in porttitor tellus, eu aliquam elit. Curabitur eu aliquam eros. Nulla accumsan augue quam, et consectetur quam eleifend eget. Donec cursus dolor lacus, eget pellentesque risus tincidunt at. Pellentesque rhoncus purus eget semper porta. Duis in magna tincidunt, fermentum orci non, consectetur nibh. Aliquam tortor eros, dignissim a posuere ac, rhoncus a justo. Sed sagittis velit ac massa pulvinar, ac pharetra ipsum fermentum. Etiam commodo lorem a scelerisque facilisis.') + ), + "simple structure" + ) + ); + + $data = array(); + foreach ($zip64Options as $zip64) { + foreach ($compressOptions as $compress) { + $levels = $defaultLevelOption; + if (COMPR::DEFLATE == $compress[0]) { + $levels = array_merge($levels, $levelOptions); + } + foreach ($levels as $level) { + foreach ($fileSets as $fileSet) { + $options = array( + 'zip64' => $zip64[0], + 'compress' => $compress[0], + 'level' => $level[0] + ); + $description = $fileSet[1] . ' (options = array(zip64=' . $zip64[1] . ', compress=' . $compress[1] . ', level=' . $level[1] . '))'; + array_push($data, array( + $options, + $fileSet[0], + $description + )); + } + } + } + } + return $data; + } + + /** + * @dataProvider providerZipfileOK + */ + public function testZipfile($options, $files, $description) { + $options = array_merge($options, array('outstream' => $this->outstream)); + $zip = new ZipStreamer($options); + foreach ($files as $file) { + if (File::DIR == $file->type) { + $zip->addEmptyDir($file->filename, array('timestamp' => $file->date)); + } else { + $stream = fopen('php://memory', 'r+'); + fwrite($stream, $file->data); + rewind($stream); + $zip->addFileFromStream($stream, $file->filename, array('timestamp' => $file->date)); + fclose($stream); + } + } + $zip->finalize(); + + $this->assertOutputZipfileOK($files, $options); + } + + /** https://github.com/McNetic/PHPZipStreamer/issues/29 + * ZipStreamer produces an error when the size of a file to be added is a + * multiple of the STREAM_CHUNK_SIZE (also for empty files) + */ + public function testIssue29() { + $options = array('zip64' => True,'compress' => COMPR::DEFLATE, 'outstream' => $this->outstream); + $zip = new ZipStreamer($options); + $stream = fopen('php://memory', 'r+'); + $zip->addFileFromStream($stream, "test.bin"); + fclose($stream); + $zip->finalize(); + } +} + +?> diff --git a/deepdiver/zipstreamer/test/bootstrap.php b/deepdiver/zipstreamer/test/bootstrap.php new file mode 100644 index 000000000..cf564ec7d --- /dev/null +++ b/deepdiver/zipstreamer/test/bootstrap.php @@ -0,0 +1,3 @@ + + +# This Dockerfile containerizes p7zip. +# +# You must run it using the correct UID/GID via the -u switch to `docker run` +# or the permissions will be wrong. +# +# Example usage +# docker run --rm -u "$(id -u):$(id -g)" -v "$(pwd)":/data datawraith/p7zip a archive.7z file1 file2 file3 + +RUN apk --update add \ + p7zip \ + && rm -rf /var/cache/apk/* + +RUN mkdir /data +WORKDIR /data + +ENTRYPOINT ["7z"] diff --git a/deepdiver/zipstreamer/test/integration/UnpackTest.php b/deepdiver/zipstreamer/test/integration/UnpackTest.php new file mode 100644 index 000000000..61b9e64ab --- /dev/null +++ b/deepdiver/zipstreamer/test/integration/UnpackTest.php @@ -0,0 +1,49 @@ + + * + */ + +class UnpackTest extends \PHPUnit\Framework\TestCase +{ + private $tmpfname; + + function setUp() + { + parent::setUp(); + + // create a zip file in tmp folder + $this->tmpfname = tempnam("/tmp", "FOO"); + $outstream = fopen($this->tmpfname, 'w'); + + $zip = new ZipStreamer\ZipStreamer((array( + 'outstream' => $outstream + ))); + $stream = fopen(__DIR__ . "/../../README.md", "r"); + $zip->addFileFromStream($stream, 'README.test'); + fclose($stream); + $zip->finalize(); + + fflush($outstream); + fclose($outstream); + } + + public function test7zip() { + $output = []; + $return_var = -1; + exec('docker run --rm -u "$(id -u):$(id -g)" -v /tmp:/data datawraith/p7zip t ' . escapeshellarg(basename($this->tmpfname)), $output, $return_var); + $fullOutput = implode("\n", $output); + $this->assertEquals($output[1], '7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21', $fullOutput); + $this->assertEquals(0, $return_var, $fullOutput); + $this->assertTrue(in_array('1 file, 943 bytes (1 KiB)', $output), $fullOutput); + } + + public function testUnzip() { + $output = []; + $return_var = -1; + exec('unzip -t ' . escapeshellarg($this->tmpfname), $output, $return_var); + $fullOutput = implode("\n", $output); + $this->assertEquals(0, $return_var, $fullOutput); + $this->assertTrue(in_array(' testing: README.test OK', $output), $fullOutput); + } +} diff --git a/deepdiver/zipstreamer/test/phpunit.xml b/deepdiver/zipstreamer/test/phpunit.xml new file mode 100644 index 000000000..eec7c7428 --- /dev/null +++ b/deepdiver/zipstreamer/test/phpunit.xml @@ -0,0 +1,14 @@ + + + + ZipStreamerTest.php + lib + integration + +