Translated using Weblate (Estonian)
[phpmyadmin.git] / libraries / classes / ZipExtension.php
blobdc8f48c6168e496897726fc5bf2d2bc4801d9623
1 <?php
2 /* vim: set expandtab sw=4 ts=4 sts=4: */
3 /**
4 * Interface for the zip extension
6 * @package PhpMyAdmin
7 */
8 declare(strict_types=1);
10 namespace PhpMyAdmin;
12 use ZipArchive;
14 /**
15 * Transformations class
17 * @package PhpMyAdmin
19 class ZipExtension
21 /**
22 * @var ZipArchive
24 private $zip;
26 /**
27 * Constructor
29 public function __construct()
31 $this->zip = new ZipArchive();
34 /**
35 * Gets zip file contents
37 * @param string $file path to zip file
38 * @param string $specific_entry regular expression to match a file
40 * @return array ($error_message, $file_data); $error_message
41 * is empty if no error
43 public function getContents($file, $specific_entry = null)
45 /**
46 * This function is used to "import" a SQL file which has been exported earlier
47 * That means that this function works on the assumption that the zip file contains only a single SQL file
48 * It might also be an ODS file, look below
51 $error_message = '';
52 $file_data = '';
54 $res = $this->zip->open($file);
56 if ($res === true) {
57 if ($this->zip->numFiles === 0) {
58 $error_message = __('No files found inside ZIP archive!');
59 $this->zip->close();
60 return [
61 'error' => $error_message,
62 'data' => $file_data,
66 /* Is the the zip really an ODS file? */
67 $ods_mime = 'application/vnd.oasis.opendocument.spreadsheet';
68 $first_zip_entry = $this->zip->getFromIndex(0);
69 if (! strcmp($ods_mime, $first_zip_entry)) {
70 $specific_entry = '/^content\.xml$/';
73 if (! isset($specific_entry)) {
74 $file_data = $first_zip_entry;
75 $this->zip->close();
76 return [
77 'error' => $error_message,
78 'data' => $file_data,
82 /* Return the correct contents, not just the first entry */
83 for ($i = 0; $i < $this->zip->numFiles; $i++) {
84 if (@preg_match($specific_entry, $this->zip->getNameIndex($i))) {
85 $file_data = $this->zip->getFromIndex($i);
86 break;
90 /* Couldn't find any files that matched $specific_entry */
91 if (empty($file_data)) {
92 $error_message = __('Error in ZIP archive:')
93 . ' Could not find "' . $specific_entry . '"';
96 $this->zip->close();
97 return [
98 'error' => $error_message,
99 'data' => $file_data,
101 } else {
102 $error_message = __('Error in ZIP archive:') . ' ' . $this->zip->getStatusString();
103 $this->zip->close();
104 return [
105 'error' => $error_message,
106 'data' => $file_data,
112 * Returns the filename of the first file that matches the given $file_regexp.
114 * @param string $file path to zip file
115 * @param string $regex regular expression for the file name to match
117 * @return string|false the file name of the first file that matches the given regular expression
119 public function findFile($file, $regex)
121 $res = $this->zip->open($file);
123 if ($res === true) {
124 for ($i = 0; $i < $this->zip->numFiles; $i++) {
125 if (preg_match($regex, $this->zip->getNameIndex($i))) {
126 $filename = $this->zip->getNameIndex($i);
127 $this->zip->close();
128 return $filename;
132 return false;
136 * Returns the number of files in the zip archive.
138 * @param string $file path to zip file
140 * @return int the number of files in the zip archive or 0, either if there wern't any files or an error occured.
142 public function getNumberOfFiles($file)
144 $num = 0;
145 $res = $this->zip->open($file);
147 if ($res === true) {
148 $num = $this->zip->numFiles;
150 return $num;
154 * Extracts the content of $entry.
156 * @param string $file path to zip file
157 * @param string $entry file in the archive that should be extracted
159 * @return string|bool data on sucess, false otherwise
161 public function extract($file, $entry)
163 if ($this->zip->open($file) === true) {
164 $result = $this->zip->getFromName($entry);
165 $this->zip->close();
166 return $result;
168 return false;
172 * Creates a zip file.
173 * If $data is an array and $name is a string, the filenames will be indexed.
174 * The function will return false if $data is a string but $name is an array
175 * or if $data is an array and $name is an array, but they don't have the
176 * same amount of elements.
178 * @param array|string $data contents of the file/files
179 * @param array|string $name name of the file/files in the archive
180 * @param integer $time the current timestamp
182 * @return string|bool the ZIP file contents, or false if there was an error.
184 public function createFile($data, $name, $time = 0)
186 $datasec = []; // Array to store compressed data
187 $ctrl_dir = []; // Central directory
188 $old_offset = 0; // Last offset position
189 $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00"; // End of central directory record
191 if (is_string($data) && is_string($name)) {
192 $data = [$name => $data];
193 } elseif (is_array($data) && is_string($name)) {
194 $ext_pos = strpos($name, '.');
195 $extension = substr($name, $ext_pos);
196 $newData = [];
197 foreach ($data as $key => $value) {
198 $newName = str_replace(
199 $extension,
200 '_' . $key . $extension,
201 $name
203 $newData[$newName] = $value;
205 $data = $newData;
206 } elseif (is_array($data) && is_array($name) && count($data) === count($name)) {
207 $data = array_combine($name, $data);
208 } else {
209 return false;
212 foreach ($data as $table => $dump) {
213 $temp_name = str_replace('\\', '/', $table);
215 /* Get Local Time */
216 $timearray = getdate();
218 if ($timearray['year'] < 1980) {
219 $timearray['year'] = 1980;
220 $timearray['mon'] = 1;
221 $timearray['mday'] = 1;
222 $timearray['hours'] = 0;
223 $timearray['minutes'] = 0;
224 $timearray['seconds'] = 0;
227 $time = (($timearray['year'] - 1980) << 25)
228 | ($timearray['mon'] << 21)
229 | ($timearray['mday'] << 16)
230 | ($timearray['hours'] << 11)
231 | ($timearray['minutes'] << 5)
232 | ($timearray['seconds'] >> 1);
234 $hexdtime = pack('V', $time);
236 $unc_len = strlen($dump);
237 $crc = crc32($dump);
238 $zdata = gzcompress($dump);
239 $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
240 $c_len = strlen($zdata);
241 $fr = "\x50\x4b\x03\x04"
242 . "\x14\x00" // ver needed to extract
243 . "\x00\x00" // gen purpose bit flag
244 . "\x08\x00" // compression method
245 . $hexdtime // last mod time and date
247 // "local file header" segment
248 . pack('V', $crc) // crc32
249 . pack('V', $c_len) // compressed filesize
250 . pack('V', $unc_len) // uncompressed filesize
251 . pack('v', strlen($temp_name)) // length of filename
252 . pack('v', 0) // extra field length
253 . $temp_name
255 // "file data" segment
256 . $zdata;
258 $datasec[] = $fr;
260 // now add to central directory record
261 $cdrec = "\x50\x4b\x01\x02"
262 . "\x00\x00" // version made by
263 . "\x14\x00" // version needed to extract
264 . "\x00\x00" // gen purpose bit flag
265 . "\x08\x00" // compression method
266 . $hexdtime // last mod time & date
267 . pack('V', $crc) // crc32
268 . pack('V', $c_len) // compressed filesize
269 . pack('V', $unc_len) // uncompressed filesize
270 . pack('v', strlen($temp_name)) // length of filename
271 . pack('v', 0) // extra field length
272 . pack('v', 0) // file comment length
273 . pack('v', 0) // disk number start
274 . pack('v', 0) // internal file attributes
275 . pack('V', 32) // external file attributes
276 // - 'archive' bit set
277 . pack('V', $old_offset) // relative offset of local header
278 . $temp_name; // filename
279 $old_offset += strlen($fr);
280 // optional extra field, file comment goes here
281 // save to central directory
282 $ctrl_dir[] = $cdrec;
285 /* Build string to return */
286 $temp_ctrldir = implode('', $ctrl_dir);
287 $header = $temp_ctrldir .
288 $eof_ctrl_dir .
289 pack('v', count($ctrl_dir)) . //total #of entries "on this disk"
290 pack('v', count($ctrl_dir)) . //total #of entries overall
291 pack('V', strlen($temp_ctrldir)) . //size of central dir
292 pack('V', $old_offset) . //offset to start of central dir
293 "\x00\x00"; //.zip file comment length
295 $data = implode('', $datasec);
297 return $data . $header;