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