[ Index ]
 

Code source de GeekLog 1.4.1

Accédez au Source d'autres logiciels libres

title

Body

[fermer]

/system/pear/PEAR/ -> Downloader.php (source)

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


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