Current oav website
This commit is contained in:
523
dotclear._no/inc/libs/clearbricks/zip/class.unzip.php
Normal file
523
dotclear._no/inc/libs/clearbricks/zip/class.unzip.php
Normal file
@ -0,0 +1,523 @@
|
||||
<?php
|
||||
/**
|
||||
* @class fileUnzip
|
||||
*
|
||||
* @package Clearbricks
|
||||
* @subpackage Zip
|
||||
*
|
||||
* @copyright Olivier Meunier & Association Dotclear
|
||||
* @copyright GPL-2.0-only
|
||||
*/
|
||||
|
||||
class fileUnzip
|
||||
{
|
||||
protected $file_name;
|
||||
protected $compressed_list = [];
|
||||
protected $eo_central = [];
|
||||
|
||||
protected $zip_sig = "\x50\x4b\x03\x04"; # local file header signature
|
||||
protected $dir_sig = "\x50\x4b\x01\x02"; # central dir header signature
|
||||
protected $dir_sig_e = "\x50\x4b\x05\x06"; # end of central dir signature
|
||||
protected $fp = null;
|
||||
|
||||
protected $memory_limit = null;
|
||||
|
||||
protected $exclude_pattern = '';
|
||||
|
||||
public function __construct($file_name)
|
||||
{
|
||||
$this->file_name = $file_name;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->fp) {
|
||||
fclose($this->fp);
|
||||
$this->fp = null;
|
||||
}
|
||||
|
||||
if ($this->memory_limit) {
|
||||
ini_set('memory_limit', $this->memory_limit);
|
||||
}
|
||||
}
|
||||
|
||||
public function getList($stop_on_file = false, $exclude = false)
|
||||
{
|
||||
if (!empty($this->compressed_list)) {
|
||||
return $this->compressed_list;
|
||||
}
|
||||
|
||||
if (!$this->loadFileListByEOF($stop_on_file, $exclude)) {
|
||||
if (!$this->loadFileListBySignatures($stop_on_file, $exclude)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->compressed_list;
|
||||
}
|
||||
|
||||
public function unzipAll($target)
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList();
|
||||
}
|
||||
|
||||
foreach ($this->compressed_list as $k => $v) {
|
||||
if ($v['is_dir']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->unzip($k, $target . '/' . $k);
|
||||
}
|
||||
}
|
||||
|
||||
public function unzip($file_name, $target = false)
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList($file_name);
|
||||
}
|
||||
|
||||
if (!isset($this->compressed_list[$file_name])) {
|
||||
throw new Exception(sprintf(__('File %s is not compressed in the zip.'), $file_name));
|
||||
}
|
||||
if ($this->isFileExcluded($file_name)) {
|
||||
return;
|
||||
}
|
||||
$details = &$this->compressed_list[$file_name];
|
||||
|
||||
if ($details['is_dir']) {
|
||||
throw new Exception(sprintf(__('Trying to unzip a folder name %s'), $file_name));
|
||||
}
|
||||
|
||||
if ($target) {
|
||||
$this->testTargetDir(dirname($target));
|
||||
}
|
||||
|
||||
if (!$details['uncompressed_size']) {
|
||||
return $this->putContent('', $target);
|
||||
}
|
||||
|
||||
fseek($this->fp(), $details['contents_start_offset']);
|
||||
|
||||
$this->memoryAllocate($details['compressed_size']);
|
||||
return $this->uncompress(
|
||||
fread($this->fp(), $details['compressed_size']),
|
||||
$details['compression_method'],
|
||||
$details['uncompressed_size'],
|
||||
$target
|
||||
);
|
||||
}
|
||||
|
||||
public function getFilesList()
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList();
|
||||
}
|
||||
|
||||
$res = [];
|
||||
foreach ($this->compressed_list as $k => $v) {
|
||||
if (!$v['is_dir']) {
|
||||
$res[] = $k;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function getDirsList()
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList();
|
||||
}
|
||||
|
||||
$res = [];
|
||||
foreach ($this->compressed_list as $k => $v) {
|
||||
if ($v['is_dir']) {
|
||||
$res[] = substr($k, 0, -1);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
public function getRootDir()
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList();
|
||||
}
|
||||
|
||||
$files = $this->getFilesList();
|
||||
$dirs = $this->getDirsList();
|
||||
|
||||
$root_files = 0;
|
||||
$root_dirs = 0;
|
||||
foreach ($files as $v) {if (strpos($v, '/') === false) {$root_files++;}}
|
||||
foreach ($dirs as $v) {if (strpos($v, '/') === false) {$root_dirs++;}}
|
||||
|
||||
if ($root_files == 0 && $root_dirs == 1) {
|
||||
return $dirs[0];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isEmpty()
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList();
|
||||
}
|
||||
|
||||
return count($this->compressed_list) == 0;
|
||||
}
|
||||
|
||||
public function hasFile($f)
|
||||
{
|
||||
if (empty($this->compressed_list)) {
|
||||
$this->getList();
|
||||
}
|
||||
|
||||
return isset($this->compressed_list[$f]);
|
||||
}
|
||||
|
||||
public function setExcludePattern($pattern)
|
||||
{
|
||||
$this->exclude_pattern = $pattern;
|
||||
}
|
||||
|
||||
protected function fp()
|
||||
{
|
||||
if ($this->fp === null) {
|
||||
$this->fp = @fopen($this->file_name, 'rb');
|
||||
}
|
||||
|
||||
if ($this->fp === false) {
|
||||
throw new Exception('Unable to open file.');
|
||||
}
|
||||
|
||||
return $this->fp;
|
||||
}
|
||||
|
||||
protected function isFileExcluded($f)
|
||||
{
|
||||
if (!$this->exclude_pattern) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return preg_match($this->exclude_pattern, $f);
|
||||
}
|
||||
|
||||
protected function putContent($content, $target = false)
|
||||
{
|
||||
if ($target) {
|
||||
$r = @file_put_contents($target, $content);
|
||||
if ($r === false) {
|
||||
throw new Exception(__('Unable to write destination file.'));
|
||||
}
|
||||
files::inheritChmod($target);
|
||||
return true;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function testTargetDir($dir)
|
||||
{
|
||||
if (is_dir($dir) && !is_writable($dir)) {
|
||||
throw new Exception(__('Unable to write in target directory, permission denied.'));
|
||||
}
|
||||
|
||||
if (!is_dir($dir)) {
|
||||
files::makeDir($dir, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected function uncompress($content, $mode, $size, $target = false)
|
||||
{
|
||||
switch ($mode) {
|
||||
case 0:
|
||||
# Not compressed
|
||||
$this->memoryAllocate($size * 2);
|
||||
return $this->putContent($content, $target);
|
||||
case 1:
|
||||
throw new Exception('Shrunk mode is not supported.');
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
throw new Exception('Compression factor ' . ($mode - 1) . ' is not supported.');
|
||||
case 6:
|
||||
throw new Exception('Implode is not supported.');
|
||||
case 7:
|
||||
throw new Exception('Tokenizing compression algorithm is not supported.');
|
||||
case 8:
|
||||
# Deflate
|
||||
if (!function_exists('gzinflate')) {
|
||||
throw new Exception('Gzip functions are not available.');
|
||||
}
|
||||
$this->memoryAllocate($size * 2);
|
||||
return $this->putContent(gzinflate($content, $size), $target);
|
||||
case 9:
|
||||
throw new Exception('Enhanced Deflating is not supported.');
|
||||
case 10:
|
||||
throw new Exception('PKWARE Date Compression Library Impoloding is not supported.');
|
||||
case 12:
|
||||
# Bzip2
|
||||
if (!function_exists('bzdecompress')) {
|
||||
throw new Exception('Bzip2 functions are not available.');
|
||||
}
|
||||
$this->memoryAllocate($size * 2);
|
||||
return $this->putContent(bzdecompress($content), $target);
|
||||
case 18:
|
||||
throw new Exception('IBM TERSE is not supported.');
|
||||
default:
|
||||
throw new Exception('Unknown uncompress method');
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadFileListByEOF($stop_on_file = false, $exclude = false)
|
||||
{
|
||||
$fp = $this->fp();
|
||||
|
||||
for ($x = 0; $x < 1024; $x++) {
|
||||
fseek($fp, -22 - $x, SEEK_END);
|
||||
$signature = fread($fp, 4);
|
||||
|
||||
if ($signature == $this->dir_sig_e) {
|
||||
$dir_list = [];
|
||||
|
||||
$eodir = [
|
||||
'disk_number_this' => unpack('v', fread($fp, 2)),
|
||||
'disk_number' => unpack('v', fread($fp, 2)),
|
||||
'total_entries_this' => unpack('v', fread($fp, 2)),
|
||||
'total_entries' => unpack('v', fread($fp, 2)),
|
||||
'size_of_cd' => unpack('V', fread($fp, 4)),
|
||||
'offset_start_cd' => unpack('V', fread($fp, 4))
|
||||
];
|
||||
|
||||
$zip_comment_len = unpack('v', fread($fp, 2));
|
||||
$eodir['zipfile_comment'] = $zip_comment_len[1] ? fread($fp, $zip_comment_len) : '';
|
||||
|
||||
$this->eo_central = [
|
||||
'disk_number_this' => $eodir['disk_number_this'][1],
|
||||
'disk_number' => $eodir['disk_number'][1],
|
||||
'total_entries_this' => $eodir['total_entries_this'][1],
|
||||
'total_entries' => $eodir['total_entries'][1],
|
||||
'size_of_cd' => $eodir['size_of_cd'][1],
|
||||
'offset_start_cd' => $eodir['offset_start_cd'][1],
|
||||
'zipfile_comment' => $eodir['zipfile_comment']
|
||||
];
|
||||
|
||||
fseek($fp, $this->eo_central['offset_start_cd']);
|
||||
$signature = fread($fp, 4);
|
||||
|
||||
while ($signature == $this->dir_sig) {
|
||||
$dir = [];
|
||||
$dir['version_madeby'] = unpack("v", fread($fp, 2)); # version made by
|
||||
$dir['version_needed'] = unpack("v", fread($fp, 2)); # version needed to extract
|
||||
$dir['general_bit_flag'] = unpack("v", fread($fp, 2)); # general purpose bit flag
|
||||
$dir['compression_method'] = unpack("v", fread($fp, 2)); # compression method
|
||||
$dir['lastmod_time'] = unpack("v", fread($fp, 2)); # last mod file time
|
||||
$dir['lastmod_date'] = unpack("v", fread($fp, 2)); # last mod file date
|
||||
$dir['crc-32'] = fread($fp, 4); # crc-32
|
||||
$dir['compressed_size'] = unpack("V", fread($fp, 4)); # compressed size
|
||||
$dir['uncompressed_size'] = unpack("V", fread($fp, 4)); # uncompressed size
|
||||
|
||||
$file_name_len = unpack("v", fread($fp, 2)); # filename length
|
||||
$extra_field_len = unpack("v", fread($fp, 2)); # extra field length
|
||||
$file_comment_len = unpack("v", fread($fp, 2)); # file comment length
|
||||
|
||||
$dir['disk_number_start'] = unpack("v", fread($fp, 2)); # disk number start
|
||||
$dir['internal_attributes'] = unpack("v", fread($fp, 2)); # internal file attributes-byte1
|
||||
$dir['external_attributes1'] = unpack("v", fread($fp, 2)); # external file attributes-byte2
|
||||
$dir['external_attributes2'] = unpack("v", fread($fp, 2)); # external file attributes
|
||||
$dir['relative_offset'] = unpack("V", fread($fp, 4)); # relative offset of local header
|
||||
$dir['file_name'] = $this->cleanFileName(fread($fp, $file_name_len[1])); # filename
|
||||
$dir['extra_field'] = $extra_field_len[1] ? fread($fp, $extra_field_len[1]) : ''; # extra field
|
||||
$dir['file_comment'] = $file_comment_len[1] ? fread($fp, $file_comment_len[1]) : ''; # file comment
|
||||
|
||||
$dir_list[$dir['file_name']] = [
|
||||
'version_madeby' => $dir['version_madeby'][1],
|
||||
'version_needed' => $dir['version_needed'][1],
|
||||
'general_bit_flag' => str_pad(decbin($dir['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
|
||||
'compression_method' => $dir['compression_method'][1],
|
||||
'lastmod_datetime' => $this->getTimeStamp($dir['lastmod_date'][1], $dir['lastmod_time'][1]),
|
||||
'crc-32' => str_pad(dechex(ord($dir['crc-32'][3])), 2, '0', STR_PAD_LEFT) .
|
||||
str_pad(dechex(ord($dir['crc-32'][2])), 2, '0', STR_PAD_LEFT) .
|
||||
str_pad(dechex(ord($dir['crc-32'][1])), 2, '0', STR_PAD_LEFT) .
|
||||
str_pad(dechex(ord($dir['crc-32'][0])), 2, '0', STR_PAD_LEFT),
|
||||
'compressed_size' => $dir['compressed_size'][1],
|
||||
'uncompressed_size' => $dir['uncompressed_size'][1],
|
||||
'disk_number_start' => $dir['disk_number_start'][1],
|
||||
'internal_attributes' => $dir['internal_attributes'][1],
|
||||
'external_attributes1' => $dir['external_attributes1'][1],
|
||||
'external_attributes2' => $dir['external_attributes2'][1],
|
||||
'relative_offset' => $dir['relative_offset'][1],
|
||||
'file_name' => $dir['file_name'],
|
||||
'extra_field' => $dir['extra_field'],
|
||||
'file_comment' => $dir['file_comment']
|
||||
];
|
||||
$signature = fread($fp, 4);
|
||||
}
|
||||
|
||||
foreach ($dir_list as $k => $v) {
|
||||
if ($exclude && preg_match($exclude, $k)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$i = $this->getFileHeaderInformation($v['relative_offset']);
|
||||
|
||||
$this->compressed_list[$k]['file_name'] = $k;
|
||||
$this->compressed_list[$k]['is_dir'] = $v['external_attributes1'] == 16 || substr($k, -1, 1) == '/';
|
||||
$this->compressed_list[$k]['compression_method'] = $v['compression_method'];
|
||||
$this->compressed_list[$k]['version_needed'] = $v['version_needed'];
|
||||
$this->compressed_list[$k]['lastmod_datetime'] = $v['lastmod_datetime'];
|
||||
$this->compressed_list[$k]['crc-32'] = $v['crc-32'];
|
||||
$this->compressed_list[$k]['compressed_size'] = $v['compressed_size'];
|
||||
$this->compressed_list[$k]['uncompressed_size'] = $v['uncompressed_size'];
|
||||
$this->compressed_list[$k]['lastmod_datetime'] = $v['lastmod_datetime'];
|
||||
$this->compressed_list[$k]['extra_field'] = $i['extra_field'];
|
||||
$this->compressed_list[$k]['contents_start_offset'] = $i['contents_start_offset'];
|
||||
|
||||
if (strtolower($stop_on_file) == strtolower($k)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function loadFileListBySignatures($stop_on_file = false, $exclude = false)
|
||||
{
|
||||
$fp = $this->fp();
|
||||
fseek($fp, 0);
|
||||
|
||||
$return = false;
|
||||
while (true) {
|
||||
$details = $this->getFileHeaderInformation();
|
||||
if (!$details) {
|
||||
fseek($fp, 12 - 4, SEEK_CUR); # 12: Data descriptor - 4: Signature (that will be read again)
|
||||
$details = $this->getFileHeaderInformation();
|
||||
}
|
||||
if (!$details) {
|
||||
break;
|
||||
}
|
||||
$filename = $details['file_name'];
|
||||
|
||||
if ($exclude && preg_match($exclude, $filename)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->compressed_list[$filename] = $details;
|
||||
$return = true;
|
||||
|
||||
if (strtolower($stop_on_file) == strtolower($filename)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected function getFileHeaderInformation($start_offset = false)
|
||||
{
|
||||
$fp = $this->fp();
|
||||
|
||||
if ($start_offset !== false) {
|
||||
fseek($fp, $start_offset);
|
||||
}
|
||||
|
||||
$signature = fread($fp, 4);
|
||||
if ($signature == $this->zip_sig) {
|
||||
# Get information about the zipped file
|
||||
$file = [];
|
||||
$file['version_needed'] = unpack("v", fread($fp, 2)); # version needed to extract
|
||||
$file['general_bit_flag'] = unpack("v", fread($fp, 2)); # general purpose bit flag
|
||||
$file['compression_method'] = unpack("v", fread($fp, 2)); # compression method
|
||||
$file['lastmod_time'] = unpack("v", fread($fp, 2)); # last mod file time
|
||||
$file['lastmod_date'] = unpack("v", fread($fp, 2)); # last mod file date
|
||||
$file['crc-32'] = fread($fp, 4); # crc-32
|
||||
$file['compressed_size'] = unpack("V", fread($fp, 4)); # compressed size
|
||||
$file['uncompressed_size'] = unpack("V", fread($fp, 4)); # uncompressed size
|
||||
|
||||
$file_name_len = unpack("v", fread($fp, 2)); # filename length
|
||||
$extra_field_len = unpack("v", fread($fp, 2)); # extra field length
|
||||
|
||||
$file['file_name'] = $this->cleanFileName(fread($fp, $file_name_len[1])); # filename
|
||||
$file['extra_field'] = $extra_field_len[1] ? fread($fp, $extra_field_len[1]) : ''; # extra field
|
||||
$file['contents_start_offset'] = ftell($fp);
|
||||
|
||||
# Look for the next file
|
||||
fseek($fp, $file['compressed_size'][1], SEEK_CUR);
|
||||
|
||||
# Mount file table
|
||||
$i = [
|
||||
'file_name' => $file['file_name'],
|
||||
'is_dir' => substr($file['file_name'], -1, 1) == '/',
|
||||
'compression_method' => $file['compression_method'][1],
|
||||
'version_needed' => $file['version_needed'][1],
|
||||
'lastmod_datetime' => $this->getTimeStamp($file['lastmod_date'][1], $file['lastmod_time'][1]),
|
||||
'crc-32' => str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT) .
|
||||
str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT) .
|
||||
str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT) .
|
||||
str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
|
||||
'compressed_size' => $file['compressed_size'][1],
|
||||
'uncompressed_size' => $file['uncompressed_size'][1],
|
||||
'extra_field' => $file['extra_field'],
|
||||
'general_bit_flag' => str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
|
||||
'contents_start_offset' => $file['contents_start_offset']
|
||||
];
|
||||
return $i;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getTimeStamp($date, $time)
|
||||
{
|
||||
$BINlastmod_date = str_pad(decbin($date), 16, '0', STR_PAD_LEFT);
|
||||
$BINlastmod_time = str_pad(decbin($time), 16, '0', STR_PAD_LEFT);
|
||||
$lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7)) + 1980;
|
||||
$lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
|
||||
$lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
|
||||
$lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
|
||||
$lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
|
||||
$lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5)) * 2;
|
||||
|
||||
return mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY);
|
||||
}
|
||||
|
||||
protected function cleanFileName($n)
|
||||
{
|
||||
$n = str_replace('../', '', $n);
|
||||
$n = preg_replace('#^/+#', '', $n);
|
||||
return $n;
|
||||
}
|
||||
|
||||
protected function memoryAllocate($size)
|
||||
{
|
||||
$mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000;
|
||||
$mem_limit = @ini_get('memory_limit');
|
||||
if ($mem_limit && trim($mem_limit) === '-1' || !files::str2bytes($mem_limit)) {
|
||||
// Cope with memory_limit set to -1 in PHP.ini
|
||||
return;
|
||||
}
|
||||
if ($mem_used && $mem_limit) {
|
||||
$mem_limit = files::str2bytes($mem_limit);
|
||||
$mem_avail = $mem_limit - $mem_used - (512 * 1024);
|
||||
$mem_needed = $size;
|
||||
|
||||
if ($mem_needed > $mem_avail) {
|
||||
if (@ini_set('memory_limit', $mem_limit + $mem_needed + $mem_used) === false) {
|
||||
throw new Exception(__('Not enough memory to open file.'));
|
||||
}
|
||||
|
||||
if (!$this->memory_limit) {
|
||||
$this->memory_limit = $mem_limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
356
dotclear._no/inc/libs/clearbricks/zip/class.zip.php
Normal file
356
dotclear._no/inc/libs/clearbricks/zip/class.zip.php
Normal file
@ -0,0 +1,356 @@
|
||||
<?php
|
||||
/**
|
||||
* @class fileZip
|
||||
*
|
||||
* @package Clearbricks
|
||||
* @subpackage Zip
|
||||
*
|
||||
* @copyright Olivier Meunier & Association Dotclear
|
||||
* @copyright GPL-2.0-only
|
||||
*/
|
||||
|
||||
class fileZip
|
||||
{
|
||||
protected $entries = [];
|
||||
protected $root_dir = null;
|
||||
|
||||
protected $ctrl_dir = [];
|
||||
protected $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
|
||||
protected $old_offset = 0;
|
||||
|
||||
protected $fp;
|
||||
protected $memory_limit = null;
|
||||
|
||||
protected $exclusions = [];
|
||||
|
||||
public function __construct($out_fp)
|
||||
{
|
||||
if (!is_resource($out_fp)) {
|
||||
throw new Exception('Output file descriptor is not a resource');
|
||||
}
|
||||
|
||||
if (!in_array(get_resource_type($out_fp), ['stream', 'file'])) {
|
||||
throw new Exception('Output file descriptor is not a valid resource');
|
||||
}
|
||||
|
||||
$this->fp = $out_fp;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if ($this->memory_limit) {
|
||||
ini_set('memory_limit', $this->memory_limit);
|
||||
}
|
||||
}
|
||||
|
||||
public function addExclusion($reg)
|
||||
{
|
||||
$this->exclusions[] = $reg;
|
||||
}
|
||||
|
||||
public function addFile($file, $name = null)
|
||||
{
|
||||
$file = preg_replace('#[\\\/]+#', '/', $file);
|
||||
|
||||
if (!$name) {
|
||||
$name = $file;
|
||||
}
|
||||
$name = $this->formatName($name);
|
||||
|
||||
if ($this->isExcluded($name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file_exists($file) || !is_file($file)) {
|
||||
throw new Exception(__('File does not exist'));
|
||||
}
|
||||
if (!is_readable($file)) {
|
||||
throw new Exception(__('Cannot read file'));
|
||||
}
|
||||
|
||||
$info = stat($file);
|
||||
|
||||
$this->entries[$name] = [
|
||||
'file' => $file,
|
||||
'is_dir' => false,
|
||||
'mtime' => $info['mtime'],
|
||||
'size' => $info['size']
|
||||
];
|
||||
}
|
||||
|
||||
public function addDirectory($dir, $name = null, $recursive = false)
|
||||
{
|
||||
$dir = preg_replace('#[\\\/]+#', '/', $dir);
|
||||
if (substr($dir, -1 - 1) != '/') {
|
||||
$dir .= '/';
|
||||
}
|
||||
|
||||
if (!$name && $name !== '') {
|
||||
$name = $dir;
|
||||
}
|
||||
|
||||
if ($this->isExcluded($name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($name !== '') {
|
||||
if (substr($name, -1, 1) != '/') {
|
||||
$name .= '/';
|
||||
}
|
||||
|
||||
$name = $this->formatName($name);
|
||||
|
||||
if ($name !== '') {
|
||||
$this->entries[$name] = [
|
||||
'file' => null,
|
||||
'is_dir' => true,
|
||||
'mtime' => time(),
|
||||
'size' => 0
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if ($recursive) {
|
||||
if (!is_dir($dir)) {
|
||||
throw new Exception(__('Directory does not exist'));
|
||||
}
|
||||
if (!is_readable($dir)) {
|
||||
throw new Exception(__('Cannot read directory'));
|
||||
}
|
||||
|
||||
$D = dir($dir);
|
||||
while (($e = $D->read()) !== false) {
|
||||
if ($e == '.' || $e == '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_dir($dir . '/' . $e)) {
|
||||
$this->addDirectory($dir . $e, $name . $e, true);
|
||||
} elseif (is_file($dir . '/' . $e)) {
|
||||
$this->addFile($dir . $e, $name . $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function write()
|
||||
{
|
||||
foreach ($this->entries as $name => $v) {
|
||||
if ($v['is_dir']) {
|
||||
$this->writeDirectory($name);
|
||||
} else {
|
||||
$this->writeFile($name, $v['file'], $v['size'], $v['mtime']);
|
||||
}
|
||||
}
|
||||
|
||||
$ctrldir = implode('', $this->ctrl_dir);
|
||||
|
||||
fwrite($this->fp,
|
||||
$ctrldir .
|
||||
$this->eof_ctrl_dir .
|
||||
pack('v', sizeof($this->ctrl_dir)) . # total # of entries "on this disk"
|
||||
pack('v', sizeof($this->ctrl_dir)) . # total # of entries overall
|
||||
pack('V', strlen($ctrldir)) . # size of central dir
|
||||
pack('V', $this->old_offset) . # offset to start of central dir
|
||||
"\x00\x00" # .zip file comment length
|
||||
);
|
||||
}
|
||||
|
||||
protected function writeDirectory($name)
|
||||
{
|
||||
if (!isset($this->entries[$name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mdate = $this->makeDate(time());
|
||||
$mtime = $this->makeTime(time());
|
||||
|
||||
# Data descriptor
|
||||
$data_desc =
|
||||
"\x50\x4b\x03\x04" .
|
||||
"\x0a\x00" . # ver needed to extract
|
||||
"\x00\x00" . # gen purpose bit flag
|
||||
"\x00\x00" . # compression method
|
||||
pack('v', $mtime) . # last mod time
|
||||
pack('v', $mdate) . # last mod date
|
||||
pack('V', 0) . # crc32
|
||||
pack('V', 0) . # compressed filesize
|
||||
pack('V', 0) . # uncompressed filesize
|
||||
pack('v', strlen($name)) . # length of pathname
|
||||
pack('v', 0) . # extra field length
|
||||
$name . # end of "local file header" segment
|
||||
pack('V', 0) . # crc32
|
||||
pack('V', 0) . # compressed filesize
|
||||
pack('V', 0); # uncompressed filesize
|
||||
|
||||
$new_offset = $this->old_offset + strlen($data_desc);
|
||||
fwrite($this->fp, $data_desc);
|
||||
|
||||
# Add to central record
|
||||
$cdrec =
|
||||
"\x50\x4b\x01\x02" .
|
||||
"\x00\x00" . # version made by
|
||||
"\x0a\x00" . # version needed to extract
|
||||
"\x00\x00" . # gen purpose bit flag
|
||||
"\x00\x00" . # compression method
|
||||
pack('v', $mtime) . # last mod time
|
||||
pack('v', $mdate) . # last mod date
|
||||
pack('V', 0) . # crc32
|
||||
pack('V', 0) . # compressed filesize
|
||||
pack('V', 0) . # uncompressed filesize
|
||||
pack('v', strlen($name)) . # length of filename
|
||||
pack('v', 0) . # extra field length
|
||||
pack('v', 0) . # file comment length
|
||||
pack('v', 0) . # disk number start
|
||||
pack('v', 0) . # internal file attributes
|
||||
pack('V', 16) . # external file attributes - 'directory' bit set
|
||||
pack('V', $this->old_offset) . # relative offset of local header
|
||||
$name;
|
||||
|
||||
$this->old_offset = $new_offset;
|
||||
$this->ctrl_dir[] = $cdrec;
|
||||
}
|
||||
|
||||
protected function writeFile($name, $file, $size, $mtime)
|
||||
{
|
||||
if (!isset($this->entries[$name])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$size = filesize($file);
|
||||
$this->memoryAllocate($size * 3);
|
||||
|
||||
$content = file_get_contents($file);
|
||||
|
||||
$unc_len = strlen($content);
|
||||
$crc = crc32($content);
|
||||
$zdata = gzdeflate($content);
|
||||
$c_len = strlen($zdata);
|
||||
|
||||
unset($content);
|
||||
|
||||
$mdate = $this->makeDate($mtime);
|
||||
$mtime = $this->makeTime($mtime);
|
||||
|
||||
# Data descriptor
|
||||
$data_desc =
|
||||
"\x50\x4b\x03\x04" .
|
||||
"\x14\x00" . # ver needed to extract
|
||||
"\x00\x00" . # gen purpose bit flag
|
||||
"\x08\x00" . # compression method
|
||||
pack('v', $mtime) . # last mod time
|
||||
pack('v', $mdate) . # last mod date
|
||||
pack('V', $crc) . # crc32
|
||||
pack('V', $c_len) . # compressed filesize
|
||||
pack('V', $unc_len) . # uncompressed filesize
|
||||
pack('v', strlen($name)) . # length of filename
|
||||
pack('v', 0) . # extra field length
|
||||
$name . # end of "local file header" segment
|
||||
$zdata . # "file data" segment
|
||||
pack('V', $crc) . # crc32
|
||||
pack('V', $c_len) . # compressed filesize
|
||||
pack('V', $unc_len); # uncompressed filesize
|
||||
|
||||
fwrite($this->fp, $data_desc);
|
||||
unset($zdata);
|
||||
|
||||
$new_offset = $this->old_offset + strlen($data_desc);
|
||||
|
||||
# Add to central directory record
|
||||
$cdrec =
|
||||
"\x50\x4b\x01\x02" .
|
||||
"\x00\x00" . # version made by
|
||||
"\x14\x00" . # version needed to extract
|
||||
"\x00\x00" . # gen purpose bit flag
|
||||
"\x08\x00" . # compression method
|
||||
pack('v', $mtime) . # last mod time
|
||||
pack('v', $mdate) . # last mod date
|
||||
pack('V', $crc) . # crc32
|
||||
pack('V', $c_len) . # compressed filesize
|
||||
pack('V', $unc_len) . # uncompressed filesize
|
||||
pack('v', strlen($name)) . # length of filename
|
||||
pack('v', 0) . # extra field length
|
||||
pack('v', 0) . # file comment length
|
||||
pack('v', 0) . # disk number start
|
||||
pack('v', 0) . # internal file attributes
|
||||
pack('V', 32) . # external file attributes - 'archive' bit set
|
||||
pack('V', $this->old_offset) . # relative offset of local header
|
||||
$name;
|
||||
|
||||
$this->old_offset = $new_offset;
|
||||
$this->ctrl_dir[] = $cdrec;
|
||||
}
|
||||
|
||||
protected function formatName($name)
|
||||
{
|
||||
if (substr($name, 0, 1) == '/') {
|
||||
$name = substr($name, 1);
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
protected function isExcluded($name)
|
||||
{
|
||||
foreach ($this->exclusions as $reg) {
|
||||
if (preg_match($reg, $name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function makeDate($ts)
|
||||
{
|
||||
$year = date('Y', $ts) - 1980;
|
||||
if ($year < 0) {
|
||||
$year = 0;
|
||||
}
|
||||
|
||||
$year = sprintf('%07b', $year);
|
||||
$month = sprintf('%04b', date('n', $ts));
|
||||
$day = sprintf('%05b', date('j', $ts));
|
||||
|
||||
return bindec($year . $month . $day);
|
||||
}
|
||||
|
||||
protected function makeTime($ts)
|
||||
{
|
||||
$hour = sprintf('%05b', date('G', $ts));
|
||||
$minute = sprintf('%06b', date('i', $ts));
|
||||
$second = sprintf('%05b', ceil(date('s', $ts) / 2));
|
||||
|
||||
return bindec($hour . $minute . $second);
|
||||
}
|
||||
|
||||
protected function memoryAllocate($size)
|
||||
{
|
||||
$mem_used = function_exists('memory_get_usage') ? @memory_get_usage() : 4000000;
|
||||
$mem_limit = @ini_get('memory_limit');
|
||||
if ($mem_limit && trim($mem_limit) === '-1' || !files::str2bytes($mem_limit)) {
|
||||
// Cope with memory_limit set to -1 in PHP.ini
|
||||
return;
|
||||
}
|
||||
if ($mem_used && $mem_limit) {
|
||||
$mem_limit = files::str2bytes($mem_limit);
|
||||
$mem_avail = $mem_limit - $mem_used - (512 * 1024);
|
||||
$mem_needed = $size;
|
||||
|
||||
if ($mem_needed > $mem_avail) {
|
||||
if (@ini_set('memory_limit', $mem_limit + $mem_needed + $mem_used) === false) {
|
||||
throw new Exception(__('Not enough memory to open file.'));
|
||||
}
|
||||
|
||||
if (!$this->memory_limit) {
|
||||
$this->memory_limit = $mem_limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user