unzip.lib.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /* $Id: unzip.lib.php 10142 2007-03-20 10:32:13Z cybot_tm $ */
  4. /**
  5. * ZIP file unpack classes. Contributed to the phpMyAdmin project.
  6. *
  7. * @category phpPublic
  8. * @package File-Formats-ZIP
  9. * @subpackage Unzip
  10. * @filesource unzip.lib.php
  11. * @version 1.0.1
  12. *
  13. * @author Holger Boskugel <vbwebprofi@gmx.de>
  14. * @copyright Copyright © 2003, Holger Boskugel, Berlin, Germany
  15. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  16. *
  17. * @history
  18. * 2003-12-02 - HB : Patched : naming bug : Time/Size of file
  19. * Added : ZIP file comment
  20. * Added : Check BZIP2 support of PHP
  21. * 2003-11-29 - HB * Initial version
  22. */
  23. /**
  24. * Unzip class, which retrieves entries from ZIP files.
  25. *
  26. * Supports only the compression modes
  27. * - 0 : Stored,
  28. * - 8 : Deflated and
  29. * - 12 : BZIP2
  30. *
  31. * Based on :<BR>
  32. * <BR>
  33. * {@link http://www.pkware.com/products/enterprise/white_papers/appnote.html
  34. * * Official ZIP file format}<BR>
  35. * {@link http://msdn.microsoft.com/library/en-us/w98ddk/hh/w98ddk/storage_5l4m.asp
  36. * * Microsoft DOS date/time format}
  37. *
  38. * @category phpPublic
  39. * @package File-Formats-ZIP
  40. * @subpackage Unzip
  41. * @version 1.0.1
  42. * @author Holger Boskugel <vbwebprofi@gmx.de>
  43. * @uses SimpleUnzipEntry
  44. * @example example.unzip.php Two examples
  45. */
  46. class SimpleUnzip {
  47. // 2003-12-02 - HB >
  48. /**
  49. * Array to store file entries
  50. *
  51. * @var string
  52. * @access public
  53. * @see ReadFile()
  54. * @since 1.0.1
  55. */
  56. var $Comment = '';
  57. // 2003-12-02 - HB <
  58. /**
  59. * Array to store file entries
  60. *
  61. * @var array
  62. * @access public
  63. * @see ReadFile()
  64. * @since 1.0
  65. */
  66. var $Entries = array();
  67. /**
  68. * Name of the ZIP file
  69. *
  70. * @var string
  71. * @access public
  72. * @see ReadFile()
  73. * @since 1.0
  74. */
  75. var $Name = '';
  76. /**
  77. * Size of the ZIP file
  78. *
  79. * @var integer
  80. * @access public
  81. * @see ReadFile()
  82. * @since 1.0
  83. */
  84. var $Size = 0;
  85. /**
  86. * Time of the ZIP file (unix timestamp)
  87. *
  88. * @var integer
  89. * @access public
  90. * @see ReadFile()
  91. * @since 1.0
  92. */
  93. var $Time = 0;
  94. /**
  95. * Contructor of the class
  96. *
  97. * @param string File name
  98. * @return SimpleUnzip Instanced class
  99. * @access public
  100. * @uses SimpleUnzip::ReadFile() Opens file on new if specified
  101. * @since 1.0
  102. */
  103. function SimpleUnzip($in_FileName = '')
  104. {
  105. if ($in_FileName !== '') {
  106. SimpleUnzip::ReadFile($in_FileName);
  107. }
  108. } // end of the 'SimpleUnzip' constructor
  109. /**
  110. * Counts the entries
  111. *
  112. * @return integer Count of ZIP entries
  113. * @access public
  114. * @uses $Entries
  115. * @since 1.0
  116. */
  117. function Count()
  118. {
  119. return count($this->Entries);
  120. } // end of the 'Count()' method
  121. /**
  122. * Gets data of the specified ZIP entry
  123. *
  124. * @param integer Index of the ZIP entry
  125. * @return mixed Data for the ZIP entry
  126. * @uses SimpleUnzipEntry::$Data
  127. * @access public
  128. * @since 1.0
  129. */
  130. function GetData($in_Index)
  131. {
  132. return $this->Entries[$in_Index]->Data;
  133. } // end of the 'GetData()' method
  134. /**
  135. * Gets an entry of the ZIP file
  136. *
  137. * @param integer Index of the ZIP entry
  138. * @return SimpleUnzipEntry Entry of the ZIP file
  139. * @uses $Entries
  140. * @access public
  141. * @since 1.0
  142. */
  143. function GetEntry($in_Index)
  144. {
  145. return $this->Entries[$in_Index];
  146. } // end of the 'GetEntry()' method
  147. /**
  148. * Gets error code for the specified ZIP entry
  149. *
  150. * @param integer Index of the ZIP entry
  151. * @return integer Error code for the ZIP entry
  152. * @uses SimpleUnzipEntry::$Error
  153. * @access public
  154. * @since 1.0
  155. */
  156. function GetError($in_Index)
  157. {
  158. return $this->Entries[$in_Index]->Error;
  159. } // end of the 'GetError()' method
  160. /**
  161. * Gets error message for the specified ZIP entry
  162. *
  163. * @param integer Index of the ZIP entry
  164. * @return string Error message for the ZIP entry
  165. * @uses SimpleUnzipEntry::$ErrorMsg
  166. * @access public
  167. * @since 1.0
  168. */
  169. function GetErrorMsg($in_Index)
  170. {
  171. return $this->Entries[$in_Index]->ErrorMsg;
  172. } // end of the 'GetErrorMsg()' method
  173. /**
  174. * Gets file name for the specified ZIP entry
  175. *
  176. * @param integer Index of the ZIP entry
  177. * @return string File name for the ZIP entry
  178. * @uses SimpleUnzipEntry::$Name
  179. * @access public
  180. * @since 1.0
  181. */
  182. function GetName($in_Index)
  183. {
  184. return $this->Entries[$in_Index]->Name;
  185. } // end of the 'GetName()' method
  186. /**
  187. * Gets path of the file for the specified ZIP entry
  188. *
  189. * @param integer Index of the ZIP entry
  190. * @return string Path of the file for the ZIP entry
  191. * @uses SimpleUnzipEntry::$Path
  192. * @access public
  193. * @since 1.0
  194. */
  195. function GetPath($in_Index)
  196. {
  197. return $this->Entries[$in_Index]->Path;
  198. } // end of the 'GetPath()' method
  199. /**
  200. * Gets file time for the specified ZIP entry
  201. *
  202. * @param integer Index of the ZIP entry
  203. * @return integer File time for the ZIP entry (unix timestamp)
  204. * @uses SimpleUnzipEntry::$Time
  205. * @access public
  206. * @since 1.0
  207. */
  208. function GetTime($in_Index)
  209. {
  210. return $this->Entries[$in_Index]->Time;
  211. } // end of the 'GetTime()' method
  212. /**
  213. * Reads ZIP file and extracts the entries
  214. *
  215. * @param string File name of the ZIP archive
  216. * @return array ZIP entry list (see also class variable {@link $Entries $Entries})
  217. * @uses SimpleUnzipEntry For the entries
  218. * @access public
  219. * @since 1.0
  220. */
  221. function ReadFile($in_FileName)
  222. {
  223. $this->Entries = array();
  224. // Get file parameters
  225. $this->Name = $in_FileName;
  226. $this->Time = filemtime($in_FileName);
  227. $this->Size = filesize($in_FileName);
  228. // Read file
  229. $oF = fopen($in_FileName, 'rb');
  230. $vZ = fread($oF, $this->Size);
  231. fclose($oF);
  232. // 2003-12-02 - HB >
  233. // Cut end of central directory
  234. $aE = explode("\x50\x4b\x05\x06", $vZ);
  235. // Easiest way, but not sure if format changes
  236. //$this->Comment = substr($aE[1], 18);
  237. // Normal way
  238. $aP = unpack('x16/v1CL', $aE[1]);
  239. $this->Comment = substr($aE[1], 18, $aP['CL']);
  240. // Translates end of line from other operating systems
  241. $this->Comment = strtr($this->Comment, array("\r\n" => "\n",
  242. "\r" => "\n"));
  243. // 2003-12-02 - HB <
  244. // Cut the entries from the central directory
  245. $aE = explode("\x50\x4b\x01\x02", $vZ);
  246. // Explode to each part
  247. $aE = explode("\x50\x4b\x03\x04", $aE[0]);
  248. // Shift out spanning signature or empty entry
  249. array_shift($aE);
  250. // Loop through the entries
  251. foreach ($aE as $vZ) {
  252. $aI = array();
  253. $aI['E'] = 0;
  254. $aI['EM'] = '';
  255. // Retrieving local file header information
  256. $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL', $vZ);
  257. // Check if data is encrypted
  258. $bE = ($aP['GPF'] & 0x0001) ? TRUE : FALSE;
  259. $nF = $aP['FNL'];
  260. // Special case : value block after the compressed data
  261. if ($aP['GPF'] & 0x0008) {
  262. $aP1 = unpack('V1CRC/V1CS/V1UCS', substr($vZ, -12));
  263. $aP['CRC'] = $aP1['CRC'];
  264. $aP['CS'] = $aP1['CS'];
  265. $aP['UCS'] = $aP1['UCS'];
  266. $vZ = substr($vZ, 0, -12);
  267. }
  268. // Getting stored filename
  269. $aI['N'] = substr($vZ, 26, $nF);
  270. if (substr($aI['N'], -1) == '/') {
  271. // is a directory entry - will be skipped
  272. continue;
  273. }
  274. // Truncate full filename in path and filename
  275. $aI['P'] = dirname($aI['N']);
  276. $aI['P'] = $aI['P'] == '.' ? '' : $aI['P'];
  277. $aI['N'] = basename($aI['N']);
  278. $vZ = substr($vZ, 26 + $nF);
  279. if (strlen($vZ) != $aP['CS']) {
  280. $aI['E'] = 1;
  281. $aI['EM'] = 'Compressed size is not equal with the value in header information.';
  282. } else {
  283. if ($bE) {
  284. $aI['E'] = 5;
  285. $aI['EM'] = 'File is encrypted, which is not supported from this class.';
  286. } else {
  287. switch($aP['CM']) {
  288. case 0: // Stored
  289. // Here is nothing to do, the file ist flat.
  290. break;
  291. case 8: // Deflated
  292. $vZ = gzinflate($vZ);
  293. break;
  294. case 12: // BZIP2
  295. // 2003-12-02 - HB >
  296. if (! extension_loaded('bz2')) {
  297. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
  298. @dl('php_bz2.dll');
  299. } else {
  300. @dl('bz2.so');
  301. }
  302. }
  303. if (extension_loaded('bz2')) {
  304. // 2003-12-02 - HB <
  305. $vZ = bzdecompress($vZ);
  306. // 2003-12-02 - HB >
  307. } else {
  308. $aI['E'] = 7;
  309. $aI['EM'] = "PHP BZIP2 extension not available.";
  310. }
  311. // 2003-12-02 - HB <
  312. break;
  313. default:
  314. $aI['E'] = 6;
  315. $aI['EM'] = "De-/Compression method {$aP['CM']} is not supported.";
  316. }
  317. // 2003-12-02 - HB >
  318. if (! $aI['E']) {
  319. // 2003-12-02 - HB <
  320. if ($vZ === FALSE) {
  321. $aI['E'] = 2;
  322. $aI['EM'] = 'Decompression of data failed.';
  323. } else {
  324. if (strlen($vZ) != $aP['UCS']) {
  325. $aI['E'] = 3;
  326. $aI['EM'] = 'Uncompressed size is not equal with the value in header information.';
  327. } else {
  328. if (crc32($vZ) != $aP['CRC']) {
  329. $aI['E'] = 4;
  330. $aI['EM'] = 'CRC32 checksum is not equal with the value in header information.';
  331. }
  332. }
  333. }
  334. // 2003-12-02 - HB >
  335. }
  336. // 2003-12-02 - HB <
  337. }
  338. }
  339. $aI['D'] = $vZ;
  340. // DOS to UNIX timestamp
  341. $aI['T'] = mktime(($aP['FT'] & 0xf800) >> 11,
  342. ($aP['FT'] & 0x07e0) >> 5,
  343. ($aP['FT'] & 0x001f) << 1,
  344. ($aP['FD'] & 0x01e0) >> 5,
  345. ($aP['FD'] & 0x001f),
  346. (($aP['FD'] & 0xfe00) >> 9) + 1980);
  347. $this->Entries[] = &new SimpleUnzipEntry($aI);
  348. } // end for each entries
  349. return $this->Entries;
  350. } // end of the 'ReadFile()' method
  351. } // end of the 'SimpleUnzip' class
  352. /**
  353. * Entry of the ZIP file.
  354. *
  355. * @category phpPublic
  356. * @package File-Formats-ZIP
  357. * @subpackage Unzip
  358. * @version 1.0
  359. * @author Holger Boskugel <vbwebprofi@gmx.de>
  360. * @example example.unzip.php Two examples
  361. */
  362. class SimpleUnzipEntry {
  363. /**
  364. * Data of the file entry
  365. *
  366. * @var mixed
  367. * @access public
  368. * @see SimpleUnzipEntry()
  369. * @since 1.0
  370. */
  371. var $Data = '';
  372. /**
  373. * Error of the file entry
  374. *
  375. * - 0 : No error raised.<BR>
  376. * - 1 : Compressed size is not equal with the value in header information.<BR>
  377. * - 2 : Decompression of data failed.<BR>
  378. * - 3 : Uncompressed size is not equal with the value in header information.<BR>
  379. * - 4 : CRC32 checksum is not equal with the value in header information.<BR>
  380. * - 5 : File is encrypted, which is not supported from this class.<BR>
  381. * - 6 : De-/Compression method ... is not supported.<BR>
  382. * - 7 : PHP BZIP2 extension not available.
  383. *
  384. * @var integer
  385. * @access public
  386. * @see SimpleUnzipEntry()
  387. * @since 1.0
  388. */
  389. var $Error = 0;
  390. /**
  391. * Error message of the file entry
  392. *
  393. * @var string
  394. * @access public
  395. * @see SimpleUnzipEntry()
  396. * @since 1.0
  397. */
  398. var $ErrorMsg = '';
  399. /**
  400. * File name of the file entry
  401. *
  402. * @var string
  403. * @access public
  404. * @see SimpleUnzipEntry()
  405. * @since 1.0
  406. */
  407. var $Name = '';
  408. /**
  409. * File path of the file entry
  410. *
  411. * @var string
  412. * @access public
  413. * @see SimpleUnzipEntry()
  414. * @since 1.0
  415. */
  416. var $Path = '';
  417. /**
  418. * File time of the file entry (unix timestamp)
  419. *
  420. * @var integer
  421. * @access public
  422. * @see SimpleUnzipEntry()
  423. * @since 1.0
  424. */
  425. var $Time = 0;
  426. /**
  427. * Contructor of the class
  428. *
  429. * @param array Entry datas
  430. * @return SimpleUnzipEntry Instanced class
  431. * @access public
  432. * @since 1.0
  433. */
  434. function SimpleUnzipEntry($in_Entry)
  435. {
  436. $this->Data = $in_Entry['D'];
  437. $this->Error = $in_Entry['E'];
  438. $this->ErrorMsg = $in_Entry['EM'];
  439. $this->Name = $in_Entry['N'];
  440. $this->Path = $in_Entry['P'];
  441. $this->Time = $in_Entry['T'];
  442. } // end of the 'SimpleUnzipEntry' constructor
  443. } // end of the 'SimpleUnzipEntry' class
  444. ?>