Seditio Source
Root |
./othercms/dotclear-2.22/inc/libs/clearbricks/zip/class.zip.php
<?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('#[\\\/]+#', '/', (string) $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('#[\\\/]+#', '/', (string) $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((string) $reg, (string) $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((string) $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', (string) ($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;
                }
            }
        }
    }
}