PageRenderTime 55ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/saf/lib/PEAR/PEAR/Installer.php

https://github.com/cbrunet/sitellite
PHP | 872 lines | 682 code | 57 blank | 133 comment | 102 complexity | 4a0cafc6a06f0c2b58aa08f8e0781109 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stig Bakken <ssb@fast.no> |
  17. // | Tomas V.V.Cox <cox@idecnet.com> |
  18. // +----------------------------------------------------------------------+
  19. //
  20. // $Id: Installer.php,v 1.1.1.1 2005/04/29 04:44:36 lux Exp $
  21. require_once 'PEAR/Common.php';
  22. require_once 'PEAR/Registry.php';
  23. require_once 'PEAR/Dependency.php';
  24. require_once 'System.php';
  25. define('PEAR_INSTALLER_OK', 1);
  26. define('PEAR_INSTALLER_FAILED', 0);
  27. define('PEAR_INSTALLER_SKIPPED', -1);
  28. /**
  29. * Administration class used to install PEAR packages and maintain the
  30. * installed package database.
  31. *
  32. * TODO:
  33. * - Check dependencies break on package uninstall (when no force given)
  34. * - add a guessInstallDest() method with the code from _installFile() and
  35. * use that method in Registry::_rebuildFileMap() & Command_Registry::doList(),
  36. * others..
  37. *
  38. * @since PHP 4.0.2
  39. * @author Stig Bakken <ssb@fast.no>
  40. */
  41. class PEAR_Installer extends PEAR_Common
  42. {
  43. // {{{ properties
  44. /** name of the package directory, for example Foo-1.0
  45. * @var string
  46. */
  47. var $pkgdir;
  48. /** directory where PHP code files go
  49. * @var string
  50. */
  51. var $phpdir;
  52. /** directory where PHP extension files go
  53. * @var string
  54. */
  55. var $extdir;
  56. /** directory where documentation goes
  57. * @var string
  58. */
  59. var $docdir;
  60. /** installation root directory (ala PHP's INSTALL_ROOT or
  61. * automake's DESTDIR
  62. * @var string
  63. */
  64. var $installroot = '';
  65. /** debug level
  66. * @var int
  67. */
  68. var $debug = 1;
  69. /** temporary directory
  70. * @var string
  71. */
  72. var $tmpdir;
  73. /** PEAR_Registry object used by the installer
  74. * @var object
  75. */
  76. var $registry;
  77. /** List of file transactions queued for an install/upgrade/uninstall.
  78. *
  79. * Format:
  80. * array(
  81. * 0 => array("rename => array("from-file", "to-file")),
  82. * 1 => array("delete" => array("file-to-delete")),
  83. * ...
  84. * )
  85. *
  86. * @var array
  87. */
  88. var $file_operations = array();
  89. // }}}
  90. // {{{ constructor
  91. /**
  92. * PEAR_Installer constructor.
  93. *
  94. * @param object $ui user interface object (instance of PEAR_Frontend_*)
  95. *
  96. * @access public
  97. */
  98. function PEAR_Installer(&$ui)
  99. {
  100. parent::PEAR_Common();
  101. $this->setFrontendObject($ui);
  102. $this->debug = $this->config->get('verbose');
  103. $this->registry = &new PEAR_Registry($this->config->get('php_dir'));
  104. }
  105. // }}}
  106. // {{{ _deletePackageFiles()
  107. /**
  108. * Delete a package's installed files, remove empty directories.
  109. *
  110. * @param string $package package name
  111. *
  112. * @return bool TRUE on success, or a PEAR error on failure
  113. *
  114. * @access private
  115. */
  116. function _deletePackageFiles($package)
  117. {
  118. if (!strlen($package)) {
  119. return $this->raiseError("No package to uninstall given");
  120. }
  121. $filelist = $this->registry->packageInfo($package, 'filelist');
  122. if ($filelist == null) {
  123. return $this->raiseError("$package not installed");
  124. }
  125. foreach ($filelist as $file => $props) {
  126. if (empty($props['installed_as'])) {
  127. continue;
  128. }
  129. $path = $this->_prependPath($props['installed_as'], $this->installroot);
  130. $this->addFileOperation('delete', array($path));
  131. }
  132. return true;
  133. }
  134. // }}}
  135. // {{{ _installFile()
  136. function _installFile($file, $atts, $tmp_path)
  137. {
  138. static $os;
  139. if (isset($atts['platform'])) {
  140. if (empty($os)) {
  141. include_once "OS/Guess.php";
  142. $os = new OS_Guess();
  143. }
  144. // return if this file is meant for another platform
  145. if (!$os->matchSignature($atts['platform'])) {
  146. $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
  147. return PEAR_INSTALLER_SKIPPED;
  148. }
  149. }
  150. switch ($atts['role']) {
  151. case 'doc':
  152. case 'data':
  153. case 'test':
  154. $dest_dir = $this->config->get($atts['role'] . '_dir') .
  155. DIRECTORY_SEPARATOR . $this->pkginfo['package'];
  156. unset($atts['baseinstalldir']);
  157. break;
  158. case 'ext':
  159. case 'php':
  160. $dest_dir = $this->config->get($atts['role'] . '_dir');
  161. break;
  162. case 'script':
  163. $dest_dir = $this->config->get('bin_dir');
  164. break;
  165. case 'src':
  166. case 'extsrc':
  167. $this->source_files++;
  168. return;
  169. default:
  170. return $this->raiseError("Invalid role `$atts[role]' for file $file");
  171. }
  172. if (!empty($atts['baseinstalldir'])) {
  173. $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  174. }
  175. if (dirname($file) != '.' && empty($atts['install-as'])) {
  176. $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
  177. }
  178. if (empty($atts['install-as'])) {
  179. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
  180. } else {
  181. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
  182. }
  183. $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
  184. // Clean up the DIRECTORY_SEPARATOR mess
  185. $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  186. list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
  187. DIRECTORY_SEPARATOR,
  188. array($dest_file, $orig_file));
  189. $installed_as = $dest_file;
  190. $final_dest_file = $this->_prependPath($dest_file, $this->installroot);
  191. $dest_dir = dirname($final_dest_file);
  192. $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
  193. if (!@is_dir($dest_dir)) {
  194. if (!$this->mkDirHier($dest_dir)) {
  195. return $this->raiseError("failed to mkdir $dest_dir",
  196. PEAR_INSTALLER_FAILED);
  197. }
  198. $this->log(3, "+ mkdir $dest_dir");
  199. }
  200. if (empty($atts['replacements'])) {
  201. if (!@copy($orig_file, $dest_file)) {
  202. return $this->raiseError("failed to write $dest_file",
  203. PEAR_INSTALLER_FAILED);
  204. }
  205. $this->log(3, "+ cp $orig_file $dest_file");
  206. if (isset($atts['md5sum'])) {
  207. $md5sum = md5_file($dest_file);
  208. }
  209. } else {
  210. $fp = fopen($orig_file, "r");
  211. $contents = fread($fp, filesize($orig_file));
  212. fclose($fp);
  213. if (isset($atts['md5sum'])) {
  214. $md5sum = md5($contents);
  215. }
  216. $subst_from = $subst_to = array();
  217. foreach ($atts['replacements'] as $a) {
  218. $to = '';
  219. if ($a['type'] == 'php-const') {
  220. if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) {
  221. eval("\$to = $a[to];");
  222. } else {
  223. $this->log(0, "invalid php-const replacement: $a[to]");
  224. continue;
  225. }
  226. } elseif ($a['type'] == 'pear-config') {
  227. $to = $this->config->get($a['to']);
  228. } elseif ($a['type'] == 'package-info') {
  229. $to = $this->pkginfo[$a['to']];
  230. }
  231. if ($to) {
  232. $subst_from[] = $a['from'];
  233. $subst_to[] = $to;
  234. }
  235. }
  236. $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
  237. if (sizeof($subst_from)) {
  238. $contents = str_replace($subst_from, $subst_to, $contents);
  239. }
  240. $wp = @fopen($dest_file, "w");
  241. if (!is_resource($wp)) {
  242. return $this->raiseError("failed to create $dest_file: $php_errormsg",
  243. PEAR_INSTALLER_FAILED);
  244. }
  245. if (!fwrite($wp, $contents)) {
  246. return $this->raiseError("failed writing to $dest_file: $php_errormsg",
  247. PEAR_INSTALLER_FAILED);
  248. }
  249. fclose($wp);
  250. }
  251. if (isset($md5sum)) {
  252. if ($md5sum == $atts['md5sum']) {
  253. $this->log(3, "md5sum ok: $final_dest_file");
  254. } else {
  255. $this->log(0, "warning : bad md5sum for file $final_dest_file");
  256. }
  257. }
  258. if (!OS_WINDOWS) {
  259. if ($atts['role'] == 'script') {
  260. $mode = 0777 & ~(int)octdec($this->config->get('umask'));
  261. $this->log(3, "+ chmod +x $dest_file");
  262. } else {
  263. $mode = 0666 & ~(int)octdec($this->config->get('umask'));
  264. }
  265. $this->addFileOperation("chmod", array($mode, $dest_file));
  266. if (!@chmod($dest_file, $mode)) {
  267. $this->log(0, "failed to change mode of $dest_file");
  268. }
  269. }
  270. $this->addFileOperation("rename", array($dest_file, $final_dest_file));
  271. // XXX SHOULD BE DONE ONLY AFTER COMMIT
  272. // Store the full path where the file was installed for easy unistall
  273. $this->pkginfo['filelist'][$file]['installed_as'] = $installed_as;
  274. //$this->log(2, "installed: $dest_file");
  275. return PEAR_INSTALLER_OK;
  276. }
  277. // }}}
  278. // {{{ addFileOperation()
  279. function addFileOperation($type, $data)
  280. {
  281. if ($type == 'chmod') {
  282. $octmode = decoct($data[0]);
  283. $this->log(3, "adding to transaction: $type $octmode $data[1]");
  284. } else {
  285. $this->log(3, "adding to transaction: $type " . implode(" ", $data));
  286. }
  287. $this->file_operations[] = array($type, $data);
  288. }
  289. // }}}
  290. // {{{ startFileTransaction()
  291. function startFileTransaction($rollback_in_case = false)
  292. {
  293. if (count($this->file_operations) && $rollback_in_case) {
  294. $this->rollbackFileTransaction();
  295. }
  296. $this->file_operations = array();
  297. }
  298. // }}}
  299. // {{{ commitFileTransaction()
  300. function commitFileTransaction()
  301. {
  302. $n = count($this->file_operations);
  303. $this->log(2, "about to commit $n file operations");
  304. // first, check permissions and such manually
  305. $errors = array();
  306. foreach ($this->file_operations as $tr) {
  307. list($type, $data) = $tr;
  308. switch ($type) {
  309. case 'rename':
  310. // check that dest dir. is writable
  311. if (!is_writable(dirname($data[1]))) {
  312. $errors[] = "permission denied ($type): $data[1]";
  313. }
  314. break;
  315. case 'chmod':
  316. // check that file is writable
  317. if (!is_writable($data[1])) {
  318. $errors[] = "permission denied ($type): $data[1]";
  319. }
  320. break;
  321. case 'delete':
  322. // check that directory is writable
  323. if (file_exists($data[0]) && !is_writable(dirname($data[0]))) {
  324. $errors[] = "permission denied ($type): $data[0]";
  325. }
  326. break;
  327. }
  328. }
  329. $m = sizeof($errors);
  330. if ($m > 0) {
  331. foreach ($errors as $error) {
  332. $this->log(1, $error);
  333. }
  334. return false;
  335. }
  336. // really commit the transaction
  337. foreach ($this->file_operations as $tr) {
  338. list($type, $data) = $tr;
  339. switch ($type) {
  340. case 'rename':
  341. @rename($data[0], $data[1]);
  342. $this->log(3, "+ mv $data[0] $data[1]");
  343. break;
  344. case 'chmod':
  345. @chmod($data[0], $data[1]);
  346. $octmode = decoct($data[0]);
  347. $this->log(3, "+ chmod $octmode $data[1]");
  348. break;
  349. case 'delete':
  350. @unlink($data[0]);
  351. $this->log(3, "+ rm $data[0]");
  352. break;
  353. case 'rmdir':
  354. @rmdir($data[0]);
  355. $this->log(3, "+ rmdir $data[0]");
  356. break;
  357. }
  358. }
  359. $this->log(2, "successfully commited $n file operations");
  360. $this->file_operations = array();
  361. return true;
  362. }
  363. // }}}
  364. // {{{ rollbackFileTransaction()
  365. function rollbackFileTransaction()
  366. {
  367. $n = count($this->file_operations);
  368. $this->log(2, "rolling back $n file operations");
  369. foreach ($this->file_operations as $tr) {
  370. list($type, $data) = $tr;
  371. switch ($type) {
  372. case 'rename':
  373. @unlink($data[0]);
  374. $this->log(3, "+ rm $data[0]");
  375. break;
  376. case 'mkdir':
  377. @rmdir($data[0]);
  378. $this->log(3, "+ rmdir $data[0]");
  379. break;
  380. case 'chmod':
  381. break;
  382. case 'delete':
  383. break;
  384. }
  385. }
  386. $this->file_operations = array();
  387. }
  388. // }}}
  389. // {{{ getPackageDownloadUrl()
  390. function getPackageDownloadUrl($package)
  391. {
  392. if ($this === null || $this->config === null) {
  393. $package = "http://pear.php.net/get/$package";
  394. } else {
  395. $package = "http://" . $this->config->get('master_server') .
  396. "/get/$package";
  397. }
  398. if (!extension_loaded("zlib")) {
  399. $package .= '?uncompress=yes';
  400. }
  401. return $package;
  402. }
  403. // }}}
  404. // {{{ mkDirHier($dir)
  405. function mkDirHier($dir)
  406. {
  407. $this->addFileOperation('mkdir', array($dir));
  408. return parent::mkDirHier($dir);
  409. }
  410. // }}}
  411. // {{{ _prependPath($path, $prepend)
  412. function _prependPath($path, $prepend)
  413. {
  414. if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
  415. $path = $prepend . substr($path, 2);
  416. } else {
  417. $path = $prepend . $path;
  418. }
  419. return $path;
  420. }
  421. // }}}
  422. // {{{ install()
  423. /**
  424. * Installs the files within the package file specified.
  425. *
  426. * @param $pkgfile path to the package file
  427. *
  428. * @return array package info if successful, null if not
  429. */
  430. function install($pkgfile, $options = array())
  431. {
  432. // recognized options:
  433. // - force : force installation
  434. // - register-only : update registry but don't install files
  435. // - upgrade : upgrade existing install
  436. // - soft : fail silently
  437. //
  438. $php_dir = $this->config->get('php_dir');
  439. if (isset($options['installroot'])) {
  440. if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
  441. $options['installroot'] = substr($options['installroot'], 0, -1);
  442. }
  443. $php_dir = $this->_prependPath($php_dir, $options['installroot']);
  444. $this->registry = &new PEAR_Registry($php_dir);
  445. $this->installroot = $options['installroot'];
  446. } else {
  447. $registry = &$this->registry;
  448. $this->installroot = '';
  449. }
  450. $need_download = false;
  451. // ==> XXX should be removed later on
  452. $flag_old_format = false;
  453. if (preg_match('#^(http|ftp)://#', $pkgfile)) {
  454. $need_download = true;
  455. } elseif (!@is_file($pkgfile)) {
  456. if ($this->validPackageName($pkgfile)) {
  457. if ($this->registry->packageExists($pkgfile) &&
  458. empty($options['upgrade']) && empty($options['force']))
  459. {
  460. return $this->raiseError("$pkgfile already installed");
  461. }
  462. $pkgfile = $this->getPackageDownloadUrl($pkgfile);
  463. $need_download = true;
  464. } else {
  465. if (strlen($pkgfile)) {
  466. return $this->raiseError("Could not open the package file: $pkgfile");
  467. } else {
  468. return $this->raiseError("No package file given");
  469. }
  470. }
  471. }
  472. // Download package -----------------------------------------------
  473. if ($need_download) {
  474. $downloaddir = $this->config->get('download_dir');
  475. if (empty($downloaddir)) {
  476. if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
  477. return $downloaddir;
  478. }
  479. $this->log(2, '+ tmp dir created at ' . $downloaddir);
  480. }
  481. $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
  482. $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
  483. if (PEAR::isError($file)) {
  484. return $this->raiseError($file);
  485. }
  486. $pkgfile = $file;
  487. }
  488. if (substr($pkgfile, -4) == '.xml') {
  489. $descfile = $pkgfile;
  490. } else {
  491. // Decompress pack in tmp dir -------------------------------------
  492. // To allow relative package file names
  493. $oldcwd = getcwd();
  494. if (@chdir(dirname($pkgfile))) {
  495. $pkgfile = getcwd() . DIRECTORY_SEPARATOR . basename($pkgfile);
  496. chdir($oldcwd);
  497. }
  498. if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
  499. return $tmpdir;
  500. }
  501. $this->log(2, '+ tmp dir created at ' . $tmpdir);
  502. $tar = new Archive_Tar($pkgfile);
  503. if (!@$tar->extract($tmpdir)) {
  504. return $this->raiseError("unable to unpack $pkgfile");
  505. }
  506. // ----- Look for existing package file
  507. $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
  508. if (!is_file($descfile)) {
  509. // ----- Look for old package archive format
  510. // In this format the package.xml file was inside the
  511. // Package-n.n directory
  512. $dp = opendir($tmpdir);
  513. do {
  514. $pkgdir = readdir($dp);
  515. } while ($pkgdir{0} == '.');
  516. $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml';
  517. $flag_old_format = true;
  518. $this->log(0, "warning : you are using an archive with an old format");
  519. }
  520. // <== XXX This part should be removed later on
  521. }
  522. if (!is_file($descfile)) {
  523. return $this->raiseError("no package.xml file after extracting the archive");
  524. }
  525. // Parse xml file -----------------------------------------------
  526. $pkginfo = $this->infoFromDescriptionFile($descfile);
  527. if (PEAR::isError($pkginfo)) {
  528. return $pkginfo;
  529. }
  530. $this->validatePackageInfo($pkginfo, $errors, $warnings);
  531. // XXX We allow warnings, do we have to do it?
  532. if (count($errors)) {
  533. if (empty($options['force'])) {
  534. return $this->raiseError("The following errors where found (use force option to install anyway):\n".
  535. implode("\n", $errors));
  536. } else {
  537. $this->log(0, "warning : the following errors were found:\n".
  538. implode("\n", $errors));
  539. }
  540. }
  541. $pkgname = $pkginfo['package'];
  542. // Check dependencies -------------------------------------------
  543. if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) {
  544. $error = $this->checkDeps($pkginfo);
  545. if ($error) {
  546. if (empty($options['soft'])) {
  547. $this->log(0, $error);
  548. }
  549. return $this->raiseError("$pkgname: dependencies failed");
  550. }
  551. }
  552. if (empty($options['force'])) {
  553. // checks to do when not in "force" mode
  554. $test = $this->registry->checkFileMap($pkginfo);
  555. if (sizeof($test)) {
  556. $tmp = $test;
  557. foreach ($tmp as $file => $pkg) {
  558. if ($pkg == $pkgname) {
  559. unset($test[$file]);
  560. }
  561. }
  562. if (sizeof($test)) {
  563. $msg = "$pkgname: conflicting files found:\n";
  564. $longest = max(array_map("strlen", array_keys($test)));
  565. $fmt = "%${longest}s (%s)\n";
  566. foreach ($test as $file => $pkg) {
  567. $msg .= sprintf($fmt, $file, $pkg);
  568. }
  569. return $this->raiseError($msg);
  570. }
  571. }
  572. }
  573. if (empty($options['upgrade'])) {
  574. // checks to do only when installing new packages
  575. if (empty($options['force']) && $this->registry->packageExists($pkgname)) {
  576. return $this->raiseError("$pkgname already installed");
  577. }
  578. } else {
  579. // checks to do only when upgrading packages
  580. if (!$this->registry->packageExists($pkgname)) {
  581. return $this->raiseError("$pkgname not installed");
  582. }
  583. $v1 = $this->registry->packageInfo($pkgname, 'version');
  584. $v2 = $pkginfo['version'];
  585. $cmp = version_compare("$v1", "$v2", 'gt');
  586. if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
  587. return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
  588. }
  589. if (empty($options['register-only'])) {
  590. // when upgrading, remove old release's files first:
  591. if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
  592. return $this->raiseError($err);
  593. }
  594. }
  595. }
  596. // Copy files to dest dir ---------------------------------------
  597. // info from the package it self we want to access from _installFile
  598. $this->pkginfo = &$pkginfo;
  599. // used to determine whether we should build any C code
  600. $this->source_files = 0;
  601. if (empty($options['register-only'])) {
  602. if (!is_dir($php_dir)) {
  603. return $this->raiseError("no script destination directory\n",
  604. null, PEAR_ERROR_DIE);
  605. }
  606. // don't want strange characters
  607. $pkgname = ereg_replace ('[^a-zA-Z0-9._]', '_', $pkginfo['package']);
  608. $pkgversion = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $pkginfo['version']);
  609. $tmp_path = dirname($descfile);
  610. if (substr($pkgfile, -4) != '.xml') {
  611. $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkgversion;
  612. }
  613. // ==> XXX This part should be removed later on
  614. if ($flag_old_format) {
  615. $tmp_path = dirname($descfile);
  616. }
  617. // <== XXX This part should be removed later on
  618. foreach ($pkginfo['filelist'] as $file => $atts) {
  619. $this->expectError(PEAR_INSTALLER_FAILED);
  620. $res = $this->_installFile($file, $atts, $tmp_path);
  621. $this->popExpect();
  622. if (PEAR::isError($res)) {
  623. if (empty($options['ignore-errors'])) {
  624. $this->rollbackFileTransaction();
  625. return $this->raiseError($res);
  626. } else {
  627. $this->log(0, "Warning: " . $res->getMessage());
  628. }
  629. }
  630. if ($res != PEAR_INSTALLER_OK) {
  631. // Do not register files that were not installed
  632. unset($pkginfo['filelist'][$file]);
  633. }
  634. }
  635. if ($this->source_files > 0 && empty($options['nobuild'])) {
  636. $this->log(1, "$this->source_files source files, building");
  637. $bob = &new PEAR_Builder($this->ui);
  638. $bob->debug = $this->debug;
  639. $built = $bob->build($descfile, array(&$this, '_buildCallback'));
  640. if (PEAR::isError($built)) {
  641. $this->rollbackFileTransaction();
  642. return $built;
  643. }
  644. foreach ($built as $ext) {
  645. $bn = basename($ext['file']);
  646. $this->log(2, "installing $bn");
  647. $dest = $this->config->get('ext_dir') . DIRECTORY_SEPARATOR . $bn;
  648. $this->log(3, "+ cp $ext[file] ext_dir");
  649. $copyto = $this->_prependPath($dest, $this->installroot);
  650. if (!@copy($ext['file'], $copyto)) {
  651. $this->rollbackFileTransaction();
  652. return $this->raiseError("failed to copy $bn to $copyto");
  653. }
  654. $pkginfo['filelist'][$bn] = array(
  655. 'role' => 'ext',
  656. 'installed_as' => $dest,
  657. 'php_api' => $ext['php_api'],
  658. 'zend_mod_api' => $ext['zend_mod_api'],
  659. 'zend_ext_api' => $ext['zend_ext_api'],
  660. );
  661. }
  662. }
  663. }
  664. if (!$this->commitFileTransaction()) {
  665. $this->rollbackFileTransaction();
  666. return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
  667. }
  668. // Register that the package is installed -----------------------
  669. if (empty($options['upgrade'])) {
  670. // if 'force' is used, replace the info in registry
  671. if (!empty($options['force']) && $this->registry->packageExists($pkgname)) {
  672. $this->registry->deletePackage($pkgname);
  673. }
  674. $ret = $this->registry->addPackage($pkgname, $pkginfo);
  675. } else {
  676. $ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
  677. }
  678. if (!$ret) {
  679. return null;
  680. }
  681. return $pkginfo;
  682. }
  683. // }}}
  684. // {{{ uninstall()
  685. function uninstall($package, $options = array())
  686. {
  687. $php_dir = $this->config->get('php_dir');
  688. if (isset($options['installroot'])) {
  689. if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
  690. $options['installroot'] = substr($options['installroot'], 0, -1);
  691. }
  692. $this->installroot = $options['installroot'];
  693. $php_dir = $this->_prependPath($php_dir, $this->installroot);
  694. } else {
  695. $this->installroot = '';
  696. }
  697. $this->registry = &new PEAR_Registry($php_dir);
  698. // Delete the files
  699. if (PEAR::isError($err = $this->_deletePackageFiles($package))) {
  700. $this->rollbackFileTransaction();
  701. return $this->raiseError($err);
  702. }
  703. if (!$this->commitFileTransaction()) {
  704. $this->rollbackFileTransaction();
  705. return $this->raiseError("uninstall failed");
  706. }
  707. // Register that the package is no longer installed
  708. return $this->registry->deletePackage($package);
  709. }
  710. // }}}
  711. // {{{ checkDeps()
  712. function checkDeps(&$pkginfo)
  713. {
  714. $depchecker = &new PEAR_Dependency($this->registry);
  715. $error = $errors = '';
  716. $failed_deps = array();
  717. if (is_array($pkginfo['release_deps'])) {
  718. foreach($pkginfo['release_deps'] as $dep) {
  719. $code = $depchecker->callCheckMethod($error, $dep);
  720. if ($code) {
  721. $failed_deps[] = array($dep, $code, $error);
  722. }
  723. }
  724. $n = count($failed_deps);
  725. if ($n > 0) {
  726. $depinstaller =& new PEAR_Installer($this->ui);
  727. $to_install = array();
  728. for ($i = 0; $i < $n; $i++) {
  729. if (isset($failed_deps[$i]['type'])) {
  730. $type = $failed_deps[$i]['type'];
  731. } else {
  732. $type = 'pkg';
  733. }
  734. switch ($failed_deps[$i][1]) {
  735. case PEAR_DEPENDENCY_MISSING:
  736. if ($type == 'pkg') {
  737. // install
  738. }
  739. $errors .= "\n" . $failed_deps[$i][2];
  740. break;
  741. case PEAR_DEPENDENCY_UPGRADE_MINOR:
  742. if ($type == 'pkg') {
  743. // upgrade
  744. }
  745. $errors .= "\n" . $failed_deps[$i][2];
  746. break;
  747. default:
  748. $errors .= "\n" . $failed_deps[$i][2];
  749. break;
  750. }
  751. }
  752. return substr($errors, 1);
  753. }
  754. }
  755. return false;
  756. }
  757. // }}}
  758. // {{{ _downloadCallback()
  759. function _downloadCallback($msg, $params = null)
  760. {
  761. switch ($msg) {
  762. case 'saveas':
  763. $this->log(1, "downloading $params ...");
  764. break;
  765. case 'done':
  766. $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
  767. break;
  768. }
  769. if (method_exists($this->ui, '_downloadCallback'))
  770. $this->ui->_downloadCallback($msg, $params);
  771. }
  772. // }}}
  773. // {{{ _buildCallback()
  774. function _buildCallback($what, $data)
  775. {
  776. if (($what == 'cmdoutput' && $this->debug > 1) ||
  777. ($what == 'output' && $this->debug > 0)) {
  778. $this->ui->outputData(rtrim($data), 'build');
  779. }
  780. }
  781. // }}}
  782. }
  783. if (!function_exists("md5_file")) {
  784. function md5_file($filename) {
  785. $fp = fopen($filename, "r");
  786. if (!$fp) return null;
  787. $contents = fread($fp, filesize($filename));
  788. fclose($fp);
  789. return md5($contents);
  790. }
  791. }
  792. ?>