Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
70.83% |
221 / 312 |
|
38.89% |
7 / 18 |
CRAP | |
0.00% |
0 / 3 |
compress | |
80.85% |
38 / 47 |
|
40.00% |
2 / 5 |
24.10 | |
0.00% |
0 / 1 |
add_file | |
94.44% |
17 / 18 |
|
0.00% |
0 / 1 |
12.02 | |||
add_custom_file | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
add_data | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
unique_filename | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
methods | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
compress_zip | |
65.79% |
100 / 152 |
|
28.57% |
2 / 7 |
135.45 | |
0.00% |
0 / 1 |
__construct | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
3.07 | |||
unix_to_dos_time | |
50.00% |
3 / 6 |
|
0.00% |
0 / 1 |
4.12 | |||
extract | |
49.23% |
32 / 65 |
|
0.00% |
0 / 1 |
139.05 | |||
close | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
data | |
94.44% |
51 / 54 |
|
0.00% |
0 / 1 |
5.00 | |||
file | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
1 | |||
download | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
compress_tar | |
73.45% |
83 / 113 |
|
50.00% |
3 / 6 |
147.51 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
extract | |
86.36% |
38 / 44 |
|
0.00% |
0 / 1 |
28.85 | |||
close | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
10 | |||
data | |
100.00% |
28 / 28 |
|
100.00% |
1 / 1 |
10 | |||
open | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
9.00 | |||
download | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
72 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | /** |
15 | * @ignore |
16 | */ |
17 | if (!defined('IN_PHPBB')) |
18 | { |
19 | exit; |
20 | } |
21 | |
22 | /** |
23 | * Class for handling archives (compression/decompression) |
24 | */ |
25 | class compress |
26 | { |
27 | var $fp = 0; |
28 | |
29 | /** |
30 | * @var array |
31 | */ |
32 | protected $filelist = array(); |
33 | |
34 | /** |
35 | * Add file to archive |
36 | */ |
37 | function add_file($src, $src_rm_prefix = '', $src_add_prefix = '', $skip_files = '') |
38 | { |
39 | global $phpbb_root_path; |
40 | |
41 | $skip_files = explode(',', $skip_files); |
42 | |
43 | // Remove rm prefix from src path |
44 | $src_path = ($src_rm_prefix) ? preg_replace('#^(' . preg_quote($src_rm_prefix, '#') . ')#', '', $src) : $src; |
45 | // Add src prefix |
46 | $src_path = ($src_add_prefix) ? ($src_add_prefix . ((substr($src_add_prefix, -1) != '/') ? '/' : '') . $src_path) : $src_path; |
47 | // Remove initial "/" if present |
48 | $src_path = (substr($src_path, 0, 1) == '/') ? substr($src_path, 1) : $src_path; |
49 | |
50 | if (is_file($phpbb_root_path . $src)) |
51 | { |
52 | $this->data($src_path, file_get_contents("$phpbb_root_path$src"), stat("$phpbb_root_path$src"), false); |
53 | } |
54 | else if (is_dir($phpbb_root_path . $src)) |
55 | { |
56 | // Clean up path, add closing / if not present |
57 | $src_path = ($src_path && substr($src_path, -1) != '/') ? $src_path . '/' : $src_path; |
58 | |
59 | $filelist = filelist("$phpbb_root_path$src", '', '*'); |
60 | krsort($filelist); |
61 | |
62 | /** |
63 | * Commented out, as adding the folders produces corrupted archives |
64 | if ($src_path) |
65 | { |
66 | $this->data($src_path, '', true, stat("$phpbb_root_path$src")); |
67 | } |
68 | */ |
69 | |
70 | foreach ($filelist as $path => $file_ary) |
71 | { |
72 | /** |
73 | * Commented out, as adding the folders produces corrupted archives |
74 | if ($path) |
75 | { |
76 | // Same as for src_path |
77 | $path = (substr($path, 0, 1) == '/') ? substr($path, 1) : $path; |
78 | $path = ($path && substr($path, -1) != '/') ? $path . '/' : $path; |
79 | |
80 | $this->data("$src_path$path", '', true, stat("$phpbb_root_path$src$path")); |
81 | } |
82 | */ |
83 | |
84 | foreach ($file_ary as $file) |
85 | { |
86 | if (in_array($path . $file, $skip_files)) |
87 | { |
88 | continue; |
89 | } |
90 | |
91 | $this->data("$src_path$path$file", file_get_contents("$phpbb_root_path$src$path$file"), stat("$phpbb_root_path$src$path$file"), false); |
92 | } |
93 | } |
94 | } |
95 | else |
96 | { |
97 | // $src does not exist |
98 | return false; |
99 | } |
100 | |
101 | return true; |
102 | } |
103 | |
104 | /** |
105 | * Add custom file (the filepath will not be adjusted) |
106 | */ |
107 | function add_custom_file($src, $filename) |
108 | { |
109 | if (!file_exists($src)) |
110 | { |
111 | return false; |
112 | } |
113 | |
114 | $this->data($filename, file_get_contents($src), stat($src), false); |
115 | return true; |
116 | } |
117 | |
118 | /** |
119 | * Add file data |
120 | */ |
121 | function add_data($src, $name) |
122 | { |
123 | $stat = array(); |
124 | $stat[2] = 436; //384 |
125 | $stat[4] = $stat[5] = 0; |
126 | $stat[7] = strlen($src); |
127 | $stat[9] = time(); |
128 | $this->data($name, $src, $stat, false); |
129 | return true; |
130 | } |
131 | |
132 | /** |
133 | * Checks if a file by that name as already been added and, if it has, |
134 | * returns a new, unique name. |
135 | * |
136 | * @param string $name The filename |
137 | * @return string A unique filename |
138 | */ |
139 | protected function unique_filename($name) |
140 | { |
141 | if (isset($this->filelist[$name])) |
142 | { |
143 | $start = $name; |
144 | $ext = ''; |
145 | $this->filelist[$name]++; |
146 | |
147 | // Separate the extension off the end of the filename to preserve it |
148 | $pos = strrpos($name, '.'); |
149 | if ($pos !== false) |
150 | { |
151 | $start = substr($name, 0, $pos); |
152 | $ext = substr($name, $pos); |
153 | } |
154 | |
155 | return $start . '_' . $this->filelist[$name] . $ext; |
156 | } |
157 | |
158 | $this->filelist[$name] = 0; |
159 | return $name; |
160 | } |
161 | |
162 | /** |
163 | * Return available methods |
164 | * |
165 | * @return array Array of strings of available compression methods (.tar, .tar.gz, .zip, etc.) |
166 | */ |
167 | public static function methods() |
168 | { |
169 | $methods = array('.tar'); |
170 | $available_methods = array('.tar.gz' => 'zlib', '.tar.bz2' => 'bz2', '.zip' => 'zlib'); |
171 | |
172 | foreach ($available_methods as $type => $module) |
173 | { |
174 | if (!@extension_loaded($module)) |
175 | { |
176 | continue; |
177 | } |
178 | $methods[] = $type; |
179 | } |
180 | |
181 | return $methods; |
182 | } |
183 | } |
184 | |
185 | /** |
186 | * Zip creation class from phpMyAdmin 2.3.0 (c) Tobias Ratschiller, Olivier Müller, Loïc Chapeaux, |
187 | * Marc Delisle, http://www.phpmyadmin.net/ |
188 | * |
189 | * Zip extraction function by Alexandre Tedeschi, alexandrebr at gmail dot com |
190 | * |
191 | * Modified extensively by psoTFX and DavidMJ, (c) phpBB Limited, 2003 |
192 | * |
193 | * Based on work by Eric Mueller and Denis125 |
194 | * Official ZIP file format: http://www.pkware.com/appnote.txt |
195 | */ |
196 | class compress_zip extends compress |
197 | { |
198 | var $datasec = array(); |
199 | var $ctrl_dir = array(); |
200 | var $eof_cdh = "\x50\x4b\x05\x06\x00\x00\x00\x00"; |
201 | |
202 | var $old_offset = 0; |
203 | var $datasec_len = 0; |
204 | |
205 | /** |
206 | * @var \phpbb\filesystem\filesystem_interface |
207 | */ |
208 | protected $filesystem; |
209 | |
210 | /** |
211 | * Constructor |
212 | */ |
213 | function __construct($mode, $file) |
214 | { |
215 | global $phpbb_filesystem; |
216 | |
217 | $this->fp = @fopen($file, $mode . 'b'); |
218 | $this->filesystem = ($phpbb_filesystem instanceof \phpbb\filesystem\filesystem_interface) ? $phpbb_filesystem : new \phpbb\filesystem\filesystem(); |
219 | |
220 | if (!$this->fp) |
221 | { |
222 | trigger_error('Unable to open file ' . $file . ' [' . $mode . 'b]'); |
223 | } |
224 | } |
225 | |
226 | /** |
227 | * Convert unix to dos time |
228 | */ |
229 | function unix_to_dos_time($time) |
230 | { |
231 | $timearray = (!$time) ? getdate() : getdate($time); |
232 | |
233 | if ($timearray['year'] < 1980) |
234 | { |
235 | $timearray['year'] = 1980; |
236 | $timearray['mon'] = $timearray['mday'] = 1; |
237 | $timearray['hours'] = $timearray['minutes'] = $timearray['seconds'] = 0; |
238 | } |
239 | |
240 | return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) | ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1); |
241 | } |
242 | |
243 | /** |
244 | * Extract archive |
245 | */ |
246 | function extract($dst) |
247 | { |
248 | // Loop the file, looking for files and folders |
249 | $dd_try = false; |
250 | rewind($this->fp); |
251 | |
252 | while (!feof($this->fp)) |
253 | { |
254 | // Check if the signature is valid... |
255 | $signature = fread($this->fp, 4); |
256 | |
257 | switch ($signature) |
258 | { |
259 | // 'Local File Header' |
260 | case "\x50\x4b\x03\x04": |
261 | // Lets get everything we need. |
262 | // We don't store the version needed to extract, the general purpose bit flag or the date and time fields |
263 | $data = unpack("@4/vc_method/@10/Vcrc/Vc_size/Vuc_size/vname_len/vextra_field", fread($this->fp, 26)); |
264 | $file_name = fread($this->fp, $data['name_len']); // filename |
265 | |
266 | if ($data['extra_field']) |
267 | { |
268 | fread($this->fp, $data['extra_field']); // extra field |
269 | } |
270 | |
271 | $target_filename = "$dst$file_name"; |
272 | |
273 | if (!$data['uc_size'] && !$data['crc'] && substr($file_name, -1, 1) == '/') |
274 | { |
275 | if (!is_dir($target_filename)) |
276 | { |
277 | $str = ''; |
278 | $folders = explode('/', $target_filename); |
279 | |
280 | // Create and folders and subfolders if they do not exist |
281 | foreach ($folders as $folder) |
282 | { |
283 | $folder = trim($folder); |
284 | if (!$folder) |
285 | { |
286 | continue; |
287 | } |
288 | |
289 | $str = (!empty($str)) ? $str . '/' . $folder : $folder; |
290 | if (!is_dir($str)) |
291 | { |
292 | if (!@mkdir($str, 0777)) |
293 | { |
294 | trigger_error("Could not create directory $folder"); |
295 | } |
296 | |
297 | try |
298 | { |
299 | $this->filesystem->phpbb_chmod($str, \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE); |
300 | } |
301 | catch (\phpbb\filesystem\exception\filesystem_exception $e) |
302 | { |
303 | // Do nothing |
304 | } |
305 | } |
306 | } |
307 | } |
308 | // This is a directory, we are not writing files |
309 | continue 2; |
310 | } |
311 | else |
312 | { |
313 | // Some archivers are punks, they don't include folders in their archives! |
314 | $str = ''; |
315 | $folders = explode('/', pathinfo($target_filename, PATHINFO_DIRNAME)); |
316 | |
317 | // Create and folders and subfolders if they do not exist |
318 | foreach ($folders as $folder) |
319 | { |
320 | $folder = trim($folder); |
321 | if (!$folder) |
322 | { |
323 | continue; |
324 | } |
325 | |
326 | $str = (!empty($str)) ? $str . '/' . $folder : $folder; |
327 | if (!is_dir($str)) |
328 | { |
329 | if (!@mkdir($str, 0777)) |
330 | { |
331 | trigger_error("Could not create directory $folder"); |
332 | } |
333 | |
334 | try |
335 | { |
336 | $this->filesystem->phpbb_chmod($str, \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE); |
337 | } |
338 | catch (\phpbb\filesystem\exception\filesystem_exception $e) |
339 | { |
340 | // Do nothing |
341 | } |
342 | } |
343 | } |
344 | } |
345 | |
346 | if (!$data['uc_size']) |
347 | { |
348 | $content = ''; |
349 | } |
350 | else |
351 | { |
352 | $content = fread($this->fp, $data['c_size']); |
353 | } |
354 | |
355 | $fp = fopen($target_filename, "w"); |
356 | |
357 | switch ($data['c_method']) |
358 | { |
359 | case 0: |
360 | // Not compressed |
361 | fwrite($fp, $content); |
362 | break; |
363 | |
364 | case 8: |
365 | // Deflate |
366 | fwrite($fp, gzinflate($content, $data['uc_size'])); |
367 | break; |
368 | |
369 | case 12: |
370 | // Bzip2 |
371 | fwrite($fp, bzdecompress($content)); |
372 | break; |
373 | } |
374 | |
375 | fclose($fp); |
376 | break; |
377 | |
378 | // We hit the 'Central Directory Header', we can stop because nothing else in here requires our attention |
379 | // or we hit the end of the central directory record, we can safely end the loop as we are totally finished with looking for files and folders |
380 | case "\x50\x4b\x01\x02": |
381 | // This case should simply never happen.. but it does exist.. |
382 | case "\x50\x4b\x05\x06": |
383 | break 2; |
384 | |
385 | // 'Packed to Removable Disk', ignore it and look for the next signature... |
386 | case 'PK00': |
387 | continue 2; |
388 | |
389 | // We have encountered a header that is weird. Lets look for better data... |
390 | default: |
391 | if (!$dd_try) |
392 | { |
393 | // Unexpected header. Trying to detect wrong placed 'Data Descriptor'; |
394 | $dd_try = true; |
395 | fseek($this->fp, 8, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4) |
396 | continue 2; |
397 | } |
398 | trigger_error("Unexpected header, ending loop"); |
399 | break 2; |
400 | } |
401 | |
402 | $dd_try = false; |
403 | } |
404 | } |
405 | |
406 | /** |
407 | * Close archive |
408 | */ |
409 | function close() |
410 | { |
411 | // Write out central file directory and footer ... if it exists |
412 | if (count($this->ctrl_dir)) |
413 | { |
414 | fwrite($this->fp, $this->file()); |
415 | } |
416 | fclose($this->fp); |
417 | } |
418 | |
419 | /** |
420 | * Create the structures ... note we assume version made by is MSDOS |
421 | */ |
422 | function data($name, $data, $stat, $is_dir = false) |
423 | { |
424 | $name = str_replace('\\', '/', $name); |
425 | $name = $this->unique_filename($name); |
426 | |
427 | $hexdtime = pack('V', $this->unix_to_dos_time($stat[9])); |
428 | |
429 | if ($is_dir) |
430 | { |
431 | $unc_len = $c_len = $crc = 0; |
432 | $zdata = ''; |
433 | $var_ext = 10; |
434 | } |
435 | else |
436 | { |
437 | $unc_len = strlen($data); |
438 | $crc = crc32($data); |
439 | $zdata = gzdeflate($data); |
440 | $c_len = strlen($zdata); |
441 | $var_ext = 20; |
442 | |
443 | // Did we compress? No, then use data as is |
444 | if ($c_len >= $unc_len) |
445 | { |
446 | $zdata = $data; |
447 | $c_len = $unc_len; |
448 | $var_ext = 10; |
449 | } |
450 | } |
451 | unset($data); |
452 | |
453 | // If we didn't compress set method to store, else deflate |
454 | $c_method = ($c_len == $unc_len) ? "\x00\x00" : "\x08\x00"; |
455 | |
456 | // Are we a file or a directory? Set archive for file |
457 | $attrib = ($is_dir) ? 16 : 32; |
458 | |
459 | // File Record Header |
460 | $fr = "\x50\x4b\x03\x04"; // Local file header 4bytes |
461 | $fr .= pack('v', $var_ext); // ver needed to extract 2bytes |
462 | $fr .= "\x00\x00"; // gen purpose bit flag 2bytes |
463 | $fr .= $c_method; // compression method 2bytes |
464 | $fr .= $hexdtime; // last mod time and date 2+2bytes |
465 | $fr .= pack('V', $crc); // crc32 4bytes |
466 | $fr .= pack('V', $c_len); // compressed filesize 4bytes |
467 | $fr .= pack('V', $unc_len); // uncompressed filesize 4bytes |
468 | $fr .= pack('v', strlen($name));// length of filename 2bytes |
469 | |
470 | $fr .= pack('v', 0); // extra field length 2bytes |
471 | $fr .= $name; |
472 | $fr .= $zdata; |
473 | unset($zdata); |
474 | |
475 | $this->datasec_len += strlen($fr); |
476 | |
477 | // Add data to file ... by writing data out incrementally we save some memory |
478 | fwrite($this->fp, $fr); |
479 | unset($fr); |
480 | |
481 | // Central Directory Header |
482 | $cdrec = "\x50\x4b\x01\x02"; // header 4bytes |
483 | $cdrec .= "\x00\x00"; // version made by |
484 | $cdrec .= pack('v', $var_ext); // version needed to extract |
485 | $cdrec .= "\x00\x00"; // gen purpose bit flag |
486 | $cdrec .= $c_method; // compression method |
487 | $cdrec .= $hexdtime; // last mod time & date |
488 | $cdrec .= pack('V', $crc); // crc32 |
489 | $cdrec .= pack('V', $c_len); // compressed filesize |
490 | $cdrec .= pack('V', $unc_len); // uncompressed filesize |
491 | $cdrec .= pack('v', strlen($name)); // length of filename |
492 | $cdrec .= pack('v', 0); // extra field length |
493 | $cdrec .= pack('v', 0); // file comment length |
494 | $cdrec .= pack('v', 0); // disk number start |
495 | $cdrec .= pack('v', 0); // internal file attributes |
496 | $cdrec .= pack('V', $attrib); // external file attributes |
497 | $cdrec .= pack('V', $this->old_offset); // relative offset of local header |
498 | $cdrec .= $name; |
499 | |
500 | // Save to central directory |
501 | $this->ctrl_dir[] = $cdrec; |
502 | |
503 | $this->old_offset = $this->datasec_len; |
504 | } |
505 | |
506 | /** |
507 | * file |
508 | */ |
509 | function file() |
510 | { |
511 | $ctrldir = implode('', $this->ctrl_dir); |
512 | |
513 | return $ctrldir . $this->eof_cdh . |
514 | pack('v', count($this->ctrl_dir)) . // total # of entries "on this disk" |
515 | pack('v', count($this->ctrl_dir)) . // total # of entries overall |
516 | pack('V', strlen($ctrldir)) . // size of central dir |
517 | pack('V', $this->datasec_len) . // offset to start of central dir |
518 | "\x00\x00"; // .zip file comment length |
519 | } |
520 | |
521 | /** |
522 | * Download archive |
523 | */ |
524 | function download($filename, $download_name = false) |
525 | { |
526 | global $phpbb_root_path; |
527 | |
528 | if ($download_name === false) |
529 | { |
530 | $download_name = $filename; |
531 | } |
532 | |
533 | $mimetype = 'application/zip'; |
534 | |
535 | header('Cache-Control: private, no-cache'); |
536 | header("Content-Type: $mimetype; name=\"$download_name.zip\""); |
537 | header("Content-disposition: attachment; filename=$download_name.zip"); |
538 | |
539 | $fp = @fopen("{$phpbb_root_path}store/$filename.zip", 'rb'); |
540 | if ($fp) |
541 | { |
542 | while ($buffer = fread($fp, 1024)) |
543 | { |
544 | echo $buffer; |
545 | } |
546 | fclose($fp); |
547 | } |
548 | } |
549 | } |
550 | |
551 | /** |
552 | * Tar/tar.gz compression routine |
553 | * Header/checksum creation derived from tarfile.pl, (c) Tom Horsley, 1994 |
554 | */ |
555 | class compress_tar extends compress |
556 | { |
557 | var $isgz = false; |
558 | var $isbz = false; |
559 | var $filename = ''; |
560 | var $mode = ''; |
561 | var $type = ''; |
562 | var $wrote = false; |
563 | |
564 | /** |
565 | * @var \phpbb\filesystem\filesystem_interface |
566 | */ |
567 | protected $filesystem; |
568 | |
569 | /** |
570 | * Constructor |
571 | */ |
572 | function __construct($mode, $file, $type = '') |
573 | { |
574 | global $phpbb_filesystem; |
575 | |
576 | $type = (!$type) ? $file : $type; |
577 | $this->isgz = preg_match('#(\.tar\.gz|\.tgz)$#', $type); |
578 | $this->isbz = preg_match('#\.tar\.bz2$#', $type); |
579 | |
580 | $this->mode = &$mode; |
581 | $this->file = &$file; |
582 | $this->type = &$type; |
583 | $this->open(); |
584 | |
585 | $this->filesystem = ($phpbb_filesystem instanceof \phpbb\filesystem\filesystem_interface) ? $phpbb_filesystem : new \phpbb\filesystem\filesystem(); |
586 | } |
587 | |
588 | /** |
589 | * Extract archive |
590 | */ |
591 | function extract($dst) |
592 | { |
593 | $fzread = ($this->isbz && function_exists('bzread')) ? 'bzread' : (($this->isgz && @extension_loaded('zlib')) ? 'gzread' : 'fread'); |
594 | |
595 | // Run through the file and grab directory entries |
596 | while ($buffer = $fzread($this->fp, 512)) |
597 | { |
598 | $tmp = unpack('A6magic', substr($buffer, 257, 6)); |
599 | |
600 | if (trim($tmp['magic']) == 'ustar') |
601 | { |
602 | $tmp = unpack('A100name', $buffer); |
603 | $filename = trim($tmp['name']); |
604 | |
605 | $tmp = unpack('Atype', substr($buffer, 156, 1)); |
606 | $filetype = (int) trim($tmp['type']); |
607 | |
608 | $tmp = unpack('A12size', substr($buffer, 124, 12)); |
609 | $filesize = octdec((int) trim($tmp['size'])); |
610 | |
611 | $target_filename = "$dst$filename"; |
612 | |
613 | if ($filetype == 5) |
614 | { |
615 | if (!is_dir($target_filename)) |
616 | { |
617 | $str = ''; |
618 | $folders = explode('/', $target_filename); |
619 | |
620 | // Create and folders and subfolders if they do not exist |
621 | foreach ($folders as $folder) |
622 | { |
623 | $folder = trim($folder); |
624 | if (!$folder) |
625 | { |
626 | continue; |
627 | } |
628 | |
629 | $str = (!empty($str)) ? $str . '/' . $folder : $folder; |
630 | if (!is_dir($str)) |
631 | { |
632 | if (!@mkdir($str, 0777)) |
633 | { |
634 | trigger_error("Could not create directory $folder"); |
635 | } |
636 | |
637 | try |
638 | { |
639 | $this->filesystem->phpbb_chmod($str, \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE); |
640 | } |
641 | catch (\phpbb\filesystem\exception\filesystem_exception $e) |
642 | { |
643 | // Do nothing |
644 | } |
645 | } |
646 | } |
647 | } |
648 | } |
649 | else if ($filesize >= 0 && ($filetype == 0 || $filetype == "\0")) |
650 | { |
651 | // Some archivers are punks, they don't properly order the folders in their archives! |
652 | $str = ''; |
653 | $folders = explode('/', pathinfo($target_filename, PATHINFO_DIRNAME)); |
654 | |
655 | // Create and folders and subfolders if they do not exist |
656 | foreach ($folders as $folder) |
657 | { |
658 | $folder = trim($folder); |
659 | if (!$folder) |
660 | { |
661 | continue; |
662 | } |
663 | |
664 | $str = (!empty($str)) ? $str . '/' . $folder : $folder; |
665 | if (!is_dir($str)) |
666 | { |
667 | if (!@mkdir($str, 0777)) |
668 | { |
669 | trigger_error("Could not create directory $folder"); |
670 | } |
671 | |
672 | try |
673 | { |
674 | $this->filesystem->phpbb_chmod($str, \phpbb\filesystem\filesystem_interface::CHMOD_READ | \phpbb\filesystem\filesystem_interface::CHMOD_WRITE); |
675 | } |
676 | catch (\phpbb\filesystem\exception\filesystem_exception $e) |
677 | { |
678 | // Do nothing |
679 | } |
680 | } |
681 | } |
682 | |
683 | // Write out the files |
684 | if (!($fp = fopen($target_filename, 'wb'))) |
685 | { |
686 | trigger_error("Couldn't create file $filename"); |
687 | } |
688 | |
689 | try |
690 | { |
691 | $this->filesystem->phpbb_chmod($target_filename, \phpbb\filesystem\filesystem_interface::CHMOD_READ); |
692 | } |
693 | catch (\phpbb\filesystem\exception\filesystem_exception $e) |
694 | { |
695 | // Do nothing |
696 | } |
697 | |
698 | // Grab the file contents |
699 | fwrite($fp, ($filesize) ? $fzread($this->fp, ($filesize + 511) &~ 511) : '', $filesize); |
700 | fclose($fp); |
701 | } |
702 | } |
703 | } |
704 | } |
705 | |
706 | /** |
707 | * Close archive |
708 | */ |
709 | function close() |
710 | { |
711 | $fzclose = ($this->isbz && function_exists('bzclose')) ? 'bzclose' : (($this->isgz && @extension_loaded('zlib')) ? 'gzclose' : 'fclose'); |
712 | |
713 | if ($this->wrote) |
714 | { |
715 | $fzwrite = ($this->isbz && function_exists('bzwrite')) ? 'bzwrite' : (($this->isgz && @extension_loaded('zlib')) ? 'gzwrite' : 'fwrite'); |
716 | |
717 | // The end of a tar archive ends in two records of all NULLs (1024 bytes of \0) |
718 | $fzwrite($this->fp, str_repeat("\0", 1024)); |
719 | } |
720 | |
721 | $fzclose($this->fp); |
722 | } |
723 | |
724 | /** |
725 | * Create the structures |
726 | */ |
727 | function data($name, $data, $stat, $is_dir = false) |
728 | { |
729 | $name = $this->unique_filename($name); |
730 | $this->wrote = true; |
731 | $fzwrite = ($this->isbz && function_exists('bzwrite')) ? 'bzwrite' : (($this->isgz && @extension_loaded('zlib')) ? 'gzwrite' : 'fwrite'); |
732 | |
733 | $typeflag = ($is_dir) ? '5' : ''; |
734 | |
735 | // This is the header data, it contains all the info we know about the file or folder that we are about to archive |
736 | $header = ''; |
737 | $header .= pack('a100', $name); // file name |
738 | $header .= pack('a8', sprintf("%07o", $stat[2])); // file mode |
739 | $header .= pack('a8', sprintf("%07o", $stat[4])); // owner id |
740 | $header .= pack('a8', sprintf("%07o", $stat[5])); // group id |
741 | $header .= pack('a12', sprintf("%011o", $stat[7])); // file size |
742 | $header .= pack('a12', sprintf("%011o", $stat[9])); // last mod time |
743 | |
744 | // Checksum |
745 | $checksum = 0; |
746 | for ($i = 0; $i < 148; $i++) |
747 | { |
748 | $checksum += ord($header[$i]); |
749 | } |
750 | |
751 | // We precompute the rest of the hash, this saves us time in the loop and allows us to insert our hash without resorting to string functions |
752 | $checksum += 2415 + (($is_dir) ? 53 : 0); |
753 | |
754 | $header .= pack('a8', sprintf("%07o", $checksum)); // checksum |
755 | $header .= pack('a1', $typeflag); // link indicator |
756 | $header .= pack('a100', ''); // name of linked file |
757 | $header .= pack('a6', 'ustar'); // ustar indicator |
758 | $header .= pack('a2', '00'); // ustar version |
759 | $header .= pack('a32', 'Unknown'); // owner name |
760 | $header .= pack('a32', 'Unknown'); // group name |
761 | $header .= pack('a8', ''); // device major number |
762 | $header .= pack('a8', ''); // device minor number |
763 | $header .= pack('a155', ''); // filename prefix |
764 | $header .= pack('a12', ''); // end |
765 | |
766 | // This writes the entire file in one shot. Header, followed by data and then null padded to a multiple of 512 |
767 | $fzwrite($this->fp, $header . (($stat[7] !== 0 && !$is_dir) ? $data . str_repeat("\0", (($stat[7] + 511) &~ 511) - $stat[7]) : '')); |
768 | unset($data); |
769 | } |
770 | |
771 | /** |
772 | * Open archive |
773 | */ |
774 | function open() |
775 | { |
776 | $fzopen = ($this->isbz && function_exists('bzopen')) ? 'bzopen' : (($this->isgz && @extension_loaded('zlib')) ? 'gzopen' : 'fopen'); |
777 | $this->fp = @$fzopen($this->file, $this->mode . (($fzopen == 'bzopen') ? '' : 'b') . (($fzopen == 'gzopen') ? '9' : '')); |
778 | |
779 | if (!$this->fp) |
780 | { |
781 | trigger_error('Unable to open file ' . $this->file . ' [' . $fzopen . ' - ' . $this->mode . 'b]'); |
782 | } |
783 | } |
784 | |
785 | /** |
786 | * Download archive |
787 | */ |
788 | function download($filename, $download_name = false) |
789 | { |
790 | global $phpbb_root_path; |
791 | |
792 | if ($download_name === false) |
793 | { |
794 | $download_name = $filename; |
795 | } |
796 | |
797 | switch ($this->type) |
798 | { |
799 | case '.tar': |
800 | $mimetype = 'application/x-tar'; |
801 | break; |
802 | |
803 | case '.tar.gz': |
804 | $mimetype = 'application/x-gzip'; |
805 | break; |
806 | |
807 | case '.tar.bz2': |
808 | $mimetype = 'application/x-bzip2'; |
809 | break; |
810 | |
811 | default: |
812 | $mimetype = 'application/octet-stream'; |
813 | break; |
814 | } |
815 | |
816 | header('Cache-Control: private, no-cache'); |
817 | header("Content-Type: $mimetype; name=\"$download_name$this->type\""); |
818 | header("Content-disposition: attachment; filename=$download_name$this->type"); |
819 | |
820 | $fp = @fopen("{$phpbb_root_path}store/$filename$this->type", 'rb'); |
821 | if ($fp) |
822 | { |
823 | while ($buffer = fread($fp, 1024)) |
824 | { |
825 | echo $buffer; |
826 | } |
827 | fclose($fp); |
828 | } |
829 | } |
830 | } |