PageRenderTime 27ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/code/php/functions/gunzip.php

http://hm2k.googlecode.com/
PHP | 451 lines | 270 code | 45 blank | 136 comment | 70 complexity | 8a48d96b1fe7bcb0915051c8ea609bbd MD5 | raw file
  1. <?php
  2. function gunzip($file,$path='./') {
  3. //$path=realpath($path);
  4. if (!file_exists($file)) { user_error("'$file' does not exist."); return; }
  5. if (!is_writable($path)) { user_error("'$path' is not writable."); return; }
  6. if (!function_exists('gzopen')) { user_error("gzopen() function does not exist."); return; }
  7. $tar = new ArchiveTar($file);
  8. $contentList = $tar->getContentList();
  9. if (!count($contentList)) {
  10. user_error("Can not unpack '$file'. File is corrupt.");
  11. return;
  12. }
  13. //print_r($contentList); //debug
  14. $status=1;
  15. foreach ($contentList as $file) {
  16. if ($file['type'] != 'folder') {
  17. if (!is_dir(dirname($path.$file['filename']))) {
  18. $status=rmkdir(dirname($path.$file['filename']));
  19. }
  20. $status=$tar->extract($file['index'], $path.$file['filename']);
  21. }
  22. if (!$status) { return $status; }
  23. }
  24. $tar->close();
  25. return $status;
  26. }
  27. /**
  28. * Opens tar or tar.gz archives.
  29. *
  30. * $tar = new ArchiveTar('archive.tar');
  31. * $contentList = $tar->getContentList();
  32. * foreach ($contentList as $key => $val) {
  33. * $tar->extract($key, DESTINATION);
  34. * }
  35. */
  36. class ArchiveTar {
  37. protected $archiveName = '';
  38. protected $contentList = array();
  39. protected $opened = false;
  40. protected $read = false;
  41. protected $file = null;
  42. protected $isZipped = false;
  43. protected $mode = 'rb';
  44. /**
  45. * Creates a new ArchiveTar object.
  46. * archiveName must be tarball or gzipped tarball
  47. *
  48. * @param string $archiveName
  49. */
  50. public function __construct($archiveName) {
  51. $match = array();
  52. if (!is_file($archiveName)) {
  53. user_error("unable to find tar archive '".$archiveName."'");
  54. }
  55. $this->archiveName = $archiveName;
  56. $this->open();
  57. $this->readContent();
  58. }
  59. /**
  60. * Destructor of this class, closes tar archive.
  61. */
  62. public function __destruct() {
  63. $this->close();
  64. }
  65. /**
  66. * Opens the tar archive and stores filehandle.
  67. */
  68. public function open() {
  69. if (!$this->opened) {
  70. if ($this->isZipped) $this->file = new ArchiveZipFile($this->archiveName, $this->mode);
  71. else {
  72. // test compression
  73. $this->file = new ArchiveFile($this->archiveName, $this->mode);
  74. if ($this->file->read(2) == "\37\213") {
  75. $this->file->close();
  76. $this->isZipped = true;
  77. $this->file = new ArchiveZipFile($this->archiveName, $this->mode);
  78. }
  79. else {
  80. $this->file->seek(0);
  81. }
  82. }
  83. $this->opened = true;
  84. }
  85. }
  86. /**
  87. * Closes the opened file.
  88. */
  89. public function close() {
  90. if ($this->opened) {
  91. $this->file->close();
  92. $this->opened = false;
  93. }
  94. }
  95. /**
  96. * Returns the table of contents (TOC) list for this tar archive.
  97. *
  98. * @return array list of content
  99. */
  100. public function getContentList() {
  101. if (!$this->read) {
  102. $this->open();
  103. $this->readContent();
  104. }
  105. return $this->contentList;
  106. }
  107. /**
  108. * Returns an associative array with information
  109. * about a specific file in the archive.
  110. *
  111. * @param mixed $fileindex index or name of the requested file
  112. * @return array $fileInfo
  113. */
  114. public function getFileInfo($fileIndex) {
  115. if (!is_int($fileIndex)) {
  116. $fileIndex = $this->getIndexByFilename($fileIndex);
  117. }
  118. if (!isset($this->contentList[$fileIndex])) {
  119. user_error("Tar: could find file '$index' in archive");
  120. }
  121. return $this->contentList[$fileIndex];
  122. }
  123. /**
  124. * Searchs a file in the tar archive
  125. * and returns the numeric fileindex.
  126. * Returns false if not found.
  127. *
  128. * @param string $filename
  129. * @return integer index of the requested file
  130. */
  131. public function getIndexByFilename($filename) {
  132. foreach ($this->contentList as $index => $file) {
  133. if ($file['filename'] == $filename) {
  134. return $index;
  135. }
  136. }
  137. return false;
  138. }
  139. /**
  140. * Extracts a specific file and returns the content as string.
  141. * Returns false if extraction failed.
  142. *
  143. * @param mixed $index index or name of the requested file
  144. * @return string content of the requested file
  145. */
  146. public function extractToString($index) {
  147. if (!$this->read) {
  148. $this->open();
  149. $this->readContent();
  150. }
  151. $header = $this->getFileInfo($index);
  152. // can not extract a folder
  153. if ($header['type'] != 'file') {
  154. return false;
  155. }
  156. // seek to offset
  157. $this->file->seek($header['offset']);
  158. // read data
  159. $content = '';
  160. $n = floor($header['size'] / 512);
  161. for($i = 0; $i < $n; $i++) {
  162. $content .= $this->file->read(512);
  163. }
  164. if(($header['size'] % 512) != 0) {
  165. $buffer = $this->file->read(512);
  166. $content .= substr($buffer, 0, ($header['size'] % 512));
  167. }
  168. return $content;
  169. }
  170. /**
  171. * Extracts a specific file and writes it's content
  172. * to the file specified with $destination.
  173. *
  174. * @param mixed $index index or name of the requested file
  175. * @param string $destination
  176. * @return boolean $success
  177. */
  178. public function extract($index, $destination) {
  179. if (!$this->read) {
  180. $this->open();
  181. $this->readContent();
  182. }
  183. $header = $this->getFileInfo($index);
  184. // can not extract a folder
  185. if ($header['type'] != 'file') {
  186. return false;
  187. }
  188. // seek to offset
  189. $this->file->seek($header['offset']);
  190. $targetFile = new ArchiveFile($destination);
  191. // read data
  192. $n = floor($header['size'] / 512);
  193. for ($i = 0; $i < $n; $i++) {
  194. $content = $this->file->read(512);
  195. $targetFile->write($content, 512);
  196. }
  197. if (($header['size'] % 512) != 0) {
  198. $content = $this->file->read(512);
  199. $targetFile->write($content, ($header['size'] % 512));
  200. }
  201. $targetFile->close();
  202. if (function_exists('apache_get_version') || !@$targetFile->is_writable()) {
  203. @$targetFile->chmod(0777);
  204. }
  205. else {
  206. @$targetFile->chmod(0755);
  207. }
  208. if ($header['mtime']) {
  209. @$targetFile->touch($header['mtime']);
  210. }
  211. // check filesize
  212. if (filesize($destination) != $header['size']) {
  213. user_error("Could not untar file '".$header['filename']."' to '".$destination."'. Maybe disk quota exceeded in folder '".dirname($destination)."'.");
  214. }
  215. return true;
  216. }
  217. /**
  218. * Reads table of contents (TOC) from tar archive.
  219. * This does not get the entire to memory but only parts of it.
  220. */
  221. protected function readContent() {
  222. $this->contentList = array();
  223. $this->read = true;
  224. $i = 0;
  225. // Read the 512 bytes header
  226. while (strlen($binaryData = $this->file->read(512)) != 0) {
  227. // read header
  228. $header = $this->readHeader($binaryData);
  229. if ($header === false) {
  230. continue;
  231. }
  232. $this->contentList[$i] = $header;
  233. $this->contentList[$i]['index'] = $i;
  234. $i++;
  235. $this->file->seek($this->file->tell() + (512 * ceil(($header['size'] / 512))));
  236. }
  237. }
  238. /**
  239. * Unpacks file header for one file entry.
  240. *
  241. * @param string $binaryData
  242. * @return array $fileheader
  243. */
  244. protected function readHeader($binaryData) {
  245. if (strlen($binaryData) != 512) {
  246. return false;
  247. }
  248. $header = array();
  249. $checksum = 0;
  250. // First part of the header
  251. for ($i = 0; $i < 148; $i++) {
  252. $checksum += ord(substr($binaryData, $i, 1));
  253. }
  254. // Calculate the checksum
  255. // Ignore the checksum value and replace it by ' ' (space)
  256. for ($i = 148; $i < 156; $i++) {
  257. $checksum += ord(' ');
  258. }
  259. // Last part of the header
  260. for ($i = 156; $i < 512; $i++) {
  261. $checksum += ord(substr($binaryData, $i, 1));
  262. }
  263. // Extract the values
  264. $data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor/a155prefix", $binaryData);
  265. // Extract the properties
  266. $header['checksum'] = octDec(trim($data['checksum']));
  267. if ($header['checksum'] == $checksum) {
  268. $header['filename'] = trim($data['filename']);
  269. $header['mode'] = octDec(trim($data['mode']));
  270. $header['uid'] = octDec(trim($data['uid']));
  271. $header['gid'] = octDec(trim($data['gid']));
  272. $header['size'] = octDec(trim($data['size']));
  273. $header['mtime'] = octDec(trim($data['mtime']));
  274. $header['prefix'] = trim($data['prefix']);
  275. if ($header['prefix']) {
  276. $header['filename'] = $header['prefix'].'/'.$header['filename'];
  277. }
  278. if (($header['typeflag'] = $data['typeflag']) == '5') {
  279. $header['size'] = 0;
  280. $header['type'] = 'folder';
  281. }
  282. else {
  283. $header['type'] = 'file';
  284. }
  285. $header['offset'] = $this->file->tell();
  286. return $header;
  287. }
  288. else {
  289. return false;
  290. }
  291. }
  292. }
  293. /**
  294. * The File class handles all file operations.
  295. *
  296. * Example:
  297. * using php functions:
  298. * $fp = fopen('filename', 'wb');
  299. * fwrite($fp, '...');
  300. * fclose($fp);
  301. *
  302. * using this class:
  303. * $file = new ArchiveFile('filename');
  304. * $file->write('...');
  305. * $file->close();
  306. */
  307. class ArchiveFile {
  308. protected $resource = null;
  309. protected $filename;
  310. /**
  311. * Opens a new file.
  312. *
  313. * @param string $filename
  314. * @param string $mode
  315. */
  316. public function __construct($filename, $mode = 'wb') {
  317. $this->filename = $filename;
  318. $this->resource = fopen($filename, $mode);
  319. if ($this->resource === false) {
  320. user_error('Can not open file ' . $filename);
  321. }
  322. }
  323. /**
  324. * Calls the specified function on the open file.
  325. * Do not call this function directly. Use $file->write('') instead.
  326. *
  327. * @param string $function
  328. * @param array $arguments
  329. */
  330. public function __call($function, $arguments) {
  331. if (function_exists('f' . $function)) {
  332. array_unshift($arguments, $this->resource);
  333. return call_user_func_array('f' . $function, $arguments);
  334. }
  335. else if (function_exists($function)) {
  336. array_unshift($arguments, $this->filename);
  337. return call_user_func_array($function, $arguments);
  338. }
  339. else {
  340. user_error('Can not call file method ' . $function);
  341. }
  342. }
  343. }
  344. /**
  345. * The File class handles all file operations on a zipped file.
  346. *
  347. */
  348. class ArchiveZipFile extends ArchiveFile {
  349. /**
  350. * Opens a new zipped file.
  351. *
  352. * @param string $filename
  353. * @param string $mode
  354. */
  355. public function __construct($filename, $mode = 'wb') {
  356. $this->filename = $filename;
  357. if (!function_exists('gzopen')) {
  358. user_error('Can not find functions of the zlib extension');
  359. }
  360. $this->resource = @gzopen($filename, $mode);
  361. if ($this->resource === false) {
  362. user_error('Can not open file ' . $filename);
  363. }
  364. }
  365. /**
  366. * Calls the specified function on the open file.
  367. *
  368. * @param string $function
  369. * @param array $arguments
  370. */
  371. public function __call($function, $arguments) {
  372. if (function_exists('gz' . $function)) {
  373. array_unshift($arguments, $this->resource);
  374. return call_user_func_array('gz' . $function, $arguments);
  375. }
  376. else if (function_exists($function)) {
  377. array_unshift($arguments, $this->filename);
  378. return call_user_func_array($function, $arguments);
  379. }
  380. else {
  381. user_error('Can not call method ' . $function);
  382. }
  383. }
  384. /**
  385. * Returns the filesize of the unzipped file
  386. */
  387. public function getFileSize() {
  388. $byteBlock = 1<<14;
  389. $eof = $byteBlock;
  390. // the correction is for zip files that are too small
  391. // to get in the first while loop
  392. $correction = 1;
  393. while ($this->seek($eof) == 0) {
  394. $eof += $byteBlock;
  395. $correction = 0;
  396. }
  397. while ($byteBlock > 1) {
  398. $byteBlock >>= 1;
  399. $eof += $byteBlock * ($this->seek($eof) ? -1 : 1);
  400. }
  401. if ($this->seek($eof) == -1) $eof -= 1;
  402. $this->rewind();
  403. return $eof - $correction;
  404. }
  405. }
  406. //EOF