PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/phpBB/includes/functions_compress.php

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