[ Index ]
 

Code source de GeekLog 1.4.1

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/system/pear/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.228.2.1 2006/03/05 20:07:52 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.4.11
  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']) && !@is_dir($dest_dir)) {
 299              if (!$this->mkDirHier($dest_dir)) {
 300                  return $this->raiseError("failed to mkdir $dest_dir",
 301                                           PEAR_INSTALLER_FAILED);
 302              }
 303              $this->log(3, "+ mkdir $dest_dir");
 304          }
 305          // pretty much nothing happens if we are only registering the install
 306          if (empty($this->_options['register-only'])) {
 307              if (empty($atts['replacements'])) {
 308                  if (!file_exists($orig_file)) {
 309                      return $this->raiseError("file $orig_file does not exist",
 310                                               PEAR_INSTALLER_FAILED);
 311                  }
 312                  if (!@copy($orig_file, $dest_file)) {
 313                      return $this->raiseError("failed to write $dest_file",
 314                                               PEAR_INSTALLER_FAILED);
 315                  }
 316                  $this->log(3, "+ cp $orig_file $dest_file");
 317                  if (isset($atts['md5sum'])) {
 318                      $md5sum = md5_file($dest_file);
 319                  }
 320              } else {
 321                  // {{{ file with replacements
 322                  if (!file_exists($orig_file)) {
 323                      return $this->raiseError("file does not exist",
 324                                               PEAR_INSTALLER_FAILED);
 325                  }
 326                  if (function_exists('file_get_contents')) {
 327                      $contents = file_get_contents($orig_file);
 328                  } else {
 329                      $fp = fopen($orig_file, "r");
 330                      $contents = @fread($fp, filesize($orig_file));
 331                      fclose($fp);
 332                  }
 333                  if ($contents === false) {
 334                      $contents = '';
 335                  }
 336                  if (isset($atts['md5sum'])) {
 337                      $md5sum = md5($contents);
 338                  }
 339                  $subst_from = $subst_to = array();
 340                  foreach ($atts['replacements'] as $a) {
 341                      $to = '';
 342                      if ($a['type'] == 'php-const') {
 343                          if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) {
 344                              eval("\$to = $a[to];");
 345                          } else {
 346                              if (!isset($options['soft'])) {
 347                                  $this->log(0, "invalid php-const replacement: $a[to]");
 348                              }
 349                              continue;
 350                          }
 351                      } elseif ($a['type'] == 'pear-config') {
 352                          if ($a['to'] == 'master_server') {
 353                              $chan = $this->_registry->getChannel($channel);
 354                              if (!PEAR::isError($chan)) {
 355                                  $to = $chan->getServer();
 356                              } else {
 357                                  $to = $this->config->get($a['to'], null, $channel);
 358                              }
 359                          } else {
 360                              $to = $this->config->get($a['to'], null, $channel);
 361                          }
 362                          if (is_null($to)) {
 363                              if (!isset($options['soft'])) {
 364                                  $this->log(0, "invalid pear-config replacement: $a[to]");
 365                              }
 366                              continue;
 367                          }
 368                      } elseif ($a['type'] == 'package-info') {
 369                          if ($t = $this->pkginfo->packageInfo($a['to'])) {
 370                              $to = $t;
 371                          } else {
 372                              if (!isset($options['soft'])) {
 373                                  $this->log(0, "invalid package-info replacement: $a[to]");
 374                              }
 375                              continue;
 376                          }
 377                      }
 378                      if (!is_null($to)) {
 379                          $subst_from[] = $a['from'];
 380                          $subst_to[] = $to;
 381                      }
 382                  }
 383                  $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
 384                  if (sizeof($subst_from)) {
 385                      $contents = str_replace($subst_from, $subst_to, $contents);
 386                  }
 387                  $wp = @fopen($dest_file, "wb");
 388                  if (!is_resource($wp)) {
 389                      return $this->raiseError("failed to create $dest_file: $php_errormsg",
 390                                               PEAR_INSTALLER_FAILED);
 391                  }
 392                  if (fwrite($wp, $contents) === false) {
 393                      return $this->raiseError("failed writing to $dest_file: $php_errormsg",
 394                                               PEAR_INSTALLER_FAILED);
 395                  }
 396                  fclose($wp);
 397                  // }}}
 398              }
 399              // {{{ check the md5
 400              if (isset($md5sum)) {
 401                  if (strtolower($md5sum) == strtolower($atts['md5sum'])) {
 402                      $this->log(2, "md5sum ok: $final_dest_file");
 403                  } else {
 404                      if (empty($options['force'])) {
 405                          // delete the file
 406                          @unlink($dest_file);
 407                          if (!isset($options['ignore-errors'])) {
 408                              return $this->raiseError("bad md5sum for file $final_dest_file",
 409                                                   PEAR_INSTALLER_FAILED);
 410                          } else {
 411                              if (!isset($options['soft'])) {
 412                                  $this->log(0, "warning : bad md5sum for file $final_dest_file");
 413                              }
 414                          }
 415                      } else {
 416                          if (!isset($options['soft'])) {
 417                              $this->log(0, "warning : bad md5sum for file $final_dest_file");
 418                          }
 419                      }
 420                  }
 421              }
 422              // }}}
 423              // {{{ set file permissions
 424              if (!OS_WINDOWS) {
 425                  if ($atts['role'] == 'script') {
 426                      $mode = 0777 & ~(int)octdec($this->config->get('umask'));
 427                      $this->log(3, "+ chmod +x $dest_file");
 428                  } else {
 429                      $mode = 0666 & ~(int)octdec($this->config->get('umask'));
 430                  }
 431                  $this->addFileOperation("chmod", array($mode, $dest_file));
 432                  if (!@chmod($dest_file, $mode)) {
 433                      if (!isset($options['soft'])) {
 434                          $this->log(0, "failed to change mode of $dest_file");
 435                      }
 436                  }
 437              }
 438              // }}}
 439              $this->addFileOperation("rename", array($dest_file, $final_dest_file,
 440                  $atts['role'] == 'ext'));
 441          }
 442          // Store the full path where the file was installed for easy unistall
 443          $this->addFileOperation("installed_as", array($file, $installed_as,
 444                                  $save_destdir, dirname(substr($installedas_dest_file, strlen($save_destdir)))));
 445  
 446          //$this->log(2, "installed: $dest_file");
 447          return PEAR_INSTALLER_OK;
 448      }
 449  
 450      // }}}
 451      // {{{ _installFile2()
 452  
 453      /**
 454       * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
 455       * @param string filename
 456       * @param array attributes from <file> tag in package.xml
 457       * @param string path to install the file in
 458       * @param array options from command-line
 459       * @access private
 460       */
 461      function _installFile2(&$pkg, $file, $atts, $tmp_path, $options)
 462      {
 463          if (!isset($this->_registry)) {
 464              $this->_registry = &$this->config->getRegistry();
 465          }
 466  
 467          $channel = $pkg->getChannel();
 468          // {{{ assemble the destination paths
 469          if (!in_array($atts['attribs']['role'],
 470                PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
 471              return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
 472                      "' for file $file");
 473          }
 474          $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
 475          $err = $role->setup($this, $pkg, $atts['attribs'], $file);
 476          if (PEAR::isError($err)) {
 477              return $err;
 478          }
 479          if (!$role->isInstallable()) {
 480              return;
 481          }
 482          $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
 483          if (PEAR::isError($info)) {
 484              return $info;
 485          } else {
 486              list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
 487          }
 488          $final_dest_file = $installed_as = $dest_file;
 489          if (isset($this->_options['packagingroot'])) {
 490              $final_dest_file = $this->_prependPath($final_dest_file,
 491                  $this->_options['packagingroot']);
 492          }
 493          $dest_dir = dirname($final_dest_file);
 494          $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
 495          // }}}
 496  
 497          if (empty($this->_options['register-only'])) {
 498              if (!@is_dir($dest_dir)) {
 499                  if (!$this->mkDirHier($dest_dir)) {
 500                      return $this->raiseError("failed to mkdir $dest_dir",
 501                                               PEAR_INSTALLER_FAILED);
 502                  }
 503                  $this->log(3, "+ mkdir $dest_dir");
 504              }
 505          }
 506          $attribs = $atts['attribs'];
 507          unset($atts['attribs']);
 508          // pretty much nothing happens if we are only registering the install
 509          if (empty($this->_options['register-only'])) {
 510              if (!count($atts)) { // no tasks
 511                  if (!file_exists($orig_file)) {
 512                      return $this->raiseError("file $orig_file does not exist",
 513                                               PEAR_INSTALLER_FAILED);
 514                  }
 515                  if (!@copy($orig_file, $dest_file)) {
 516                      return $this->raiseError("failed to write $dest_file",
 517                                               PEAR_INSTALLER_FAILED);
 518                  }
 519                  $this->log(3, "+ cp $orig_file $dest_file");
 520                  if (isset($attribs['md5sum'])) {
 521                      $md5sum = md5_file($dest_file);
 522                  }
 523              } else { // file with tasks
 524                  if (!file_exists($orig_file)) {
 525                      return $this->raiseError("file $orig_file does not exist",
 526                                               PEAR_INSTALLER_FAILED);
 527                  }
 528                  if (function_exists('file_get_contents')) {
 529                      $contents = file_get_contents($orig_file);
 530                  } else {
 531                      $fp = fopen($orig_file, "r");
 532                      $contents = @fread($fp, filesize($orig_file)); // filesize can be 0
 533                      fclose($fp);
 534                  }
 535                  if ($contents === false) {
 536                      $contents = '';
 537                  }
 538                  if (isset($attribs['md5sum'])) {
 539                      $md5sum = md5($contents);
 540                  }
 541                  foreach ($atts as $tag => $raw) {
 542                      $tag = str_replace($pkg->getTasksNs() . ':', '', $tag);
 543                      $task = "PEAR_Task_$tag";
 544                      $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
 545                      if (!$task->isScript()) { // scripts are only handled after installation
 546                          $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
 547                          $res = $task->startSession($pkg, $contents, $final_dest_file);
 548                          if ($res === false) {
 549                              continue; // skip this file
 550                          }
 551                          if (PEAR::isError($res)) {
 552                              return $res;
 553                          }
 554                          $contents = $res; // save changes
 555                      }
 556                      $wp = @fopen($dest_file, "wb");
 557                      if (!is_resource($wp)) {
 558                          return $this->raiseError("failed to create $dest_file: $php_errormsg",
 559                                                   PEAR_INSTALLER_FAILED);
 560                      }
 561                      if (fwrite($wp, $contents) === false) {
 562                          return $this->raiseError("failed writing to $dest_file: $php_errormsg",
 563                                                   PEAR_INSTALLER_FAILED);
 564                      }
 565                      fclose($wp);
 566                  }
 567              }
 568              // {{{ check the md5
 569              if (isset($md5sum)) {
 570                  if (strtolower($md5sum) == strtolower($attribs['md5sum'])) {
 571                      $this->log(2, "md5sum ok: $final_dest_file");
 572                  } else {
 573                      if (empty($options['force'])) {
 574                          // delete the file
 575                          @unlink($dest_file);
 576                          if (!isset($options['ignore-errors'])) {
 577                              return $this->raiseError("bad md5sum for file $final_dest_file",
 578                                                       PEAR_INSTALLER_FAILED);
 579                          } else {
 580                              if (!isset($options['soft'])) {
 581                                  $this->log(0, "warning : bad md5sum for file $final_dest_file");
 582                              }
 583                          }
 584                      } else {
 585                          if (!isset($options['soft'])) {
 586                              $this->log(0, "warning : bad md5sum for file $final_dest_file");
 587                          }
 588                      }
 589                  }
 590              }
 591              // }}}
 592              // {{{ set file permissions
 593              if (!OS_WINDOWS) {
 594                  if ($role->isExecutable()) {
 595                      $mode = 0777 & ~(int)octdec($this->config->get('umask'));
 596                      $this->log(3, "+ chmod +x $dest_file");
 597                  } else {
 598                      $mode = 0666 & ~(int)octdec($this->config->get('umask'));
 599                  }
 600                  $this->addFileOperation("chmod", array($mode, $dest_file));
 601                  if (!@chmod($dest_file, $mode)) {
 602                      if (!isset($options['soft'])) {
 603                          $this->log(0, "failed to change mode of $dest_file");
 604                      }
 605                  }
 606              }
 607              // }}}
 608              $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
 609          }
 610          // Store the full path where the file was installed for easy uninstall
 611          $this->addFileOperation("installed_as", array($file, $installed_as,
 612                              $save_destdir, dirname(substr($dest_file, strlen($save_destdir)))));
 613  
 614          //$this->log(2, "installed: $dest_file");
 615          return PEAR_INSTALLER_OK;
 616      }
 617  
 618      // }}}
 619      // {{{ addFileOperation()
 620  
 621      /**
 622       * Add a file operation to the current file transaction.
 623       *
 624       * @see startFileTransaction()
 625       * @param string $type This can be one of:
 626       *    - rename:  rename a file ($data has 3 values)
 627       *    - backup:  backup an existing file ($data has 1 value)
 628       *    - removebackup:  clean up backups created during install ($data has 1 value)
 629       *    - chmod:   change permissions on a file ($data has 2 values)
 630       *    - delete:  delete a file ($data has 1 value)
 631       *    - rmdir:   delete a directory if empty ($data has 1 value)
 632       *    - installed_as: mark a file as installed ($data has 4 values).
 633       * @param array $data For all file operations, this array must contain the
 634       *    full path to the file or directory that is being operated on.  For
 635       *    the rename command, the first parameter must be the file to rename,
 636       *    the second its new name, the third whether this is a PHP extension.
 637       *
 638       *    The installed_as operation contains 4 elements in this order:
 639       *    1. Filename as listed in the filelist element from package.xml
 640       *    2. Full path to the installed file
 641       *    3. Full path from the php_dir configuration variable used in this
 642       *       installation
 643       *    4. Relative path from the php_dir that this file is installed in
 644       */
 645      function addFileOperation($type, $data)
 646      {
 647          if (!is_array($data)) {
 648              return $this->raiseError('Internal Error: $data in addFileOperation'
 649                  . ' must be an array, was ' . gettype($data));
 650          }
 651          if ($type == 'chmod') {
 652              $octmode = decoct($data[0]);
 653              $this->log(3, "adding to transaction: $type $octmode $data[1]");
 654          } else {
 655              $this->log(3, "adding to transaction: $type " . implode(" ", $data));
 656          }
 657          $this->file_operations[] = array($type, $data);
 658      }
 659  
 660      // }}}
 661      // {{{ startFileTransaction()
 662  
 663      function startFileTransaction($rollback_in_case = false)
 664      {
 665          if (count($this->file_operations) && $rollback_in_case) {
 666              $this->rollbackFileTransaction();
 667          }
 668          $this->file_operations = array();
 669      }
 670  
 671      // }}}
 672      // {{{ commitFileTransaction()
 673  
 674      function commitFileTransaction()
 675      {
 676          $n = count($this->file_operations);
 677          $this->log(2, "about to commit $n file operations");
 678          // {{{ first, check permissions and such manually
 679          $errors = array();
 680          foreach ($this->file_operations as $tr) {
 681              list($type, $data) = $tr;
 682              switch ($type) {
 683                  case 'rename':
 684                      if (!file_exists($data[0])) {
 685                          $errors[] = "cannot rename file $data[0], doesn't exist";
 686                      }
 687                      // check that dest dir. is writable
 688                      if (!is_writable(dirname($data[1]))) {
 689                          $errors[] = "permission denied ($type): $data[1]";
 690                      }
 691                      break;
 692                  case 'chmod':
 693                      // check that file is writable
 694                      if (!is_writable($data[1])) {
 695                          $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
 696                      }
 697                      break;
 698                  case 'delete':
 699                      if (!file_exists($data[0])) {
 700                          $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
 701                      }
 702                      // check that directory is writable
 703                      if (file_exists($data[0])) {
 704                          if (!is_writable(dirname($data[0]))) {
 705                              $errors[] = "permission denied ($type): $data[0]";
 706                          } else {
 707                              // make sure the file to be deleted can be opened for writing
 708                              $fp = false;
 709                              if (!is_dir($data[0]) && !($fp = @fopen($data[0], 'a'))) {
 710                                  $errors[] = "permission denied ($type): $data[0]";
 711                              } elseif ($fp) {
 712                                  fclose($fp);
 713                              }
 714                          }
 715                      }
 716                      break;
 717              }
 718  
 719          }
 720          // }}}
 721          $m = sizeof($errors);
 722          if ($m > 0) {
 723              foreach ($errors as $error) {
 724                  if (!isset($this->_options['soft'])) {
 725                      $this->log(1, $error);
 726                  }
 727              }
 728              if (!isset($this->_options['ignore-errors'])) {
 729                  return false;
 730              }
 731          }
 732          $this->_dirtree = array();
 733          // {{{ really commit the transaction
 734          foreach ($this->file_operations as $tr) {
 735              list($type, $data) = $tr;
 736              switch ($type) {
 737                  case 'backup':
 738                      @copy($data[0], $data[0] . '.bak');
 739                      $this->log(3, "+ backup $data[0] to $data[0].bak");
 740                      break;
 741                  case 'removebackup':
 742                      @unlink($data[0] . '.bak');
 743                      $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
 744                      break;
 745                  case 'rename':
 746                      $test = @unlink($data[1]);
 747                      if (!$test && file_exists($data[1])) {
 748                          if ($data[2]) {
 749                              $extra = ', this extension must be installed manually.  Rename to "' .
 750                                  basename($data[1]) . '"';
 751                          } else {
 752                              $extra = '';
 753                          }
 754                          if (!isset($this->_options['soft'])) {
 755                              $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
 756                                  $data[0] . $extra);
 757                          }
 758                          if (!isset($this->_options['ignore-errors'])) {
 759                              return false;
 760                          }
 761                      }
 762                      @rename($data[0], $data[1]);
 763                      $this->log(3, "+ mv $data[0] $data[1]");
 764                      break;
 765                  case 'chmod':
 766                      @chmod($data[1], $data[0]);
 767                      $octmode = decoct($data[0]);
 768                      $this->log(3, "+ chmod $octmode $data[1]");
 769                      break;
 770                  case 'delete':
 771                      @unlink($data[0]);
 772                      $this->log(3, "+ rm $data[0]");
 773                      break;
 774                  case 'rmdir':
 775                      @rmdir($data[0]);
 776                      $this->log(3, "+ rmdir $data[0]");
 777                      break;
 778                  case 'installed_as':
 779                      $this->pkginfo->setInstalledAs($data[0], $data[1]);
 780                      if (!isset($this->_dirtree[dirname($data[1])])) {
 781                          $this->_dirtree[dirname($data[1])] = true;
 782                          $this->pkginfo->setDirtree(dirname($data[1]));
 783  
 784                          while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
 785                                && $data[3] != '.') {
 786                              $this->pkginfo->setDirtree($pp =
 787                                  $this->_prependPath($data[3], $data[2]));
 788                              $this->_dirtree[$pp] = true;
 789                              $data[3] = dirname($data[3]);
 790                          }
 791                      }
 792                      break;
 793              }
 794          }
 795          // }}}
 796          $this->log(2, "successfully committed $n file operations");
 797          $this->file_operations = array();
 798          return true;
 799      }
 800  
 801      // }}}
 802      // {{{ rollbackFileTransaction()
 803  
 804      function rollbackFileTransaction()
 805      {
 806          $n = count($this->file_operations);
 807          $this->log(2, "rolling back $n file operations");
 808          foreach ($this->file_operations as $tr) {
 809              list($type, $data) = $tr;
 810              switch ($type) {
 811                  case 'backup':
 812                      if (file_exists($data[0] . '.bak')) {
 813                          @unlink($data[0]);
 814                          @copy($data[0] . '.bak', $data[0]);
 815                          $this->log(3, "+ restore $data[0] from $data[0].bak");
 816                      }
 817                      break;
 818                  case 'rename':
 819                      @unlink($data[0]);
 820                      $this->log(3, "+ rm $data[0]");
 821                      break;
 822                  case 'mkdir':
 823                      @rmdir($data[0]);
 824                      $this->log(3, "+ rmdir $data[0]");
 825                      break;
 826                  case 'chmod':
 827                      break;
 828                  case 'delete':
 829                      break;
 830                  case 'installed_as':
 831                      $this->pkginfo->setInstalledAs($data[0], false);
 832                      break;
 833              }
 834          }
 835          $this->pkginfo->resetDirtree();
 836          $this->file_operations = array();
 837      }
 838  
 839      // }}}
 840      // {{{ mkDirHier($dir)
 841  
 842      function mkDirHier($dir)
 843      {
 844          $this->addFileOperation('mkdir', array($dir));
 845          return parent::mkDirHier($dir);
 846      }
 847  
 848      // }}}
 849      // {{{ download()
 850  
 851      /**
 852       * Download any files and their dependencies, if necessary
 853       *
 854       * @param array a mixed list of package names, local files, or package.xml
 855       * @param PEAR_Config
 856       * @param array options from the command line
 857       * @param array this is the array that will be populated with packages to
 858       *              install.  Format of each entry:
 859       *
 860       * <code>
 861       * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
 862       *    'info' => array() // parsed package.xml
 863       * );
 864       * </code>
 865       * @param array this will be populated with any error messages
 866       * @param false private recursion variable
 867       * @param false private recursion variable
 868       * @param false private recursion variable
 869       * @deprecated in favor of PEAR_Downloader
 870       */
 871      function download($packages, $options, &$config, &$installpackages,
 872                        &$errors, $installed = false, $willinstall = false, $state = false)
 873      {
 874          // trickiness: initialize here
 875          parent::PEAR_Downloader($this->ui, $options, $config);
 876          $ret = parent::download($packages);
 877          $errors = $this->getErrorMsgs();
 878          $installpackages = $this->getDownloadedPackages();
 879          trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
 880                        "in favor of PEAR_Downloader class", E_USER_WARNING);
 881          return $ret;
 882      }
 883  
 884      // }}}
 885      // {{{ _parsePackageXml()
 886  
 887      function _parsePackageXml(&$descfile, &$tmpdir)
 888      {
 889          if (substr($descfile, -4) == '.xml') {
 890              $tmpdir = false;
 891          } else {
 892              // {{{ Decompress pack in tmp dir -------------------------------------
 893  
 894              // To allow relative package file names
 895              $descfile = realpath($descfile);
 896  
 897              if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
 898                  return $tmpdir;
 899              }
 900              $this->log(3, '+ tmp dir created at ' . $tmpdir);
 901              // }}}
 902          }
 903          // Parse xml file -----------------------------------------------
 904          $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir);
 905          PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 906          $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
 907          PEAR::staticPopErrorHandling();
 908          if (PEAR::isError($p)) {
 909              if (is_array($p->getUserInfo())) {
 910                  foreach ($p->getUserInfo() as $err) {
 911                      $loglevel = $err['level'] == 'error' ? 0 : 1;
 912                      if (!isset($this->_options['soft'])) {
 913                          $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
 914                      }
 915                  }
 916              }
 917              return $this->raiseError('Installation failed: invalid package file');
 918          } else {
 919              $descfile = $p->getPackageFile();
 920          }
 921          return $p;
 922      }
 923  
 924      // }}}
 925      /**
 926       * Set the list of PEAR_Downloader_Package objects to allow more sane
 927       * dependency validation
 928       * @param array
 929       */
 930      function setDownloadedPackages(&$pkgs)
 931      {
 932          PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 933          $err = $this->analyzeDependencies($pkgs);
 934          PEAR::popErrorHandling();
 935          if (PEAR::isError($err)) {
 936              return $err;
 937          }
 938          $this->_downloadedPackages = &$pkgs;
 939      }
 940  
 941      /**
 942       * Set the list of PEAR_Downloader_Package objects to allow more sane
 943       * dependency validation
 944       * @param array
 945       */
 946      function setUninstallPackages(&$pkgs)
 947      {
 948          $this->_downloadedPackages = &$pkgs;
 949      }
 950  
 951      function getInstallPackages()
 952      {
 953          return $this->_downloadedPackages;
 954      }
 955  
 956      // {{{ install()
 957  
 958      /**
 959       * Installs the files within the package file specified.
 960       *
 961       * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
 962       *        or a pre-initialized packagefile object
 963       * @param array $options
 964       * recognized options:
 965       * - installroot   : optional prefix directory for installation
 966       * - force         : force installation
 967       * - register-only : update registry but don't install files
 968       * - upgrade       : upgrade existing install
 969       * - soft          : fail silently
 970       * - nodeps        : ignore dependency conflicts/missing dependencies
 971       * - alldeps       : install all dependencies
 972       * - onlyreqdeps   : install only required dependencies
 973       *
 974       * @return array|PEAR_Error package info if successful
 975       */
 976  
 977      function install($pkgfile, $options = array())
 978      {
 979          $this->_options = $options;
 980          $this->_registry = &$this->config->getRegistry();
 981          if (is_object($pkgfile)) {
 982              $dlpkg = &$pkgfile;
 983              $pkg = $pkgfile->getPackageFile();
 984              $pkgfile = $pkg->getArchiveFile();
 985              $descfile = $pkg->getPackageFile();
 986              $tmpdir = dirname($descfile);
 987          } else {
 988              $descfile = $pkgfile;
 989              $tmpdir = '';
 990              if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) {
 991                  return $pkg;
 992              }
 993          }
 994  
 995          if (realpath($descfile) != realpath($pkgfile)) {
 996              $tar = new Archive_Tar($pkgfile);
 997              if (!@$tar->extract($tmpdir)) {
 998                  return $this->raiseError("unable to unpack $pkgfile");
 999              }
1000          }
1001  
1002          $pkgname = $pkg->getName();
1003          $channel = $pkg->getChannel();
1004          if (isset($this->_options['packagingroot'])) {
1005              $packrootphp_dir = $this->_prependPath(
1006                  $this->config->get('php_dir', null, $channel),
1007                  $this->_options['packagingroot']);
1008          }
1009  
1010          if (isset($options['installroot'])) {
1011              $this->config->setInstallRoot($options['installroot']);
1012              $this->_registry = &$this->config->getRegistry();
1013              $installregistry = &$this->_registry;
1014              $this->installroot = ''; // all done automagically now
1015              $php_dir = $this->config->get('php_dir', null, $channel);
1016          } else {
1017              $this->config->setInstallRoot(false);
1018              $this->_registry = &$this->config->getRegistry();
1019              if (isset($this->_options['packagingroot'])) {
1020                  $installregistry = &new PEAR_Registry($packrootphp_dir);
1021                  $php_dir = $packrootphp_dir;
1022              } else {
1023                  $installregistry = &$this->_registry;
1024                  $php_dir = $this->config->get('php_dir', null, $channel);
1025              }
1026              $this->installroot = '';
1027          }
1028  
1029          // {{{ checks to do when not in "force" mode
1030          if (empty($options['force']) && @is_dir($this->config->get('php_dir'))) {
1031              $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
1032              $instfilelist = $pkg->getInstallationFileList(true);
1033              if (PEAR::isError($instfilelist)) {
1034                  return $instfilelist;
1035              }
1036              $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
1037              if (PEAR::isError($test)) {
1038                  return $test;
1039              }
1040              if (sizeof($test)) {
1041                  $pkgs = $this->getInstallPackages();
1042                  $found = false;
1043                  foreach ($pkgs as $param) {
1044                      if ($pkg->isSubpackageOf($param)) {
1045                          $found = true;
1046                          break;
1047                      }
1048                  }
1049                  if ($found) {
1050                      // subpackages can conflict with earlier versions of parent packages
1051                      $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
1052                      $tmp = $test;
1053                      foreach ($tmp as $file => $info) {
1054                          if (is_array($info)) {
1055                              if (strtolower($info[1]) == strtolower($param->getPackage()) &&
1056                                    strtolower($info[0]) == strtolower($param->getChannel())) {
1057                                  unset($test[$file]);
1058                                  unset($parentreg['filelist'][$file]);
1059                              }
1060                          } else {
1061                              if (strtolower($param->getChannel()) != 'pear.php.net') {
1062                                  continue;
1063                              }
1064                              if (strtolower($info) == strtolower($param->getPackage())) {
1065                                  unset($test[$file]);
1066                                  unset($parentreg['filelist'][$file]);
1067                              }
1068                          }
1069                      }
1070                      $pfk = &new PEAR_PackageFile($this->config);
1071                      $parentpkg = &$pfk->fromArray($parentreg);
1072                      $installregistry->updatePackage2($parentpkg);
1073                  }
1074                  if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
1075                      $tmp = $test;
1076                      foreach ($tmp as $file => $info) {
1077                          if (is_string($info)) {
1078                              // pear.php.net packages are always stored as strings
1079                              if (strtolower($info) == strtolower($param->getPackage())) {
1080                                  // upgrading existing package
1081                                  unset($test[$file]);
1082                              }
1083                          }
1084                      }
1085                  }
1086                  if (sizeof($test)) {
1087                      $msg = "$channel/$pkgname: conflicting files found:\n";
1088                      $longest = max(array_map("strlen", array_keys($test)));
1089                      $fmt = "%$longest}s (%s)\n";
1090                      foreach ($test as $file => $info) {
1091                          if (!is_array($info)) {
1092                              $info = array('pear.php.net', $info);
1093                          }
1094                          $info = $info[0] . '/' . $info[1];
1095                          $msg .= sprintf($fmt, $file, $info);
1096                      }
1097                      if (!isset($options['ignore-errors'])) {
1098                          return $this->raiseError($msg);
1099                      } else {
1100                          if (!isset($options['soft'])) {
1101                              $this->log(0, "WARNING: $msg");
1102                          }
1103                      }
1104                  }
1105              }
1106          }
1107          // }}}
1108  
1109          $this->startFileTransaction();
1110  
1111          if (empty($options['upgrade']) && empty($options['soft'])) {
1112              // checks to do only when installing new packages
1113              if ($channel == 'pecl.php.net') {
1114                  $test = $installregistry->packageExists($pkgname, $channel);
1115                  if (!$test) {
1116                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1117                  }
1118              } else {
1119                  $test = $installregistry->packageExists($pkgname, $channel);
1120              }
1121              if (empty($options['force']) && $test) {
1122                  return $this->raiseError("$channel/$pkgname is already installed");
1123              }
1124          } else {
1125              $usechannel = $channel;
1126              if ($channel == 'pecl.php.net') {
1127                  $test = $installregistry->packageExists($pkgname, $channel);
1128                  if (!$test) {
1129                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1130                      $usechannel = 'pear.php.net';
1131                  }
1132              } else {
1133                  $test = $installregistry->packageExists($pkgname, $channel);
1134              }
1135              if ($test) {
1136                  $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
1137                  $v2 = $pkg->getVersion();
1138                  $cmp = version_compare("$v1", "$v2", 'gt');
1139                  if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
1140                      return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
1141                  }
1142                  if (empty($options['register-only'])) {
1143                      // when upgrading, remove old release's files first:
1144                      if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
1145                            true))) {
1146                          if (!isset($options['ignore-errors'])) {
1147                              return $this->raiseError($err);
1148                          } else {
1149                              if (!isset($options['soft'])) {
1150                                  $this->log(0, 'WARNING: ' . $err->getMessage());
1151                              }
1152                          }
1153                      } else {
1154                          $backedup = $err;
1155                      }
1156                  }
1157              }
1158          }
1159  
1160          // {{{ Copy files to dest dir ---------------------------------------
1161  
1162          // info from the package it self we want to access from _installFile
1163          $this->pkginfo = &$pkg;
1164          // used to determine whether we should build any C code
1165          $this->source_files = 0;
1166  
1167          $savechannel = $this->config->get('default_channel');
1168          if (empty($options['register-only']) && !is_dir($php_dir)) {
1169              if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
1170                  return $this->raiseError("no installation destination directory '$php_dir'\n");
1171              }
1172          }
1173  
1174          $tmp_path = dirname($descfile);
1175          if (substr($pkgfile, -4) != '.xml') {
1176              $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
1177          }
1178  
1179          $this->configSet('default_channel', $channel);
1180          // {{{ install files
1181          
1182          if ($pkg->getPackagexmlVersion() == '2.0') {
1183              $filelist = $pkg->getInstallationFilelist();
1184          } else {
1185              $filelist = $pkg->getFileList();
1186          }
1187          if (PEAR::isError($filelist)) {
1188              return $filelist;
1189          }
1190          $pkg->resetFilelist();
1191          $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
1192              'version', $pkg->getChannel()));
1193          foreach ($filelist as $file => $atts) {
1194              if ($pkg->getPackagexmlVersion() == '1.0') {
1195                  $this->expectError(PEAR_INSTALLER_FAILED);
1196                  $res = $this->_installFile($file, $atts, $tmp_path, $options);
1197                  $this->popExpect();
1198              } else {
1199                  $this->expectError(PEAR_INSTALLER_FAILED);
1200                  $res = $this->_installFile2($pkg, $file, $atts, $tmp_path, $options);
1201                  $this->popExpect();
1202              }
1203              if (PEAR::isError($res)) {
1204                  if (empty($options['ignore-errors'])) {
1205                      $this->rollbackFileTransaction();
1206                      if ($res->getMessage() == "file does not exist") {
1207                          $this->raiseError("file $file in package.xml does not exist");
1208                      }
1209                      return $this->raiseError($res);
1210                  } else {
1211                      if (!isset($options['soft'])) {
1212                          $this->log(0, "Warning: " . $res->getMessage());
1213                      }
1214                  }
1215              }
1216              if ($res == PEAR_INSTALLER_OK) {
1217                  // Register files that were installed
1218                  $pkg->installedFile($file, $atts);
1219              }
1220          }
1221          // }}}
1222  
1223          // {{{ compile and install source files
1224          if ($this->source_files > 0 && empty($options['nobuild'])) {
1225              if (PEAR::isError($err =
1226                    $this->_compileSourceFiles($savechannel, $pkg))) {
1227                  return $err;
1228              }
1229          }
1230          // }}}
1231  
1232          if (isset($backedup)) {
1233              $this->_removeBackups($backedup);
1234          }
1235          if (!$this->commitFileTransaction()) {
1236              $this->rollbackFileTransaction();
1237              $this->configSet('default_channel', $savechannel);
1238              return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
1239          }
1240          // }}}
1241  
1242          $ret = false;
1243          $installphase = 'install';
1244          $oldversion = false;
1245          // {{{ Register that the package is installed -----------------------
1246          if (empty($options['upgrade'])) {
1247              // if 'force' is used, replace the info in registry
1248              $usechannel = $channel;
1249              if ($channel == 'pecl.php.net') {
1250                  $test = $installregistry->packageExists($pkgname, $channel);
1251                  if (!$test) {
1252                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1253                      $usechannel = 'pear.php.net';
1254                  }
1255              } else {
1256                  $test = $installregistry->packageExists($pkgname, $channel);
1257              }
1258              if (!empty($options['force']) && $test) {
1259                  $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
1260                  $installregistry->deletePackage($pkgname, $usechannel);
1261              }
1262              $ret = $installregistry->addPackage2($pkg);
1263          } else {
1264              $usechannel = $channel;
1265              if ($channel == 'pecl.php.net') {
1266                  $test = $installregistry->packageExists($pkgname, $channel);
1267                  if (!$test) {
1268                      $test = $installregistry->packageExists($pkgname, 'pear.php.net');
1269                      $usechannel = 'pear.php.net';
1270                  }
1271              } else {
1272                  $test = $installregistry->packageExists($pkgname, $channel);
1273              }
1274              // new: upgrade installs a package if it isn't installed
1275              if (!$test) {
1276                  $ret = $installregistry->addPackage2($pkg);
1277              } else {
1278                  if ($usechannel != $channel) {
1279                      $installregistry->deletePackage($pkgname, $usechannel);
1280                      $ret = $installregistry->addPackage2($pkg);
1281                  } else {
1282                      $ret = $installregistry->updatePackage2($pkg);
1283                  }
1284                  $installphase = 'upgrade';
1285              }
1286          }
1287          if (!$ret) {
1288              $this->configSet('default_channel', $savechannel);
1289              return $this->raiseError("Adding package $channel/$pkgname to registry failed");
1290          }
1291          // }}}
1292          $this->configSet('default_channel', $savechannel);
1293          if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
1294              if (PEAR_Task_Common::hasPostinstallTasks()) {
1295                  PEAR_Task_Common::runPostinstallTasks($installphase);
1296              }
1297          }
1298          return $pkg->toArray(true);
1299      }
1300  
1301      // }}}
1302  
1303      // {{{ _compileSourceFiles()
1304      /**
1305       * @param string
1306       * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1307       */
1308      function _compileSourceFiles($savechannel, &$filelist)
1309      {
1310          require_once 'PEAR/Builder.php';
1311          $this->log(1, "$this->source_files source files, building");
1312          $bob = &new PEAR_Builder($this->ui);
1313          $bob->debug = $this->debug;
1314          $built = $bob->build($filelist, array(&$this, '_buildCallback'));
1315          if (PEAR::isError($built)) {
1316              $this->rollbackFileTransaction();
1317              $this->configSet('default_channel', $savechannel);
1318              return $built;
1319          }
1320          $this->log(1, "\nBuild process completed successfully");
1321          foreach ($built as $ext) {
1322              $bn = basename($ext['file']);
1323              list($_ext_name, $_ext_suff) = explode('.', $bn);
1324              if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
1325                  if (extension_loaded($_ext_name)) {
1326                      $this->raiseError("Extension '$_ext_name' already loaded. " .
1327                                        'Please unload it in your php.ini file ' .
1328                                        'prior to install or upgrade');
1329                  }
1330                  $role = 'ext';
1331              } else {
1332                  $role = 'src';
1333              }
1334              $dest = $ext['dest'];
1335              $this->log(1, "Installing '$ext[file]'");
1336              $packagingroot = '';
1337              if (isset($this->_options['packagingroot'])) {
1338                  $packagingroot = $this->_options['packagingroot'];
1339              }
1340              $copyto = $this->_prependPath($dest, $packagingroot);
1341              $copydir = dirname($copyto);
1342              // pretty much nothing happens if we are only registering the install
1343              if (empty($this->_options['register-only'])) {
1344                  if (!@is_dir($copydir)) {
1345                      if (!$this->mkDirHier($copydir)) {
1346                          return $this->raiseError("failed to mkdir $copydir",
1347                              PEAR_INSTALLER_FAILED);
1348                      }
1349                      $this->log(3, "+ mkdir $copydir");
1350                  }
1351                  if (!@copy($ext['file'], $copyto)) {
1352                      return $this->raiseError("failed to write $copyto", PEAR_INSTALLER_FAILED);
1353                  }
1354                  $this->log(3, "+ cp $ext[file] $copyto");
1355                  if (!OS_WINDOWS) {
1356                      $mode = 0666 & ~(int)octdec($this->config->get('umask'));
1357                      $this->addFileOperation('chmod', array($mode, $copyto));
1358                      if (!@chmod($copyto, $mode)) {
1359                          $this->log(0, "failed to change mode of $copyto");
1360                      }
1361                  }
1362                  $this->addFileOperation('rename', array($ext['file'], $copyto));
1363              }
1364  
1365              if ($filelist->getPackageXmlVersion() == '1.0') {
1366                  $filelist->installedFile($bn, array(
1367                      'role' => $role,
1368                      'name' => $bn,
1369                      'installed_as' => $dest,
1370                      'php_api' => $ext['php_api'],
1371                      'zend_mod_api' => $ext['zend_mod_api'],
1372                      'zend_ext_api' => $ext['zend_ext_api'],
1373                      ));
1374              } else {
1375                  $filelist->installedFile($bn, array('attribs' => array(
1376                      'role' => $role,
1377                      'name' => $bn,
1378                      'installed_as' => $dest,
1379                      'php_api' => $ext['php_api'],
1380                      'zend_mod_api' => $ext['zend_mod_api'],
1381                      'zend_ext_api' => $ext['zend_ext_api'],
1382                      )));
1383              }
1384          }
1385      }
1386  
1387      // }}}
1388      function &getUninstallPackages()
1389      {
1390          return $this->_downloadedPackages;
1391      }
1392      // {{{ uninstall()
1393  
1394      /**
1395       * Uninstall a package
1396       *
1397       * This method removes all files installed by the application, and then
1398       * removes any empty directories.
1399       * @param string package name
1400       * @param array Command-line options.  Possibilities include:
1401       *
1402       *              - installroot: base installation dir, if not the default
1403       *              - nodeps: do not process dependencies of other packages to ensure
1404       *                        uninstallation does not break things
1405       */
1406      function uninstall($package, $options = array())
1407      {
1408          if (isset($options['installroot'])) {
1409              $this->config->setInstallRoot($options['installroot']);
1410              $this->installroot = '';
1411          } else {
1412              $this->config->setInstallRoot('');
1413              $this->installroot = '';
1414          }
1415          $this->_registry = &$this->config->getRegistry();
1416          if (is_object($package)) {
1417              $channel = $package->getChannel();
1418              $pkg = $package;
1419              $package = $pkg->getPackage();
1420          } else {
1421              $pkg = false;
1422              $info = $this->_registry->parsePackageName($package,
1423                  $this->config->get('default_channel'));
1424              $channel = $info['channel'];
1425              $package = $info['package'];
1426          }
1427          $savechannel = $this->config->get('default_channel');
1428          $this->configSet('default_channel', $channel);
1429          if (!is_object($pkg)) {
1430              $pkg = $this->_registry->getPackage($package, $channel);
1431          }
1432          if (!$pkg) {
1433              $this->configSet('default_channel', $savechannel);
1434              return $this->raiseError($this->_registry->parsedPackageNameToString(
1435                  array(
1436                      'channel' => $channel,
1437                      'package' => $package
1438                  ), true) . ' not installed');
1439          }
1440          if ($pkg->getInstalledBinary()) {
1441              // this is just an alias for a binary package
1442              return $this->_registry->deletePackage($package, $channel);
1443          }
1444          $filelist = $pkg->getFilelist();
1445          PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
1446          if (!class_exists('PEAR_Dependency2')) {
1447              require_once 'PEAR/Dependency2.php';
1448          }
1449          $depchecker = &new PEAR_Dependency2($this->config, $options, 
1450              array('channel' => $channel, 'package' => $package),
1451              PEAR_VALIDATE_UNINSTALLING);
1452          $e = $depchecker->validatePackageUninstall($this);
1453          PEAR::staticPopErrorHandling();
1454          if (PEAR::isError($e)) {
1455              if (!isset($options['ignore-errors'])) {
1456                  return $this->raiseError($e);
1457              } else {
1458                  if (!isset($options['soft'])) {
1459                      $this->log(0, 'WARNING: ' . $e->getMessage());
1460                  }
1461              }
1462          } elseif (is_array($e)) {
1463              if (!isset($options['soft'])) {
1464                  $this->log(0, $e[0]);
1465              }
1466          }
1467          // {{{ Delete the files
1468          $this->startFileTransaction();
1469          PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1470          if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
1471              PEAR::popErrorHandling();
1472              $this->rollbackFileTransaction();
1473              $this->configSet('default_channel', $savechannel);
1474              if (!isset($options['ignore-errors'])) {
1475                  return $this->raiseError($err);
1476              } else {
1477                  if (!isset($options['soft'])) {
1478                      $this->log(0, 'WARNING: ' . $err->getMessage());
1479                  }
1480              }
1481          } else {
1482              PEAR::popErrorHandling();
1483          }
1484          if (!$this->commitFileTransaction()) {
1485              $this->rollbackFileTransaction();
1486              if (!isset($options['ignore-errors'])) {
1487                  return $this->raiseError("uninstall failed");
1488              } elseif (!isset($options['soft'])) {
1489                  $this->log(0, 'WARNING: uninstall failed');
1490              }
1491          } else {
1492              $this->startFileTransaction();
1493              if ($dirtree = $pkg->getDirTree()) {
1494                  // attempt to delete empty directories
1495                  uksort($dirtree, array($this, '_sortDirs'));
1496                  foreach($dirtree as $dir => $notused) {
1497                      $this->addFileOperation('rmdir', array($dir));
1498                  }
1499              } else {
1500                  $this->configSet('default_channel', $savechannel);
1501                  return $this->_registry->deletePackage($package, $channel);
1502              }
1503              if (!$this->commitFileTransaction()) {
1504                  $this->rollbackFileTransaction();
1505              }
1506          }
1507          // }}}
1508  
1509          $this->configSet('default_channel', $savechannel);
1510          // Register that the package is no longer installed
1511          return $this->_registry->deletePackage($package, $channel);
1512      }
1513  
1514      /**
1515       * Sort a list of arrays of array(downloaded packagefilename) by dependency.
1516       *
1517       * It also removes duplicate dependencies
1518       * @param array an array of PEAR_PackageFile_v[1/2] objects
1519       * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
1520       */
1521      function sortPackagesForUninstall(&$packages)
1522      {
1523          $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
1524          if (PEAR::isError($this->_dependencyDB)) {
1525              return $this->_dependencyDB;
1526          }
1527          usort($packages, array(&$this, '_sortUninstall'));
1528      }
1529  
1530      function _sortUninstall($a, $b)
1531      {
1532          if (!$a->getDeps() && !$b->getDeps()) {
1533              return 0; // neither package has dependencies, order is insignificant
1534          }
1535          if ($a->getDeps() && !$b->getDeps()) {
1536              return -1; // $a must be installed after $b because $a has dependencies
1537          }
1538          if (!$a->getDeps() && $b->getDeps()) {
1539              return 1; // $b must be installed after $a because $b has dependencies
1540          }
1541          // both packages have dependencies
1542          if ($this->_dependencyDB->dependsOn($a, $b)) {
1543              return -1;
1544          }
1545          if ($this->_dependencyDB->dependsOn($b, $a)) {
1546              return 1;
1547          }
1548          return 0;
1549      }
1550  
1551      // }}}
1552      // {{{ _sortDirs()
1553      function _sortDirs($a, $b)
1554      {
1555          if (strnatcmp($a, $b) == -1) return 1;
1556          if (strnatcmp($a, $b) == 1) return -1;
1557          return 0;
1558      }
1559  
1560      // }}}
1561  
1562      // {{{ _buildCallback()
1563  
1564      function _buildCallback($what, $data)
1565      {
1566          if (($what == 'cmdoutput' && $this->debug > 1) ||
1567              ($what == 'output' && $this->debug > 0)) {
1568              $this->ui->outputData(rtrim($data), 'build');
1569          }
1570      }
1571  
1572      // }}}
1573  }
1574  
1575  // {{{ md5_file() utility function
1576  if (!function_exists("md5_file")) {
1577      function md5_file($filename) {
1578          $fp = fopen($filename, "r");
1579          if (!$fp) return null;
1580          if (function_exists('file_get_contents')) {
1581              fclose($fp);
1582              $contents = file_get_contents($filename);
1583          } else {
1584              $contents = fread($fp, filesize($filename));
1585              fclose($fp);
1586          }
1587          return md5($contents);
1588      }
1589  }
1590  // }}}
1591  
1592  ?>


Généré le : Wed Nov 21 12:27:40 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics