12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823 |
- <?php
- /**
- * PEAR_Installer
- *
- * PHP versions 4 and 5
- *
- * @category pear
- * @package PEAR
- * @author Stig Bakken <ssb@php.net>
- * @author Tomas V.V. Cox <cox@idecnet.com>
- * @author Martin Jansen <mj@php.net>
- * @author Greg Beaver <cellog@php.net>
- * @copyright 1997-2009 The Authors
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- * @version CVS: $Id: Installer.php 313024 2011-07-06 19:51:24Z dufuz $
- * @link http://pear.php.net/package/PEAR
- * @since File available since Release 0.1
- */
- /**
- * Used for installation groups in package.xml 2.0 and platform exceptions
- */
- require_once 'OS/Guess.php';
- require_once 'PEAR/Downloader.php';
- define('PEAR_INSTALLER_NOBINARY', -240);
- /**
- * Administration class used to install PEAR packages and maintain the
- * installed package database.
- *
- * @category pear
- * @package PEAR
- * @author Stig Bakken <ssb@php.net>
- * @author Tomas V.V. Cox <cox@idecnet.com>
- * @author Martin Jansen <mj@php.net>
- * @author Greg Beaver <cellog@php.net>
- * @copyright 1997-2009 The Authors
- * @license http://opensource.org/licenses/bsd-license.php New BSD License
- * @version Release: 1.9.4
- * @link http://pear.php.net/package/PEAR
- * @since Class available since Release 0.1
- */
- class PEAR_Installer extends PEAR_Downloader
- {
- // {{{ properties
- /** name of the package directory, for example Foo-1.0
- * @var string
- */
- var $pkgdir;
- /** directory where PHP code files go
- * @var string
- */
- var $phpdir;
- /** directory where PHP extension files go
- * @var string
- */
- var $extdir;
- /** directory where documentation goes
- * @var string
- */
- var $docdir;
- /** installation root directory (ala PHP's INSTALL_ROOT or
- * automake's DESTDIR
- * @var string
- */
- var $installroot = '';
- /** debug level
- * @var int
- */
- var $debug = 1;
- /** temporary directory
- * @var string
- */
- var $tmpdir;
- /**
- * PEAR_Registry object used by the installer
- * @var PEAR_Registry
- */
- var $registry;
- /**
- * array of PEAR_Downloader_Packages
- * @var array
- */
- var $_downloadedPackages;
- /** List of file transactions queued for an install/upgrade/uninstall.
- *
- * Format:
- * array(
- * 0 => array("rename => array("from-file", "to-file")),
- * 1 => array("delete" => array("file-to-delete")),
- * ...
- * )
- *
- * @var array
- */
- var $file_operations = array();
- // }}}
- // {{{ constructor
- /**
- * PEAR_Installer constructor.
- *
- * @param object $ui user interface object (instance of PEAR_Frontend_*)
- *
- * @access public
- */
- function PEAR_Installer(&$ui)
- {
- parent::PEAR_Common();
- $this->setFrontendObject($ui);
- $this->debug = $this->config->get('verbose');
- }
- function setOptions($options)
- {
- $this->_options = $options;
- }
- function setConfig(&$config)
- {
- $this->config = &$config;
- $this->_registry = &$config->getRegistry();
- }
- // }}}
- function _removeBackups($files)
- {
- foreach ($files as $path) {
- $this->addFileOperation('removebackup', array($path));
- }
- }
- // {{{ _deletePackageFiles()
- /**
- * Delete a package's installed files, does not remove empty directories.
- *
- * @param string package name
- * @param string channel name
- * @param bool if true, then files are backed up first
- * @return bool TRUE on success, or a PEAR error on failure
- * @access protected
- */
- function _deletePackageFiles($package, $channel = false, $backup = false)
- {
- if (!$channel) {
- $channel = 'pear.php.net';
- }
- if (!strlen($package)) {
- return $this->raiseError("No package to uninstall given");
- }
- if (strtolower($package) == 'pear' && $channel == 'pear.php.net') {
- // to avoid race conditions, include all possible needed files
- require_once 'PEAR/Task/Common.php';
- require_once 'PEAR/Task/Replace.php';
- require_once 'PEAR/Task/Unixeol.php';
- require_once 'PEAR/Task/Windowseol.php';
- require_once 'PEAR/PackageFile/v1.php';
- require_once 'PEAR/PackageFile/v2.php';
- require_once 'PEAR/PackageFile/Generator/v1.php';
- require_once 'PEAR/PackageFile/Generator/v2.php';
- }
- $filelist = $this->_registry->packageInfo($package, 'filelist', $channel);
- if ($filelist == null) {
- return $this->raiseError("$channel/$package not installed");
- }
- $ret = array();
- foreach ($filelist as $file => $props) {
- if (empty($props['installed_as'])) {
- continue;
- }
- $path = $props['installed_as'];
- if ($backup) {
- $this->addFileOperation('backup', array($path));
- $ret[] = $path;
- }
- $this->addFileOperation('delete', array($path));
- }
- if ($backup) {
- return $ret;
- }
- return true;
- }
- // }}}
- // {{{ _installFile()
- /**
- * @param string filename
- * @param array attributes from <file> tag in package.xml
- * @param string path to install the file in
- * @param array options from command-line
- * @access private
- */
- function _installFile($file, $atts, $tmp_path, $options)
- {
- // {{{ return if this file is meant for another platform
- static $os;
- if (!isset($this->_registry)) {
- $this->_registry = &$this->config->getRegistry();
- }
- if (isset($atts['platform'])) {
- if (empty($os)) {
- $os = new OS_Guess();
- }
- if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
- $negate = true;
- $platform = substr($atts['platform'], 1);
- } else {
- $negate = false;
- $platform = $atts['platform'];
- }
- if ((bool) $os->matchSignature($platform) === $negate) {
- $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
- return PEAR_INSTALLER_SKIPPED;
- }
- }
- // }}}
- $channel = $this->pkginfo->getChannel();
- // {{{ assemble the destination paths
- switch ($atts['role']) {
- case 'src':
- case 'extsrc':
- $this->source_files++;
- return;
- case 'doc':
- case 'data':
- case 'test':
- $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel) .
- DIRECTORY_SEPARATOR . $this->pkginfo->getPackage();
- unset($atts['baseinstalldir']);
- break;
- case 'ext':
- case 'php':
- $dest_dir = $this->config->get($atts['role'] . '_dir', null, $channel);
- break;
- case 'script':
- $dest_dir = $this->config->get('bin_dir', null, $channel);
- break;
- default:
- return $this->raiseError("Invalid role `$atts[role]' for file $file");
- }
- $save_destdir = $dest_dir;
- if (!empty($atts['baseinstalldir'])) {
- $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
- }
- if (dirname($file) != '.' && empty($atts['install-as'])) {
- $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
- }
- if (empty($atts['install-as'])) {
- $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
- } else {
- $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
- }
- $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
- // Clean up the DIRECTORY_SEPARATOR mess
- $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
- list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
- array(DIRECTORY_SEPARATOR,
- DIRECTORY_SEPARATOR,
- DIRECTORY_SEPARATOR),
- array($dest_file, $orig_file));
- $final_dest_file = $installed_as = $dest_file;
- if (isset($this->_options['packagingroot'])) {
- $installedas_dest_dir = dirname($final_dest_file);
- $installedas_dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
- $final_dest_file = $this->_prependPath($final_dest_file, $this->_options['packagingroot']);
- } else {
- $installedas_dest_dir = dirname($final_dest_file);
- $installedas_dest_file = $installedas_dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
- }
- $dest_dir = dirname($final_dest_file);
- $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
- if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
- return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
- }
- // }}}
- if (empty($this->_options['register-only']) &&
- (!file_exists($dest_dir) || !is_dir($dest_dir))) {
- if (!$this->mkDirHier($dest_dir)) {
- return $this->raiseError("failed to mkdir $dest_dir",
- PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ mkdir $dest_dir");
- }
- // pretty much nothing happens if we are only registering the install
- if (empty($this->_options['register-only'])) {
- if (empty($atts['replacements'])) {
- if (!file_exists($orig_file)) {
- return $this->raiseError("file $orig_file does not exist",
- PEAR_INSTALLER_FAILED);
- }
- if (!@copy($orig_file, $dest_file)) {
- return $this->raiseError("failed to write $dest_file: $php_errormsg",
- PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ cp $orig_file $dest_file");
- if (isset($atts['md5sum'])) {
- $md5sum = md5_file($dest_file);
- }
- } else {
- // {{{ file with replacements
- if (!file_exists($orig_file)) {
- return $this->raiseError("file does not exist",
- PEAR_INSTALLER_FAILED);
- }
- $contents = file_get_contents($orig_file);
- if ($contents === false) {
- $contents = '';
- }
- if (isset($atts['md5sum'])) {
- $md5sum = md5($contents);
- }
- $subst_from = $subst_to = array();
- foreach ($atts['replacements'] as $a) {
- $to = '';
- if ($a['type'] == 'php-const') {
- if (preg_match('/^[a-z0-9_]+\\z/i', $a['to'])) {
- eval("\$to = $a[to];");
- } else {
- if (!isset($options['soft'])) {
- $this->log(0, "invalid php-const replacement: $a[to]");
- }
- continue;
- }
- } elseif ($a['type'] == 'pear-config') {
- if ($a['to'] == 'master_server') {
- $chan = $this->_registry->getChannel($channel);
- if (!PEAR::isError($chan)) {
- $to = $chan->getServer();
- } else {
- $to = $this->config->get($a['to'], null, $channel);
- }
- } else {
- $to = $this->config->get($a['to'], null, $channel);
- }
- if (is_null($to)) {
- if (!isset($options['soft'])) {
- $this->log(0, "invalid pear-config replacement: $a[to]");
- }
- continue;
- }
- } elseif ($a['type'] == 'package-info') {
- if ($t = $this->pkginfo->packageInfo($a['to'])) {
- $to = $t;
- } else {
- if (!isset($options['soft'])) {
- $this->log(0, "invalid package-info replacement: $a[to]");
- }
- continue;
- }
- }
- if (!is_null($to)) {
- $subst_from[] = $a['from'];
- $subst_to[] = $to;
- }
- }
- $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
- if (sizeof($subst_from)) {
- $contents = str_replace($subst_from, $subst_to, $contents);
- }
- $wp = @fopen($dest_file, "wb");
- if (!is_resource($wp)) {
- return $this->raiseError("failed to create $dest_file: $php_errormsg",
- PEAR_INSTALLER_FAILED);
- }
- if (@fwrite($wp, $contents) === false) {
- return $this->raiseError("failed writing to $dest_file: $php_errormsg",
- PEAR_INSTALLER_FAILED);
- }
- fclose($wp);
- // }}}
- }
- // {{{ check the md5
- if (isset($md5sum)) {
- if (strtolower($md5sum) === strtolower($atts['md5sum'])) {
- $this->log(2, "md5sum ok: $final_dest_file");
- } else {
- if (empty($options['force'])) {
- // delete the file
- if (file_exists($dest_file)) {
- unlink($dest_file);
- }
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError("bad md5sum for file $final_dest_file",
- PEAR_INSTALLER_FAILED);
- }
- if (!isset($options['soft'])) {
- $this->log(0, "warning : bad md5sum for file $final_dest_file");
- }
- } else {
- if (!isset($options['soft'])) {
- $this->log(0, "warning : bad md5sum for file $final_dest_file");
- }
- }
- }
- }
- // }}}
- // {{{ set file permissions
- if (!OS_WINDOWS) {
- if ($atts['role'] == 'script') {
- $mode = 0777 & ~(int)octdec($this->config->get('umask'));
- $this->log(3, "+ chmod +x $dest_file");
- } else {
- $mode = 0666 & ~(int)octdec($this->config->get('umask'));
- }
- if ($atts['role'] != 'src') {
- $this->addFileOperation("chmod", array($mode, $dest_file));
- if (!@chmod($dest_file, $mode)) {
- if (!isset($options['soft'])) {
- $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
- }
- }
- }
- }
- // }}}
- if ($atts['role'] == 'src') {
- rename($dest_file, $final_dest_file);
- $this->log(2, "renamed source file $dest_file to $final_dest_file");
- } else {
- $this->addFileOperation("rename", array($dest_file, $final_dest_file,
- $atts['role'] == 'ext'));
- }
- }
- // Store the full path where the file was installed for easy unistall
- if ($atts['role'] != 'script') {
- $loc = $this->config->get($atts['role'] . '_dir');
- } else {
- $loc = $this->config->get('bin_dir');
- }
- if ($atts['role'] != 'src') {
- $this->addFileOperation("installed_as", array($file, $installed_as,
- $loc,
- dirname(substr($installedas_dest_file, strlen($loc)))));
- }
- //$this->log(2, "installed: $dest_file");
- return PEAR_INSTALLER_OK;
- }
- // }}}
- // {{{ _installFile2()
- /**
- * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
- * @param string filename
- * @param array attributes from <file> tag in package.xml
- * @param string path to install the file in
- * @param array options from command-line
- * @access private
- */
- function _installFile2(&$pkg, $file, &$real_atts, $tmp_path, $options)
- {
- $atts = $real_atts;
- if (!isset($this->_registry)) {
- $this->_registry = &$this->config->getRegistry();
- }
- $channel = $pkg->getChannel();
- // {{{ assemble the destination paths
- if (!in_array($atts['attribs']['role'],
- PEAR_Installer_Role::getValidRoles($pkg->getPackageType()))) {
- return $this->raiseError('Invalid role `' . $atts['attribs']['role'] .
- "' for file $file");
- }
- $role = &PEAR_Installer_Role::factory($pkg, $atts['attribs']['role'], $this->config);
- $err = $role->setup($this, $pkg, $atts['attribs'], $file);
- if (PEAR::isError($err)) {
- return $err;
- }
- if (!$role->isInstallable()) {
- return;
- }
- $info = $role->processInstallation($pkg, $atts['attribs'], $file, $tmp_path);
- if (PEAR::isError($info)) {
- return $info;
- }
- list($save_destdir, $dest_dir, $dest_file, $orig_file) = $info;
- if (preg_match('~/\.\.(/|\\z)|^\.\./~', str_replace('\\', '/', $dest_file))) {
- return $this->raiseError("SECURITY ERROR: file $file (installed to $dest_file) contains parent directory reference ..", PEAR_INSTALLER_FAILED);
- }
- $final_dest_file = $installed_as = $dest_file;
- if (isset($this->_options['packagingroot'])) {
- $final_dest_file = $this->_prependPath($final_dest_file,
- $this->_options['packagingroot']);
- }
- $dest_dir = dirname($final_dest_file);
- $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
- // }}}
- if (empty($this->_options['register-only'])) {
- if (!file_exists($dest_dir) || !is_dir($dest_dir)) {
- if (!$this->mkDirHier($dest_dir)) {
- return $this->raiseError("failed to mkdir $dest_dir",
- PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ mkdir $dest_dir");
- }
- }
- $attribs = $atts['attribs'];
- unset($atts['attribs']);
- // pretty much nothing happens if we are only registering the install
- if (empty($this->_options['register-only'])) {
- if (!count($atts)) { // no tasks
- if (!file_exists($orig_file)) {
- return $this->raiseError("file $orig_file does not exist",
- PEAR_INSTALLER_FAILED);
- }
- if (!@copy($orig_file, $dest_file)) {
- return $this->raiseError("failed to write $dest_file: $php_errormsg",
- PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ cp $orig_file $dest_file");
- if (isset($attribs['md5sum'])) {
- $md5sum = md5_file($dest_file);
- }
- } else { // file with tasks
- if (!file_exists($orig_file)) {
- return $this->raiseError("file $orig_file does not exist",
- PEAR_INSTALLER_FAILED);
- }
- $contents = file_get_contents($orig_file);
- if ($contents === false) {
- $contents = '';
- }
- if (isset($attribs['md5sum'])) {
- $md5sum = md5($contents);
- }
- foreach ($atts as $tag => $raw) {
- $tag = str_replace(array($pkg->getTasksNs() . ':', '-'), array('', '_'), $tag);
- $task = "PEAR_Task_$tag";
- $task = &new $task($this->config, $this, PEAR_TASK_INSTALL);
- if (!$task->isScript()) { // scripts are only handled after installation
- $task->init($raw, $attribs, $pkg->getLastInstalledVersion());
- $res = $task->startSession($pkg, $contents, $final_dest_file);
- if ($res === false) {
- continue; // skip this file
- }
- if (PEAR::isError($res)) {
- return $res;
- }
- $contents = $res; // save changes
- }
- $wp = @fopen($dest_file, "wb");
- if (!is_resource($wp)) {
- return $this->raiseError("failed to create $dest_file: $php_errormsg",
- PEAR_INSTALLER_FAILED);
- }
- if (fwrite($wp, $contents) === false) {
- return $this->raiseError("failed writing to $dest_file: $php_errormsg",
- PEAR_INSTALLER_FAILED);
- }
- fclose($wp);
- }
- }
- // {{{ check the md5
- if (isset($md5sum)) {
- // Make sure the original md5 sum matches with expected
- if (strtolower($md5sum) === strtolower($attribs['md5sum'])) {
- $this->log(2, "md5sum ok: $final_dest_file");
- if (isset($contents)) {
- // set md5 sum based on $content in case any tasks were run.
- $real_atts['attribs']['md5sum'] = md5($contents);
- }
- } else {
- if (empty($options['force'])) {
- // delete the file
- if (file_exists($dest_file)) {
- unlink($dest_file);
- }
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError("bad md5sum for file $final_dest_file",
- PEAR_INSTALLER_FAILED);
- }
- if (!isset($options['soft'])) {
- $this->log(0, "warning : bad md5sum for file $final_dest_file");
- }
- } else {
- if (!isset($options['soft'])) {
- $this->log(0, "warning : bad md5sum for file $final_dest_file");
- }
- }
- }
- } else {
- $real_atts['attribs']['md5sum'] = md5_file($dest_file);
- }
- // }}}
- // {{{ set file permissions
- if (!OS_WINDOWS) {
- if ($role->isExecutable()) {
- $mode = 0777 & ~(int)octdec($this->config->get('umask'));
- $this->log(3, "+ chmod +x $dest_file");
- } else {
- $mode = 0666 & ~(int)octdec($this->config->get('umask'));
- }
- if ($attribs['role'] != 'src') {
- $this->addFileOperation("chmod", array($mode, $dest_file));
- if (!@chmod($dest_file, $mode)) {
- if (!isset($options['soft'])) {
- $this->log(0, "failed to change mode of $dest_file: $php_errormsg");
- }
- }
- }
- }
- // }}}
- if ($attribs['role'] == 'src') {
- rename($dest_file, $final_dest_file);
- $this->log(2, "renamed source file $dest_file to $final_dest_file");
- } else {
- $this->addFileOperation("rename", array($dest_file, $final_dest_file, $role->isExtension()));
- }
- }
- // Store the full path where the file was installed for easy uninstall
- if ($attribs['role'] != 'src') {
- $loc = $this->config->get($role->getLocationConfig(), null, $channel);
- $this->addFileOperation('installed_as', array($file, $installed_as,
- $loc,
- dirname(substr($installed_as, strlen($loc)))));
- }
- //$this->log(2, "installed: $dest_file");
- return PEAR_INSTALLER_OK;
- }
- // }}}
- // {{{ addFileOperation()
- /**
- * Add a file operation to the current file transaction.
- *
- * @see startFileTransaction()
- * @param string $type This can be one of:
- * - rename: rename a file ($data has 3 values)
- * - backup: backup an existing file ($data has 1 value)
- * - removebackup: clean up backups created during install ($data has 1 value)
- * - chmod: change permissions on a file ($data has 2 values)
- * - delete: delete a file ($data has 1 value)
- * - rmdir: delete a directory if empty ($data has 1 value)
- * - installed_as: mark a file as installed ($data has 4 values).
- * @param array $data For all file operations, this array must contain the
- * full path to the file or directory that is being operated on. For
- * the rename command, the first parameter must be the file to rename,
- * the second its new name, the third whether this is a PHP extension.
- *
- * The installed_as operation contains 4 elements in this order:
- * 1. Filename as listed in the filelist element from package.xml
- * 2. Full path to the installed file
- * 3. Full path from the php_dir configuration variable used in this
- * installation
- * 4. Relative path from the php_dir that this file is installed in
- */
- function addFileOperation($type, $data)
- {
- if (!is_array($data)) {
- return $this->raiseError('Internal Error: $data in addFileOperation'
- . ' must be an array, was ' . gettype($data));
- }
- if ($type == 'chmod') {
- $octmode = decoct($data[0]);
- $this->log(3, "adding to transaction: $type $octmode $data[1]");
- } else {
- $this->log(3, "adding to transaction: $type " . implode(" ", $data));
- }
- $this->file_operations[] = array($type, $data);
- }
- // }}}
- // {{{ startFileTransaction()
- function startFileTransaction($rollback_in_case = false)
- {
- if (count($this->file_operations) && $rollback_in_case) {
- $this->rollbackFileTransaction();
- }
- $this->file_operations = array();
- }
- // }}}
- // {{{ commitFileTransaction()
- function commitFileTransaction()
- {
- // {{{ first, check permissions and such manually
- $errors = array();
- foreach ($this->file_operations as $key => $tr) {
- list($type, $data) = $tr;
- switch ($type) {
- case 'rename':
- if (!file_exists($data[0])) {
- $errors[] = "cannot rename file $data[0], doesn't exist";
- }
- // check that dest dir. is writable
- if (!is_writable(dirname($data[1]))) {
- $errors[] = "permission denied ($type): $data[1]";
- }
- break;
- case 'chmod':
- // check that file is writable
- if (!is_writable($data[1])) {
- $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
- }
- break;
- case 'delete':
- if (!file_exists($data[0])) {
- $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
- }
- // check that directory is writable
- if (file_exists($data[0])) {
- if (!is_writable(dirname($data[0]))) {
- $errors[] = "permission denied ($type): $data[0]";
- } else {
- // make sure the file to be deleted can be opened for writing
- $fp = false;
- if (!is_dir($data[0]) &&
- (!is_writable($data[0]) || !($fp = @fopen($data[0], 'a')))) {
- $errors[] = "permission denied ($type): $data[0]";
- } elseif ($fp) {
- fclose($fp);
- }
- }
- /* Verify we are not deleting a file owned by another package
- * This can happen when a file moves from package A to B in
- * an upgrade ala http://pear.php.net/17986
- */
- $info = array(
- 'package' => strtolower($this->pkginfo->getName()),
- 'channel' => strtolower($this->pkginfo->getChannel()),
- );
- $result = $this->_registry->checkFileMap($data[0], $info, '1.1');
- if (is_array($result)) {
- $res = array_diff($result, $info);
- if (!empty($res)) {
- $new = $this->_registry->getPackage($result[1], $result[0]);
- $this->file_operations[$key] = false;
- $this->log(3, "file $data[0] was scheduled for removal from {$this->pkginfo->getName()} but is owned by {$new->getChannel()}/{$new->getName()}, removal has been cancelled.");
- }
- }
- }
- break;
- }
- }
- // }}}
- $n = count($this->file_operations);
- $this->log(2, "about to commit $n file operations for " . $this->pkginfo->getName());
- $m = count($errors);
- if ($m > 0) {
- foreach ($errors as $error) {
- if (!isset($this->_options['soft'])) {
- $this->log(1, $error);
- }
- }
- if (!isset($this->_options['ignore-errors'])) {
- return false;
- }
- }
- $this->_dirtree = array();
- // {{{ really commit the transaction
- foreach ($this->file_operations as $i => $tr) {
- if (!$tr) {
- // support removal of non-existing backups
- continue;
- }
- list($type, $data) = $tr;
- switch ($type) {
- case 'backup':
- if (!file_exists($data[0])) {
- $this->file_operations[$i] = false;
- break;
- }
- if (!@copy($data[0], $data[0] . '.bak')) {
- $this->log(1, 'Could not copy ' . $data[0] . ' to ' . $data[0] .
- '.bak ' . $php_errormsg);
- return false;
- }
- $this->log(3, "+ backup $data[0] to $data[0].bak");
- break;
- case 'removebackup':
- if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
- unlink($data[0] . '.bak');
- $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
- }
- break;
- case 'rename':
- $test = file_exists($data[1]) ? @unlink($data[1]) : null;
- if (!$test && file_exists($data[1])) {
- if ($data[2]) {
- $extra = ', this extension must be installed manually. Rename to "' .
- basename($data[1]) . '"';
- } else {
- $extra = '';
- }
- if (!isset($this->_options['soft'])) {
- $this->log(1, 'Could not delete ' . $data[1] . ', cannot rename ' .
- $data[0] . $extra);
- }
- if (!isset($this->_options['ignore-errors'])) {
- return false;
- }
- }
- // permissions issues with rename - copy() is far superior
- $perms = @fileperms($data[0]);
- if (!@copy($data[0], $data[1])) {
- $this->log(1, 'Could not rename ' . $data[0] . ' to ' . $data[1] .
- ' ' . $php_errormsg);
- return false;
- }
- // copy over permissions, otherwise they are lost
- @chmod($data[1], $perms);
- @unlink($data[0]);
- $this->log(3, "+ mv $data[0] $data[1]");
- break;
- case 'chmod':
- if (!@chmod($data[1], $data[0])) {
- $this->log(1, 'Could not chmod ' . $data[1] . ' to ' .
- decoct($data[0]) . ' ' . $php_errormsg);
- return false;
- }
- $octmode = decoct($data[0]);
- $this->log(3, "+ chmod $octmode $data[1]");
- break;
- case 'delete':
- if (file_exists($data[0])) {
- if (!@unlink($data[0])) {
- $this->log(1, 'Could not delete ' . $data[0] . ' ' .
- $php_errormsg);
- return false;
- }
- $this->log(3, "+ rm $data[0]");
- }
- break;
- case 'rmdir':
- if (file_exists($data[0])) {
- do {
- $testme = opendir($data[0]);
- while (false !== ($entry = readdir($testme))) {
- if ($entry == '.' || $entry == '..') {
- continue;
- }
- closedir($testme);
- break 2; // this directory is not empty and can't be
- // deleted
- }
- closedir($testme);
- if (!@rmdir($data[0])) {
- $this->log(1, 'Could not rmdir ' . $data[0] . ' ' .
- $php_errormsg);
- return false;
- }
- $this->log(3, "+ rmdir $data[0]");
- } while (false);
- }
- break;
- case 'installed_as':
- $this->pkginfo->setInstalledAs($data[0], $data[1]);
- if (!isset($this->_dirtree[dirname($data[1])])) {
- $this->_dirtree[dirname($data[1])] = true;
- $this->pkginfo->setDirtree(dirname($data[1]));
- while(!empty($data[3]) && dirname($data[3]) != $data[3] &&
- $data[3] != '/' && $data[3] != '\\') {
- $this->pkginfo->setDirtree($pp =
- $this->_prependPath($data[3], $data[2]));
- $this->_dirtree[$pp] = true;
- $data[3] = dirname($data[3]);
- }
- }
- break;
- }
- }
- // }}}
- $this->log(2, "successfully committed $n file operations");
- $this->file_operations = array();
- return true;
- }
- // }}}
- // {{{ rollbackFileTransaction()
- function rollbackFileTransaction()
- {
- $n = count($this->file_operations);
- $this->log(2, "rolling back $n file operations");
- foreach ($this->file_operations as $tr) {
- list($type, $data) = $tr;
- switch ($type) {
- case 'backup':
- if (file_exists($data[0] . '.bak')) {
- if (file_exists($data[0] && is_writable($data[0]))) {
- unlink($data[0]);
- }
- @copy($data[0] . '.bak', $data[0]);
- $this->log(3, "+ restore $data[0] from $data[0].bak");
- }
- break;
- case 'removebackup':
- if (file_exists($data[0] . '.bak') && is_writable($data[0] . '.bak')) {
- unlink($data[0] . '.bak');
- $this->log(3, "+ rm backup of $data[0] ($data[0].bak)");
- }
- break;
- case 'rename':
- @unlink($data[0]);
- $this->log(3, "+ rm $data[0]");
- break;
- case 'mkdir':
- @rmdir($data[0]);
- $this->log(3, "+ rmdir $data[0]");
- break;
- case 'chmod':
- break;
- case 'delete':
- break;
- case 'installed_as':
- $this->pkginfo->setInstalledAs($data[0], false);
- break;
- }
- }
- $this->pkginfo->resetDirtree();
- $this->file_operations = array();
- }
- // }}}
- // {{{ mkDirHier($dir)
- function mkDirHier($dir)
- {
- $this->addFileOperation('mkdir', array($dir));
- return parent::mkDirHier($dir);
- }
- // }}}
- // {{{ download()
- /**
- * Download any files and their dependencies, if necessary
- *
- * @param array a mixed list of package names, local files, or package.xml
- * @param PEAR_Config
- * @param array options from the command line
- * @param array this is the array that will be populated with packages to
- * install. Format of each entry:
- *
- * <code>
- * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
- * 'info' => array() // parsed package.xml
- * );
- * </code>
- * @param array this will be populated with any error messages
- * @param false private recursion variable
- * @param false private recursion variable
- * @param false private recursion variable
- * @deprecated in favor of PEAR_Downloader
- */
- function download($packages, $options, &$config, &$installpackages,
- &$errors, $installed = false, $willinstall = false, $state = false)
- {
- // trickiness: initialize here
- parent::PEAR_Downloader($this->ui, $options, $config);
- $ret = parent::download($packages);
- $errors = $this->getErrorMsgs();
- $installpackages = $this->getDownloadedPackages();
- trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
- "in favor of PEAR_Downloader class", E_USER_WARNING);
- return $ret;
- }
- // }}}
- // {{{ _parsePackageXml()
- function _parsePackageXml(&$descfile)
- {
- // Parse xml file -----------------------------------------------
- $pkg = new PEAR_PackageFile($this->config, $this->debug);
- PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
- $p = &$pkg->fromAnyFile($descfile, PEAR_VALIDATE_INSTALLING);
- PEAR::staticPopErrorHandling();
- if (PEAR::isError($p)) {
- if (is_array($p->getUserInfo())) {
- foreach ($p->getUserInfo() as $err) {
- $loglevel = $err['level'] == 'error' ? 0 : 1;
- if (!isset($this->_options['soft'])) {
- $this->log($loglevel, ucfirst($err['level']) . ': ' . $err['message']);
- }
- }
- }
- return $this->raiseError('Installation failed: invalid package file');
- }
- $descfile = $p->getPackageFile();
- return $p;
- }
- // }}}
- /**
- * Set the list of PEAR_Downloader_Package objects to allow more sane
- * dependency validation
- * @param array
- */
- function setDownloadedPackages(&$pkgs)
- {
- PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
- $err = $this->analyzeDependencies($pkgs);
- PEAR::popErrorHandling();
- if (PEAR::isError($err)) {
- return $err;
- }
- $this->_downloadedPackages = &$pkgs;
- }
- /**
- * Set the list of PEAR_Downloader_Package objects to allow more sane
- * dependency validation
- * @param array
- */
- function setUninstallPackages(&$pkgs)
- {
- $this->_downloadedPackages = &$pkgs;
- }
- function getInstallPackages()
- {
- return $this->_downloadedPackages;
- }
- // {{{ install()
- /**
- * Installs the files within the package file specified.
- *
- * @param string|PEAR_Downloader_Package $pkgfile path to the package file,
- * or a pre-initialized packagefile object
- * @param array $options
- * recognized options:
- * - installroot : optional prefix directory for installation
- * - force : force installation
- * - register-only : update registry but don't install files
- * - upgrade : upgrade existing install
- * - soft : fail silently
- * - nodeps : ignore dependency conflicts/missing dependencies
- * - alldeps : install all dependencies
- * - onlyreqdeps : install only required dependencies
- *
- * @return array|PEAR_Error package info if successful
- */
- function install($pkgfile, $options = array())
- {
- $this->_options = $options;
- $this->_registry = &$this->config->getRegistry();
- if (is_object($pkgfile)) {
- $dlpkg = &$pkgfile;
- $pkg = $pkgfile->getPackageFile();
- $pkgfile = $pkg->getArchiveFile();
- $descfile = $pkg->getPackageFile();
- } else {
- $descfile = $pkgfile;
- $pkg = $this->_parsePackageXml($descfile);
- if (PEAR::isError($pkg)) {
- return $pkg;
- }
- }
- $tmpdir = dirname($descfile);
- if (realpath($descfile) != realpath($pkgfile)) {
- // Use the temp_dir since $descfile can contain the download dir path
- $tmpdir = $this->config->get('temp_dir', null, 'pear.php.net');
- $tmpdir = System::mktemp('-d -t "' . $tmpdir . '"');
- $tar = new Archive_Tar($pkgfile);
- if (!$tar->extract($tmpdir)) {
- return $this->raiseError("unable to unpack $pkgfile");
- }
- }
- $pkgname = $pkg->getName();
- $channel = $pkg->getChannel();
- if (isset($this->_options['packagingroot'])) {
- $regdir = $this->_prependPath(
- $this->config->get('php_dir', null, 'pear.php.net'),
- $this->_options['packagingroot']);
- $packrootphp_dir = $this->_prependPath(
- $this->config->get('php_dir', null, $channel),
- $this->_options['packagingroot']);
- }
- if (isset($options['installroot'])) {
- $this->config->setInstallRoot($options['installroot']);
- $this->_registry = &$this->config->getRegistry();
- $installregistry = &$this->_registry;
- $this->installroot = ''; // all done automagically now
- $php_dir = $this->config->get('php_dir', null, $channel);
- } else {
- $this->config->setInstallRoot(false);
- $this->_registry = &$this->config->getRegistry();
- if (isset($this->_options['packagingroot'])) {
- $installregistry = &new PEAR_Registry($regdir);
- if (!$installregistry->channelExists($channel, true)) {
- // we need to fake a channel-discover of this channel
- $chanobj = $this->_registry->getChannel($channel, true);
- $installregistry->addChannel($chanobj);
- }
- $php_dir = $packrootphp_dir;
- } else {
- $installregistry = &$this->_registry;
- $php_dir = $this->config->get('php_dir', null, $channel);
- }
- $this->installroot = '';
- }
- // {{{ checks to do when not in "force" mode
- if (empty($options['force']) &&
- (file_exists($this->config->get('php_dir')) &&
- is_dir($this->config->get('php_dir')))) {
- $testp = $channel == 'pear.php.net' ? $pkgname : array($channel, $pkgname);
- $instfilelist = $pkg->getInstallationFileList(true);
- if (PEAR::isError($instfilelist)) {
- return $instfilelist;
- }
- // ensure we have the most accurate registry
- $installregistry->flushFileMap();
- $test = $installregistry->checkFileMap($instfilelist, $testp, '1.1');
- if (PEAR::isError($test)) {
- return $test;
- }
- if (sizeof($test)) {
- $pkgs = $this->getInstallPackages();
- $found = false;
- foreach ($pkgs as $param) {
- if ($pkg->isSubpackageOf($param)) {
- $found = true;
- break;
- }
- }
- if ($found) {
- // subpackages can conflict with earlier versions of parent packages
- $parentreg = $installregistry->packageInfo($param->getPackage(), null, $param->getChannel());
- $tmp = $test;
- foreach ($tmp as $file => $info) {
- if (is_array($info)) {
- if (strtolower($info[1]) == strtolower($param->getPackage()) &&
- strtolower($info[0]) == strtolower($param->getChannel())
- ) {
- if (isset($parentreg['filelist'][$file])) {
- unset($parentreg['filelist'][$file]);
- } else{
- $pos = strpos($file, '/');
- $basedir = substr($file, 0, $pos);
- $file2 = substr($file, $pos + 1);
- if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
- && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
- ) {
- unset($parentreg['filelist'][$file2]);
- }
- }
- unset($test[$file]);
- }
- } else {
- if (strtolower($param->getChannel()) != 'pear.php.net') {
- continue;
- }
- if (strtolower($info) == strtolower($param->getPackage())) {
- if (isset($parentreg['filelist'][$file])) {
- unset($parentreg['filelist'][$file]);
- } else{
- $pos = strpos($file, '/');
- $basedir = substr($file, 0, $pos);
- $file2 = substr($file, $pos + 1);
- if (isset($parentreg['filelist'][$file2]['baseinstalldir'])
- && $parentreg['filelist'][$file2]['baseinstalldir'] === $basedir
- ) {
- unset($parentreg['filelist'][$file2]);
- }
- }
- unset($test[$file]);
- }
- }
- }
- $pfk = &new PEAR_PackageFile($this->config);
- $parentpkg = &$pfk->fromArray($parentreg);
- $installregistry->updatePackage2($parentpkg);
- }
- if ($param->getChannel() == 'pecl.php.net' && isset($options['upgrade'])) {
- $tmp = $test;
- foreach ($tmp as $file => $info) {
- if (is_string($info)) {
- // pear.php.net packages are always stored as strings
- if (strtolower($info) == strtolower($param->getPackage())) {
- // upgrading existing package
- unset($test[$file]);
- }
- }
- }
- }
- if (count($test)) {
- $msg = "$channel/$pkgname: conflicting files found:\n";
- $longest = max(array_map("strlen", array_keys($test)));
- $fmt = "%${longest}s (%s)\n";
- foreach ($test as $file => $info) {
- if (!is_array($info)) {
- $info = array('pear.php.net', $info);
- }
- $info = $info[0] . '/' . $info[1];
- $msg .= sprintf($fmt, $file, $info);
- }
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError($msg);
- }
- if (!isset($options['soft'])) {
- $this->log(0, "WARNING: $msg");
- }
- }
- }
- }
- // }}}
- $this->startFileTransaction();
- $usechannel = $channel;
- if ($channel == 'pecl.php.net') {
- $test = $installregistry->packageExists($pkgname, $channel);
- if (!$test) {
- $test = $installregistry->packageExists($pkgname, 'pear.php.net');
- $usechannel = 'pear.php.net';
- }
- } else {
- $test = $installregistry->packageExists($pkgname, $channel);
- }
- if (empty($options['upgrade']) && empty($options['soft'])) {
- // checks to do only when installing new packages
- if (empty($options['force']) && $test) {
- return $this->raiseError("$channel/$pkgname is already installed");
- }
- } else {
- // Upgrade
- if ($test) {
- $v1 = $installregistry->packageInfo($pkgname, 'version', $usechannel);
- $v2 = $pkg->getVersion();
- $cmp = version_compare("$v1", "$v2", 'gt');
- if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
- return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
- }
- }
- }
- // Do cleanups for upgrade and install, remove old release's files first
- if ($test && empty($options['register-only'])) {
- // when upgrading, remove old release's files first:
- if (PEAR::isError($err = $this->_deletePackageFiles($pkgname, $usechannel,
- true))) {
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError($err);
- }
- if (!isset($options['soft'])) {
- $this->log(0, 'WARNING: ' . $err->getMessage());
- }
- } else {
- $backedup = $err;
- }
- }
- // {{{ Copy files to dest dir ---------------------------------------
- // info from the package it self we want to access from _installFile
- $this->pkginfo = &$pkg;
- // used to determine whether we should build any C code
- $this->source_files = 0;
- $savechannel = $this->config->get('default_channel');
- if (empty($options['register-only']) && !is_dir($php_dir)) {
- if (PEAR::isError(System::mkdir(array('-p'), $php_dir))) {
- return $this->raiseError("no installation destination directory '$php_dir'\n");
- }
- }
- if (substr($pkgfile, -4) != '.xml') {
- $tmpdir .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkg->getVersion();
- }
- $this->configSet('default_channel', $channel);
- // {{{ install files
- $ver = $pkg->getPackagexmlVersion();
- if (version_compare($ver, '2.0', '>=')) {
- $filelist = $pkg->getInstallationFilelist();
- } else {
- $filelist = $pkg->getFileList();
- }
- if (PEAR::isError($filelist)) {
- return $filelist;
- }
- $p = &$installregistry->getPackage($pkgname, $channel);
- $dirtree = (empty($options['register-only']) && $p) ? $p->getDirTree() : false;
- $pkg->resetFilelist();
- $pkg->setLastInstalledVersion($installregistry->packageInfo($pkg->getPackage(),
- 'version', $pkg->getChannel()));
- foreach ($filelist as $file => $atts) {
- $this->expectError(PEAR_INSTALLER_FAILED);
- if ($pkg->getPackagexmlVersion() == '1.0') {
- $res = $this->_installFile($file, $atts, $tmpdir, $options);
- } else {
- $res = $this->_installFile2($pkg, $file, $atts, $tmpdir, $options);
- }
- $this->popExpect();
- if (PEAR::isError($res)) {
- if (empty($options['ignore-errors'])) {
- $this->rollbackFileTransaction();
- if ($res->getMessage() == "file does not exist") {
- $this->raiseError("file $file in package.xml does not exist");
- }
- return $this->raiseError($res);
- }
- if (!isset($options['soft'])) {
- $this->log(0, "Warning: " . $res->getMessage());
- }
- }
- $real = isset($atts['attribs']) ? $atts['attribs'] : $atts;
- if ($res == PEAR_INSTALLER_OK && $real['role'] != 'src') {
- // Register files that were installed
- $pkg->installedFile($file, $atts);
- }
- }
- // }}}
- // {{{ compile and install source files
- if ($this->source_files > 0 && empty($options['nobuild'])) {
- if (PEAR::isError($err =
- $this->_compileSourceFiles($savechannel, $pkg))) {
- return $err;
- }
- }
- // }}}
- if (isset($backedup)) {
- $this->_removeBackups($backedup);
- }
- if (!$this->commitFileTransaction()) {
- $this->rollbackFileTransaction();
- $this->configSet('default_channel', $savechannel);
- return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
- }
- // }}}
- $ret = false;
- $installphase = 'install';
- $oldversion = false;
- // {{{ Register that the package is installed -----------------------
- if (empty($options['upgrade'])) {
- // if 'force' is used, replace the info in registry
- $usechannel = $channel;
- if ($channel == 'pecl.php.net') {
- $test = $installregistry->packageExists($pkgname, $channel);
- if (!$test) {
- $test = $installregistry->packageExists($pkgname, 'pear.php.net');
- $usechannel = 'pear.php.net';
- }
- } else {
- $test = $installregistry->packageExists($pkgname, $channel);
- }
- if (!empty($options['force']) && $test) {
- $oldversion = $installregistry->packageInfo($pkgname, 'version', $usechannel);
- $installregistry->deletePackage($pkgname, $usechannel);
- }
- $ret = $installregistry->addPackage2($pkg);
- } else {
- if ($dirtree) {
- $this->startFileTransaction();
- // attempt to delete empty directories
- uksort($dirtree, array($this, '_sortDirs'));
- foreach($dirtree as $dir => $notused) {
- $this->addFileOperation('rmdir', array($dir));
- }
- $this->commitFileTransaction();
- }
- $usechannel = $channel;
- if ($channel == 'pecl.php.net') {
- $test = $installregistry->packageExists($pkgname, $channel);
- if (!$test) {
- $test = $installregistry->packageExists($pkgname, 'pear.php.net');
- $usechannel = 'pear.php.net';
- }
- } else {
- $test = $installregistry->packageExists($pkgname, $channel);
- }
- // new: upgrade installs a package if it isn't installed
- if (!$test) {
- $ret = $installregistry->addPackage2($pkg);
- } else {
- if ($usechannel != $channel) {
- $installregistry->deletePackage($pkgname, $usechannel);
- $ret = $installregistry->addPackage2($pkg);
- } else {
- $ret = $installregistry->updatePackage2($pkg);
- }
- $installphase = 'upgrade';
- }
- }
- if (!$ret) {
- $this->configSet('default_channel', $savechannel);
- return $this->raiseError("Adding package $channel/$pkgname to registry failed");
- }
- // }}}
- $this->configSet('default_channel', $savechannel);
- if (class_exists('PEAR_Task_Common')) { // this is auto-included if any tasks exist
- if (PEAR_Task_Common::hasPostinstallTasks()) {
- PEAR_Task_Common::runPostinstallTasks($installphase);
- }
- }
- return $pkg->toArray(true);
- }
- // }}}
- // {{{ _compileSourceFiles()
- /**
- * @param string
- * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
- */
- function _compileSourceFiles($savechannel, &$filelist)
- {
- require_once 'PEAR/Builder.php';
- $this->log(1, "$this->source_files source files, building");
- $bob = &new PEAR_Builder($this->ui);
- $bob->debug = $this->debug;
- $built = $bob->build($filelist, array(&$this, '_buildCallback'));
- if (PEAR::isError($built)) {
- $this->rollbackFileTransaction();
- $this->configSet('default_channel', $savechannel);
- return $built;
- }
- $this->log(1, "\nBuild process completed successfully");
- foreach ($built as $ext) {
- $bn = basename($ext['file']);
- list($_ext_name, $_ext_suff) = explode('.', $bn);
- if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
- if (extension_loaded($_ext_name)) {
- $this->raiseError("Extension '$_ext_name' already loaded. " .
- 'Please unload it in your php.ini file ' .
- 'prior to install or upgrade');
- }
- $role = 'ext';
- } else {
- $role = 'src';
- }
- $dest = $ext['dest'];
- $packagingroot = '';
- if (isset($this->_options['packagingroot'])) {
- $packagingroot = $this->_options['packagingroot'];
- }
- $copyto = $this->_prependPath($dest, $packagingroot);
- $extra = $copyto != $dest ? " as '$copyto'" : '';
- $this->log(1, "Installing '$dest'$extra");
- $copydir = dirname($copyto);
- // pretty much nothing happens if we are only registering the install
- if (empty($this->_options['register-only'])) {
- if (!file_exists($copydir) || !is_dir($copydir)) {
- if (!$this->mkDirHier($copydir)) {
- return $this->raiseError("failed to mkdir $copydir",
- PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ mkdir $copydir");
- }
- if (!@copy($ext['file'], $copyto)) {
- return $this->raiseError("failed to write $copyto ($php_errormsg)", PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ cp $ext[file] $copyto");
- $this->addFileOperation('rename', array($ext['file'], $copyto));
- if (!OS_WINDOWS) {
- $mode = 0666 & ~(int)octdec($this->config->get('umask'));
- $this->addFileOperation('chmod', array($mode, $copyto));
- if (!@chmod($copyto, $mode)) {
- $this->log(0, "failed to change mode of $copyto ($php_errormsg)");
- }
- }
- }
- $data = array(
- 'role' => $role,
- 'name' => $bn,
- 'installed_as' => $dest,
- 'php_api' => $ext['php_api'],
- 'zend_mod_api' => $ext['zend_mod_api'],
- 'zend_ext_api' => $ext['zend_ext_api'],
- );
- if ($filelist->getPackageXmlVersion() == '1.0') {
- $filelist->installedFile($bn, $data);
- } else {
- $filelist->installedFile($bn, array('attribs' => $data));
- }
- }
- }
- // }}}
- function &getUninstallPackages()
- {
- return $this->_downloadedPackages;
- }
- // {{{ uninstall()
- /**
- * Uninstall a package
- *
- * This method removes all files installed by the application, and then
- * removes any empty directories.
- * @param string package name
- * @param array Command-line options. Possibilities include:
- *
- * - installroot: base installation dir, if not the default
- * - register-only : update registry but don't remove files
- * - nodeps: do not process dependencies of other packages to ensure
- * uninstallation does not break things
- */
- function uninstall($package, $options = array())
- {
- $installRoot = isset($options['installroot']) ? $options['installroot'] : '';
- $this->config->setInstallRoot($installRoot);
- $this->installroot = '';
- $this->_registry = &$this->config->getRegistry();
- if (is_object($package)) {
- $channel = $package->getChannel();
- $pkg = $package;
- $package = $pkg->getPackage();
- } else {
- $pkg = false;
- $info = $this->_registry->parsePackageName($package,
- $this->config->get('default_channel'));
- $channel = $info['channel'];
- $package = $info['package'];
- }
- $savechannel = $this->config->get('default_channel');
- $this->configSet('default_channel', $channel);
- if (!is_object($pkg)) {
- $pkg = $this->_registry->getPackage($package, $channel);
- }
- if (!$pkg) {
- $this->configSet('default_channel', $savechannel);
- return $this->raiseError($this->_registry->parsedPackageNameToString(
- array(
- 'channel' => $channel,
- 'package' => $package
- ), true) . ' not installed');
- }
- if ($pkg->getInstalledBinary()) {
- // this is just an alias for a binary package
- return $this->_registry->deletePackage($package, $channel);
- }
- $filelist = $pkg->getFilelist();
- PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
- if (!class_exists('PEAR_Dependency2')) {
- require_once 'PEAR/Dependency2.php';
- }
- $depchecker = &new PEAR_Dependency2($this->config, $options,
- array('channel' => $channel, 'package' => $package),
- PEAR_VALIDATE_UNINSTALLING);
- $e = $depchecker->validatePackageUninstall($this);
- PEAR::staticPopErrorHandling();
- if (PEAR::isError($e)) {
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError($e);
- }
- if (!isset($options['soft'])) {
- $this->log(0, 'WARNING: ' . $e->getMessage());
- }
- } elseif (is_array($e)) {
- if (!isset($options['soft'])) {
- $this->log(0, $e[0]);
- }
- }
- $this->pkginfo = &$pkg;
- // pretty much nothing happens if we are only registering the uninstall
- if (empty($options['register-only'])) {
- // {{{ Delete the files
- $this->startFileTransaction();
- PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
- if (PEAR::isError($err = $this->_deletePackageFiles($package, $channel))) {
- PEAR::popErrorHandling();
- $this->rollbackFileTransaction();
- $this->configSet('default_channel', $savechannel);
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError($err);
- }
- if (!isset($options['soft'])) {
- $this->log(0, 'WARNING: ' . $err->getMessage());
- }
- } else {
- PEAR::popErrorHandling();
- }
- if (!$this->commitFileTransaction()) {
- $this->rollbackFileTransaction();
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError("uninstall failed");
- }
- if (!isset($options['soft'])) {
- $this->log(0, 'WARNING: uninstall failed');
- }
- } else {
- $this->startFileTransaction();
- $dirtree = $pkg->getDirTree();
- if ($dirtree === false) {
- $this->configSet('default_channel', $savechannel);
- return $this->_registry->deletePackage($package, $channel);
- }
- // attempt to delete empty directories
- uksort($dirtree, array($this, '_sortDirs'));
- foreach($dirtree as $dir => $notused) {
- $this->addFileOperation('rmdir', array($dir));
- }
- if (!$this->commitFileTransaction()) {
- $this->rollbackFileTransaction();
- if (!isset($options['ignore-errors'])) {
- return $this->raiseError("uninstall failed");
- }
- if (!isset($options['soft'])) {
- $this->log(0, 'WARNING: uninstall failed');
- }
- }
- }
- // }}}
- }
- $this->configSet('default_channel', $savechannel);
- // Register that the package is no longer installed
- return $this->_registry->deletePackage($package, $channel);
- }
- /**
- * Sort a list of arrays of array(downloaded packagefilename) by dependency.
- *
- * It also removes duplicate dependencies
- * @param array an array of PEAR_PackageFile_v[1/2] objects
- * @return array|PEAR_Error array of array(packagefilename, package.xml contents)
- */
- function sortPackagesForUninstall(&$packages)
- {
- $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->config);
- if (PEAR::isError($this->_dependencyDB)) {
- return $this->_dependencyDB;
- }
- usort($packages, array(&$this, '_sortUninstall'));
- }
- function _sortUninstall($a, $b)
- {
- if (!$a->getDeps() && !$b->getDeps()) {
- return 0; // neither package has dependencies, order is insignificant
- }
- if ($a->getDeps() && !$b->getDeps()) {
- return -1; // $a must be installed after $b because $a has dependencies
- }
- if (!$a->getDeps() && $b->getDeps()) {
- return 1; // $b must be installed after $a because $b has dependencies
- }
- // both packages have dependencies
- if ($this->_dependencyDB->dependsOn($a, $b)) {
- return -1;
- }
- if ($this->_dependencyDB->dependsOn($b, $a)) {
- return 1;
- }
- return 0;
- }
- // }}}
- // {{{ _sortDirs()
- function _sortDirs($a, $b)
- {
- if (strnatcmp($a, $b) == -1) return 1;
- if (strnatcmp($a, $b) == 1) return -1;
- return 0;
- }
- // }}}
- // {{{ _buildCallback()
- function _buildCallback($what, $data)
- {
- if (($what == 'cmdoutput' && $this->debug > 1) ||
- ($what == 'output' && $this->debug > 0)) {
- $this->ui->outputData(rtrim($data), 'build');
- }
- }
- // }}}
- }
|