diff options
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/ZipExtension.php')
| -rw-r--r-- | srcs/phpmyadmin/libraries/classes/ZipExtension.php | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/ZipExtension.php b/srcs/phpmyadmin/libraries/classes/ZipExtension.php new file mode 100644 index 0000000..dc8f48c --- /dev/null +++ b/srcs/phpmyadmin/libraries/classes/ZipExtension.php @@ -0,0 +1,299 @@ +<?php +/* vim: set expandtab sw=4 ts=4 sts=4: */ +/** + * Interface for the zip extension + * + * @package PhpMyAdmin + */ +declare(strict_types=1); + +namespace PhpMyAdmin; + +use ZipArchive; + +/** + * Transformations class + * + * @package PhpMyAdmin + */ +class ZipExtension +{ + /** + * @var ZipArchive + */ + private $zip; + + /** + * Constructor + */ + public function __construct() + { + $this->zip = new ZipArchive(); + } + + /** + * Gets zip file contents + * + * @param string $file path to zip file + * @param string $specific_entry regular expression to match a file + * + * @return array ($error_message, $file_data); $error_message + * is empty if no error + */ + public function getContents($file, $specific_entry = null) + { + /** + * This function is used to "import" a SQL file which has been exported earlier + * That means that this function works on the assumption that the zip file contains only a single SQL file + * It might also be an ODS file, look below + */ + + $error_message = ''; + $file_data = ''; + + $res = $this->zip->open($file); + + if ($res === true) { + if ($this->zip->numFiles === 0) { + $error_message = __('No files found inside ZIP archive!'); + $this->zip->close(); + return [ + 'error' => $error_message, + 'data' => $file_data, + ]; + } + + /* Is the the zip really an ODS file? */ + $ods_mime = 'application/vnd.oasis.opendocument.spreadsheet'; + $first_zip_entry = $this->zip->getFromIndex(0); + if (! strcmp($ods_mime, $first_zip_entry)) { + $specific_entry = '/^content\.xml$/'; + } + + if (! isset($specific_entry)) { + $file_data = $first_zip_entry; + $this->zip->close(); + return [ + 'error' => $error_message, + 'data' => $file_data, + ]; + } + + /* Return the correct contents, not just the first entry */ + for ($i = 0; $i < $this->zip->numFiles; $i++) { + if (@preg_match($specific_entry, $this->zip->getNameIndex($i))) { + $file_data = $this->zip->getFromIndex($i); + break; + } + } + + /* Couldn't find any files that matched $specific_entry */ + if (empty($file_data)) { + $error_message = __('Error in ZIP archive:') + . ' Could not find "' . $specific_entry . '"'; + } + + $this->zip->close(); + return [ + 'error' => $error_message, + 'data' => $file_data, + ]; + } else { + $error_message = __('Error in ZIP archive:') . ' ' . $this->zip->getStatusString(); + $this->zip->close(); + return [ + 'error' => $error_message, + 'data' => $file_data, + ]; + } + } + + /** + * Returns the filename of the first file that matches the given $file_regexp. + * + * @param string $file path to zip file + * @param string $regex regular expression for the file name to match + * + * @return string|false the file name of the first file that matches the given regular expression + */ + public function findFile($file, $regex) + { + $res = $this->zip->open($file); + + if ($res === true) { + for ($i = 0; $i < $this->zip->numFiles; $i++) { + if (preg_match($regex, $this->zip->getNameIndex($i))) { + $filename = $this->zip->getNameIndex($i); + $this->zip->close(); + return $filename; + } + } + } + return false; + } + + /** + * Returns the number of files in the zip archive. + * + * @param string $file path to zip file + * + * @return int the number of files in the zip archive or 0, either if there wern't any files or an error occured. + */ + public function getNumberOfFiles($file) + { + $num = 0; + $res = $this->zip->open($file); + + if ($res === true) { + $num = $this->zip->numFiles; + } + return $num; + } + + /** + * Extracts the content of $entry. + * + * @param string $file path to zip file + * @param string $entry file in the archive that should be extracted + * + * @return string|bool data on sucess, false otherwise + */ + public function extract($file, $entry) + { + if ($this->zip->open($file) === true) { + $result = $this->zip->getFromName($entry); + $this->zip->close(); + return $result; + } + return false; + } + + /** + * Creates a zip file. + * If $data is an array and $name is a string, the filenames will be indexed. + * The function will return false if $data is a string but $name is an array + * or if $data is an array and $name is an array, but they don't have the + * same amount of elements. + * + * @param array|string $data contents of the file/files + * @param array|string $name name of the file/files in the archive + * @param integer $time the current timestamp + * + * @return string|bool the ZIP file contents, or false if there was an error. + */ + public function createFile($data, $name, $time = 0) + { + $datasec = []; // Array to store compressed data + $ctrl_dir = []; // Central directory + $old_offset = 0; // Last offset position + $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; // End of central directory record + + if (is_string($data) && is_string($name)) { + $data = [$name => $data]; + } elseif (is_array($data) && is_string($name)) { + $ext_pos = strpos($name, '.'); + $extension = substr($name, $ext_pos); + $newData = []; + foreach ($data as $key => $value) { + $newName = str_replace( + $extension, + '_' . $key . $extension, + $name + ); + $newData[$newName] = $value; + } + $data = $newData; + } elseif (is_array($data) && is_array($name) && count($data) === count($name)) { + $data = array_combine($name, $data); + } else { + return false; + } + + foreach ($data as $table => $dump) { + $temp_name = str_replace('\\', '/', $table); + + /* Get Local Time */ + $timearray = getdate(); + + if ($timearray['year'] < 1980) { + $timearray['year'] = 1980; + $timearray['mon'] = 1; + $timearray['mday'] = 1; + $timearray['hours'] = 0; + $timearray['minutes'] = 0; + $timearray['seconds'] = 0; + } + + $time = (($timearray['year'] - 1980) << 25) + | ($timearray['mon'] << 21) + | ($timearray['mday'] << 16) + | ($timearray['hours'] << 11) + | ($timearray['minutes'] << 5) + | ($timearray['seconds'] >> 1); + + $hexdtime = pack('V', $time); + + $unc_len = strlen($dump); + $crc = crc32($dump); + $zdata = gzcompress($dump); + $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug + $c_len = strlen($zdata); + $fr = "\x50\x4b\x03\x04" + . "\x14\x00" // ver needed to extract + . "\x00\x00" // gen purpose bit flag + . "\x08\x00" // compression method + . $hexdtime // last mod time and date + + // "local file header" segment + . pack('V', $crc) // crc32 + . pack('V', $c_len) // compressed filesize + . pack('V', $unc_len) // uncompressed filesize + . pack('v', strlen($temp_name)) // length of filename + . pack('v', 0) // extra field length + . $temp_name + + // "file data" segment + . $zdata; + + $datasec[] = $fr; + + // now 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 + . $hexdtime // last mod time & date + . pack('V', $crc) // crc32 + . pack('V', $c_len) // compressed filesize + . pack('V', $unc_len) // uncompressed filesize + . pack('v', strlen($temp_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', $old_offset) // relative offset of local header + . $temp_name; // filename + $old_offset += strlen($fr); + // optional extra field, file comment goes here + // save to central directory + $ctrl_dir[] = $cdrec; + } + + /* Build string to return */ + $temp_ctrldir = implode('', $ctrl_dir); + $header = $temp_ctrldir . + $eof_ctrl_dir . + pack('v', count($ctrl_dir)) . //total #of entries "on this disk" + pack('v', count($ctrl_dir)) . //total #of entries overall + pack('V', strlen($temp_ctrldir)) . //size of central dir + pack('V', $old_offset) . //offset to start of central dir + "\x00\x00"; //.zip file comment length + + $data = implode('', $datasec); + + return $data . $header; + } +} |
