Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add data descriptor header #40

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/composer.phar
/vendor/
/composer.lock
40 changes: 17 additions & 23 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,37 +1,31 @@
language: php
sudo: false
dist: trusty
sudo: required

services:
- docker

env:
- PECL_HTTP_VERSION=1.7.6
- PECL_HTTP_VERSION=2.5.0
- PECL_HTTP_VERSION=3.0.0RC1
- PECL_HTTP_VERSION=3.1.1RC1

php:
- 7.2
- 7.1
- 7.0
- 5.6
- 5.5
- 5.4
- 5.3

before_install:
- sh -c "mkdir -p ${TRAVIS_BUILD_DIR}/travis/module-cache/`php-config --vernum`"

before_script:
- ./test/travis/php-modules-install.sh
- docker build -t datawraith/p7zip test/integration
- composer install --dev
- sudo apt-get install unzip libgnutls-dev
- pecl channel-update pecl.php.net
- printf "\n\n\n" | pecl install pecl_http-$PECL_HTTP_VERSION

script:
- phpunit --configuration test/phpunit.xml

cache:
directories:
- ${TRAVIS_BUILD_DIR}/travis/module-cache
- vendor/bin/phpunit --configuration test/phpunit.xml

matrix:
fast_finish: true
exclude:
- php: 5.5
env: PECL_HTTP_VERSION=1.7.6
include:
- php: 5.6
env: PECL_HTTP_VERSION=1.7.6
- php: 7.0
env: PECL_HTTP_VERSION=1.7.6
env: PECL_HTTP_VERSION=2.6.0

20 changes: 16 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "mcnetic/zipstreamer",
"name": "deepdiver/zipstreamer",
"type": "library",
"description": "Stream zip files without i/o overhead",
"keywords": ["zip", "stream"],
"homepage": "https://github.com/McNetic/PHPZipStreamer",
"homepage": "https://github.com/DeepDiver1975/PHPZipStreamer",
"license": "GPL-3.0+",
"version": "1.1.0",
"authors": [ {
"name": "Nicolai Ehemann",
"email": "[email protected]",
Expand All @@ -17,19 +18,30 @@
"name": "Lukas Reschke",
"email": "[email protected]",
"role": "Contributor"
},{
"name": "Thomas Müller",
"email": "[email protected]",
"role": "Contributor"
},{
"name": "Roeland Jago Douma",
"email": "[email protected]",
"role": "Contributor"
}
],
"repositories": [ {
"type": "vcs",
"url": "https://github.com/McNetic/PHPZipStreamer"
"url": "https://github.com/DeepDiver1975/PHPZipStreamer"
}
],
"require": {
"php": ">=5.3.0"
"php": ">=5.6.0"
},
"autoload": {
"psr-4": {
"ZipStreamer\\": "src/"
}
},
"require-dev": {
"phpunit/phpunit": "^5.7"
}
}
18 changes: 12 additions & 6 deletions src/ZipStreamer.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
*/
namespace ZipStreamer;

require "lib/Count64.php";
require_once __DIR__ . "/lib/Count64.php";

class COMPR {
// compression method
Expand All @@ -49,6 +49,7 @@ class ZipStreamer {
const VERSION = "1.0";

const ZIP_LOCAL_FILE_HEADER = 0x04034b50; // local file header signature
const ZIP_DATA_DESCRIPTOR_HEADER = 0x08074b50; //data descriptor header signature
const ZIP_CENTRAL_FILE_HEADER = 0x02014b50; // central file header signature
const ZIP_END_OF_CENTRAL_DIRECTORY = 0x06054b50; // end of central directory record
const ZIP64_END_OF_CENTRAL_DIRECTORY = 0x06064b50; //zip64 end of central directory record
Expand Down Expand Up @@ -76,8 +77,10 @@ class ZipStreamer {
private $offset;
/** @var boolean indicates zip is finalized and sent to client; no further addition possible */
private $isFinalized = false;
/** @var bool only used for unit testing */
public $turnOffOutputBuffering = true;

/**
/**
* Constructor. Initializes ZipStreamer object for immediate usage.
* @param array $options Optional, ZipStreamer and zip file options as key/value pairs.
* Valid options are:
Expand Down Expand Up @@ -178,7 +181,9 @@ public function sendHeaders($archiveName = 'archive.zip', $contentType = 'applic
}
$this->flush();
// turn off output buffering
@ob_end_flush();
if ($this->turnOffOutputBuffering) {
@ob_end_flush();
}
}

/**
Expand Down Expand Up @@ -427,16 +432,17 @@ private function buildLocalFileHeader($filePath, $timestamp, $gpFlags,

private function addDataDescriptor($dataLength, $gzLength, $dataCRC32) {
if ($this->zip64) {
$length = 20;
$length = 24;
$packedGzLength = pack64le($gzLength);
$packedDataLength = pack64le($dataLength);
} else {
$length = 12;
$length = 16;
$packedGzLength = pack32le($gzLength->getLoBytes());
$packedDataLength = pack32le($dataLength->getLoBytes());
}

$this->write(''
. pack32le(self::ZIP_DATA_DESCRIPTOR_HEADER) // data descriptor header signature 4 bytes (0x08074b50)
. pack32le($dataCRC32) // crc-32 4 bytes
. $packedGzLength // compressed size 4/8 bytes (depending on zip64 enabled)
. $packedDataLength // uncompressed size 4/8 bytes (depending on zip64 enabled)
Expand Down Expand Up @@ -525,7 +531,7 @@ private function buildCentralDirectoryHeader($filePath, $timestamp, $gpFlags,
private function buildEndOfCentralDirectoryRecord($cdRecLength) {
if ($this->zip64) {
$diskNumber = -1;
$cdRecCount = -1;
$cdRecCount = min(sizeof($this->cdRec), 0xffff);
$cdRecLength = -1;
$offset = -1;
} else {
Expand Down
2 changes: 0 additions & 2 deletions src/lib/Count64.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,5 +300,3 @@ public static function construct($value = 0, $limit32Bit = False) {
}
}
}

?>
19 changes: 14 additions & 5 deletions test/ZipComponents.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
*/
namespace ZipStreamer;

require_once "src/ZipStreamer.php";

/**
* @codeCoverageIgnore
*/
Expand Down Expand Up @@ -471,9 +469,9 @@ public function readFromString($str, $pos, $size = -1) {
}
if (GPFLAGS::ADD & $this->lfh->gpFlags) {
if (is_null($this->lfh->z64Ext)) {
$ddLength = 12;
$ddLength = 16;
} else {
$ddLength = 20;
$ddLength = 24;
}
$this->dd = DataDescriptor::constructFromString($str, $pos, $ddLength);
$pos = $this->dd->end + 1;
Expand Down Expand Up @@ -570,6 +568,7 @@ public function readFromString($str, $pos, $size = -1) {
* @codeCoverageIgnore
*/
class DataDescriptor extends zipRecord {
protected static $MAGIC = 0x08074b50; // data descriptor header signature
protected static $shortName = "DD";
public $dataCRC32;
public $sizeCompressed;
Expand All @@ -586,13 +585,23 @@ public function __toString() {
}

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 (20 == $size) {
if (24 == $size) {
$this->sizeCompressed = unpack64le(readstr($str, $pos, 8));
$this->size = unpack64le(readstr($str, $pos, 8));
} else {
Expand Down
13 changes: 7 additions & 6 deletions test/ZipStreamerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
*/
namespace ZipStreamer;

require "src/ZipStreamer.php";
require "test/ZipComponents.php";
require_once __DIR__ . "/../src/ZipStreamer.php";
require_once __DIR__ . "/ZipComponents.php";

class File {
const FILE = 1;
Expand Down Expand Up @@ -65,12 +65,12 @@ protected static function getVersionToExtract($zip64, $isDir) {
}

protected function assertOutputEqualsFile($filename) {
return $this->assertEquals(file_get_contents($filename), $this->getOutput());
$this->assertEquals(file_get_contents($filename), $this->getOutput());
}

protected function assertContainsOneMatch($pattern, $input) {
$results = preg_grep($pattern, $input);
return $this->assertEquals(1, sizeof($results));
$this->assertEquals(1, sizeof($results));
}

protected function assertOutputZipfileOK($files, $options) {
Expand All @@ -88,8 +88,8 @@ protected function assertOutputZipfileOK($files, $options) {
$eocdrec->assertValues(array(
"numberDisk" => 0xffff,
"numberDiskStartCD" => 0xffff,
"numberEntriesDisk" => 0xffff,
"numberEntriesCD" => 0xffff,
"numberEntriesDisk" => sizeof($files),
"numberEntriesCD" => sizeof($files),
"size" => 0xffffffff,
"offsetStart" => 0xffffffff,
"lengthComment" => 0,
Expand Down Expand Up @@ -311,6 +311,7 @@ public function testSendHeadersOKWithRegularBrowser(array $arguments,
$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();
Expand Down
19 changes: 19 additions & 0 deletions test/integration/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM alpine:3.6
MAINTAINER Johannes Holzfuß <[email protected]>

# 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"]
49 changes: 49 additions & 0 deletions test/integration/UnpackTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php
/**
* @author Thomas Müller <[email protected]>
*
*/

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);
}
}
3 changes: 3 additions & 0 deletions test/lib/Count64Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
* This file is licensed under the GNU GPL version 3 or later.
* See COPYING for details.
*/

require_once __DIR__ . '/../../src/lib/Count64.php';

class TestPack extends PHPUnit_Framework_TestCase
{
public function providerPack16leValues() {
Expand Down
4 changes: 3 additions & 1 deletion test/phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
debug="true"
>
<testsuite name="ZipStreamer">
<directory>./</directory>
<file>ZipStreamerTest.php</file>
<directory>lib</directory>
<directory>integration</directory>
</testsuite>
</phpunit>
Loading