From 46a8ebfe478f94048cd3a8873d6e6de8e236699a Mon Sep 17 00:00:00 2001 From: David M Date: Sun, 18 Dec 2005 04:35:51 +0000 Subject: This code works. All of it works. At least, it should. Extraction of TAR files: - It now works! - If it could work before (which it couldn't) , it would create nasty issues with spaces. This is resolved - If it could work before (which it couldn't) , it would try to create folders in such a way where some (all) folders could not be created. This is resolved. Creation of TAR files: - It now works! - Before, it created malformed TAR files. This is resolved. Now, the really really cool code... Extraction of ZIP files: - Totally new code - Fault tolerant - Very efficient Need sleep... *mumbles the TAR file header* git-svn-id: file:///svn/phpbb/trunk@5351 89ea8834-ac86-4346-8a33-228a782c2dd0 --- phpBB/includes/functions_compress.php | 304 ++++++++++++++++++---------------- 1 file changed, 163 insertions(+), 141 deletions(-) (limited to 'phpBB/includes/functions_compress.php') diff --git a/phpBB/includes/functions_compress.php b/phpBB/includes/functions_compress.php index 2c2d032b0e..8972f4f600 100644 --- a/phpBB/includes/functions_compress.php +++ b/phpBB/includes/functions_compress.php @@ -52,8 +52,7 @@ class compress if ($src_path) { - $mtime = (file_exists("$phpbb_root_path$src_path")) ? filemtime("$phpbb_root_path$src_path") : time(); - $this->data($src_path, '', $mtime, true); + $this->data($src_path, '', $mtime, true, stat("$phpbb_root_path$src_path")); } foreach ($filelist as $path => $file_ary) @@ -64,7 +63,7 @@ class compress $path = (substr($path, 0, 1) == '/') ? substr($path, 1) : $path; $path = ($path && substr($path, -1) != '/') ? $path . '/' : $path; - $this->data("$src_path$path", '', filemtime("$phpbb_root_path$src_path$path"), true); + $this->data("$src_path$path", '', true, stat("$phpbb_root_path$src_path$path")); } foreach ($file_ary as $file) @@ -74,7 +73,7 @@ class compress continue; } - $this->data("$src_path$path$file", implode('', file("$phpbb_root_path$src_path$path$file")), filemtime("$phpbb_root_path$src_path$path$file"), false); + $this->data("$src_path$path$file", implode('', file("$phpbb_root_path$src_path$path$file")), false, stat("$phpbb_root_path$src_path$path$file")); } } @@ -84,13 +83,17 @@ class compress function add_custom_file($src, $filename) { - $this->data($filename, implode('', file($src))); + $this->data($filename, implode('', file($src)), false, stat($src)); return true; } function add_data($src, $name) { - $this->data($name, $src); + $stat[2] = 436; //384 + $stat[4] = $stat[5] = 0; + $stat[7] = strlen($src); + $stat[9] = time(); + $this->data($name, $src, false, $stat); return true; } @@ -118,7 +121,9 @@ class compress * Zip creation class from phpMyAdmin 2.3.0 © Tobias Ratschiller, Olivier Müller, Loïc Chapeaux, * Marc Delisle, http://www.phpmyadmin.net/ * -* Modified extensively by psoTFX, © phpBB Group, 2003 +* Zip extraction function by Alexandre Tedeschi, alexandrebr at gmail dot com +* +* Modified extensively by psoTFX and DavidMJ, © phpBB Group, 2003 * * Based on work by Eric Mueller and Denis125 * Official ZIP file format: http://www.pkware.com/appnote.txt @@ -152,127 +157,129 @@ class compress_zip extends compress } function extract($dst) - { - $header = $data = ''; - $seek_ary = $mkdir_ary = array(); - $j = 0; - - fseek($this->fp, -14, SEEK_END); - $tmp = unpack("ventries/vtotentries/Vctsize/Vctpos", fread($this->fp, 12)); - $entries = (int) trim($tmp['entries']); - $totentries = (int) trim($tmp['totentries']); - $ctsize = (int) trim($tmp['ctsize']); - $ctpos = (int) trim($tmp['ctpos']); - - fseek($this->fp, $ctpos); - - // First scan entries, pull out position of data, length, etc. - // and directory structure - for ($i = 0; $i < $entries; $i++) - { - $buffer = fread($this->fp, 46); - - $tmp = unpack("vc_method/Vmtime/Vcrc/Vc_size/Vuc_size/vstrlen", substr($buffer, 10, 20)); - $c_method = (int) trim($tmp['c_method']); - $crc = (int) trim($tmp['crc']); - $strlen = (int) trim($tmp['strlen']); - $uc_size = (int) trim($tmp['uc_size']); - $c_size = (int) trim($tmp['c_size']); - - $tmp = unpack("Vattrib/Voffset", substr($buffer, 38, 8)); - $attrib = (int) trim($tmp['attrib']); - $offset = (int) trim($tmp['offset']); + { + // Loop the file, looking for files and folders + $ddTry = false; + fseek($this->fp, 0); - $filename = fread($this->fp, $strlen); - - if ($attrib == 16 || $attrib == 0x41FF0010 || (!$uc_size && !$crc)) - { - $mkdir_ary[] = "$dst$filename"; - } - else + while (true) + { + // Check if the signature is valid... + $signature = fread($this->fp, 4); + if (feof($this->fp)) { - $seek_ary[$j]['c_method'] = $c_method; - $seek_ary[$j]['crc'] = $crc; - $seek_ary[$j]['strlen'] = $strlen; - $seek_ary[$j]['uc_size'] = $uc_size; - $seek_ary[$j]['c_size'] = $c_size; - - $seek_ary[$j]['offset'] = $offset; - $seek_ary[$j]['filename'] = "$dst$filename"; - - $j++; + break; } - } - // Create directory structure on fs - if (is_array($mkdir_ary)) - { - sort($mkdir_ary); - foreach ($mkdir_ary as $dir) + switch ($signature) { - if (!@mkdir($dir, 0777)) - { - trigger_error("Could not create directory $dir"); - } - @chmod("$dir", 0777); + // 'Local File Header' + case "\x50\x4b\x03\x04": + // Get information about the zipped file but skip all the junk we don't need + fread($this->fp, 4); + $file['c_method'] = unpack("v", fread($this->fp, 2)); // compression method + fread($this->fp, 8); + $file['c_size'] = unpack("V", fread($this->fp, 4)); // compressed size + $file['uc_size'] = unpack("V", fread($this->fp, 4)); // uncompressed size + $file_name_length = unpack("v", fread($this->fp, 2)); // filename length + $extra_field_length = unpack("v", fread($this->fp, 2)); // extra field length + $file_name = fread($this->fp, $file_name_length[1]); // filename + fread($this->fp, $extra_field_length[1]); + $file['offset'] = ftell($this->fp); + + // Bypass the whole compressed contents, and look for the next file + fseek($this->fp, $file['c_size'][1], SEEK_CUR); + + // Mount file table + $seek_ary[$file_name] = array( + 'c_method' => $file['c_method'][1], + 'c_size' => $file['c_size'][1], + 'uc_size' => $file['uc_size'][1], + 'offset' => $file['offset'] + ); + break; + case "\x50\x4b\x01\x02": + fread($this->fp, 24); + fread($this->fp, 12 + current(unpack("v", fread($this->fp, 2))) + current(unpack("v", fread($this->fp, 2))) + current(unpack("v", fread($this->fp, 2)))); + break; + // We safely end the loop as we are totally finished with looking for files and folders + case "\x50\x4b\x05\x06": + break 2; + // Look for the next signature... + case 'PK00': + continue 2; + // We have encountered a header that is weird. Lets look for better data... + default: + if(!$ddTry) + { + // Unexpected header. Trying to detect wrong placed 'Data Descriptor'; + $ddTry = true; + fseek($this->fp, 8, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4) + continue 2; + } + trigger_error("Unexpected header, ending loop"); + break 2; } + $ddTry = false; } - - // Extract files - foreach ($seek_ary as $seek) + if (sizeof($seek_ary)) { - $filename = $seek['filename']; - -// fseek($this->fp, $seek['offset'] + 8); // To grab file header info -// fseek($this->fp, $seek['offset'] + 30 + $tmp['strlen'] + $tmp['c_size']); // To grab file header info2 - - // Jump to data - fseek($this->fp, $seek['offset'] + 30 + $seek['strlen']); - - // Was data compressed? If so we have to fudge a solution thanks - // to some "issues" with gzuncompress. Else we just write out the - // data - if ($seek['c_method'] == 8) + foreach ($seek_ary as $filename => $trash) { - // Temp gzip file -> .gz header -> data -> gz footer - if (!($fp = fopen($filename . '.gz', 'wb'))) + $dirname = dirname($filename); + + if (!is_dir("$dst$dirname")) { - trigger_error("Could not open temporary $filename.gz"); + $str = ''; + $folders = explode('/', $dirname); + // Create and folders and subfolders if they do not exist + foreach ($folders as $folder) + { + $str = (!empty($str)) ? $str . '/' . $folder : $folder; + if(!is_dir("$dst$str")) + { + if (!@mkdir("$dst$str", 0777)) + { + trigger_error("Could not create directory $dir"); + } + @chmod("$dir", 0777); + } + } } - fwrite($fp, pack('va1a1Va1a1', 0x8b1f, chr(0x08), chr(0x00), time(), chr(0x00), chr(3))); - fwrite($fp, fread($this->fp, $seek['c_size'])); - fwrite($fp, pack("VV", $seek['crc'], $seek['uc_size'])); - fclose($fp); - if (!($fp = fopen($filename, 'wb'))) + if(substr($filename, -1, 1) == '/') { - trigger_error("Could not create $filename"); + continue; } - @chmod($filename, 0777); - if (!($gzfp = gzopen($filename . '.gz', 'rb'))) - { - die("Could not open temporary $filename.gz"); - } + $target_filename = "$dst$filename"; + $fdetails = &$seek_ary[$filename]; - while ($buffer = gzread($gzfp, 1024)) + if(!$fdetails['uc_size']) { - fwrite($fp, $buffer); + $fp = fopen($target_filename, "w"); + fwrite($fp, ''); + fclose($fp); } - gzclose($gzfp); - fclose($fp); - unlink($filename . '.gz'); - } - else - { - if (!($fp = fopen($filename, 'wb'))) + + fseek($this->fp, $fdetails['offset']); + $mode = $fdetails['c_method']; + $content = fread($this->fp, $fdetails['c_size']); + switch($mode) { - trigger_error("Could not create $filename"); + case 0: + // Not compressed + $fp = fopen($target_filename, "w"); + fwrite($fp, $content); + fclose($fp); + break; + case 8: + // Deflate + $fp = fopen($target_filename, "w"); + fwrite($fp, gzinflate($content, $fdetails['uc_size'])); + fclose($fp); + break; } - @chmod($filename, 0777); - - fwrite($fp, fread($this->fp, $seek['uc_size'])); - fclose($fp); } } } @@ -288,11 +295,11 @@ class compress_zip extends compress } // Create the structures ... note we assume version made by is MSDOS - function data($name, $data, $mtime = false, $is_dir = false) + function data($name, $data, $is_dir = false, $stat) { $name = str_replace('\\', '/', $name); - $dtime = dechex($this->unix_to_dos_time($mtime)); + $dtime = dechex($this->unix_to_dos_time($stat[9])); $hexdtime = '\x' . $dtime[6] . $dtime[7] . '\x' . $dtime[4] . $dtime[5] . '\x' . $dtime[2] . $dtime[3] . '\x' . $dtime[0] . $dtime[1]; eval('$hexdtime = "' . $hexdtime . '";'); @@ -471,11 +478,20 @@ class compress_tar extends compress sort($mkdir_ary); foreach ($mkdir_ary as $dir) { - if (!@mkdir($dir, 0777)) + $folders = explode('/', $dir); + foreach ($folders as $folder) { - trigger_error("Could not create directory $dir"); + $str = (!empty($str)) ? $str . '/' . $folder : $folder; + if(!is_dir($str)) + { + if (!@mkdir($str, 0777)) + { + trigger_error("Could not create directory $folder"); + } + @chmod("$str", 0777); + } } - @chmod("$dir", 0777); + unset($str); } } @@ -505,14 +521,14 @@ class compress_tar extends compress $tmp = unpack("Atype", substr($buffer, 156, 1)); $filetype = (int) trim($tmp['type']); - if ($filetype == 0 || $filetype == "\0") - { - $tmp = unpack("A12size", substr($buffer, 124, 12)); - $filesize = octdec((int) trim($tmp['size'])); + $tmp = unpack("A12size", substr($buffer, 124, 12)); + $filesize = octdec((int) trim($tmp['size'])); + if ($filesize != 0 && ($filetype == 0 || $filetype == "\0")) + { if (!($fp = fopen("$dst$filename", 'wb'))) { - trigger_error("Could create file $filename"); + trigger_error("Couldn't create file $filename"); } @chmod("$dst$filename", 0777); @@ -524,7 +540,7 @@ class compress_tar extends compress $size += 512; $length = ($size > $filesize) ? 512 - ($size - $filesize) : 512; - $tmp = unpack("A512data", $buffer); + $tmp = unpack("a512data", $buffer); fwrite($fp, (string) $tmp['data'], $length); unset($buffer); @@ -533,33 +549,38 @@ class compress_tar extends compress function close() { + $fzwrite = ($this->isbz && function_exists('bzwrite')) ? 'bzwrite' : (($this->isgz && extension_loaded('zlib')) ? 'gzwrite' : 'fwrite'); $fzclose = ($this->isbz && function_exists('bzclose')) ? 'bzclose' : (($this->isgz && extension_loaded('zlib')) ? 'gzclose' : 'fclose'); + if ($this->wrote) $fzwrite($this->fp, pack("a1024", "")); $fzclose($this->fp); } - function data($name, $data, $mtime = false, $is_dir = false) + function data($name, $data, $is_dir = false, $stat) { + $this->wrote = true; $fzwrite = ($this->isbz && function_exists('bzwrite')) ? 'bzwrite' : (($this->isgz && extension_loaded('zlib')) ? 'gzwrite' : 'fwrite'); - $mode = ($is_dir) ? '493' : '436'; - $mtime = (!$mtime) ? time() : $mtime; - $filesize = ($is_dir) ? 0 : strlen($data); $typeflag = ($is_dir) ? '5' : ''; + // This is the header data, it contains all the info we know about the file or folder that we are about to archive $header = ''; $header .= pack("a100", $name); - $header .= pack("a8", sprintf("%07o", $mode)); - $header .= pack("a8", sprintf("%07o", 0)); - $header .= pack("a8", sprintf("%07o", 0)); - $header .= pack("a12", sprintf("%011o", $filesize)); - $header .= pack("A12", sprintf("%011o", $mtime)); // From a12 to A12 - $header .= ' '; - $header .= pack("a", $typeflag); + $header .= pack("a8", sprintf("%07o", $stat[2])); + $header .= pack("a8", sprintf("%07o", $stat[4])); + $header .= pack("a8", sprintf("%07o", $stat[5])); + $header .= pack("a12", sprintf("%011o", $stat[7])); + $header .= pack("a12", sprintf("%011o", $stat[9])); + $header .= pack("a8", ' '); + $header .= pack("a1", $typeflag); $header .= pack("a100", ''); - $header .= 'ustar'; - $header .= pack("x"); - $header .= '00'; - $header .= pack("x247"); + $header .= pack("a6", 'ustar'); + $header .= pack("a2", '00'); + $header .= pack("a32", 'Unknown'); + $header .= pack("a32", 'Unknown'); + $header .= pack("a8", ''); + $header .= pack("a8", ''); + $header .= pack("a155", ''); + $header .= pack("a12", ''); // Checksum $checksum = 0; @@ -568,18 +589,19 @@ class compress_tar extends compress $b = unpack("c1char", substr($header, $i, 1)); $checksum += $b['char']; } - $header = substr_replace($header, pack("a8",sprintf("%07o", $checksum)), 148, 8); + $header = substr_replace($header, pack("a8", sprintf("%07o", $checksum)), 148, 8); $fzwrite($this->fp, $header); - $i = 0; - // Read the data 512 bytes at a time and write it out - while ($buffer = substr($data, $i, 512)) + if ($stat[7] !== 0 && !$is_dir) { - $fzwrite($this->fp, pack("a512", $buffer)); - $i += 512; + $fzwrite($this->fp, $data); + unset($data); + if ($stat[7] % 512 > 0) + { + $fzwrite($this->fp, str_repeat("\0", 512 - $stat[7] % 512)); + } } - unset($data); } function open() -- cgit v1.2.1