[ Index ] |
|
Code source de PHP PEAR 1.4.5 |
1 <?php 2 /** 3 * PEAR_Installer 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 Martin Jansen <mj@php.net> 18 * @author Greg Beaver <cellog@php.net> 19 * @copyright 1997-2006 The PHP Group 20 * @license http://www.php.net/license/3_0.txt PHP License 3.0 21 * @version CVS: $Id: Installer.php,v 1.241 2007/01/08 05:43:49 cellog Exp $ 22 * @link http://pear.php.net/package/PEAR 23 * @since File available since Release 0.1 24 */ 25 26 /** 27 * Used for installation groups in package.xml 2.0 and platform exceptions 28 */ 29 require_once 'OS/Guess.php'; 30 require_once 'PEAR/Downloader.php'; 31 32 define('PEAR_INSTALLER_NOBINARY', -240); 33 /** 34 * Administration class used to install PEAR packages and maintain the 35 * installed package database. 36 * 37 * @category pear 38 * @package PEAR 39 * @author Stig Bakken <ssb@php.net> 40 * @author Tomas V.V. Cox <cox@idecnet.com> 41 * @author Martin Jansen <mj@php.net> 42 * @author Greg Beaver <cellog@php.net> 43 * @copyright 1997-2006 The PHP Group 44 * @license http://www.php.net/license/3_0.txt PHP License 3.0 45 * @version Release: 1.5.0 46 * @link http://pear.php.net/package/PEAR 47 * @since Class available since Release 0.1 48 */ 49 class PEAR_Installer extends PEAR_Downloader 50 { 51 // {{{ properties 52 53 /** name of the package directory, for example Foo-1.0 54 * @var string 55 */ 56 var $pkgdir; 57 58 /** directory where PHP code files go 59 * @var string 60 */ 61 var $phpdir; 62 63 /** directory where PHP extension files go 64 * @var string 65 */ 66 var $extdir; 67 68 /** directory where documentation goes 69 * @var string 70 */ 71 var $docdir; 72 73 /** installation root directory (ala PHP's INSTALL_ROOT or 74 * automake's DESTDIR 75 * @var string 76 */ 77 var $installroot = ''; 78 79 /** debug level 80 * @var int 81 */ 82 var $debug = 1; 83 84 /** temporary directory 85 * @var string 86 */ 87 var $tmpdir; 88 89 /** 90 * PEAR_Registry object used by the installer 91 * @var PEAR_Registry 92 */ 93 var $registry; 94 95 /** 96 * array of PEAR_Downloader_Packages 97 * @var array 98 */ 99 var $_downloadedPackages; 100 101 /** List of file transactions queued for an install/upgrade/uninstall. 102 * 103 * Format: 104 * array( 105 * 0 => array("rename => array("from-file", "to-file")), 106 * 1 => array("delete" => array("file-to-delete")), 107 * ... 108 * ) 109 * 110 * @var array 111 */ 112 var $file_operations = array(); 113 114 // }}} 115 116 // {{{ constructor 117 118 /** 119 * PEAR_Installer constructor. 120 * 121 * @param object $ui user interface object (instance of PEAR_Frontend_*) 122 * 123 * @access public 124 */ 125 function PEAR_Installer(&$ui) 126 { 127 parent::PEAR_Common(); 128 $this->setFrontendObject($ui); 129 $this->debug = $this->config->get('verbose'); 130 } 131 132 function setOptions($options) 133 { 134 $this->_options = $options; 135 } 136 137 function setConfig(&$config) 138 { 139 $this->config = &$config; 140 $this->_registry = &$config->getRegistry(); 141 } 142 143 // }}} 144 145 function _removeBackups($files) 146 { 147 foreach ($files as $path) { 148 $this->addFileOperation('removebackup', array($path)); 149 } 150 } 151 152 // {{{ _deletePackageFiles() 153 154 /** 155 * Delete a package's installed files, does not remove empty directories. 156 * 157 * @param string package name 158 * @param string channel name 159 * @param bool if true, then files are backed up first 160 * @return bool TRUE on success, or a PEAR error on failure 161 * @access protected 162 */ 163 function _deletePackageFiles($package, $channel = false, $backup = false) 164 { 165 if (!$channel) { 166 $channel = 'pear.php.net'; 167 } 168 if (!strlen($package)) { 169 return $this->raiseError("No package to uninstall given"); 170 } 171 if (strtolower($package) == 'pear' && $channel == 'pear.php.net') { 172 // to avoid race conditions, include all possible needed files 173 require_once 'PEAR/Task/Common.php'; 174 require_once 'PEAR/Task/Replace.php'; 175 require_once 'PEAR/Task/Unixeol.php'; 176 require_once 'PEAR/Task/Windowseol.php'; 177 require_once 'PEAR/PackageFile/v1.php'; 178 require_once 'PEAR/PackageFile/v2.php'; 179 require_once 'PEAR/PackageFile/Generator/v1.php'; 180 require_once 'PEAR/PackageFile/Generator/v2.php'; 181 } 182 $filelist = $this->_registry->packageInfo($package, 'filelist', $channel); 183 if ($filelist == null) { 184 return $this->raiseError("$channel/$package not installed"); 185 } 186 $ret = array(); 187 foreach ($filelist as $file => $props) { 188 if (empty($props['installed_as'])) { 189 continue; 190 } 191 $path = $props['installed_as']; 192 if ($backup) { 193 $this->addFileOperation('backup', array($path)); 194 $ret[] = $path; 195 } 196 $this->addFileOperation('delete', array($path)); 197 } 198 if ($backup) { 199 return $ret; 200 } 201 return true; 202 } 203 204 // }}} 205 // {{{ _installFile() 206 207 /** 208 * @param string filename 209 * @param array attributes from <file> tag in package.xml 210 * @param string path to install the file in 211 * @param array options from command-line 212 * @access private 213 */ 214 function _installFile($file, $atts, $tmp_path, $options) 215 { 216 // {{{ return if this file is meant for another platform 217 static $os; 218 if (!isset($this->_registry)) { 219 $this->_registry = &$this->config->getRegistry(); 220 } 221 if (isset($atts['platform'])) { 222 if (empty($os)) { 223 $os = new OS_Guess(); 224 } 225 if (strlen($atts['platform']) && $atts['platform']{0} == '!') { 226 $negate = true; 227 $platform = substr($atts['platform'], 1); 228 } else { 229 $negate = false; 230 $platform = $atts['platform']; 231 } 232 if ((bool) $os->matchSignature($platform) === $negate) { 233 $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")"); 234 return PEAR_INSTALLER_SKIPPED; 235 } 236 } 237 // }}} 238 239 $channel = $this->pkginfo->getChannel(); 240 // {{{ assemble the destination paths 241 switch ($atts['role']) { 242 case 'doc': 243 case 'data': 244 case 'test': 245 $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) . 246 DIRECTORY_SEPARATOR . $this->pkginfo->getPackage(); 247 unset($atts['baseinstalldir']); 248 break; 249 case 'ext': 250 case 'php': 251 $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel); 252 break; 253 case 'script': 254 $dest_dir = $this->config->get('bin_dir', null, $channel); 255 break; 256 case 'src': 257 case 'extsrc': 258 $this->source_files++; 259 return; 260 default: 261 return $this->raiseError("Invalid role `$atts[role]' for file $file"); 262 } 263 $save_destdir = $dest_dir; 264 if (!empty($atts['baseinstalldir'])) { 265 $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir']; 266 } 267 if (dirname($file) != '.' && empty($atts['install-as'])) { 268 $dest_dir .= DIRECTORY_SEPARATOR . dirname($file); 269 } 270 if (empty($atts['install-as'])) { 271 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file); 272 } else { 273 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as']; 274 } 275 $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file; 276 277 // Clean up the DIRECTORY_SEPARATOR mess 278 $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR; 279 list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"), 280 array(DIRECTORY_SEPARATOR, 281 DIRECTORY_SEPARATOR, 282 DIRECTORY_SEPARATOR), 283 array($dest_file, $orig_file)); 284 $final_dest_file = $installed_as = $dest_file; 285 if (isset($this->_options['packagingroot'])) { 286 $installedas_dest_dir = dirname($final_dest_file); 287 $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 288 $final_dest_file = $this->_prependPath($final_dest_file, 289 $this->_options['packagingroot']); 290 } else { 291 $installedas_dest_dir = dirname($final_dest_file); 292 $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 293 } 294 $dest_dir = dirname($final_dest_file); 295 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 296 // }}} 297 298 if (empty($this->_options['register-only']) && 299 (!file_exists($dest_dir) || !is_dir($dest_dir))) { 300 if (!$this->mkDirHier($dest_dir)) { 301 return $this->raiseError("failed to mkdir $dest_dir", 302 PEAR_INSTALLER_FAILED); 303 } 304 $this->log(3, "+ mkdir $dest_dir"); 305 } 306 // pretty much nothing happens if we are only registering the install 307 if (empty($this->_options['register-only'])) { 308 if (empty($atts['replacements'])) { 309 if (!file_exists($orig_file)) { 310 return $this->raiseError("file $orig_file does not exist", 311 PEAR_INSTALLER_FAILED); 312 } 313 if (!@copy($orig_file, $dest_file)) { 314 return $this->raiseError("failed to write $dest_file: $php_errormsg", 315 PEAR_INSTALLER_FAILED); 316 } 317 $this->log(3, "+ cp $orig_file $dest_file"); 318 if (isset($atts['md5sum'])) { 319 $md5sum = md5_file($dest_file); 320 } 321 } else { 322 // {{{ file with replacements 323 if (!file_exists($orig_file)) { 324 return $this->raiseError("file does not exist", 325 PEAR_INSTALLER_FAILED); 326 } 327 $contents = file_get_contents($orig_file); 328 if ($contents === false) { 329 $contents = ''; 330 } 331 if (isset($atts['md5sum'])) { 332 $md5sum = md5($contents); 333 } 334 $subst_from = $subst_to = array(); 335 foreach ($atts['replacements'] as $a) { 336 $to = ''; 337 if ($a['type'] == 'php-const') { 338 if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) { 339 eval("\$to = $a[to];"); 340 } else { 341 if (!isset($options['soft'])) { 342 $this->log(0, "invalid php-const replacement: $a[to]"); 343 } 344 continue; 345 } 346 } elseif ($a['type'] == 'pear-config') { 347 if ($a['to'] == 'master_server') { 348 $chan = $this->_registry->getChannel($channel); 349 if (!PEAR::isError($chan)) { 350 $to = $chan->getServer(); 351 } else { 352 $to = $this->config->get($a['to'], null, $channel); 353 } 354 } else { 355 $to = $this->config->get($a['to'], null, $channel); 356 } 357 if (is_null($to)) { 358 if (!isset($options['soft'])) { 359 $this->log(0, "invalid pear-config replacement: $a[to]"); 360 } 361 continue; 362 } 363 } elseif ($a['type'] == 'package-info') { 364 if ($t = $this->pkginfo->packageInfo($a['to'])) { 365 $to = $t; 366 } else { 367 if (!isset($options['soft'])) { 368 $this->log(0, "invalid package-info replacement: $a[to]"); 369 } 370 continue; 371 } 372 } 373 if (!is_null($to)) { 374 $subst_from[] = $a['from']; 375 $subst_to[] = $to; 376 } 377 } 378 $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file"); 379 if (sizeof($subst_from)) { 380 $contents = str_replace($subst_from, $subst_to, $contents); 381 } 382 $wp = @fopen($dest_file, "wb"); 383 if (!is_resource($wp)) { 384 return $this->raiseError("failed to create $dest_file: $php_errormsg", 385 PEAR_INSTALLER_FAILED); 386 } 387 if (@fwrite($wp, $contents) === false) { 388 return $this->raiseError("failed writing to $dest_file: $php_errormsg", 389 PEAR_INSTALLER_FAILED); 390 } 391 fclose($wp); 392 // }}} 393 } 394 // {{{ check the md5 395 if (isset($md5sum)) { 396 if (strtolower($md5sum) == strtolower($atts['md5sum'])) { 397 $this->log(2, "md5sum ok: $final_dest_file"); 398 } else { 399 if (empty($options['force'])) { 400 // delete the file 401 if (file_exists($dest_file)) { 402 unlink($dest_file); 403 } 404 if (!isset($options['ignore-errors'])) { 405 return $this->raiseError("bad md5sum for file $final_dest_file", 406 PEAR_INSTALLER_FAILED); 407 } else { 408 if (!isset($options['soft'])) { 409 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 410 } 411 } 412 } else { 413 if (!isset($options['soft'])) { 414 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 415 } 416 } 417 } 418 } 419 // }}} 420 // {{{ set file permissions 421 if (!OS_WINDOWS) { 422 if ($atts['role'] == 'script') { 423 $mode = 0777 & ~(int)octdec($this->config->get('umask')); 424 $this->log(3, "+ chmod +x $dest_file"); 425 } else { 426 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 427 } 428 $this->addFileOperation("chmod", array($mode, $dest_file)); 429 if (!@chmod($dest_file, $mode)) { 430 if (!isset($options['soft'])) { 431 $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); 432 } 433 } 434 } 435 // }}} 436 $this->addFileOperation("rename", array($dest_file, $final_dest_file, 437 $atts['role'] == 'ext')); 438 } 439 // Store the full path where the file was installed for easy unistall 440 $this->addFileOperation("installed_as", array($file, $installed_as, 441 $save_destdir, dirname(substr($installedas_dest_file, strlen($save_destdir))))); 442 443 //$this->log(2, "installed: $dest_file"); 444 return PEAR_INSTALLER_OK; 445 } 446 447 // }}} 448 // {{{ _installFile2() 449 450 /** 451 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 452 * @param string filename 453 * @param array attributes from <file> tag in package.xml 454 * @param string path to install the file in 455 * @param array options from command-line 456 * @access private 457 */ 458 function _installFile2(&$pkg, $file, $atts, $tmp_path, $options) 459 { 460 if (!isset($this->_registry)) { 461 $this->_registry = &$this->config->getRegistry(); 462 } 463 464 $channel = $pkg->getChannel(); 465 // {{{ assemble the destination paths 466 if (!in_array($atts['attribs']['role'], 467 PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) { 468 return $this->raiseError('Invalid role `' . $atts['attribs']['role'] . 469 "' for file $file"); 470 } 471 $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config); 472 $err = $role->setup($this, $pkg, $atts['attribs'], $file); 473 if (PEAR::isError($err)) { 474 return $err; 475 } 476 if (!$role->isInstallable()) { 477 return; 478 } 479 $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path); 480 if (PEAR::isError($info)) { 481 return $info; 482 } else { 483 list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info; 484 } 485 $final_dest_file = $installed_as = $dest_file; 486 if (isset($this->_options['packagingroot'])) { 487 $final_dest_file = $this->_prependPath($final_dest_file, 488 $this->_options['packagingroot']); 489 } 490 $dest_dir = dirname($final_dest_file); 491 $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file); 492 // }}} 493 494 if (empty($this->_options['register-only'])) { 495 if (!file_exists($dest_dir) || !is_dir($dest_dir)) { 496 if (!$this->mkDirHier($dest_dir)) { 497 return $this->raiseError("failed to mkdir $dest_dir", 498 PEAR_INSTALLER_FAILED); 499 } 500 $this->log(3, "+ mkdir $dest_dir"); 501 } 502 } 503 $attribs = $atts['attribs']; 504 unset($atts['attribs']); 505 // pretty much nothing happens if we are only registering the install 506 if (empty($this->_options['register-only'])) { 507 if (!count($atts)) { // no tasks 508 if (!file_exists($orig_file)) { 509 return $this->raiseError("file $orig_file does not exist", 510 PEAR_INSTALLER_FAILED); 511 } 512 if (!@copy($orig_file, $dest_file)) { 513 return $this->raiseError("failed to write $dest_file: $php_errormsg", 514 PEAR_INSTALLER_FAILED); 515 } 516 $this->log(3, "+ cp $orig_file $dest_file"); 517 if (isset($attribs['md5sum'])) { 518 $md5sum = md5_file($dest_file); 519 } 520 } else { // file with tasks 521 if (!file_exists($orig_file)) { 522 return $this->raiseError("file $orig_file does not exist", 523 PEAR_INSTALLER_FAILED); 524 } 525 $contents = file_get_contents($orig_file); 526 if ($contents === false) { 527 $contents = ''; 528 } 529 if (isset($attribs['md5sum'])) { 530 $md5sum = md5($contents); 531 } 532 foreach ($atts as $tag => $raw) { 533 $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), 534 array('', '_'), $tag); 535 $task = "PEAR_Task_$tag"; 536 $task = &new $task($this->config, $this, PEAR_TASK_INSTALL); 537 if (!$task->isScript()) { // scripts are only handled after installation 538 $task->init($raw, $attribs, $pkg->getLastInstalledVersion()); 539 $res = $task->startSession($pkg, $contents, $final_dest_file); 540 if ($res === false) { 541 continue; // skip this file 542 } 543 if (PEAR::isError($res)) { 544 return $res; 545 } 546 $contents = $res; // save changes 547 } 548 $wp = @fopen($dest_file, "wb"); 549 if (!is_resource($wp)) { 550 return $this->raiseError("failed to create $dest_file: $php_errormsg", 551 PEAR_INSTALLER_FAILED); 552 } 553 if (fwrite($wp, $contents) === false) { 554 return $this->raiseError("failed writing to $dest_file: $php_errormsg", 555 PEAR_INSTALLER_FAILED); 556 } 557 fclose($wp); 558 } 559 } 560 // {{{ check the md5 561 if (isset($md5sum)) { 562 if (strtolower($md5sum) == strtolower($attribs['md5sum'])) { 563 $this->log(2, "md5sum ok: $final_dest_file"); 564 } else { 565 if (empty($options['force'])) { 566 // delete the file 567 if (file_exists($dest_file)) { 568 unlink($dest_file); 569 } 570 if (!isset($options['ignore-errors'])) { 571 return $this->raiseError("bad md5sum for file $final_dest_file", 572 PEAR_INSTALLER_FAILED); 573 } else { 574 if (!isset($options['soft'])) { 575 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 576 } 577 } 578 } else { 579 if (!isset($options['soft'])) { 580 $this->log(0, "warning : bad md5sum for file $final_dest_file"); 581 } 582 } 583 } 584 } 585 // }}} 586 // {{{ set file permissions 587 if (!OS_WINDOWS) { 588 if ($role->isExecutable()) { 589 $mode = 0777 & ~(int)octdec($this->config->get('umask')); 590 $this->log(3, "+ chmod +x $dest_file"); 591 } else { 592 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 593 } 594 $this->addFileOperation("chmod", array($mode, $dest_file)); 595 if (!@chmod($dest_file, $mode)) { 596 if (!isset($options['soft'])) { 597 $this->log(0, "failed to change mode of $dest_file: $php_errormsg"); 598 } 599 } 600 } 601 // }}} 602 $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension())); 603 } 604 // Store the full path where the file was installed for easy uninstall 605 $this->addFileOperation("installed_as", array($file, $installed_as, 606 $save_destdir, dirname(substr($dest_file, strlen($save_destdir))))); 607 608 //$this->log(2, "installed: $dest_file"); 609 return PEAR_INSTALLER_OK; 610 } 611 612 // }}} 613 // {{{ addFileOperation() 614 615 /** 616 * Add a file operation to the current file transaction. 617 * 618 * @see startFileTransaction() 619 * @param string $type This can be one of: 620 * - rename: rename a file ($data has 3 values) 621 * - backup: backup an existing file ($data has 1 value) 622 * - removebackup: clean up backups created during install ($data has 1 value) 623 * - chmod: change permissions on a file ($data has 2 values) 624 * - delete: delete a file ($data has 1 value) 625 * - rmdir: delete a directory if empty ($data has 1 value) 626 * - installed_as: mark a file as installed ($data has 4 values). 627 * @param array $data For all file operations, this array must contain the 628 * full path to the file or directory that is being operated on. For 629 * the rename command, the first parameter must be the file to rename, 630 * the second its new name, the third whether this is a PHP extension. 631 * 632 * The installed_as operation contains 4 elements in this order: 633 * 1. Filename as listed in the filelist element from package.xml 634 * 2. Full path to the installed file 635 * 3. Full path from the php_dir configuration variable used in this 636 * installation 637 * 4. Relative path from the php_dir that this file is installed in 638 */ 639 function addFileOperation($type, $data) 640 { 641 if (!is_array($data)) { 642 return $this->raiseError('Internal Error: $data in addFileOperation' 643 . ' must be an array, was ' . gettype($data)); 644 } 645 if ($type == 'chmod') { 646 $octmode = decoct($data[0]); 647 $this->log(3, "adding to transaction: $type $octmode $data[1]"); 648 } else { 649 $this->log(3, "adding to transaction: $type " . implode(" ", $data)); 650 } 651 $this->file_operations[] = array($type, $data); 652 } 653 654 // }}} 655 // {{{ startFileTransaction() 656 657 function startFileTransaction($rollback_in_case = false) 658 { 659 if (count($this->file_operations) && $rollback_in_case) { 660 $this->rollbackFileTransaction(); 661 } 662 $this->file_operations = array(); 663 } 664 665 // }}} 666 // {{{ commitFileTransaction() 667 668 function commitFileTransaction() 669 { 670 $n = count($this->file_operations); 671 $this->log(2, "about to commit $n file operations"); 672 // {{{ first, check permissions and such manually 673 $errors = array(); 674 foreach ($this->file_operations as $tr) { 675 list($type, $data) = $tr; 676 switch ($type) { 677 case 'rename': 678 if (!file_exists($data[0])) { 679 $errors[] = "cannot rename file $data[0], doesn't exist"; 680 } 681 // check that dest dir. is writable 682 if (!is_writable(dirname($data[1]))) { 683 $errors[] = "permission denied ($type): $data[1]"; 684 } 685 break; 686 case 'chmod': 687 // check that file is writable 688 if (!is_writable($data[1])) { 689 $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]); 690 } 691 break; 692 case 'delete': 693 if (!file_exists($data[0])) { 694 $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted"); 695 } 696 // check that directory is writable 697 if (file_exists($data[0])) { 698 if (!is_writable(dirname($data[0]))) { 699 $errors[] = "permission denied ($type): $data[0]"; 700 } else { 701 // make sure the file to be deleted can be opened for writing 702 $fp = false; 703 if (!is_dir($data[0]) && 704 (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) { 705 $errors[] = "permission denied ($type): $data[0]"; 706 } elseif ($fp) { 707 fclose($fp); 708 } 709 } 710 } 711 break; 712 } 713 714 } 715 // }}} 716 $m = sizeof($errors); 717 if ($m > 0) { 718 foreach ($errors as $error) { 719 if (!isset($this->_options['soft'])) { 720 $this->log(1, $error); 721 } 722 } 723 if (!isset($this->_options['ignore-errors'])) { 724 return false; 725 } 726 } 727 $this->_dirtree = array(); 728 // {{{ really commit the transaction 729 foreach ($this->file_operations as $tr) { 730 list($type, $data) = $tr; 731 switch ($type) { 732 case 'backup': 733 if (!@copy($data[0], $data[0] . '.bak')) { 734 $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] . 735 '.bak ' . $php_errormsg); 736 return false; 737 } 738 $this->log(3, "+ backup $data[0] to $data[0].bak"); 739 break; 740 case 'removebackup': 741 if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) { 742 unlink($data[0] . '.bak'); 743 $this->log(3, "+ rm backup of $data[0] ($data[0].bak)"); 744 } 745 break; 746 case 'rename': 747 if (file_exists($data[1])) { 748 $test = @unlink($data[1]); 749 } else { 750 $test = null; 751 } 752 if (!$test && file_exists($data[1])) { 753 if ($data[2]) { 754 $extra = ', this extension must be installed manually. Rename to "' . 755 basename($data[1]) . '"'; 756 } else { 757 $extra = ''; 758 } 759 if (!isset($this->_options['soft'])) { 760 $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' . 761 $data[0] . $extra); 762 } 763 if (!isset($this->_options['ignore-errors'])) { 764 return false; 765 } 766 } 767 // permissions issues with rename - copy() is far superior 768 $perms = @fileperms($data[0]); 769 if (!@copy($data[0], $data[1])) { 770 $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] . 771 ' ' . $php_errormsg); 772 return false; 773 } 774 // copy over permissions, otherwise they are lost 775 @chmod($data[1], $perms); 776 @unlink($data[0]); 777 $this->log(3, "+ mv $data[0] $data[1]"); 778 break; 779 case 'chmod': 780 if (!@chmod($data[1], $data[0])) { 781 $this->log(1, 'Could not chmod ' . $data[1] . ' to ' . 782 decoct($data[0]) . ' ' . $php_errormsg); 783 return false; 784 } 785 $octmode = decoct($data[0]); 786 $this->log(3, "+ chmod $octmode $data[1]"); 787 break; 788 case 'delete': 789 if (file_exists($data[0])) { 790 if (!@unlink($data[0])) { 791 $this->log(1, 'Could not delete ' . $data[0] . ' ' . 792 $php_errormsg); 793 return false; 794 } 795 $this->log(3, "+ rm $data[0]"); 796 } 797 break; 798 case 'rmdir': 799 if (file_exists($data[0])) { 800 do { 801 $testme = opendir($data[0]); 802 while (false !== ($entry = readdir($testme))) { 803 if ($entry == '.' || $entry == '..') { 804 continue; 805 } 806 closedir($testme); 807 break 2; // this directory is not empty and can't be 808 // deleted 809 } 810 closedir($testme); 811 if (!@rmdir($data[0])) { 812 $this->log(1, 'Could not rmdir ' . $data[0] . ' ' . 813 $php_errormsg); 814 return false; 815 } 816 $this->log(3, "+ rmdir $data[0]"); 817 } while (false); 818 } 819 break; 820 case 'installed_as': 821 $this->pkginfo->setInstalledAs($data[0], $data[1]); 822 if (!isset($this->_dirtree[dirname($data[1])])) { 823 $this->_dirtree[dirname($data[1])] = true; 824 $this->pkginfo->setDirtree(dirname($data[1])); 825 826 while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\' 827 && $data[3] != '.') { 828 $this->pkginfo->setDirtree($pp = 829 $this->_prependPath($data[3], $data[2])); 830 $this->_dirtree[$pp] = true; 831 $data[3] = dirname($data[3]); 832 } 833 } 834 break; 835 } 836 } 837 // }}} 838 $this->log(2, "successfully committed $n file operations"); 839 $this->file_operations = array(); 840 return true; 841 } 842 843 // }}} 844 // {{{ rollbackFileTransaction() 845 846 function rollbackFileTransaction() 847 { 848 $n = count($this->file_operations); 849 $this->log(2, "rolling back $n file operations"); 850 foreach ($this->file_operations as $tr) { 851 list($type, $data) = $tr; 852 switch ($type) { 853 case 'backup': 854 if (file_exists($data[0] . '.bak')) { 855 @unlink($data[0]); 856 @copy($data[0] . '.bak', $data[0]); 857 $this->log(3, "+ restore $data[0] from $data[0].bak"); 858 } 859 break; 860 case 'rename': 861 @unlink($data[0]); 862 $this->log(3, "+ rm $data[0]"); 863 break; 864 case 'mkdir': 865 @rmdir($data[0]); 866 $this->log(3, "+ rmdir $data[0]"); 867 break; 868 case 'chmod': 869 break; 870 case 'delete': 871 break; 872 case 'installed_as': 873 $this->pkginfo->setInstalledAs($data[0], false); 874 break; 875 } 876 } 877 $this->pkginfo->resetDirtree(); 878 $this->file_operations = array(); 879 } 880 881 // }}} 882 // {{{ mkDirHier($dir) 883 884 function mkDirHier($dir) 885 { 886 $this->addFileOperation('mkdir', array($dir)); 887 return parent::mkDirHier($dir); 888 } 889 890 // }}} 891 // {{{ download() 892 893 /** 894 * Download any files and their dependencies, if necessary 895 * 896 * @param array a mixed list of package names, local files, or package.xml 897 * @param PEAR_Config 898 * @param array options from the command line 899 * @param array this is the array that will be populated with packages to 900 * install. Format of each entry: 901 * 902 * <code> 903 * array('pkg' => 'package_name', 'file' => '/path/to/local/file', 904 * 'info' => array() // parsed package.xml 905 * ); 906 * </code> 907 * @param array this will be populated with any error messages 908 * @param false private recursion variable 909 * @param false private recursion variable 910 * @param false private recursion variable 911 * @deprecated in favor of PEAR_Downloader 912 */ 913 function download($packages, $options, &$config, &$installpackages, 914 &$errors, $installed = false, $willinstall = false, $state = false) 915 { 916 // trickiness: initialize here 917 parent::PEAR_Downloader($this->ui, $options, $config); 918 $ret = parent::download($packages); 919 $errors = $this->getErrorMsgs(); 920 $installpackages = $this->getDownloadedPackages(); 921 trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " . 922 "in favor of PEAR_Downloader class", E_USER_WARNING); 923 return $ret; 924 } 925 926 // }}} 927 // {{{ _parsePackageXml() 928 929 function _parsePackageXml(&$descfile, &$tmpdir) 930 { 931 if (substr($descfile, -4) == '.xml') { 932 $tmpdir = false; 933 } else { 934 // {{{ Decompress pack in tmp dir ------------------------------------- 935 936 // To allow relative package file names 937 $descfile = realpath($descfile); 938 939 if (PEAR::isError($tmpdir = System::mktemp('-d'))) { 940 return $tmpdir; 941 } 942 $this->log(3, '+ tmp dir created at ' . $tmpdir); 943 // }}} 944 } 945 // Parse xml file ----------------------------------------------- 946 $pkg = new PEAR_PackageFile($this->config, $this->debug, $tmpdir); 947 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 948 $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING); 949 PEAR::staticPopErrorHandling(); 950 if (PEAR::isError($p)) { 951 if (is_array($p->getUserInfo())) { 952 foreach ($p->getUserInfo() as $err) { 953 $loglevel = $err['level'] == 'error' ? 0 : 1; 954 if (!isset($this->_options['soft'])) { 955 $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']); 956 } 957 } 958 } 959 return $this->raiseError('Installation failed: invalid package file'); 960 } else { 961 $descfile = $p->getPackageFile(); 962 } 963 return $p; 964 } 965 966 // }}} 967 /** 968 * Set the list of PEAR_Downloader_Package objects to allow more sane 969 * dependency validation 970 * @param array 971 */ 972 function setDownloadedPackages(&$pkgs) 973 { 974 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 975 $err = $this->analyzeDependencies($pkgs); 976 PEAR::popErrorHandling(); 977 if (PEAR::isError($err)) { 978 return $err; 979 } 980 $this->_downloadedPackages = &$pkgs; 981 } 982 983 /** 984 * Set the list of PEAR_Downloader_Package objects to allow more sane 985 * dependency validation 986 * @param array 987 */ 988 function setUninstallPackages(&$pkgs) 989 { 990 $this->_downloadedPackages = &$pkgs; 991 } 992 993 function getInstallPackages() 994 { 995 return $this->_downloadedPackages; 996 } 997 998 // {{{ install() 999 1000 /** 1001 * Installs the files within the package file specified. 1002 * 1003 * @param string|PEAR_Downloader_Package $pkgfile path to the package file, 1004 * or a pre-initialized packagefile object 1005 * @param array $options 1006 * recognized options: 1007 * - installroot : optional prefix directory for installation 1008 * - force : force installation 1009 * - register-only : update registry but don't install files 1010 * - upgrade : upgrade existing install 1011 * - soft : fail silently 1012 * - nodeps : ignore dependency conflicts/missing dependencies 1013 * - alldeps : install all dependencies 1014 * - onlyreqdeps : install only required dependencies 1015 * 1016 * @return array|PEAR_Error package info if successful 1017 */ 1018 1019 function install($pkgfile, $options = array()) 1020 { 1021 $this->_options = $options; 1022 $this->_registry = &$this->config->getRegistry(); 1023 if (is_object($pkgfile)) { 1024 $dlpkg = &$pkgfile; 1025 $pkg = $pkgfile->getPackageFile(); 1026 $pkgfile = $pkg->getArchiveFile(); 1027 $descfile = $pkg->getPackageFile(); 1028 $tmpdir = dirname($descfile); 1029 } else { 1030 $descfile = $pkgfile; 1031 $tmpdir = ''; 1032 if (PEAR::isError($pkg = &$this->_parsePackageXml($descfile, $tmpdir))) { 1033 return $pkg; 1034 } 1035 } 1036 1037 if (realpath($descfile) != realpath($pkgfile)) { 1038 $tar = new Archive_Tar($pkgfile); 1039 if (!$tar->extract($tmpdir)) { 1040 return $this->raiseError("unable to unpack $pkgfile"); 1041 } 1042 } 1043 1044 $pkgname = $pkg->getName(); 1045 $channel = $pkg->getChannel(); 1046 if (isset($this->_options['packagingroot'])) { 1047 $regdir = $this->_prependPath( 1048 $this->config->get('php_dir', null, 'pear.php.net'), 1049 $this->_options['packagingroot']); 1050 $packrootphp_dir = $this->_prependPath( 1051 $this->config->get('php_dir', null, $channel), 1052 $this->_options['packagingroot']); 1053 } 1054 1055 if (isset($options['installroot'])) { 1056 $this->config->setInstallRoot($options['installroot']); 1057 $this->_registry = &$this->config->getRegistry(); 1058 $installregistry = &$this->_registry; 1059 $this->installroot = ''; // all done automagically now 1060 $php_dir = $this->config->get('php_dir', null, $channel); 1061 } else { 1062 $this->config->setInstallRoot(false); 1063 $this->_registry = &$this->config->getRegistry(); 1064 if (isset($this->_options['packagingroot'])) { 1065 $installregistry = &new PEAR_Registry($regdir); 1066 if (!$installregistry->channelExists($channel, true)) { 1067 // we need to fake a channel-discover of this channel 1068 $chanobj = $this->_registry->getChannel($channel, true); 1069 $installregistry->addChannel($chanobj); 1070 } 1071 $php_dir = $packrootphp_dir; 1072 } else { 1073 $installregistry = &$this->_registry; 1074 $php_dir = $this->config->get('php_dir', null, $channel); 1075 } 1076 $this->installroot = ''; 1077 } 1078 1079 // {{{ checks to do when not in "force" mode 1080 if (empty($options['force']) && 1081 (file_exists($this->config->get('php_dir')) && 1082 is_dir($this->config->get('php_dir')))) { 1083 $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname); 1084 $instfilelist = $pkg->getInstallationFileList(true); 1085 if (PEAR::isError($instfilelist)) { 1086 return $instfilelist; 1087 } 1088 // ensure we have the most accurate registry 1089 $installregistry->flushFileMap(); 1090 $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1'); 1091 if (PEAR::isError($test)) { 1092 return $test; 1093 } 1094 if (sizeof($test)) { 1095 $pkgs = $this->getInstallPackages(); 1096 $found = false; 1097 foreach ($pkgs as $param) { 1098 if ($pkg->isSubpackageOf($param)) { 1099 $found = true; 1100 break; 1101 } 1102 } 1103 if ($found) { 1104 // subpackages can conflict with earlier versions of parent packages 1105 $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel()); 1106 $tmp = $test; 1107 foreach ($tmp as $file => $info) { 1108 if (is_array($info)) { 1109 if (strtolower($info[1]) == strtolower($param->getPackage()) && 1110 strtolower($info[0]) == strtolower($param->getChannel())) { 1111 unset($test[$file]); 1112 unset($parentreg['filelist'][$file]); 1113 } 1114 } else { 1115 if (strtolower($param->getChannel()) != 'pear.php.net') { 1116 continue; 1117 } 1118 if (strtolower($info) == strtolower($param->getPackage())) { 1119 unset($test[$file]); 1120 unset($parentreg['filelist'][$file]); 1121 } 1122 } 1123 } 1124 $pfk = &new PEAR_PackageFile($this->config); 1125 $parentpkg = &$pfk->fromArray($parentreg); 1126 $installregistry->updatePackage2($parentpkg); 1127 } 1128 if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) { 1129 $tmp = $test; 1130 foreach ($tmp as $file => $info) { 1131 if (is_string($info)) { 1132 // pear.php.net packages are always stored as strings 1133 if (strtolower($info) == strtolower($param->getPackage())) { 1134 // upgrading existing package 1135 unset($test[$file]); 1136 } 1137 } 1138 } 1139 } 1140 if (sizeof($test)) { 1141 $msg = "$channel/$pkgname: conflicting files found:\n"; 1142 $longest = max(array_map("strlen", array_keys($test))); 1143 $fmt = "%$longest}s (%s)\n"; 1144 foreach ($test as $file => $info) { 1145 if (!is_array($info)) { 1146 $info = array('pear.php.net', $info); 1147 } 1148 $info = $info[0] . '/' . $info[1]; 1149 $msg .= sprintf($fmt, $file, $info); 1150 } 1151 if (!isset($options['ignore-errors'])) { 1152 return $this->raiseError($msg); 1153 } else { 1154 if (!isset($options['soft'])) { 1155 $this->log(0, "WARNING: $msg"); 1156 } 1157 } 1158 } 1159 } 1160 } 1161 // }}} 1162 1163 $this->startFileTransaction(); 1164 1165 if (empty($options['upgrade']) && empty($options['soft'])) { 1166 // checks to do only when installing new packages 1167 if ($channel == 'pecl.php.net') { 1168 $test = $installregistry->packageExists($pkgname, $channel); 1169 if (!$test) { 1170 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1171 } 1172 } else { 1173 $test = $installregistry->packageExists($pkgname, $channel); 1174 } 1175 if (empty($options['force']) && $test) { 1176 return $this->raiseError("$channel/$pkgname is already installed"); 1177 } 1178 } else { 1179 $usechannel = $channel; 1180 if ($channel == 'pecl.php.net') { 1181 $test = $installregistry->packageExists($pkgname, $channel); 1182 if (!$test) { 1183 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1184 $usechannel = 'pear.php.net'; 1185 } 1186 } else { 1187 $test = $installregistry->packageExists($pkgname, $channel); 1188 } 1189 if ($test) { 1190 $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel); 1191 $v2 = $pkg->getVersion(); 1192 $cmp = version_compare("$v1", "$v2", 'gt'); 1193 if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) { 1194 return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)"); 1195 } 1196 if (empty($options['register-only'])) { 1197 // when upgrading, remove old release's files first: 1198 if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel, 1199 true))) { 1200 if (!isset($options['ignore-errors'])) { 1201 return $this->raiseError($err); 1202 } else { 1203 if (!isset($options['soft'])) { 1204 $this->log(0, 'WARNING: ' . $err->getMessage()); 1205 } 1206 } 1207 } else { 1208 $backedup = $err; 1209 } 1210 } 1211 } 1212 } 1213 1214 // {{{ Copy files to dest dir --------------------------------------- 1215 1216 // info from the package it self we want to access from _installFile 1217 $this->pkginfo = &$pkg; 1218 // used to determine whether we should build any C code 1219 $this->source_files = 0; 1220 1221 $savechannel = $this->config->get('default_channel'); 1222 if (empty($options['register-only']) && !is_dir($php_dir)) { 1223 if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) { 1224 return $this->raiseError("no installation destination directory '$php_dir'\n"); 1225 } 1226 } 1227 1228 $tmp_path = dirname($descfile); 1229 if (substr($pkgfile, -4) != '.xml') { 1230 $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion(); 1231 } 1232 1233 $this->configSet('default_channel', $channel); 1234 // {{{ install files 1235 1236 $ver = $pkg->getPackagexmlVersion(); 1237 if (version_compare($ver, '2.0', '>=')) { 1238 $filelist = $pkg->getInstallationFilelist(); 1239 } else { 1240 $filelist = $pkg->getFileList(); 1241 } 1242 if (PEAR::isError($filelist)) { 1243 return $filelist; 1244 } 1245 $pkg->resetFilelist(); 1246 $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(), 1247 'version', $pkg->getChannel())); 1248 foreach ($filelist as $file => $atts) { 1249 if ($pkg->getPackagexmlVersion() == '1.0') { 1250 $this->expectError(PEAR_INSTALLER_FAILED); 1251 $res = $this->_installFile($file, $atts, $tmp_path, $options); 1252 $this->popExpect(); 1253 } else { 1254 $this->expectError(PEAR_INSTALLER_FAILED); 1255 $res = $this->_installFile2($pkg, $file, $atts, $tmp_path, $options); 1256 $this->popExpect(); 1257 } 1258 if (PEAR::isError($res)) { 1259 if (empty($options['ignore-errors'])) { 1260 $this->rollbackFileTransaction(); 1261 if ($res->getMessage() == "file does not exist") { 1262 $this->raiseError("file $file in package.xml does not exist"); 1263 } 1264 return $this->raiseError($res); 1265 } else { 1266 if (!isset($options['soft'])) { 1267 $this->log(0, "Warning: " . $res->getMessage()); 1268 } 1269 } 1270 } 1271 if ($res == PEAR_INSTALLER_OK) { 1272 // Register files that were installed 1273 $pkg->installedFile($file, $atts); 1274 } 1275 } 1276 // }}} 1277 1278 // {{{ compile and install source files 1279 if ($this->source_files > 0 && empty($options['nobuild'])) { 1280 if (PEAR::isError($err = 1281 $this->_compileSourceFiles($savechannel, $pkg))) { 1282 return $err; 1283 } 1284 } 1285 // }}} 1286 1287 if (isset($backedup)) { 1288 $this->_removeBackups($backedup); 1289 } 1290 if (!$this->commitFileTransaction()) { 1291 $this->rollbackFileTransaction(); 1292 $this->configSet('default_channel', $savechannel); 1293 return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED); 1294 } 1295 // }}} 1296 1297 $ret = false; 1298 $installphase = 'install'; 1299 $oldversion = false; 1300 // {{{ Register that the package is installed ----------------------- 1301 if (empty($options['upgrade'])) { 1302 // if 'force' is used, replace the info in registry 1303 $usechannel = $channel; 1304 if ($channel == 'pecl.php.net') { 1305 $test = $installregistry->packageExists($pkgname, $channel); 1306 if (!$test) { 1307 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1308 $usechannel = 'pear.php.net'; 1309 } 1310 } else { 1311 $test = $installregistry->packageExists($pkgname, $channel); 1312 } 1313 if (!empty($options['force']) && $test) { 1314 $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel); 1315 $installregistry->deletePackage($pkgname, $usechannel); 1316 } 1317 $ret = $installregistry->addPackage2($pkg); 1318 } else { 1319 $usechannel = $channel; 1320 if ($channel == 'pecl.php.net') { 1321 $test = $installregistry->packageExists($pkgname, $channel); 1322 if (!$test) { 1323 $test = $installregistry->packageExists($pkgname, 'pear.php.net'); 1324 $usechannel = 'pear.php.net'; 1325 } 1326 } else { 1327 $test = $installregistry->packageExists($pkgname, $channel); 1328 } 1329 // new: upgrade installs a package if it isn't installed 1330 if (!$test) { 1331 $ret = $installregistry->addPackage2($pkg); 1332 } else { 1333 if ($usechannel != $channel) { 1334 $installregistry->deletePackage($pkgname, $usechannel); 1335 $ret = $installregistry->addPackage2($pkg); 1336 } else { 1337 $ret = $installregistry->updatePackage2($pkg); 1338 } 1339 $installphase = 'upgrade'; 1340 } 1341 } 1342 if (!$ret) { 1343 $this->configSet('default_channel', $savechannel); 1344 return $this->raiseError("Adding package $channel/$pkgname to registry failed"); 1345 } 1346 // }}} 1347 $this->configSet('default_channel', $savechannel); 1348 if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist 1349 if (PEAR_Task_Common::hasPostinstallTasks()) { 1350 PEAR_Task_Common::runPostinstallTasks($installphase); 1351 } 1352 } 1353 return $pkg->toArray(true); 1354 } 1355 1356 // }}} 1357 1358 // {{{ _compileSourceFiles() 1359 /** 1360 * @param string 1361 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2 1362 */ 1363 function _compileSourceFiles($savechannel, &$filelist) 1364 { 1365 require_once 'PEAR/Builder.php'; 1366 $this->log(1, "$this->source_files source files, building"); 1367 $bob = &new PEAR_Builder($this->ui); 1368 $bob->debug = $this->debug; 1369 $built = $bob->build($filelist, array(&$this, '_buildCallback')); 1370 if (PEAR::isError($built)) { 1371 $this->rollbackFileTransaction(); 1372 $this->configSet('default_channel', $savechannel); 1373 return $built; 1374 } 1375 $this->log(1, "\nBuild process completed successfully"); 1376 foreach ($built as $ext) { 1377 $bn = basename($ext['file']); 1378 list($_ext_name, $_ext_suff) = explode('.', $bn); 1379 if ($_ext_suff == '.so' || $_ext_suff == '.dll') { 1380 if (extension_loaded($_ext_name)) { 1381 $this->raiseError("Extension '$_ext_name' already loaded. " . 1382 'Please unload it in your php.ini file ' . 1383 'prior to install or upgrade'); 1384 } 1385 $role = 'ext'; 1386 } else { 1387 $role = 'src'; 1388 } 1389 $dest = $ext['dest']; 1390 $packagingroot = ''; 1391 if (isset($this->_options['packagingroot'])) { 1392 $packagingroot = $this->_options['packagingroot']; 1393 } 1394 $copyto = $this->_prependPath($dest, $packagingroot); 1395 if ($copyto != $dest) { 1396 $this->log(1, "Installing '$dest' as '$copyto'"); 1397 } else { 1398 $this->log(1, "Installing '$dest'"); 1399 } 1400 $copydir = dirname($copyto); 1401 // pretty much nothing happens if we are only registering the install 1402 if (empty($this->_options['register-only'])) { 1403 if (!file_exists($copydir) || !is_dir($copydir)) { 1404 if (!$this->mkDirHier($copydir)) { 1405 return $this->raiseError("failed to mkdir $copydir", 1406 PEAR_INSTALLER_FAILED); 1407 } 1408 $this->log(3, "+ mkdir $copydir"); 1409 } 1410 if (!@copy($ext['file'], $copyto)) { 1411 return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED); 1412 } 1413 $this->log(3, "+ cp $ext[file] $copyto"); 1414 $this->addFileOperation('rename', array($ext['file'], $copyto)); 1415 if (!OS_WINDOWS) { 1416 $mode = 0666 & ~(int)octdec($this->config->get('umask')); 1417 $this->addFileOperation('chmod', array($mode, $copyto)); 1418 if (!@chmod($copyto, $mode)) { 1419 $this->log(0, "failed to change mode of $copyto ($php_errormsg)"); 1420 } 1421 } 1422 } 1423 1424 if ($filelist->getPackageXmlVersion() == '1.0') { 1425 $filelist->installedFile($bn, array( 1426 'role' => $role, 1427 'name' => $bn, 1428 'installed_as' => $dest, 1429 'php_api' => $ext['php_api'], 1430 'zend_mod_api' => $ext['zend_mod_api'], 1431 'zend_ext_api' => $ext['zend_ext_api'], 1432 )); 1433 } else { 1434 $filelist->installedFile($bn, array('attribs' => array( 1435 'role' => $role, 1436 'name' => $bn, 1437 'installed_as' => $dest, 1438 'php_api' => $ext['php_api'], 1439 'zend_mod_api' => $ext['zend_mod_api'], 1440 'zend_ext_api' => $ext['zend_ext_api'], 1441 ))); 1442 } 1443 } 1444 } 1445 1446 // }}} 1447 function &getUninstallPackages() 1448 { 1449 return $this->_downloadedPackages; 1450 } 1451 // {{{ uninstall() 1452 1453 /** 1454 * Uninstall a package 1455 * 1456 * This method removes all files installed by the application, and then 1457 * removes any empty directories. 1458 * @param string package name 1459 * @param array Command-line options. Possibilities include: 1460 * 1461 * - installroot: base installation dir, if not the default 1462 * - register-only : update registry but don't remove files 1463 * - nodeps: do not process dependencies of other packages to ensure 1464 * uninstallation does not break things 1465 */ 1466 function uninstall($package, $options = array()) 1467 { 1468 if (isset($options['installroot'])) { 1469 $this->config->setInstallRoot($options['installroot']); 1470 $this->installroot = ''; 1471 } else { 1472 $this->config->setInstallRoot(''); 1473 $this->installroot = ''; 1474 } 1475 $this->_registry = &$this->config->getRegistry(); 1476 if (is_object($package)) { 1477 $channel = $package->getChannel(); 1478 $pkg = $package; 1479 $package = $pkg->getPackage(); 1480 } else { 1481 $pkg = false; 1482 $info = $this->_registry->parsePackageName($package, 1483 $this->config->get('default_channel')); 1484 $channel = $info['channel']; 1485 $package = $info['package']; 1486 } 1487 $savechannel = $this->config->get('default_channel'); 1488 $this->configSet('default_channel', $channel); 1489 if (!is_object($pkg)) { 1490 $pkg = $this->_registry->getPackage($package, $channel); 1491 } 1492 if (!$pkg) { 1493 $this->configSet('default_channel', $savechannel); 1494 return $this->raiseError($this->_registry->parsedPackageNameToString( 1495 array( 1496 'channel' => $channel, 1497 'package' => $package 1498 ), true) . ' not installed'); 1499 } 1500 if ($pkg->getInstalledBinary()) { 1501 // this is just an alias for a binary package 1502 return $this->_registry->deletePackage($package, $channel); 1503 } 1504 $filelist = $pkg->getFilelist(); 1505 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN); 1506 if (!class_exists('PEAR_Dependency2')) { 1507 require_once 'PEAR/Dependency2.php'; 1508 } 1509 $depchecker = &new PEAR_Dependency2($this->config, $options, 1510 array('channel' => $channel, 'package' => $package), 1511 PEAR_VALIDATE_UNINSTALLING); 1512 $e = $depchecker->validatePackageUninstall($this); 1513 PEAR::staticPopErrorHandling(); 1514 if (PEAR::isError($e)) { 1515 if (!isset($options['ignore-errors'])) { 1516 return $this->raiseError($e); 1517 } else { 1518 if (!isset($options['soft'])) { 1519 $this->log(0, 'WARNING: ' . $e->getMessage()); 1520 } 1521 } 1522 } elseif (is_array($e)) { 1523 if (!isset($options['soft'])) { 1524 $this->log(0, $e[0]); 1525 } 1526 } 1527 $this->pkginfo = &$pkg; 1528 // pretty much nothing happens if we are only registering the uninstall 1529 if (empty($options['register-only'])) { 1530 // {{{ Delete the files 1531 $this->startFileTransaction(); 1532 PEAR::pushErrorHandling(PEAR_ERROR_RETURN); 1533 if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) { 1534 PEAR::popErrorHandling(); 1535 $this->rollbackFileTransaction(); 1536 $this->configSet('default_channel', $savechannel); 1537 if (!isset($options['ignore-errors'])) { 1538 return $this->raiseError($err); 1539 } else { 1540 if (!isset($options['soft'])) { 1541 $this->log(0, 'WARNING: ' . $err->getMessage()); 1542 } 1543 } 1544 } else { 1545 PEAR::popErrorHandling(); 1546 } 1547 if (!$this->commitFileTransaction()) { 1548 $this->rollbackFileTransaction(); 1549 if (!isset($options['ignore-errors'])) { 1550 return $this->raiseError("uninstall failed"); 1551 } elseif (!isset($options['soft'])) { 1552 $this->log(0, 'WARNING: uninstall failed'); 1553 } 1554 } else { 1555 $this->startFileTransaction(); 1556 if ($dirtree = $pkg->getDirTree()) { 1557 // attempt to delete empty directories 1558 uksort($dirtree, array($this, '_sortDirs')); 1559 foreach($dirtree as $dir => $notused) { 1560 $this->addFileOperation('rmdir', array($dir)); 1561 } 1562 } else { 1563 $this->configSet('default_channel', $savechannel); 1564 return $this->_registry->deletePackage($package, $channel); 1565 } 1566 if (!$this->commitFileTransaction()) { 1567 $this->rollbackFileTransaction(); 1568 if (!isset($options['ignore-errors'])) { 1569 return $this->raiseError("uninstall failed"); 1570 } elseif (!isset($options['soft'])) { 1571 $this->log(0, 'WARNING: uninstall failed'); 1572 } 1573 } 1574 } 1575 // }}} 1576 } 1577 1578 $this->configSet('default_channel', $savechannel); 1579 // Register that the package is no longer installed 1580 return $this->_registry->deletePackage($package, $channel); 1581 } 1582 1583 /** 1584 * Sort a list of arrays of array(downloaded packagefilename) by dependency. 1585 * 1586 * It also removes duplicate dependencies 1587 * @param array an array of PEAR_PackageFile_v[1/2] objects 1588 * @return array|PEAR_Error array of array(packagefilename, package.xml contents) 1589 */ 1590 function sortPackagesForUninstall(&$packages) 1591 { 1592 $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config); 1593 if (PEAR::isError($this->_dependencyDB)) { 1594 return $this->_dependencyDB; 1595 } 1596 usort($packages, array(&$this, '_sortUninstall')); 1597 } 1598 1599 function _sortUninstall($a, $b) 1600 { 1601 if (!$a->getDeps() && !$b->getDeps()) { 1602 return 0; // neither package has dependencies, order is insignificant 1603 } 1604 if ($a->getDeps() && !$b->getDeps()) { 1605 return -1; // $a must be installed after $b because $a has dependencies 1606 } 1607 if (!$a->getDeps() && $b->getDeps()) { 1608 return 1; // $b must be installed after $a because $b has dependencies 1609 } 1610 // both packages have dependencies 1611 if ($this->_dependencyDB->dependsOn($a, $b)) { 1612 return -1; 1613 } 1614 if ($this->_dependencyDB->dependsOn($b, $a)) { 1615 return 1; 1616 } 1617 return 0; 1618 } 1619 1620 // }}} 1621 // {{{ _sortDirs() 1622 function _sortDirs($a, $b) 1623 { 1624 if (strnatcmp($a, $b) == -1) return 1; 1625 if (strnatcmp($a, $b) == 1) return -1; 1626 return 0; 1627 } 1628 1629 // }}} 1630 1631 // {{{ _buildCallback() 1632 1633 function _buildCallback($what, $data) 1634 { 1635 if (($what == 'cmdoutput' && $this->debug > 1) || 1636 ($what == 'output' && $this->debug > 0)) { 1637 $this->ui->outputData(rtrim($data), 'build'); 1638 } 1639 } 1640 1641 // }}} 1642 } 1643 1644 // {{{ md5_file() utility function 1645 if (!function_exists("md5_file")) { 1646 function md5_file($filename) { 1647 if (!$fd = @fopen($file, 'r')) { 1648 return false; 1649 } 1650 fclose($fd); 1651 return md5(file_get_contents($filename)); 1652 } 1653 } 1654 // }}} 1655 1656 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 14:08:00 2007 | par Balluche grâce à PHPXref 0.7 |