1<?php 2/** 3 * PEAR_Downloader_Package 4 * 5 * PHP versions 4 and 5 6 * 7 * @category pear 8 * @package PEAR 9 * @author Greg Beaver <cellog@php.net> 10 * @copyright 1997-2009 The Authors 11 * @license http://opensource.org/licenses/bsd-license.php New BSD License 12 * @link http://pear.php.net/package/PEAR 13 * @since File available since Release 1.4.0a1 14 */ 15 16/** 17 * Error code when parameter initialization fails because no releases 18 * exist within preferred_state, but releases do exist 19 */ 20define('PEAR_DOWNLOADER_PACKAGE_STATE', -1003); 21/** 22 * Error code when parameter initialization fails because no releases 23 * exist that will work with the existing PHP version 24 */ 25define('PEAR_DOWNLOADER_PACKAGE_PHPVERSION', -1004); 26 27/** 28 * Coordinates download parameters and manages their dependencies 29 * prior to downloading them. 30 * 31 * Input can come from three sources: 32 * 33 * - local files (archives or package.xml) 34 * - remote files (downloadable urls) 35 * - abstract package names 36 * 37 * The first two elements are handled cleanly by PEAR_PackageFile, but the third requires 38 * accessing pearweb's xml-rpc interface to determine necessary dependencies, and the 39 * format returned of dependencies is slightly different from that used in package.xml. 40 * 41 * This class hides the differences between these elements, and makes automatic 42 * dependency resolution a piece of cake. It also manages conflicts when 43 * two classes depend on incompatible dependencies, or differing versions of the same 44 * package dependency. In addition, download will not be attempted if the php version is 45 * not supported, PEAR installer version is not supported, or non-PECL extensions are not 46 * installed. 47 * @category pear 48 * @package PEAR 49 * @author Greg Beaver <cellog@php.net> 50 * @copyright 1997-2009 The Authors 51 * @license http://opensource.org/licenses/bsd-license.php New BSD License 52 * @version Release: @PEAR-VER@ 53 * @link http://pear.php.net/package/PEAR 54 * @since Class available since Release 1.4.0a1 55 */ 56class PEAR_Downloader_Package 57{ 58 /** 59 * @var PEAR_Downloader 60 */ 61 var $_downloader; 62 /** 63 * @var PEAR_Config 64 */ 65 var $_config; 66 /** 67 * @var PEAR_Registry 68 */ 69 var $_registry; 70 /** 71 * Used to implement packagingroot properly 72 * @var PEAR_Registry 73 */ 74 var $_installRegistry; 75 /** 76 * @var PEAR_PackageFile_v1|PEAR_PackageFile|v2 77 */ 78 var $_packagefile; 79 /** 80 * @var array 81 */ 82 var $_parsedname; 83 /** 84 * @var array 85 */ 86 var $_downloadURL; 87 /** 88 * @var array 89 */ 90 var $_downloadDeps = array(); 91 /** 92 * @var boolean 93 */ 94 var $_valid = false; 95 /** 96 * @var boolean 97 */ 98 var $_analyzed = false; 99 /** 100 * if this or a parent package was invoked with Package-state, this is set to the 101 * state variable. 102 * 103 * This allows temporary reassignment of preferred_state for a parent package and all of 104 * its dependencies. 105 * @var string|false 106 */ 107 var $_explicitState = false; 108 /** 109 * If this package is invoked with Package#group, this variable will be true 110 */ 111 var $_explicitGroup = false; 112 /** 113 * Package type local|url 114 * @var string 115 */ 116 var $_type; 117 /** 118 * Contents of package.xml, if downloaded from a remote channel 119 * @var string|false 120 * @access private 121 */ 122 var $_rawpackagefile; 123 /** 124 * @var boolean 125 * @access private 126 */ 127 var $_validated = false; 128 129 /** 130 * @param PEAR_Downloader 131 */ 132 function __construct(&$downloader) 133 { 134 $this->_downloader = &$downloader; 135 $this->_config = &$this->_downloader->config; 136 $this->_registry = &$this->_config->getRegistry(); 137 $options = $downloader->getOptions(); 138 if (isset($options['packagingroot'])) { 139 $this->_config->setInstallRoot($options['packagingroot']); 140 $this->_installRegistry = &$this->_config->getRegistry(); 141 $this->_config->setInstallRoot(false); 142 } else { 143 $this->_installRegistry = &$this->_registry; 144 } 145 $this->_valid = $this->_analyzed = false; 146 } 147 148 /** 149 * Parse the input and determine whether this is a local file, a remote uri, or an 150 * abstract package name. 151 * 152 * This is the heart of the PEAR_Downloader_Package(), and is used in 153 * {@link PEAR_Downloader::download()} 154 * @param string 155 * @return bool|PEAR_Error 156 */ 157 function initialize($param) 158 { 159 $origErr = $this->_fromFile($param); 160 if ($this->_valid) { 161 return true; 162 } 163 164 $options = $this->_downloader->getOptions(); 165 if (isset($options['offline'])) { 166 if (PEAR::isError($origErr) && !isset($options['soft'])) { 167 foreach ($origErr->getUserInfo() as $userInfo) { 168 if (isset($userInfo['message'])) { 169 $this->_downloader->log(0, $userInfo['message']); 170 } 171 } 172 173 $this->_downloader->log(0, $origErr->getMessage()); 174 } 175 176 return PEAR::raiseError('Cannot download non-local package "' . $param . '"'); 177 } 178 179 $err = $this->_fromUrl($param); 180 if (PEAR::isError($err) || !$this->_valid) { 181 if ($this->_type == 'url') { 182 if (PEAR::isError($err) && !isset($options['soft'])) { 183 $this->_downloader->log(0, $err->getMessage()); 184 } 185 186 return PEAR::raiseError("Invalid or missing remote package file"); 187 } 188 189 $err = $this->_fromString($param); 190 if (PEAR::isError($err) || !$this->_valid) { 191 if (PEAR::isError($err) && $err->getCode() == PEAR_DOWNLOADER_PACKAGE_STATE) { 192 return false; // instruct the downloader to silently skip 193 } 194 195 if (isset($this->_type) && $this->_type == 'local' && PEAR::isError($origErr)) { 196 if (is_array($origErr->getUserInfo())) { 197 foreach ($origErr->getUserInfo() as $err) { 198 if (is_array($err)) { 199 $err = $err['message']; 200 } 201 202 if (!isset($options['soft'])) { 203 $this->_downloader->log(0, $err); 204 } 205 } 206 } 207 208 if (!isset($options['soft'])) { 209 $this->_downloader->log(0, $origErr->getMessage()); 210 } 211 212 if (is_array($param)) { 213 $param = $this->_registry->parsedPackageNameToString($param, true); 214 } 215 216 if (!isset($options['soft'])) { 217 $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); 218 } 219 220 // Passing no message back - already logged above 221 return PEAR::raiseError(); 222 } 223 224 if (PEAR::isError($err) && !isset($options['soft'])) { 225 $this->_downloader->log(0, $err->getMessage()); 226 } 227 228 if (is_array($param)) { 229 $param = $this->_registry->parsedPackageNameToString($param, true); 230 } 231 232 if (!isset($options['soft'])) { 233 $this->_downloader->log(2, "Cannot initialize '$param', invalid or missing package file"); 234 } 235 236 // Passing no message back - already logged above 237 return PEAR::raiseError(); 238 } 239 } 240 241 return true; 242 } 243 244 /** 245 * Retrieve any non-local packages 246 * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|PEAR_Error 247 */ 248 function &download() 249 { 250 if (isset($this->_packagefile)) { 251 return $this->_packagefile; 252 } 253 254 if (isset($this->_downloadURL['url'])) { 255 $this->_isvalid = false; 256 $info = $this->getParsedPackage(); 257 foreach ($info as $i => $p) { 258 $info[$i] = strtolower($p); 259 } 260 261 $err = $this->_fromUrl($this->_downloadURL['url'], 262 $this->_registry->parsedPackageNameToString($this->_parsedname, true)); 263 $newinfo = $this->getParsedPackage(); 264 foreach ($newinfo as $i => $p) { 265 $newinfo[$i] = strtolower($p); 266 } 267 268 if ($info != $newinfo) { 269 do { 270 if ($info['channel'] == 'pecl.php.net' && $newinfo['channel'] == 'pear.php.net') { 271 $info['channel'] = 'pear.php.net'; 272 if ($info == $newinfo) { 273 // skip the channel check if a pecl package says it's a PEAR package 274 break; 275 } 276 } 277 if ($info['channel'] == 'pear.php.net' && $newinfo['channel'] == 'pecl.php.net') { 278 $info['channel'] = 'pecl.php.net'; 279 if ($info == $newinfo) { 280 // skip the channel check if a pecl package says it's a PEAR package 281 break; 282 } 283 } 284 285 return PEAR::raiseError('CRITICAL ERROR: We are ' . 286 $this->_registry->parsedPackageNameToString($info) . ', but the file ' . 287 'downloaded claims to be ' . 288 $this->_registry->parsedPackageNameToString($this->getParsedPackage())); 289 } while (false); 290 } 291 292 if (PEAR::isError($err) || !$this->_valid) { 293 return $err; 294 } 295 } 296 297 $this->_type = 'local'; 298 return $this->_packagefile; 299 } 300 301 function &getPackageFile() 302 { 303 return $this->_packagefile; 304 } 305 306 function &getDownloader() 307 { 308 return $this->_downloader; 309 } 310 311 function getType() 312 { 313 return $this->_type; 314 } 315 316 /** 317 * Like {@link initialize()}, but operates on a dependency 318 */ 319 function fromDepURL($dep) 320 { 321 $this->_downloadURL = $dep; 322 if (isset($dep['uri'])) { 323 $options = $this->_downloader->getOptions(); 324 if (!extension_loaded("zlib") || isset($options['nocompress'])) { 325 $ext = '.tar'; 326 } else { 327 $ext = '.tgz'; 328 } 329 330 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 331 $err = $this->_fromUrl($dep['uri'] . $ext); 332 PEAR::popErrorHandling(); 333 if (PEAR::isError($err)) { 334 if (!isset($options['soft'])) { 335 $this->_downloader->log(0, $err->getMessage()); 336 } 337 338 return PEAR::raiseError('Invalid uri dependency "' . $dep['uri'] . $ext . '", ' . 339 'cannot download'); 340 } 341 } else { 342 $this->_parsedname = 343 array( 344 'package' => $dep['info']->getPackage(), 345 'channel' => $dep['info']->getChannel(), 346 'version' => $dep['version'] 347 ); 348 if (!isset($dep['nodefault'])) { 349 $this->_parsedname['group'] = 'default'; // download the default dependency group 350 $this->_explicitGroup = false; 351 } 352 353 $this->_rawpackagefile = $dep['raw']; 354 } 355 } 356 357 function detectDependencies($params) 358 { 359 $options = $this->_downloader->getOptions(); 360 if (isset($options['downloadonly'])) { 361 return; 362 } 363 364 if (isset($options['offline'])) { 365 $this->_downloader->log(3, 'Skipping dependency download check, --offline specified'); 366 return; 367 } 368 369 $pname = $this->getParsedPackage(); 370 if (!$pname) { 371 return; 372 } 373 374 $deps = $this->getDeps(); 375 if (!$deps) { 376 return; 377 } 378 379 if (isset($deps['required'])) { // package.xml 2.0 380 return $this->_detect2($deps, $pname, $options, $params); 381 } 382 383 return $this->_detect1($deps, $pname, $options, $params); 384 } 385 386 function setValidated() 387 { 388 $this->_validated = true; 389 } 390 391 function alreadyValidated() 392 { 393 return $this->_validated; 394 } 395 396 /** 397 * Remove packages to be downloaded that are already installed 398 * @param array of PEAR_Downloader_Package objects 399 */ 400 public static function removeInstalled(&$params) 401 { 402 if (!isset($params[0])) { 403 return; 404 } 405 406 $options = $params[0]->_downloader->getOptions(); 407 if (!isset($options['downloadonly'])) { 408 foreach ($params as $i => $param) { 409 $package = $param->getPackage(); 410 $channel = $param->getChannel(); 411 // remove self if already installed with this version 412 // this does not need any pecl magic - we only remove exact matches 413 if ($param->_installRegistry->packageExists($package, $channel)) { 414 $packageVersion = $param->_installRegistry->packageInfo($package, 'version', $channel); 415 if (version_compare($packageVersion, $param->getVersion(), '==')) { 416 if (!isset($options['force']) && !isset($options['packagingroot'])) { 417 $info = $param->getParsedPackage(); 418 unset($info['version']); 419 unset($info['state']); 420 if (!isset($options['soft'])) { 421 $param->_downloader->log(1, 'Skipping package "' . 422 $param->getShortName() . 423 '", already installed as version ' . $packageVersion); 424 } 425 $params[$i] = false; 426 } 427 } elseif (!isset($options['force']) && !isset($options['upgrade']) && 428 !isset($options['soft']) && !isset($options['packagingroot'])) { 429 $info = $param->getParsedPackage(); 430 $param->_downloader->log(1, 'Skipping package "' . 431 $param->getShortName() . 432 '", already installed as version ' . $packageVersion); 433 $params[$i] = false; 434 } 435 } 436 } 437 } 438 439 PEAR_Downloader_Package::removeDuplicates($params); 440 } 441 442 function _detect2($deps, $pname, $options, $params) 443 { 444 $this->_downloadDeps = array(); 445 $groupnotfound = false; 446 foreach (array('package', 'subpackage') as $packagetype) { 447 // get required dependency group 448 if (isset($deps['required'][$packagetype])) { 449 if (isset($deps['required'][$packagetype][0])) { 450 foreach ($deps['required'][$packagetype] as $dep) { 451 if (isset($dep['conflicts'])) { 452 // skip any package that this package conflicts with 453 continue; 454 } 455 $ret = $this->_detect2Dep($dep, $pname, 'required', $params); 456 if (is_array($ret)) { 457 $this->_downloadDeps[] = $ret; 458 } elseif (PEAR::isError($ret) && !isset($options['soft'])) { 459 $this->_downloader->log(0, $ret->getMessage()); 460 } 461 } 462 } else { 463 $dep = $deps['required'][$packagetype]; 464 if (!isset($dep['conflicts'])) { 465 // skip any package that this package conflicts with 466 $ret = $this->_detect2Dep($dep, $pname, 'required', $params); 467 if (is_array($ret)) { 468 $this->_downloadDeps[] = $ret; 469 } elseif (PEAR::isError($ret) && !isset($options['soft'])) { 470 $this->_downloader->log(0, $ret->getMessage()); 471 } 472 } 473 } 474 } 475 476 // get optional dependency group, if any 477 if (isset($deps['optional'][$packagetype])) { 478 $skipnames = array(); 479 if (!isset($deps['optional'][$packagetype][0])) { 480 $deps['optional'][$packagetype] = array($deps['optional'][$packagetype]); 481 } 482 483 foreach ($deps['optional'][$packagetype] as $dep) { 484 $skip = false; 485 if (!isset($options['alldeps'])) { 486 $dep['package'] = $dep['name']; 487 if (!isset($options['soft'])) { 488 $this->_downloader->log(3, 'Notice: package "' . 489 $this->_registry->parsedPackageNameToString($this->getParsedPackage(), 490 true) . '" optional dependency "' . 491 $this->_registry->parsedPackageNameToString(array('package' => 492 $dep['name'], 'channel' => 'pear.php.net'), true) . 493 '" will not be automatically downloaded'); 494 } 495 $skipnames[] = $this->_registry->parsedPackageNameToString($dep, true); 496 $skip = true; 497 unset($dep['package']); 498 } 499 500 $ret = $this->_detect2Dep($dep, $pname, 'optional', $params); 501 if (PEAR::isError($ret) && !isset($options['soft'])) { 502 $this->_downloader->log(0, $ret->getMessage()); 503 } 504 505 if (!$ret) { 506 $dep['package'] = $dep['name']; 507 $skip = count($skipnames) ? 508 $skipnames[count($skipnames) - 1] : ''; 509 if ($skip == 510 $this->_registry->parsedPackageNameToString($dep, true)) { 511 array_pop($skipnames); 512 } 513 } 514 515 if (!$skip && is_array($ret)) { 516 $this->_downloadDeps[] = $ret; 517 } 518 } 519 520 if (count($skipnames)) { 521 if (!isset($options['soft'])) { 522 $this->_downloader->log(1, 'Did not download optional dependencies: ' . 523 implode(', ', $skipnames) . 524 ', use --alldeps to download automatically'); 525 } 526 } 527 } 528 529 // get requested dependency group, if any 530 $groupname = $this->getGroup(); 531 $explicit = $this->_explicitGroup; 532 if (!$groupname) { 533 if (!$this->canDefault()) { 534 continue; 535 } 536 537 $groupname = 'default'; // try the default dependency group 538 } 539 540 if ($groupnotfound) { 541 continue; 542 } 543 544 if (isset($deps['group'])) { 545 if (isset($deps['group']['attribs'])) { 546 if (strtolower($deps['group']['attribs']['name']) == strtolower($groupname)) { 547 $group = $deps['group']; 548 } elseif ($explicit) { 549 if (!isset($options['soft'])) { 550 $this->_downloader->log(0, 'Warning: package "' . 551 $this->_registry->parsedPackageNameToString($pname, true) . 552 '" has no dependency ' . 'group named "' . $groupname . '"'); 553 } 554 555 $groupnotfound = true; 556 continue; 557 } 558 } else { 559 $found = false; 560 foreach ($deps['group'] as $group) { 561 if (strtolower($group['attribs']['name']) == strtolower($groupname)) { 562 $found = true; 563 break; 564 } 565 } 566 567 if (!$found) { 568 if ($explicit) { 569 if (!isset($options['soft'])) { 570 $this->_downloader->log(0, 'Warning: package "' . 571 $this->_registry->parsedPackageNameToString($pname, true) . 572 '" has no dependency ' . 'group named "' . $groupname . '"'); 573 } 574 } 575 576 $groupnotfound = true; 577 continue; 578 } 579 } 580 } 581 582 if (isset($group) && isset($group[$packagetype])) { 583 if (isset($group[$packagetype][0])) { 584 foreach ($group[$packagetype] as $dep) { 585 $ret = $this->_detect2Dep($dep, $pname, 'dependency group "' . 586 $group['attribs']['name'] . '"', $params); 587 if (is_array($ret)) { 588 $this->_downloadDeps[] = $ret; 589 } elseif (PEAR::isError($ret) && !isset($options['soft'])) { 590 $this->_downloader->log(0, $ret->getMessage()); 591 } 592 } 593 } else { 594 $ret = $this->_detect2Dep($group[$packagetype], $pname, 595 'dependency group "' . 596 $group['attribs']['name'] . '"', $params); 597 if (is_array($ret)) { 598 $this->_downloadDeps[] = $ret; 599 } elseif (PEAR::isError($ret) && !isset($options['soft'])) { 600 $this->_downloader->log(0, $ret->getMessage()); 601 } 602 } 603 } 604 } 605 } 606 607 function _detect2Dep($dep, $pname, $group, $params) 608 { 609 if (isset($dep['conflicts'])) { 610 return true; 611 } 612 613 $options = $this->_downloader->getOptions(); 614 if (isset($dep['uri'])) { 615 return array('uri' => $dep['uri'], 'dep' => $dep);; 616 } 617 618 $testdep = $dep; 619 $testdep['package'] = $dep['name']; 620 if (PEAR_Downloader_Package::willDownload($testdep, $params)) { 621 $dep['package'] = $dep['name']; 622 if (!isset($options['soft'])) { 623 $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group . 624 ' dependency "' . 625 $this->_registry->parsedPackageNameToString($dep, true) . 626 '", will be installed'); 627 } 628 return false; 629 } 630 631 $options = $this->_downloader->getOptions(); 632 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 633 if ($this->_explicitState) { 634 $pname['state'] = $this->_explicitState; 635 } 636 637 $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); 638 if (PEAR::isError($url)) { 639 PEAR::popErrorHandling(); 640 return $url; 641 } 642 643 $dep['package'] = $dep['name']; 644 $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, $group == 'optional' && 645 !isset($options['alldeps']), true); 646 PEAR::popErrorHandling(); 647 if (PEAR::isError($ret)) { 648 if (!isset($options['soft'])) { 649 $this->_downloader->log(0, $ret->getMessage()); 650 } 651 652 return false; 653 } 654 655 // check to see if a dep is already installed and is the same or newer 656 if (!isset($dep['min']) && !isset($dep['max']) && !isset($dep['recommended'])) { 657 $oper = 'has'; 658 } else { 659 $oper = 'gt'; 660 } 661 662 // do not try to move this before getDepPackageDownloadURL 663 // we can't determine whether upgrade is necessary until we know what 664 // version would be downloaded 665 if (!isset($options['force']) && $this->isInstalled($ret, $oper)) { 666 $version = $this->_installRegistry->packageInfo($dep['name'], 'version', $dep['channel']); 667 $dep['package'] = $dep['name']; 668 if (!isset($options['soft'])) { 669 $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . 670 ' dependency "' . 671 $this->_registry->parsedPackageNameToString($dep, true) . 672 '" version ' . $url['version'] . ', already installed as version ' . 673 $version); 674 } 675 676 return false; 677 } 678 679 if (isset($dep['nodefault'])) { 680 $ret['nodefault'] = true; 681 } 682 683 return $ret; 684 } 685 686 function _detect1($deps, $pname, $options, $params) 687 { 688 $this->_downloadDeps = array(); 689 $skipnames = array(); 690 foreach ($deps as $dep) { 691 $nodownload = false; 692 if (isset ($dep['type']) && $dep['type'] === 'pkg') { 693 $dep['channel'] = 'pear.php.net'; 694 $dep['package'] = $dep['name']; 695 switch ($dep['rel']) { 696 case 'not' : 697 continue 2; 698 case 'ge' : 699 case 'eq' : 700 case 'gt' : 701 case 'has' : 702 $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? 703 'required' : 704 'optional'; 705 if (PEAR_Downloader_Package::willDownload($dep, $params)) { 706 $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group 707 . ' dependency "' . 708 $this->_registry->parsedPackageNameToString($dep, true) . 709 '", will be installed'); 710 continue 2; 711 } 712 $fakedp = new PEAR_PackageFile_v1; 713 $fakedp->setPackage($dep['name']); 714 // skip internet check if we are not upgrading (bug #5810) 715 if (!isset($options['upgrade']) && $this->isInstalled( 716 $fakedp, $dep['rel'])) { 717 $this->_downloader->log(2, $this->getShortName() . ': Skipping ' . $group 718 . ' dependency "' . 719 $this->_registry->parsedPackageNameToString($dep, true) . 720 '", is already installed'); 721 continue 2; 722 } 723 } 724 725 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 726 if ($this->_explicitState) { 727 $pname['state'] = $this->_explicitState; 728 } 729 730 $url = $this->_downloader->_getDepPackageDownloadUrl($dep, $pname); 731 $chan = 'pear.php.net'; 732 if (PEAR::isError($url)) { 733 // check to see if this is a pecl package that has jumped 734 // from pear.php.net to pecl.php.net channel 735 if (!class_exists('PEAR_Dependency2')) { 736 require_once 'PEAR/Dependency2.php'; 737 } 738 739 $newdep = PEAR_Dependency2::normalizeDep($dep); 740 $newdep = $newdep[0]; 741 $newdep['channel'] = 'pecl.php.net'; 742 $chan = 'pecl.php.net'; 743 $url = $this->_downloader->_getDepPackageDownloadUrl($newdep, $pname); 744 $obj = &$this->_installRegistry->getPackage($dep['name']); 745 if (PEAR::isError($url)) { 746 PEAR::popErrorHandling(); 747 if ($obj !== null && $this->isInstalled($obj, $dep['rel'])) { 748 $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? 749 'required' : 750 'optional'; 751 $dep['package'] = $dep['name']; 752 if (!isset($options['soft'])) { 753 $this->_downloader->log(3, $this->getShortName() . 754 ': Skipping ' . $group . ' dependency "' . 755 $this->_registry->parsedPackageNameToString($dep, true) . 756 '", already installed as version ' . $obj->getVersion()); 757 } 758 $skip = count($skipnames) ? 759 $skipnames[count($skipnames) - 1] : ''; 760 if ($skip == 761 $this->_registry->parsedPackageNameToString($dep, true)) { 762 array_pop($skipnames); 763 } 764 continue; 765 } else { 766 if (isset($dep['optional']) && $dep['optional'] == 'yes') { 767 $this->_downloader->log(2, $this->getShortName() . 768 ': Skipping optional dependency "' . 769 $this->_registry->parsedPackageNameToString($dep, true) . 770 '", no releases exist'); 771 continue; 772 } else { 773 return $url; 774 } 775 } 776 } 777 } 778 779 PEAR::popErrorHandling(); 780 if (!isset($options['alldeps'])) { 781 if (isset($dep['optional']) && $dep['optional'] == 'yes') { 782 if (!isset($options['soft'])) { 783 $this->_downloader->log(3, 'Notice: package "' . 784 $this->getShortName() . 785 '" optional dependency "' . 786 $this->_registry->parsedPackageNameToString( 787 array('channel' => $chan, 'package' => 788 $dep['name']), true) . 789 '" will not be automatically downloaded'); 790 } 791 $skipnames[] = $this->_registry->parsedPackageNameToString( 792 array('channel' => $chan, 'package' => 793 $dep['name']), true); 794 $nodownload = true; 795 } 796 } 797 798 if (!isset($options['alldeps']) && !isset($options['onlyreqdeps'])) { 799 if (!isset($dep['optional']) || $dep['optional'] == 'no') { 800 if (!isset($options['soft'])) { 801 $this->_downloader->log(3, 'Notice: package "' . 802 $this->getShortName() . 803 '" required dependency "' . 804 $this->_registry->parsedPackageNameToString( 805 array('channel' => $chan, 'package' => 806 $dep['name']), true) . 807 '" will not be automatically downloaded'); 808 } 809 $skipnames[] = $this->_registry->parsedPackageNameToString( 810 array('channel' => $chan, 'package' => 811 $dep['name']), true); 812 $nodownload = true; 813 } 814 } 815 816 // check to see if a dep is already installed 817 // do not try to move this before getDepPackageDownloadURL 818 // we can't determine whether upgrade is necessary until we know what 819 // version would be downloaded 820 if (!isset($options['force']) && $this->isInstalled( 821 $url, $dep['rel'])) { 822 $group = (!isset($dep['optional']) || $dep['optional'] == 'no') ? 823 'required' : 824 'optional'; 825 $dep['package'] = $dep['name']; 826 if (isset($newdep)) { 827 $version = $this->_installRegistry->packageInfo($newdep['name'], 'version', $newdep['channel']); 828 } else { 829 $version = $this->_installRegistry->packageInfo($dep['name'], 'version'); 830 } 831 832 $dep['version'] = $url['version']; 833 if (!isset($options['soft'])) { 834 $this->_downloader->log(3, $this->getShortName() . ': Skipping ' . $group . 835 ' dependency "' . 836 $this->_registry->parsedPackageNameToString($dep, true) . 837 '", already installed as version ' . $version); 838 } 839 840 $skip = count($skipnames) ? 841 $skipnames[count($skipnames) - 1] : ''; 842 if ($skip == 843 $this->_registry->parsedPackageNameToString($dep, true)) { 844 array_pop($skipnames); 845 } 846 847 continue; 848 } 849 850 if ($nodownload) { 851 continue; 852 } 853 854 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 855 if (isset($newdep)) { 856 $dep = $newdep; 857 } 858 859 $dep['package'] = $dep['name']; 860 $ret = $this->_analyzeDownloadURL($url, 'dependency', $dep, $params, 861 isset($dep['optional']) && $dep['optional'] == 'yes' && 862 !isset($options['alldeps']), true); 863 PEAR::popErrorHandling(); 864 if (PEAR::isError($ret)) { 865 if (!isset($options['soft'])) { 866 $this->_downloader->log(0, $ret->getMessage()); 867 } 868 continue; 869 } 870 871 $this->_downloadDeps[] = $ret; 872 } 873 } 874 875 if (count($skipnames)) { 876 if (!isset($options['soft'])) { 877 $this->_downloader->log(1, 'Did not download dependencies: ' . 878 implode(', ', $skipnames) . 879 ', use --alldeps or --onlyreqdeps to download automatically'); 880 } 881 } 882 } 883 884 function setDownloadURL($pkg) 885 { 886 $this->_downloadURL = $pkg; 887 } 888 889 /** 890 * Set the package.xml object for this downloaded package 891 * 892 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 $pkg 893 */ 894 function setPackageFile(&$pkg) 895 { 896 $this->_packagefile = &$pkg; 897 } 898 899 function getShortName() 900 { 901 return $this->_registry->parsedPackageNameToString(array('channel' => $this->getChannel(), 902 'package' => $this->getPackage()), true); 903 } 904 905 function getParsedPackage() 906 { 907 if (isset($this->_packagefile) || isset($this->_parsedname)) { 908 return array('channel' => $this->getChannel(), 909 'package' => $this->getPackage(), 910 'version' => $this->getVersion()); 911 } 912 913 return false; 914 } 915 916 function getDownloadURL() 917 { 918 return $this->_downloadURL; 919 } 920 921 function canDefault() 922 { 923 if (isset($this->_downloadURL) && isset($this->_downloadURL['nodefault'])) { 924 return false; 925 } 926 927 return true; 928 } 929 930 function getPackage() 931 { 932 if (isset($this->_packagefile)) { 933 return $this->_packagefile->getPackage(); 934 } elseif (isset($this->_downloadURL['info'])) { 935 return $this->_downloadURL['info']->getPackage(); 936 } 937 938 return false; 939 } 940 941 /** 942 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 943 */ 944 function isSubpackage(&$pf) 945 { 946 if (isset($this->_packagefile)) { 947 return $this->_packagefile->isSubpackage($pf); 948 } elseif (isset($this->_downloadURL['info'])) { 949 return $this->_downloadURL['info']->isSubpackage($pf); 950 } 951 952 return false; 953 } 954 955 function getPackageType() 956 { 957 if (isset($this->_packagefile)) { 958 return $this->_packagefile->getPackageType(); 959 } elseif (isset($this->_downloadURL['info'])) { 960 return $this->_downloadURL['info']->getPackageType(); 961 } 962 963 return false; 964 } 965 966 function isBundle() 967 { 968 if (isset($this->_packagefile)) { 969 return $this->_packagefile->getPackageType() == 'bundle'; 970 } 971 972 return false; 973 } 974 975 function getPackageXmlVersion() 976 { 977 if (isset($this->_packagefile)) { 978 return $this->_packagefile->getPackagexmlVersion(); 979 } elseif (isset($this->_downloadURL['info'])) { 980 return $this->_downloadURL['info']->getPackagexmlVersion(); 981 } 982 983 return '1.0'; 984 } 985 986 function getChannel() 987 { 988 if (isset($this->_packagefile)) { 989 return $this->_packagefile->getChannel(); 990 } elseif (isset($this->_downloadURL['info'])) { 991 return $this->_downloadURL['info']->getChannel(); 992 } 993 994 return false; 995 } 996 997 function getURI() 998 { 999 if (isset($this->_packagefile)) { 1000 return $this->_packagefile->getURI(); 1001 } elseif (isset($this->_downloadURL['info'])) { 1002 return $this->_downloadURL['info']->getURI(); 1003 } 1004 1005 return false; 1006 } 1007 1008 function getVersion() 1009 { 1010 if (isset($this->_packagefile)) { 1011 return $this->_packagefile->getVersion(); 1012 } elseif (isset($this->_downloadURL['version'])) { 1013 return $this->_downloadURL['version']; 1014 } 1015 1016 return false; 1017 } 1018 1019 function isCompatible($pf) 1020 { 1021 if (isset($this->_packagefile)) { 1022 return $this->_packagefile->isCompatible($pf); 1023 } elseif (isset($this->_downloadURL['info'])) { 1024 return $this->_downloadURL['info']->isCompatible($pf); 1025 } 1026 1027 return true; 1028 } 1029 1030 function setGroup($group) 1031 { 1032 $this->_parsedname['group'] = $group; 1033 } 1034 1035 function getGroup() 1036 { 1037 if (isset($this->_parsedname['group'])) { 1038 return $this->_parsedname['group']; 1039 } 1040 1041 return ''; 1042 } 1043 1044 function isExtension($name) 1045 { 1046 if (isset($this->_packagefile)) { 1047 return $this->_packagefile->isExtension($name); 1048 } elseif (isset($this->_downloadURL['info'])) { 1049 if ($this->_downloadURL['info']->getPackagexmlVersion() == '2.0') { 1050 return $this->_downloadURL['info']->getProvidesExtension() == $name; 1051 } 1052 1053 return false; 1054 } 1055 1056 return false; 1057 } 1058 1059 function getDeps() 1060 { 1061 if (isset($this->_packagefile)) { 1062 $ver = $this->_packagefile->getPackagexmlVersion(); 1063 if (version_compare($ver, '2.0', '>=')) { 1064 return $this->_packagefile->getDeps(true); 1065 } 1066 1067 return $this->_packagefile->getDeps(); 1068 } elseif (isset($this->_downloadURL['info'])) { 1069 $ver = $this->_downloadURL['info']->getPackagexmlVersion(); 1070 if (version_compare($ver, '2.0', '>=')) { 1071 return $this->_downloadURL['info']->getDeps(true); 1072 } 1073 1074 return $this->_downloadURL['info']->getDeps(); 1075 } 1076 1077 return array(); 1078 } 1079 1080 /** 1081 * @param array Parsed array from {@link PEAR_Registry::parsePackageName()} or a dependency 1082 * returned from getDepDownloadURL() 1083 */ 1084 function isEqual($param) 1085 { 1086 if (is_object($param)) { 1087 $channel = $param->getChannel(); 1088 $package = $param->getPackage(); 1089 if ($param->getURI()) { 1090 $param = array( 1091 'channel' => $param->getChannel(), 1092 'package' => $param->getPackage(), 1093 'version' => $param->getVersion(), 1094 'uri' => $param->getURI(), 1095 ); 1096 } else { 1097 $param = array( 1098 'channel' => $param->getChannel(), 1099 'package' => $param->getPackage(), 1100 'version' => $param->getVersion(), 1101 ); 1102 } 1103 } else { 1104 if (isset($param['uri'])) { 1105 if ($this->getChannel() != '__uri') { 1106 return false; 1107 } 1108 return $param['uri'] == $this->getURI(); 1109 } 1110 1111 $package = isset($param['package']) ? $param['package'] : $param['info']->getPackage(); 1112 $channel = isset($param['channel']) ? $param['channel'] : $param['info']->getChannel(); 1113 if (isset($param['rel'])) { 1114 if (!class_exists('PEAR_Dependency2')) { 1115 require_once 'PEAR/Dependency2.php'; 1116 } 1117 1118 $newdep = PEAR_Dependency2::normalizeDep($param); 1119 $newdep = $newdep[0]; 1120 } elseif (isset($param['min'])) { 1121 $newdep = $param; 1122 } 1123 } 1124 1125 if (isset($newdep)) { 1126 if (!isset($newdep['min'])) { 1127 $newdep['min'] = '0'; 1128 } 1129 1130 if (!isset($newdep['max'])) { 1131 $newdep['max'] = '100000000000000000000'; 1132 } 1133 1134 // use magic to support pecl packages suddenly jumping to the pecl channel 1135 // we need to support both dependency possibilities 1136 if ($channel == 'pear.php.net' && $this->getChannel() == 'pecl.php.net') { 1137 if ($package == $this->getPackage()) { 1138 $channel = 'pecl.php.net'; 1139 } 1140 } 1141 if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { 1142 if ($package == $this->getPackage()) { 1143 $channel = 'pear.php.net'; 1144 } 1145 } 1146 1147 return (strtolower($package) == strtolower($this->getPackage()) && 1148 $channel == $this->getChannel() && 1149 version_compare($newdep['min'], $this->getVersion(), '<=') && 1150 version_compare($newdep['max'], $this->getVersion(), '>=')); 1151 } 1152 1153 // use magic to support pecl packages suddenly jumping to the pecl channel 1154 if ($channel == 'pecl.php.net' && $this->getChannel() == 'pear.php.net') { 1155 if (strtolower($package) == strtolower($this->getPackage())) { 1156 $channel = 'pear.php.net'; 1157 } 1158 } 1159 1160 if (isset($param['version'])) { 1161 return (strtolower($package) == strtolower($this->getPackage()) && 1162 $channel == $this->getChannel() && 1163 $param['version'] == $this->getVersion()); 1164 } 1165 1166 return strtolower($package) == strtolower($this->getPackage()) && 1167 $channel == $this->getChannel(); 1168 } 1169 1170 function isInstalled($dep, $oper = '==') 1171 { 1172 if (!$dep) { 1173 return false; 1174 } 1175 1176 if ($oper != 'ge' && $oper != 'gt' && $oper != 'has' && $oper != '==') { 1177 return false; 1178 } 1179 1180 if (is_object($dep)) { 1181 $package = $dep->getPackage(); 1182 $channel = $dep->getChannel(); 1183 if ($dep->getURI()) { 1184 $dep = array( 1185 'uri' => $dep->getURI(), 1186 'version' => $dep->getVersion(), 1187 ); 1188 } else { 1189 $dep = array( 1190 'version' => $dep->getVersion(), 1191 ); 1192 } 1193 } else { 1194 if (isset($dep['uri'])) { 1195 $channel = '__uri'; 1196 $package = $dep['dep']['name']; 1197 } else { 1198 $channel = $dep['info']->getChannel(); 1199 $package = $dep['info']->getPackage(); 1200 } 1201 } 1202 1203 $options = $this->_downloader->getOptions(); 1204 $test = $this->_installRegistry->packageExists($package, $channel); 1205 if (!$test && $channel == 'pecl.php.net') { 1206 // do magic to allow upgrading from old pecl packages to new ones 1207 $test = $this->_installRegistry->packageExists($package, 'pear.php.net'); 1208 $channel = 'pear.php.net'; 1209 } 1210 1211 if ($test) { 1212 if (isset($dep['uri'])) { 1213 if ($this->_installRegistry->packageInfo($package, 'uri', '__uri') == $dep['uri']) { 1214 return true; 1215 } 1216 } 1217 1218 if (isset($options['upgrade'])) { 1219 $packageVersion = $this->_installRegistry->packageInfo($package, 'version', $channel); 1220 if (version_compare($packageVersion, $dep['version'], '>=')) { 1221 return true; 1222 } 1223 1224 return false; 1225 } 1226 1227 return true; 1228 } 1229 1230 return false; 1231 } 1232 1233 /** 1234 * Detect duplicate package names with differing versions 1235 * 1236 * If a user requests to install Date 1.4.6 and Date 1.4.7, 1237 * for instance, this is a logic error. This method 1238 * detects this situation. 1239 * 1240 * @param array $params array of PEAR_Downloader_Package objects 1241 * @param array $errorparams empty array 1242 * @return array array of stupid duplicated packages in PEAR_Downloader_Package obejcts 1243 */ 1244 public static function detectStupidDuplicates($params, &$errorparams) 1245 { 1246 $existing = array(); 1247 foreach ($params as $i => $param) { 1248 $package = $param->getPackage(); 1249 $channel = $param->getChannel(); 1250 $group = $param->getGroup(); 1251 if (!isset($existing[$channel . '/' . $package])) { 1252 $existing[$channel . '/' . $package] = array(); 1253 } 1254 1255 if (!isset($existing[$channel . '/' . $package][$group])) { 1256 $existing[$channel . '/' . $package][$group] = array(); 1257 } 1258 1259 $existing[$channel . '/' . $package][$group][] = $i; 1260 } 1261 1262 $indices = array(); 1263 foreach ($existing as $package => $groups) { 1264 foreach ($groups as $group => $dupes) { 1265 if (count($dupes) > 1) { 1266 $indices = $indices + $dupes; 1267 } 1268 } 1269 } 1270 1271 $indices = array_unique($indices); 1272 foreach ($indices as $index) { 1273 $errorparams[] = $params[$index]; 1274 } 1275 1276 return count($errorparams); 1277 } 1278 1279 /** 1280 * @param array 1281 * @param bool ignore install groups - for final removal of dupe packages 1282 */ 1283 public static function removeDuplicates(&$params, $ignoreGroups = false) 1284 { 1285 $pnames = array(); 1286 foreach ($params as $i => $param) { 1287 if (!$param) { 1288 continue; 1289 } 1290 1291 if ($param->getPackage()) { 1292 $group = $ignoreGroups ? '' : $param->getGroup(); 1293 $pnames[$i] = $param->getChannel() . '/' . 1294 $param->getPackage() . '-' . $param->getVersion() . '#' . $group; 1295 } 1296 } 1297 1298 $pnames = array_unique($pnames); 1299 $unset = array_diff(array_keys($params), array_keys($pnames)); 1300 $testp = array_flip($pnames); 1301 foreach ($params as $i => $param) { 1302 if (!$param) { 1303 $unset[] = $i; 1304 continue; 1305 } 1306 1307 if (!is_a($param, 'PEAR_Downloader_Package')) { 1308 $unset[] = $i; 1309 continue; 1310 } 1311 1312 $group = $ignoreGroups ? '' : $param->getGroup(); 1313 if (!isset($testp[$param->getChannel() . '/' . $param->getPackage() . '-' . 1314 $param->getVersion() . '#' . $group])) { 1315 $unset[] = $i; 1316 } 1317 } 1318 1319 foreach ($unset as $i) { 1320 unset($params[$i]); 1321 } 1322 1323 $ret = array(); 1324 foreach ($params as $i => $param) { 1325 $ret[] = &$params[$i]; 1326 } 1327 1328 $params = array(); 1329 foreach ($ret as $i => $param) { 1330 $params[] = &$ret[$i]; 1331 } 1332 } 1333 1334 function explicitState() 1335 { 1336 return $this->_explicitState; 1337 } 1338 1339 function setExplicitState($s) 1340 { 1341 $this->_explicitState = $s; 1342 } 1343 1344 /** 1345 */ 1346 public static function mergeDependencies(&$params) 1347 { 1348 $bundles = $newparams = array(); 1349 foreach ($params as $i => $param) { 1350 if (!$param->isBundle()) { 1351 continue; 1352 } 1353 1354 $bundles[] = $i; 1355 $pf = &$param->getPackageFile(); 1356 $newdeps = array(); 1357 $contents = $pf->getBundledPackages(); 1358 if (!is_array($contents)) { 1359 $contents = array($contents); 1360 } 1361 1362 foreach ($contents as $file) { 1363 $filecontents = $pf->getFileContents($file); 1364 $dl = &$param->getDownloader(); 1365 $options = $dl->getOptions(); 1366 if (PEAR::isError($dir = $dl->getDownloadDir())) { 1367 return $dir; 1368 } 1369 1370 $fp = @fopen($dir . DIRECTORY_SEPARATOR . $file, 'wb'); 1371 if (!$fp) { 1372 continue; 1373 } 1374 1375 // FIXME do symlink check 1376 1377 fwrite($fp, $filecontents, strlen($filecontents)); 1378 fclose($fp); 1379 if ($s = $params[$i]->explicitState()) { 1380 $obj->setExplicitState($s); 1381 } 1382 1383 $obj = new PEAR_Downloader_Package($params[$i]->getDownloader()); 1384 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1385 if (PEAR::isError($dir = $dl->getDownloadDir())) { 1386 PEAR::popErrorHandling(); 1387 return $dir; 1388 } 1389 $a = $dir . DIRECTORY_SEPARATOR . $file; 1390 $e = $obj->_fromFile($a); 1391 PEAR::popErrorHandling(); 1392 if (PEAR::isError($e)) { 1393 if (!isset($options['soft'])) { 1394 $dl->log(0, $e->getMessage()); 1395 } 1396 continue; 1397 } 1398 1399 if (!PEAR_Downloader_Package::willDownload($obj, 1400 array_merge($params, $newparams)) && !$param->isInstalled($obj)) { 1401 $newparams[] = $obj; 1402 } 1403 } 1404 } 1405 1406 foreach ($bundles as $i) { 1407 unset($params[$i]); // remove bundles - only their contents matter for installation 1408 } 1409 1410 PEAR_Downloader_Package::removeDuplicates($params); // strip any unset indices 1411 if (count($newparams)) { // add in bundled packages for install 1412 foreach ($newparams as $i => $unused) { 1413 $params[] = &$newparams[$i]; 1414 } 1415 $newparams = array(); 1416 } 1417 1418 foreach ($params as $i => $param) { 1419 $newdeps = array(); 1420 foreach ($param->_downloadDeps as $dep) { 1421 $merge = array_merge($params, $newparams); 1422 if (!PEAR_Downloader_Package::willDownload($dep, $merge) 1423 && !$param->isInstalled($dep) 1424 ) { 1425 $newdeps[] = $dep; 1426 } else { 1427 //var_dump($dep); 1428 // detect versioning conflicts here 1429 } 1430 } 1431 1432 // convert the dependencies into PEAR_Downloader_Package objects for the next time around 1433 $params[$i]->_downloadDeps = array(); 1434 foreach ($newdeps as $dep) { 1435 $obj = new PEAR_Downloader_Package($params[$i]->getDownloader()); 1436 if ($s = $params[$i]->explicitState()) { 1437 $obj->setExplicitState($s); 1438 } 1439 1440 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1441 $e = $obj->fromDepURL($dep); 1442 PEAR::popErrorHandling(); 1443 if (PEAR::isError($e)) { 1444 if (!isset($options['soft'])) { 1445 $obj->_downloader->log(0, $e->getMessage()); 1446 } 1447 continue; 1448 } 1449 1450 $e = $obj->detectDependencies($params); 1451 if (PEAR::isError($e)) { 1452 if (!isset($options['soft'])) { 1453 $obj->_downloader->log(0, $e->getMessage()); 1454 } 1455 } 1456 1457 $newparams[] = $obj; 1458 } 1459 } 1460 1461 if (count($newparams)) { 1462 foreach ($newparams as $i => $unused) { 1463 $params[] = &$newparams[$i]; 1464 } 1465 return true; 1466 } 1467 1468 return false; 1469 } 1470 1471 1472 /** 1473 */ 1474 public static function willDownload($param, $params) 1475 { 1476 if (!is_array($params)) { 1477 return false; 1478 } 1479 1480 foreach ($params as $obj) { 1481 if ($obj->isEqual($param)) { 1482 return true; 1483 } 1484 } 1485 1486 return false; 1487 } 1488 1489 /** 1490 * For simpler unit-testing 1491 * @param PEAR_Config 1492 * @param int 1493 * @param string 1494 */ 1495 function &getPackagefileObject(&$c, $d) 1496 { 1497 $a = new PEAR_PackageFile($c, $d); 1498 return $a; 1499 } 1500 1501 /** 1502 * This will retrieve from a local file if possible, and parse out 1503 * a group name as well. The original parameter will be modified to reflect this. 1504 * @param string|array can be a parsed package name as well 1505 * @access private 1506 */ 1507 function _fromFile(&$param) 1508 { 1509 $saveparam = $param; 1510 if (is_string($param) && substr($param, 0, 10) !== 'channel://') { 1511 if (!@file_exists($param)) { 1512 $test = explode('#', $param); 1513 $group = array_pop($test); 1514 if (@file_exists(implode('#', $test))) { 1515 $this->setGroup($group); 1516 $param = implode('#', $test); 1517 $this->_explicitGroup = true; 1518 } 1519 } 1520 1521 if (@is_file($param)) { 1522 $this->_type = 'local'; 1523 $options = $this->_downloader->getOptions(); 1524 $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->_debug); 1525 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1526 $pf = &$pkg->fromAnyFile($param, PEAR_VALIDATE_INSTALLING); 1527 PEAR::popErrorHandling(); 1528 if (PEAR::isError($pf)) { 1529 $this->_valid = false; 1530 $param = $saveparam; 1531 return $pf; 1532 } 1533 $this->_packagefile = &$pf; 1534 if (!$this->getGroup()) { 1535 $this->setGroup('default'); // install the default dependency group 1536 } 1537 return $this->_valid = true; 1538 } 1539 } 1540 $param = $saveparam; 1541 return $this->_valid = false; 1542 } 1543 1544 function _fromUrl($param, $saveparam = '') 1545 { 1546 if (!is_array($param) && (preg_match('#^(http|https|ftp)://#', $param))) { 1547 $options = $this->_downloader->getOptions(); 1548 $this->_type = 'url'; 1549 $callback = $this->_downloader->ui ? 1550 array(&$this->_downloader, '_downloadCallback') : null; 1551 $this->_downloader->pushErrorHandling(PEAR_ERROR_RETURN); 1552 if (PEAR::isError($dir = $this->_downloader->getDownloadDir())) { 1553 $this->_downloader->popErrorHandling(); 1554 return $dir; 1555 } 1556 1557 $this->_downloader->log(3, 'Downloading "' . $param . '"'); 1558 $file = $this->_downloader->downloadHttp($param, $this->_downloader->ui, 1559 $dir, $callback, null, false, $this->getChannel()); 1560 $this->_downloader->popErrorHandling(); 1561 if (PEAR::isError($file)) { 1562 if (!empty($saveparam)) { 1563 $saveparam = ", cannot download \"$saveparam\""; 1564 } 1565 $err = PEAR::raiseError('Could not download from "' . $param . 1566 '"' . $saveparam . ' (' . $file->getMessage() . ')'); 1567 return $err; 1568 } 1569 1570 if ($this->_rawpackagefile) { 1571 require_once 'Archive/Tar.php'; 1572 $tar = new Archive_Tar($file); 1573 $packagexml = $tar->extractInString('package2.xml'); 1574 if (!$packagexml) { 1575 $packagexml = $tar->extractInString('package.xml'); 1576 } 1577 1578 if (str_replace(array("\n", "\r"), array('',''), $packagexml) != 1579 str_replace(array("\n", "\r"), array('',''), $this->_rawpackagefile)) { 1580 if ($this->getChannel() != 'pear.php.net') { 1581 return PEAR::raiseError('CRITICAL ERROR: package.xml downloaded does ' . 1582 'not match value returned from xml-rpc'); 1583 } 1584 1585 // be more lax for the existing PEAR packages that have not-ok 1586 // characters in their package.xml 1587 $this->_downloader->log(0, 'CRITICAL WARNING: The "' . 1588 $this->getPackage() . '" package has invalid characters in its ' . 1589 'package.xml. The next version of PEAR may not be able to install ' . 1590 'this package for security reasons. Please open a bug report at ' . 1591 'http://pear.php.net/package/' . $this->getPackage() . '/bugs'); 1592 } 1593 } 1594 1595 // whew, download worked! 1596 $pkg = &$this->getPackagefileObject($this->_config, $this->_downloader->debug); 1597 1598 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1599 $pf = &$pkg->fromAnyFile($file, PEAR_VALIDATE_INSTALLING); 1600 PEAR::popErrorHandling(); 1601 if (PEAR::isError($pf)) { 1602 if (is_array($pf->getUserInfo())) { 1603 foreach ($pf->getUserInfo() as $err) { 1604 if (is_array($err)) { 1605 $err = $err['message']; 1606 } 1607 1608 if (!isset($options['soft'])) { 1609 $this->_downloader->log(0, "Validation Error: $err"); 1610 } 1611 } 1612 } 1613 1614 if (!isset($options['soft'])) { 1615 $this->_downloader->log(0, $pf->getMessage()); 1616 } 1617 1618 ///FIXME need to pass back some error code that we can use to match with to cancel all further operations 1619 /// At least stop all deps of this package from being installed 1620 $out = $saveparam ? $saveparam : $param; 1621 $err = PEAR::raiseError('Download of "' . $out . '" succeeded, but it is not a valid package archive'); 1622 $this->_valid = false; 1623 return $err; 1624 } 1625 1626 $this->_packagefile = &$pf; 1627 $this->setGroup('default'); // install the default dependency group 1628 return $this->_valid = true; 1629 } 1630 1631 return $this->_valid = false; 1632 } 1633 1634 /** 1635 * 1636 * @param string|array pass in an array of format 1637 * array( 1638 * 'package' => 'pname', 1639 * ['channel' => 'channame',] 1640 * ['version' => 'version',] 1641 * ['state' => 'state',]) 1642 * or a string of format [channame/]pname[-version|-state] 1643 */ 1644 function _fromString($param) 1645 { 1646 $options = $this->_downloader->getOptions(); 1647 $channel = $this->_config->get('default_channel'); 1648 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1649 $pname = $this->_registry->parsePackageName($param, $channel); 1650 PEAR::popErrorHandling(); 1651 if (PEAR::isError($pname)) { 1652 if ($pname->getCode() == 'invalid') { 1653 $this->_valid = false; 1654 return false; 1655 } 1656 1657 if ($pname->getCode() == 'channel') { 1658 $parsed = $pname->getUserInfo(); 1659 if ($this->_downloader->discover($parsed['channel'])) { 1660 if ($this->_config->get('auto_discover')) { 1661 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1662 $pname = $this->_registry->parsePackageName($param, $channel); 1663 PEAR::popErrorHandling(); 1664 } else { 1665 if (!isset($options['soft'])) { 1666 $this->_downloader->log(0, 'Channel "' . $parsed['channel'] . 1667 '" is not initialized, use ' . 1668 '"pear channel-discover ' . $parsed['channel'] . '" to initialize' . 1669 'or pear config-set auto_discover 1'); 1670 } 1671 } 1672 } 1673 1674 if (PEAR::isError($pname)) { 1675 if (!isset($options['soft'])) { 1676 $this->_downloader->log(0, $pname->getMessage()); 1677 } 1678 1679 if (is_array($param)) { 1680 $param = $this->_registry->parsedPackageNameToString($param); 1681 } 1682 1683 $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); 1684 $this->_valid = false; 1685 return $err; 1686 } 1687 } else { 1688 if (!isset($options['soft'])) { 1689 $this->_downloader->log(0, $pname->getMessage()); 1690 } 1691 1692 $err = PEAR::raiseError('invalid package name/package file "' . $param . '"'); 1693 $this->_valid = false; 1694 return $err; 1695 } 1696 } 1697 1698 if (!isset($this->_type)) { 1699 $this->_type = 'rest'; 1700 } 1701 1702 $this->_parsedname = $pname; 1703 $this->_explicitState = isset($pname['state']) ? $pname['state'] : false; 1704 $this->_explicitGroup = isset($pname['group']) ? true : false; 1705 1706 $info = $this->_downloader->_getPackageDownloadUrl($pname); 1707 if (PEAR::isError($info)) { 1708 if ($info->getCode() != -976 && $pname['channel'] == 'pear.php.net') { 1709 // try pecl 1710 $pname['channel'] = 'pecl.php.net'; 1711 if ($test = $this->_downloader->_getPackageDownloadUrl($pname)) { 1712 if (!PEAR::isError($test)) { 1713 $info = PEAR::raiseError($info->getMessage() . ' - package ' . 1714 $this->_registry->parsedPackageNameToString($pname, true) . 1715 ' can be installed with "pecl install ' . $pname['package'] . 1716 '"'); 1717 } else { 1718 $pname['channel'] = 'pear.php.net'; 1719 } 1720 } else { 1721 $pname['channel'] = 'pear.php.net'; 1722 } 1723 } 1724 1725 return $info; 1726 } 1727 1728 $this->_rawpackagefile = $info['raw']; 1729 $ret = $this->_analyzeDownloadURL($info, $param, $pname); 1730 if (PEAR::isError($ret)) { 1731 return $ret; 1732 } 1733 1734 if ($ret) { 1735 $this->_downloadURL = $ret; 1736 return $this->_valid = (bool) $ret; 1737 } 1738 } 1739 1740 /** 1741 * @param array output of package.getDownloadURL 1742 * @param string|array|object information for detecting packages to be downloaded, and 1743 * for errors 1744 * @param array name information of the package 1745 * @param array|null packages to be downloaded 1746 * @param bool is this an optional dependency? 1747 * @param bool is this any kind of dependency? 1748 * @access private 1749 */ 1750 function _analyzeDownloadURL($info, $param, $pname, $params = null, $optional = false, 1751 $isdependency = false) 1752 { 1753 if (!is_string($param) && PEAR_Downloader_Package::willDownload($param, $params)) { 1754 return false; 1755 } 1756 1757 if ($info === false) { 1758 $saveparam = !is_string($param) ? ", cannot download \"$param\"" : ''; 1759 1760 // no releases exist 1761 return PEAR::raiseError('No releases for package "' . 1762 $this->_registry->parsedPackageNameToString($pname, true) . '" exist' . $saveparam); 1763 } 1764 1765 if (strtolower($info['info']->getChannel()) != strtolower($pname['channel'])) { 1766 $err = false; 1767 if ($pname['channel'] == 'pecl.php.net') { 1768 if ($info['info']->getChannel() != 'pear.php.net') { 1769 $err = true; 1770 } 1771 } elseif ($info['info']->getChannel() == 'pecl.php.net') { 1772 if ($pname['channel'] != 'pear.php.net') { 1773 $err = true; 1774 } 1775 } else { 1776 $err = true; 1777 } 1778 1779 if ($err) { 1780 return PEAR::raiseError('SECURITY ERROR: package in channel "' . $pname['channel'] . 1781 '" retrieved another channel\'s name for download! ("' . 1782 $info['info']->getChannel() . '")'); 1783 } 1784 } 1785 1786 $preferred_state = $this->_config->get('preferred_state'); 1787 if (!isset($info['url'])) { 1788 $package_version = $this->_registry->packageInfo($info['info']->getPackage(), 1789 'version', $info['info']->getChannel()); 1790 if ($this->isInstalled($info)) { 1791 if ($isdependency && version_compare($info['version'], $package_version, '<=')) { 1792 // ignore bogus errors of "failed to download dependency" 1793 // if it is already installed and the one that would be 1794 // downloaded is older or the same version (Bug #7219) 1795 return false; 1796 } 1797 } 1798 1799 if ($info['version'] === $package_version) { 1800 if (!isset($options['soft'])) { 1801 $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . 1802 '/' . $pname['package'] . '-' . $package_version. ', additionally the suggested version' . 1803 ' (' . $package_version . ') is the same as the locally installed one.'); 1804 } 1805 1806 return false; 1807 } 1808 1809 if (version_compare($info['version'], $package_version, '<=')) { 1810 if (!isset($options['soft'])) { 1811 $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . 1812 '/' . $pname['package'] . '-' . $package_version . ', additionally the suggested version' . 1813 ' (' . $info['version'] . ') is a lower version than the locally installed one (' . $package_version . ').'); 1814 } 1815 1816 return false; 1817 } 1818 1819 $instead = ', will instead download version ' . $info['version'] . 1820 ', stability "' . $info['info']->getState() . '"'; 1821 // releases exist, but we failed to get any 1822 if (isset($this->_downloader->_options['force'])) { 1823 if (isset($pname['version'])) { 1824 $vs = ', version "' . $pname['version'] . '"'; 1825 } elseif (isset($pname['state'])) { 1826 $vs = ', stability "' . $pname['state'] . '"'; 1827 } elseif ($param == 'dependency') { 1828 if (!class_exists('PEAR_Common')) { 1829 require_once 'PEAR/Common.php'; 1830 } 1831 1832 if (!in_array($info['info']->getState(), 1833 PEAR_Common::betterStates($preferred_state, true))) { 1834 if ($optional) { 1835 // don't spit out confusing error message 1836 return $this->_downloader->_getPackageDownloadUrl( 1837 array('package' => $pname['package'], 1838 'channel' => $pname['channel'], 1839 'version' => $info['version'])); 1840 } 1841 $vs = ' within preferred state "' . $preferred_state . 1842 '"'; 1843 } else { 1844 if (!class_exists('PEAR_Dependency2')) { 1845 require_once 'PEAR/Dependency2.php'; 1846 } 1847 1848 if ($optional) { 1849 // don't spit out confusing error message 1850 return $this->_downloader->_getPackageDownloadUrl( 1851 array('package' => $pname['package'], 1852 'channel' => $pname['channel'], 1853 'version' => $info['version'])); 1854 } 1855 $vs = PEAR_Dependency2::_getExtraString($pname); 1856 $instead = ''; 1857 } 1858 } else { 1859 $vs = ' within preferred state "' . $preferred_state . '"'; 1860 } 1861 1862 if (!isset($options['soft'])) { 1863 $this->_downloader->log(1, 'WARNING: failed to download ' . $pname['channel'] . 1864 '/' . $pname['package'] . $vs . $instead); 1865 } 1866 1867 // download the latest release 1868 return $this->_downloader->_getPackageDownloadUrl( 1869 array('package' => $pname['package'], 1870 'channel' => $pname['channel'], 1871 'version' => $info['version'])); 1872 } else { 1873 if (isset($info['php']) && $info['php']) { 1874 $err = PEAR::raiseError('Failed to download ' . 1875 $this->_registry->parsedPackageNameToString( 1876 array('channel' => $pname['channel'], 1877 'package' => $pname['package']), 1878 true) . 1879 ', latest release is version ' . $info['php']['v'] . 1880 ', but it requires PHP version "' . 1881 $info['php']['m'] . '", use "' . 1882 $this->_registry->parsedPackageNameToString( 1883 array('channel' => $pname['channel'], 'package' => $pname['package'], 1884 'version' => $info['php']['v'])) . '" to install', 1885 PEAR_DOWNLOADER_PACKAGE_PHPVERSION); 1886 return $err; 1887 } 1888 1889 // construct helpful error message 1890 if (isset($pname['version'])) { 1891 $vs = ', version "' . $pname['version'] . '"'; 1892 } elseif (isset($pname['state'])) { 1893 $vs = ', stability "' . $pname['state'] . '"'; 1894 } elseif ($param == 'dependency') { 1895 if (!class_exists('PEAR_Common')) { 1896 require_once 'PEAR/Common.php'; 1897 } 1898 1899 if (!in_array($info['info']->getState(), 1900 PEAR_Common::betterStates($preferred_state, true))) { 1901 if ($optional) { 1902 // don't spit out confusing error message, and don't die on 1903 // optional dep failure! 1904 return $this->_downloader->_getPackageDownloadUrl( 1905 array('package' => $pname['package'], 1906 'channel' => $pname['channel'], 1907 'version' => $info['version'])); 1908 } 1909 $vs = ' within preferred state "' . $preferred_state . '"'; 1910 } else { 1911 if (!class_exists('PEAR_Dependency2')) { 1912 require_once 'PEAR/Dependency2.php'; 1913 } 1914 1915 if ($optional) { 1916 // don't spit out confusing error message, and don't die on 1917 // optional dep failure! 1918 return $this->_downloader->_getPackageDownloadUrl( 1919 array('package' => $pname['package'], 1920 'channel' => $pname['channel'], 1921 'version' => $info['version'])); 1922 } 1923 $vs = PEAR_Dependency2::_getExtraString($pname); 1924 } 1925 } else { 1926 $vs = ' within preferred state "' . $this->_downloader->config->get('preferred_state') . '"'; 1927 } 1928 1929 $options = $this->_downloader->getOptions(); 1930 // this is only set by the "download-all" command 1931 if (isset($options['ignorepreferred_state'])) { 1932 $err = PEAR::raiseError( 1933 'Failed to download ' . $this->_registry->parsedPackageNameToString( 1934 array('channel' => $pname['channel'], 'package' => $pname['package']), 1935 true) 1936 . $vs . 1937 ', latest release is version ' . $info['version'] . 1938 ', stability "' . $info['info']->getState() . '", use "' . 1939 $this->_registry->parsedPackageNameToString( 1940 array('channel' => $pname['channel'], 'package' => $pname['package'], 1941 'version' => $info['version'])) . '" to install', 1942 PEAR_DOWNLOADER_PACKAGE_STATE); 1943 return $err; 1944 } 1945 1946 // Checks if the user has a package installed already and checks the release against 1947 // the state against the installed package, this allows upgrades for packages 1948 // with lower stability than the preferred_state 1949 $stability = $this->_registry->packageInfo($pname['package'], 'stability', $pname['channel']); 1950 if (!$this->isInstalled($info) 1951 || !in_array($info['info']->getState(), PEAR_Common::betterStates($stability['release'], true)) 1952 ) { 1953 $err = PEAR::raiseError( 1954 'Failed to download ' . $this->_registry->parsedPackageNameToString( 1955 array('channel' => $pname['channel'], 'package' => $pname['package']), 1956 true) 1957 . $vs . 1958 ', latest release is version ' . $info['version'] . 1959 ', stability "' . $info['info']->getState() . '", use "' . 1960 $this->_registry->parsedPackageNameToString( 1961 array('channel' => $pname['channel'], 'package' => $pname['package'], 1962 'version' => $info['version'])) . '" to install'); 1963 return $err; 1964 } 1965 } 1966 } 1967 1968 if (isset($info['deprecated']) && $info['deprecated']) { 1969 $this->_downloader->log(0, 1970 'WARNING: "' . 1971 $this->_registry->parsedPackageNameToString( 1972 array('channel' => $info['info']->getChannel(), 1973 'package' => $info['info']->getPackage()), true) . 1974 '" is deprecated in favor of "' . 1975 $this->_registry->parsedPackageNameToString($info['deprecated'], true) . 1976 '"'); 1977 } 1978 1979 return $info; 1980 } 1981} 1982