PEAR
[ class tree: PEAR ] [ index: PEAR ] [ all elements ]

Source for file Installer.php

Documentation is available at Installer.php

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

Documentation generated on Wed, 06 Jul 2011 23:30:55 +0000 by phpDocumentor 1.4.3. PEAR Logo Copyright © PHP Group 2004.