aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/vendor/splitbrain/php-archive/src/Zip.php
diff options
context:
space:
mode:
authorAndreas Gohr <andi@splitbrain.org>2017-03-31 09:41:47 +0200
committerAndreas Gohr <andi@splitbrain.org>2017-03-31 09:41:47 +0200
commit361134418da8d033cd456de05c7a57c3cb528004 (patch)
tree115fa1cdc321b185da6d8285163fd3192c0c3e15 /vendor/splitbrain/php-archive/src/Zip.php
parente0dd796db52c9730bcf77d7776c8237fcee03811 (diff)
downloaddokuwiki-361134418da8d033cd456de05c7a57c3cb528004.tar.gz
dokuwiki-361134418da8d033cd456de05c7a57c3cb528004.zip
updated php-archive library
Diffstat (limited to 'vendor/splitbrain/php-archive/src/Zip.php')
-rw-r--r--vendor/splitbrain/php-archive/src/Zip.php139
1 files changed, 133 insertions, 6 deletions
diff --git a/vendor/splitbrain/php-archive/src/Zip.php b/vendor/splitbrain/php-archive/src/Zip.php
index 1bc1ac1b7..272a9027b 100644
--- a/vendor/splitbrain/php-archive/src/Zip.php
+++ b/vendor/splitbrain/php-archive/src/Zip.php
@@ -272,7 +272,7 @@ class Zip extends Archive
* Add a file to the current archive using an existing file in the filesystem
*
* @param string $file path to the original file
- * @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data, empty to take from original
+ * @param string|FileInfo $fileinfo either the name to use in archive (string) or a FileInfo oject with all meta data, empty to take from original
* @throws ArchiveIOException
*/
public function addFile($file, $fileinfo = '')
@@ -295,7 +295,7 @@ class Zip extends Archive
}
/**
- * Add a file to the current TAR archive using the given $data as content
+ * Add a file to the current Zip archive using the given $data as content
*
* @param string|FileInfo $fileinfo either the name to us in archive (string) or a FileInfo oject with all meta data
* @param string $data binary content of the file to add
@@ -495,8 +495,10 @@ class Zip extends Archive
if ($header['extra_len'] != 0) {
$header['extra'] = fread($this->fh, $header['extra_len']);
+ $header['extradata'] = $this->parseExtra($header['extra']);
} else {
$header['extra'] = '';
+ $header['extradata'] = array();
}
if ($header['comment_len'] != 0) {
@@ -536,8 +538,10 @@ class Zip extends Archive
$header['filename'] = fread($this->fh, $data['filename_len']);
if ($data['extra_len'] != 0) {
$header['extra'] = fread($this->fh, $data['extra_len']);
+ $header['extradata'] = array_merge($header['extradata'], $this->parseExtra($header['extra']));
} else {
$header['extra'] = '';
+ $header['extradata'] = array();
}
$header['compression'] = $data['compression'];
@@ -560,6 +564,35 @@ class Zip extends Archive
}
/**
+ * Parse the extra headers into fields
+ *
+ * @param string $header
+ * @return array
+ */
+ protected function parseExtra($header)
+ {
+ $extra = array();
+ // parse all extra fields as raw values
+ while (strlen($header) !== 0) {
+ $set = unpack('vid/vlen', $header);
+ $header = substr($header, 4);
+ $value = substr($header, 0, $set['len']);
+ $header = substr($header, $set['len']);
+ $extra[$set['id']] = $value;
+ }
+
+ // handle known ones
+ if(isset($extra[0x6375])) {
+ $extra['utf8comment'] = substr($extra[0x7075], 5); // strip version and crc
+ }
+ if(isset($extra[0x7075])) {
+ $extra['utf8path'] = substr($extra[0x7075], 5); // strip version and crc
+ }
+
+ return $extra;
+ }
+
+ /**
* Create fileinfo object from header data
*
* @param $header
@@ -568,16 +601,76 @@ class Zip extends Archive
protected function header2fileinfo($header)
{
$fileinfo = new FileInfo();
- $fileinfo->setPath($header['filename']);
$fileinfo->setSize($header['size']);
$fileinfo->setCompressedSize($header['compressed_size']);
$fileinfo->setMtime($header['mtime']);
$fileinfo->setComment($header['comment']);
$fileinfo->setIsdir($header['external'] == 0x41FF0010 || $header['external'] == 16);
+
+ if(isset($header['extradata']['utf8path'])) {
+ $fileinfo->setPath($header['extradata']['utf8path']);
+ } else {
+ $fileinfo->setPath($this->cpToUtf8($header['filename']));
+ }
+
+ if(isset($header['extradata']['utf8comment'])) {
+ $fileinfo->setComment($header['extradata']['utf8comment']);
+ } else {
+ $fileinfo->setComment($this->cpToUtf8($header['comment']));
+ }
+
return $fileinfo;
}
/**
+ * Convert the given CP437 encoded string to UTF-8
+ *
+ * Tries iconv with the correct encoding first, falls back to mbstring with CP850 which is
+ * similar enough. CP437 seems not to be available in mbstring. Lastly falls back to keeping the
+ * string as is, which is still better than nothing.
+ *
+ * @param $string
+ * @return string
+ */
+ protected function cpToUtf8($string)
+ {
+ if (function_exists('iconv')) {
+ return iconv('CP437', 'UTF-8', $string);
+ } elseif (function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding($string, 'UTF-8', 'CP850');
+ } else {
+ return $string;
+ }
+ }
+
+ /**
+ * Convert the given UTF-8 encoded string to CP437
+ *
+ * Same caveats as for cpToUtf8() apply
+ *
+ * @param $string
+ * @return string
+ */
+ protected function utf8ToCp($string)
+ {
+ // try iconv first
+ if (function_exists('iconv')) {
+ $conv = @iconv('UTF-8', 'CP437//IGNORE', $string);
+ if($conv) return $conv; // it worked
+ }
+
+ // still here? iconv failed to convert the string. Try another method
+ // see http://php.net/manual/en/function.iconv.php#108643
+
+ if (function_exists('mb_convert_encoding')) {
+ return mb_convert_encoding($string, 'CP850', 'UTF-8');
+ } else {
+ return $string;
+ }
+ }
+
+
+ /**
* Write to the open filepointer or memory
*
* @param string $data
@@ -684,6 +777,8 @@ class Zip extends Archive
$comp = $comp ? 8 : 0;
$dtime = dechex($this->makeDosTime($ts));
+ list($name, $extra) = $this->encodeFilename($name);
+
$header = "\x50\x4b\x01\x02"; // central file header signature
$header .= pack('v', 14); // version made by - VFAT
$header .= pack('v', 20); // version needed to extract - 2.0
@@ -700,13 +795,14 @@ class Zip extends Archive
$header .= pack('V', $clen); // compressed size
$header .= pack('V', $len); // uncompressed size
$header .= pack('v', strlen($name)); // file name length
- $header .= pack('v', 0); // extra field length
+ $header .= pack('v', strlen($extra)); // extra field length
$header .= pack('v', 0); // file comment length
$header .= pack('v', 0); // disk number start
$header .= pack('v', 0); // internal file attributes
$header .= pack('V', 0); // external file attributes @todo was 0x32!?
$header .= pack('V', $offset); // relative offset of local header
$header .= $name; // file name
+ $header .= $extra; // extra (utf-8 filename)
return $header;
}
@@ -728,6 +824,8 @@ class Zip extends Archive
$comp = $comp ? 8 : 0;
$dtime = dechex($this->makeDosTime($ts));
+ list($name, $extra) = $this->encodeFilename($name);
+
$header = "\x50\x4b\x03\x04"; // local file header signature
$header .= pack('v', 20); // version needed to extract - 2.0
$header .= pack('v', 0); // general purpose flag - no flags set
@@ -743,8 +841,37 @@ class Zip extends Archive
$header .= pack('V', $clen); // compressed size
$header .= pack('V', $len); // uncompressed size
$header .= pack('v', strlen($name)); // file name length
- $header .= pack('v', 0); // extra field length
- $header .= $name;
+ $header .= pack('v', strlen($extra)); // extra field length
+ $header .= $name; // file name
+ $header .= $extra; // extra (utf-8 filename)
return $header;
}
+
+ /**
+ * Returns an allowed filename and an extra field header
+ *
+ * When encoding stuff outside the 7bit ASCII range it needs to be placed in a separate
+ * extra field
+ *
+ * @param $original
+ * @return array($filename, $extra)
+ */
+ protected function encodeFilename($original)
+ {
+ $cp437 = $this->utf8ToCp($original);
+ if ($cp437 === $original) {
+ return array($original, '');
+ }
+
+ $extra = pack(
+ 'vvCV',
+ 0x7075, // tag
+ strlen($original) + 5, // length of file + version + crc
+ 1, // version
+ crc32($original) // crc
+ );
+ $extra .= $original;
+
+ return array($cp437, $extra);
+ }
}