PageRenderTime 114ms CodeModel.GetById 74ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/php/PEAR/Downloader.php

https://bitbucket.org/adarshj/convenient_website
PHP | 1766 lines | 1465 code | 123 blank | 178 comment | 237 complexity | 3f62de6899c2ca37c89810e3510b77b9 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 */
  23require_once 'PEAR/Common.php';
  24
  25define('PEAR_INSTALLER_OK',       1);
  26define('PEAR_INSTALLER_FAILED',   0);
  27define('PEAR_INSTALLER_SKIPPED', -1);
  28define('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 */
  46class 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        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 187        $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
 188        if (!class_exists('System')) {
 189            require_once 'System.php';
 190        }
 191
 192        $tmpdir = $this->config->get('temp_dir');
 193        $tmp = System::mktemp('-d -t "' . $tmpdir . '"');
 194        $a   = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
 195        PEAR::popErrorHandling();
 196        if (PEAR::isError($a)) {
 197            // Attempt to fallback to https automatically.
 198            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 199            $this->log(1, 'Attempting fallback to https instead of http on channel "' . $channel . '"...');
 200            $a = $this->downloadHttp('https://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
 201            PEAR::popErrorHandling();
 202            if (PEAR::isError($a)) {
 203                return false;
 204            }
 205        }
 206
 207        list($a, $lastmodified) = $a;
 208        if (!class_exists('PEAR_ChannelFile')) {
 209            require_once 'PEAR/ChannelFile.php';
 210        }
 211
 212        $b = new PEAR_ChannelFile;
 213        if ($b->fromXmlFile($a)) {
 214            unlink($a);
 215            if ($this->config->get('auto_discover')) {
 216                $this->_registry->addChannel($b, $lastmodified);
 217                $alias = $b->getName();
 218                if ($b->getName() == $this->_registry->channelName($b->getAlias())) {
 219                    $alias = $b->getAlias();
 220                }
 221
 222                $this->log(1, 'Auto-discovered channel "' . $channel .
 223                    '", alias "' . $alias . '", adding to registry');
 224            }
 225
 226            return true;
 227        }
 228
 229        unlink($a);
 230        return false;
 231    }
 232
 233    /**
 234     * For simpler unit-testing
 235     * @param PEAR_Downloader
 236     * @return PEAR_Downloader_Package
 237     */
 238    function &newDownloaderPackage(&$t)
 239    {
 240        if (!class_exists('PEAR_Downloader_Package')) {
 241            require_once 'PEAR/Downloader/Package.php';
 242        }
 243        $a = &new PEAR_Downloader_Package($t);
 244        return $a;
 245    }
 246
 247    /**
 248     * For simpler unit-testing
 249     * @param PEAR_Config
 250     * @param array
 251     * @param array
 252     * @param int
 253     */
 254    function &getDependency2Object(&$c, $i, $p, $s)
 255    {
 256        if (!class_exists('PEAR_Dependency2')) {
 257            require_once 'PEAR/Dependency2.php';
 258        }
 259        $z = &new PEAR_Dependency2($c, $i, $p, $s);
 260        return $z;
 261    }
 262
 263    function &download($params)
 264    {
 265        if (!count($params)) {
 266            $a = array();
 267            return $a;
 268        }
 269
 270        if (!isset($this->_registry)) {
 271            $this->_registry = &$this->config->getRegistry();
 272        }
 273
 274        $channelschecked = array();
 275        // convert all parameters into PEAR_Downloader_Package objects
 276        foreach ($params as $i => $param) {
 277            $params[$i] = &$this->newDownloaderPackage($this);
 278            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 279            $err = $params[$i]->initialize($param);
 280            PEAR::staticPopErrorHandling();
 281            if (!$err) {
 282                // skip parameters that were missed by preferred_state
 283                continue;
 284            }
 285
 286            if (PEAR::isError($err)) {
 287                if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
 288                    $this->log(0, $err->getMessage());
 289                }
 290
 291                $params[$i] = false;
 292                if (is_object($param)) {
 293                    $param = $param->getChannel() . '/' . $param->getPackage();
 294                }
 295
 296                if (!isset($this->_options['soft'])) {
 297                    $this->log(2, 'Package "' . $param . '" is not valid');
 298                }
 299
 300                // Message logged above in a specific verbose mode, passing null to not show up on CLI
 301                $this->pushError(null, PEAR_INSTALLER_SKIPPED);
 302            } else {
 303                do {
 304                    if ($params[$i] && $params[$i]->getType() == 'local') {
 305                        // bug #7090 skip channel.xml check for local packages
 306                        break;
 307                    }
 308
 309                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
 310                          !isset($this->_options['offline'])
 311                    ) {
 312                        $channelschecked[$params[$i]->getChannel()] = true;
 313                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 314                        if (!class_exists('System')) {
 315                            require_once 'System.php';
 316                        }
 317
 318                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
 319                        if (PEAR::isError($curchannel)) {
 320                            PEAR::staticPopErrorHandling();
 321                            return $this->raiseError($curchannel);
 322                        }
 323
 324                        if (PEAR::isError($dir = $this->getDownloadDir())) {
 325                            PEAR::staticPopErrorHandling();
 326                            break;
 327                        }
 328
 329                        $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
 330                        $url    = 'http://' . $mirror . '/channel.xml';
 331                        $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
 332
 333                        PEAR::staticPopErrorHandling();
 334                        if (PEAR::isError($a) || !$a) {
 335                            // Attempt fallback to https automatically
 336                            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 337                            $a = $this->downloadHttp('https://' . $mirror .
 338                                '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
 339
 340                            PEAR::staticPopErrorHandling();
 341                            if (PEAR::isError($a) || !$a) {
 342                                break;
 343                            }
 344                        }
 345                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
 346                            'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
 347                            '" to update');
 348                    }
 349                } while (false);
 350
 351                if ($params[$i] && !isset($this->_options['downloadonly'])) {
 352                    if (isset($this->_options['packagingroot'])) {
 353                        $checkdir = $this->_prependPath(
 354                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
 355                            $this->_options['packagingroot']);
 356                    } else {
 357                        $checkdir = $this->config->get('php_dir',
 358                            null, $params[$i]->getChannel());
 359                    }
 360
 361                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
 362                        $checkdir = dirname($checkdir);
 363                    }
 364
 365                    if ($checkdir == '.') {
 366                        $checkdir = '/';
 367                    }
 368
 369                    if (!is_writeable($checkdir)) {
 370                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
 371                            $params[$i]->getChannel() . '" is not writeable by the current user');
 372                    }
 373                }
 374            }
 375        }
 376
 377        unset($channelschecked);
 378        PEAR_Downloader_Package::removeDuplicates($params);
 379        if (!count($params)) {
 380            $a = array();
 381            return $a;
 382        }
 383
 384        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
 385            $reverify = true;
 386            while ($reverify) {
 387                $reverify = false;
 388                foreach ($params as $i => $param) {
 389                    //PHP Bug 40768 / PEAR Bug #10944
 390                    //Nested foreaches fail in PHP 5.2.1
 391                    key($params);
 392                    $ret = $params[$i]->detectDependencies($params);
 393                    if (PEAR::isError($ret)) {
 394                        $reverify = true;
 395                        $params[$i] = false;
 396                        PEAR_Downloader_Package::removeDuplicates($params);
 397                        if (!isset($this->_options['soft'])) {
 398                            $this->log(0, $ret->getMessage());
 399                        }
 400                        continue 2;
 401                    }
 402                }
 403            }
 404        }
 405
 406        if (isset($this->_options['offline'])) {
 407            $this->log(3, 'Skipping dependency download check, --offline specified');
 408        }
 409
 410        if (!count($params)) {
 411            $a = array();
 412            return $a;
 413        }
 414
 415        while (PEAR_Downloader_Package::mergeDependencies($params));
 416        PEAR_Downloader_Package::removeDuplicates($params, true);
 417        $errorparams = array();
 418        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
 419            if (count($errorparams)) {
 420                foreach ($errorparams as $param) {
 421                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
 422                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
 423                }
 424                $a = array();
 425                return $a;
 426            }
 427        }
 428
 429        PEAR_Downloader_Package::removeInstalled($params);
 430        if (!count($params)) {
 431            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
 432            $a = array();
 433            return $a;
 434        }
 435
 436        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 437        $err = $this->analyzeDependencies($params);
 438        PEAR::popErrorHandling();
 439        if (!count($params)) {
 440            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
 441            $a = array();
 442            return $a;
 443        }
 444
 445        $ret = array();
 446        $newparams = array();
 447        if (isset($this->_options['pretend'])) {
 448            return $params;
 449        }
 450
 451        $somefailed = false;
 452        foreach ($params as $i => $package) {
 453            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 454            $pf = &$params[$i]->download();
 455            PEAR::staticPopErrorHandling();
 456            if (PEAR::isError($pf)) {
 457                if (!isset($this->_options['soft'])) {
 458                    $this->log(1, $pf->getMessage());
 459                    $this->log(0, 'Error: cannot download "' .
 460                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
 461                            true) .
 462                        '"');
 463                }
 464                $somefailed = true;
 465                continue;
 466            }
 467
 468            $newparams[] = &$params[$i];
 469            $ret[] = array(
 470                'file' => $pf->getArchiveFile(),
 471                'info' => &$pf,
 472                'pkg'  => $pf->getPackage()
 473            );
 474        }
 475
 476        if ($somefailed) {
 477            // remove params that did not download successfully
 478            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 479            $err = $this->analyzeDependencies($newparams, true);
 480            PEAR::popErrorHandling();
 481            if (!count($newparams)) {
 482                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
 483                $a = array();
 484                return $a;
 485            }
 486        }
 487
 488        $this->_downloadedPackages = $ret;
 489        return $newparams;
 490    }
 491
 492    /**
 493     * @param array all packages to be installed
 494     */
 495    function analyzeDependencies(&$params, $force = false)
 496    {
 497        if (isset($this->_options['downloadonly'])) {
 498            return;
 499        }
 500
 501        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 502        $redo  = true;
 503        $reset = $hasfailed = $failed = false;
 504        while ($redo) {
 505            $redo = false;
 506            foreach ($params as $i => $param) {
 507                $deps = $param->getDeps();
 508                if (!$deps) {
 509                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
 510                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
 511                    $send = $param->getPackageFile();
 512
 513                    $installcheck = $depchecker->validatePackage($send, $this, $params);
 514                    if (PEAR::isError($installcheck)) {
 515                        if (!isset($this->_options['soft'])) {
 516                            $this->log(0, $installcheck->getMessage());
 517                        }
 518                        $hasfailed  = true;
 519                        $params[$i] = false;
 520                        $reset      = true;
 521                        $redo       = true;
 522                        $failed     = false;
 523                        PEAR_Downloader_Package::removeDuplicates($params);
 524                        continue 2;
 525                    }
 526                    continue;
 527                }
 528
 529                if (!$reset && $param->alreadyValidated() && !$force) {
 530                    continue;
 531                }
 532
 533                if (count($deps)) {
 534                    $depchecker = &$this->getDependency2Object($this->config, $this->getOptions(),
 535                        $param->getParsedPackage(), PEAR_VALIDATE_DOWNLOADING);
 536                    $send = $param->getPackageFile();
 537                    if ($send === null) {
 538                        $send = $param->getDownloadURL();
 539                    }
 540
 541                    $installcheck = $depchecker->validatePackage($send, $this, $params);
 542                    if (PEAR::isError($installcheck)) {
 543                        if (!isset($this->_options['soft'])) {
 544                            $this->log(0, $installcheck->getMessage());
 545                        }
 546                        $hasfailed  = true;
 547                        $params[$i] = false;
 548                        $reset      = true;
 549                        $redo       = true;
 550                        $failed     = false;
 551                        PEAR_Downloader_Package::removeDuplicates($params);
 552                        continue 2;
 553                    }
 554
 555                    $failed = false;
 556                    if (isset($deps['required']) && is_array($deps['required'])) {
 557                        foreach ($deps['required'] as $type => $dep) {
 558                            // note: Dependency2 will never return a PEAR_Error if ignore-errors
 559                            // is specified, so soft is needed to turn off logging
 560                            if (!isset($dep[0])) {
 561                                if (PEAR::isError($e = $depchecker->{"validate{$type}Dependency"}($dep,
 562                                      true, $params))) {
 563                                    $failed = true;
 564                                    if (!isset($this->_options['soft'])) {
 565                                        $this->log(0, $e->getMessage());
 566                                    }
 567                                } elseif (is_array($e) && !$param->alreadyValidated()) {
 568                                    if (!isset($this->_options['soft'])) {
 569                                        $this->log(0, $e[0]);
 570                                    }
 571                                }
 572                            } else {
 573                                foreach ($dep as $d) {
 574                                    if (PEAR::isError($e =
 575                                          $depchecker->{"validate{$type}Dependency"}($d,
 576                                          true, $params))) {
 577                                        $failed = true;
 578                                        if (!isset($this->_options['soft'])) {
 579                                            $this->log(0, $e->getMessage());
 580                                        }
 581                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
 582                                        if (!isset($this->_options['soft'])) {
 583                                            $this->log(0, $e[0]);
 584                                        }
 585                                    }
 586                                }
 587                            }
 588                        }
 589
 590                        if (isset($deps['optional']) && is_array($deps['optional'])) {
 591                            foreach ($deps['optional'] as $type => $dep) {
 592                                if (!isset($dep[0])) {
 593                                    if (PEAR::isError($e =
 594                                          $depchecker->{"validate{$type}Dependency"}($dep,
 595                                          false, $params))) {
 596                                        $failed = true;
 597                                        if (!isset($this->_options['soft'])) {
 598                                            $this->log(0, $e->getMessage());
 599                                        }
 600                                    } elseif (is_array($e) && !$param->alreadyValidated()) {
 601                                        if (!isset($this->_options['soft'])) {
 602                                            $this->log(0, $e[0]);
 603                                        }
 604                                    }
 605                                } else {
 606                                    foreach ($dep as $d) {
 607                                        if (PEAR::isError($e =
 608                                              $depchecker->{"validate{$type}Dependency"}($d,
 609                                              false, $params))) {
 610                                            $failed = true;
 611                                            if (!isset($this->_options['soft'])) {
 612                                                $this->log(0, $e->getMessage());
 613                                            }
 614                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
 615                                            if (!isset($this->_options['soft'])) {
 616                                                $this->log(0, $e[0]);
 617                                            }
 618                                        }
 619                                    }
 620                                }
 621                            }
 622                        }
 623
 624                        $groupname = $param->getGroup();
 625                        if (isset($deps['group']) && $groupname) {
 626                            if (!isset($deps['group'][0])) {
 627                                $deps['group'] = array($deps['group']);
 628                            }
 629
 630                            $found = false;
 631                            foreach ($deps['group'] as $group) {
 632                                if ($group['attribs']['name'] == $groupname) {
 633                                    $found = true;
 634                                    break;
 635                                }
 636                            }
 637
 638                            if ($found) {
 639                                unset($group['attribs']);
 640                                foreach ($group as $type => $dep) {
 641                                    if (!isset($dep[0])) {
 642                                        if (PEAR::isError($e =
 643                                              $depchecker->{"validate{$type}Dependency"}($dep,
 644                                              false, $params))) {
 645                                            $failed = true;
 646                                            if (!isset($this->_options['soft'])) {
 647                                                $this->log(0, $e->getMessage());
 648                                            }
 649                                        } elseif (is_array($e) && !$param->alreadyValidated()) {
 650                                            if (!isset($this->_options['soft'])) {
 651                                                $this->log(0, $e[0]);
 652                                            }
 653                                        }
 654                                    } else {
 655                                        foreach ($dep as $d) {
 656                                            if (PEAR::isError($e =
 657                                                  $depchecker->{"validate{$type}Dependency"}($d,
 658                                                  false, $params))) {
 659                                                $failed = true;
 660                                                if (!isset($this->_options['soft'])) {
 661                                                    $this->log(0, $e->getMessage());
 662                                                }
 663                                            } elseif (is_array($e) && !$param->alreadyValidated()) {
 664                                                if (!isset($this->_options['soft'])) {
 665                                                    $this->log(0, $e[0]);
 666                                                }
 667                                            }
 668                                        }
 669                                    }
 670                                }
 671                            }
 672                        }
 673                    } else {
 674                        foreach ($deps as $dep) {
 675                            if (PEAR::isError($e = $depchecker->validateDependency1($dep, $params))) {
 676                                $failed = true;
 677                                if (!isset($this->_options['soft'])) {
 678                                    $this->log(0, $e->getMessage());
 679                                }
 680                            } elseif (is_array($e) && !$param->alreadyValidated()) {
 681                                if (!isset($this->_options['soft'])) {
 682                                    $this->log(0, $e[0]);
 683                                }
 684                            }
 685                        }
 686                    }
 687                    $params[$i]->setValidated();
 688                }
 689
 690                if ($failed) {
 691                    $hasfailed  = true;
 692                    $params[$i] = false;
 693                    $reset      = true;
 694                    $redo       = true;
 695                    $failed     = false;
 696                    PEAR_Downloader_Package::removeDuplicates($params);
 697                    continue 2;
 698                }
 699            }
 700        }
 701
 702        PEAR::staticPopErrorHandling();
 703        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
 704              isset($this->_options['nodeps']))) {
 705            // this is probably not needed, but just in case
 706            if (!isset($this->_options['soft'])) {
 707                $this->log(0, 'WARNING: dependencies failed');
 708            }
 709        }
 710    }
 711
 712    /**
 713     * Retrieve the directory that downloads will happen in
 714     * @access private
 715     * @return string
 716     */
 717    function getDownloadDir()
 718    {
 719        if (isset($this->_downloadDir)) {
 720            return $this->_downloadDir;
 721        }
 722
 723        $downloaddir = $this->config->get('download_dir');
 724        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
 725            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
 726                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
 727                    '" is not writeable.  Change download_dir config variable to ' .
 728                    'a writeable dir to avoid this warning');
 729            }
 730
 731            if (!class_exists('System')) {
 732                require_once 'System.php';
 733            }
 734
 735            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
 736                return $downloaddir;
 737            }
 738            $this->log(3, '+ tmp dir created at ' . $downloaddir);
 739        }
 740
 741        if (!is_writable($downloaddir)) {
 742            if (PEAR::isError(System::mkdir(array('-p', $downloaddir))) ||
 743                  !is_writable($downloaddir)) {
 744                return PEAR::raiseError('download directory "' . $downloaddir .
 745                    '" is not writeable.  Change download_dir config variable to ' .
 746                    'a writeable dir');
 747            }
 748        }
 749
 750        return $this->_downloadDir = $downloaddir;
 751    }
 752
 753    function setDownloadDir($dir)
 754    {
 755        if (!@is_writable($dir)) {
 756            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
 757                return PEAR::raiseError('download directory "' . $dir .
 758                    '" is not writeable.  Change download_dir config variable to ' .
 759                    'a writeable dir');
 760            }
 761        }
 762        $this->_downloadDir = $dir;
 763    }
 764
 765    function configSet($key, $value, $layer = 'user', $channel = false)
 766    {
 767        $this->config->set($key, $value, $layer, $channel);
 768        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
 769        if (!$this->_preferredState) {
 770            // don't inadvertantly use a non-set preferred_state
 771            $this->_preferredState = null;
 772        }
 773    }
 774
 775    function setOptions($options)
 776    {
 777        $this->_options = $options;
 778    }
 779
 780    function getOptions()
 781    {
 782        return $this->_options;
 783    }
 784
 785
 786    /**
 787     * @param array output of {@link parsePackageName()}
 788     * @access private
 789     */
 790    function _getPackageDownloadUrl($parr)
 791    {
 792        $curchannel = $this->config->get('default_channel');
 793        $this->configSet('default_channel', $parr['channel']);
 794        // getDownloadURL returns an array.  On error, it only contains information
 795        // on the latest release as array(version, info).  On success it contains
 796        // array(version, info, download url string)
 797        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
 798        if (!$this->_registry->channelExists($parr['channel'])) {
 799            do {
 800                if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
 801                    break;
 802                }
 803
 804                $this->configSet('default_channel', $curchannel);
 805                return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
 806            } while (false);
 807        }
 808
 809        $chan = &$this->_registry->getChannel($parr['channel']);
 810        if (PEAR::isError($chan)) {
 811            return $chan;
 812        }
 813
 814        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 815        $version   = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
 816        $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
 817        // package is installed - use the installed release stability level
 818        if (!isset($parr['state']) && $stability !== null) {
 819            $state = $stability['release'];
 820        }
 821        PEAR::staticPopErrorHandling();
 822        $base2 = false;
 823
 824        $preferred_mirror = $this->config->get('preferred_mirror');
 825        if (!$chan->supportsREST($preferred_mirror) ||
 826              (
 827               !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
 828               &&
 829               !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
 830              )
 831        ) {
 832            return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
 833        }
 834
 835        if ($base2) {
 836            $rest = &$this->config->getREST('1.3', $this->_options);
 837            $base = $base2;
 838        } else {
 839            $rest = &$this->config->getREST('1.0', $this->_options);
 840        }
 841
 842        $downloadVersion = false;
 843        if (!isset($parr['version']) && !isset($parr['state']) && $version
 844              && !PEAR::isError($version)
 845              && !isset($this->_options['downloadonly'])
 846        ) {
 847            $downloadVersion = $version;
 848        }
 849
 850        $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
 851        if (PEAR::isError($url)) {
 852            $this->configSet('default_channel', $curchannel);
 853            return $url;
 854        }
 855
 856        if ($parr['channel'] != $curchannel) {
 857            $this->configSet('default_channel', $curchannel);
 858        }
 859
 860        if (!is_array($url)) {
 861            return $url;
 862        }
 863
 864        $url['raw'] = false; // no checking is necessary for REST
 865        if (!is_array($url['info'])) {
 866            return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
 867                'this should never happen');
 868        }
 869
 870        if (!isset($this->_options['force']) &&
 871              !isset($this->_options['downloadonly']) &&
 872              $version &&
 873              !PEAR::isError($version) &&
 874              !isset($parr['group'])
 875        ) {
 876            if (version_compare($version, $url['version'], '=')) {
 877                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
 878                    $parr, true) . ' is already installed and is the same as the ' .
 879                    'released version ' . $url['version'], -976);
 880            }
 881
 882            if (version_compare($version, $url['version'], '>')) {
 883                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
 884                    $parr, true) . ' is already installed and is newer than detected ' .
 885                    'released version ' . $url['version'], -976);
 886            }
 887        }
 888
 889        if (isset($url['info']['required']) || $url['compatible']) {
 890            require_once 'PEAR/PackageFile/v2.php';
 891            $pf = new PEAR_PackageFile_v2;
 892            $pf->setRawChannel($parr['channel']);
 893            if ($url['compatible']) {
 894                $pf->setRawCompatible($url['compatible']);
 895            }
 896        } else {
 897            require_once 'PEAR/PackageFile/v1.php';
 898            $pf = new PEAR_PackageFile_v1;
 899        }
 900
 901        $pf->setRawPackage($url['package']);
 902        $pf->setDeps($url['info']);
 903        if ($url['compatible']) {
 904            $pf->setCompatible($url['compatible']);
 905        }
 906
 907        $pf->setRawState($url['stability']);
 908        $url['info'] = &$pf;
 909        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
 910            $ext = '.tar';
 911        } else {
 912            $ext = '.tgz';
 913        }
 914
 915        if (is_array($url) && isset($url['url'])) {
 916            $url['url'] .= $ext;
 917        }
 918
 919        return $url;
 920    }
 921
 922    /**
 923     * @param array dependency array
 924     * @access private
 925     */
 926    function _getDepPackageDownloadUrl($dep, $parr)
 927    {
 928        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
 929        $curchannel = $this->config->get('default_channel');
 930        if (isset($dep['uri'])) {
 931            $xsdversion = '2.0';
 932            $chan = &$this->_registry->getChannel('__uri');
 933            if (PEAR::isError($chan)) {
 934                return $chan;
 935            }
 936
 937            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
 938            $this->configSet('default_channel', '__uri');
 939        } else {
 940            if (isset($dep['channel'])) {
 941                $remotechannel = $dep['channel'];
 942            } else {
 943                $remotechannel = 'pear.php.net';
 944            }
 945
 946            if (!$this->_registry->channelExists($remotechannel)) {
 947                do {
 948                    if ($this->config->get('auto_discover')) {
 949                        if ($this->discover($remotechannel)) {
 950                            break;
 951                        }
 952                    }
 953                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
 954                } while (false);
 955            }
 956
 957            $chan = &$this->_registry->getChannel($remotechannel);
 958            if (PEAR::isError($chan)) {
 959                return $chan;
 960            }
 961
 962            $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
 963            $this->configSet('default_channel', $remotechannel);
 964        }
 965
 966        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
 967        if (isset($parr['state']) && isset($parr['version'])) {
 968            unset($parr['state']);
 969        }
 970
 971        if (isset($dep['uri'])) {
 972            $info = &$this->newDownloaderPackage($this);
 973            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 974            $err = $info->initialize($dep);
 975            PEAR::staticPopErrorHandling();
 976            if (!$err) {
 977                // skip parameters that were missed by preferred_state
 978                return PEAR::raiseError('Cannot initialize dependency');
 979            }
 980
 981            if (PEAR::isError($err)) {
 982                if (!isset($this->_options['soft'])) {
 983                    $this->log(0, $err->getMessage());
 984                }
 985
 986                if (is_object($info)) {
 987                    $param = $info->getChannel() . '/' . $info->getPackage();
 988                }
 989                return PEAR::raiseError('Package "' . $param . '" is not valid');
 990            }
 991            return $info;
 992        } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
 993              &&
 994                (
 995                  ($base2 = $chan->getBaseURL('REST1.3', $this->config->get('preferred_mirror')))
 996                    ||
 997                  ($base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror')))
 998                )
 999        ) {
1000            if ($base2) {
1001                $base = $base2;
1002                $rest = &$this->config->getREST('1.3', $this->_options);
1003            } else {
1004                $rest = &$this->config->getREST('1.0', $this->_options);
1005            }
1006
1007            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
1008                    $state, $version, $chan->getName());
1009            if (PEAR::isError($url)) {
1010                return $url;
1011            }
1012
1013            if ($parr['channel'] != $curchannel) {
1014                $this->configSet('default_channel', $curchannel);
1015            }
1016
1017            if (!is_array($url)) {
1018                return $url;
1019            }
1020
1021            $url['raw'] = false; // no checking is necessary for REST
1022            if (!is_array($url['info'])) {
1023                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
1024                    'this should never happen');
1025            }
1026
1027            if (isset($url['info']['required'])) {
1028                if (!class_exists('PEAR_PackageFile_v2')) {
1029                    require_once 'PEAR/PackageFile/v2.php';
1030                }
1031                $pf = new PEAR_PackageFile_v2;
1032                $pf->setRawChannel($remotechannel);
1033            } else {
1034                if (!class_exists('PEAR_PackageFile_v1')) {
1035                    require_once 'PEAR/PackageFile/v1.php';
1036                }
1037                $pf = new PEAR_PackageFile_v1;
1038
1039            }
1040            $pf->setRawPackage($url['package']);
1041            $pf->setDeps($url['info']);
1042            if ($url['compatible']) {
1043                $pf->setCompatible($url['compatible']);
1044            }
1045
1046            $pf->setRawState($url['stability']);
1047            $url['info'] = &$pf;
1048            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
1049                $ext = '.tar';
1050            } else {
1051                $ext = '.tgz';
1052            }
1053
1054            if (is_array($url) && isset($url['url'])) {
1055                $url['url'] .= $ext;
1056            }
1057
1058            return $url;
1059        }
1060
1061        return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
1062    }
1063
1064    /**
1065     * @deprecated in favor of _getPackageDownloadUrl
1066     */
1067    function getPackageDownloadUrl($package, $version = null, $channel = false)
1068    {
1069        if ($version) {
1070            $package .= "-$version";
1071        }
1072        if ($this === null || $this->_registry === null) {
1073            $package = "http://pear.php.net/get/$package";
1074        } else {
1075            $chan = $this->_registry->getChannel($channel);
1076            if (PEAR::isError($chan)) {
1077                return '';
1078            }
1079            $package = "http://" . $chan->getServer() . "/get/$package";
1080        }
1081        if (!extension_loaded("zlib")) {
1082            $package .= '?uncompress=yes';
1083        }
1084        return $package;
1085    }
1086
1087    /**
1088     * Retrieve a list of downloaded packages after a call to {@link download()}.
1089     *
1090     * Also resets the list of downloaded packages.
1091     * @return array
1092     */
1093    function getDownloadedPackages()
1094    {
1095        $ret = $this->_downloadedPackages;
1096        $this->_downloadedPackages = array();
1097        $this->_toDownload = array();
1098        return $ret;
1099    }
1100
1101    function _downloadCallback($msg, $params = null)
1102    {
1103        switch ($msg) {
1104            case 'saveas':
1105                $this->log(1, "downloading $params ...");
1106                break;
1107            case 'done':
1108                $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
1109                break;
1110            case 'bytesread':
1111                static $bytes;
1112                if (empty($bytes)) {
1113                    $bytes = 0;
1114                }
1115                if (!($bytes % 10240)) {
1116                    $this->log(1, '.', false);
1117                }
1118                $bytes += $params;
1119                break;
1120            case 'start':
1121                if($params[1] == -1) {
1122                    $length = "Unknown size";
1123                } else {
1124                    $length = number_format($params[1], 0, '', ',')." bytes";
1125                }
1126                $this->log(1, "Starting to download {$params[0]} ($length)");
1127                break;
1128        }
1129        if (method_exists($this->ui, '_downloadCallback'))
1130            $this->ui->_downloadCallback($msg, $params);
1131    }
1132
1133    function _prependPath($path, $prepend)
1134    {
1135        if (strlen($prepend) > 0) {
1136            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
1137                if (preg_match('/^[a-z]:/i', $prepend)) {
1138                    $prepend = substr($prepend, 2);
1139                } elseif ($prepend{0} != '\\') {
1140                    $prepend = "\\$prepend";
1141                }
1142                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
1143            } else {
1144                $path = $prepend . $path;
1145            }
1146        }
1147        return $path;
1148    }
1149
1150    /**
1151     * @param string
1152     * @param integer
1153     */
1154    function pushError($errmsg, $code = -1)
1155    {
1156        array_push($this->_errorStack, array($errmsg, $code));
1157    }
1158
1159    function getErrorMsgs()
1160    {
1161        $msgs = array();
1162        $errs = $this->_errorStack;
1163        foreach ($errs as $err) {
1164            $msgs[] = $err[0];
1165        }
1166        $this->_errorStack = array();
1167        return $msgs;
1168    }
1169
1170    /**
1171     * for BC
1172     *
1173     * @deprecated
1174     */
1175    function sortPkgDeps(&$packages, $uninstall = false)
1176    {
1177        $uninstall ?
1178            $this->sortPackagesForUninstall($packages) :
1179            $this->sortPackagesForInstall($packages);
1180    }
1181
1182    /**
1183     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
1184     *
1185     * This uses the topological sort method from graph theory, and the
1186     * Structures_Graph package to properly sort dependencies for installation.
1187     * @param array an array of downloaded PEAR_Downloader_Packages
1188     * @return array array of array(packagefilename, package.xml contents)
1189     */
1190    function sortPackagesForInstall(&$packages)
1191    {
1192        require_once 'Structures/Graph.php';
1193        require_once 'Structures/Graph/Node.php';
1194        require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
1195        $depgraph = new Structures_Graph(true);
1196        $nodes = array();
1197        $reg = &$this->config->getRegistry();
1198        foreach ($packages as $i => $package) {
1199            $pname = $reg->parsedPackageNameToString(
1200                array(
1201                    'channel' => $package->getChannel(),
1202                    'package' => strtolower($package->getPackage()),
1203                ));
1204            $nodes[$pname] = new Structures_Graph_Node;
1205            $nodes[$pname]->setData($packages[$i]);
1206            $depgraph->addNode($nodes[$pname]);
1207        }
1208
1209        $deplinks = array();
1210        foreach ($nodes as $package => $node) {
1211            $pf = &$node->getData();
1212            $pdeps = $pf->getDeps(true);
1213            if (!$pdeps) {
1214                continue;
1215            }
1216
1217            if ($pf->getPackagexmlVersion() == '1.0') {
1218                foreach ($pdeps as $dep) {
1219                    if ($dep['type'] != 'pkg' ||
1220                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
1221                        continue;
1222                    }
1223
1224                    $dname = $reg->parsedPackageNameToString(
1225                          array(
1226                              'channel' => 'pear.php.net',
1227                              'package' => strtolower($dep['name']),
1228                          ));
1229
1230                    if (isset($nodes[$dname])) {
1231                        if (!isset($deplinks[$dname])) {
1232                            $deplinks[$dname] = array();
1233                        }
1234
1235                        $deplinks[$dname][$package] = 1;
1236                        // dependency is in installed packages
1237                        continue;
1238                    }
1239
1240                    $dname = $reg->parsedPackageNameToString(
1241                          array(
1242                              'channel' => 'pecl.php.net',
1243                              'package' => strtolower($dep['name']),
1244                          ));
1245
1246                    if (isset($nodes[$dname])) {
1247                        if (!isset($deplinks[$dname])) {
1248                            $deplinks[$dname] = array();
1249                        }
1250
1251                        $deplinks[$dname][$package] = 1;
1252                        // dependency is in installed packages
1253                        continue;
1254                    }
1255                }
1256            } else {
1257                // the only ordering we care about is:
1258                // 1) subpackages must be installed before packages that depend on them
1259                // 2) required deps must be installed before packages that depend on them
1260                if (isset($pdeps['required']['subpackage'])) {
1261                    $t = $pdeps['required']['subpackage'];
1262                    if (!isset($t[0])) {
1263                        $t = array($t);
1264                    }
1265
1266                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1267                }
1268
1269                if (isset($pdeps['group'])) {
1270                    if (!isset($pdeps['group'][0])) {
1271                        $pdeps['group'] = array($pdeps['group']);
1272                    }
1273
1274                    foreach ($pdeps['group'] as $group) {
1275                        if (isset($group['subpackage'])) {
1276                            $t = $group['subpackage'];
1277                            if (!isset($t[0])) {
1278                                $t = array($t);
1279                            }
1280
1281                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1282                        }
1283                    }
1284                }
1285
1286                if (isset($pdeps['optional']['subpackage'])) {
1287                    $t = $pdeps['optional']['subpackage'];
1288                    if (!isset($t[0])) {
1289                        $t = array($t);
1290                    }
1291
1292                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1293                }
1294
1295                if (isset($pdeps['required']['package'])) {
1296                    $t = $pdeps['required']['package'];
1297                    if (!isset($t[0])) {
1298                        $t = array($t);
1299                    }
1300
1301                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1302                }
1303
1304                if (isset($pdeps['group'])) {
1305                    if (!isset($pdeps['group'][0])) {
1306                        $pdeps['group'] = array($pdeps['group']);
1307                    }
1308
1309                    foreach ($pdeps['group'] as $group) {
1310                        if (isset($group['package'])) {
1311                            $t = $group['package'];
1312                            if (!isset($t[0])) {
1313                                $t = array($t);
1314                            }
1315
1316                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1317                        }
1318                    }
1319                }
1320            }
1321        }
1322
1323        $this->_detectDepCycle($deplinks);
1324        foreach ($deplinks as $dependent => $parents) {
1325            foreach ($parents as $parent => $unused) {
1326                $nodes[$dependent]->connectTo($nodes[$parent]);
1327            }
1328        }
1329
1330        $installOrder = Structures_Graph_Manipulator_TopologicalSorter:

Large files files are truncated, but you can click here to view the full file