Tar.php 86 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * File::CSV
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * Copyright (c) 1997-2008,
  9. * Vincent Blavet <vincent@phpconcept.net>
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions are met:
  14. *
  15. * * Redistributions of source code must retain the above copyright notice,
  16. * this list of conditions and the following disclaimer.
  17. * * Redistributions in binary form must reproduce the above copyright
  18. * notice, this list of conditions and the following disclaimer in the
  19. * documentation and/or other materials provided with the distribution.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  24. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  25. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  27. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  28. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. *
  32. * @category File_Formats
  33. * @package Archive_Tar
  34. * @author Vincent Blavet <vincent@phpconcept.net>
  35. * @copyright 1997-2010 The Authors
  36. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  37. * @version CVS: $Id$
  38. * @link http://pear.php.net/package/Archive_Tar
  39. */
  40. // If the PEAR class cannot be loaded via the autoloader,
  41. // then try to require_once it from the PHP include path.
  42. if (!class_exists('PEAR')) {
  43. require_once 'PEAR.php';
  44. }
  45. define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
  46. define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
  47. if (!function_exists('gzopen') && function_exists('gzopen64')) {
  48. function gzopen($filename, $mode, $use_include_path = 0)
  49. {
  50. return gzopen64($filename, $mode, $use_include_path);
  51. }
  52. }
  53. if (!function_exists('gztell') && function_exists('gztell64')) {
  54. function gztell($zp)
  55. {
  56. return gztell64($zp);
  57. }
  58. }
  59. if (!function_exists('gzseek') && function_exists('gzseek64')) {
  60. function gzseek($zp, $offset, $whence = SEEK_SET)
  61. {
  62. return gzseek64($zp, $offset, $whence);
  63. }
  64. }
  65. /**
  66. * Creates a (compressed) Tar archive
  67. *
  68. * @package Archive_Tar
  69. * @author Vincent Blavet <vincent@phpconcept.net>
  70. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  71. * @version $Revision$
  72. */
  73. class Archive_Tar extends PEAR
  74. {
  75. /**
  76. * @var string Name of the Tar
  77. */
  78. public $_tarname = '';
  79. /**
  80. * @var boolean if true, the Tar file will be gzipped
  81. */
  82. public $_compress = false;
  83. /**
  84. * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2'
  85. */
  86. public $_compress_type = 'none';
  87. /**
  88. * @var string Explode separator
  89. */
  90. public $_separator = ' ';
  91. /**
  92. * @var file descriptor
  93. */
  94. public $_file = 0;
  95. /**
  96. * @var string Local Tar name of a remote Tar (http:// or ftp://)
  97. */
  98. public $_temp_tarname = '';
  99. /**
  100. * @var string regular expression for ignoring files or directories
  101. */
  102. public $_ignore_regexp = '';
  103. /**
  104. * @var object PEAR_Error object
  105. */
  106. public $error_object = null;
  107. /**
  108. * Format for data extraction
  109. *
  110. * @var string
  111. */
  112. public $_fmt = '';
  113. /**
  114. * @var int Length of the read buffer in bytes
  115. */
  116. protected $buffer_length;
  117. /**
  118. * Archive_Tar Class constructor. This flavour of the constructor only
  119. * declare a new Archive_Tar object, identifying it by the name of the
  120. * tar file.
  121. * If the compress argument is set the tar will be read or created as a
  122. * gzip or bz2 compressed TAR file.
  123. *
  124. * @param string $p_tarname The name of the tar archive to create
  125. * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This
  126. * parameter indicates if gzip, bz2 or lzma2 compression
  127. * is required. For compatibility reason the
  128. * boolean value 'true' means 'gz'.
  129. * @param int $buffer_length Length of the read buffer in bytes
  130. *
  131. * @return bool
  132. */
  133. public function __construct($p_tarname, $p_compress = null, $buffer_length = 512)
  134. {
  135. parent::__construct();
  136. $this->_compress = false;
  137. $this->_compress_type = 'none';
  138. if (($p_compress === null) || ($p_compress == '')) {
  139. if (@file_exists($p_tarname)) {
  140. if ($fp = @fopen($p_tarname, "rb")) {
  141. // look for gzip magic cookie
  142. $data = fread($fp, 2);
  143. fclose($fp);
  144. if ($data == "\37\213") {
  145. $this->_compress = true;
  146. $this->_compress_type = 'gz';
  147. // No sure it's enought for a magic code ....
  148. } elseif ($data == "BZ") {
  149. $this->_compress = true;
  150. $this->_compress_type = 'bz2';
  151. } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') {
  152. $this->_compress = true;
  153. $this->_compress_type = 'lzma2';
  154. }
  155. }
  156. } else {
  157. // probably a remote file or some file accessible
  158. // through a stream interface
  159. if (substr($p_tarname, -2) == 'gz') {
  160. $this->_compress = true;
  161. $this->_compress_type = 'gz';
  162. } elseif ((substr($p_tarname, -3) == 'bz2') ||
  163. (substr($p_tarname, -2) == 'bz')
  164. ) {
  165. $this->_compress = true;
  166. $this->_compress_type = 'bz2';
  167. } else {
  168. if (substr($p_tarname, -2) == 'xz') {
  169. $this->_compress = true;
  170. $this->_compress_type = 'lzma2';
  171. }
  172. }
  173. }
  174. } else {
  175. if (($p_compress === true) || ($p_compress == 'gz')) {
  176. $this->_compress = true;
  177. $this->_compress_type = 'gz';
  178. } else {
  179. if ($p_compress == 'bz2') {
  180. $this->_compress = true;
  181. $this->_compress_type = 'bz2';
  182. } else {
  183. if ($p_compress == 'lzma2') {
  184. $this->_compress = true;
  185. $this->_compress_type = 'lzma2';
  186. } else {
  187. $this->_error(
  188. "Unsupported compression type '$p_compress'\n" .
  189. "Supported types are 'gz', 'bz2' and 'lzma2'.\n"
  190. );
  191. return false;
  192. }
  193. }
  194. }
  195. }
  196. $this->_tarname = $p_tarname;
  197. if ($this->_compress) { // assert zlib or bz2 or xz extension support
  198. if ($this->_compress_type == 'gz') {
  199. $extname = 'zlib';
  200. } else {
  201. if ($this->_compress_type == 'bz2') {
  202. $extname = 'bz2';
  203. } else {
  204. if ($this->_compress_type == 'lzma2') {
  205. $extname = 'xz';
  206. }
  207. }
  208. }
  209. if (!extension_loaded($extname)) {
  210. PEAR::loadExtension($extname);
  211. }
  212. if (!extension_loaded($extname)) {
  213. $this->_error(
  214. "The extension '$extname' couldn't be found.\n" .
  215. "Please make sure your version of PHP was built " .
  216. "with '$extname' support.\n"
  217. );
  218. return false;
  219. }
  220. }
  221. if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
  222. $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
  223. "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
  224. "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
  225. } else {
  226. $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
  227. "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
  228. "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
  229. }
  230. $this->buffer_length = $buffer_length;
  231. }
  232. public function __destruct()
  233. {
  234. $this->_close();
  235. // ----- Look for a local copy to delete
  236. if ($this->_temp_tarname != '' && (bool) preg_match('/^tar[[:alnum:]]*\.tmp$/', $this->_temp_tarname)) {
  237. @unlink($this->_temp_tarname);
  238. }
  239. }
  240. /**
  241. * This method creates the archive file and add the files / directories
  242. * that are listed in $p_filelist.
  243. * If a file with the same name exist and is writable, it is replaced
  244. * by the new tar.
  245. * The method return false and a PEAR error text.
  246. * The $p_filelist parameter can be an array of string, each string
  247. * representing a filename or a directory name with their path if
  248. * needed. It can also be a single string with names separated by a
  249. * single blank.
  250. * For each directory added in the archive, the files and
  251. * sub-directories are also added.
  252. * See also createModify() method for more details.
  253. *
  254. * @param array $p_filelist An array of filenames and directory names, or a
  255. * single string with names separated by a single
  256. * blank space.
  257. *
  258. * @return true on success, false on error.
  259. * @see createModify()
  260. */
  261. public function create($p_filelist)
  262. {
  263. return $this->createModify($p_filelist, '', '');
  264. }
  265. /**
  266. * This method add the files / directories that are listed in $p_filelist in
  267. * the archive. If the archive does not exist it is created.
  268. * The method return false and a PEAR error text.
  269. * The files and directories listed are only added at the end of the archive,
  270. * even if a file with the same name is already archived.
  271. * See also createModify() method for more details.
  272. *
  273. * @param array $p_filelist An array of filenames and directory names, or a
  274. * single string with names separated by a single
  275. * blank space.
  276. *
  277. * @return true on success, false on error.
  278. * @see createModify()
  279. * @access public
  280. */
  281. public function add($p_filelist)
  282. {
  283. return $this->addModify($p_filelist, '', '');
  284. }
  285. /**
  286. * @param string $p_path
  287. * @param bool $p_preserve
  288. * @param bool $p_symlinks
  289. * @return bool
  290. */
  291. public function extract($p_path = '', $p_preserve = false, $p_symlinks = true)
  292. {
  293. return $this->extractModify($p_path, '', $p_preserve, $p_symlinks);
  294. }
  295. /**
  296. * @return array|int
  297. */
  298. public function listContent()
  299. {
  300. $v_list_detail = array();
  301. if ($this->_openRead()) {
  302. if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  303. unset($v_list_detail);
  304. $v_list_detail = 0;
  305. }
  306. $this->_close();
  307. }
  308. return $v_list_detail;
  309. }
  310. /**
  311. * This method creates the archive file and add the files / directories
  312. * that are listed in $p_filelist.
  313. * If the file already exists and is writable, it is replaced by the
  314. * new tar. It is a create and not an add. If the file exists and is
  315. * read-only or is a directory it is not replaced. The method return
  316. * false and a PEAR error text.
  317. * The $p_filelist parameter can be an array of string, each string
  318. * representing a filename or a directory name with their path if
  319. * needed. It can also be a single string with names separated by a
  320. * single blank.
  321. * The path indicated in $p_remove_dir will be removed from the
  322. * memorized path of each file / directory listed when this path
  323. * exists. By default nothing is removed (empty path '')
  324. * The path indicated in $p_add_dir will be added at the beginning of
  325. * the memorized path of each file / directory listed. However it can
  326. * be set to empty ''. The adding of a path is done after the removing
  327. * of path.
  328. * The path add/remove ability enables the user to prepare an archive
  329. * for extraction in a different path than the origin files are.
  330. * See also addModify() method for file adding properties.
  331. *
  332. * @param array $p_filelist An array of filenames and directory names,
  333. * or a single string with names separated by
  334. * a single blank space.
  335. * @param string $p_add_dir A string which contains a path to be added
  336. * to the memorized path of each element in
  337. * the list.
  338. * @param string $p_remove_dir A string which contains a path to be
  339. * removed from the memorized path of each
  340. * element in the list, when relevant.
  341. *
  342. * @return boolean true on success, false on error.
  343. * @see addModify()
  344. */
  345. public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '')
  346. {
  347. $v_result = true;
  348. if (!$this->_openWrite()) {
  349. return false;
  350. }
  351. if ($p_filelist != '') {
  352. if (is_array($p_filelist)) {
  353. $v_list = $p_filelist;
  354. } elseif (is_string($p_filelist)) {
  355. $v_list = explode($this->_separator, $p_filelist);
  356. } else {
  357. $this->_cleanFile();
  358. $this->_error('Invalid file list');
  359. return false;
  360. }
  361. $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  362. }
  363. if ($v_result) {
  364. $this->_writeFooter();
  365. $this->_close();
  366. } else {
  367. $this->_cleanFile();
  368. }
  369. return $v_result;
  370. }
  371. /**
  372. * This method add the files / directories listed in $p_filelist at the
  373. * end of the existing archive. If the archive does not yet exists it
  374. * is created.
  375. * The $p_filelist parameter can be an array of string, each string
  376. * representing a filename or a directory name with their path if
  377. * needed. It can also be a single string with names separated by a
  378. * single blank.
  379. * The path indicated in $p_remove_dir will be removed from the
  380. * memorized path of each file / directory listed when this path
  381. * exists. By default nothing is removed (empty path '')
  382. * The path indicated in $p_add_dir will be added at the beginning of
  383. * the memorized path of each file / directory listed. However it can
  384. * be set to empty ''. The adding of a path is done after the removing
  385. * of path.
  386. * The path add/remove ability enables the user to prepare an archive
  387. * for extraction in a different path than the origin files are.
  388. * If a file/dir is already in the archive it will only be added at the
  389. * end of the archive. There is no update of the existing archived
  390. * file/dir. However while extracting the archive, the last file will
  391. * replace the first one. This results in a none optimization of the
  392. * archive size.
  393. * If a file/dir does not exist the file/dir is ignored. However an
  394. * error text is send to PEAR error.
  395. * If a file/dir is not readable the file/dir is ignored. However an
  396. * error text is send to PEAR error.
  397. *
  398. * @param array $p_filelist An array of filenames and directory
  399. * names, or a single string with names
  400. * separated by a single blank space.
  401. * @param string $p_add_dir A string which contains a path to be
  402. * added to the memorized path of each
  403. * element in the list.
  404. * @param string $p_remove_dir A string which contains a path to be
  405. * removed from the memorized path of
  406. * each element in the list, when
  407. * relevant.
  408. *
  409. * @return true on success, false on error.
  410. */
  411. public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '')
  412. {
  413. $v_result = true;
  414. if (!$this->_isArchive()) {
  415. $v_result = $this->createModify(
  416. $p_filelist,
  417. $p_add_dir,
  418. $p_remove_dir
  419. );
  420. } else {
  421. if (is_array($p_filelist)) {
  422. $v_list = $p_filelist;
  423. } elseif (is_string($p_filelist)) {
  424. $v_list = explode($this->_separator, $p_filelist);
  425. } else {
  426. $this->_error('Invalid file list');
  427. return false;
  428. }
  429. $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  430. }
  431. return $v_result;
  432. }
  433. /**
  434. * This method add a single string as a file at the
  435. * end of the existing archive. If the archive does not yet exists it
  436. * is created.
  437. *
  438. * @param string $p_filename A string which contains the full
  439. * filename path that will be associated
  440. * with the string.
  441. * @param string $p_string The content of the file added in
  442. * the archive.
  443. * @param bool|int $p_datetime A custom date/time (unix timestamp)
  444. * for the file (optional).
  445. * @param array $p_params An array of optional params:
  446. * stamp => the datetime (replaces
  447. * datetime above if it exists)
  448. * mode => the permissions on the
  449. * file (600 by default)
  450. * type => is this a link? See the
  451. * tar specification for details.
  452. * (default = regular file)
  453. * uid => the user ID of the file
  454. * (default = 0 = root)
  455. * gid => the group ID of the file
  456. * (default = 0 = root)
  457. *
  458. * @return true on success, false on error.
  459. */
  460. public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
  461. {
  462. $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
  463. $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
  464. $p_type = @$p_params["type"] ? $p_params["type"] : "";
  465. $p_uid = @$p_params["uid"] ? $p_params["uid"] : "";
  466. $p_gid = @$p_params["gid"] ? $p_params["gid"] : "";
  467. $v_result = true;
  468. if (!$this->_isArchive()) {
  469. if (!$this->_openWrite()) {
  470. return false;
  471. }
  472. $this->_close();
  473. }
  474. if (!$this->_openAppend()) {
  475. return false;
  476. }
  477. // Need to check the get back to the temporary file ? ....
  478. $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params);
  479. $this->_writeFooter();
  480. $this->_close();
  481. return $v_result;
  482. }
  483. /**
  484. * This method extract all the content of the archive in the directory
  485. * indicated by $p_path. When relevant the memorized path of the
  486. * files/dir can be modified by removing the $p_remove_path path at the
  487. * beginning of the file/dir path.
  488. * While extracting a file, if the directory path does not exists it is
  489. * created.
  490. * While extracting a file, if the file already exists it is replaced
  491. * without looking for last modification date.
  492. * While extracting a file, if the file already exists and is write
  493. * protected, the extraction is aborted.
  494. * While extracting a file, if a directory with the same name already
  495. * exists, the extraction is aborted.
  496. * While extracting a directory, if a file with the same name already
  497. * exists, the extraction is aborted.
  498. * While extracting a file/directory if the destination directory exist
  499. * and is write protected, or does not exist but can not be created,
  500. * the extraction is aborted.
  501. * If after extraction an extracted file does not show the correct
  502. * stored file size, the extraction is aborted.
  503. * When the extraction is aborted, a PEAR error text is set and false
  504. * is returned. However the result can be a partial extraction that may
  505. * need to be manually cleaned.
  506. *
  507. * @param string $p_path The path of the directory where the
  508. * files/dir need to by extracted.
  509. * @param string $p_remove_path Part of the memorized path that can be
  510. * removed if present at the beginning of
  511. * the file/dir path.
  512. * @param boolean $p_preserve Preserve user/group ownership of files
  513. * @param boolean $p_symlinks Allow symlinks.
  514. *
  515. * @return boolean true on success, false on error.
  516. * @see extractList()
  517. */
  518. public function extractModify($p_path, $p_remove_path, $p_preserve = false, $p_symlinks = true)
  519. {
  520. $v_result = true;
  521. $v_list_detail = array();
  522. if ($v_result = $this->_openRead()) {
  523. $v_result = $this->_extractList(
  524. $p_path,
  525. $v_list_detail,
  526. "complete",
  527. 0,
  528. $p_remove_path,
  529. $p_preserve,
  530. $p_symlinks
  531. );
  532. $this->_close();
  533. }
  534. return $v_result;
  535. }
  536. /**
  537. * This method extract from the archive one file identified by $p_filename.
  538. * The return value is a string with the file content, or NULL on error.
  539. *
  540. * @param string $p_filename The path of the file to extract in a string.
  541. *
  542. * @return a string with the file content or NULL.
  543. */
  544. public function extractInString($p_filename)
  545. {
  546. if ($this->_openRead()) {
  547. $v_result = $this->_extractInString($p_filename);
  548. $this->_close();
  549. } else {
  550. $v_result = null;
  551. }
  552. return $v_result;
  553. }
  554. /**
  555. * This method extract from the archive only the files indicated in the
  556. * $p_filelist. These files are extracted in the current directory or
  557. * in the directory indicated by the optional $p_path parameter.
  558. * If indicated the $p_remove_path can be used in the same way as it is
  559. * used in extractModify() method.
  560. *
  561. * @param array $p_filelist An array of filenames and directory names,
  562. * or a single string with names separated
  563. * by a single blank space.
  564. * @param string $p_path The path of the directory where the
  565. * files/dir need to by extracted.
  566. * @param string $p_remove_path Part of the memorized path that can be
  567. * removed if present at the beginning of
  568. * the file/dir path.
  569. * @param boolean $p_preserve Preserve user/group ownership of files
  570. * @param boolean $p_symlinks Allow symlinks.
  571. *
  572. * @return true on success, false on error.
  573. * @see extractModify()
  574. */
  575. public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true)
  576. {
  577. $v_result = true;
  578. $v_list_detail = array();
  579. if (is_array($p_filelist)) {
  580. $v_list = $p_filelist;
  581. } elseif (is_string($p_filelist)) {
  582. $v_list = explode($this->_separator, $p_filelist);
  583. } else {
  584. $this->_error('Invalid string list');
  585. return false;
  586. }
  587. if ($v_result = $this->_openRead()) {
  588. $v_result = $this->_extractList(
  589. $p_path,
  590. $v_list_detail,
  591. "partial",
  592. $v_list,
  593. $p_remove_path,
  594. $p_preserve,
  595. $p_symlinks
  596. );
  597. $this->_close();
  598. }
  599. return $v_result;
  600. }
  601. /**
  602. * This method set specific attributes of the archive. It uses a variable
  603. * list of parameters, in the format attribute code + attribute values :
  604. * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
  605. *
  606. * @return true on success, false on error.
  607. */
  608. public function setAttribute()
  609. {
  610. $v_result = true;
  611. // ----- Get the number of variable list of arguments
  612. if (($v_size = func_num_args()) == 0) {
  613. return true;
  614. }
  615. // ----- Get the arguments
  616. $v_att_list = func_get_args();
  617. // ----- Read the attributes
  618. $i = 0;
  619. while ($i < $v_size) {
  620. // ----- Look for next option
  621. switch ($v_att_list[$i]) {
  622. // ----- Look for options that request a string value
  623. case ARCHIVE_TAR_ATT_SEPARATOR :
  624. // ----- Check the number of parameters
  625. if (($i + 1) >= $v_size) {
  626. $this->_error(
  627. 'Invalid number of parameters for '
  628. . 'attribute ARCHIVE_TAR_ATT_SEPARATOR'
  629. );
  630. return false;
  631. }
  632. // ----- Get the value
  633. $this->_separator = $v_att_list[$i + 1];
  634. $i++;
  635. break;
  636. default :
  637. $this->_error('Unknown attribute code ' . $v_att_list[$i] . '');
  638. return false;
  639. }
  640. // ----- Next attribute
  641. $i++;
  642. }
  643. return $v_result;
  644. }
  645. /**
  646. * This method sets the regular expression for ignoring files and directories
  647. * at import, for example:
  648. * $arch->setIgnoreRegexp("#CVS|\.svn#");
  649. *
  650. * @param string $regexp regular expression defining which files or directories to ignore
  651. */
  652. public function setIgnoreRegexp($regexp)
  653. {
  654. $this->_ignore_regexp = $regexp;
  655. }
  656. /**
  657. * This method sets the regular expression for ignoring all files and directories
  658. * matching the filenames in the array list at import, for example:
  659. * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
  660. *
  661. * @param array $list a list of file or directory names to ignore
  662. *
  663. * @access public
  664. */
  665. public function setIgnoreList($list)
  666. {
  667. $list = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
  668. $regexp = '#/' . join('$|/', $list) . '#';
  669. $this->setIgnoreRegexp($regexp);
  670. }
  671. /**
  672. * @param string $p_message
  673. */
  674. public function _error($p_message)
  675. {
  676. $this->error_object = $this->raiseError($p_message);
  677. }
  678. /**
  679. * @param string $p_message
  680. */
  681. public function _warning($p_message)
  682. {
  683. $this->error_object = $this->raiseError($p_message);
  684. }
  685. /**
  686. * @param string $p_filename
  687. * @return bool
  688. */
  689. public function _isArchive($p_filename = null)
  690. {
  691. if ($p_filename == null) {
  692. $p_filename = $this->_tarname;
  693. }
  694. clearstatcache();
  695. return @is_file($p_filename) && !@is_link($p_filename);
  696. }
  697. /**
  698. * @return bool
  699. */
  700. public function _openWrite()
  701. {
  702. if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
  703. $this->_file = @gzopen($this->_tarname, "wb9");
  704. } else {
  705. if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
  706. $this->_file = @bzopen($this->_tarname, "w");
  707. } else {
  708. if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
  709. $this->_file = @xzopen($this->_tarname, 'w');
  710. } else {
  711. if ($this->_compress_type == 'none') {
  712. $this->_file = @fopen($this->_tarname, "wb");
  713. } else {
  714. $this->_error(
  715. 'Unknown or missing compression type ('
  716. . $this->_compress_type . ')'
  717. );
  718. return false;
  719. }
  720. }
  721. }
  722. }
  723. if ($this->_file == 0) {
  724. $this->_error(
  725. 'Unable to open in write mode \''
  726. . $this->_tarname . '\''
  727. );
  728. return false;
  729. }
  730. return true;
  731. }
  732. /**
  733. * @return bool
  734. */
  735. public function _openRead()
  736. {
  737. if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  738. // ----- Look if a local copy need to be done
  739. if ($this->_temp_tarname == '') {
  740. $this->_temp_tarname = uniqid('tar') . '.tmp';
  741. if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  742. $this->_error(
  743. 'Unable to open in read mode \''
  744. . $this->_tarname . '\''
  745. );
  746. $this->_temp_tarname = '';
  747. return false;
  748. }
  749. if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  750. $this->_error(
  751. 'Unable to open in write mode \''
  752. . $this->_temp_tarname . '\''
  753. );
  754. $this->_temp_tarname = '';
  755. return false;
  756. }
  757. while ($v_data = @fread($v_file_from, 1024)) {
  758. @fwrite($v_file_to, $v_data);
  759. }
  760. @fclose($v_file_from);
  761. @fclose($v_file_to);
  762. }
  763. // ----- File to open if the local copy
  764. $v_filename = $this->_temp_tarname;
  765. } else {
  766. // ----- File to open if the normal Tar file
  767. $v_filename = $this->_tarname;
  768. }
  769. if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
  770. $this->_file = @gzopen($v_filename, "rb");
  771. } else {
  772. if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
  773. $this->_file = @bzopen($v_filename, "r");
  774. } else {
  775. if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
  776. $this->_file = @xzopen($v_filename, "r");
  777. } else {
  778. if ($this->_compress_type == 'none') {
  779. $this->_file = @fopen($v_filename, "rb");
  780. } else {
  781. $this->_error(
  782. 'Unknown or missing compression type ('
  783. . $this->_compress_type . ')'
  784. );
  785. return false;
  786. }
  787. }
  788. }
  789. }
  790. if ($this->_file == 0) {
  791. $this->_error('Unable to open in read mode \'' . $v_filename . '\'');
  792. return false;
  793. }
  794. return true;
  795. }
  796. /**
  797. * @return bool
  798. */
  799. public function _openReadWrite()
  800. {
  801. if ($this->_compress_type == 'gz') {
  802. $this->_file = @gzopen($this->_tarname, "r+b");
  803. } else {
  804. if ($this->_compress_type == 'bz2') {
  805. $this->_error(
  806. 'Unable to open bz2 in read/write mode \''
  807. . $this->_tarname . '\' (limitation of bz2 extension)'
  808. );
  809. return false;
  810. } else {
  811. if ($this->_compress_type == 'lzma2') {
  812. $this->_error(
  813. 'Unable to open lzma2 in read/write mode \''
  814. . $this->_tarname . '\' (limitation of lzma2 extension)'
  815. );
  816. return false;
  817. } else {
  818. if ($this->_compress_type == 'none') {
  819. $this->_file = @fopen($this->_tarname, "r+b");
  820. } else {
  821. $this->_error(
  822. 'Unknown or missing compression type ('
  823. . $this->_compress_type . ')'
  824. );
  825. return false;
  826. }
  827. }
  828. }
  829. }
  830. if ($this->_file == 0) {
  831. $this->_error(
  832. 'Unable to open in read/write mode \''
  833. . $this->_tarname . '\''
  834. );
  835. return false;
  836. }
  837. return true;
  838. }
  839. /**
  840. * @return bool
  841. */
  842. public function _close()
  843. {
  844. //if (isset($this->_file)) {
  845. if (is_resource($this->_file)) {
  846. if ($this->_compress_type == 'gz') {
  847. @gzclose($this->_file);
  848. } else {
  849. if ($this->_compress_type == 'bz2') {
  850. @bzclose($this->_file);
  851. } else {
  852. if ($this->_compress_type == 'lzma2') {
  853. @xzclose($this->_file);
  854. } else {
  855. if ($this->_compress_type == 'none') {
  856. @fclose($this->_file);
  857. } else {
  858. $this->_error(
  859. 'Unknown or missing compression type ('
  860. . $this->_compress_type . ')'
  861. );
  862. }
  863. }
  864. }
  865. }
  866. $this->_file = 0;
  867. }
  868. // ----- Look if a local copy need to be erase
  869. // Note that it might be interesting to keep the url for a time : ToDo
  870. if ($this->_temp_tarname != '') {
  871. @unlink($this->_temp_tarname);
  872. $this->_temp_tarname = '';
  873. }
  874. return true;
  875. }
  876. /**
  877. * @return bool
  878. */
  879. public function _cleanFile()
  880. {
  881. $this->_close();
  882. // ----- Look for a local copy
  883. if ($this->_temp_tarname != '') {
  884. // ----- Remove the local copy but not the remote tarname
  885. @unlink($this->_temp_tarname);
  886. $this->_temp_tarname = '';
  887. } else {
  888. // ----- Remove the local tarname file
  889. @unlink($this->_tarname);
  890. }
  891. $this->_tarname = '';
  892. return true;
  893. }
  894. /**
  895. * @param mixed $p_binary_data
  896. * @param integer $p_len
  897. * @return bool
  898. */
  899. public function _writeBlock($p_binary_data, $p_len = null)
  900. {
  901. if (is_resource($this->_file)) {
  902. if ($p_len === null) {
  903. if ($this->_compress_type == 'gz') {
  904. @gzputs($this->_file, $p_binary_data);
  905. } else {
  906. if ($this->_compress_type == 'bz2') {
  907. @bzwrite($this->_file, $p_binary_data);
  908. } else {
  909. if ($this->_compress_type == 'lzma2') {
  910. @xzwrite($this->_file, $p_binary_data);
  911. } else {
  912. if ($this->_compress_type == 'none') {
  913. @fputs($this->_file, $p_binary_data);
  914. } else {
  915. $this->_error(
  916. 'Unknown or missing compression type ('
  917. . $this->_compress_type . ')'
  918. );
  919. }
  920. }
  921. }
  922. }
  923. } else {
  924. if ($this->_compress_type == 'gz') {
  925. @gzputs($this->_file, $p_binary_data, $p_len);
  926. } else {
  927. if ($this->_compress_type == 'bz2') {
  928. @bzwrite($this->_file, $p_binary_data, $p_len);
  929. } else {
  930. if ($this->_compress_type == 'lzma2') {
  931. @xzwrite($this->_file, $p_binary_data, $p_len);
  932. } else {
  933. if ($this->_compress_type == 'none') {
  934. @fputs($this->_file, $p_binary_data, $p_len);
  935. } else {
  936. $this->_error(
  937. 'Unknown or missing compression type ('
  938. . $this->_compress_type . ')'
  939. );
  940. }
  941. }
  942. }
  943. }
  944. }
  945. }
  946. return true;
  947. }
  948. /**
  949. * @return null|string
  950. */
  951. public function _readBlock()
  952. {
  953. $v_block = null;
  954. if (is_resource($this->_file)) {
  955. if ($this->_compress_type == 'gz') {
  956. $v_block = @gzread($this->_file, 512);
  957. } else {
  958. if ($this->_compress_type == 'bz2') {
  959. $v_block = @bzread($this->_file, 512);
  960. } else {
  961. if ($this->_compress_type == 'lzma2') {
  962. $v_block = @xzread($this->_file, 512);
  963. } else {
  964. if ($this->_compress_type == 'none') {
  965. $v_block = @fread($this->_file, 512);
  966. } else {
  967. $this->_error(
  968. 'Unknown or missing compression type ('
  969. . $this->_compress_type . ')'
  970. );
  971. }
  972. }
  973. }
  974. }
  975. }
  976. return $v_block;
  977. }
  978. /**
  979. * @param null $p_len
  980. * @return bool
  981. */
  982. public function _jumpBlock($p_len = null)
  983. {
  984. if (is_resource($this->_file)) {
  985. if ($p_len === null) {
  986. $p_len = 1;
  987. }
  988. if ($this->_compress_type == 'gz') {
  989. @gzseek($this->_file, gztell($this->_file) + ($p_len * 512));
  990. } else {
  991. if ($this->_compress_type == 'bz2') {
  992. // ----- Replace missing bztell() and bzseek()
  993. for ($i = 0; $i < $p_len; $i++) {
  994. $this->_readBlock();
  995. }
  996. } else {
  997. if ($this->_compress_type == 'lzma2') {
  998. // ----- Replace missing xztell() and xzseek()
  999. for ($i = 0; $i < $p_len; $i++) {
  1000. $this->_readBlock();
  1001. }
  1002. } else {
  1003. if ($this->_compress_type == 'none') {
  1004. @fseek($this->_file, $p_len * 512, SEEK_CUR);
  1005. } else {
  1006. $this->_error(
  1007. 'Unknown or missing compression type ('
  1008. . $this->_compress_type . ')'
  1009. );
  1010. }
  1011. }
  1012. }
  1013. }
  1014. }
  1015. return true;
  1016. }
  1017. /**
  1018. * @return bool
  1019. */
  1020. public function _writeFooter()
  1021. {
  1022. if (is_resource($this->_file)) {
  1023. // ----- Write the last 0 filled block for end of archive
  1024. $v_binary_data = pack('a1024', '');
  1025. $this->_writeBlock($v_binary_data);
  1026. }
  1027. return true;
  1028. }
  1029. /**
  1030. * @param array $p_list
  1031. * @param string $p_add_dir
  1032. * @param string $p_remove_dir
  1033. * @return bool
  1034. */
  1035. public function _addList($p_list, $p_add_dir, $p_remove_dir)
  1036. {
  1037. $v_result = true;
  1038. $v_header = array();
  1039. // ----- Remove potential windows directory separator
  1040. $p_add_dir = $this->_translateWinPath($p_add_dir);
  1041. $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  1042. if (!$this->_file) {
  1043. $this->_error('Invalid file descriptor');
  1044. return false;
  1045. }
  1046. if (sizeof($p_list) == 0) {
  1047. return true;
  1048. }
  1049. foreach ($p_list as $v_filename) {
  1050. if (!$v_result) {
  1051. break;
  1052. }
  1053. // ----- Skip the current tar name
  1054. if ($v_filename == $this->_tarname) {
  1055. continue;
  1056. }
  1057. if ($v_filename == '') {
  1058. continue;
  1059. }
  1060. // ----- ignore files and directories matching the ignore regular expression
  1061. if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) {
  1062. $this->_warning("File '$v_filename' ignored");
  1063. continue;
  1064. }
  1065. if (!file_exists($v_filename) && !is_link($v_filename)) {
  1066. $this->_warning("File '$v_filename' does not exist");
  1067. continue;
  1068. }
  1069. // ----- Add the file or directory header
  1070. if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) {
  1071. return false;
  1072. }
  1073. if (@is_dir($v_filename) && !@is_link($v_filename)) {
  1074. if (!($p_hdir = opendir($v_filename))) {
  1075. $this->_warning("Directory '$v_filename' can not be read");
  1076. continue;
  1077. }
  1078. while (false !== ($p_hitem = readdir($p_hdir))) {
  1079. if (($p_hitem != '.') && ($p_hitem != '..')) {
  1080. if ($v_filename != ".") {
  1081. $p_temp_list[0] = $v_filename . '/' . $p_hitem;
  1082. } else {
  1083. $p_temp_list[0] = $p_hitem;
  1084. }
  1085. $v_result = $this->_addList(
  1086. $p_temp_list,
  1087. $p_add_dir,
  1088. $p_remove_dir
  1089. );
  1090. }
  1091. }
  1092. unset($p_temp_list);
  1093. unset($p_hdir);
  1094. unset($p_hitem);
  1095. }
  1096. }
  1097. return $v_result;
  1098. }
  1099. /**
  1100. * @param string $p_filename
  1101. * @param mixed $p_header
  1102. * @param string $p_add_dir
  1103. * @param string $p_remove_dir
  1104. * @param null $v_stored_filename
  1105. * @return bool
  1106. */
  1107. public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null)
  1108. {
  1109. if (!$this->_file) {
  1110. $this->_error('Invalid file descriptor');
  1111. return false;
  1112. }
  1113. if ($p_filename == '') {
  1114. $this->_error('Invalid file name');
  1115. return false;
  1116. }
  1117. if (is_null($v_stored_filename)) {
  1118. // ----- Calculate the stored filename
  1119. $p_filename = $this->_translateWinPath($p_filename, false);
  1120. $v_stored_filename = $p_filename;
  1121. if (strcmp($p_filename, $p_remove_dir) == 0) {
  1122. return true;
  1123. }
  1124. if ($p_remove_dir != '') {
  1125. if (substr($p_remove_dir, -1) != '/') {
  1126. $p_remove_dir .= '/';
  1127. }
  1128. if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) {
  1129. $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  1130. }
  1131. }
  1132. $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  1133. if ($p_add_dir != '') {
  1134. if (substr($p_add_dir, -1) == '/') {
  1135. $v_stored_filename = $p_add_dir . $v_stored_filename;
  1136. } else {
  1137. $v_stored_filename = $p_add_dir . '/' . $v_stored_filename;
  1138. }
  1139. }
  1140. $v_stored_filename = $this->_pathReduction($v_stored_filename);
  1141. }
  1142. if ($this->_isArchive($p_filename)) {
  1143. if (($v_file = @fopen($p_filename, "rb")) == 0) {
  1144. $this->_warning(
  1145. "Unable to open file '" . $p_filename
  1146. . "' in binary read mode"
  1147. );
  1148. return true;
  1149. }
  1150. if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
  1151. return false;
  1152. }
  1153. while (($v_buffer = fread($v_file, $this->buffer_length)) != '') {
  1154. $buffer_length = strlen("$v_buffer");
  1155. if ($buffer_length != $this->buffer_length) {
  1156. $pack_size = ((int)($buffer_length / 512) + ($buffer_length % 512 !== 0 ? 1 : 0)) * 512;
  1157. $pack_format = sprintf('a%d', $pack_size);
  1158. } else {
  1159. $pack_format = sprintf('a%d', $this->buffer_length);
  1160. }
  1161. $v_binary_data = pack($pack_format, "$v_buffer");
  1162. $this->_writeBlock($v_binary_data);
  1163. }
  1164. fclose($v_file);
  1165. } else {
  1166. // ----- Only header for dir
  1167. if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
  1168. return false;
  1169. }
  1170. }
  1171. return true;
  1172. }
  1173. /**
  1174. * @param string $p_filename
  1175. * @param string $p_string
  1176. * @param bool $p_datetime
  1177. * @param array $p_params
  1178. * @return bool
  1179. */
  1180. public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
  1181. {
  1182. $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
  1183. $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
  1184. $p_type = @$p_params["type"] ? $p_params["type"] : "";
  1185. $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0;
  1186. $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0;
  1187. if (!$this->_file) {
  1188. $this->_error('Invalid file descriptor');
  1189. return false;
  1190. }
  1191. if ($p_filename == '') {
  1192. $this->_error('Invalid file name');
  1193. return false;
  1194. }
  1195. // ----- Calculate the stored filename
  1196. $p_filename = $this->_translateWinPath($p_filename, false);
  1197. // ----- If datetime is not specified, set current time
  1198. if ($p_datetime === false) {
  1199. $p_datetime = time();
  1200. }
  1201. if (!$this->_writeHeaderBlock(
  1202. $p_filename,
  1203. strlen($p_string),
  1204. $p_stamp,
  1205. $p_mode,
  1206. $p_type,
  1207. $p_uid,
  1208. $p_gid
  1209. )
  1210. ) {
  1211. return false;
  1212. }
  1213. $i = 0;
  1214. while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') {
  1215. $v_binary_data = pack("a512", $v_buffer);
  1216. $this->_writeBlock($v_binary_data);
  1217. }
  1218. return true;
  1219. }
  1220. /**
  1221. * @param string $p_filename
  1222. * @param string $p_stored_filename
  1223. * @return bool
  1224. */
  1225. public function _writeHeader($p_filename, $p_stored_filename)
  1226. {
  1227. if ($p_stored_filename == '') {
  1228. $p_stored_filename = $p_filename;
  1229. }
  1230. $v_reduced_filename = $this->_pathReduction($p_stored_filename);
  1231. if (strlen($v_reduced_filename) > 99) {
  1232. if (!$this->_writeLongHeader($v_reduced_filename, false)) {
  1233. return false;
  1234. }
  1235. }
  1236. $v_linkname = '';
  1237. if (@is_link($p_filename)) {
  1238. $v_linkname = readlink($p_filename);
  1239. }
  1240. if (strlen($v_linkname) > 99) {
  1241. if (!$this->_writeLongHeader($v_linkname, true)) {
  1242. return false;
  1243. }
  1244. }
  1245. $v_info = lstat($p_filename);
  1246. $v_uid = sprintf("%07s", DecOct($v_info[4]));
  1247. $v_gid = sprintf("%07s", DecOct($v_info[5]));
  1248. $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
  1249. $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
  1250. if (@is_link($p_filename)) {
  1251. $v_typeflag = '2';
  1252. $v_size = sprintf("%011s", DecOct(0));
  1253. } elseif (@is_dir($p_filename)) {
  1254. $v_typeflag = "5";
  1255. $v_size = sprintf("%011s", DecOct(0));
  1256. } else {
  1257. $v_typeflag = '0';
  1258. clearstatcache();
  1259. $v_size = sprintf("%011s", DecOct($v_info['size']));
  1260. }
  1261. $v_magic = 'ustar ';
  1262. $v_version = ' ';
  1263. $v_uname = '';
  1264. $v_gname = '';
  1265. if (function_exists('posix_getpwuid')) {
  1266. $userinfo = posix_getpwuid($v_info[4]);
  1267. $groupinfo = posix_getgrgid($v_info[5]);
  1268. if (isset($userinfo['name'])) {
  1269. $v_uname = $userinfo['name'];
  1270. }
  1271. if (isset($groupinfo['name'])) {
  1272. $v_gname = $groupinfo['name'];
  1273. }
  1274. }
  1275. $v_devmajor = '';
  1276. $v_devminor = '';
  1277. $v_prefix = '';
  1278. $v_binary_data_first = pack(
  1279. "a100a8a8a8a12a12",
  1280. $v_reduced_filename,
  1281. $v_perms,
  1282. $v_uid,
  1283. $v_gid,
  1284. $v_size,
  1285. $v_mtime
  1286. );
  1287. $v_binary_data_last = pack(
  1288. "a1a100a6a2a32a32a8a8a155a12",
  1289. $v_typeflag,
  1290. $v_linkname,
  1291. $v_magic,
  1292. $v_version,
  1293. $v_uname,
  1294. $v_gname,
  1295. $v_devmajor,
  1296. $v_devminor,
  1297. $v_prefix,
  1298. ''
  1299. );
  1300. // ----- Calculate the checksum
  1301. $v_checksum = 0;
  1302. // ..... First part of the header
  1303. for ($i = 0; $i < 148; $i++) {
  1304. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  1305. }
  1306. // ..... Ignore the checksum value and replace it by ' ' (space)
  1307. for ($i = 148; $i < 156; $i++) {
  1308. $v_checksum += ord(' ');
  1309. }
  1310. // ..... Last part of the header
  1311. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  1312. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  1313. }
  1314. // ----- Write the first 148 bytes of the header in the archive
  1315. $this->_writeBlock($v_binary_data_first, 148);
  1316. // ----- Write the calculated checksum
  1317. $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
  1318. $v_binary_data = pack("a8", $v_checksum);
  1319. $this->_writeBlock($v_binary_data, 8);
  1320. // ----- Write the last 356 bytes of the header in the archive
  1321. $this->_writeBlock($v_binary_data_last, 356);
  1322. return true;
  1323. }
  1324. /**
  1325. * @param string $p_filename
  1326. * @param int $p_size
  1327. * @param int $p_mtime
  1328. * @param int $p_perms
  1329. * @param string $p_type
  1330. * @param int $p_uid
  1331. * @param int $p_gid
  1332. * @return bool
  1333. */
  1334. public function _writeHeaderBlock(
  1335. $p_filename,
  1336. $p_size,
  1337. $p_mtime = 0,
  1338. $p_perms = 0,
  1339. $p_type = '',
  1340. $p_uid = 0,
  1341. $p_gid = 0
  1342. )
  1343. {
  1344. $p_filename = $this->_pathReduction($p_filename);
  1345. if (strlen($p_filename) > 99) {
  1346. if (!$this->_writeLongHeader($p_filename, false)) {
  1347. return false;
  1348. }
  1349. }
  1350. if ($p_type == "5") {
  1351. $v_size = sprintf("%011s", DecOct(0));
  1352. } else {
  1353. $v_size = sprintf("%011s", DecOct($p_size));
  1354. }
  1355. $v_uid = sprintf("%07s", DecOct($p_uid));
  1356. $v_gid = sprintf("%07s", DecOct($p_gid));
  1357. $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
  1358. $v_mtime = sprintf("%11s", DecOct($p_mtime));
  1359. $v_linkname = '';
  1360. $v_magic = 'ustar ';
  1361. $v_version = ' ';
  1362. if (function_exists('posix_getpwuid')) {
  1363. $userinfo = posix_getpwuid($p_uid);
  1364. $groupinfo = posix_getgrgid($p_gid);
  1365. if ($userinfo === false || $groupinfo === false) {
  1366. $v_uname = '';
  1367. $v_gname = '';
  1368. } else {
  1369. $v_uname = $userinfo['name'];
  1370. $v_gname = $groupinfo['name'];
  1371. }
  1372. } else {
  1373. $v_uname = '';
  1374. $v_gname = '';
  1375. }
  1376. $v_devmajor = '';
  1377. $v_devminor = '';
  1378. $v_prefix = '';
  1379. $v_binary_data_first = pack(
  1380. "a100a8a8a8a12A12",
  1381. $p_filename,
  1382. $v_perms,
  1383. $v_uid,
  1384. $v_gid,
  1385. $v_size,
  1386. $v_mtime
  1387. );
  1388. $v_binary_data_last = pack(
  1389. "a1a100a6a2a32a32a8a8a155a12",
  1390. $p_type,
  1391. $v_linkname,
  1392. $v_magic,
  1393. $v_version,
  1394. $v_uname,
  1395. $v_gname,
  1396. $v_devmajor,
  1397. $v_devminor,
  1398. $v_prefix,
  1399. ''
  1400. );
  1401. // ----- Calculate the checksum
  1402. $v_checksum = 0;
  1403. // ..... First part of the header
  1404. for ($i = 0; $i < 148; $i++) {
  1405. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  1406. }
  1407. // ..... Ignore the checksum value and replace it by ' ' (space)
  1408. for ($i = 148; $i < 156; $i++) {
  1409. $v_checksum += ord(' ');
  1410. }
  1411. // ..... Last part of the header
  1412. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  1413. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  1414. }
  1415. // ----- Write the first 148 bytes of the header in the archive
  1416. $this->_writeBlock($v_binary_data_first, 148);
  1417. // ----- Write the calculated checksum
  1418. $v_checksum = sprintf("%06s ", DecOct($v_checksum));
  1419. $v_binary_data = pack("a8", $v_checksum);
  1420. $this->_writeBlock($v_binary_data, 8);
  1421. // ----- Write the last 356 bytes of the header in the archive
  1422. $this->_writeBlock($v_binary_data_last, 356);
  1423. return true;
  1424. }
  1425. /**
  1426. * @param string $p_filename
  1427. * @return bool
  1428. */
  1429. public function _writeLongHeader($p_filename, $is_link = false)
  1430. {
  1431. $v_uid = sprintf("%07s", 0);
  1432. $v_gid = sprintf("%07s", 0);
  1433. $v_perms = sprintf("%07s", 0);
  1434. $v_size = sprintf("%'011s", DecOct(strlen($p_filename)));
  1435. $v_mtime = sprintf("%011s", 0);
  1436. $v_typeflag = ($is_link ? 'K' : 'L');
  1437. $v_linkname = '';
  1438. $v_magic = 'ustar ';
  1439. $v_version = ' ';
  1440. $v_uname = '';
  1441. $v_gname = '';
  1442. $v_devmajor = '';
  1443. $v_devminor = '';
  1444. $v_prefix = '';
  1445. $v_binary_data_first = pack(
  1446. "a100a8a8a8a12a12",
  1447. '././@LongLink',
  1448. $v_perms,
  1449. $v_uid,
  1450. $v_gid,
  1451. $v_size,
  1452. $v_mtime
  1453. );
  1454. $v_binary_data_last = pack(
  1455. "a1a100a6a2a32a32a8a8a155a12",
  1456. $v_typeflag,
  1457. $v_linkname,
  1458. $v_magic,
  1459. $v_version,
  1460. $v_uname,
  1461. $v_gname,
  1462. $v_devmajor,
  1463. $v_devminor,
  1464. $v_prefix,
  1465. ''
  1466. );
  1467. // ----- Calculate the checksum
  1468. $v_checksum = 0;
  1469. // ..... First part of the header
  1470. for ($i = 0; $i < 148; $i++) {
  1471. $v_checksum += ord(substr($v_binary_data_first, $i, 1));
  1472. }
  1473. // ..... Ignore the checksum value and replace it by ' ' (space)
  1474. for ($i = 148; $i < 156; $i++) {
  1475. $v_checksum += ord(' ');
  1476. }
  1477. // ..... Last part of the header
  1478. for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
  1479. $v_checksum += ord(substr($v_binary_data_last, $j, 1));
  1480. }
  1481. // ----- Write the first 148 bytes of the header in the archive
  1482. $this->_writeBlock($v_binary_data_first, 148);
  1483. // ----- Write the calculated checksum
  1484. $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum));
  1485. $v_binary_data = pack("a8", $v_checksum);
  1486. $this->_writeBlock($v_binary_data, 8);
  1487. // ----- Write the last 356 bytes of the header in the archive
  1488. $this->_writeBlock($v_binary_data_last, 356);
  1489. // ----- Write the filename as content of the block
  1490. $i = 0;
  1491. while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') {
  1492. $v_binary_data = pack("a512", "$v_buffer");
  1493. $this->_writeBlock($v_binary_data);
  1494. }
  1495. return true;
  1496. }
  1497. /**
  1498. * @param mixed $v_binary_data
  1499. * @param mixed $v_header
  1500. * @return bool
  1501. */
  1502. public function _readHeader($v_binary_data, &$v_header)
  1503. {
  1504. if (strlen($v_binary_data) == 0) {
  1505. $v_header['filename'] = '';
  1506. return true;
  1507. }
  1508. if (strlen($v_binary_data) != 512) {
  1509. $v_header['filename'] = '';
  1510. $this->_error('Invalid block size : ' . strlen($v_binary_data));
  1511. return false;
  1512. }
  1513. if (!is_array($v_header)) {
  1514. $v_header = array();
  1515. }
  1516. // ----- Calculate the checksum
  1517. $v_checksum = 0;
  1518. // ..... First part of the header
  1519. $v_binary_split = str_split($v_binary_data);
  1520. $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148)));
  1521. $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',)));
  1522. $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512)));
  1523. $v_data = unpack($this->_fmt, $v_binary_data);
  1524. if (strlen($v_data["prefix"]) > 0) {
  1525. $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
  1526. }
  1527. // ----- Extract the checksum
  1528. $v_data_checksum = trim($v_data['checksum']);
  1529. if (!preg_match('/^[0-7]*$/', $v_data_checksum)) {
  1530. $this->_error(
  1531. 'Invalid checksum for file "' . $v_data['filename']
  1532. . '" : ' . $v_data_checksum . ' extracted'
  1533. );
  1534. return false;
  1535. }
  1536. $v_header['checksum'] = OctDec($v_data_checksum);
  1537. if ($v_header['checksum'] != $v_checksum) {
  1538. $v_header['filename'] = '';
  1539. // ----- Look for last block (empty block)
  1540. if (($v_checksum == 256) && ($v_header['checksum'] == 0)) {
  1541. return true;
  1542. }
  1543. $this->_error(
  1544. 'Invalid checksum for file "' . $v_data['filename']
  1545. . '" : ' . $v_checksum . ' calculated, '
  1546. . $v_header['checksum'] . ' expected'
  1547. );
  1548. return false;
  1549. }
  1550. // ----- Extract the properties
  1551. $v_header['filename'] = rtrim($v_data['filename'], "\0");
  1552. if ($this->_isMaliciousFilename($v_header['filename'])) {
  1553. $this->_error(
  1554. 'Malicious .tar detected, file "' . $v_header['filename'] .
  1555. '" will not install in desired directory tree'
  1556. );
  1557. return false;
  1558. }
  1559. $v_header['mode'] = OctDec(trim($v_data['mode']));
  1560. $v_header['uid'] = OctDec(trim($v_data['uid']));
  1561. $v_header['gid'] = OctDec(trim($v_data['gid']));
  1562. $v_header['size'] = $this->_tarRecToSize($v_data['size']);
  1563. $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  1564. if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  1565. $v_header['size'] = 0;
  1566. }
  1567. $v_header['link'] = trim($v_data['link']);
  1568. /* ----- All these fields are removed form the header because
  1569. they do not carry interesting info
  1570. $v_header[magic] = trim($v_data[magic]);
  1571. $v_header[version] = trim($v_data[version]);
  1572. $v_header[uname] = trim($v_data[uname]);
  1573. $v_header[gname] = trim($v_data[gname]);
  1574. $v_header[devmajor] = trim($v_data[devmajor]);
  1575. $v_header[devminor] = trim($v_data[devminor]);
  1576. */
  1577. return true;
  1578. }
  1579. /**
  1580. * Convert Tar record size to actual size
  1581. *
  1582. * @param string $tar_size
  1583. * @return size of tar record in bytes
  1584. */
  1585. private function _tarRecToSize($tar_size)
  1586. {
  1587. /*
  1588. * First byte of size has a special meaning if bit 7 is set.
  1589. *
  1590. * Bit 7 indicates base-256 encoding if set.
  1591. * Bit 6 is the sign bit.
  1592. * Bits 5:0 are most significant value bits.
  1593. */
  1594. $ch = ord($tar_size[0]);
  1595. if ($ch & 0x80) {
  1596. // Full 12-bytes record is required.
  1597. $rec_str = $tar_size . "\x00";
  1598. $size = ($ch & 0x40) ? -1 : 0;
  1599. $size = ($size << 6) | ($ch & 0x3f);
  1600. for ($num_ch = 1; $num_ch < 12; ++$num_ch) {
  1601. $size = ($size * 256) + ord($rec_str[$num_ch]);
  1602. }
  1603. return $size;
  1604. } else {
  1605. return OctDec(trim($tar_size));
  1606. }
  1607. }
  1608. /**
  1609. * Detect and report a malicious file name
  1610. *
  1611. * @param string $file
  1612. *
  1613. * @return bool
  1614. */
  1615. private function _isMaliciousFilename($file)
  1616. {
  1617. if (strpos($file, '://') !== false) {
  1618. return true;
  1619. }
  1620. if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) {
  1621. return true;
  1622. }
  1623. return false;
  1624. }
  1625. /**
  1626. * @param $v_header
  1627. * @return bool
  1628. */
  1629. public function _readLongHeader(&$v_header)
  1630. {
  1631. $v_filename = '';
  1632. $v_filesize = $v_header['size'];
  1633. $n = floor($v_header['size'] / 512);
  1634. for ($i = 0; $i < $n; $i++) {
  1635. $v_content = $this->_readBlock();
  1636. $v_filename .= $v_content;
  1637. }
  1638. if (($v_header['size'] % 512) != 0) {
  1639. $v_content = $this->_readBlock();
  1640. $v_filename .= $v_content;
  1641. }
  1642. // ----- Read the next header
  1643. $v_binary_data = $this->_readBlock();
  1644. if (!$this->_readHeader($v_binary_data, $v_header)) {
  1645. return false;
  1646. }
  1647. $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0");
  1648. $v_header['filename'] = $v_filename;
  1649. if ($this->_isMaliciousFilename($v_filename)) {
  1650. $this->_error(
  1651. 'Malicious .tar detected, file "' . $v_filename .
  1652. '" will not install in desired directory tree'
  1653. );
  1654. return false;
  1655. }
  1656. return true;
  1657. }
  1658. /**
  1659. * This method extract from the archive one file identified by $p_filename.
  1660. * The return value is a string with the file content, or null on error.
  1661. *
  1662. * @param string $p_filename The path of the file to extract in a string.
  1663. *
  1664. * @return a string with the file content or null.
  1665. */
  1666. private function _extractInString($p_filename)
  1667. {
  1668. $v_result_str = "";
  1669. while (strlen($v_binary_data = $this->_readBlock()) != 0) {
  1670. if (!$this->_readHeader($v_binary_data, $v_header)) {
  1671. return null;
  1672. }
  1673. if ($v_header['filename'] == '') {
  1674. continue;
  1675. }
  1676. switch ($v_header['typeflag']) {
  1677. case 'L':
  1678. {
  1679. if (!$this->_readLongHeader($v_header)) {
  1680. return null;
  1681. }
  1682. }
  1683. break;
  1684. case 'K':
  1685. {
  1686. $v_link_header = $v_header;
  1687. if (!$this->_readLongHeader($v_link_header)) {
  1688. return null;
  1689. }
  1690. $v_header['link'] = $v_link_header['filename'];
  1691. }
  1692. break;
  1693. }
  1694. if ($v_header['filename'] == $p_filename) {
  1695. if ($v_header['typeflag'] == "5") {
  1696. $this->_error(
  1697. 'Unable to extract in string a directory '
  1698. . 'entry {' . $v_header['filename'] . '}'
  1699. );
  1700. return null;
  1701. } else {
  1702. $n = floor($v_header['size'] / 512);
  1703. for ($i = 0; $i < $n; $i++) {
  1704. $v_result_str .= $this->_readBlock();
  1705. }
  1706. if (($v_header['size'] % 512) != 0) {
  1707. $v_content = $this->_readBlock();
  1708. $v_result_str .= substr(
  1709. $v_content,
  1710. 0,
  1711. ($v_header['size'] % 512)
  1712. );
  1713. }
  1714. return $v_result_str;
  1715. }
  1716. } else {
  1717. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  1718. }
  1719. }
  1720. return null;
  1721. }
  1722. /**
  1723. * @param string $p_path
  1724. * @param string $p_list_detail
  1725. * @param string $p_mode
  1726. * @param string $p_file_list
  1727. * @param string $p_remove_path
  1728. * @param bool $p_preserve
  1729. * @param bool $p_symlinks
  1730. * @return bool
  1731. */
  1732. public function _extractList(
  1733. $p_path,
  1734. &$p_list_detail,
  1735. $p_mode,
  1736. $p_file_list,
  1737. $p_remove_path,
  1738. $p_preserve = false,
  1739. $p_symlinks = true
  1740. )
  1741. {
  1742. $v_result = true;
  1743. $v_nb = 0;
  1744. $v_extract_all = true;
  1745. $v_listing = false;
  1746. $p_path = $this->_translateWinPath($p_path, false);
  1747. if ($p_path == '' || (substr($p_path, 0, 1) != '/'
  1748. && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))
  1749. ) {
  1750. $p_path = "./" . $p_path;
  1751. }
  1752. $p_remove_path = $this->_translateWinPath($p_remove_path);
  1753. // ----- Look for path to remove format (should end by /)
  1754. if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) {
  1755. $p_remove_path .= '/';
  1756. }
  1757. $p_remove_path_size = strlen($p_remove_path);
  1758. switch ($p_mode) {
  1759. case "complete" :
  1760. $v_extract_all = true;
  1761. $v_listing = false;
  1762. break;
  1763. case "partial" :
  1764. $v_extract_all = false;
  1765. $v_listing = false;
  1766. break;
  1767. case "list" :
  1768. $v_extract_all = false;
  1769. $v_listing = true;
  1770. break;
  1771. default :
  1772. $this->_error('Invalid extract mode (' . $p_mode . ')');
  1773. return false;
  1774. }
  1775. clearstatcache();
  1776. while (strlen($v_binary_data = $this->_readBlock()) != 0) {
  1777. $v_extract_file = false;
  1778. $v_extraction_stopped = 0;
  1779. if (!$this->_readHeader($v_binary_data, $v_header)) {
  1780. return false;
  1781. }
  1782. if ($v_header['filename'] == '') {
  1783. continue;
  1784. }
  1785. switch ($v_header['typeflag']) {
  1786. case 'L':
  1787. {
  1788. if (!$this->_readLongHeader($v_header)) {
  1789. return null;
  1790. }
  1791. }
  1792. break;
  1793. case 'K':
  1794. {
  1795. $v_link_header = $v_header;
  1796. if (!$this->_readLongHeader($v_link_header)) {
  1797. return null;
  1798. }
  1799. $v_header['link'] = $v_link_header['filename'];
  1800. }
  1801. break;
  1802. }
  1803. // ignore extended / pax headers
  1804. if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') {
  1805. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  1806. continue;
  1807. }
  1808. if ((!$v_extract_all) && (is_array($p_file_list))) {
  1809. // ----- By default no unzip if the file is not found
  1810. $v_extract_file = false;
  1811. for ($i = 0; $i < sizeof($p_file_list); $i++) {
  1812. // ----- Look if it is a directory
  1813. if (substr($p_file_list[$i], -1) == '/') {
  1814. // ----- Look if the directory is in the filename path
  1815. if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
  1816. && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
  1817. == $p_file_list[$i])
  1818. ) {
  1819. $v_extract_file = true;
  1820. break;
  1821. }
  1822. } // ----- It is a file, so compare the file names
  1823. elseif ($p_file_list[$i] == $v_header['filename']) {
  1824. $v_extract_file = true;
  1825. break;
  1826. }
  1827. }
  1828. } else {
  1829. $v_extract_file = true;
  1830. }
  1831. // ----- Look if this file need to be extracted
  1832. if (($v_extract_file) && (!$v_listing)) {
  1833. if (($p_remove_path != '')
  1834. && (substr($v_header['filename'] . '/', 0, $p_remove_path_size)
  1835. == $p_remove_path)
  1836. ) {
  1837. $v_header['filename'] = substr(
  1838. $v_header['filename'],
  1839. $p_remove_path_size
  1840. );
  1841. if ($v_header['filename'] == '') {
  1842. continue;
  1843. }
  1844. }
  1845. if (($p_path != './') && ($p_path != '/')) {
  1846. while (substr($p_path, -1) == '/') {
  1847. $p_path = substr($p_path, 0, strlen($p_path) - 1);
  1848. }
  1849. if (substr($v_header['filename'], 0, 1) == '/') {
  1850. $v_header['filename'] = $p_path . $v_header['filename'];
  1851. } else {
  1852. $v_header['filename'] = $p_path . '/' . $v_header['filename'];
  1853. }
  1854. }
  1855. if (file_exists($v_header['filename'])) {
  1856. if ((@is_dir($v_header['filename']))
  1857. && ($v_header['typeflag'] == '')
  1858. ) {
  1859. $this->_error(
  1860. 'File ' . $v_header['filename']
  1861. . ' already exists as a directory'
  1862. );
  1863. return false;
  1864. }
  1865. if (($this->_isArchive($v_header['filename']))
  1866. && ($v_header['typeflag'] == "5")
  1867. ) {
  1868. $this->_error(
  1869. 'Directory ' . $v_header['filename']
  1870. . ' already exists as a file'
  1871. );
  1872. return false;
  1873. }
  1874. if (!is_writeable($v_header['filename'])) {
  1875. $this->_error(
  1876. 'File ' . $v_header['filename']
  1877. . ' already exists and is write protected'
  1878. );
  1879. return false;
  1880. }
  1881. if (filemtime($v_header['filename']) > $v_header['mtime']) {
  1882. // To be completed : An error or silent no replace ?
  1883. }
  1884. } // ----- Check the directory availability and create it if necessary
  1885. elseif (($v_result
  1886. = $this->_dirCheck(
  1887. ($v_header['typeflag'] == "5"
  1888. ? $v_header['filename']
  1889. : dirname($v_header['filename']))
  1890. )) != 1
  1891. ) {
  1892. $this->_error('Unable to create path for ' . $v_header['filename']);
  1893. return false;
  1894. }
  1895. if ($v_extract_file) {
  1896. if ($v_header['typeflag'] == "5") {
  1897. if (!@file_exists($v_header['filename'])) {
  1898. if (!@mkdir($v_header['filename'], 0777)) {
  1899. $this->_error(
  1900. 'Unable to create directory {'
  1901. . $v_header['filename'] . '}'
  1902. );
  1903. return false;
  1904. }
  1905. }
  1906. } elseif ($v_header['typeflag'] == "2") {
  1907. if (!$p_symlinks) {
  1908. $this->_warning('Symbolic links are not allowed. '
  1909. . 'Unable to extract {'
  1910. . $v_header['filename'] . '}'
  1911. );
  1912. return false;
  1913. }
  1914. $absolute_link = FALSE;
  1915. $link_depth = 0;
  1916. if (strpos($v_header['link'], "/") === 0 || strpos($v_header['link'], ':') !== FALSE) {
  1917. $absolute_link = TRUE;
  1918. }
  1919. else {
  1920. $s_filename = preg_replace('@^' . preg_quote($p_path) . '@', "", $v_header['filename']);
  1921. $s_linkname = str_replace('\\', '/', $v_header['link']);
  1922. foreach (explode("/", $s_filename) as $dir) {
  1923. if ($dir === "..") {
  1924. $link_depth--;
  1925. } elseif ($dir !== "" && $dir !== "." ) {
  1926. $link_depth++;
  1927. }
  1928. }
  1929. foreach (explode("/", $s_linkname) as $dir){
  1930. if ($link_depth <= 0) {
  1931. break;
  1932. }
  1933. if ($dir === "..") {
  1934. $link_depth--;
  1935. } elseif ($dir !== "" && $dir !== ".") {
  1936. $link_depth++;
  1937. }
  1938. }
  1939. }
  1940. if ($absolute_link || $link_depth <= 0) {
  1941. $this->_error(
  1942. 'Out-of-path file extraction {'
  1943. . $v_header['filename'] . ' --> ' .
  1944. $v_header['link'] . '}'
  1945. );
  1946. return false;
  1947. }
  1948. if (@file_exists($v_header['filename'])) {
  1949. @unlink($v_header['filename']);
  1950. }
  1951. if (!@symlink($v_header['link'], $v_header['filename'])) {
  1952. $this->_error(
  1953. 'Unable to extract symbolic link {'
  1954. . $v_header['filename'] . '}'
  1955. );
  1956. return false;
  1957. }
  1958. } else {
  1959. if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  1960. $this->_error(
  1961. 'Error while opening {' . $v_header['filename']
  1962. . '} in write binary mode'
  1963. );
  1964. return false;
  1965. } else {
  1966. $n = floor($v_header['size'] / 512);
  1967. for ($i = 0; $i < $n; $i++) {
  1968. $v_content = $this->_readBlock();
  1969. fwrite($v_dest_file, $v_content, 512);
  1970. }
  1971. if (($v_header['size'] % 512) != 0) {
  1972. $v_content = $this->_readBlock();
  1973. fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  1974. }
  1975. @fclose($v_dest_file);
  1976. if ($p_preserve) {
  1977. @chown($v_header['filename'], $v_header['uid']);
  1978. @chgrp($v_header['filename'], $v_header['gid']);
  1979. }
  1980. // ----- Change the file mode, mtime
  1981. @touch($v_header['filename'], $v_header['mtime']);
  1982. if ($v_header['mode'] & 0111) {
  1983. // make file executable, obey umask
  1984. $mode = fileperms($v_header['filename']) | (~umask() & 0111);
  1985. @chmod($v_header['filename'], $mode);
  1986. }
  1987. }
  1988. // ----- Check the file size
  1989. clearstatcache();
  1990. if (!is_file($v_header['filename'])) {
  1991. $this->_error(
  1992. 'Extracted file ' . $v_header['filename']
  1993. . 'does not exist. Archive may be corrupted.'
  1994. );
  1995. return false;
  1996. }
  1997. $filesize = filesize($v_header['filename']);
  1998. if ($filesize != $v_header['size']) {
  1999. $this->_error(
  2000. 'Extracted file ' . $v_header['filename']
  2001. . ' does not have the correct file size \''
  2002. . $filesize
  2003. . '\' (' . $v_header['size']
  2004. . ' expected). Archive may be corrupted.'
  2005. );
  2006. return false;
  2007. }
  2008. }
  2009. } else {
  2010. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  2011. }
  2012. } else {
  2013. $this->_jumpBlock(ceil(($v_header['size'] / 512)));
  2014. }
  2015. /* TBC : Seems to be unused ...
  2016. if ($this->_compress)
  2017. $v_end_of_file = @gzeof($this->_file);
  2018. else
  2019. $v_end_of_file = @feof($this->_file);
  2020. */
  2021. if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  2022. // ----- Log extracted files
  2023. if (($v_file_dir = dirname($v_header['filename']))
  2024. == $v_header['filename']
  2025. ) {
  2026. $v_file_dir = '';
  2027. }
  2028. if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) {
  2029. $v_file_dir = '/';
  2030. }
  2031. $p_list_detail[$v_nb++] = $v_header;
  2032. if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
  2033. return true;
  2034. }
  2035. }
  2036. }
  2037. return true;
  2038. }
  2039. /**
  2040. * @return bool
  2041. */
  2042. public function _openAppend()
  2043. {
  2044. if (filesize($this->_tarname) == 0) {
  2045. return $this->_openWrite();
  2046. }
  2047. if ($this->_compress) {
  2048. $this->_close();
  2049. if (!@rename($this->_tarname, $this->_tarname . ".tmp")) {
  2050. $this->_error(
  2051. 'Error while renaming \'' . $this->_tarname
  2052. . '\' to temporary file \'' . $this->_tarname
  2053. . '.tmp\''
  2054. );
  2055. return false;
  2056. }
  2057. if ($this->_compress_type == 'gz') {
  2058. $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb");
  2059. } elseif ($this->_compress_type == 'bz2') {
  2060. $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r");
  2061. } elseif ($this->_compress_type == 'lzma2') {
  2062. $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r");
  2063. }
  2064. if ($v_temp_tar == 0) {
  2065. $this->_error(
  2066. 'Unable to open file \'' . $this->_tarname
  2067. . '.tmp\' in binary read mode'
  2068. );
  2069. @rename($this->_tarname . ".tmp", $this->_tarname);
  2070. return false;
  2071. }
  2072. if (!$this->_openWrite()) {
  2073. @rename($this->_tarname . ".tmp", $this->_tarname);
  2074. return false;
  2075. }
  2076. if ($this->_compress_type == 'gz') {
  2077. $end_blocks = 0;
  2078. while (!@gzeof($v_temp_tar)) {
  2079. $v_buffer = @gzread($v_temp_tar, 512);
  2080. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  2081. $end_blocks++;
  2082. // do not copy end blocks, we will re-make them
  2083. // after appending
  2084. continue;
  2085. } elseif ($end_blocks > 0) {
  2086. for ($i = 0; $i < $end_blocks; $i++) {
  2087. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  2088. }
  2089. $end_blocks = 0;
  2090. }
  2091. $v_binary_data = pack("a512", $v_buffer);
  2092. $this->_writeBlock($v_binary_data);
  2093. }
  2094. @gzclose($v_temp_tar);
  2095. } elseif ($this->_compress_type == 'bz2') {
  2096. $end_blocks = 0;
  2097. while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
  2098. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  2099. $end_blocks++;
  2100. // do not copy end blocks, we will re-make them
  2101. // after appending
  2102. continue;
  2103. } elseif ($end_blocks > 0) {
  2104. for ($i = 0; $i < $end_blocks; $i++) {
  2105. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  2106. }
  2107. $end_blocks = 0;
  2108. }
  2109. $v_binary_data = pack("a512", $v_buffer);
  2110. $this->_writeBlock($v_binary_data);
  2111. }
  2112. @bzclose($v_temp_tar);
  2113. } elseif ($this->_compress_type == 'lzma2') {
  2114. $end_blocks = 0;
  2115. while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) {
  2116. if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
  2117. $end_blocks++;
  2118. // do not copy end blocks, we will re-make them
  2119. // after appending
  2120. continue;
  2121. } elseif ($end_blocks > 0) {
  2122. for ($i = 0; $i < $end_blocks; $i++) {
  2123. $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
  2124. }
  2125. $end_blocks = 0;
  2126. }
  2127. $v_binary_data = pack("a512", $v_buffer);
  2128. $this->_writeBlock($v_binary_data);
  2129. }
  2130. @xzclose($v_temp_tar);
  2131. }
  2132. if (!@unlink($this->_tarname . ".tmp")) {
  2133. $this->_error(
  2134. 'Error while deleting temporary file \''
  2135. . $this->_tarname . '.tmp\''
  2136. );
  2137. }
  2138. } else {
  2139. // ----- For not compressed tar, just add files before the last
  2140. // one or two 512 bytes block
  2141. if (!$this->_openReadWrite()) {
  2142. return false;
  2143. }
  2144. clearstatcache();
  2145. $v_size = filesize($this->_tarname);
  2146. // We might have zero, one or two end blocks.
  2147. // The standard is two, but we should try to handle
  2148. // other cases.
  2149. fseek($this->_file, $v_size - 1024);
  2150. if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  2151. fseek($this->_file, $v_size - 1024);
  2152. } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  2153. fseek($this->_file, $v_size - 512);
  2154. }
  2155. }
  2156. return true;
  2157. }
  2158. /**
  2159. * @param $p_filelist
  2160. * @param string $p_add_dir
  2161. * @param string $p_remove_dir
  2162. * @return bool
  2163. */
  2164. public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '')
  2165. {
  2166. if (!$this->_openAppend()) {
  2167. return false;
  2168. }
  2169. if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) {
  2170. $this->_writeFooter();
  2171. }
  2172. $this->_close();
  2173. return true;
  2174. }
  2175. /**
  2176. * Check if a directory exists and create it (including parent
  2177. * dirs) if not.
  2178. *
  2179. * @param string $p_dir directory to check
  2180. *
  2181. * @return bool true if the directory exists or was created
  2182. */
  2183. public function _dirCheck($p_dir)
  2184. {
  2185. clearstatcache();
  2186. if ((@is_dir($p_dir)) || ($p_dir == '')) {
  2187. return true;
  2188. }
  2189. $p_parent_dir = dirname($p_dir);
  2190. if (($p_parent_dir != $p_dir) &&
  2191. ($p_parent_dir != '') &&
  2192. (!$this->_dirCheck($p_parent_dir))
  2193. ) {
  2194. return false;
  2195. }
  2196. if (!@mkdir($p_dir, 0777)) {
  2197. $this->_error("Unable to create directory '$p_dir'");
  2198. return false;
  2199. }
  2200. return true;
  2201. }
  2202. /**
  2203. * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
  2204. * rand emove double slashes.
  2205. *
  2206. * @param string $p_dir path to reduce
  2207. *
  2208. * @return string reduced path
  2209. */
  2210. private function _pathReduction($p_dir)
  2211. {
  2212. $v_result = '';
  2213. // ----- Look for not empty path
  2214. if ($p_dir != '') {
  2215. // ----- Explode path by directory names
  2216. $v_list = explode('/', $p_dir);
  2217. // ----- Study directories from last to first
  2218. for ($i = sizeof($v_list) - 1; $i >= 0; $i--) {
  2219. // ----- Look for current path
  2220. if ($v_list[$i] == ".") {
  2221. // ----- Ignore this directory
  2222. // Should be the first $i=0, but no check is done
  2223. } else {
  2224. if ($v_list[$i] == "..") {
  2225. // ----- Ignore it and ignore the $i-1
  2226. $i--;
  2227. } else {
  2228. if (($v_list[$i] == '')
  2229. && ($i != (sizeof($v_list) - 1))
  2230. && ($i != 0)
  2231. ) {
  2232. // ----- Ignore only the double '//' in path,
  2233. // but not the first and last /
  2234. } else {
  2235. $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/'
  2236. . $v_result : '');
  2237. }
  2238. }
  2239. }
  2240. }
  2241. }
  2242. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  2243. $v_result = strtr($v_result, '\\', '/');
  2244. }
  2245. return $v_result;
  2246. }
  2247. /**
  2248. * @param $p_path
  2249. * @param bool $p_remove_disk_letter
  2250. * @return string
  2251. */
  2252. public function _translateWinPath($p_path, $p_remove_disk_letter = true)
  2253. {
  2254. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  2255. // ----- Look for potential disk letter
  2256. if (($p_remove_disk_letter)
  2257. && (($v_position = strpos($p_path, ':')) != false)
  2258. ) {
  2259. $p_path = substr($p_path, $v_position + 1);
  2260. }
  2261. // ----- Change potential windows directory separator
  2262. if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) {
  2263. $p_path = strtr($p_path, '\\', '/');
  2264. }
  2265. }
  2266. return $p_path;
  2267. }
  2268. }