/wcfsetup/install/files/lib/system/io/TarWriter.class.php

https://github.com/KomHunter2/WCF · PHP · 274 lines · 146 code · 39 blank · 89 comment · 44 complexity · b6077e514df8a725b879148ff9aee218 MD5 · raw file

  1. <?php
  2. namespace wcf\system\io;
  3. use wcf\system\exception\SystemException;
  4. use wcf\util\FileUtil;
  5. /**
  6. * Creates a tar file archive.
  7. *
  8. * Usage:
  9. * ------
  10. * $tar = new TarWriter('archive.tar', true);
  11. * $tar->add(array('file1', 'file2'));
  12. * $tar->create();
  13. *
  14. * @author Marcel Werk
  15. * @copyright 2001-2009 WoltLab GmbH
  16. * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  17. * @package com.woltlab.wcf
  18. * @subpackage system.io
  19. * @category Community Framework
  20. */
  21. class TarWriter extends Tar {
  22. /**
  23. * @see wcf\system\io\Tar::$mode
  24. */
  25. protected $mode = 'wb+';
  26. /**
  27. * Creates a new TarWriter object.
  28. *
  29. * @param string $archiveName
  30. * @param boolean $compress enables gzip compression
  31. */
  32. public function __construct($archiveName, $compress = false) {
  33. $this->archiveName = $archiveName;
  34. $this->isZipped = $compress;
  35. if ($compress) $this->mode = 'wb9'; // set compression level
  36. $this->open();
  37. }
  38. /**
  39. * Writes the last 0 filled block for end of archive.
  40. */
  41. protected function writeFooter() {
  42. $this->file->write(pack('a512', ''));
  43. }
  44. /**
  45. * Creates the tar archive.
  46. */
  47. public function create() {
  48. $this->writeFooter();
  49. $this->close();
  50. }
  51. /**
  52. * Adds a string to the tar archive.
  53. *
  54. * @param string $filename
  55. * @param string $string file content
  56. * @return boolean result
  57. */
  58. public function addString($filename, $string) {
  59. if (empty($filename)) return false;
  60. $filename = FileUtil::unifyDirSeperator($filename);
  61. if (!$this->writeHeaderBlock($filename, strlen($string), TIME_NOW, 33279)) {
  62. return false;
  63. }
  64. $i = 0;
  65. while (($buffer = substr($string, (($i++) * 512), 512)) != '') {
  66. $this->file->write(pack("a512", $buffer));
  67. }
  68. return true;
  69. }
  70. /**
  71. * Adds a list of files or directories to the tar archive.
  72. *
  73. * @param mixed $files
  74. * @param string $addDir
  75. * @param string $removeDir
  76. * @return boolean result
  77. */
  78. public function add($files, $addDir = '', $removeDir = '') {
  79. if (!is_array($files)) $files = array($files);
  80. if (!count($files)) return false;
  81. $result = true;
  82. // unify dir seperator
  83. $addDir = FileUtil::unifyDirSeperator($addDir);
  84. $removeDir = FileUtil::unifyDirSeperator($removeDir);
  85. foreach ($files as $filename) {
  86. if (!$result) {
  87. break;
  88. }
  89. if (!$filename || $filename == $this->archiveName) {
  90. continue;
  91. }
  92. if (!file_exists($filename)) {
  93. throw new SystemException("Unable to find file '".$filename."'", 11002);
  94. }
  95. // add file
  96. if (!$this->addFile($filename, $addDir, $removeDir)) {
  97. return false;
  98. }
  99. // handle directories
  100. if (@is_dir($filename)) {
  101. $handle = opendir($filename);
  102. while (($dirFile = readdir($handle)) !== false) {
  103. if (($dirFile != '.') && ($dirFile != '..')) {
  104. if ($filename != ".") $dirFile = $filename.'/'.$dirFile;
  105. $result = $this->add($dirFile, $addDir, $removeDir);
  106. }
  107. }
  108. closedir($handle);
  109. }
  110. }
  111. return $result;
  112. }
  113. /**
  114. * Adds a file to the tar archive.
  115. *
  116. * @param string $filename
  117. * @param string $addDir
  118. * @param string $removeDir
  119. * @return boolean result
  120. */
  121. protected function addFile($filename, $addDir, $removeDir) {
  122. $filename = FileUtil::unifyDirSeperator($filename);
  123. $storedFilename = $filename;
  124. if (!empty($removeDir)) $storedFilename = StringUtil::replaceIgnoreCase($removeDir, '', $filename);
  125. if (!empty($addDir)) $storedFilename = $addDir . $storedFilename;
  126. if (is_file($filename)) {
  127. // open file
  128. $file = new File($filename, 'rb');
  129. // write header
  130. if (!$this->writeFileHeader($filename, $storedFilename)) {
  131. return false;
  132. }
  133. // write file content
  134. while (($buffer = $file->read(512)) != '') {
  135. $this->file->write(pack('a512', $buffer));
  136. }
  137. // close file
  138. $file->close();
  139. }
  140. else {
  141. // only directory header
  142. if (!$this->writeFileHeader($filename, $storedFilename)) {
  143. return false;
  144. }
  145. }
  146. return true;
  147. }
  148. /**
  149. * Writes the file header.
  150. *
  151. * @param string $filename
  152. * @param string $storedFilename
  153. * @return boolean result
  154. */
  155. protected function writeFileHeader($filename, $storedFilename) {
  156. $fileInfo = stat($filename);
  157. $permissions = fileperms($filename);
  158. $mtime = filemtime($filename);
  159. if (@is_dir($filename)) {
  160. $typeFlag = '5';
  161. $size = 0;
  162. }
  163. else {
  164. $typeFlag = '';
  165. clearstatcache();
  166. $size = filesize($filename);
  167. }
  168. return $this->writeHeaderBlock($storedFilename, $size, $mtime, $permissions, $typeFlag, $fileInfo[4], $fileInfo[5]);
  169. }
  170. /**
  171. * Writes header block.
  172. *
  173. * @param string $filename
  174. * @param integer $size
  175. * @param integer $mtime
  176. * @param integer $permissions
  177. * @param string $typeFlag
  178. * @param integer $uid
  179. * @param integer $gid
  180. * @return boolean result
  181. */
  182. public function writeHeaderBlock($filename, $size, $mtime = 0, $permissions = 0, $typeFlag = '', $uid = 0, $gid = 0) {
  183. if (strlen($filename) > 99) {
  184. if (!$this->writeLongHeaderBlock($filename)) {
  185. return false;
  186. }
  187. }
  188. if ($typeFlag == "5") {
  189. $size = sprintf("%11s ", decOct(0));
  190. }
  191. else {
  192. $size = sprintf("%11s ", decOct($size));
  193. }
  194. $uid = sprintf("%6s ", decOct($uid));
  195. $gid = sprintf("%6s ", decOct($gid));
  196. $permissions = sprintf("%6s ", decOct($permissions));
  197. $mtime = sprintf("%11s", decOct($mtime));
  198. $binaryDataFirst = pack('a100a8a8a8a12A12', $filename, $permissions, $uid, $gid, $size, $mtime);
  199. $binaryDataLast = pack('a1a100a6a2a32a32a8a8a155a12', $typeFlag, '', '', '', '', '', '', '', '', '');
  200. // calculate the checksum
  201. $checksum = 0;
  202. for ($i = 0; $i < 148; $i++) $checksum += ord(substr($binaryDataFirst, $i, 1));
  203. for ($i = 148; $i < 156; $i++) $checksum += ord(' ');
  204. for ($i = 156, $j = 0; $i < 512; $i++, $j++) $checksum += ord(substr($binaryDataLast, $j, 1));
  205. $this->file->write($binaryDataFirst, 148);
  206. $this->file->write(pack("a8", sprintf("%6s ", decOct($checksum))), 8); // write the checksum
  207. $this->file->write($binaryDataLast, 356);
  208. return true;
  209. }
  210. /**
  211. * Writes a long header block.
  212. *
  213. * @param string $filename
  214. * @return boolean
  215. */
  216. protected function writeLongHeaderBlock($filename) {
  217. $size = sprintf("%11s ", decOct(strlen($filename)));
  218. $typeFlag = 'L';
  219. $binaryDataFirst = pack("a100a8a8a8a12A12", '././@LongLink', 0, 0, 0, $size, 0);
  220. $binaryDataLast = pack("a1a100a6a2a32a32a8a8a155a12", $typeFlag, '', '', '', '', '', '', '', '', '');
  221. // calculate the checksum
  222. $checksum = 0;
  223. for ($i = 0; $i < 148; $i++) $checksum += ord(substr($binaryDataFirst, $i, 1));
  224. for ($i = 148; $i < 156; $i++) $checksum += ord(' ');
  225. for ($i = 156, $j = 0; $i < 512; $i++, $j++) $checksum += ord(substr($binaryDataLast, $j, 1));
  226. $this->file->write($binaryDataFirst, 148);
  227. $this->file->write(pack("a8", sprintf("%6s ", decOct($checksum))), 8); // write the checksum
  228. $this->file->write($binaryDataLast, 356);
  229. $i = 0;
  230. while (($buffer = substr($filename, (($i++) * 512), 512)) != '') {
  231. $this->file->write(pack("a512", $buffer));
  232. }
  233. return true;
  234. }
  235. }