[ Index ]
 

Code source de PHP PEAR 1.4.5

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/PEAR/ -> Installer.php (source)

   1  <?php
   2  /**
   3   * PEAR_Installer
   4   *
   5   * PHP versions 4 and 5
   6   *
   7   * LICENSE: This source file is subject to version 3.0 of the PHP license
   8   * that is available through the world-wide-web at the following URI:
   9   * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  10   * the PHP License and are unable to obtain it through the web, please
  11   * send a note to license@php.net so we can mail you a copy immediately.
  12   *
  13   * @category   pear
  14   * @package    PEAR
  15   * @author     Stig Bakken <ssb@php.net>
  16   * @author     Tomas V.V. Cox <cox@idecnet.com>
  17   * @author     Martin Jansen <mj@php.net>
  18   * @author     Greg Beaver <cellog@php.net>
  19   * @copyright  1997-2006 The PHP Group
  20   * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  21   * @version    CVS: $Id: Installer.php,v 1.241 2007/01/08 05:43:49 cellog Exp $
  22   * @link       http://pear.php.net/package/PEAR
  23   * @since      File available since Release 0.1
  24   */
  25  
  26  /**
  27   * Used for installation groups in package.xml 2.0 and platform exceptions
  28   */
  29  require_once  'OS/Guess.php';
  30  require_once  'PEAR/Downloader.php';
  31  
  32  define('PEAR_INSTALLER_NOBINARY', -240);
  33  /**
  34   * Administration class used to install PEAR packages and maintain the
  35   * installed package database.
  36   *
  37   * @category   pear
  38   * @package    PEAR
  39   * @author     Stig Bakken <ssb@php.net>
  40   * @author     Tomas V.V. Cox <cox@idecnet.com>
  41   * @author     Martin Jansen <mj@php.net>
  42   * @author     Greg Beaver <cellog@php.net>
  43   * @copyright  1997-2006 The PHP Group
  44   * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  45   * @version    Release: 1.5.0
  46   * @link       http://pear.php.net/package/PEAR
  47   * @since      Class available since Release 0.1
  48   */
  49  class PEAR_Installer extends PEAR_Downloader
  50  {
  51      // {{{ properties
  52  
  53      /** name of the package directory, for example Foo-1.0
  54       * @var string
  55       */
  56      var $pkgdir;
  57  
  58      /** directory where PHP code files go
  59       * @var string
  60       */
  61      var $phpdir;
  62  
  63      /** directory where PHP extension files go
  64       * @var string
  65       */
  66      var $extdir;
  67  
  68      /** directory where documentation goes
  69       * @var string
  70       */
  71      var $docdir;
  72  
  73      /** installation root directory (ala PHP's INSTALL_ROOT or
  74       * automake's DESTDIR
  75       * @var string
  76       */
  77      var $installroot = '';
  78  
  79      /** debug level
  80       * @var int
  81       */
  82      var $debug = 1;
  83  
  84      /** temporary directory
  85       * @var string
  86       */
  87      var $tmpdir;
  88  
  89      /**
  90       * PEAR_Registry object used by the installer
  91       * @var PEAR_Registry
  92       */
  93      var $registry;
  94  
  95      /**
  96       * array of PEAR_Downloader_Packages
  97       * @var array
  98       */
  99      var $_downloadedPackages;
 100  
 101      /** List of file transactions queued for an install/upgrade/uninstall.
 102       *
 103       *  Format:
 104       *    array(
 105       *      0 => array("rename => array("from-file", "to-file")),
 106       *      1 => array("delete" => array("file-to-delete")),
 107       *      ...
 108       *    )
 109       *
 110       * @var array
 111       */
 112      var $file_operations = array();
 113  
 114      // }}}
 115  
 116      // {{{ constructor
 117  
 118      /**
 119       * PEAR_Installer constructor.
 120       *
 121       * @param object $ui user interface object (instance of PEAR_Frontend_*)
 122       *
 123       * @access public
 124       */
 125      function PEAR_Installer(&$ui)
 126      {
 127          parent::PEAR_Common();
 128          $this->setFrontendObject($ui);
 129          $this->debug = $this->config->get('verbose');
 130      }
 131  
 132      function setOptions($options)
 133      {
 134          $this->_options = $options;
 135      }
 136  
 137      function setConfig(&$config)
 138      {
 139          $this->config = &$config;
 140          $this->_registry = &$config->getRegistry();
 141      }
 142  
 143      // }}}
 144  
 145      function _removeBackups($files)
 146      {
 147          foreach ($files as $path) {
 148              $this->addFileOperation('removebackup', array($path));
 149          }
 150      }
 151  
 152      // {{{ _deletePackageFiles()
 153  
 154      /**
 155       * Delete a package's installed files, does not remove empty directories.
 156       *
 157       * @param string package name
 158       * @param string channel name
 159       * @param bool if true, then files are backed up first
 160       * @return bool TRUE on success, or a PEAR error on failure
 161       * @access protected
 162       */
 163      function _deletePackageFiles($package, $channel = false, $backup = false)
 164      {
 165          if (!$channel) {
 166              $channel = 'pear.php.net';
 167          }
 168          if (!strlen($package)) {
 169              return $this->raiseError("No package to uninstall given");
 170          }
 171          if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
 172              // to avoid race conditions, include all possible needed files
 173              require_once  'PEAR/Task/Common.php';
 174              require_once  'PEAR/Task/Replace.php';
 175              require_once  'PEAR/Task/Unixeol.php';
 176              require_once  'PEAR/Task/Windowseol.php';
 177              require_once  'PEAR/PackageFile/v1.php';
 178              require_once  'PEAR/PackageFile/v2.php';
 179              require_once  'PEAR/PackageFile/Generator/v1.php';
 180              require_once  'PEAR/PackageFile/Generator/v2.php';
 181          }
 182          $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
 183          if ($filelist == null) {
 184              return $this->raiseError("$channel/$package not installed");
 185          }
 186          $ret = array();
 187          foreach ($filelist as $file => $props) {
 188              if (empty($props['installed_as'])) {
 189                  continue;
 190              }
 191              $path = $props['installed_as'];
 192              if ($backup) {
 193                  $this->addFileOperation('backup', array($path));
 194                  $ret[] = $path;
 195              }
 196              $this->addFileOperation('delete', array($path));
 197          }
 198          if ($backup) {
 199              return $ret;
 200          }
 201          return true;
 202      }
 203  
 204      // }}}
 205      // {{{ _installFile()
 206  
 207      /**
 208       * @param string filename
 209       * @param array attributes from <file> tag in package.xml
 210       * @param string path to install the file in
 211       * @param array options from command-line
 212       * @access private
 213       */
 214      function _installFile($file, $atts, $tmp_path, $options)
 215      {
 216          // {{{ return if this file is meant for another platform
 217          static $os;
 218          if (!isset($this->_registry)) {
 219              $this->_registry = &$this->config->getRegistry();
 220          }
 221          if (isset($atts['platform'])) {
 222              if (empty($os)) {
 223                  $os = new OS_Guess();
 224              }
 225              if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
 226                  $negate = true;
 227                  $platform = substr($atts['platform'], 1);
 228              } else {
 229                  $negate = false;
 230                  $platform = $atts['platform'];
 231              }
 232              if ((bool) $os->matchSignature($platform) === $negate) {
 233                  $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
 234                  return PEAR_INSTALLER_SKIPPED;
 235              }
 236          }
 237          // }}}
 238  
 239          $channel = $this->pkginfo->getChannel();
 240          // {{{ assemble the destination paths
 241          switch ($atts['role']) {
 242              case 'doc':
 243              case 'data':
 244              case 'test':
 245                  $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
 246                              DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
 247                  unset($atts['baseinstalldir']);
 248                  break;
 249              case 'ext':
 250              case 'php':
 251                  $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
 252                  break;
 253              case 'script':
 254                  $dest_dir = $this->config->get('bin_dir', null, $channel);
 255                  break;
 256              case 'src':
 257              case 'extsrc':
 258                  $this->source_files++;
 259                  return;
 260              default:
 261                  return $this->raiseError("Invalid role `$atts[role]' for file $file");
 262          }
 263          $save_destdir = $dest_dir;
 264          if (!empty($atts['baseinstalldir'])) {
 265              $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
 266          }
 267          if (dirname($file) != '.' && empty($atts['install-as'])) {
 268              $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
 269          }
 270          if (empty($atts['install-as'])) {
 271              $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
 272          } else {
 273              $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
 274          }
 275          $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
 276  
 277          // Clean up the DIRECTORY_SEPARATOR mess
 278          $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
 279          list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
 280                                                      array(DIRECTORY_SEPARATOR,
 281                                                            DIRECTORY_SEPARATOR,
 282                                                            DIRECTORY_SEPARATOR),
 283                                                      array($dest_file, $orig_file));
 284          $final_dest_file = $installed_as = $dest_file;
 285          if (isset($this->_options['packagingroot'])) {
 286              $installedas_dest_dir = dirname($final_dest_file);
 287              $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
 288              $final_dest_file = $this->_prependPath($final_dest_file,
 289                  $this->_options['packagingroot']);
 290          } else {
 291              $installedas_dest_dir = dirname($final_dest_file);
 292              $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
 293          }
 294          $dest_dir = dirname($final_dest_file);
 295          $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
 296          // }}}
 297  
 298          if (empty($this->_options['register-only']) &&
 299                (!file_exists($dest_dir) || !is_dir($dest_dir))) {
 300              if (!$this->mkDirHier($dest_dir)) {
 301                  return $this->raiseError("failed to mkdir $dest_dir",
 302                                           PEAR_INSTALLER_FAILED);
 303              }
 304              $this->log(3, "+ mkdir $dest_dir");
 305          }
 306          // pretty much nothing happens if we are only registering the install
 307          if (empty($this->_options['register-only'])) {
 308              if (empty($atts['replacements'])) {
 309                  if (!file_exists($orig_file)) {
 310                      return $this->raiseError("file $orig_file does not exist",
 311                                               PEAR_INSTALLER_FAILED);
 312                  }
 313                  if (!@copy($orig_file, $dest_file)) {
 314                      return $this->raiseError("failed to write $dest_file: $php_errormsg",
 315                                               PEAR_INSTALLER_FAILED);
 316                  }
 317                  $this->log(3, "+ cp $orig_file $dest_file");
 318                  if (isset($atts['md5sum'])) {
 319                      $md5sum = md5_file($dest_file);
 320                  }
 321              } else {
 322                  // {{{ file with replacements
 323                  if (!file_exists($orig_file)) {
 324                      return $this->raiseError("file does not exist",
 325                                               PEAR_INSTALLER_FAILED);
 326                  }
 327                  $contents = file_get_contents($orig_file);
 328                  if ($contents === false) {
 329                      $contents = '';
 330                  }
 331                  if (isset($atts['md5sum'])) {
 332                      $md5sum = md5($contents);
 333                  }
 334                  $subst_from = $subst_to = array();
 335                  foreach ($atts['replacements'] as $a) {
 336                      $to = '';
 337                      if ($a['type'] == 'php-const') {
 338                          if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) {
 339                              eval("\$to = $a[to];");
 340                          } else {
 341                              if (!isset($options['soft'])) {
 342                                  $this->log(0, "invalid php-const replacement: $a[to]");
 343                              }
 344                              continue;
 345                          }
 346                      } elseif ($a['type'] == 'pear-config') {
 347                          if ($a['to'] == 'master_server') {
 348                              $chan = $this->_registry->getChannel($channel);
 349                              if (!PEAR::isError($chan)) {
 350                                  $to = $chan->getServer();
 351                              } else {
 352                                  $to = $this->config->get($a['to'], null, $channel);
 353                              }
 354                          } else {
 355                              $to = $this->config->get($a['to'], null, $channel);
 356                          }
 357                          if (is_null($to)) {
 358                              if (!isset($options['soft'])) {
 359                                  $this->log(0, "invalid pear-config replacement: $a[to]");
 360                              }
 361                              continue;
 362                          }
 363                      } elseif ($a['type'] == 'package-info') {
 364                          if ($t = $this->pkginfo->packageInfo($a['to'])) {
 365                              $to = $t;
 366                          } else {
 367                              if (!isset($options['soft'])) {
 368                                  $this->log(0, "invalid package-info replacement: $a[to]");
 369                              }
 370                              continue;
 371                          }
 372                      }
 373                      if (!is_null($to)) {
 374                          $subst_from[] = $a['from'];
 375                          $subst_to[] = $to;
 376                      }
 377                  }
 378                  $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
 379                  if (sizeof($subst_from)) {
 380                      $contents = str_replace($subst_from, $subst_to, $contents);
 381                  }
 382                  $wp = @fopen($dest_file, "wb");
 383                  if (!is_resource($wp)) {
 384                      return $this->raiseError("failed to create $dest_file: $php_errormsg",
 385                                               PEAR_INSTALLER_FAILED);
 386                  }
 387                  if (@fwrite($wp, $contents) === false) {
 388                      return $this->raiseError("failed writing to $dest_file: $php_errormsg",
 389                                               PEAR_INSTALLER_FAILED);
 390                  }
 391                  fclose($wp);
 392                  // }}}
 393              }
 394              // {{{ check the md5
 395              if (isset($md5sum)) {
 396                  if (strtolower($md5sum) == strtolower($atts['md5sum'])) {
 397                      $this->log(2, "md5sum ok: $final_dest_file");
 398                  } else {
 399                      if (empty($options['force'])) {
 400                          // delete the file
 401                          if (file_exists($dest_file)) {
 402                              unlink($dest_file);
 403                          }
 404                          if (!isset($options['ignore-errors'])) {
 405                              return $this->raiseError("bad md5sum for file $final_dest_file",
 406                                                   PEAR_INSTALLER_FAILED);
 407                          } else {
 408                              if (!isset($options['soft'])) {
 409                                  $this->log(0, "warning : bad md5sum for file $final_dest_file");
 410                              }
 411                          }
 412                      } else {
 413                          if (!isset($options['soft'])) {
 414                              $this->log(0, "warning : bad md5sum for file $final_dest_file");
 415                          }
 416                      }
 417                  }
 418              }
 419              // }}}
 420              // {{{ set file permissions
 421              if (!OS_WINDOWS) {
 422                  if ($atts['role'] == 'script') {
 423                      $mode = 0777 & ~(int)octdec($this->config->get('umask'));
 424                      $this->log(3, "+ chmod +x $dest_file");
 425                  } else {
 426                      $mode = 0666 & ~(int)octdec($this->config->get('umask'));
 427                  }
 428                  $this->addFileOperation("chmod", array($mode, $dest_file));
 429                  if (!@chmod($dest_file, $mode)) {
 430                      if (!isset($options['soft'])) {
 431                          $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
 432                      }
 433                  }
 434              }
 435              // }}}
 436              $this->addFileOperation("rename", array($dest_file, $final_dest_file,
 437                  $atts['role'] == 'ext'));
 438          }
 439          // Store the full path where the file was installed for easy unistall
 440          $this->addFileOperation("installed_as", array($file, $installed_as,
 441                                  $save_destdir, dirname(substr($installedas_dest_file, strlen($save_destdir)))));
 442  
 443          //$this->log(2, "installed: $dest_file");
 444          return PEAR_INSTALLER_OK;
 445      }
 446  
 447      // }}}
 448      // {{{ _installFile2()
 449  
 450      /**
 451       * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
 452       * @param string filename
 453       * @param array attributes from <file> tag in package.xml
 454       * @param string path to install the file in
 455       * @param array options from command-line
 456       * @access private
 457       */
 458      function _installFile2(&$pkg, $file, $atts, $tmp_path, $options)
 459      {
 460          if (!isset($this->_registry)) {
 461              $this->_registry = &$this->config->getRegistry();
 462          }
 463  
 464          $channel = $pkg->getChannel();
 465          // {{{ assemble the destination paths
 466          if (!in_array($atts['attribs']['role'],
 467                PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
 468              return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
 469                      "' for file $file");
 470          }
 471          $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
 472          $err = $role->setup($this, $pkg, $atts['attribs'], $file);
 473          if (PEAR::isError($err)) {
 474              return $err;
 475          }
 476          if (!$role->isInstallable()) {
 477              return;
 478          }
 479          $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
 480          if (PEAR::isError($info)) {
 481              return $info;
 482          } else {
 483              list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
 484          }
 485          $final_dest_file = $installed_as = $dest_file;
 486          if (isset($this->_options['packagingroot'])) {
 487              $final_dest_file = $this->_prependPath($final_dest_file,
 488                  $this->_options['packagingroot']);
 489          }
 490          $dest_dir = dirname($final_dest_file);
 491          $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
 492          // }}}
 493  
 494          if (empty($this->_options['register-only'])) {
 495              if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
 496                  if (!$this->mkDirHier($dest_dir)) {
 497                      return $this->raiseError("failed to mkdir $dest_dir",
 498                                               PEAR_INSTALLER_FAILED);
 499                  }
 500                  $this->log(3, "+ mkdir $dest_dir");
 501              }
 502          }
 503          $attribs = $atts['attribs'];
 504          unset($atts['attribs']);
 505          // pretty much nothing happens if we are only registering the install
 506          if (empty($this->_options['register-only'])) {
 507              if (!count($atts)) { // no tasks
 508                  if (!file_exists($orig_file)) {
 509                      return $this->raiseError("file $orig_file does not exist",
 510                                               PEAR_INSTALLER_FAILED);
 511                  }
 512                  if (!@copy($orig_file, $dest_file)) {
 513                      return $this->raiseError("failed to write $dest_file: $php_errormsg",
 514                                               PEAR_INSTALLER_FAILED);
 515                  }
 516                  $this->log(3, "+ cp $orig_file $dest_file");
 517                  if (isset($attribs['md5sum'])) {
 518                      $md5sum = md5_file($dest_file);
 519                  }
 520              } else { // file with tasks
 521                  if (!file_exists($orig_file)) {
 522                      return $this->raiseError("file $orig_file does not exist",
 523                                               PEAR_INSTALLER_FAILED);
 524                  }
 525                  $contents = file_get_contents($orig_file);
 526                  if ($contents === false) {
 527                      $contents = '';
 528                  }
 529                  if (isset($attribs['md5sum'])) {
 530                      $md5sum = md5($contents);
 531                  }
 532                  foreach ($atts as $tag => $raw) {
 533                      $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), 
 534                          array('', '_'), $tag);
 535                      $task = "PEAR_Task_$tag";
 536                      $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
 537                      if (!$task->isScript()) { // scripts are only handled after installation
 538                          $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
 539                          $res = $task->startSession($pkg, $contents, $final_dest_file);
 540                          if ($res === false) {
 541                              continue; // skip this file
 542                          }
 543                          if (PEAR::isError($res)) {
 544                              return $res;
 545                          }
 546                          $contents = $res; // save changes
 547                      }
 548                      $wp = @fopen($dest_file, "wb");
 549                      if (!is_resource($wp)) {
 550                          return $this->raiseError("failed to create $dest_file: $php_errormsg",
 551                                                   PEAR_INSTALLER_FAILED);
 552                      }
 553                      if (fwrite($wp, $contents) === false) {
 554                          return $this->raiseError("failed writing to $dest_file: $php_errormsg",
 555                                                   PEAR_INSTALLER_FAILED);
 556                      }
 557                      fclose($wp);
 558                  }
 559              }
 560              // {{{ check the md5
 561              if (isset($md5sum)) {
 562                  if (strtolower($md5sum) == strtolower($attribs['md5sum'])) {
 563                      $this->log(2, "md5sum ok: $final_dest_file");
 564                  } else {
 565                      if (empty($options['force'])) {
 566                          // delete the file
 567                          if (file_exists($dest_file)) {
 568                              unlink($dest_file);
 569                          }
 570                          if (!isset($options['ignore-errors'])) {
 571                              return $this->raiseError("bad md5sum for file $final_dest_file",
 572                                                       PEAR_INSTALLER_FAILED);
 573                          } else {
 574                              if (!isset($options['soft'])) {
 575                                  $this->log(0, "warning : bad md5sum for file $final_dest_file");
 576                              }
 577                          }
 578                      } else {
 579                          if (!isset($options['soft'])) {
 580                              $this->log(0, "warning : bad md5sum for file $final_dest_file");
 581                          }
 582                      }
 583                  }
 584              }
 585              // }}}
 586              // {{{ set file permissions
 587              if (!OS_WINDOWS) {
 588                  if ($role->isExecutable()) {
 589                      $mode = 0777 & ~(int)octdec($this->config->get('umask'));
 590                      $this->log(3, "+ chmod +x $dest_file");
 591                  } else {
 592                      $mode = 0666 & ~(int)octdec($this->config->get('umask'));
 593                  }
 594                  $this->addFileOperation("chmod", array($mode, $dest_file));
 595                  if (!@chmod($dest_file, $mode)) {
 596                      if (!isset($options['soft'])) {
 597                          $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
 598                      }
 599                  }
 600              }
 601              // }}}
 602              $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
 603          }
 604          // Store the full path where the file was installed for easy uninstall
 605          $this->addFileOperation("installed_as", array($file, $installed_as,
 606                              $save_destdir, dirname(substr($dest_file, strlen($save_destdir)))));
 607  
 608          //$this->log(2, "installed: $dest_file");
 609          return PEAR_INSTALLER_OK;
 610      }
 611  
 612      // }}}
 613      // {{{ addFileOperation()
 614  
 615      /**
 616       * Add a file operation to the current file transaction.
 617       *
 618       * @see startFileTransaction()
 619       * @param string $type This can be one of:
 620       *    - rename:  rename a file ($data has 3 values)
 621       *    - backup:  backup an existing file ($data has 1 value)
 622       *    - removebackup:  clean up backups created during install ($data has 1 value)
 623       *    - chmod:   change permissions on a file ($data has 2 values)
 624       *    - delete:  delete a file ($data has 1 value)
 625       *    - rmdir:   delete a directory if empty ($data has 1 value)
 626       *    - installed_as: mark a file as installed ($data has 4 values).
 627       * @param array $data For all file operations, this array must contain the
 628       *    full path to the file or directory that is being operated on.  For
 629       *    the rename command, the first parameter must be the file to rename,
 630       *    the second its new name, the third whether this is a PHP extension.
 631       *
 632       *    The installed_as operation contains 4 elements in this order:
 633       *    1. Filename as listed in the filelist element from package.xml
 634       *    2. Full path to the installed file
 635       *    3. Full path from the php_dir configuration variable used in this
 636       *       installation
 637       *    4. Relative path from the php_dir that this file is installed in
 638       */
 639      function addFileOperation($type, $data)
 640      {
 641          if (!is_array($data)) {
 642              return $this->raiseError('Internal Error: $data in addFileOperation'
 643                  . ' must be an array, was ' . gettype($data));
 644          }
 645          if ($type == 'chmod') {
 646              $octmode = decoct($data[0]);
 647              $this->log(3, "adding to transaction: $type $octmode $data[1]");
 648          } else {
 649              $this->log(3, "adding to transaction: $type " . implode(" ", $data));
 650          }
 651          $this->file_operations[] = array($type, $data);
 652      }
 653  
 654      // }}}
 655      // {{{ startFileTransaction()
 656  
 657      function startFileTransaction($rollback_in_case = false)
 658      {
 659          if (count($this->file_operations) && $rollback_in_case) {
 660              $this->rollbackFileTransaction();
 661          }
 662          $this->file_operations = array();
 663      }
 664  
 665      // }}}
 666      // {{{ commitFileTransaction()
 667  
 668      function commitFileTransaction()
 669      {
 670          $n = count($this->file_operations);
 671          $this->log(2, "about to commit $n file operations");
 672          // {{{ first, check permissions and such manually
 673          $errors = array();
 674          foreach ($this->file_operations as $tr) {
 675              list($type, $data) = $tr;
 676              switch ($type) {
 677                  case 'rename':
 678                      if (!file_exists($data[0])) {
 679                          $errors[] = "cannot rename file $data[0], doesn't exist";
 680                      }
 681                      // check that dest dir. is writable
 682                      if (!is_writable(dirname($data[1]))) {
 683                          $errors[] = "permission denied ($type): $data[1]";
 684                      }
 685                      break;
 686                  case 'chmod':
 687                      // check that file is writable
 688                      if (!is_writable($data[1])) {
 689                          $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
 690                      }
 691                      break;
 692                  case 'delete':
 693                      if (!file_exists($data[0])) {
 694                          $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
 695                      }
 696                      // check that directory is writable
 697                      if (file_exists($data[0])) {
 698                          if (!is_writable(dirname($data[0]))) {
 699                              $errors[] = "permission denied ($type): $data[0]";
 700                          } else {
 701                              // make sure the file to be deleted can be opened for writing
 702                              $fp = false;
 703                              if (!is_dir($data[0]) &&
 704                                    (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
 705                                  $errors[] = "permission denied ($type): $data[0]";
 706                              } elseif ($fp) {
 707                                  fclose($fp);
 708                              }
 709                          }
 710                      }
 711                      break;
 712              }
 713  
 714          }
 715          // }}}
 716          $m = sizeof($errors);
 717          if ($m > 0) {
 718              foreach ($errors as $error) {
 719                  if (!isset($this->_options['soft'])) {
 720                      $this->log(1, $error);
 721                  }
 722              }
 723              if (!isset($this->_options['ignore-errors'])) {
 724                  return false;
 725              }
 726          }
 727          $this->_dirtree = array();
 728          // {{{ really commit the transaction
 729          foreach ($this->file_operations as $tr) {
 730              list($type, $data) = $tr;
 731              switch ($type) {
 732                  case 'backup':
 733                      if (!@copy($data[0], $data[0] . '.bak')) {
 734                          $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
 735                              '.bak ' . $php_errormsg);
 736                          return false;
 737                      }
 738                      $this->log(3, "+ backup $data[0] to $data[0].bak");
 739                      break;
 740                  case 'removebackup':
 741                      if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
 742                          unlink($data[0] . '.bak');
 743                          $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
 744                      }
 745                      break;
 746                  case 'rename':
 747                      if (file_exists($data[1])) {
 748                          $test = @unlink($data[1]);
 749                      } else {
 750                          $test = null;
 751                      }
 752                      if (!$test && file_exists($data[1])) {
 753                          if ($data[2]) {
 754                              $extra = ', this extension must be installed manually.  Rename to "' .
 755                                  basename($data[1]) . '"';
 756                          } else {
 757                              $extra = '';
 758                          }
 759                          if (!isset($this->_options['soft'])) {
 760                              $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
 761                                  $data[0] . $extra);
 762                          }
 763                          if (!isset($this->_options['ignore-errors'])) {
 764                              return false;
 765                          }
 766                      }
 767                      // permissions issues with rename - copy() is far superior
 768                      $perms = @fileperms($data[0]);
 769                      if (!@copy($data[0], $data[1])) {
 770                          $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
 771                              ' ' . $php_errormsg);
 772                          return false;
 773                      }
 774                      // copy over permissions, otherwise they are lost
 775                      @chmod($data[1], $perms);
 776                      @unlink($data[0]);
 777                      $this->log(3, "+ mv $data[0] $data[1]");
 778                      break;
 779                  case 'chmod':
 780                      if (!@chmod($data[1], $data[0])) {
 781                          $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
 782                              decoct($data[0]) . ' ' . $php_errormsg);
 783                          return false;
 784                      }
 785                      $octmode = decoct($data[0]);
 786                      $this->log(3, "+ chmod $octmode $data[1]");
 787                      break;
 788                  case 'delete':
 789                      if (file_exists($data[0])) {
 790                          if (!@unlink($data[0])) {
 791                              $this->log(1, 'Could not delete ' . $data[0] . ' ' .
 792                                  $php_errormsg);
 793                              return false;
 794                          }
 795                          $this->log(3, "+ rm $data[0]");
 796                      }
 797                      break;
 798                  case 'rmdir':
 799                      if (file_exists($data[0])) {
 800                          do {
 801                              $testme = opendir($data[0]);
 802                              while (false !== ($entry = readdir($testme))) {
 803                                  if ($entry == '.' || $entry == '..') {
 804                                      continue;
 805                                  }
 806                                  closedir($testme);
 807                                  break 2; // this directory is not empty and can't be
 808                                           // deleted
 809                              }
 810                              closedir($testme);
 811                              if (!@rmdir($data[0])) {
 812                                  $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
 813                                      $php_errormsg);
 814                                  return false;
 815                              }
 816                              $this->log(3, "+ rmdir $data[0]");
 817                          } while (false);
 818                      }
 819                      break;
 820                  case 'installed_as':
 821                      $this->pkginfo->setInstalledAs($data[0], $data[1]);
 822                      if (!isset($this->_dirtree[dirname($data[1])])) {
 823                          $this->_dirtree[dirname($data[1])] = true;
 824                          $this->pkginfo->setDirtree(dirname($data[1]));
 825  
 826                          while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
 827                                && $data[3] != '.') {
 828                              $this->pkginfo->setDirtree($pp =
 829                                  $this->_prependPath($data[3], $data[2]));
 830                              $this->_dirtree[$pp] = true;
 831                              $data[3] = dirname($data[3]);
 832                          }
 833                      }
 834                      break;
 835              }
 836          }
 837          // }}}
 838          $this->log(2, "successfully committed $n file operations");
 839          $this->file_operations = array();
 840          return true;
 841      }
 842  
 843      // }}}
 844      // {{{ rollbackFileTransaction()
 845  
 846      function rollbackFileTransaction()
 847      {
 848          $n = count($this->file_operations);
 849          $this->log(2, "rolling back $n file operations");
 850          foreach ($this->file_operations as $tr) {
 851              list($type, $data) = $tr;
 852              switch ($type) {
 853                  case 'backup':
 854                      if (file_exists($data[0] . '.bak')) {
 855                          @unlink($data[0]);
 856                          @copy($data[0] . '.bak', $data[0]);
 857                          $this->log(3, "+ restore $data[0] from $data[0].bak");
 858                      }
 859                      break;
 860                  case 'rename':
 861                      @unlink($data[0]);
 862                      $this->log(3, "+ rm $data[0]");
 863                      break;
 864                  case 'mkdir':
 865                      @rmdir($data[0]);
 866                      $this->log(3, "+ rmdir $data[0]");
 867                      break;
 868                  case 'chmod':
 869                      break;
 870                  case 'delete':
 871                      break;
 872                  case 'installed_as':
 873                      $this->pkginfo->setInstalledAs($data[0], false);
 874                      break;
 875              }
 876          }
 877          $this->pkginfo->resetDirtree();
 878          $this->file_operations = array();
 879      }
 880  
 881      // }}}
 882      // {{{ mkDirHier($dir)
 883  
 884      function mkDirHier($dir)
 885      {
 886          $this->addFileOperation('mkdir', array($dir));
 887          return parent::mkDirHier($dir);
 888      }
 889  
 890      // }}}
 891      // {{{ download()
 892  
 893      /**
 894       * Download any files and their dependencies, if necessary
 895       *
 896       * @param array a mixed list of package names, local files, or package.xml
 897       * @param PEAR_Config
 898       * @param array options from the command line
 899       * @param array this is the array that will be populated with packages to
 900       *              install.  Format of each entry:
 901       *
 902       * <code>
 903       * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
 904       *    'info' => array() // parsed package.xml
 905       * );
 906       * </code>
 907       * @param array this will be populated with any error messages
 908       * @param false private recursion variable
 909       * @param false private recursion variable
 910       * @param false private recursion variable
 911       * @deprecated in favor of PEAR_Downloader
 912       */
 913      function download($packages, $options, &$config, &$installpackages,
 914                        &$errors, $installed = false, $willinstall = false, $state = false)
 915      {
 916          // trickiness: initialize here
 917          parent::PEAR_Downloader($this->ui, $options, $config);
 918          $ret = parent::download($packages);
 919          $errors = $this->getErrorMsgs();
 920          $installpackages = $this->getDownloadedPackages();
 921          trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
 922                        "in favor of PEAR_Downloader class", E_USER_WARNING);
 923          return $ret;
 924      }
 925  
 926      // }}}
 927      // {{{ _parsePackageXml()
 928  
 929      function _parsePackageXml(&$descfile, &$tmpdir)
 930      {
 931          if (substr($descfile, -4) == '.xml') {
 932              $tmpdir = false;
 933          } else {
 934              // {{{ Decompress pack in tmp dir -------------------------------------
 935  
 936              // To allow relative package file names
 937              $descfile = realpath($descfile);
 938  
 939              if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
 940                  return $tmpdir;
 941              }
 942              $this->log(3, '+ tmp dir created at ' . $tmpdir);
 943              // }}}
 944          }
 945          // Parse xml file -----------------------------------------------
 946          $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir);
 947          PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 948          $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
 949          PEAR::staticPopErrorHandling();
 950          if (PEAR::isError($p)) {
 951              if (is_array($p->getUserInfo())) {
 952                  foreach ($p->getUserInfo() as $err) {
 953                      $loglevel = $err['level'] == 'error' ? 0 : 1;
 954                      if (!isset($this->_options['soft'])) {
 955                          $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
 956                      }
 957                  }
 958              }
 959              return $this->raiseError('Installation failed: invalid package file');
 960          } else {
 961              $descfile = $p->getPackageFile();
 962          }
 963          return $p;
 964      }
 965  
 966      // }}}
 967      /**
 968       * Set the list of PEAR_Downloader_Package objects to allow more sane
 969       * dependency validation
 970       * @param array
 971       */
 972      function setDownloadedPackages(&$pkgs)
 973      {
 974          PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 975          $err = $this->analyzeDependencies($pkgs);
 976          PEAR::popErrorHandling();
 977          if (PEAR::isError($err)) {
 978              return $err;
 979          }
 980          $this->_downloadedPackages = &$pkgs;
 981      }
 982  
 983      /**
 984       * Set the list of PEAR_Downloader_Package objects to allow more sane
 985       * dependency validation
 986       * @param array
 987       */
 988      function setUninstallPackages(&$pkgs)
 989      {
 990          $this->_downloadedPackages = &$pkgs;
 991      }
 992  
 993      function getInstallPackages()
 994      {
 995          return $this->_downloadedPackages;
 996      }
 997  
 998      // {{{ install()
 999  
1000      /**
1001       * Installs the files within the package file specified.
1002       *
1003       * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
1004       *        or a pre-initialized packagefile object
1005       * @param array $options
1006       * recognized options:
1007       * - installroot   : optional prefix directory for installation
1008       * - force         : force installation
1009       * - register-only : update registry but don't install files
1010       * - upgrade       : upgrade existing install
1011       * - soft          : fail silently
1012       * - nodeps        : ignore dependency conflicts/missing dependencies
1013       * - alldeps       : install all dependencies
1014       * - onlyreqdeps   : install only required dependencies
1015       *
1016       * @return array|PEAR_Error package info if successful
1017       */
1018  
1019      function install($pkgfile, $options = array())
1020      {
1021          $this->_options = $options;
1022          $this->_registry = &$this->config->getRegistry();
1023          if (is_object($pkgfile)) {
1024              $dlpkg = &$pkgfile;
1025              $pkg = $pkgfile->getPackageFile();
1026              $pkgfile = $pkg->getArchiveFile();
1027              $descfile = $pkg->getPackageFile();
1028              $tmpdir = dirname($descfile);
1029          } else {
1030              $descfile = $pkgfile;
1031              $tmpdir = '';
1032              if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) {
1033                  return $pkg;
1034              }
1035          }
1036  
1037          if (realpath($descfile) != realpath($pkgfile)) {
1038              $tar = new Archive_Tar($pkgfile);
1039              if (!$tar->extract($tmpdir)) {
1040                  return $this->raiseError("unable to unpack $pkgfile");
1041              }
1042          }
1043  
1044          $pkgname = $pkg->getName();
1045          $channel = $pkg->getChannel();
1046          if (isset($this->_options['packagingroot'])) {
1047              $regdir = $this->_prependPath(
1048                  $this->config->get('php_dir', null, 'pear.php.net'),
1049                  $this->_options['packagingroot']);
1050              $packrootphp_dir = $this->_prependPath(
1051                  $this->config->get('php_dir', null, $channel),
1052                  $this->_options['packagingroot']);
1053          }
1054  
1055          if (isset($options['installroot'])) {
1056              $this->config->setInstallRoot($options['installroot']);
1057              $this->_registry = &$this->config->getRegistry();
1058              $installregistry = &$this->_registry;
1059              $this->installroot = ''; // all done automagically now
1060              $php_dir = $this->config->get('php_dir', null, $channel);
1061          } else {
1062              $this->config->setInstallRoot(false);
1063              $this->_registry = &$this->config->getRegistry();
1064              if (isset($this->_options['packagingroot'])) {
1065                  $installregistry = &new PEAR_Registry($regdir);
1066                  if (!$installregistry->channelExists($channel, true)) {
1067                      // we need to fake a channel-discover of this channel
1068                      $chanobj = $this->_registry->getChannel($channel, true);
1069                      $installregistry->addChannel($chanobj);
1070                  }
1071                  $php_dir = $packrootphp_dir;
1072              } else {
1073                  $installregistry = &$this->_registry;
1074                  $php_dir = $this->config->get('php_dir', null, $channel);
1075              }
1076              $this->installroot = '';
1077          }
1078  
1079          // {{{ checks to do when not in "force" mode
1080          if (empty($options['force']) &&
1081                (file_exists($this->config->get('php_dir')) &&
1082                 is_dir($this->config->get('php_dir')))) {
1083              $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
1084              $instfilelist = $pkg->getInstallationFileList(true);
1085              if (PEAR::isError($instfilelist)) {
1086                  return $instfilelist;
1087              }
1088              // ensure we have the most accurate registry
1089              $installregistry->flushFileMap();
1090              $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
1091              if (PEAR::isError($test)) {
1092                  return $test;
1093              }
1094              if (sizeof($test)) {
1095                  $pkgs = $this->getInstallPackages();
1096                  $found = false;
1097                  foreach ($pkgs as $param) {
1098                      if ($pkg->isSubpackageOf($param)) {
1099                          $found = true;
1100                          break;
1101                      }
1102                  }
1103                  if ($found) {
1104                      // subpackages can conflict with earlier versions of parent packages
1105                      $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
1106                      $tmp = $test;
1107                      foreach ($tmp as $file => $info) {
1108                          if (is_array($info)) {
1109                              if (strtolower($info[1]) == strtolower($param->getPackage()) &&
1110                                    strtolower($info[0]) == strtolower($param->getChannel())) {
1111                                  unset($test[$file]);
1112                                  unset($parentreg['filelist'][$file]);
1113                              }
1114                          } else {
1115                              if (strtolower($param->getChannel()) != 'pear.php.net') {
1116                                  continue;
1117                              }
1118                              if (strtolower($info) == strtolower($param->getPackage())) {
1119                                  unset($test[$file]);
1120                                  unset($parentreg['filelist'][$file]);
1121                              }
1122                          }
1123                      }
1124                      $pfk = &new PEAR_PackageFile($this->config);
1125                      $parentpkg = &$pfk->fromArray($parentreg);
1126                      $installregistry->updatePackage2($parentpkg);
1127                  }
1128                  if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
1129                      $tmp = $test;
1130                      foreach ($tmp as $file => $info) {
1131                          if (is_string($info)) {
1132                              // pear.php.net packages are always stored as strings
1133                              if (strtolower($info) == strtolower($param->getPackage())) {
1134                                  // upgrading existing package
1135                                  unset($test[$file]);
1136                              }
1137                          }
1138                      }
1139                  }
1140                  if (sizeof($test)) {
1141                      $msg = "$channel/$pkgname: conflicting files found:\n";
1142                      $longest = max(array_map("strlen", array_keys($test)));
1143                      $fmt = "%$longest}s (%s)\n";
1144                      foreach ($test as $file => $info) {
1145                          if (!is_array($info)) {
1146                              $info = array('pear.php.net', $info);
1147                          }
1148                          $info = $info[0] . '/' . $info[1];
1149                          $msg .= sprintf($fmt, $file, $info);
1150                      }
1151                      if (!isset($options['ignore-errors'])) {
1152                          return $this->raiseError($msg);
1153                      } else {
1154                          if (!isset($options['soft'])) {
1155                              $this->log(0, "WARNING: $msg");
1156                          }
1157                      }
1158                  }
1159              }
1160          }
1161          // }}}
1162  
1163          $this->startFileTransaction();
1164  
1165          if (empty($options['upgrade']) && empty($options['soft'])) {
1166              // checks to do only when installing new packages
1167              if ($channel == 'pecl.php.net') {
1168                  $test = $installregistry->packageExists($pkgname, $channel);
1169                  if (!$test) {
1170                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1171                  }
1172              } else {
1173                  $test = $installregistry->packageExists($pkgname, $channel);
1174              }
1175              if (empty($options['force']) && $test) {
1176                  return $this->raiseError("$channel/$pkgname is already installed");
1177              }
1178          } else {
1179              $usechannel = $channel;
1180              if ($channel == 'pecl.php.net') {
1181                  $test = $installregistry->packageExists($pkgname, $channel);
1182                  if (!$test) {
1183                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1184                      $usechannel = 'pear.php.net';
1185                  }
1186              } else {
1187                  $test = $installregistry->packageExists($pkgname, $channel);
1188              }
1189              if ($test) {
1190                  $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
1191                  $v2 = $pkg->getVersion();
1192                  $cmp = version_compare("$v1", "$v2", 'gt');
1193                  if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
1194                      return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
1195                  }
1196                  if (empty($options['register-only'])) {
1197                      // when upgrading, remove old release's files first:
1198                      if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
1199                            true))) {
1200                          if (!isset($options['ignore-errors'])) {
1201                              return $this->raiseError($err);
1202                          } else {
1203                              if (!isset($options['soft'])) {
1204                                  $this->log(0, 'WARNING: ' . $err->getMessage());
1205                              }
1206                          }
1207                      } else {
1208                          $backedup = $err;
1209                      }
1210                  }
1211              }
1212          }
1213  
1214          // {{{ Copy files to dest dir ---------------------------------------
1215  
1216          // info from the package it self we want to access from _installFile
1217          $this->pkginfo = &$pkg;
1218          // used to determine whether we should build any C code
1219          $this->source_files = 0;
1220  
1221          $savechannel = $this->config->get('default_channel');
1222          if (empty($options['register-only']) && !is_dir($php_dir)) {
1223              if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
1224                  return $this->raiseError("no installation destination directory '$php_dir'\n");
1225              }
1226          }
1227  
1228          $tmp_path = dirname($descfile);
1229          if (substr($pkgfile, -4) != '.xml') {
1230              $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
1231          }
1232  
1233          $this->configSet('default_channel', $channel);
1234          // {{{ install files
1235  
1236          $ver = $pkg->getPackagexmlVersion();
1237          if (version_compare($ver, '2.0', '>=')) {
1238              $filelist = $pkg->getInstallationFilelist();
1239          } else {
1240              $filelist = $pkg->getFileList();
1241          }
1242          if (PEAR::isError($filelist)) {
1243              return $filelist;
1244          }
1245          $pkg->resetFilelist();
1246          $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
1247              'version', $pkg->getChannel()));
1248          foreach ($filelist as $file => $atts) {
1249              if ($pkg->getPackagexmlVersion() == '1.0') {
1250                  $this->expectError(PEAR_INSTALLER_FAILED);
1251                  $res = $this->_installFile($file, $atts, $tmp_path, $options);
1252                  $this->popExpect();
1253              } else {
1254                  $this->expectError(PEAR_INSTALLER_FAILED);
1255                  $res = $this->_installFile2($pkg, $file, $atts, $tmp_path, $options);
1256                  $this->popExpect();
1257              }
1258              if (PEAR::isError($res)) {
1259                  if (empty($options['ignore-errors'])) {
1260                      $this->rollbackFileTransaction();
1261                      if ($res->getMessage() == "file does not exist") {
1262                          $this->raiseError("file $file in package.xml does not exist");
1263                      }
1264                      return $this->raiseError($res);
1265                  } else {
1266                      if (!isset($options['soft'])) {
1267                          $this->log(0, "Warning: " . $res->getMessage());
1268                      }
1269                  }
1270              }
1271              if ($res == PEAR_INSTALLER_OK) {
1272                  // Register files that were installed
1273                  $pkg->installedFile($file, $atts);
1274              }
1275          }
1276          // }}}
1277  
1278          // {{{ compile and install source files
1279          if ($this->source_files > 0 && empty($options['nobuild'])) {
1280              if (PEAR::isError($err =
1281                    $this->_compileSourceFiles($savechannel, $pkg))) {
1282                  return $err;
1283              }
1284          }
1285          // }}}
1286  
1287          if (isset($backedup)) {
1288              $this->_removeBackups($backedup);
1289          }
1290          if (!$this->commitFileTransaction()) {
1291              $this->rollbackFileTransaction();
1292              $this->configSet('default_channel', $savechannel);
1293              return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
1294          }
1295          // }}}
1296  
1297          $ret = false;
1298          $installphase = 'install';
1299          $oldversion = false;
1300          // {{{ Register that the package is installed -----------------------
1301          if (empty($options['upgrade'])) {
1302              // if 'force' is used, replace the info in registry
1303              $usechannel = $channel;
1304              if ($channel == 'pecl.php.net') {
1305                  $test = $installregistry->packageExists($pkgname, $channel);
1306                  if (!$test) {
1307                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1308                      $usechannel = 'pear.php.net';
1309                  }
1310              } else {
1311                  $test = $installregistry->packageExists($pkgname, $channel);
1312              }
1313              if (!empty($options['force']) && $test) {
1314                  $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
1315                  $installregistry->deletePackage($pkgname, $usechannel);
1316              }
1317              $ret = $installregistry->addPackage2($pkg);
1318          } else {
1319              $usechannel = $channel;
1320              if ($channel == 'pecl.php.net') {
1321                  $test = $installregistry->packageExists($pkgname, $channel);
1322                  if (!$test) {
1323                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1324                      $usechannel = 'pear.php.net';
1325                  }
1326              } else {
1327                  $test = $installregistry->packageExists($pkgname, $channel);
1328              }
1329              // new: upgrade installs a package if it isn't installed
1330              if (!$test) {
1331                  $ret = $installregistry->addPackage2($pkg);
1332              } else {
1333                  if ($usechannel != $channel) {
1334                      $installregistry->deletePackage($pkgname, $usechannel);
1335                      $ret = $installregistry->addPackage2($pkg);
1336                  } else {
1337                      $ret = $installregistry->updatePackage2($pkg);
1338                  }
1339                  $installphase = 'upgrade';
1340              }
1341          }
1342          if (!$ret) {
1343              $this->configSet('default_channel', $savechannel);
1344              return $this->raiseError("Adding package $channel/$pkgname to registry failed");
1345          }
1346          // }}}
1347          $this->configSet('default_channel', $savechannel);
1348          if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
1349              if (PEAR_Task_Common::hasPostinstallTasks()) {
1350                  PEAR_Task_Common::runPostinstallTasks($installphase);
1351              }
1352          }
1353          return $pkg->toArray(true);
1354      }
1355  
1356      // }}}
1357  
1358      // {{{ _compileSourceFiles()
1359      /**
1360       * @param string
1361       * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1362       */
1363      function _compileSourceFiles($savechannel, &$filelist)
1364      {
1365          require_once  'PEAR/Builder.php';
1366          $this->log(1, "$this->source_files source files, building");
1367          $bob = &new PEAR_Builder($this->ui);
1368          $bob->debug = $this->debug;
1369          $built = $bob->build($filelist, array(&$this, '_buildCallback'));
1370          if (PEAR::isError($built)) {
1371              $this->rollbackFileTransaction();
1372              $this->configSet('default_channel', $savechannel);
1373              return $built;
1374          }
1375          $this->log(1, "\nBuild process completed successfully");
1376          foreach ($built as $ext) {
1377              $bn = basename($ext['file']);
1378              list($_ext_name, $_ext_suff) = explode('.', $bn);
1379              if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
1380                  if (extension_loaded($_ext_name)) {
1381                      $this->raiseError("Extension '$_ext_name' already loaded. " .
1382                                        'Please unload it in your php.ini file ' .
1383                                        'prior to install or upgrade');
1384                  }
1385                  $role = 'ext';
1386              } else {
1387                  $role = 'src';
1388              }
1389              $dest = $ext['dest'];
1390              $packagingroot = '';
1391              if (isset($this->_options['packagingroot'])) {
1392                  $packagingroot = $this->_options['packagingroot'];
1393              }
1394              $copyto = $this->_prependPath($dest, $packagingroot);
1395              if ($copyto != $dest) {
1396                  $this->log(1, "Installing '$dest' as '$copyto'");
1397              } else {
1398                  $this->log(1, "Installing '$dest'");
1399              }
1400              $copydir = dirname($copyto);
1401              // pretty much nothing happens if we are only registering the install
1402              if (empty($this->_options['register-only'])) {
1403                  if (!file_exists($copydir) || !is_dir($copydir)) {
1404                      if (!$this->mkDirHier($copydir)) {
1405                          return $this->raiseError("failed to mkdir $copydir",
1406                              PEAR_INSTALLER_FAILED);
1407                      }
1408                      $this->log(3, "+ mkdir $copydir");
1409                  }
1410                  if (!@copy($ext['file'], $copyto)) {
1411                      return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
1412                  }
1413                  $this->log(3, "+ cp $ext[file] $copyto");
1414                  $this->addFileOperation('rename', array($ext['file'], $copyto));
1415                  if (!OS_WINDOWS) {
1416                      $mode = 0666 & ~(int)octdec($this->config->get('umask'));
1417                      $this->addFileOperation('chmod', array($mode, $copyto));
1418                      if (!@chmod($copyto, $mode)) {
1419                          $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
1420                      }
1421                  }
1422              }
1423  
1424              if ($filelist->getPackageXmlVersion() == '1.0') {
1425                  $filelist->installedFile($bn, array(
1426                      'role' => $role,
1427                      'name' => $bn,
1428                      'installed_as' => $dest,
1429                      'php_api' => $ext['php_api'],
1430                      'zend_mod_api' => $ext['zend_mod_api'],
1431                      'zend_ext_api' => $ext['zend_ext_api'],
1432                      ));
1433              } else {
1434                  $filelist->installedFile($bn, array('attribs' => array(
1435                      'role' => $role,
1436                      'name' => $bn,
1437                      'installed_as' => $dest,
1438                      'php_api' => $ext['php_api'],
1439                      'zend_mod_api' => $ext['zend_mod_api'],
1440                      'zend_ext_api' => $ext['zend_ext_api'],
1441                      )));
1442              }
1443          }
1444      }
1445  
1446      // }}}
1447      function &getUninstallPackages()
1448      {
1449          return $this->_downloadedPackages;
1450      }
1451      // {{{ uninstall()
1452  
1453      /**
1454       * Uninstall a package
1455       *
1456       * This method removes all files installed by the application, and then
1457       * removes any empty directories.
1458       * @param string package name
1459       * @param array Command-line options.  Possibilities include:
1460       *
1461       *              - installroot: base installation dir, if not the default
1462       *              - register-only : update registry but don't remove files
1463       *              - nodeps: do not process dependencies of other packages to ensure
1464       *                        uninstallation does not break things
1465       */
1466      function uninstall($package, $options = array())
1467      {
1468          if (isset($options['installroot'])) {
1469              $this->config->setInstallRoot($options['installroot']);
1470              $this->installroot = '';
1471          } else {
1472              $this->config->setInstallRoot('');
1473              $this->installroot = '';
1474          }
1475          $this->_registry = &$this->config->getRegistry();
1476          if (is_object($package)) {
1477              $channel = $package->getChannel();
1478              $pkg = $package;
1479              $package = $pkg->getPackage();
1480          } else {
1481              $pkg = false;
1482              $info = $this->_registry->parsePackageName($package,
1483                  $this->config->get('default_channel'));
1484              $channel = $info['channel'];
1485              $package = $info['package'];
1486          }
1487          $savechannel = $this->config->get('default_channel');
1488          $this->configSet('default_channel', $channel);
1489          if (!is_object($pkg)) {
1490              $pkg = $this->_registry->getPackage($package, $channel);
1491          }
1492          if (!$pkg) {
1493              $this->configSet('default_channel', $savechannel);
1494              return $this->raiseError($this->_registry->parsedPackageNameToString(
1495                  array(
1496                      'channel' => $channel,
1497                      'package' => $package
1498                  ), true) . ' not installed');
1499          }
1500          if ($pkg->getInstalledBinary()) {
1501              // this is just an alias for a binary package
1502              return $this->_registry->deletePackage($package, $channel);
1503          }
1504          $filelist = $pkg->getFilelist();
1505          PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
1506          if (!class_exists('PEAR_Dependency2')) {
1507              require_once  'PEAR/Dependency2.php';
1508          }
1509          $depchecker = &new PEAR_Dependency2($this->config, $options, 
1510              array('channel' => $channel, 'package' => $package),
1511              PEAR_VALIDATE_UNINSTALLING);
1512          $e = $depchecker->validatePackageUninstall($this);
1513          PEAR::staticPopErrorHandling();
1514          if (PEAR::isError($e)) {
1515              if (!isset($options['ignore-errors'])) {
1516                  return $this->raiseError($e);
1517              } else {
1518                  if (!isset($options['soft'])) {
1519                      $this->log(0, 'WARNING: ' . $e->getMessage());
1520                  }
1521              }
1522          } elseif (is_array($e)) {
1523              if (!isset($options['soft'])) {
1524                  $this->log(0, $e[0]);
1525              }
1526          }
1527          $this->pkginfo = &$pkg;
1528          // pretty much nothing happens if we are only registering the uninstall
1529          if (empty($options['register-only'])) {
1530              // {{{ Delete the files
1531              $this->startFileTransaction();
1532              PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1533              if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
1534                  PEAR::popErrorHandling();
1535                  $this->rollbackFileTransaction();
1536                  $this->configSet('default_channel', $savechannel);
1537                  if (!isset($options['ignore-errors'])) {
1538                      return $this->raiseError($err);
1539                  } else {
1540                      if (!isset($options['soft'])) {
1541                          $this->log(0, 'WARNING: ' . $err->getMessage());
1542                      }
1543                  }
1544              } else {
1545                  PEAR::popErrorHandling();
1546              }
1547              if (!$this->commitFileTransaction()) {
1548                  $this->rollbackFileTransaction();
1549                  if (!isset($options['ignore-errors'])) {
1550                      return $this->raiseError("uninstall failed");
1551                  } elseif (!isset($options['soft'])) {
1552                      $this->log(0, 'WARNING: uninstall failed');
1553                  }
1554              } else {
1555                  $this->startFileTransaction();
1556                  if ($dirtree = $pkg->getDirTree()) {
1557                      // attempt to delete empty directories
1558                      uksort($dirtree, array($this, '_sortDirs'));
1559                      foreach($dirtree as $dir => $notused) {
1560                          $this->addFileOperation('rmdir', array($dir));
1561                      }
1562                  } else {
1563                      $this->configSet('default_channel', $savechannel);
1564                      return $this->_registry->deletePackage($package, $channel);
1565                  }
1566                  if (!$this->commitFileTransaction()) {
1567                      $this->rollbackFileTransaction();
1568                      if (!isset($options['ignore-errors'])) {
1569                          return $this->raiseError("uninstall failed");
1570                      } elseif (!isset($options['soft'])) {
1571                          $this->log(0, 'WARNING: uninstall failed');
1572                      }
1573                  }
1574              }
1575              // }}}
1576          }
1577  
1578          $this->configSet('default_channel', $savechannel);
1579          // Register that the package is no longer installed
1580          return $this->_registry->deletePackage($package, $channel);
1581      }
1582  
1583      /**
1584       * Sort a list of arrays of array(downloaded packagefilename) by dependency.
1585       *
1586       * It also removes duplicate dependencies
1587       * @param array an array of PEAR_PackageFile_v[1/2] objects
1588       * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
1589       */
1590      function sortPackagesForUninstall(&$packages)
1591      {
1592          $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
1593          if (PEAR::isError($this->_dependencyDB)) {
1594              return $this->_dependencyDB;
1595          }
1596          usort($packages, array(&$this, '_sortUninstall'));
1597      }
1598  
1599      function _sortUninstall($a, $b)
1600      {
1601          if (!$a->getDeps() && !$b->getDeps()) {
1602              return 0; // neither package has dependencies, order is insignificant
1603          }
1604          if ($a->getDeps() && !$b->getDeps()) {
1605              return -1; // $a must be installed after $b because $a has dependencies
1606          }
1607          if (!$a->getDeps() && $b->getDeps()) {
1608              return 1; // $b must be installed after $a because $b has dependencies
1609          }
1610          // both packages have dependencies
1611          if ($this->_dependencyDB->dependsOn($a, $b)) {
1612              return -1;
1613          }
1614          if ($this->_dependencyDB->dependsOn($b, $a)) {
1615              return 1;
1616          }
1617          return 0;
1618      }
1619  
1620      // }}}
1621      // {{{ _sortDirs()
1622      function _sortDirs($a, $b)
1623      {
1624          if (strnatcmp($a, $b) == -1) return 1;
1625          if (strnatcmp($a, $b) == 1) return -1;
1626          return 0;
1627      }
1628  
1629      // }}}
1630  
1631      // {{{ _buildCallback()
1632  
1633      function _buildCallback($what, $data)
1634      {
1635          if (($what == 'cmdoutput' && $this->debug > 1) ||
1636              ($what == 'output' && $this->debug > 0)) {
1637              $this->ui->outputData(rtrim($data), 'build');
1638          }
1639      }
1640  
1641      // }}}
1642  }
1643  
1644  // {{{ md5_file() utility function
1645  if (!function_exists("md5_file")) {
1646      function md5_file($filename) {
1647          if (!$fd = @fopen($file, 'r')) {
1648              return false;
1649          }
1650          fclose($fd);
1651          return md5(file_get_contents($filename));
1652      }
1653  }
1654  // }}}
1655  
1656  ?>


Généré le : Sun Feb 25 14:08:00 2007 par Balluche grâce à PHPXref 0.7