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

Source for file Downloader.php

Documentation is available at Downloader.php

  1. <?php
  2. /**
  3.  * PEAR_Downloader, the PEAR Installer's download utility class
  4.  *
  5.  * PHP versions 4 and 5
  6.  *
  7.  * @category   pear
  8.  * @package    PEAR
  9.  * @author     Greg Beaver <cellog@php.net>
  10.  * @author     Stig Bakken <ssb@php.net>
  11.  * @author     Tomas V. V. Cox <cox@idecnet.com>
  12.  * @author     Martin Jansen <mj@php.net>
  13.  * @copyright  1997-2009 The Authors
  14.  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
  15.  * @version    CVS: $Id: Downloader.php 313024 2011-07-06 19:51:24Z dufuz $
  16.  * @link       http://pear.php.net/package/PEAR
  17.  * @since      File available since Release 1.3.0
  18.  */
  19.  
  20. /**
  21.  * Needed for constants, extending
  22.  */
  23. require_once 'PEAR/Common.php';
  24.  
  25. define('PEAR_INSTALLER_OK',       1);
  26. define('PEAR_INSTALLER_FAILED',   0);
  27. define('PEAR_INSTALLER_SKIPPED'-1);
  28. define('PEAR_INSTALLER_ERROR_NO_PREF_STATE'2);
  29.  
  30. /**
  31.  * Administration class used to download anything from the internet (PEAR Packages,
  32.  * static URLs, xml files)
  33.  *
  34.  * @category   pear
  35.  * @package    PEAR
  36.  * @author     Greg Beaver <cellog@php.net>
  37.  * @author     Stig Bakken <ssb@php.net>
  38.  * @author     Tomas V. V. Cox <cox@idecnet.com>
  39.  * @author     Martin Jansen <mj@php.net>
  40.  * @copyright  1997-2009 The Authors
  41.  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
  42.  * @version    Release: 1.9.4
  43.  * @link       http://pear.php.net/package/PEAR
  44.  * @since      Class available since Release 1.3.0
  45.  */
  46. class PEAR_Downloader extends PEAR_Common
  47. {
  48.     /**
  49.      * @var PEAR_Registry 
  50.      * @access private
  51.      */
  52.     var $_registry;
  53.  
  54.     /**
  55.      * Preferred Installation State (snapshot, devel, alpha, beta, stable)
  56.      * @var string|null
  57.      * @access private
  58.      */
  59.     var $_preferredState;
  60.  
  61.     /**
  62.      * Options from command-line passed to Install.
  63.      *
  64.      * Recognized options:<br />
  65.      *  - onlyreqdeps   : install all required dependencies as well
  66.      *  - alldeps       : install all dependencies, including optional
  67.      *  - installroot   : base relative path to install files in
  68.      *  - force         : force a download even if warnings would prevent it
  69.      *  - nocompress    : download uncompressed tarballs
  70.      * @see PEAR_Command_Install
  71.      * @access private
  72.      * @var array 
  73.      */
  74.     var $_options;
  75.  
  76.     /**
  77.      * Downloaded Packages after a call to download().
  78.      *
  79.      * Format of each entry:
  80.      *
  81.      * <code>
  82.      * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  83.      *    'info' => array() // parsed package.xml
  84.      * );
  85.      * </code>
  86.      * @access private
  87.      * @var array 
  88.      */
  89.     var $_downloadedPackages = array();
  90.  
  91.     /**
  92.      * Packages slated for download.
  93.      *
  94.      * This is used to prevent downloading a package more than once should it be a dependency
  95.      * for two packages to be installed.
  96.      * Format of each entry:
  97.      *
  98.      * <pre>
  99.      * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
  100.      * );
  101.      * </pre>
  102.      * @access private
  103.      * @var array 
  104.      */
  105.     var $_toDownload = array();
  106.  
  107.     /**
  108.      * Array of every package installed, with names lower-cased.
  109.      *
  110.      * Format:
  111.      * <code>
  112.      * array('package1' => 0, 'package2' => 1, );
  113.      * </code>
  114.      * @var array 
  115.      */
  116.     var $_installed = array();
  117.  
  118.     /**
  119.      * @var array 
  120.      * @access private
  121.      */
  122.     var $_errorStack = array();
  123.  
  124.     /**
  125.      * @var boolean 
  126.      * @access private
  127.      */
  128.     var $_internalDownload = false;
  129.  
  130.     /**
  131.      * Temporary variable used in sorting packages by dependency in {@link sortPkgDeps()}
  132.      * @var array 
  133.      * @access private
  134.      */
  135.     var $_packageSortTree;
  136.  
  137.     /**
  138.      * Temporary directory, or configuration value where downloads will occur
  139.      * @var string 
  140.      */
  141.     var $_downloadDir;
  142.  
  143.     /**
  144.      * @param PEAR_Frontend_* 
  145.      * @param array 
  146.      * @param PEAR_Config 
  147.      */
  148.     function PEAR_Downloader(&$ui$options&$config)
  149.     {
  150.         parent::PEAR_Common();
  151.         $this->_options $options;
  152.         $this->config = &$config;
  153.         $this->_preferredState $this->config->get('preferred_state');
  154.         $this->ui = &$ui;
  155.         if (!$this->_preferredState{
  156.             // don't inadvertantly use a non-set preferred_state
  157.             $this->_preferredState = null;
  158.         }
  159.  
  160.         if (isset($this->_options['installroot'])) {
  161.             $this->config->setInstallRoot($this->_options['installroot']);
  162.         }
  163.         $this->_registry &$config->getRegistry();
  164.  
  165.         if (isset($this->_options['alldeps']|| isset($this->_options['onlyreqdeps'])) {
  166.             $this->_installed $this->_registry->listAllPackages();
  167.             foreach ($this->_installed as $key => $unused{
  168.                 if (!count($unused)) {
  169.                     continue;
  170.                 }
  171.                 $strtolower create_function('$a','return strtolower($a);');
  172.                 array_walk($this->_installed[$key]$strtolower);
  173.             }
  174.         }
  175.     }
  176.  
  177.     /**
  178.      * Attempt to discover a channel's remote capabilities from
  179.      * its server name
  180.      * @param string 
  181.      * @return boolean 
  182.      */
  183.     function discover($channel)
  184.     {
  185.         $this->log(1'Attempting to discover channel "' $channel '"...');
  186.         $callback $this->ui ? array(&$this'_downloadCallback': null;
  187.         if (!class_exists('System')) {
  188.             require_once 'System.php';
  189.         }
  190.  
  191.         $tmpdir $this->config->get('temp_dir');
  192.         $tmp System::mktemp('-d -t "' $tmpdir '"');
  193.         $a   $this->downloadHttp('http://' $channel '/channel.xml'$this->ui$tmp$callbackfalse);
  194.         PEAR::popErrorHandling();
  195.         if (PEAR::isError($a)) {
  196.             // Attempt to fallback to https automatically.
  197.             PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  198.             $this->log(1'Attempting fallback to https instead of http on channel "' $channel '"...');
  199.             $a $this->downloadHttp('https://' $channel '/channel.xml'$this->ui$tmp$callbackfalse);
  200.             PEAR::popErrorHandling();
  201.             if (PEAR::isError($a)) {
  202.                 return false;
  203.             }
  204.         }
  205.  
  206.         list($a$lastmodified$a;
  207.         if (!class_exists('PEAR_ChannelFile')) {
  208.             require_once 'PEAR/ChannelFile.php';
  209.         }
  210.  
  211.         $b = new PEAR_ChannelFile;
  212.         if ($b->fromXmlFile($a)) {
  213.             unlink($a);
  214.             if ($this->config->get('auto_discover')) {
  215.                 $this->_registry->addChannel($b$lastmodified);
  216.                 $alias $b->getName();
  217.                 if ($b->getName(== $this->_registry->channelName($b->getAlias())) {
  218.                     $alias $b->getAlias();
  219.                 }
  220.  
  221.                 $this->log(1'Auto-discovered channel "' $channel .
  222.                     '", alias "' $alias '", adding to registry');
  223.             }
  224.  
  225.             return true;
  226.         }
  227.  
  228.         unlink($a);
  229.         return false;
  230.     }
  231.  
  232.     /**
  233.      * For simpler unit-testing
  234.      * @param PEAR_Downloader 
  235.      * @return PEAR_Downloader_Package 
  236.      */
  237.     function &newDownloaderPackage(&$t)
  238.     {
  239.         if (!class_exists('PEAR_Downloader_Package')) {
  240.             require_once 'PEAR/Downloader/Package.php';
  241.         }
  242.         $a &new PEAR_Downloader_Package($t);
  243.         return $a;
  244.     }
  245.  
  246.     /**
  247.      * For simpler unit-testing
  248.      * @param PEAR_Config 
  249.      * @param array 
  250.      * @param array 
  251.      * @param int 
  252.      */
  253.     function &getDependency2Object(&$c$i$p$s)
  254.     {
  255.         if (!class_exists('PEAR_Dependency2')) {
  256.             require_once 'PEAR/Dependency2.php';
  257.         }
  258.         $z &new PEAR_Dependency2($c$i$p$s);
  259.         return $z;
  260.     }
  261.  
  262.     function &download($params)
  263.     {
  264.         if (!count($params)) {
  265.             $a = array();
  266.             return $a;
  267.         }
  268.  
  269.         if (!isset($this->_registry)) {
  270.             $this->_registry &$this->config->getRegistry();
  271.         }
  272.  
  273.         $channelschecked = array();
  274.         // convert all parameters into PEAR_Downloader_Package objects
  275.         foreach ($params as $i => $param{
  276.             $params[$i&$this->newDownloaderPackage($this);
  277.             $err $params[$i]->initialize($param);
  278.             PEAR::staticPopErrorHandling();
  279.             if (!$err{
  280.                 // skip parameters that were missed by preferred_state
  281.                 continue;
  282.             }
  283.  
  284.             if (PEAR::isError($err)) {
  285.                 if (!isset($this->_options['soft']&& $err->getMessage(!== ''{
  286.                     $this->log(0$err->getMessage());
  287.                 }
  288.  
  289.                 $params[$i= false;
  290.                 if (is_object($param)) {
  291.                     $param $param->getChannel('/' $param->getPackage();
  292.                 }
  293.  
  294.                 if (!isset($this->_options['soft'])) {
  295.                     $this->log(2'Package "' $param '" is not valid');
  296.                 }
  297.  
  298.                 // Message logged above in a specific verbose mode, passing null to not show up on CLI
  299.                 $this->pushError(nullPEAR_INSTALLER_SKIPPED);
  300.             else {
  301.                 do {
  302.                     if ($params[$i&& $params[$i]->getType(== 'local'{
  303.                         // bug #7090 skip channel.xml check for local packages
  304.                         break;
  305.                     }
  306.  
  307.                     if ($params[$i&& !isset($channelschecked[$params[$i]->getChannel()]&&
  308.                           !isset($this->_options['offline'])
  309.                     {
  310.                         $channelschecked[$params[$i]->getChannel()= true;
  311.                         PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
  312.                         if (!class_exists('System')) {
  313.                             require_once 'System.php';
  314.                         }
  315.  
  316.                         $curchannel &$this->_registry->getChannel($params[$i]->getChannel());
  317.                         if (PEAR::isError($curchannel)) {
  318.                             PEAR::staticPopErrorHandling();
  319.                             return $this->raiseError($curchannel);
  320.                         }
  321.  
  322.                         if (PEAR::isError($dir $this->getDownloadDir())) {
  323.                             PEAR::staticPopErrorHandling();
  324.                             break;
  325.                         }
  326.  
  327.                         $mirror $this->config->get('preferred_mirror'null$params[$i]->getChannel());
  328.                         $url    'http://' $mirror '/channel.xml';
  329.                         $a $this->downloadHttp($url$this->ui$dirnull$curchannel->lastModified());
  330.  
  331.                         PEAR::staticPopErrorHandling();
  332.                         if (PEAR::isError($a|| !$a{
  333.                             // Attempt fallback to https automatically
  334.                             PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  335.                             $a $this->downloadHttp('https://' $mirror .
  336.                                 '/channel.xml'$this->ui$dirnull$curchannel->lastModified());
  337.  
  338.                             PEAR::staticPopErrorHandling();
  339.                             if (PEAR::isError($a|| !$a{
  340.                                 break;
  341.                             }
  342.                         }
  343.                         $this->log(0'WARNING: channel "' $params[$i]->getChannel('" has ' .
  344.                             'updated its protocols, use "' PEAR_RUNTYPE . ' channel-update ' $params[$i]->getChannel(.
  345.                             '" to update');
  346.                     }
  347.                 while (false);
  348.  
  349.                 if ($params[$i&& !isset($this->_options['downloadonly'])) {
  350.                     if (isset($this->_options['packagingroot'])) {
  351.                         $checkdir $this->_prependPath(
  352.                             $this->config->get('php_dir'null$params[$i]->getChannel()),
  353.                             $this->_options['packagingroot']);
  354.                     else {
  355.                         $checkdir $this->config->get('php_dir',
  356.                             null$params[$i]->getChannel());
  357.                     }
  358.  
  359.                     while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
  360.                         $checkdir dirname($checkdir);
  361.                     }
  362.  
  363.                     if ($checkdir == '.'{
  364.                         $checkdir '/';
  365.                     }
  366.  
  367.                     if (!is_writeable($checkdir)) {
  368.                         return PEAR::raiseError('Cannot install, php_dir for channel "' .
  369.                             $params[$i]->getChannel('" is not writeable by the current user');
  370.                     }
  371.                 }
  372.             }
  373.         }
  374.  
  375.         unset($channelschecked);
  376.         PEAR_Downloader_Package::removeDuplicates($params);
  377.         if (!count($params)) {
  378.             $a = array();
  379.             return $a;
  380.         }
  381.  
  382.         if (!isset($this->_options['nodeps']&& !isset($this->_options['offline'])) {
  383.             $reverify = true;
  384.             while ($reverify{
  385.                 $reverify = false;
  386.                 foreach ($params as $i => $param{
  387.                     //PHP Bug 40768 / PEAR Bug #10944
  388.                     //Nested foreaches fail in PHP 5.2.1
  389.                     key($params);
  390.                     $ret $params[$i]->detectDependencies($params);
  391.                     if (PEAR::isError($ret)) {
  392.                         $reverify = true;
  393.                         $params[$i= false;
  394.                         PEAR_Downloader_Package::removeDuplicates($params);
  395.                         if (!isset($this->_options['soft'])) {
  396.                             $this->log(0$ret->getMessage());
  397.                         }
  398.                         continue 2;
  399.                     }
  400.                 }
  401.             }
  402.         }
  403.  
  404.         if (isset($this->_options['offline'])) {
  405.             $this->log(3'Skipping dependency download check, --offline specified');
  406.         }
  407.  
  408.         if (!count($params)) {
  409.             $a = array();
  410.             return $a;
  411.         }
  412.  
  413.         while (PEAR_Downloader_Package::mergeDependencies($params));
  414.         PEAR_Downloader_Package::removeDuplicates($paramstrue);
  415.         $errorparams = array();
  416.         if (PEAR_Downloader_Package::detectStupidDuplicates($params$errorparams)) {
  417.             if (count($errorparams)) {
  418.                 foreach ($errorparams as $param{
  419.                     $name $this->_registry->parsedPackageNameToString($param->getParsedPackage());
  420.                     $this->pushError('Duplicate package ' $name ' found'PEAR_INSTALLER_FAILED);
  421.                 }
  422.                 $a = array();
  423.                 return $a;
  424.             }
  425.         }
  426.  
  427.         PEAR_Downloader_Package::removeInstalled($params);
  428.         if (!count($params)) {
  429.             $this->pushError('No valid packages found'PEAR_INSTALLER_FAILED);
  430.             $a = array();
  431.             return $a;
  432.         }
  433.  
  434.         $err $this->analyzeDependencies($params);
  435.         PEAR::popErrorHandling();
  436.         if (!count($params)) {
  437.             $this->pushError('No valid packages found'PEAR_INSTALLER_FAILED);
  438.             $a = array();
  439.             return $a;
  440.         }
  441.  
  442.         $ret = array();
  443.         $newparams = array();
  444.         if (isset($this->_options['pretend'])) {
  445.             return $params;
  446.         }
  447.  
  448.         $somefailed = false;
  449.         foreach ($params as $i => $package{
  450.             $pf &$params[$i]->download();
  451.             PEAR::staticPopErrorHandling();
  452.             if (PEAR::isError($pf)) {
  453.                 if (!isset($this->_options['soft'])) {
  454.                     $this->log(1$pf->getMessage());
  455.                     $this->log(0'Error: cannot download "' .
  456.                         $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
  457.                             true.
  458.                         '"');
  459.                 }
  460.                 $somefailed = true;
  461.                 continue;
  462.             }
  463.  
  464.             $newparams[&$params[$i];
  465.             $ret[= array(
  466.                 'file' => $pf->getArchiveFile(),
  467.                 'info' => &$pf,
  468.                 'pkg'  => $pf->getPackage()
  469.             );
  470.         }
  471.  
  472.         if ($somefailed{
  473.             // remove params that did not download successfully
  474.             PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
  475.             $err $this->analyzeDependencies($newparamstrue);
  476.             PEAR::popErrorHandling();
  477.             if (!count($newparams)) {
  478.                 $this->pushError('Download failed'PEAR_INSTALLER_FAILED);
  479.                 $a = array();
  480.                 return $a;
  481.             }
  482.         }
  483.  
  484.         $this->_downloadedPackages $ret;
  485.         return $newparams;
  486.     }
  487.  
  488.     /**
  489.      * @param array all packages to be installed
  490.      */
  491.     function analyzeDependencies(&$params$force = false)
  492.     {
  493.         if (isset($this->_options['downloadonly'])) {
  494.             return;
  495.         }
  496.  
  497.         $redo  = true;
  498.         $reset $hasfailed $failed = false;
  499.         while ($redo{
  500.             $redo = false;
  501.             foreach ($params as $i => $param{
  502.                 $deps $param->getDeps();
  503.                 if (!$deps{
  504.                     $depchecker &$this->getDependency2Object($this->config$this->getOptions(),
  505.                         $param->getParsedPackage()PEAR_VALIDATE_DOWNLOADING);
  506.                     $send $param->getPackageFile();
  507.  
  508.                     $installcheck $depchecker->validatePackage($send$this$params);
  509.                     if (PEAR::isError($installcheck)) {
  510.                         if (!isset($this->_options['soft'])) {
  511.                             $this->log(0$installcheck->getMessage());
  512.                         }
  513.                         $hasfailed  = true;
  514.                         $params[$i= false;
  515.                         $reset      = true;
  516.                         $redo       = true;
  517.                         $failed     = false;
  518.                         PEAR_Downloader_Package::removeDuplicates($params);
  519.                         continue 2;
  520.                     }
  521.                     continue;
  522.                 }
  523.  
  524.                 if (!$reset && $param->alreadyValidated(&& !$force{
  525.                     continue;
  526.                 }
  527.  
  528.                 if (count($deps)) {
  529.                     $depchecker &$this->getDependency2Object($this->config$this->getOptions(),
  530.                         $param->getParsedPackage()PEAR_VALIDATE_DOWNLOADING);
  531.                     $send $param->getPackageFile();
  532.                     if ($send === null{
  533.                         $send $param->getDownloadURL();
  534.                     }
  535.  
  536.                     $installcheck $depchecker->validatePackage($send$this$params);
  537.                     if (PEAR::isError($installcheck)) {
  538.                         if (!isset($this->_options['soft'])) {
  539.                             $this->log(0$installcheck->getMessage());
  540.                         }
  541.                         $hasfailed  = true;
  542.                         $params[$i= false;
  543.                         $reset      = true;
  544.                         $redo       = true;
  545.                         $failed     = false;
  546.                         PEAR_Downloader_Package::removeDuplicates($params);
  547.                         continue 2;
  548.                     }
  549.  
  550.                     $failed = false;
  551.                     if (isset($deps['required']&& is_array($deps['required'])) {
  552.                         foreach ($deps['required'as $type => $dep{
  553.                             // note: Dependency2 will never return a PEAR_Error if ignore-errors
  554.                             // is specified, so soft is needed to turn off logging
  555.                             if (!isset($dep[0])) {
  556.                                 if (PEAR::isError($e $depchecker->{"validate{$type}Dependency"}($dep,
  557.                                       true$params))) {
  558.                                     $failed = true;
  559.                                     if (!isset($this->_options['soft'])) {
  560.                                         $this->log(0$e->getMessage());
  561.                                     }
  562.                                 elseif (is_array($e&& !$param->alreadyValidated()) {
  563.                                     if (!isset($this->_options['soft'])) {
  564.                                         $this->log(0$e[0]);
  565.                                     }
  566.                                 }
  567.                             else {
  568.                                 foreach ($dep as $d{
  569.                                     if (PEAR::isError($e =
  570.                                           $depchecker->{"validate{$type}Dependency"}($d,
  571.                                           true$params))) {
  572.                                         $failed = true;
  573.                                         if (!isset($this->_options['soft'])) {
  574.                                             $this->log(0$e->getMessage());
  575.                                         }
  576.                                     elseif (is_array($e&& !$param->alreadyValidated()) {
  577.                                         if (!isset($this->_options['soft'])) {
  578.                                             $this->log(0$e[0]);
  579.                                         }
  580.                                     }
  581.                                 }
  582.                             }
  583.                         }
  584.  
  585.                         if (isset($deps['optional']&& is_array($deps['optional'])) {
  586.                             foreach ($deps['optional'as $type => $dep{
  587.                                 if (!isset($dep[0])) {
  588.                                     if (PEAR::isError($e =
  589.                                           $depchecker->{"validate{$type}Dependency"}($dep,
  590.                                           false$params))) {
  591.                                         $failed = true;
  592.                                         if (!isset($this->_options['soft'])) {
  593.                                             $this->log(0$e->getMessage());
  594.                                         }
  595.                                     elseif (is_array($e&& !$param->alreadyValidated()) {
  596.                                         if (!isset($this->_options['soft'])) {
  597.                                             $this->log(0$e[0]);
  598.                                         }
  599.                                     }
  600.                                 else {
  601.                                     foreach ($dep as $d{
  602.                                         if (PEAR::isError($e =
  603.                                               $depchecker->{"validate{$type}Dependency"}($d,
  604.                                               false$params))) {
  605.                                             $failed = true;
  606.                                             if (!isset($this->_options['soft'])) {
  607.                                                 $this->log(0$e->getMessage());
  608.                                             }
  609.                                         elseif (is_array($e&& !$param->alreadyValidated()) {
  610.                                             if (!isset($this->_options['soft'])) {
  611.                                                 $this->log(0$e[0]);
  612.                                             }
  613.                                         }
  614.                                     }
  615.                                 }
  616.                             }
  617.                         }
  618.  
  619.                         $groupname $param->getGroup();
  620.                         if (isset($deps['group']&& $groupname{
  621.                             if (!isset($deps['group'][0])) {
  622.                                 $deps['group'= array($deps['group']);
  623.                             }
  624.  
  625.                             $found = false;
  626.                             foreach ($deps['group'as $group{
  627.                                 if ($group['attribs']['name'== $groupname{
  628.                                     $found = true;
  629.                                     break;
  630.                                 }
  631.                             }
  632.  
  633.                             if ($found{
  634.                                 unset($group['attribs']);
  635.                                 foreach ($group as $type => $dep{
  636.                                     if (!isset($dep[0])) {
  637.                                         if (PEAR::isError($e =
  638.                                               $depchecker->{"validate{$type}Dependency"}($dep,
  639.                                               false$params))) {
  640.                                             $failed = true;
  641.                                             if (!isset($this->_options['soft'])) {
  642.                                                 $this->log(0$e->getMessage());
  643.                                             }
  644.                                         elseif (is_array($e&& !$param->alreadyValidated()) {
  645.                                             if (!isset($this->_options['soft'])) {
  646.                                                 $this->log(0$e[0]);
  647.                                             }
  648.                                         }
  649.                                     else {
  650.                                         foreach ($dep as $d{
  651.                                             if (PEAR::isError($e =
  652.                                                   $depchecker->{"validate{$type}Dependency"}($d,
  653.                                                   false$params))) {
  654.                                                 $failed = true;
  655.                                                 if (!isset($this->_options['soft'])) {
  656.                                                     $this->log(0$e->getMessage());
  657.                                                 }
  658.                                             elseif (is_array($e&& !$param->alreadyValidated()) {
  659.                                                 if (!isset($this->_options['soft'])) {
  660.                                                     $this->log(0$e[0]);
  661.                                                 }
  662.                                             }
  663.                                         }
  664.                                     }
  665.                                 }
  666.                             }
  667.                         }
  668.                     else {
  669.                         foreach ($deps as $dep{
  670.                             if (PEAR::isError($e $depchecker->validateDependency1($dep$params))) {
  671.                                 $failed = true;
  672.                                 if (!isset($this->_options['soft'])) {
  673.                                     $this->log(0$e->getMessage());
  674.                                 }
  675.                             elseif (is_array($e&& !$param->alreadyValidated()) {
  676.                                 if (!isset($this->_options['soft'])) {
  677.                                     $this->log(0$e[0]);
  678.                                 }
  679.                             }
  680.                         }
  681.                     }
  682.                     $params[$i]->setValidated();
  683.                 }
  684.  
  685.                 if ($failed{
  686.                     $hasfailed  = true;
  687.                     $params[$i= false;
  688.                     $reset      = true;
  689.                     $redo       = true;
  690.                     $failed     = false;
  691.                     PEAR_Downloader_Package::removeDuplicates($params);
  692.                     continue 2;
  693.                 }
  694.             }
  695.         }
  696.  
  697.         PEAR::staticPopErrorHandling();
  698.         if ($hasfailed && (isset($this->_options['ignore-errors']||
  699.               isset($this->_options['nodeps']))) {
  700.             // this is probably not needed, but just in case
  701.             if (!isset($this->_options['soft'])) {
  702.                 $this->log(0'WARNING: dependencies failed');
  703.             }
  704.         }
  705.     }
  706.  
  707.     /**
  708.      * Retrieve the directory that downloads will happen in
  709.      * @access private
  710.      * @return string 
  711.      */
  712.     function getDownloadDir()
  713.     {
  714.         if (isset($this->_downloadDir)) {
  715.             return $this->_downloadDir;
  716.         }
  717.  
  718.         $downloaddir $this->config->get('download_dir');
  719.         if (empty($downloaddir|| (is_dir($downloaddir&& !is_writable($downloaddir))) {
  720.             if  (is_dir($downloaddir&& !is_writable($downloaddir)) {
  721.                 $this->log(0'WARNING: configuration download directory "' $downloaddir .
  722.                     '" is not writeable.  Change download_dir config variable to ' .
  723.                     'a writeable dir to avoid this warning');
  724.             }
  725.  
  726.             if (!class_exists('System')) {
  727.                 require_once 'System.php';
  728.             }
  729.  
  730.             if (PEAR::isError($downloaddir System::mktemp('-d'))) {
  731.                 return $downloaddir;
  732.             }
  733.             $this->log(3'+ tmp dir created at ' $downloaddir);
  734.         }
  735.  
  736.         if (!is_writable($downloaddir)) {
  737.             if (PEAR::isError(System::mkdir(array('-p'$downloaddir))) ||
  738.                   !is_writable($downloaddir)) {
  739.                 return PEAR::raiseError('download directory "' $downloaddir .
  740.                     '" is not writeable.  Change download_dir config variable to ' .
  741.                     'a writeable dir');
  742.             }
  743.         }
  744.  
  745.         return $this->_downloadDir = $downloaddir;
  746.     }
  747.  
  748.     function setDownloadDir($dir)
  749.     {
  750.         if (!@is_writable($dir)) {
  751.             if (PEAR::isError(System::mkdir(array('-p'$dir)))) {
  752.                 return PEAR::raiseError('download directory "' $dir .
  753.                     '" is not writeable.  Change download_dir config variable to ' .
  754.                     'a writeable dir');
  755.             }
  756.         }
  757.         $this->_downloadDir = $dir;
  758.     }
  759.  
  760.     function configSet($key$value$layer 'user'$channel = false)
  761.     {
  762.         $this->config->set($key$value$layer$channel);
  763.         $this->_preferredState = $this->config->get('preferred_state'null$channel);
  764.         if (!$this->_preferredState{
  765.             // don't inadvertantly use a non-set preferred_state
  766.             $this->_preferredState = null;
  767.         }
  768.     }
  769.  
  770.     function setOptions($options)
  771.     {
  772.         $this->_options = $options;
  773.     }
  774.  
  775.     function getOptions()
  776.     {
  777.         return $this->_options;
  778.     }
  779.  
  780.  
  781.     /**
  782.      * @param array output of {@link parsePackageName()}
  783.      * @access private
  784.      */
  785.     function _getPackageDownloadUrl($parr)
  786.     {
  787.         $curchannel $this->config->get('default_channel');
  788.         $this->configSet('default_channel'$parr['channel']);
  789.         // getDownloadURL returns an array.  On error, it only contains information
  790.         // on the latest release as array(version, info).  On success it contains
  791.         // array(version, info, download url string)
  792.         $state = isset($parr['state']$parr['state'$this->config->get('preferred_state');
  793.         if (!$this->_registry->channelExists($parr['channel'])) {
  794.             do {
  795.                 if ($this->config->get('auto_discover'&& $this->discover($parr['channel'])) {
  796.                     break;
  797.                 }
  798.  
  799.                 $this->configSet('default_channel'$curchannel);
  800.                 return PEAR::raiseError('Unknown remote channel: ' $parr['channel']);
  801.             while (false);
  802.         }
  803.  
  804.         $chan &$this->_registry->getChannel($parr['channel']);
  805.         if (PEAR::isError($chan)) {
  806.             return $chan;
  807.         }
  808.  
  809.         $version   $this->_registry->packageInfo($parr['package']'version'$parr['channel']);
  810.         $stability $this->_registry->packageInfo($parr['package']'stability'$parr['channel']);
  811.         // package is installed - use the installed release stability level
  812.         if (!isset($parr['state']&& $stability !== null{
  813.             $state $stability['release'];
  814.         }
  815.         PEAR::staticPopErrorHandling();
  816.         $base2 = false;
  817.  
  818.         $preferred_mirror $this->config->get('preferred_mirror');
  819.         if (!$chan->supportsREST($preferred_mirror||
  820.               (
  821.                !($base2 $chan->getBaseURL('REST1.3'$preferred_mirror))
  822.                &&
  823.                !($base $chan->getBaseURL('REST1.0'$preferred_mirror))
  824.               )
  825.         {
  826.             return $this->raiseError($parr['channel'' is using a unsupported protocol - This should never happen.');
  827.         }
  828.  
  829.         if ($base2{
  830.             $rest &$this->config->getREST('1.3'$this->_options);
  831.             $base $base2;
  832.         else {
  833.             $rest &$this->config->getREST('1.0'$this->_options);
  834.         }
  835.  
  836.         $downloadVersion = false;
  837.         if (!isset($parr['version']&& !isset($parr['state']&& $version
  838.               && !PEAR::isError($version)
  839.               && !isset($this->_options['downloadonly'])
  840.         {
  841.             $downloadVersion $version;
  842.         }
  843.  
  844.         $url $rest->getDownloadURL($base$parr$state$downloadVersion$chan->getName());
  845.         if (PEAR::isError($url)) {
  846.             $this->configSet('default_channel'$curchannel);
  847.             return $url;
  848.         }
  849.  
  850.         if ($parr['channel'!= $curchannel{
  851.             $this->configSet('default_channel'$curchannel);
  852.         }
  853.  
  854.         if (!is_array($url)) {
  855.             return $url;
  856.         }
  857.  
  858.         $url['raw'= false; // no checking is necessary for REST
  859.         if (!is_array($url['info'])) {
  860.             return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  861.                 'this should never happen');
  862.         }
  863.  
  864.         if (!isset($this->_options['force']&&
  865.               !isset($this->_options['downloadonly']&&
  866.               $version &&
  867.               !PEAR::isError($version&&
  868.               !isset($parr['group'])
  869.         {
  870.             if (version_compare($version$url['version']'=')) {
  871.                 return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  872.                     $parrtrue' is already installed and is the same as the ' .
  873.                     'released version ' $url['version']-976);
  874.             }
  875.  
  876.             if (version_compare($version$url['version']'>')) {
  877.                 return PEAR::raiseError($this->_registry->parsedPackageNameToString(
  878.                     $parrtrue' is already installed and is newer than detected ' .
  879.                     'released version ' $url['version']-976);
  880.             }
  881.         }
  882.  
  883.         if (isset($url['info']['required']|| $url['compatible']{
  884.             require_once 'PEAR/PackageFile/v2.php';
  885.             $pf = new PEAR_PackageFile_v2;
  886.             $pf->setRawChannel($parr['channel']);
  887.             if ($url['compatible']{
  888.                 $pf->setRawCompatible($url['compatible']);
  889.             }
  890.         else {
  891.             require_once 'PEAR/PackageFile/v1.php';
  892.             $pf = new PEAR_PackageFile_v1;
  893.         }
  894.  
  895.         $pf->setRawPackage($url['package']);
  896.         $pf->setDeps($url['info']);
  897.         if ($url['compatible']{
  898.             $pf->setCompatible($url['compatible']);
  899.         }
  900.  
  901.         $pf->setRawState($url['stability']);
  902.         $url['info'&$pf;
  903.         if (!extension_loaded("zlib"|| isset($this->_options['nocompress'])) {
  904.             $ext '.tar';
  905.         else {
  906.             $ext '.tgz';
  907.         }
  908.  
  909.         if (is_array($url&& isset($url['url'])) {
  910.             $url['url'.= $ext;
  911.         }
  912.  
  913.         return $url;
  914.     }
  915.  
  916.     /**
  917.      * @param array dependency array
  918.      * @access private
  919.      */
  920.     function _getDepPackageDownloadUrl($dep$parr)
  921.     {
  922.         $xsdversion = isset($dep['rel']'1.0' '2.0';
  923.         $curchannel $this->config->get('default_channel');
  924.         if (isset($dep['uri'])) {
  925.             $xsdversion '2.0';
  926.             $chan &$this->_registry->getChannel('__uri');
  927.             if (PEAR::isError($chan)) {
  928.                 return $chan;
  929.             }
  930.  
  931.             $version $this->_registry->packageInfo($dep['name']'version''__uri');
  932.             $this->configSet('default_channel''__uri');
  933.         else {
  934.             if (isset($dep['channel'])) {
  935.                 $remotechannel $dep['channel'];
  936.             else {
  937.                 $remotechannel 'pear.php.net';
  938.             }
  939.  
  940.             if (!$this->_registry->channelExists($remotechannel)) {
  941.                 do {
  942.                     if ($this->config->get('auto_discover')) {
  943.                         if ($this->discover($remotechannel)) {
  944.                             break;
  945.                         }
  946.                     }
  947.                     return PEAR::raiseError('Unknown remote channel: ' $remotechannel);
  948.                 while (false);
  949.             }
  950.  
  951.             $chan &$this->_registry->getChannel($remotechannel);
  952.             if (PEAR::isError($chan)) {
  953.                 return $chan;
  954.             }
  955.  
  956.             $version $this->_registry->packageInfo($dep['name']'version'$remotechannel);
  957.             $this->configSet('default_channel'$remotechannel);
  958.         }
  959.  
  960.         $state = isset($parr['state']$parr['state'$this->config->get('preferred_state');
  961.         if (isset($parr['state']&& isset($parr['version'])) {
  962.             unset($parr['state']);
  963.         }
  964.  
  965.         if (isset($dep['uri'])) {
  966.             $info &$this->newDownloaderPackage($this);
  967.             $err $info->initialize($dep);
  968.             PEAR::staticPopErrorHandling();
  969.             if (!$err{
  970.                 // skip parameters that were missed by preferred_state
  971.                 return PEAR::raiseError('Cannot initialize dependency');
  972.             }
  973.  
  974.             if (PEAR::isError($err)) {
  975.                 if (!isset($this->_options['soft'])) {
  976.                     $this->log(0$err->getMessage());
  977.                 }
  978.  
  979.                 if (is_object($info)) {
  980.                     $param $info->getChannel('/' $info->getPackage();
  981.                 }
  982.                 return PEAR::raiseError('Package "' $param '" is not valid');
  983.             }
  984.             return $info;
  985.         elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
  986.               &&
  987.                 (
  988.                   ($base2 $chan->getBaseURL('REST1.3'$this->config->get('preferred_mirror')))
  989.                     ||
  990.                   ($base $chan->getBaseURL('REST1.0'$this->config->get('preferred_mirror')))
  991.                 )
  992.         {
  993.             if ($base2{
  994.                 $base $base2;
  995.                 $rest &$this->config->getREST('1.3'$this->_options);
  996.             else {
  997.                 $rest &$this->config->getREST('1.0'$this->_options);
  998.             }
  999.  
  1000.             $url $rest->getDepDownloadURL($base$xsdversion$dep$parr,
  1001.                     $state$version$chan->getName());
  1002.             if (PEAR::isError($url)) {
  1003.                 return $url;
  1004.             }
  1005.  
  1006.             if ($parr['channel'!= $curchannel{
  1007.                 $this->configSet('default_channel'$curchannel);
  1008.             }
  1009.  
  1010.             if (!is_array($url)) {
  1011.                 return $url;
  1012.             }
  1013.  
  1014.             $url['raw'= false; // no checking is necessary for REST
  1015.             if (!is_array($url['info'])) {
  1016.                 return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
  1017.                     'this should never happen');
  1018.             }
  1019.  
  1020.             if (isset($url['info']['required'])) {
  1021.                 if (!class_exists('PEAR_PackageFile_v2')) {
  1022.                     require_once 'PEAR/PackageFile/v2.php';
  1023.                 }
  1024.                 $pf = new PEAR_PackageFile_v2;
  1025.                 $pf->setRawChannel($remotechannel);
  1026.             else {
  1027.                 if (!class_exists('PEAR_PackageFile_v1')) {
  1028.                     require_once 'PEAR/PackageFile/v1.php';
  1029.                 }
  1030.                 $pf = new PEAR_PackageFile_v1;
  1031.  
  1032.             }
  1033.             $pf->setRawPackage($url['package']);
  1034.             $pf->setDeps($url['info']);
  1035.             if ($url['compatible']{
  1036.                 $pf->setCompatible($url['compatible']);
  1037.             }
  1038.  
  1039.             $pf->setRawState($url['stability']);
  1040.             $url['info'&$pf;
  1041.             if (!extension_loaded("zlib"|| isset($this->_options['nocompress'])) {
  1042.                 $ext '.tar';
  1043.             else {
  1044.                 $ext '.tgz';
  1045.             }
  1046.  
  1047.             if (is_array($url&& isset($url['url'])) {
  1048.                 $url['url'.= $ext;
  1049.             }
  1050.  
  1051.             return $url;
  1052.         }
  1053.  
  1054.         return $this->raiseError($parr['channel'' is using a unsupported protocol - This should never happen.');
  1055.     }
  1056.  
  1057.     /**
  1058.      * @deprecated in favor of _getPackageDownloadUrl
  1059.      */
  1060.     function getPackageDownloadUrl($package$version = null$channel = false)
  1061.     {
  1062.         if ($version{
  1063.             $package .= "-$version";
  1064.         }
  1065.         if ($this === null || $this->_registry === null{
  1066.             $package = "http://pear.php.net/get/$package";
  1067.         else {
  1068.             $chan $this->_registry->getChannel($channel);
  1069.             if (PEAR::isError($chan)) {
  1070.                 return '';
  1071.             }
  1072.             $package "http://" $chan->getServer(. "/get/$package";
  1073.         }
  1074.         if (!extension_loaded("zlib")) {
  1075.             $package .= '?uncompress=yes';
  1076.         }
  1077.         return $package;
  1078.     }
  1079.  
  1080.     /**
  1081.      * Retrieve a list of downloaded packages after a call to {@link download()}.
  1082.      *
  1083.      * Also resets the list of downloaded packages.
  1084.      * @return array 
  1085.      */
  1086.     function getDownloadedPackages()
  1087.     {
  1088.         $ret $this->_downloadedPackages;
  1089.         $this->_downloadedPackages = array();
  1090.         $this->_toDownload = array();
  1091.         return $ret;
  1092.     }
  1093.  
  1094.     function _downloadCallback($msg$params = null)
  1095.     {
  1096.         switch ($msg{
  1097.             case 'saveas':
  1098.                 $this->log(1"downloading $params ...");
  1099.                 break;
  1100.             case 'done':
  1101.                 $this->log(1'...done: ' number_format($params0''','' bytes');
  1102.                 break;
  1103.             case 'bytesread':
  1104.                 static $bytes;
  1105.                 if (empty($bytes)) {
  1106.                     $bytes = 0;
  1107.                 }
  1108.                 if (!($bytes % 10240)) {
  1109.                     $this->log(1'.'false);
  1110.                 }
  1111.                 $bytes += $params;
  1112.                 break;
  1113.             case 'start':
  1114.                 if($params[1== -1{
  1115.                     $length "Unknown size";
  1116.                 else {
  1117.                     $length number_format($params[1]0''',')." bytes";
  1118.                 }
  1119.                 $this->log(1"Starting to download {$params[0]} ($length)");
  1120.                 break;
  1121.         }
  1122.         if (method_exists($this->ui'_downloadCallback'))
  1123.             $this->ui->_downloadCallback($msg$params);
  1124.     }
  1125.  
  1126.     function _prependPath($path$prepend)
  1127.     {
  1128.         if (strlen($prepend> 0{
  1129.             if (OS_WINDOWS && preg_match('/^[a-z]:/i'$path)) {
  1130.                 if (preg_match('/^[a-z]:/i'$prepend)) {
  1131.                     $prepend substr($prepend2);
  1132.                 elseif ($prepend{0!= '\\'{
  1133.                     $prepend = "\\$prepend";
  1134.                 }
  1135.                 $path substr($path02$prepend substr($path2);
  1136.             else {
  1137.                 $path $prepend $path;
  1138.             }
  1139.         }
  1140.         return $path;
  1141.     }
  1142.  
  1143.     /**
  1144.      * @param string 
  1145.      * @param integer 
  1146.      */
  1147.     function pushError($errmsg$code = -1)
  1148.     {
  1149.         array_push($this->_errorStackarray($errmsg$code));
  1150.     }
  1151.  
  1152.     function getErrorMsgs()
  1153.     {
  1154.         $msgs = array();
  1155.         $errs $this->_errorStack;
  1156.         foreach ($errs as $err{
  1157.             $msgs[$err[0];
  1158.         }
  1159.         $this->_errorStack = array();
  1160.         return $msgs;
  1161.     }
  1162.  
  1163.     /**
  1164.      * for BC
  1165.      *
  1166.      * @deprecated
  1167.      */
  1168.     function sortPkgDeps(&$packages$uninstall = false)
  1169.     {
  1170.         $uninstall ?
  1171.             $this->sortPackagesForUninstall($packages:
  1172.             $this->sortPackagesForInstall($packages);
  1173.     }
  1174.  
  1175.     /**
  1176.      * Sort a list of arrays of array(downloaded packagefilename) by dependency.
  1177.      *
  1178.      * This uses the topological sort method from graph theory, and the
  1179.      * Structures_Graph package to properly sort dependencies for installation.
  1180.      * @param array an array of downloaded PEAR_Downloader_Packages
  1181.      * @return array array of array(packagefilename, package.xml contents)
  1182.      */
  1183.     function sortPackagesForInstall(&$packages)
  1184.     {
  1185.         require_once 'Structures/Graph.php';
  1186.         require_once 'Structures/Graph/Node.php';
  1187.         require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
  1188.         $depgraph = new Structures_Graph(true);
  1189.         $nodes = array();
  1190.         $reg &$this->config->getRegistry();
  1191.         foreach ($packages as $i => $package{
  1192.             $pname $reg->parsedPackageNameToString(
  1193.                 array(
  1194.                     'channel' => $package->getChannel(),
  1195.                     'package' => strtolower($package->getPackage()),
  1196.                 ));
  1197.             $nodes[$pname= new Structures_Graph_Node;
  1198.             $nodes[$pname]->setData($packages[$i]);
  1199.             $depgraph->addNode($nodes[$pname]);
  1200.         }
  1201.  
  1202.         $deplinks = array();
  1203.         foreach ($nodes as $package => $node{
  1204.             $pf &$node->getData();
  1205.             $pdeps $pf->getDeps(true);
  1206.             if (!$pdeps{
  1207.                 continue;
  1208.             }
  1209.  
  1210.             if ($pf->getPackagexmlVersion(== '1.0'{
  1211.                 foreach ($pdeps as $dep{
  1212.                     if ($dep['type'!= 'pkg' ||
  1213.                           (isset($dep['optional']&& $dep['optional'== 'yes')) {
  1214.                         continue;
  1215.                     }
  1216.  
  1217.                     $dname $reg->parsedPackageNameToString(
  1218.                           array(
  1219.                               'channel' => 'pear.php.net',
  1220.                               'package' => strtolower($dep['name']),
  1221.                           ));
  1222.  
  1223.                     if (isset($nodes[$dname])) {
  1224.                         if (!isset($deplinks[$dname])) {
  1225.                             $deplinks[$dname= array();
  1226.                         }
  1227.  
  1228.                         $deplinks[$dname][$package= 1;
  1229.                         // dependency is in installed packages
  1230.                         continue;
  1231.                     }
  1232.  
  1233.                     $dname $reg->parsedPackageNameToString(
  1234.                           array(
  1235.                               'channel' => 'pecl.php.net',
  1236.                               'package' => strtolower($dep['name']),
  1237.                           ));
  1238.  
  1239.                     if (isset($nodes[$dname])) {
  1240.                         if (!isset($deplinks[$dname])) {
  1241.                             $deplinks[$dname= array();
  1242.                         }
  1243.  
  1244.                         $deplinks[$dname][$package= 1;
  1245.                         // dependency is in installed packages
  1246.                         continue;
  1247.                     }
  1248.                 }
  1249.             else {
  1250.                 // the only ordering we care about is:
  1251.                 // 1) subpackages must be installed before packages that depend on them
  1252.                 // 2) required deps must be installed before packages that depend on them
  1253.                 if (isset($pdeps['required']['subpackage'])) {
  1254.                     $t $pdeps['required']['subpackage'];
  1255.                     if (!isset($t[0])) {
  1256.                         $t = array($t);
  1257.                     }
  1258.  
  1259.                     $this->_setupGraph($t$reg$deplinks$nodes$package);
  1260.                 }
  1261.  
  1262.                 if (isset($pdeps['group'])) {
  1263.                     if (!isset($pdeps['group'][0])) {
  1264.                         $pdeps['group'= array($pdeps['group']);
  1265.                     }
  1266.  
  1267.                     foreach ($pdeps['group'as $group{
  1268.                         if (isset($group['subpackage'])) {
  1269.                             $t $group['subpackage'];
  1270.                             if (!isset($t[0])) {
  1271.                                 $t = array($t);
  1272.                             }
  1273.  
  1274.                             $this->_setupGraph($t$reg$deplinks$nodes$package);
  1275.                         }
  1276.                     }
  1277.                 }
  1278.  
  1279.                 if (isset($pdeps['optional']['subpackage'])) {
  1280.                     $t $pdeps['optional']['subpackage'];
  1281.                     if (!isset($t[0])) {
  1282.                         $t = array($t);
  1283.                     }
  1284.  
  1285.                     $this->_setupGraph($t$reg$deplinks$nodes$package);
  1286.                 }
  1287.  
  1288.                 if (isset($pdeps['required']['package'])) {
  1289.                     $t $pdeps['required']['package'];
  1290.                     if (!isset($t[0])) {
  1291.                         $t = array($t);
  1292.                     }
  1293.  
  1294.                     $this->_setupGraph($t$reg$deplinks$nodes$package);
  1295.                 }
  1296.  
  1297.                 if (isset($pdeps['group'])) {
  1298.                     if (!isset($pdeps['group'][0])) {
  1299.                         $pdeps['group'= array($pdeps['group']);
  1300.                     }
  1301.  
  1302.                     foreach ($pdeps['group'as $group{
  1303.                         if (isset($group['package'])) {
  1304.                             $t $group['package'];
  1305.                             if (!isset($t[0])) {
  1306.                                 $t = array($t);
  1307.                             }
  1308.  
  1309.                             $this->_setupGraph($t$reg$deplinks$nodes$package);
  1310.                         }
  1311.                     }
  1312.                 }
  1313.             }
  1314.         }
  1315.  
  1316.         $this->_detectDepCycle($deplinks);
  1317.         foreach ($deplinks as $dependent => $parents{
  1318.             foreach ($parents as $parent => $unused{
  1319.                 $nodes[$dependent]->connectTo($nodes[$parent]);
  1320.             }
  1321.         }
  1322.  
  1323.         $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
  1324.         $ret = array();
  1325.         for ($i = 0$count count($installOrder)$i $count$i++{
  1326.             foreach ($installOrder[$ias $index => $sortedpackage{
  1327.                 $data &$installOrder[$i][$index]->getData();
  1328.                 $ret[&$nodes[$reg->parsedPackageNameToString(
  1329.                           array(
  1330.                               'channel' => $data->getChannel(),
  1331.                               'package' => strtolower($data->getPackage()),
  1332.                           ))]->getData();
  1333.             }
  1334.         }
  1335.  
  1336.         $packages $ret;
  1337.         return;
  1338.     }
  1339.  
  1340.     /**
  1341.      * Detect recursive links between dependencies and break the cycles
  1342.      *
  1343.      * @param array 
  1344.      * @access private
  1345.      */
  1346.     function _detectDepCycle(&$deplinks)
  1347.     {
  1348.         do {
  1349.             $keepgoing = false;
  1350.             foreach ($deplinks as $dep => $parents{
  1351.                 foreach ($parents as $parent => $unused{
  1352.                     // reset the parent cycle detector
  1353.                     $this->_testCycle(nullnullnull);
  1354.                     if ($this->_testCycle($dep$deplinks$parent)) {
  1355.                         $keepgoing = true;
  1356.                         unset($deplinks[$dep][$parent]);
  1357.                         if (count($deplinks[$dep]== 0{
  1358.                             unset($deplinks[$dep]);
  1359.                         }
  1360.  
  1361.                         continue 3;
  1362.                     }
  1363.                 }
  1364.             }
  1365.         while ($keepgoing);
  1366.     }
  1367.  
  1368.     function _testCycle($test$deplinks$dep)
  1369.     {
  1370.         static $visited = array();
  1371.         if ($test === null{
  1372.             $visited = array();
  1373.             return;
  1374.         }
  1375.  
  1376.         // this happens when a parent has a dep cycle on another dependency
  1377.         // but the child is not part of the cycle
  1378.         if (isset($visited[$dep])) {
  1379.             return false;
  1380.         }
  1381.  
  1382.         $visited[$dep= 1;
  1383.         if ($test == $dep{
  1384.             return true;
  1385.         }
  1386.  
  1387.         if (isset($deplinks[$dep])) {
  1388.             if (in_array($testarray_keys($deplinks[$dep])true)) {
  1389.                 return true;
  1390.             }
  1391.  
  1392.             foreach ($deplinks[$depas $parent => $unused{
  1393.                 if ($this->_testCycle($test$deplinks$parent)) {
  1394.                     return true;
  1395.                 }
  1396.             }
  1397.         }
  1398.  
  1399.         return false;
  1400.     }
  1401.  
  1402.     /**
  1403.      * Set up the dependency for installation parsing
  1404.      *
  1405.      * @param array $t dependency information
  1406.      * @param PEAR_Registry $reg 
  1407.      * @param array $deplinks list of dependency links already established
  1408.      * @param array $nodes all existing package nodes
  1409.      * @param string $package parent package name
  1410.      * @access private
  1411.      */
  1412.     function _setupGraph($t$reg&$deplinks&$nodes$package)
  1413.     {
  1414.         foreach ($t as $dep{
  1415.             $depchannel !isset($dep['channel']'__uri'$dep['channel'];
  1416.             $dname $reg->parsedPackageNameToString(
  1417.                   array(
  1418.                       'channel' => $depchannel,
  1419.                       'package' => strtolower($dep['name']),
  1420.                   ));
  1421.  
  1422.             if (isset($nodes[$dname])) {
  1423.                 if (!isset($deplinks[$dname])) {
  1424.                     $deplinks[$dname= array();
  1425.                 }
  1426.                 $deplinks[$dname][$package= 1;
  1427.             }
  1428.         }
  1429.     }
  1430.  
  1431.     function _dependsOn($a$b)
  1432.     {
  1433.         return $this->_checkDepTree(strtolower($a->getChannel())strtolower($a->getPackage())$b);
  1434.     }
  1435.  
  1436.     function _checkDepTree($channel$package$b$checked = array())
  1437.     {
  1438.         $checked[$channel][$package= true;
  1439.         if (!isset($this->_depTree[$channel][$package])) {
  1440.             return false;
  1441.         }
  1442.  
  1443.         if (isset($this->_depTree[$channel][$package][strtolower($b->getChannel())]
  1444.               [strtolower($b->getPackage())])) {
  1445.             return true;
  1446.         }
  1447.  
  1448.         foreach ($this->_depTree[$channel][$packageas $ch => $packages{
  1449.             foreach ($packages as $pa => $true{
  1450.                 if ($this->_checkDepTree($ch$pa$b$checked)) {
  1451.                     return true;
  1452.                 }
  1453.             }
  1454.         }
  1455.  
  1456.         return false;
  1457.     }
  1458.  
  1459.     function _sortInstall($a$b)
  1460.     {
  1461.         if (!$a->getDeps(&& !$b->getDeps()) {
  1462.             return 0; // neither package has dependencies, order is insignificant
  1463.         }
  1464.         if ($a->getDeps(&& !$b->getDeps()) {
  1465.             return 1; // $a must be installed after $b because $a has dependencies
  1466.         }
  1467.         if (!$a->getDeps(&& $b->getDeps()) {
  1468.             return -1; // $b must be installed after $a because $b has dependencies
  1469.         }
  1470.         // both packages have dependencies
  1471.         if ($this->_dependsOn($a$b)) {
  1472.             return 1;
  1473.         }
  1474.         if ($this->_dependsOn($b$a)) {
  1475.             return -1;
  1476.         }
  1477.         return 0;
  1478.     }
  1479.  
  1480.     /**
  1481.      * Download a file through HTTP.  Considers suggested file name in
  1482.      * Content-disposition: header and can run a callback function for
  1483.      * different events.  The callback will be called with two
  1484.      * parameters: the callback type, and parameters.  The implemented
  1485.      * callback types are:
  1486.      *
  1487.      *  'setup'       called at the very beginning, parameter is a UI object
  1488.      *                that should be used for all output
  1489.      *  'message'     the parameter is a string with an informational message
  1490.      *  'saveas'      may be used to save with a different file name, the
  1491.      *                parameter is the filename that is about to be used.
  1492.      *                If a 'saveas' callback returns a non-empty string,
  1493.      *                that file name will be used as the filename instead.
  1494.      *                Note that $save_dir will not be affected by this, only
  1495.      *                the basename of the file.
  1496.      *  'start'       download is starting, parameter is number of bytes
  1497.      *                that are expected, or -1 if unknown
  1498.      *  'bytesread'   parameter is the number of bytes read so far
  1499.      *  'done'        download is complete, parameter is the total number
  1500.      *                of bytes read
  1501.      *  'connfailed'  if the TCP/SSL connection fails, this callback is called
  1502.      *                with array(host,port,errno,errmsg)
  1503.      *  'writefailed' if writing to disk fails, this callback is called
  1504.      *                with array(destfile,errmsg)
  1505.      *
  1506.      * If an HTTP proxy has been configured (http_proxy PEAR_Config
  1507.      * setting), the proxy will be used.
  1508.      *
  1509.      * @param string  $url       the URL to download
  1510.      * @param object  $ui        PEAR_Frontend_* instance
  1511.      * @param object  $config    PEAR_Config instance
  1512.      * @param string  $save_dir  directory to save file in
  1513.      * @param mixed   $callback  function/method to call for status
  1514.      *                            updates
  1515.      * @param false|string|array$lastmodified header values to check against for caching
  1516.      *                            use false to return the header values from this download
  1517.      * @param false|array$accept Accept headers to send
  1518.      * @param false|string$channel Channel to use for retrieving authentication
  1519.      * @return string|array Returns the full path of the downloaded file or a PEAR
  1520.      *                        error on failure.  If the error is caused by
  1521.      *                        socket-related errors, the error object will
  1522.      *                        have the fsockopen error code available through
  1523.      *                        getCode().  If caching is requested, then return the header
  1524.      *                        values.
  1525.      *
  1526.      * @access public
  1527.      */
  1528.     function downloadHttp($url&$ui$save_dir '.'$callback = null$lastmodified = null,
  1529.                           $accept = false$channel = false)
  1530.     {
  1531.         static $redirect = 0;
  1532.         // always reset , so we are clean case of error
  1533.         $wasredirect $redirect;
  1534.         $redirect = 0;
  1535.         if ($callback{
  1536.             call_user_func($callback'setup'array(&$ui));
  1537.         }
  1538.  
  1539.         $info parse_url($url);
  1540.         if (!isset($info['scheme']|| !in_array($info['scheme']array('http''https'))) {
  1541.             return PEAR::raiseError('Cannot download non-http URL "' $url '"');
  1542.         }
  1543.  
  1544.         if (!isset($info['host'])) {
  1545.             return PEAR::raiseError('Cannot download from non-URL "' $url '"');
  1546.         }
  1547.  
  1548.         $host = isset($info['host']$info['host': null;
  1549.         $port = isset($info['port']$info['port': null;
  1550.         $path = isset($info['path']$info['path': null;
  1551.  
  1552.         if (isset($this)) {
  1553.             $config &$this->config;
  1554.         else {
  1555.             $config &PEAR_Config::singleton();
  1556.         }
  1557.  
  1558.         $proxy_host $proxy_port $proxy_user $proxy_pass '';
  1559.         if ($config->get('http_proxy'&&
  1560.               $proxy parse_url($config->get('http_proxy'))) {
  1561.             $proxy_host = isset($proxy['host']$proxy['host': null;
  1562.             if (isset($proxy['scheme']&& $proxy['scheme'== 'https'{
  1563.                 $proxy_host 'ssl://' $proxy_host;
  1564.             }
  1565.             $proxy_port = isset($proxy['port']$proxy['port': 8080;
  1566.             $proxy_user = isset($proxy['user']urldecode($proxy['user']: null;
  1567.             $proxy_pass = isset($proxy['pass']urldecode($proxy['pass']: null;
  1568.  
  1569.             if ($callback{
  1570.                 call_user_func($callback'message'"Using HTTP proxy $host:$port");
  1571.             }
  1572.         }
  1573.  
  1574.         if (empty($port)) {
  1575.             $port (isset($info['scheme']&& $info['scheme'== 'https'? 443 : 80;
  1576.         }
  1577.  
  1578.         $scheme (isset($info['scheme']&& $info['scheme'== 'https''https' 'http';
  1579.  
  1580.         if ($proxy_host != ''{
  1581.             $fp @fsockopen($proxy_host$proxy_port$errno$errstr);
  1582.             if (!$fp{
  1583.                 if ($callback{
  1584.                     call_user_func($callback'connfailed'array($proxy_host$proxy_port,
  1585.                                                                   $errno$errstr));
  1586.                 }
  1587.                 return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr"$errno);
  1588.             }
  1589.  
  1590.             if ($lastmodified === false || $lastmodified{
  1591.                 $request  = "GET $url HTTP/1.1\r\n";
  1592.                 $request .= "Host: $host\r\n";
  1593.             else {
  1594.                 $request  = "GET $url HTTP/1.0\r\n";
  1595.                 $request .= "Host: $host\r\n";
  1596.             }
  1597.         else {
  1598.             $network_host $host;
  1599.             if (isset($info['scheme']&& $info['scheme'== 'https'{
  1600.                 $network_host 'ssl://' $host;
  1601.             }
  1602.  
  1603.             $fp @fsockopen($network_host$port$errno$errstr);
  1604.             if (!$fp{
  1605.                 if ($callback{
  1606.                     call_user_func($callback'connfailed'array($host$port,
  1607.                                                                   $errno$errstr));
  1608.                 }
  1609.                 return PEAR::raiseError("Connection to `$host:$port' failed: $errstr"$errno);
  1610.             }
  1611.  
  1612.             if ($lastmodified === false || $lastmodified{
  1613.                 $request = "GET $path HTTP/1.1\r\n";
  1614.                 $request .= "Host: $host\r\n";
  1615.             else {
  1616.                 $request = "GET $path HTTP/1.0\r\n";
  1617.                 $request .= "Host: $host\r\n";
  1618.             }
  1619.         }
  1620.  
  1621.         $ifmodifiedsince '';
  1622.         if (is_array($lastmodified)) {
  1623.             if (isset($lastmodified['Last-Modified'])) {
  1624.                 $ifmodifiedsince 'If-Modified-Since: ' $lastmodified['Last-Modified'"\r\n";
  1625.             }
  1626.  
  1627.             if (isset($lastmodified['ETag'])) {
  1628.                 $ifmodifiedsince .= "If-None-Match: $lastmodified[ETag]\r\n";
  1629.             }
  1630.         else {
  1631.             $ifmodifiedsince ($lastmodified ? "If-Modified-Since: $lastmodified\r\n" : '');
  1632.         }
  1633.  
  1634.         $request .= $ifmodifiedsince .
  1635.             "User-Agent: PEAR/1.9.4/PHP/" . PHP_VERSION . "\r\n";
  1636.  
  1637.         if (isset($this)) // only pass in authentication for non-static calls
  1638.             $username $config->get('username'null$channel);
  1639.             $password $config->get('password'null$channel);
  1640.             if ($username && $password{
  1641.                 $tmp base64_encode("$username:$password");
  1642.                 $request .= "Authorization: Basic $tmp\r\n";
  1643.             }
  1644.         }
  1645.  
  1646.         if ($proxy_host != '' && $proxy_user != ''{
  1647.             $request .= 'Proxy-Authorization: Basic ' .
  1648.                 base64_encode($proxy_user ':' $proxy_pass"\r\n";
  1649.         }
  1650.  
  1651.         if ($accept{
  1652.             $request .= 'Accept: ' implode(', '$accept"\r\n";
  1653.         }
  1654.  
  1655.         $request .= "Connection: close\r\n";
  1656.         $request .= "\r\n";
  1657.         fwrite($fp$request);
  1658.         $headers = array();
  1659.         $reply = 0;
  1660.         while (trim($line fgets($fp1024))) {
  1661.             if (preg_match('/^([^:]+):\s+(.*)\s*\\z/'$line$matches)) {
  1662.                 $headers[strtolower($matches[1])trim($matches[2]);
  1663.             elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |'$line$matches)) {
  1664.                 $reply = (int)$matches[1];
  1665.                 if ($reply == 304 && ($lastmodified || ($lastmodified === false))) {
  1666.                     return false;
  1667.                 }
  1668.  
  1669.                 if (!in_array($replyarray(200301302303305307))) {
  1670.                     return PEAR::raiseError("File $scheme://$host:$port$path not valid (received: $line)");
  1671.                 }
  1672.             }
  1673.         }
  1674.  
  1675.         if ($reply != 200{
  1676.             if (!isset($headers['location'])) {
  1677.                 return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirected but no location)");
  1678.             }
  1679.  
  1680.             if ($wasredirect > 4{
  1681.                 return PEAR::raiseError("File $scheme://$host:$port$path not valid (redirection looped more than 5 times)");
  1682.             }
  1683.  
  1684.             $redirect $wasredirect + 1;
  1685.             return $this->downloadHttp($headers['location'],
  1686.                     $ui$save_dir$callback$lastmodified$accept);
  1687.         }
  1688.  
  1689.         if (isset($headers['content-disposition']&&
  1690.             preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|\\z)/'$headers['content-disposition']$matches)) {
  1691.             $save_as basename($matches[1]);
  1692.         else {
  1693.             $save_as basename($url);
  1694.         }
  1695.  
  1696.         if ($callback{
  1697.             $tmp call_user_func($callback'saveas'$save_as);
  1698.             if ($tmp{
  1699.                 $save_as $tmp;
  1700.             }
  1701.         }
  1702.  
  1703.         $dest_file $save_dir . DIRECTORY_SEPARATOR . $save_as;
  1704.         if (is_link($dest_file)) {
  1705.             return PEAR::raiseError('SECURITY ERROR: Will not write to ' $dest_file ' as it is symlinked to ' readlink($dest_file' - Possible symlink attack');
  1706.         }
  1707.  
  1708.         if (!$wp @fopen($dest_file'wb')) {
  1709.             fclose($fp);
  1710.             if ($callback{
  1711.                 call_user_func($callback'writefailed'array($dest_file$php_errormsg));
  1712.             }
  1713.             return PEAR::raiseError("could not open $dest_file for writing");
  1714.         }
  1715.  
  1716.         $length = isset($headers['content-length']$headers['content-length': -1;
  1717.  
  1718.         $bytes = 0;
  1719.         if ($callback{
  1720.             call_user_func($callback'start'array(basename($dest_file)$length));
  1721.         }
  1722.  
  1723.         while ($data fread($fp1024)) {
  1724.             $bytes += strlen($data);
  1725.             if ($callback{
  1726.                 call_user_func($callback'bytesread'$bytes);
  1727.             }
  1728.             if (!@fwrite($wp$data)) {
  1729.                 fclose($fp);
  1730.                 if ($callback{
  1731.                     call_user_func($callback'writefailed'array($dest_file$php_errormsg));
  1732.                 }
  1733.                 return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
  1734.             }
  1735.         }
  1736.  
  1737.         fclose($fp);
  1738.         fclose($wp);
  1739.         if ($callback{
  1740.             call_user_func($callback'done'$bytes);
  1741.         }
  1742.  
  1743.         if ($lastmodified === false || $lastmodified{
  1744.             if (isset($headers['etag'])) {
  1745.                 $lastmodified = array('ETag' => $headers['etag']);
  1746.             }
  1747.  
  1748.             if (isset($headers['last-modified'])) {
  1749.                 if (is_array($lastmodified)) {
  1750.                     $lastmodified['Last-Modified'$headers['last-modified'];
  1751.                 else {
  1752.                     $lastmodified $headers['last-modified'];
  1753.                 }
  1754.             }
  1755.             return array($dest_file$lastmodified$headers);
  1756.         }
  1757.         return $dest_file;
  1758.     }
  1759. }

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