1<?php 2/** 3 * PEAR_Registry 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 Stig Bakken <ssb@php.net> 16 * @author Tomas V. V. Cox <cox@idecnet.com> 17 * @author Greg Beaver <cellog@php.net> 18 * @copyright 1997-2006 The PHP Group 19 * @license http://www.php.net/license/3_0.txt PHP License 3.0 20 * @version CVS: $Id: Registry.php,v 1.150.2.2 2006/03/11 04:16:48 cellog Exp $ 21 * @link http://pear.php.net/package/PEAR 22 * @since File available since Release 0.1 23 */ 24 25/** 26 * for PEAR_Error 27 */ 28require_once 'PEAR.php'; 29require_once 'PEAR/DependencyDB.php'; 30 31define('PEAR_REGISTRY_ERROR_LOCK', -2); 32define('PEAR_REGISTRY_ERROR_FORMAT', -3); 33define('PEAR_REGISTRY_ERROR_FILE', -4); 34define('PEAR_REGISTRY_ERROR_CONFLICT', -5); 35define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6); 36 37/** 38 * Administration class used to maintain the installed package database. 39 * @category pear 40 * @package PEAR 41 * @author Stig Bakken <ssb@php.net> 42 * @author Tomas V. V. Cox <cox@idecnet.com> 43 * @author Greg Beaver <cellog@php.net> 44 * @copyright 1997-2006 The PHP Group 45 * @license http://www.php.net/license/3_0.txt PHP License 3.0 46 * @version Release: 1.4.11 47 * @link http://pear.php.net/package/PEAR 48 * @since Class available since Release 1.4.0a1 49 */ 50class PEAR_Registry extends PEAR 51{ 52 // {{{ properties 53 54 /** 55 * File containing all channel information. 56 * @var string 57 */ 58 var $channels = ''; 59 60 /** Directory where registry files are stored. 61 * @var string 62 */ 63 var $statedir = ''; 64 65 /** File where the file map is stored 66 * @var string 67 */ 68 var $filemap = ''; 69 70 /** Directory where registry files for channels are stored. 71 * @var string 72 */ 73 var $channelsdir = ''; 74 75 /** Name of file used for locking the registry 76 * @var string 77 */ 78 var $lockfile = ''; 79 80 /** File descriptor used during locking 81 * @var resource 82 */ 83 var $lock_fp = null; 84 85 /** Mode used during locking 86 * @var int 87 */ 88 var $lock_mode = 0; // XXX UNUSED 89 90 /** Cache of package information. Structure: 91 * array( 92 * 'package' => array('id' => ... ), 93 * ... ) 94 * @var array 95 */ 96 var $pkginfo_cache = array(); 97 98 /** Cache of file map. Structure: 99 * array( '/path/to/file' => 'package', ... ) 100 * @var array 101 */ 102 var $filemap_cache = array(); 103 104 /** 105 * @var false|PEAR_ChannelFile 106 */ 107 var $_pearChannel; 108 109 /** 110 * @var false|PEAR_ChannelFile 111 */ 112 var $_peclChannel; 113 114 /** 115 * @var PEAR_DependencyDB 116 */ 117 var $_dependencyDB; 118 119 /** 120 * @var PEAR_Config 121 */ 122 var $_config; 123 // }}} 124 125 // {{{ constructor 126 127 /** 128 * PEAR_Registry constructor. 129 * 130 * @param string (optional) PEAR install directory (for .php files) 131 * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if 132 * default values are not desired. Only used the very first time a PEAR 133 * repository is initialized 134 * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if 135 * default values are not desired. Only used the very first time a PEAR 136 * repository is initialized 137 * 138 * @access public 139 */ 140 function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false, 141 $pecl_channel = false) 142 { 143 parent::PEAR(); 144 $ds = DIRECTORY_SEPARATOR; 145 $this->install_dir = $pear_install_dir; 146 $this->channelsdir = $pear_install_dir.$ds.'.channels'; 147 $this->statedir = $pear_install_dir.$ds.'.registry'; 148 $this->filemap = $pear_install_dir.$ds.'.filemap'; 149 $this->lockfile = $pear_install_dir.$ds.'.lock'; 150 $this->_pearChannel = $pear_channel; 151 $this->_peclChannel = $pecl_channel; 152 $this->_config = false; 153 } 154 155 function hasWriteAccess() 156 { 157 if (!@file_exists($this->install_dir)) { 158 $dir = $this->install_dir; 159 while ($dir && $dir != '.') { 160 $dir = dirname($dir); // cd .. 161 if ($dir != '.' && @file_exists($dir)) { 162 if (@is_writeable($dir)) { 163 return true; 164 } else { 165 return false; 166 } 167 } 168 } 169 return false; 170 } 171 return @is_writeable($this->install_dir); 172 } 173 174 function setConfig(&$config) 175 { 176 $this->_config = &$config; 177 } 178 179 function _initializeChannelDirs() 180 { 181 static $running = false; 182 if (!$running) { 183 $running = true; 184 $ds = DIRECTORY_SEPARATOR; 185 if (!is_dir($this->channelsdir) || 186 !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || 187 !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || 188 !file_exists($this->channelsdir . $ds . '__uri.reg')) { 189 if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { 190 $pear_channel = $this->_pearChannel; 191 if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) { 192 if (!class_exists('PEAR_ChannelFile')) { 193 require_once 'PEAR/ChannelFile.php'; 194 } 195 $pear_channel = new PEAR_ChannelFile; 196 $pear_channel->setName('pear.php.net'); 197 $pear_channel->setAlias('pear'); 198 $pear_channel->setServer('pear.php.net'); 199 $pear_channel->setSummary('PHP Extension and Application Repository'); 200 $pear_channel->setDefaultPEARProtocols(); 201 $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); 202 $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); 203 } else { 204 $pear_channel->setName('pear.php.net'); 205 $pear_channel->setAlias('pear'); 206 } 207 $pear_channel->validate(); 208 $this->_addChannel($pear_channel); 209 } 210 if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) { 211 $pecl_channel = $this->_peclChannel; 212 if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) { 213 if (!class_exists('PEAR_ChannelFile')) { 214 require_once 'PEAR/ChannelFile.php'; 215 } 216 $pecl_channel = new PEAR_ChannelFile; 217 $pecl_channel->setName('pecl.php.net'); 218 $pecl_channel->setAlias('pecl'); 219 $pecl_channel->setServer('pecl.php.net'); 220 $pecl_channel->setSummary('PHP Extension Community Library'); 221 $pecl_channel->setDefaultPEARProtocols(); 222 $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/'); 223 $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); 224 $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); 225 } else { 226 $pecl_channel->setName('pecl.php.net'); 227 $pecl_channel->setAlias('pecl'); 228 } 229 $pecl_channel->validate(); 230 $this->_addChannel($pecl_channel); 231 } 232 if (!file_exists($this->channelsdir . $ds . '__uri.reg')) { 233 if (!class_exists('PEAR_ChannelFile')) { 234 require_once 'PEAR/ChannelFile.php'; 235 } 236 $private = new PEAR_ChannelFile; 237 $private->setName('__uri'); 238 $private->addFunction('xmlrpc', '1.0', '****'); 239 $private->setSummary('Pseudo-channel for static packages'); 240 $this->_addChannel($private); 241 } 242 $this->_rebuildFileMap(); 243 } 244 $running = false; 245 } 246 } 247 248 function _initializeDirs() 249 { 250 $ds = DIRECTORY_SEPARATOR; 251 // XXX Compatibility code should be removed in the future 252 // rename all registry files if any to lowercase 253 if (!OS_WINDOWS && $handle = @opendir($this->statedir)) { 254 $dest = $this->statedir . $ds; 255 while (false !== ($file = readdir($handle))) { 256 if (preg_match('/^.*[A-Z].*\.reg$/', $file)) { 257 rename($dest . $file, $dest . strtolower($file)); 258 } 259 } 260 closedir($handle); 261 } 262 $this->_initializeChannelDirs(); 263 if (!file_exists($this->filemap)) { 264 $this->_rebuildFileMap(); 265 } 266 $this->_initializeDepDB(); 267 } 268 269 function _initializeDepDB() 270 { 271 if (!isset($this->_dependencyDB)) { 272 static $initializing = false; 273 if (!$initializing) { 274 $initializing = true; 275 if (!$this->_config) { // never used? 276 if (OS_WINDOWS) { 277 $file = 'pear.ini'; 278 } else { 279 $file = '.pearrc'; 280 } 281 $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR . 282 $file); 283 $this->_config->setRegistry($this); 284 $this->_config->set('php_dir', $this->install_dir); 285 } 286 $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); 287 if (PEAR::isError($this->_dependencyDB)) { 288 // attempt to recover by removing the dep db 289 @unlink($this->_config->get('php_dir', null, 'pear.php.net') . 290 DIRECTORY_SEPARATOR . '.depdb'); 291 $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config); 292 if (PEAR::isError($this->_dependencyDB)) { 293 echo $this->_dependencyDB->getMessage(); 294 die('Unrecoverable error'); 295 } 296 } 297 $initializing = false; 298 } 299 } 300 } 301 // }}} 302 // {{{ destructor 303 304 /** 305 * PEAR_Registry destructor. Makes sure no locks are forgotten. 306 * 307 * @access private 308 */ 309 function _PEAR_Registry() 310 { 311 parent::_PEAR(); 312 if (is_resource($this->lock_fp)) { 313 $this->_unlock(); 314 } 315 } 316 317 // }}} 318 319 // {{{ _assertStateDir() 320 321 /** 322 * Make sure the directory where we keep registry files exists. 323 * 324 * @return bool TRUE if directory exists, FALSE if it could not be 325 * created 326 * 327 * @access private 328 */ 329 function _assertStateDir($channel = false) 330 { 331 if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { 332 return $this->_assertChannelStateDir($channel); 333 } 334 static $init = false; 335 if (!@is_dir($this->statedir)) { 336 if (!$this->hasWriteAccess()) { 337 return false; 338 } 339 require_once 'System.php'; 340 if (!System::mkdir(array('-p', $this->statedir))) { 341 return $this->raiseError("could not create directory '{$this->statedir}'"); 342 } 343 $init = true; 344 } 345 $ds = DIRECTORY_SEPARATOR; 346 if (!@is_dir($this->channelsdir) || 347 !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') || 348 !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') || 349 !file_exists($this->channelsdir . $ds . '__uri.reg')) { 350 $init = true; 351 } 352 if ($init) { 353 static $running = false; 354 if (!$running) { 355 $running = true; 356 $this->_initializeDirs(); 357 $running = false; 358 $init = false; 359 } 360 } else { 361 $this->_initializeDepDB(); 362 } 363 return true; 364 } 365 366 // }}} 367 // {{{ _assertChannelStateDir() 368 369 /** 370 * Make sure the directory where we keep registry files exists for a non-standard channel. 371 * 372 * @param string channel name 373 * @return bool TRUE if directory exists, FALSE if it could not be 374 * created 375 * 376 * @access private 377 */ 378 function _assertChannelStateDir($channel) 379 { 380 $ds = DIRECTORY_SEPARATOR; 381 if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { 382 if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { 383 $this->_initializeChannelDirs(); 384 } 385 return $this->_assertStateDir($channel); 386 } 387 $channelDir = $this->_channelDirectoryName($channel); 388 if (!is_dir($this->channelsdir) || 389 !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) { 390 $this->_initializeChannelDirs(); 391 } 392 if (!@is_dir($channelDir)) { 393 if (!$this->hasWriteAccess()) { 394 return false; 395 } 396 require_once 'System.php'; 397 if (!System::mkdir(array('-p', $channelDir))) { 398 return $this->raiseError("could not create directory '" . $channelDir . 399 "'"); 400 } 401 } 402 return true; 403 } 404 405 // }}} 406 // {{{ _assertChannelDir() 407 408 /** 409 * Make sure the directory where we keep registry files for channels exists 410 * 411 * @return bool TRUE if directory exists, FALSE if it could not be 412 * created 413 * 414 * @access private 415 */ 416 function _assertChannelDir() 417 { 418 if (!@is_dir($this->channelsdir)) { 419 if (!$this->hasWriteAccess()) { 420 return false; 421 } 422 require_once 'System.php'; 423 if (!System::mkdir(array('-p', $this->channelsdir))) { 424 return $this->raiseError("could not create directory '{$this->channelsdir}'"); 425 } 426 } 427 if (!@is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) { 428 if (!$this->hasWriteAccess()) { 429 return false; 430 } 431 require_once 'System.php'; 432 if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) { 433 return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'"); 434 } 435 } 436 return true; 437 } 438 439 // }}} 440 // {{{ _packageFileName() 441 442 /** 443 * Get the name of the file where data for a given package is stored. 444 * 445 * @param string channel name, or false if this is a PEAR package 446 * @param string package name 447 * 448 * @return string registry file name 449 * 450 * @access public 451 */ 452 function _packageFileName($package, $channel = false) 453 { 454 if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { 455 return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR . 456 strtolower($package) . '.reg'; 457 } 458 return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; 459 } 460 461 // }}} 462 // {{{ _channelFileName() 463 464 /** 465 * Get the name of the file where data for a given channel is stored. 466 * @param string channel name 467 * @return string registry file name 468 */ 469 function _channelFileName($channel, $noaliases = false) 470 { 471 if (!$noaliases) { 472 if (@file_exists($this->_getChannelAliasFileName($channel))) { 473 $channel = implode('', file($this->_getChannelAliasFileName($channel))); 474 } 475 } 476 return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_', 477 strtolower($channel)) . '.reg'; 478 } 479 480 // }}} 481 // {{{ getChannelAliasFileName() 482 483 /** 484 * @param string 485 * @return string 486 */ 487 function _getChannelAliasFileName($alias) 488 { 489 return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' . 490 DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt'; 491 } 492 493 // }}} 494 // {{{ _getChannelFromAlias() 495 496 /** 497 * Get the name of a channel from its alias 498 */ 499 function _getChannelFromAlias($channel) 500 { 501 if (!$this->_channelExists($channel)) { 502 if ($channel == 'pear.php.net') { 503 return 'pear.php.net'; 504 } 505 if ($channel == 'pecl.php.net') { 506 return 'pecl.php.net'; 507 } 508 if ($channel == '__uri') { 509 return '__uri'; 510 } 511 return false; 512 } 513 $channel = strtolower($channel); 514 if (file_exists($this->_getChannelAliasFileName($channel))) { 515 // translate an alias to an actual channel 516 return implode('', file($this->_getChannelAliasFileName($channel))); 517 } else { 518 return $channel; 519 } 520 } 521 // }}} 522 // {{{ _getChannelFromAlias() 523 524 /** 525 * Get the alias of a channel from its alias or its name 526 */ 527 function _getAlias($channel) 528 { 529 if (!$this->_channelExists($channel)) { 530 if ($channel == 'pear.php.net') { 531 return 'pear'; 532 } 533 if ($channel == 'pecl.php.net') { 534 return 'pecl'; 535 } 536 return false; 537 } 538 $channel = $this->_getChannel($channel); 539 if (PEAR::isError($channel)) { 540 return $channel; 541 } 542 return $channel->getAlias(); 543 } 544 // }}} 545 // {{{ _channelDirectoryName() 546 547 /** 548 * Get the name of the file where data for a given package is stored. 549 * 550 * @param string channel name, or false if this is a PEAR package 551 * @param string package name 552 * 553 * @return string registry file name 554 * 555 * @access public 556 */ 557 function _channelDirectoryName($channel) 558 { 559 if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { 560 return $this->statedir; 561 } else { 562 $ch = $this->_getChannelFromAlias($channel); 563 if (!$ch) { 564 $ch = $channel; 565 } 566 return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' . 567 str_replace('/', '_', $ch)); 568 } 569 } 570 571 // }}} 572 // {{{ _openPackageFile() 573 574 function _openPackageFile($package, $mode, $channel = false) 575 { 576 if (!$this->_assertStateDir($channel)) { 577 return null; 578 } 579 if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { 580 return null; 581 } 582 $file = $this->_packageFileName($package, $channel); 583 $fp = @fopen($file, $mode); 584 if (!$fp) { 585 return null; 586 } 587 return $fp; 588 } 589 590 // }}} 591 // {{{ _closePackageFile() 592 593 function _closePackageFile($fp) 594 { 595 fclose($fp); 596 } 597 598 // }}} 599 // {{{ _openPackageFile() 600 601 function _openChannelFile($channel, $mode) 602 { 603 if (!$this->_assertChannelDir()) { 604 return null; 605 } 606 if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) { 607 return null; 608 } 609 $file = $this->_channelFileName($channel); 610 $fp = @fopen($file, $mode); 611 if (!$fp) { 612 return null; 613 } 614 return $fp; 615 } 616 617 // }}} 618 // {{{ _closePackageFile() 619 620 function _closeChannelFile($fp) 621 { 622 fclose($fp); 623 } 624 625 // }}} 626 // {{{ _rebuildFileMap() 627 628 function _rebuildFileMap() 629 { 630 if (!class_exists('PEAR_Installer_Role')) { 631 require_once 'PEAR/Installer/Role.php'; 632 } 633 $channels = $this->_listAllPackages(); 634 $files = array(); 635 foreach ($channels as $channel => $packages) { 636 foreach ($packages as $package) { 637 $version = $this->_packageInfo($package, 'version', $channel); 638 $filelist = $this->_packageInfo($package, 'filelist', $channel); 639 if (!is_array($filelist)) { 640 continue; 641 } 642 foreach ($filelist as $name => $attrs) { 643 if (isset($attrs['attribs'])) { 644 $attrs = $attrs['attribs']; 645 } 646 // it is possible for conflicting packages in different channels to 647 // conflict with data files/doc files 648 if ($name == 'dirtree') { 649 continue; 650 } 651 if (isset($attrs['role']) && !in_array($attrs['role'], 652 PEAR_Installer_Role::getInstallableRoles())) { 653 // these are not installed 654 continue; 655 } 656 if (isset($attrs['role']) && !in_array($attrs['role'], 657 PEAR_Installer_Role::getBaseinstallRoles())) { 658 $attrs['baseinstalldir'] = $package; 659 } 660 if (isset($attrs['baseinstalldir'])) { 661 $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; 662 } else { 663 $file = $name; 664 } 665 $file = preg_replace(',^/+,', '', $file); 666 if ($channel != 'pear.php.net') { 667 $files[$attrs['role']][$file] = array(strtolower($channel), 668 strtolower($package)); 669 } else { 670 $files[$attrs['role']][$file] = strtolower($package); 671 } 672 } 673 } 674 } 675 $this->_assertStateDir(); 676 if (!$this->hasWriteAccess()) { 677 return false; 678 } 679 $fp = @fopen($this->filemap, 'wb'); 680 if (!$fp) { 681 return false; 682 } 683 $this->filemap_cache = $files; 684 fwrite($fp, serialize($files)); 685 fclose($fp); 686 return true; 687 } 688 689 // }}} 690 // {{{ _readFileMap() 691 692 function _readFileMap() 693 { 694 $fp = @fopen($this->filemap, 'r'); 695 if (!$fp) { 696 return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg); 697 } 698 clearstatcache(); 699 $rt = get_magic_quotes_runtime(); 700 set_magic_quotes_runtime(0); 701 $fsize = filesize($this->filemap); 702 if (function_exists('file_get_contents')) { 703 fclose($fp); 704 $data = file_get_contents($this->filemap); 705 } else { 706 $data = fread($fp, $fsize); 707 fclose($fp); 708 } 709 set_magic_quotes_runtime($rt); 710 $tmp = unserialize($data); 711 if (!$tmp && $fsize > 7) { 712 return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); 713 } 714 $this->filemap_cache = $tmp; 715 return true; 716 } 717 718 // }}} 719 // {{{ _lock() 720 721 /** 722 * Lock the registry. 723 * 724 * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN. 725 * See flock manual for more information. 726 * 727 * @return bool TRUE on success, FALSE if locking failed, or a 728 * PEAR error if some other error occurs (such as the 729 * lock file not being writable). 730 * 731 * @access private 732 */ 733 function _lock($mode = LOCK_EX) 734 { 735 if (!eregi('Windows 9', php_uname())) { 736 if ($mode != LOCK_UN && is_resource($this->lock_fp)) { 737 // XXX does not check type of lock (LOCK_SH/LOCK_EX) 738 return true; 739 } 740 if (!$this->_assertStateDir()) { 741 if ($mode == LOCK_EX) { 742 return $this->raiseError('Registry directory is not writeable by the current user'); 743 } else { 744 return true; 745 } 746 } 747 $open_mode = 'w'; 748 // XXX People reported problems with LOCK_SH and 'w' 749 if ($mode === LOCK_SH || $mode === LOCK_UN) { 750 if (@!is_file($this->lockfile)) { 751 touch($this->lockfile); 752 } 753 $open_mode = 'r'; 754 } 755 756 if (!is_resource($this->lock_fp)) { 757 $this->lock_fp = @fopen($this->lockfile, $open_mode); 758 } 759 760 if (!is_resource($this->lock_fp)) { 761 return $this->raiseError("could not create lock file" . 762 (isset($php_errormsg) ? ": " . $php_errormsg : "")); 763 } 764 if (!(int)flock($this->lock_fp, $mode)) { 765 switch ($mode) { 766 case LOCK_SH: $str = 'shared'; break; 767 case LOCK_EX: $str = 'exclusive'; break; 768 case LOCK_UN: $str = 'unlock'; break; 769 default: $str = 'unknown'; break; 770 } 771 return $this->raiseError("could not acquire $str lock ($this->lockfile)", 772 PEAR_REGISTRY_ERROR_LOCK); 773 } 774 } 775 return true; 776 } 777 778 // }}} 779 // {{{ _unlock() 780 781 function _unlock() 782 { 783 $ret = $this->_lock(LOCK_UN); 784 if (is_resource($this->lock_fp)) { 785 fclose($this->lock_fp); 786 } 787 $this->lock_fp = null; 788 return $ret; 789 } 790 791 // }}} 792 // {{{ _packageExists() 793 794 function _packageExists($package, $channel = false) 795 { 796 return file_exists($this->_packageFileName($package, $channel)); 797 } 798 799 // }}} 800 // {{{ _channelExists() 801 802 /** 803 * Determine whether a channel exists in the registry 804 * @param string Channel name 805 * @param bool if true, then aliases will be ignored 806 * @return boolean 807 */ 808 function _channelExists($channel, $noaliases = false) 809 { 810 $a = file_exists($this->_channelFileName($channel, $noaliases)); 811 if (!$a && $channel == 'pear.php.net') { 812 return true; 813 } 814 if (!$a && $channel == 'pecl.php.net') { 815 return true; 816 } 817 return $a; 818 } 819 820 // }}} 821 // {{{ _addChannel() 822 823 /** 824 * @param PEAR_ChannelFile Channel object 825 * @param donotuse 826 * @param string Last-Modified HTTP tag from remote request 827 * @return boolean|PEAR_Error True on creation, false if it already exists 828 */ 829 function _addChannel($channel, $update = false, $lastmodified = false) 830 { 831 if (!is_a($channel, 'PEAR_ChannelFile')) { 832 return false; 833 } 834 if (!$channel->validate()) { 835 return false; 836 } 837 if (file_exists($this->_channelFileName($channel->getName()))) { 838 if (!$update) { 839 return false; 840 } 841 $checker = $this->_getChannel($channel->getName()); 842 if (PEAR::isError($checker)) { 843 return $checker; 844 } 845 if ($channel->getAlias() != $checker->getAlias()) { 846 @unlink($this->_getChannelAliasFileName($checker->getAlias())); 847 } 848 } else { 849 if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net'))) { 850 return false; 851 } 852 } 853 $ret = $this->_assertChannelDir(); 854 if (PEAR::isError($ret)) { 855 return $ret; 856 } 857 $ret = $this->_assertChannelStateDir($channel->getName()); 858 if (PEAR::isError($ret)) { 859 return $ret; 860 } 861 if ($channel->getAlias() != $channel->getName()) { 862 if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) && 863 $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) { 864 $channel->setAlias($channel->getName()); 865 } 866 if (!$this->hasWriteAccess()) { 867 return false; 868 } 869 $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w'); 870 if (!$fp) { 871 return false; 872 } 873 fwrite($fp, $channel->getName()); 874 fclose($fp); 875 } 876 if (!$this->hasWriteAccess()) { 877 return false; 878 } 879 $fp = @fopen($this->_channelFileName($channel->getName()), 'wb'); 880 if (!$fp) { 881 return false; 882 } 883 $info = $channel->toArray(); 884 if ($lastmodified) { 885 $info['_lastmodified'] = $lastmodified; 886 } else { 887 $info['_lastmodified'] = date('r'); 888 } 889 fwrite($fp, serialize($info)); 890 fclose($fp); 891 return true; 892 } 893 894 // }}} 895 // {{{ _deleteChannel() 896 897 /** 898 * Deletion fails if there are any packages installed from the channel 899 * @param string|PEAR_ChannelFile channel name 900 * @return boolean|PEAR_Error True on deletion, false if it doesn't exist 901 */ 902 function _deleteChannel($channel) 903 { 904 if (!is_string($channel)) { 905 if (is_a($channel, 'PEAR_ChannelFile')) { 906 if (!$channel->validate()) { 907 return false; 908 } 909 $channel = $channel->getName(); 910 } else { 911 return false; 912 } 913 } 914 if ($this->_getChannelFromAlias($channel) == '__uri') { 915 return false; 916 } 917 if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { 918 return false; 919 } 920 if (!$this->_channelExists($channel)) { 921 return false; 922 } 923 if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') { 924 return false; 925 } 926 $channel = $this->_getChannelFromAlias($channel); 927 if ($channel == 'pear.php.net') { 928 return false; 929 } 930 $test = $this->_listChannelPackages($channel); 931 if (count($test)) { 932 return false; 933 } 934 $test = @rmdir($this->_channelDirectoryName($channel)); 935 if (!$test) { 936 return false; 937 } 938 $file = $this->_getChannelAliasFileName($this->_getAlias($channel)); 939 if (@file_exists($file)) { 940 $test = @unlink($file); 941 if (!$test) { 942 return false; 943 } 944 } 945 $file = $this->_channelFileName($channel); 946 $ret = @unlink($file); 947 return $ret; 948 } 949 950 // }}} 951 // {{{ _isChannelAlias() 952 953 /** 954 * Determine whether a channel exists in the registry 955 * @param string Channel Alias 956 * @return boolean 957 */ 958 function _isChannelAlias($alias) 959 { 960 return file_exists($this->_getChannelAliasFileName($alias)); 961 } 962 963 // }}} 964 // {{{ _packageInfo() 965 966 /** 967 * @param string|null 968 * @param string|null 969 * @param string|null 970 * @return array|null 971 * @access private 972 */ 973 function _packageInfo($package = null, $key = null, $channel = 'pear.php.net') 974 { 975 if ($package === null) { 976 if ($channel === null) { 977 $channels = $this->_listChannels(); 978 $ret = array(); 979 foreach ($channels as $channel) { 980 $channel = strtolower($channel); 981 $ret[$channel] = array(); 982 $packages = $this->_listPackages($channel); 983 foreach ($packages as $package) { 984 $ret[$channel][] = $this->_packageInfo($package, null, $channel); 985 } 986 } 987 return $ret; 988 } 989 $ps = $this->_listPackages($channel); 990 if (!count($ps)) { 991 return array(); 992 } 993 return array_map(array(&$this, '_packageInfo'), 994 $ps, array_fill(0, count($ps), null), 995 array_fill(0, count($ps), $channel)); 996 } 997 $fp = $this->_openPackageFile($package, 'r', $channel); 998 if ($fp === null) { 999 return null; 1000 } 1001 $rt = get_magic_quotes_runtime(); 1002 set_magic_quotes_runtime(0); 1003 clearstatcache(); 1004 if (function_exists('file_get_contents')) { 1005 $this->_closePackageFile($fp); 1006 $data = file_get_contents($this->_packageFileName($package, $channel)); 1007 } else { 1008 $data = fread($fp, filesize($this->_packageFileName($package, $channel))); 1009 $this->_closePackageFile($fp); 1010 } 1011 set_magic_quotes_runtime($rt); 1012 $data = unserialize($data); 1013 if ($key === null) { 1014 return $data; 1015 } 1016 // compatibility for package.xml version 2.0 1017 if (isset($data['old'][$key])) { 1018 return $data['old'][$key]; 1019 } 1020 if (isset($data[$key])) { 1021 return $data[$key]; 1022 } 1023 return null; 1024 } 1025 1026 // }}} 1027 // {{{ _channelInfo() 1028 1029 /** 1030 * @param string Channel name 1031 * @param bool whether to strictly retrieve info of channels, not just aliases 1032 * @return array|null 1033 */ 1034 function _channelInfo($channel, $noaliases = false) 1035 { 1036 if (!$this->_channelExists($channel, $noaliases)) { 1037 return null; 1038 } 1039 $fp = $this->_openChannelFile($channel, 'r'); 1040 if ($fp === null) { 1041 return null; 1042 } 1043 $rt = get_magic_quotes_runtime(); 1044 set_magic_quotes_runtime(0); 1045 clearstatcache(); 1046 if (function_exists('file_get_contents')) { 1047 $this->_closeChannelFile($fp); 1048 $data = file_get_contents($this->_channelFileName($channel)); 1049 } else { 1050 $data = fread($fp, filesize($this->_channelFileName($channel))); 1051 $this->_closeChannelFile($fp); 1052 } 1053 set_magic_quotes_runtime($rt); 1054 $data = unserialize($data); 1055 return $data; 1056 } 1057 1058 // }}} 1059 // {{{ _listChannels() 1060 1061 function _listChannels() 1062 { 1063 $channellist = array(); 1064 $dp = @opendir($this->channelsdir); 1065 if (!$dp || !@is_dir($this->channelsdir)) { 1066 return array('pear.php.net', 'pecl.php.net', '__uri'); 1067 } 1068 while ($ent = readdir($dp)) { 1069 if ($ent{0} == '.' || substr($ent, -4) != '.reg') { 1070 continue; 1071 } 1072 if ($ent == '__uri.reg') { 1073 $channellist[] = '__uri'; 1074 continue; 1075 } 1076 $channellist[] = str_replace('_', '/', substr($ent, 0, -4)); 1077 } 1078 closedir($dp); 1079 if (!in_array('pear.php.net', $channellist)) { 1080 $channellist[] = 'pear.php.net'; 1081 } 1082 if (!in_array('pecl.php.net', $channellist)) { 1083 $channellist[] = 'pecl.php.net'; 1084 } 1085 if (!in_array('__uri', $channellist)) { 1086 $channellist[] = '__uri'; 1087 } 1088 return $channellist; 1089 } 1090 1091 // }}} 1092 // {{{ _listPackages() 1093 1094 function _listPackages($channel = false) 1095 { 1096 if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') { 1097 return $this->_listChannelPackages($channel); 1098 } 1099 $pkglist = array(); 1100 $dp = @opendir($this->statedir); 1101 if (!$dp) { 1102 return $pkglist; 1103 } 1104 while ($ent = readdir($dp)) { 1105 if ($ent{0} == '.' || substr($ent, -4) != '.reg') { 1106 continue; 1107 } 1108 $pkglist[] = substr($ent, 0, -4); 1109 } 1110 closedir($dp); 1111 return $pkglist; 1112 } 1113 1114 // }}} 1115 // {{{ _listChannelPackages() 1116 1117 function _listChannelPackages($channel) 1118 { 1119 $pkglist = array(); 1120 $dp = @opendir($this->_channelDirectoryName($channel)); 1121 if (!$dp) { 1122 return $pkglist; 1123 } 1124 while ($ent = readdir($dp)) { 1125 if ($ent{0} == '.' || substr($ent, -4) != '.reg') { 1126 continue; 1127 } 1128 $pkglist[] = substr($ent, 0, -4); 1129 } 1130 closedir($dp); 1131 return $pkglist; 1132 } 1133 1134 // }}} 1135 1136 function _listAllPackages() 1137 { 1138 $ret = array(); 1139 foreach ($this->_listChannels() as $channel) { 1140 $ret[$channel] = $this->_listPackages($channel); 1141 } 1142 return $ret; 1143 } 1144 1145 /** 1146 * Add an installed package to the registry 1147 * @param string package name 1148 * @param array package info (parsed by PEAR_Common::infoFrom*() methods) 1149 * @return bool success of saving 1150 * @access private 1151 */ 1152 function _addPackage($package, $info) 1153 { 1154 if ($this->_packageExists($package)) { 1155 return false; 1156 } 1157 $fp = $this->_openPackageFile($package, 'wb'); 1158 if ($fp === null) { 1159 return false; 1160 } 1161 $info['_lastmodified'] = time(); 1162 fwrite($fp, serialize($info)); 1163 $this->_closePackageFile($fp); 1164 if (isset($info['filelist'])) { 1165 $this->_rebuildFileMap(); 1166 } 1167 return true; 1168 } 1169 1170 /** 1171 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 1172 * @return bool 1173 * @access private 1174 */ 1175 function _addPackage2($info) 1176 { 1177 if (!$info->validate()) { 1178 if (class_exists('PEAR_Common')) { 1179 $ui = PEAR_Frontend::singleton(); 1180 if ($ui) { 1181 foreach ($info->getValidationWarnings() as $err) { 1182 $ui->log(2, $err['message']); 1183 } 1184 } 1185 } 1186 return false; 1187 } 1188 $channel = $info->getChannel(); 1189 $package = $info->getPackage(); 1190 $save = $info; 1191 if ($this->_packageExists($package, $channel)) { 1192 return false; 1193 } 1194 if (!$this->_channelExists($channel, true)) { 1195 return false; 1196 } 1197 $info = $info->toArray(true); 1198 if (!$info) { 1199 return false; 1200 } 1201 $fp = $this->_openPackageFile($package, 'wb', $channel); 1202 if ($fp === null) { 1203 return false; 1204 } 1205 $info['_lastmodified'] = time(); 1206 fwrite($fp, serialize($info)); 1207 $this->_closePackageFile($fp); 1208 $this->_rebuildFileMap(); 1209 return true; 1210 } 1211 1212 /** 1213 * @param string Package name 1214 * @param array parsed package.xml 1.0 1215 * @param bool this parameter is only here for BC. Don't use it. 1216 * @access private 1217 */ 1218 function _updatePackage($package, $info, $merge = true) 1219 { 1220 $oldinfo = $this->_packageInfo($package); 1221 if (empty($oldinfo)) { 1222 return false; 1223 } 1224 $fp = $this->_openPackageFile($package, 'w'); 1225 if ($fp === null) { 1226 return false; 1227 } 1228 if (is_object($info)) { 1229 $info = $info->toArray(); 1230 } 1231 $info['_lastmodified'] = time(); 1232 $newinfo = $info; 1233 if ($merge) { 1234 $info = array_merge($oldinfo, $info); 1235 } else { 1236 $diff = $info; 1237 } 1238 fwrite($fp, serialize($info)); 1239 $this->_closePackageFile($fp); 1240 if (isset($newinfo['filelist'])) { 1241 $this->_rebuildFileMap(); 1242 } 1243 return true; 1244 } 1245 1246 /** 1247 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 1248 * @return bool 1249 * @access private 1250 */ 1251 function _updatePackage2($info) 1252 { 1253 if (!$this->_packageExists($info->getPackage(), $info->getChannel())) { 1254 return false; 1255 } 1256 $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel()); 1257 if ($fp === null) { 1258 return false; 1259 } 1260 $save = $info; 1261 $info = $save->getArray(true); 1262 $info['_lastmodified'] = time(); 1263 fwrite($fp, serialize($info)); 1264 $this->_closePackageFile($fp); 1265 $this->_rebuildFileMap(); 1266 return true; 1267 } 1268 1269 /** 1270 * @param string Package name 1271 * @param string Channel name 1272 * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null 1273 * @access private 1274 */ 1275 function &_getPackage($package, $channel = 'pear.php.net') 1276 { 1277 $info = $this->_packageInfo($package, null, $channel); 1278 if ($info === null) { 1279 return $info; 1280 } 1281 $a = $this->_config; 1282 if (!$a) { 1283 $this->_config = &new PEAR_Config; 1284 $this->_config->set('php_dir', $this->statedir); 1285 } 1286 if (!class_exists('PEAR_PackageFile')) { 1287 require_once 'PEAR/PackageFile.php'; 1288 } 1289 $pkg = &new PEAR_PackageFile($this->_config); 1290 $pf = &$pkg->fromArray($info); 1291 return $pf; 1292 } 1293 1294 /** 1295 * @param string channel name 1296 * @param bool whether to strictly retrieve channel names 1297 * @return PEAR_ChannelFile|PEAR_Error 1298 * @access private 1299 */ 1300 function &_getChannel($channel, $noaliases = false) 1301 { 1302 $ch = false; 1303 if ($this->_channelExists($channel, $noaliases)) { 1304 $chinfo = $this->_channelInfo($channel, $noaliases); 1305 if ($chinfo) { 1306 if (!class_exists('PEAR_ChannelFile')) { 1307 require_once 'PEAR/ChannelFile.php'; 1308 } 1309 $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo); 1310 } 1311 } 1312 if ($ch) { 1313 if ($ch->validate()) { 1314 return $ch; 1315 } 1316 foreach ($ch->getErrors(true) as $err) { 1317 $message = $err['message'] . "\n"; 1318 } 1319 $ch = PEAR::raiseError($message); 1320 return $ch; 1321 } 1322 if ($this->_getChannelFromAlias($channel) == 'pear.php.net') { 1323 // the registry is not properly set up, so use defaults 1324 if (!class_exists('PEAR_ChannelFile')) { 1325 require_once 'PEAR/ChannelFile.php'; 1326 } 1327 $pear_channel = new PEAR_ChannelFile; 1328 $pear_channel->setName('pear.php.net'); 1329 $pear_channel->setAlias('pear'); 1330 $pear_channel->setSummary('PHP Extension and Application Repository'); 1331 $pear_channel->setDefaultPEARProtocols(); 1332 $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/'); 1333 $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/'); 1334 return $pear_channel; 1335 } 1336 if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') { 1337 // the registry is not properly set up, so use defaults 1338 if (!class_exists('PEAR_ChannelFile')) { 1339 require_once 'PEAR/ChannelFile.php'; 1340 } 1341 $pear_channel = new PEAR_ChannelFile; 1342 $pear_channel->setName('pecl.php.net'); 1343 $pear_channel->setAlias('pecl'); 1344 $pear_channel->setSummary('PHP Extension Community Library'); 1345 $pear_channel->setDefaultPEARProtocols(); 1346 $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/'); 1347 $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/'); 1348 $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0'); 1349 return $pear_channel; 1350 } 1351 if ($this->_getChannelFromAlias($channel) == '__uri') { 1352 // the registry is not properly set up, so use defaults 1353 if (!class_exists('PEAR_ChannelFile')) { 1354 require_once 'PEAR/ChannelFile.php'; 1355 } 1356 $private = new PEAR_ChannelFile; 1357 $private->setName('__uri'); 1358 $private->addFunction('xmlrpc', '1.0', '****'); 1359 $private->setSummary('Pseudo-channel for static packages'); 1360 return $private; 1361 } 1362 return $ch; 1363 } 1364 1365 // {{{ packageExists() 1366 1367 /** 1368 * @param string Package name 1369 * @param string Channel name 1370 * @return bool 1371 */ 1372 function packageExists($package, $channel = 'pear.php.net') 1373 { 1374 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1375 return $e; 1376 } 1377 $ret = $this->_packageExists($package, $channel); 1378 $this->_unlock(); 1379 return $ret; 1380 } 1381 1382 // }}} 1383 1384 // {{{ channelExists() 1385 1386 /** 1387 * @param string channel name 1388 * @param bool if true, then aliases will be ignored 1389 * @return bool 1390 */ 1391 function channelExists($channel, $noaliases = false) 1392 { 1393 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1394 return $e; 1395 } 1396 $ret = $this->_channelExists($channel, $noaliases); 1397 $this->_unlock(); 1398 return $ret; 1399 } 1400 1401 // }}} 1402 1403 // {{{ isAlias() 1404 1405 /** 1406 * Determines whether the parameter is an alias of a channel 1407 * @param string 1408 * @return bool 1409 */ 1410 function isAlias($alias) 1411 { 1412 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1413 return $e; 1414 } 1415 $ret = $this->_isChannelAlias($alias); 1416 $this->_unlock(); 1417 return $ret; 1418 } 1419 1420 // }}} 1421 // {{{ packageInfo() 1422 1423 /** 1424 * @param string|null 1425 * @param string|null 1426 * @param string 1427 * @return array|null 1428 */ 1429 function packageInfo($package = null, $key = null, $channel = 'pear.php.net') 1430 { 1431 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1432 return $e; 1433 } 1434 $ret = $this->_packageInfo($package, $key, $channel); 1435 $this->_unlock(); 1436 return $ret; 1437 } 1438 1439 // }}} 1440 // {{{ channelInfo() 1441 1442 /** 1443 * Retrieve a raw array of channel data. 1444 * 1445 * Do not use this, instead use {@link getChannel()} for normal 1446 * operations. Array structure is undefined in this method 1447 * @param string channel name 1448 * @param bool whether to strictly retrieve information only on non-aliases 1449 * @return array|null|PEAR_Error 1450 */ 1451 function channelInfo($channel = null, $noaliases = false) 1452 { 1453 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1454 return $e; 1455 } 1456 $ret = $this->_channelInfo($channel, $noaliases); 1457 $this->_unlock(); 1458 return $ret; 1459 } 1460 1461 // }}} 1462 1463 /** 1464 * @param string 1465 */ 1466 function channelName($channel) 1467 { 1468 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1469 return $e; 1470 } 1471 $ret = $this->_getChannelFromAlias($channel); 1472 $this->_unlock(); 1473 return $ret; 1474 } 1475 1476 /** 1477 * @param string 1478 */ 1479 function channelAlias($channel) 1480 { 1481 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1482 return $e; 1483 } 1484 $ret = $this->_getAlias($channel); 1485 $this->_unlock(); 1486 return $ret; 1487 } 1488 // {{{ listPackages() 1489 1490 function listPackages($channel = false) 1491 { 1492 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1493 return $e; 1494 } 1495 $ret = $this->_listPackages($channel); 1496 $this->_unlock(); 1497 return $ret; 1498 } 1499 1500 // }}} 1501 // {{{ listAllPackages() 1502 1503 function listAllPackages() 1504 { 1505 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1506 return $e; 1507 } 1508 $ret = $this->_listAllPackages(); 1509 $this->_unlock(); 1510 return $ret; 1511 } 1512 1513 // }}} 1514 // {{{ listChannel() 1515 1516 function listChannels() 1517 { 1518 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1519 return $e; 1520 } 1521 $ret = $this->_listChannels(); 1522 $this->_unlock(); 1523 return $ret; 1524 } 1525 1526 // }}} 1527 // {{{ addPackage() 1528 1529 /** 1530 * Add an installed package to the registry 1531 * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object 1532 * that will be passed to {@link addPackage2()} 1533 * @param array package info (parsed by PEAR_Common::infoFrom*() methods) 1534 * @return bool success of saving 1535 */ 1536 function addPackage($package, $info) 1537 { 1538 if (is_object($info)) { 1539 return $this->addPackage2($info); 1540 } 1541 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1542 return $e; 1543 } 1544 $ret = $this->_addPackage($package, $info); 1545 $this->_unlock(); 1546 if ($ret) { 1547 if (!class_exists('PEAR_PackageFile_v1')) { 1548 require_once 'PEAR/PackageFile/v1.php'; 1549 } 1550 $pf = new PEAR_PackageFile_v1; 1551 $pf->setConfig($this->_config); 1552 $pf->fromArray($info); 1553 $this->_dependencyDB->uninstallPackage($pf); 1554 $this->_dependencyDB->installPackage($pf); 1555 } 1556 return $ret; 1557 } 1558 1559 // }}} 1560 // {{{ addPackage2() 1561 1562 function addPackage2($info) 1563 { 1564 if (!is_object($info)) { 1565 return $this->addPackage($info['package'], $info); 1566 } 1567 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1568 return $e; 1569 } 1570 $ret = $this->_addPackage2($info); 1571 $this->_unlock(); 1572 if ($ret) { 1573 $this->_dependencyDB->uninstallPackage($info); 1574 $this->_dependencyDB->installPackage($info); 1575 } 1576 return $ret; 1577 } 1578 1579 // }}} 1580 // {{{ updateChannel() 1581 1582 /** 1583 * For future expandibility purposes, separate this 1584 * @param PEAR_ChannelFile 1585 */ 1586 function updateChannel($channel, $lastmodified = null) 1587 { 1588 if ($channel->getName() == '__uri') { 1589 return false; 1590 } 1591 return $this->addChannel($channel, $lastmodified, true); 1592 } 1593 1594 // }}} 1595 // {{{ deleteChannel() 1596 1597 /** 1598 * Deletion fails if there are any packages installed from the channel 1599 * @param string|PEAR_ChannelFile channel name 1600 * @return boolean|PEAR_Error True on deletion, false if it doesn't exist 1601 */ 1602 function deleteChannel($channel) 1603 { 1604 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1605 return $e; 1606 } 1607 $ret = $this->_deleteChannel($channel); 1608 $this->_unlock(); 1609 if ($ret && is_a($this->_config, 'PEAR_Config')) { 1610 $this->_config->setChannels($this->listChannels()); 1611 } 1612 return $ret; 1613 } 1614 1615 // }}} 1616 // {{{ addChannel() 1617 1618 /** 1619 * @param PEAR_ChannelFile Channel object 1620 * @param string Last-Modified header from HTTP for caching 1621 * @return boolean|PEAR_Error True on creation, false if it already exists 1622 */ 1623 function addChannel($channel, $lastmodified = false, $update = false) 1624 { 1625 if (!is_a($channel, 'PEAR_ChannelFile')) { 1626 return false; 1627 } 1628 if (!$channel->validate()) { 1629 return false; 1630 } 1631 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1632 return $e; 1633 } 1634 $ret = $this->_addChannel($channel, $update, $lastmodified); 1635 $this->_unlock(); 1636 if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) { 1637 $this->_config->setChannels($this->listChannels()); 1638 } 1639 return $ret; 1640 } 1641 1642 // }}} 1643 // {{{ deletePackage() 1644 1645 function deletePackage($package, $channel = 'pear.php.net') 1646 { 1647 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1648 return $e; 1649 } 1650 $file = $this->_packageFileName($package, $channel); 1651 $ret = @unlink($file); 1652 $this->_rebuildFileMap(); 1653 $this->_unlock(); 1654 $p = array('channel' => $channel, 'package' => $package); 1655 $this->_dependencyDB->uninstallPackage($p); 1656 return $ret; 1657 } 1658 1659 // }}} 1660 // {{{ updatePackage() 1661 1662 function updatePackage($package, $info, $merge = true) 1663 { 1664 if (is_object($info)) { 1665 return $this->updatePackage2($info, $merge); 1666 } 1667 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1668 return $e; 1669 } 1670 $ret = $this->_updatePackage($package, $info, $merge); 1671 $this->_unlock(); 1672 if ($ret) { 1673 if (!class_exists('PEAR_PackageFile_v1')) { 1674 require_once 'PEAR/PackageFile/v1.php'; 1675 } 1676 $pf = new PEAR_PackageFile_v1; 1677 $pf->setConfig($this->_config); 1678 $pf->fromArray($this->packageInfo($package)); 1679 $this->_dependencyDB->uninstallPackage($pf); 1680 $this->_dependencyDB->installPackage($pf); 1681 } 1682 return $ret; 1683 } 1684 1685 // }}} 1686 // {{{ updatePackage2() 1687 1688 function updatePackage2($info) 1689 { 1690 if (!is_object($info)) { 1691 return $this->updatePackage($info['package'], $info, $merge); 1692 } 1693 if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) { 1694 return false; 1695 } 1696 if (PEAR::isError($e = $this->_lock(LOCK_EX))) { 1697 return $e; 1698 } 1699 $ret = $this->_updatePackage2($info); 1700 $this->_unlock(); 1701 if ($ret) { 1702 $this->_dependencyDB->uninstallPackage($info); 1703 $this->_dependencyDB->installPackage($info); 1704 } 1705 return $ret; 1706 } 1707 1708 // }}} 1709 // {{{ getChannel() 1710 /** 1711 * @param string channel name 1712 * @param bool whether to strictly return raw channels (no aliases) 1713 * @return PEAR_ChannelFile|PEAR_Error 1714 */ 1715 function &getChannel($channel, $noaliases = false) 1716 { 1717 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1718 return $e; 1719 } 1720 $ret = &$this->_getChannel($channel, $noaliases); 1721 if (!$ret) { 1722 return PEAR::raiseError('Unknown channel: ' . $channel); 1723 } 1724 $this->_unlock(); 1725 return $ret; 1726 } 1727 1728 // }}} 1729 // {{{ getPackage() 1730 /** 1731 * @param string package name 1732 * @param string channel name 1733 * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null 1734 */ 1735 function &getPackage($package, $channel = 'pear.php.net') 1736 { 1737 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1738 return $e; 1739 } 1740 $pf = &$this->_getPackage($package, $channel); 1741 $this->_unlock(); 1742 return $pf; 1743 } 1744 1745 // }}} 1746 1747 /** 1748 * Get PEAR_PackageFile_v[1/2] objects representing the contents of 1749 * a dependency group that are installed. 1750 * 1751 * This is used at uninstall-time 1752 * @param array 1753 * @return array|false 1754 */ 1755 function getInstalledGroup($group) 1756 { 1757 $ret = array(); 1758 if (isset($group['package'])) { 1759 if (!isset($group['package'][0])) { 1760 $group['package'] = array($group['package']); 1761 } 1762 foreach ($group['package'] as $package) { 1763 $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; 1764 $p = &$this->getPackage($package['name'], $depchannel); 1765 if ($p) { 1766 $save = &$p; 1767 $ret[] = &$save; 1768 } 1769 } 1770 } 1771 if (isset($group['subpackage'])) { 1772 if (!isset($group['subpackage'][0])) { 1773 $group['subpackage'] = array($group['subpackage']); 1774 } 1775 foreach ($group['subpackage'] as $package) { 1776 $depchannel = isset($package['channel']) ? $package['channel'] : '__uri'; 1777 $p = &$this->getPackage($package['name'], $depchannel); 1778 if ($p) { 1779 $save = &$p; 1780 $ret[] = &$save; 1781 } 1782 } 1783 } 1784 if (!count($ret)) { 1785 return false; 1786 } 1787 return $ret; 1788 } 1789 1790 // {{{ getChannelValidator() 1791 /** 1792 * @param string channel name 1793 * @return PEAR_Validate|false 1794 */ 1795 function &getChannelValidator($channel) 1796 { 1797 $chan = $this->getChannel($channel); 1798 if (PEAR::isError($chan)) { 1799 return $chan; 1800 } 1801 $val = $chan->getValidationObject(); 1802 return $val; 1803 } 1804 // }}} 1805 // {{{ getChannels() 1806 /** 1807 * @param string channel name 1808 * @return array an array of PEAR_ChannelFile objects representing every installed channel 1809 */ 1810 function &getChannels() 1811 { 1812 $ret = array(); 1813 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1814 return $e; 1815 } 1816 foreach ($this->_listChannels() as $channel) { 1817 $e = &$this->_getChannel($channel); 1818 if (!$e || PEAR::isError($e)) { 1819 continue; 1820 } 1821 $ret[] = $e; 1822 } 1823 $this->_unlock(); 1824 return $ret; 1825 } 1826 1827 // }}} 1828 // {{{ checkFileMap() 1829 1830 /** 1831 * Test whether a file or set of files belongs to a package. 1832 * 1833 * If an array is passed in 1834 * @param string|array file path, absolute or relative to the pear 1835 * install dir 1836 * @param string|array name of PEAR package or array('package' => name, 'channel' => 1837 * channel) of a package that will be ignored 1838 * @param string API version - 1.1 will exclude any files belonging to a package 1839 * @param array private recursion variable 1840 * @return array|false which package and channel the file belongs to, or an empty 1841 * string if the file does not belong to an installed package, 1842 * or belongs to the second parameter's package 1843 */ 1844 function checkFileMap($path, $package = false, $api = '1.0', $attrs = false) 1845 { 1846 if (is_array($path)) { 1847 static $notempty; 1848 if (empty($notempty)) { 1849 if (!class_exists('PEAR_Installer_Role')) { 1850 require_once 'PEAR/Installer/Role.php'; 1851 } 1852 $notempty = create_function('$a','return !empty($a);'); 1853 } 1854 $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1])) 1855 : strtolower($package); 1856 $pkgs = array(); 1857 foreach ($path as $name => $attrs) { 1858 if (is_array($attrs)) { 1859 if (isset($attrs['install-as'])) { 1860 $name = $attrs['install-as']; 1861 } 1862 if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) { 1863 // these are not installed 1864 continue; 1865 } 1866 if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) { 1867 $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package; 1868 } 1869 if (isset($attrs['baseinstalldir'])) { 1870 $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name; 1871 } 1872 } 1873 $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs); 1874 if (PEAR::isError($pkgs[$name])) { 1875 return $pkgs[$name]; 1876 } 1877 } 1878 return array_filter($pkgs, $notempty); 1879 } 1880 if (empty($this->filemap_cache)) { 1881 if (PEAR::isError($e = $this->_lock(LOCK_SH))) { 1882 return $e; 1883 } 1884 $err = $this->_readFileMap(); 1885 $this->_unlock(); 1886 if (PEAR::isError($err)) { 1887 return $err; 1888 } 1889 } 1890 if (!$attrs) { 1891 $attrs = array('role' => 'php'); // any old call would be for PHP role only 1892 } 1893 if (isset($this->filemap_cache[$attrs['role']][$path])) { 1894 if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { 1895 return false; 1896 } 1897 return $this->filemap_cache[$attrs['role']][$path]; 1898 } 1899 $l = strlen($this->install_dir); 1900 if (substr($path, 0, $l) == $this->install_dir) { 1901 $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l)); 1902 } 1903 if (isset($this->filemap_cache[$attrs['role']][$path])) { 1904 if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) { 1905 return false; 1906 } 1907 return $this->filemap_cache[$attrs['role']][$path]; 1908 } 1909 return false; 1910 } 1911 1912 // }}} 1913 // {{{ apiVersion() 1914 /** 1915 * Get the expected API version. Channels API is version 1.1, as it is backwards 1916 * compatible with 1.0 1917 * @return string 1918 */ 1919 function apiVersion() 1920 { 1921 return '1.1'; 1922 } 1923 // }}} 1924 1925 1926 /** 1927 * Parse a package name, or validate a parsed package name array 1928 * @param string|array pass in an array of format 1929 * array( 1930 * 'package' => 'pname', 1931 * ['channel' => 'channame',] 1932 * ['version' => 'version',] 1933 * ['state' => 'state',] 1934 * ['group' => 'groupname']) 1935 * or a string of format 1936 * [channel://][channame/]pname[-version|-state][/group=groupname] 1937 * @return array|PEAR_Error 1938 */ 1939 function parsePackageName($param, $defaultchannel = 'pear.php.net') 1940 { 1941 $saveparam = $param; 1942 if (is_array($param)) { 1943 // convert to string for error messages 1944 $saveparam = $this->parsedPackageNameToString($param); 1945 // process the array 1946 if (!isset($param['package'])) { 1947 return PEAR::raiseError('parsePackageName(): array $param ' . 1948 'must contain a valid package name in index "param"', 1949 'package', null, null, $param); 1950 } 1951 if (!isset($param['uri'])) { 1952 if (!isset($param['channel'])) { 1953 $param['channel'] = $defaultchannel; 1954 } 1955 } else { 1956 $param['channel'] = '__uri'; 1957 } 1958 } else { 1959 $components = @parse_url($param); 1960 if (isset($components['scheme'])) { 1961 if ($components['scheme'] == 'http') { 1962 // uri package 1963 $param = array('uri' => $param, 'channel' => '__uri'); 1964 } elseif($components['scheme'] != 'channel') { 1965 return PEAR::raiseError('parsePackageName(): only channel:// uris may ' . 1966 'be downloaded, not "' . $param . '"', 'invalid', null, null, $param); 1967 } 1968 } 1969 if (!isset($components['path'])) { 1970 return PEAR::raiseError('parsePackageName(): array $param ' . 1971 'must contain a valid package name in "' . $param . '"', 1972 'package', null, null, $param); 1973 } 1974 if (isset($components['host'])) { 1975 // remove the leading "/" 1976 $components['path'] = substr($components['path'], 1); 1977 } 1978 if (!isset($components['scheme'])) { 1979 if (strpos($components['path'], '/') !== false) { 1980 if ($components['path']{0} == '/') { 1981 return PEAR::raiseError('parsePackageName(): this is not ' . 1982 'a package name, it begins with "/" in "' . $param . '"', 1983 'invalid', null, null, $param); 1984 } 1985 $parts = explode('/', $components['path']); 1986 $components['host'] = array_shift($parts); 1987 if (count($parts) > 1) { 1988 $components['path'] = array_pop($parts); 1989 $components['host'] .= '/' . implode('/', $parts); 1990 } else { 1991 $components['path'] = implode('/', $parts); 1992 } 1993 } else { 1994 $components['host'] = $defaultchannel; 1995 } 1996 } else { 1997 if (strpos($components['path'], '/')) { 1998 $parts = explode('/', $components['path']); 1999 $components['path'] = array_pop($parts); 2000 $components['host'] .= '/' . implode('/', $parts); 2001 } 2002 } 2003 2004 if (is_array($param)) { 2005 $param['package'] = $components['path']; 2006 } else { 2007 $param = array( 2008 'package' => $components['path'] 2009 ); 2010 if (isset($components['host'])) { 2011 $param['channel'] = $components['host']; 2012 } 2013 } 2014 if (isset($components['fragment'])) { 2015 $param['group'] = $components['fragment']; 2016 } 2017 if (isset($components['user'])) { 2018 $param['user'] = $components['user']; 2019 } 2020 if (isset($components['pass'])) { 2021 $param['pass'] = $components['pass']; 2022 } 2023 if (isset($components['query'])) { 2024 parse_str($components['query'], $param['opts']); 2025 } 2026 // check for extension 2027 $pathinfo = pathinfo($param['package']); 2028 if (isset($pathinfo['extension']) && 2029 in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) { 2030 $param['extension'] = $pathinfo['extension']; 2031 $param['package'] = substr($pathinfo['basename'], 0, 2032 strlen($pathinfo['basename']) - 4); 2033 } 2034 // check for version 2035 if (strpos($param['package'], '-')) { 2036 $test = explode('-', $param['package']); 2037 if (count($test) != 2) { 2038 return PEAR::raiseError('parsePackageName(): only one version/state ' . 2039 'delimiter "-" is allowed in "' . $saveparam . '"', 2040 'version', null, null, $param); 2041 } 2042 list($param['package'], $param['version']) = $test; 2043 } 2044 } 2045 // validation 2046 $info = $this->channelExists($param['channel']); 2047 if (PEAR::isError($info)) { 2048 return $info; 2049 } 2050 if (!$info) { 2051 return PEAR::raiseError('unknown channel "' . $param['channel'] . 2052 '" in "' . $saveparam . '"', 'channel', null, null, $param); 2053 } 2054 $chan = $this->getChannel($param['channel']); 2055 if (PEAR::isError($chan)) { 2056 return $chan; 2057 } 2058 if (!$chan) { 2059 return PEAR::raiseError("Exception: corrupt registry, could not " . 2060 "retrieve channel " . $param['channel'] . " information", 2061 'registry', null, null, $param); 2062 } 2063 $param['channel'] = $chan->getName(); 2064 $validate = $chan->getValidationObject(); 2065 $vpackage = $chan->getValidationPackage(); 2066 // validate package name 2067 if (!$validate->validPackageName($param['package'], $vpackage['_content'])) { 2068 return PEAR::raiseError('parsePackageName(): invalid package name "' . 2069 $param['package'] . '" in "' . $saveparam . '"', 2070 'package', null, null, $param); 2071 } 2072 if (isset($param['group'])) { 2073 if (!PEAR_Validate::validGroupName($param['group'])) { 2074 return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] . 2075 '" is not a valid group name in "' . $saveparam . '"', 'group', null, null, 2076 $param); 2077 } 2078 } 2079 if (isset($param['state'])) { 2080 if (!in_array(strtolower($param['state']), $validate->getValidStates())) { 2081 return PEAR::raiseError('parsePackageName(): state "' . $param['state'] 2082 . '" is not a valid state in "' . $saveparam . '"', 2083 'state', null, null, $param); 2084 } 2085 } 2086 if (isset($param['version'])) { 2087 if (isset($param['state'])) { 2088 return PEAR::raiseError('parsePackageName(): cannot contain both ' . 2089 'a version and a stability (state) in "' . $saveparam . '"', 2090 'version/state', null, null, $param); 2091 } 2092 // check whether version is actually a state 2093 if (in_array(strtolower($param['version']), $validate->getValidStates())) { 2094 $param['state'] = strtolower($param['version']); 2095 unset($param['version']); 2096 } else { 2097 if (!$validate->validVersion($param['version'])) { 2098 return PEAR::raiseError('parsePackageName(): "' . $param['version'] . 2099 '" is neither a valid version nor a valid state in "' . 2100 $saveparam . '"', 'version/state', null, null, $param); 2101 } 2102 } 2103 } 2104 return $param; 2105 } 2106 2107 /** 2108 * @param array 2109 * @return string 2110 */ 2111 function parsedPackageNameToString($parsed, $brief = false) 2112 { 2113 if (is_string($parsed)) { 2114 return $parsed; 2115 } 2116 if (is_object($parsed)) { 2117 $p = $parsed; 2118 $parsed = array( 2119 'package' => $p->getPackage(), 2120 'channel' => $p->getChannel(), 2121 'version' => $p->getVersion(), 2122 ); 2123 } 2124 if (isset($parsed['uri'])) { 2125 return $parsed['uri']; 2126 } 2127 if ($brief) { 2128 if ($channel = $this->channelAlias($parsed['channel'])) { 2129 return $channel . '/' . $parsed['package']; 2130 } 2131 } 2132 $upass = ''; 2133 if (isset($parsed['user'])) { 2134 $upass = $parsed['user']; 2135 if (isset($parsed['pass'])) { 2136 $upass .= ':' . $parsed['pass']; 2137 } 2138 $upass = "$upass@"; 2139 } 2140 $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package']; 2141 if (isset($parsed['version']) || isset($parsed['state'])) { 2142 $ret .= '-' . @$parsed['version'] . @$parsed['state']; 2143 } 2144 if (isset($parsed['extension'])) { 2145 $ret .= '.' . $parsed['extension']; 2146 } 2147 if (isset($parsed['opts'])) { 2148 $ret .= '?'; 2149 foreach ($parsed['opts'] as $name => $value) { 2150 $parsed['opts'][$name] = "$name=$value"; 2151 } 2152 $ret .= implode('&', $parsed['opts']); 2153 } 2154 if (isset($parsed['group'])) { 2155 $ret .= '#' . $parsed['group']; 2156 } 2157 return $ret; 2158 } 2159} 2160 2161?> 2162