PageRenderTime 65ms CodeModel.GetById 16ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/gespac/config/PEAR/PEAR/Downloader.php

http://gespac.googlecode.com/
PHP | 1735 lines | 1433 code | 116 blank | 186 comment | 235 complexity | 0646fba5c60d4b906889973c8e7ac6ae 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 296767 2010-03-25 00:58:33Z 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.1
  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        $tmp = System::mktemp(array('-d'));
 193        $a   = $this->downloadHttp('http://' . $channel . '/channel.xml', $this->ui, $tmp, $callback, false);
 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, $callback, false);
 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            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 278            $err = $params[$i]->initialize($param);
 279            PEAR::staticPopErrorHandling();
 280            if (!$err) {
 281                // skip parameters that were missed by preferred_state
 282                continue;
 283            }
 284
 285            if (PEAR::isError($err)) {
 286                if (!isset($this->_options['soft']) && $err->getMessage() !== '') {
 287                    $this->log(0, $err->getMessage());
 288                }
 289
 290                $params[$i] = false;
 291                if (is_object($param)) {
 292                    $param = $param->getChannel() . '/' . $param->getPackage();
 293                }
 294
 295                if (!isset($this->_options['soft'])) {
 296                    $this->log(2, 'Package "' . $param . '" is not valid');
 297                }
 298
 299                // Message logged above in a specific verbose mode, passing null to not show up on CLI
 300                $this->pushError(null, PEAR_INSTALLER_SKIPPED);
 301            } else {
 302                do {
 303                    if ($params[$i] && $params[$i]->getType() == 'local') {
 304                        // bug #7090 skip channel.xml check for local packages
 305                        break;
 306                    }
 307
 308                    if ($params[$i] && !isset($channelschecked[$params[$i]->getChannel()]) &&
 309                          !isset($this->_options['offline'])
 310                    ) {
 311                        $channelschecked[$params[$i]->getChannel()] = true;
 312                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 313                        if (!class_exists('System')) {
 314                            require_once 'System.php';
 315                        }
 316
 317                        $curchannel = &$this->_registry->getChannel($params[$i]->getChannel());
 318                        if (PEAR::isError($curchannel)) {
 319                            PEAR::staticPopErrorHandling();
 320                            return $this->raiseError($curchannel);
 321                        }
 322
 323                        if (PEAR::isError($dir = $this->getDownloadDir())) {
 324                            PEAR::staticPopErrorHandling();
 325                            break;
 326                        }
 327
 328                        $mirror = $this->config->get('preferred_mirror', null, $params[$i]->getChannel());
 329                        $url    = 'http://' . $mirror . '/channel.xml';
 330                        $a = $this->downloadHttp($url, $this->ui, $dir, null, $curchannel->lastModified());
 331
 332                        PEAR::staticPopErrorHandling();
 333                        if (PEAR::isError($a) || !$a) {
 334                            // Attempt fallback to https automatically
 335                            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 336                            $a = $this->downloadHttp('https://' . $mirror .
 337                                '/channel.xml', $this->ui, $dir, null, $curchannel->lastModified());
 338
 339                            PEAR::staticPopErrorHandling();
 340                            if (PEAR::isError($a) || !$a) {
 341                                break;
 342                            }
 343                        }
 344                        $this->log(0, 'WARNING: channel "' . $params[$i]->getChannel() . '" has ' .
 345                            'updated its protocols, use "' . PEAR_RUNTYPE . ' channel-update ' . $params[$i]->getChannel() .
 346                            '" to update');
 347                    }
 348                } while (false);
 349
 350                if ($params[$i] && !isset($this->_options['downloadonly'])) {
 351                    if (isset($this->_options['packagingroot'])) {
 352                        $checkdir = $this->_prependPath(
 353                            $this->config->get('php_dir', null, $params[$i]->getChannel()),
 354                            $this->_options['packagingroot']);
 355                    } else {
 356                        $checkdir = $this->config->get('php_dir',
 357                            null, $params[$i]->getChannel());
 358                    }
 359
 360                    while ($checkdir && $checkdir != '/' && !file_exists($checkdir)) {
 361                        $checkdir = dirname($checkdir);
 362                    }
 363
 364                    if ($checkdir == '.') {
 365                        $checkdir = '/';
 366                    }
 367
 368                    if (!is_writeable($checkdir)) {
 369                        return PEAR::raiseError('Cannot install, php_dir for channel "' .
 370                            $params[$i]->getChannel() . '" is not writeable by the current user');
 371                    }
 372                }
 373            }
 374        }
 375
 376        unset($channelschecked);
 377        PEAR_Downloader_Package::removeDuplicates($params);
 378        if (!count($params)) {
 379            $a = array();
 380            return $a;
 381        }
 382
 383        if (!isset($this->_options['nodeps']) && !isset($this->_options['offline'])) {
 384            $reverify = true;
 385            while ($reverify) {
 386                $reverify = false;
 387                foreach ($params as $i => $param) {
 388                    //PHP Bug 40768 / PEAR Bug #10944
 389                    //Nested foreaches fail in PHP 5.2.1
 390                    key($params);
 391                    $ret = $params[$i]->detectDependencies($params);
 392                    if (PEAR::isError($ret)) {
 393                        $reverify = true;
 394                        $params[$i] = false;
 395                        PEAR_Downloader_Package::removeDuplicates($params);
 396                        if (!isset($this->_options['soft'])) {
 397                            $this->log(0, $ret->getMessage());
 398                        }
 399                        continue 2;
 400                    }
 401                }
 402            }
 403        }
 404
 405        if (isset($this->_options['offline'])) {
 406            $this->log(3, 'Skipping dependency download check, --offline specified');
 407        }
 408
 409        if (!count($params)) {
 410            $a = array();
 411            return $a;
 412        }
 413
 414        while (PEAR_Downloader_Package::mergeDependencies($params));
 415        PEAR_Downloader_Package::removeDuplicates($params, true);
 416        $errorparams = array();
 417        if (PEAR_Downloader_Package::detectStupidDuplicates($params, $errorparams)) {
 418            if (count($errorparams)) {
 419                foreach ($errorparams as $param) {
 420                    $name = $this->_registry->parsedPackageNameToString($param->getParsedPackage());
 421                    $this->pushError('Duplicate package ' . $name . ' found', PEAR_INSTALLER_FAILED);
 422                }
 423                $a = array();
 424                return $a;
 425            }
 426        }
 427
 428        PEAR_Downloader_Package::removeInstalled($params);
 429        if (!count($params)) {
 430            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
 431            $a = array();
 432            return $a;
 433        }
 434
 435        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 436        $err = $this->analyzeDependencies($params);
 437        PEAR::popErrorHandling();
 438        if (!count($params)) {
 439            $this->pushError('No valid packages found', PEAR_INSTALLER_FAILED);
 440            $a = array();
 441            return $a;
 442        }
 443
 444        $ret = array();
 445        $newparams = array();
 446        if (isset($this->_options['pretend'])) {
 447            return $params;
 448        }
 449
 450        $somefailed = false;
 451        foreach ($params as $i => $package) {
 452            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 453            $pf = &$params[$i]->download();
 454            PEAR::staticPopErrorHandling();
 455            if (PEAR::isError($pf)) {
 456                if (!isset($this->_options['soft'])) {
 457                    $this->log(1, $pf->getMessage());
 458                    $this->log(0, 'Error: cannot download "' .
 459                        $this->_registry->parsedPackageNameToString($package->getParsedPackage(),
 460                            true) .
 461                        '"');
 462                }
 463                $somefailed = true;
 464                continue;
 465            }
 466
 467            $newparams[] = &$params[$i];
 468            $ret[] = array(
 469                'file' => $pf->getArchiveFile(),
 470                'info' => &$pf,
 471                'pkg'  => $pf->getPackage()
 472            );
 473        }
 474
 475        if ($somefailed) {
 476            // remove params that did not download successfully
 477            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
 478            $err = $this->analyzeDependencies($newparams, true);
 479            PEAR::popErrorHandling();
 480            if (!count($newparams)) {
 481                $this->pushError('Download failed', PEAR_INSTALLER_FAILED);
 482                $a = array();
 483                return $a;
 484            }
 485        }
 486
 487        $this->_downloadedPackages = $ret;
 488        return $newparams;
 489    }
 490
 491    /**
 492     * @param array all packages to be installed
 493     */
 494    function analyzeDependencies(&$params, $force = false)
 495    {
 496        $hasfailed = $failed = false;
 497        if (isset($this->_options['downloadonly'])) {
 498            return;
 499        }
 500
 501        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 502        $redo  = true;
 503        $reset = 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'])) {
 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'])) {
 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        PEAR::staticPopErrorHandling();
 702        if ($hasfailed && (isset($this->_options['ignore-errors']) ||
 703              isset($this->_options['nodeps']))) {
 704            // this is probably not needed, but just in case
 705            if (!isset($this->_options['soft'])) {
 706                $this->log(0, 'WARNING: dependencies failed');
 707            }
 708        }
 709    }
 710
 711    /**
 712     * Retrieve the directory that downloads will happen in
 713     * @access private
 714     * @return string
 715     */
 716    function getDownloadDir()
 717    {
 718        if (isset($this->_downloadDir)) {
 719            return $this->_downloadDir;
 720        }
 721        $downloaddir = $this->config->get('download_dir');
 722        if (empty($downloaddir) || (is_dir($downloaddir) && !is_writable($downloaddir))) {
 723            if  (is_dir($downloaddir) && !is_writable($downloaddir)) {
 724                $this->log(0, 'WARNING: configuration download directory "' . $downloaddir .
 725                    '" is not writeable.  Change download_dir config variable to ' .
 726                    'a writeable dir to avoid this warning');
 727            }
 728            if (!class_exists('System')) {
 729                require_once 'System.php';
 730            }
 731            if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
 732                return $downloaddir;
 733            }
 734            $this->log(3, '+ tmp dir created at ' . $downloaddir);
 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        return $this->_downloadDir = $downloaddir;
 745    }
 746
 747    function setDownloadDir($dir)
 748    {
 749        if (!@is_writable($dir)) {
 750            if (PEAR::isError(System::mkdir(array('-p', $dir)))) {
 751                return PEAR::raiseError('download directory "' . $dir .
 752                    '" is not writeable.  Change download_dir config variable to ' .
 753                    'a writeable dir');
 754            }
 755        }
 756        $this->_downloadDir = $dir;
 757    }
 758
 759    function configSet($key, $value, $layer = 'user', $channel = false)
 760    {
 761        $this->config->set($key, $value, $layer, $channel);
 762        $this->_preferredState = $this->config->get('preferred_state', null, $channel);
 763        if (!$this->_preferredState) {
 764            // don't inadvertantly use a non-set preferred_state
 765            $this->_preferredState = null;
 766        }
 767    }
 768
 769    function setOptions($options)
 770    {
 771        $this->_options = $options;
 772    }
 773
 774    // }}}
 775    // {{{ setOptions()
 776    function getOptions()
 777    {
 778        return $this->_options;
 779    }
 780
 781    /**
 782     * For simpler unit-testing
 783     * @param PEAR_Config
 784     * @param int
 785     * @param string
 786     */
 787    function &getPackagefileObject(&$c, $d, $t = false)
 788    {
 789        if (!class_exists('PEAR_PackageFile')) {
 790            require_once 'PEAR/PackageFile.php';
 791        }
 792        $a = &new PEAR_PackageFile($c, $d, $t);
 793        return $a;
 794    }
 795
 796    /**
 797     * @param array output of {@link parsePackageName()}
 798     * @access private
 799     */
 800    function _getPackageDownloadUrl($parr)
 801    {
 802        $curchannel = $this->config->get('default_channel');
 803        $this->configSet('default_channel', $parr['channel']);
 804        // getDownloadURL returns an array.  On error, it only contains information
 805        // on the latest release as array(version, info).  On success it contains
 806        // array(version, info, download url string)
 807        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
 808        if (!$this->_registry->channelExists($parr['channel'])) {
 809            do {
 810                if ($this->config->get('auto_discover') && $this->discover($parr['channel'])) {
 811                    break;
 812                }
 813
 814                $this->configSet('default_channel', $curchannel);
 815                return PEAR::raiseError('Unknown remote channel: ' . $parr['channel']);
 816            } while (false);
 817        }
 818
 819        $chan = &$this->_registry->getChannel($parr['channel']);
 820        if (PEAR::isError($chan)) {
 821            return $chan;
 822        }
 823
 824        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 825        $version   = $this->_registry->packageInfo($parr['package'], 'version', $parr['channel']);
 826        $stability = $this->_registry->packageInfo($parr['package'], 'stability', $parr['channel']);
 827        // package is installed - use the installed release stability level
 828        if (!isset($parr['state']) && $stability !== null) {
 829            $state = $stability['release'];
 830        }
 831        PEAR::staticPopErrorHandling();
 832        $base2 = false;
 833
 834        $preferred_mirror = $this->config->get('preferred_mirror');
 835        if (!$chan->supportsREST($preferred_mirror) ||
 836              (
 837               !($base2 = $chan->getBaseURL('REST1.3', $preferred_mirror))
 838               &&
 839               !($base = $chan->getBaseURL('REST1.0', $preferred_mirror))
 840              )
 841        ) {
 842            return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
 843        }
 844
 845        if ($base2) {
 846            $rest = &$this->config->getREST('1.3', $this->_options);
 847            $base = $base2;
 848        } else {
 849            $rest = &$this->config->getREST('1.0', $this->_options);
 850        }
 851
 852        $downloadVersion = false;
 853        if (!isset($parr['version']) && !isset($parr['state']) && $version
 854              && !PEAR::isError($version)
 855              && !isset($this->_options['downloadonly'])
 856        ) {
 857            $downloadVersion = $version;
 858        }
 859
 860        $url = $rest->getDownloadURL($base, $parr, $state, $downloadVersion, $chan->getName());
 861        if (PEAR::isError($url)) {
 862            $this->configSet('default_channel', $curchannel);
 863            return $url;
 864        }
 865
 866        if ($parr['channel'] != $curchannel) {
 867            $this->configSet('default_channel', $curchannel);
 868        }
 869
 870        if (!is_array($url)) {
 871            return $url;
 872        }
 873
 874        $url['raw'] = false; // no checking is necessary for REST
 875        if (!is_array($url['info'])) {
 876            return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
 877                'this should never happen');
 878        }
 879
 880        if (!isset($this->_options['force']) &&
 881              !isset($this->_options['downloadonly']) &&
 882              $version &&
 883              !PEAR::isError($version) &&
 884              !isset($parr['group'])
 885        ) {
 886            if (version_compare($version, $url['version'], '=')) {
 887                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
 888                    $parr, true) . ' is already installed and is the same as the ' .
 889                    'released version ' . $url['version'], -976);
 890            }
 891
 892            if (version_compare($version, $url['version'], '>')) {
 893                return PEAR::raiseError($this->_registry->parsedPackageNameToString(
 894                    $parr, true) . ' is already installed and is newer than detected ' .
 895                    'released version ' . $url['version'], -976);
 896            }
 897        }
 898
 899        if (isset($url['info']['required']) || $url['compatible']) {
 900            require_once 'PEAR/PackageFile/v2.php';
 901            $pf = new PEAR_PackageFile_v2;
 902            $pf->setRawChannel($parr['channel']);
 903            if ($url['compatible']) {
 904                $pf->setRawCompatible($url['compatible']);
 905            }
 906        } else {
 907            require_once 'PEAR/PackageFile/v1.php';
 908            $pf = new PEAR_PackageFile_v1;
 909        }
 910
 911        $pf->setRawPackage($url['package']);
 912        $pf->setDeps($url['info']);
 913        if ($url['compatible']) {
 914            $pf->setCompatible($url['compatible']);
 915        }
 916
 917        $pf->setRawState($url['stability']);
 918        $url['info'] = &$pf;
 919        if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
 920            $ext = '.tar';
 921        } else {
 922            $ext = '.tgz';
 923        }
 924
 925        if (is_array($url) && isset($url['url'])) {
 926            $url['url'] .= $ext;
 927        }
 928
 929        return $url;
 930    }
 931
 932    /**
 933     * @param array dependency array
 934     * @access private
 935     */
 936    function _getDepPackageDownloadUrl($dep, $parr)
 937    {
 938        $xsdversion = isset($dep['rel']) ? '1.0' : '2.0';
 939        $curchannel = $this->config->get('default_channel');
 940        if (isset($dep['uri'])) {
 941            $xsdversion = '2.0';
 942            $chan = &$this->_registry->getChannel('__uri');
 943            if (PEAR::isError($chan)) {
 944                return $chan;
 945            }
 946
 947            $version = $this->_registry->packageInfo($dep['name'], 'version', '__uri');
 948            $this->configSet('default_channel', '__uri');
 949        } else {
 950            if (isset($dep['channel'])) {
 951                $remotechannel = $dep['channel'];
 952            } else {
 953                $remotechannel = 'pear.php.net';
 954            }
 955
 956            if (!$this->_registry->channelExists($remotechannel)) {
 957                do {
 958                    if ($this->config->get('auto_discover')) {
 959                        if ($this->discover($remotechannel)) {
 960                            break;
 961                        }
 962                    }
 963                    return PEAR::raiseError('Unknown remote channel: ' . $remotechannel);
 964                } while (false);
 965            }
 966
 967            $chan = &$this->_registry->getChannel($remotechannel);
 968            if (PEAR::isError($chan)) {
 969                return $chan;
 970            }
 971
 972            $version = $this->_registry->packageInfo($dep['name'], 'version', $remotechannel);
 973            $this->configSet('default_channel', $remotechannel);
 974        }
 975
 976        $state = isset($parr['state']) ? $parr['state'] : $this->config->get('preferred_state');
 977        if (isset($parr['state']) && isset($parr['version'])) {
 978            unset($parr['state']);
 979        }
 980
 981        if (isset($dep['uri'])) {
 982            $info = &$this->newDownloaderPackage($this);
 983            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
 984            $err = $info->initialize($dep);
 985            PEAR::staticPopErrorHandling();
 986            if (!$err) {
 987                // skip parameters that were missed by preferred_state
 988                return PEAR::raiseError('Cannot initialize dependency');
 989            }
 990
 991            if (PEAR::isError($err)) {
 992                if (!isset($this->_options['soft'])) {
 993                    $this->log(0, $err->getMessage());
 994                }
 995
 996                if (is_object($info)) {
 997                    $param = $info->getChannel() . '/' . $info->getPackage();
 998                }
 999                return PEAR::raiseError('Package "' . $param . '" is not valid');
1000            }
1001            return $info;
1002        } elseif ($chan->supportsREST($this->config->get('preferred_mirror'))
1003              && $base = $chan->getBaseURL('REST1.0', $this->config->get('preferred_mirror'))
1004        ) {
1005            $rest = &$this->config->getREST('1.0', $this->_options);
1006            $url = $rest->getDepDownloadURL($base, $xsdversion, $dep, $parr,
1007                    $state, $version, $chan->getName());
1008            if (PEAR::isError($url)) {
1009                return $url;
1010            }
1011
1012            if ($parr['channel'] != $curchannel) {
1013                $this->configSet('default_channel', $curchannel);
1014            }
1015
1016            if (!is_array($url)) {
1017                return $url;
1018            }
1019
1020            $url['raw'] = false; // no checking is necessary for REST
1021            if (!is_array($url['info'])) {
1022                return PEAR::raiseError('Invalid remote dependencies retrieved from REST - ' .
1023                    'this should never happen');
1024            }
1025
1026            if (isset($url['info']['required'])) {
1027                if (!class_exists('PEAR_PackageFile_v2')) {
1028                    require_once 'PEAR/PackageFile/v2.php';
1029                }
1030                $pf = new PEAR_PackageFile_v2;
1031                $pf->setRawChannel($remotechannel);
1032            } else {
1033                if (!class_exists('PEAR_PackageFile_v1')) {
1034                    require_once 'PEAR/PackageFile/v1.php';
1035                }
1036                $pf = new PEAR_PackageFile_v1;
1037
1038            }
1039            $pf->setRawPackage($url['package']);
1040            $pf->setDeps($url['info']);
1041            if ($url['compatible']) {
1042                $pf->setCompatible($url['compatible']);
1043            }
1044
1045            $pf->setRawState($url['stability']);
1046            $url['info'] = &$pf;
1047            if (!extension_loaded("zlib") || isset($this->_options['nocompress'])) {
1048                $ext = '.tar';
1049            } else {
1050                $ext = '.tgz';
1051            }
1052
1053            if (is_array($url) && isset($url['url'])) {
1054                $url['url'] .= $ext;
1055            }
1056
1057            return $url;
1058        }
1059
1060        return $this->raiseError($parr['channel'] . ' is using a unsupported protocol - This should never happen.');
1061    }
1062
1063    /**
1064     * @deprecated in favor of _getPackageDownloadUrl
1065     */
1066    function getPackageDownloadUrl($package, $version = null, $channel = false)
1067    {
1068        if ($version) {
1069            $package .= "-$version";
1070        }
1071        if ($this === null || $this->_registry === null) {
1072            $package = "http://pear.php.net/get/$package";
1073        } else {
1074            $chan = $this->_registry->getChannel($channel);
1075            if (PEAR::isError($chan)) {
1076                return '';
1077            }
1078            $package = "http://" . $chan->getServer() . "/get/$package";
1079        }
1080        if (!extension_loaded("zlib")) {
1081            $package .= '?uncompress=yes';
1082        }
1083        return $package;
1084    }
1085
1086    /**
1087     * Retrieve a list of downloaded packages after a call to {@link download()}.
1088     *
1089     * Also resets the list of downloaded packages.
1090     * @return array
1091     */
1092    function getDownloadedPackages()
1093    {
1094        $ret = $this->_downloadedPackages;
1095        $this->_downloadedPackages = array();
1096        $this->_toDownload = array();
1097        return $ret;
1098    }
1099
1100    function _downloadCallback($msg, $params = null)
1101    {
1102        switch ($msg) {
1103            case 'saveas':
1104                $this->log(1, "downloading $params ...");
1105                break;
1106            case 'done':
1107                $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
1108                break;
1109            case 'bytesread':
1110                static $bytes;
1111                if (empty($bytes)) {
1112                    $bytes = 0;
1113                }
1114                if (!($bytes % 10240)) {
1115                    $this->log(1, '.', false);
1116                }
1117                $bytes += $params;
1118                break;
1119            case 'start':
1120                if($params[1] == -1) {
1121                    $length = "Unknown size";
1122                } else {
1123                    $length = number_format($params[1], 0, '', ',')." bytes";
1124                }
1125                $this->log(1, "Starting to download {$params[0]} ($length)");
1126                break;
1127        }
1128        if (method_exists($this->ui, '_downloadCallback'))
1129            $this->ui->_downloadCallback($msg, $params);
1130    }
1131
1132    function _prependPath($path, $prepend)
1133    {
1134        if (strlen($prepend) > 0) {
1135            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
1136                if (preg_match('/^[a-z]:/i', $prepend)) {
1137                    $prepend = substr($prepend, 2);
1138                } elseif ($prepend{0} != '\\') {
1139                    $prepend = "\\$prepend";
1140                }
1141                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
1142            } else {
1143                $path = $prepend . $path;
1144            }
1145        }
1146        return $path;
1147    }
1148
1149    /**
1150     * @param string
1151     * @param integer
1152     */
1153    function pushError($errmsg, $code = -1)
1154    {
1155        array_push($this->_errorStack, array($errmsg, $code));
1156    }
1157
1158    function getErrorMsgs()
1159    {
1160        $msgs = array();
1161        $errs = $this->_errorStack;
1162        foreach ($errs as $err) {
1163            $msgs[] = $err[0];
1164        }
1165        $this->_errorStack = array();
1166        return $msgs;
1167    }
1168
1169    /**
1170     * for BC
1171     *
1172     * @deprecated
1173     */
1174    function sortPkgDeps(&$packages, $uninstall = false)
1175    {
1176        $uninstall ?
1177            $this->sortPackagesForUninstall($packages) :
1178            $this->sortPackagesForInstall($packages);
1179    }
1180
1181    /**
1182     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
1183     *
1184     * This uses the topological sort method from graph theory, and the
1185     * Structures_Graph package to properly sort dependencies for installation.
1186     * @param array an array of downloaded PEAR_Downloader_Packages
1187     * @return array array of array(packagefilename, package.xml contents)
1188     */
1189    function sortPackagesForInstall(&$packages)
1190    {
1191        require_once 'Structures/Graph.php';
1192        require_once 'Structures/Graph/Node.php';
1193        require_once 'Structures/Graph/Manipulator/TopologicalSorter.php';
1194        $depgraph = new Structures_Graph(true);
1195        $nodes = array();
1196        $reg = &$this->config->getRegistry();
1197        foreach ($packages as $i => $package) {
1198            $pname = $reg->parsedPackageNameToString(
1199                array(
1200                    'channel' => $package->getChannel(),
1201                    'package' => strtolower($package->getPackage()),
1202                ));
1203            $nodes[$pname] = new Structures_Graph_Node;
1204            $nodes[$pname]->setData($packages[$i]);
1205            $depgraph->addNode($nodes[$pname]);
1206        }
1207
1208        $deplinks = array();
1209        foreach ($nodes as $package => $node) {
1210            $pf = &$node->getData();
1211            $pdeps = $pf->getDeps(true);
1212            if (!$pdeps) {
1213                continue;
1214            }
1215
1216            if ($pf->getPackagexmlVersion() == '1.0') {
1217                foreach ($pdeps as $dep) {
1218                    if ($dep['type'] != 'pkg' ||
1219                          (isset($dep['optional']) && $dep['optional'] == 'yes')) {
1220                        continue;
1221                    }
1222
1223                    $dname = $reg->parsedPackageNameToString(
1224                          array(
1225                              'channel' => 'pear.php.net',
1226                              'package' => strtolower($dep['name']),
1227                          ));
1228
1229                    if (isset($nodes[$dname])) {
1230                        if (!isset($deplinks[$dname])) {
1231                            $deplinks[$dname] = array();
1232                        }
1233
1234                        $deplinks[$dname][$package] = 1;
1235                        // dependency is in installed packages
1236                        continue;
1237                    }
1238
1239                    $dname = $reg->parsedPackageNameToString(
1240                          array(
1241                              'channel' => 'pecl.php.net',
1242                              'package' => strtolower($dep['name']),
1243                          ));
1244
1245                    if (isset($nodes[$dname])) {
1246                        if (!isset($deplinks[$dname])) {
1247                            $deplinks[$dname] = array();
1248                        }
1249
1250                        $deplinks[$dname][$package] = 1;
1251                        // dependency is in installed packages
1252                        continue;
1253                    }
1254                }
1255            } else {
1256                // the only ordering we care about is:
1257                // 1) subpackages must be installed before packages that depend on them
1258                // 2) required deps must be installed before packages that depend on them
1259                if (isset($pdeps['required']['subpackage'])) {
1260                    $t = $pdeps['required']['subpackage'];
1261                    if (!isset($t[0])) {
1262                        $t = array($t);
1263                    }
1264
1265                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1266                }
1267
1268                if (isset($pdeps['group'])) {
1269                    if (!isset($pdeps['group'][0])) {
1270                        $pdeps['group'] = array($pdeps['group']);
1271                    }
1272
1273                    foreach ($pdeps['group'] as $group) {
1274                        if (isset($group['subpackage'])) {
1275                            $t = $group['subpackage'];
1276                            if (!isset($t[0])) {
1277                                $t = array($t);
1278                            }
1279
1280                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1281                        }
1282                    }
1283                }
1284
1285                if (isset($pdeps['optional']['subpackage'])) {
1286                    $t = $pdeps['optional']['subpackage'];
1287                    if (!isset($t[0])) {
1288                        $t = array($t);
1289                    }
1290
1291                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1292                }
1293
1294                if (isset($pdeps['required']['package'])) {
1295                    $t = $pdeps['required']['package'];
1296                    if (!isset($t[0])) {
1297                        $t = array($t);
1298                    }
1299
1300                    $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1301                }
1302
1303                if (isset($pdeps['group'])) {
1304                    if (!isset($pdeps['group'][0])) {
1305                        $pdeps['group'] = array($pdeps['group']);
1306                    }
1307
1308                    foreach ($pdeps['group'] as $group) {
1309                        if (isset($group['package'])) {
1310                            $t = $group['package'];
1311                            if (!isset($t[0])) {
1312                                $t = array($t);
1313                            }
1314
1315                            $this->_setupGraph($t, $reg, $deplinks, $nodes, $package);
1316                        }
1317                    }
1318                }
1319            }
1320        }
1321
1322        $this->_detectDepCycle($deplinks);
1323        foreach ($deplinks as $dependent => $parents) {
1324            foreach ($parents as $parent => $unused) {
1325                $nodes[$dependent]->connectTo($nodes[$parent]);
1326            }
1327        }
1328
1329        $installOrder = Structures_Graph_Manipulator_TopologicalSorter::sort($depgraph);
1330        $ret = array();
1331        for ($i = 0, $count = co…

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