1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069 |
- <?php
- //
- // +----------------------------------------------------------------------+
- // | PHP Version 5 |
- // +----------------------------------------------------------------------+
- // | Copyright (c) 1997-2004 The PHP Group |
- // +----------------------------------------------------------------------+
- // | This source file is subject to version 3.0 of the PHP license, |
- // | that is bundled with this package in the file LICENSE, and is |
- // | available through the world-wide-web at the following url: |
- // | http://www.php.net/license/3_0.txt. |
- // | If you did not receive a copy of the PHP license and are unable to |
- // | obtain it through the world-wide-web, please send a note to |
- // | [email protected] so we can mail you a copy immediately. |
- // +----------------------------------------------------------------------+
- // | Authors: Stig Bakken <[email protected]> |
- // | Tomas V.V.Cox <[email protected]> |
- // | Martin Jansen <[email protected]> |
- // +----------------------------------------------------------------------+
- //
- // $Id: Installer.php,v 1.150.2.2 2005/02/17 17:47:55 cellog Exp $
- require_once 'PEAR/Downloader.php';
- /**
- * Administration class used to install PEAR packages and maintain the
- * installed package database.
- *
- * TODO:
- * - Check dependencies break on package uninstall (when no force given)
- * - add a guessInstallDest() method with the code from _installFile() and
- * use that method in Registry::_rebuildFileMap() & Command_Registry::doList(),
- * others..
- *
- * @since PHP 4.0.2
- * @author Stig Bakken <[email protected]>
- * @author Martin Jansen <[email protected]>
- * @author Greg Beaver <[email protected]>
- */
- 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 object
- */
- var $registry;
- /** 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');
- //$this->registry = &new PEAR_Registry($this->config->get('php_dir'));
- }
- // }}}
- // {{{ _deletePackageFiles()
- /**
- * Delete a package's installed files, does not remove empty directories.
- *
- * @param string $package package name
- *
- * @return bool TRUE on success, or a PEAR error on failure
- *
- * @access private
- */
- function _deletePackageFiles($package)
- {
- if (!strlen($package)) {
- return $this->raiseError("No package to uninstall given");
- }
- $filelist = $this->registry->packageInfo($package, 'filelist');
- if ($filelist == null) {
- return $this->raiseError("$package not installed");
- }
- foreach ($filelist as $file => $props) {
- if (empty($props['installed_as'])) {
- continue;
- }
- $path = $this->_prependPath($props['installed_as'], $this->installroot);
- $this->addFileOperation('delete', array($path));
- }
- 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($atts['platform'])) {
- if (empty($os)) {
- include_once "OS/Guess.php";
- $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;
- }
- }
- // }}}
- // {{{ assemble the destination paths
- switch ($atts['role']) {
- case 'doc':
- case 'data':
- case 'test':
- $dest_dir = $this->config->get($atts['role'] . '_dir') .
- DIRECTORY_SEPARATOR . $this->pkginfo['package'];
- unset($atts['baseinstalldir']);
- break;
- case 'ext':
- case 'php':
- $dest_dir = $this->config->get($atts['role'] . '_dir');
- break;
- case 'script':
- $dest_dir = $this->config->get('bin_dir');
- break;
- case 'src':
- case 'extsrc':
- $this->source_files++;
- return;
- 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+!"),
- DIRECTORY_SEPARATOR,
- array($dest_file, $orig_file));
- $installed_as = $dest_file;
- $final_dest_file = $this->_prependPath($dest_file, $this->installroot);
- $dest_dir = dirname($final_dest_file);
- $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
- // }}}
- if (!@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");
- }
- if (empty($atts['replacements'])) {
- if (!file_exists($orig_file)) {
- return $this->raiseError("file does not exist",
- PEAR_INSTALLER_FAILED);
- }
- if (!@copy($orig_file, $dest_file)) {
- return $this->raiseError("failed to write $dest_file",
- 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);
- }
- $fp = fopen($orig_file, "r");
- $contents = fread($fp, filesize($orig_file));
- fclose($fp);
- 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_]+$/i', $a['to'])) {
- eval("\$to = $a[to];");
- } else {
- $this->log(0, "invalid php-const replacement: $a[to]");
- continue;
- }
- } elseif ($a['type'] == 'pear-config') {
- $to = $this->config->get($a['to']);
- if (is_null($to)) {
- $this->log(0, "invalid pear-config replacement: $a[to]");
- continue;
- }
- } elseif ($a['type'] == 'package-info') {
- if (isset($this->pkginfo[$a['to']]) && is_string($this->pkginfo[$a['to']])) {
- $to = $this->pkginfo[$a['to']];
- } else {
- $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)) {
- 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
- @unlink($dest_file);
- return $this->raiseError("bad md5sum for file $final_dest_file",
- PEAR_INSTALLER_FAILED);
- } else {
- $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'));
- }
- $this->addFileOperation("chmod", array($mode, $dest_file));
- if (!@chmod($dest_file, $mode)) {
- $this->log(0, "failed to change mode of $dest_file");
- }
- }
- // }}}
- $this->addFileOperation("rename", array($dest_file, $final_dest_file));
- // Store the full path where the file was installed for easy unistall
- $this->addFileOperation("installed_as", array($file, $installed_as,
- $save_destdir, dirname(substr($dest_file, strlen($save_destdir)))));
- //$this->log(2, "installed: $dest_file");
- return PEAR_INSTALLER_OK;
- }
- // }}}
- // {{{ addFileOperation()
- /**
- * Add a file operation to the current file transaction.
- *
- * @see startFileTransaction()
- * @var string $type This can be one of:
- * - rename: rename a file ($data has 2 values)
- * - 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).
- * @var 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 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()
- {
- $n = count($this->file_operations);
- $this->log(2, "about to commit $n file operations");
- // {{{ first, check permissions and such manually
- $errors = array();
- foreach ($this->file_operations as $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]) && !is_writable(dirname($data[0]))) {
- $errors[] = "permission denied ($type): $data[0]";
- }
- break;
- }
- }
- // }}}
- $m = sizeof($errors);
- if ($m > 0) {
- foreach ($errors as $error) {
- $this->log(1, $error);
- }
- return false;
- }
- // {{{ really commit the transaction
- foreach ($this->file_operations as $tr) {
- list($type, $data) = $tr;
- switch ($type) {
- case 'rename':
- @unlink($data[1]);
- @rename($data[0], $data[1]);
- $this->log(3, "+ mv $data[0] $data[1]");
- break;
- case 'chmod':
- @chmod($data[1], $data[0]);
- $octmode = decoct($data[0]);
- $this->log(3, "+ chmod $octmode $data[1]");
- break;
- case 'delete':
- @unlink($data[0]);
- $this->log(3, "+ rm $data[0]");
- break;
- case 'rmdir':
- @rmdir($data[0]);
- $this->log(3, "+ rmdir $data[0]");
- break;
- case 'installed_as':
- $this->pkginfo['filelist'][$data[0]]['installed_as'] = $data[1];
- if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
- $this->pkginfo['filelist']['dirtree'][dirname($data[1])] = true;
- while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
- && $data[3] != '.') {
- $this->pkginfo['filelist']['dirtree']
- [$this->_prependPath($data[3], $data[2])] = 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 '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':
- if (isset($this->pkginfo['filelist'])) {
- unset($this->pkginfo['filelist'][$data[0]]['installed_as']);
- }
- if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
- unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]);
- while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
- && $data[3] != '.') {
- unset($this->pkginfo['filelist']['dirtree']
- [$this->_prependPath($data[3], $data[2])]);
- $data[3] = dirname($data[3]);
- }
- }
- if (isset($this->pkginfo['filelist']['dirtree'])
- && !count($this->pkginfo['filelist']['dirtree'])) {
- unset($this->pkginfo['filelist']['dirtree']);
- }
- break;
- }
- }
- $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;
- }
- // }}}
- // {{{ install()
- /**
- * Installs the files within the package file specified.
- *
- * @param string $pkgfile path to the package file
- * @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())
- {
- $php_dir = $this->config->get('php_dir');
- if (isset($options['installroot'])) {
- if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
- $options['installroot'] = substr($options['installroot'], 0, -1);
- }
- $php_dir = $this->_prependPath($php_dir, $options['installroot']);
- $this->installroot = $options['installroot'];
- } else {
- $this->installroot = '';
- }
- $this->registry = &new PEAR_Registry($php_dir);
- // ==> XXX should be removed later on
- $flag_old_format = false;
- if (substr($pkgfile, -4) == '.xml') {
- $descfile = $pkgfile;
- } else {
- // {{{ Decompress pack in tmp dir -------------------------------------
- // To allow relative package file names
- $pkgfile = realpath($pkgfile);
- if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
- return $tmpdir;
- }
- $this->log(3, '+ tmp dir created at ' . $tmpdir);
- $tar = new Archive_Tar($pkgfile);
- if (!@$tar->extract($tmpdir)) {
- return $this->raiseError("unable to unpack $pkgfile");
- }
- // {{{ Look for existing package file
- $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
- if (!is_file($descfile)) {
- // ----- Look for old package archive format
- // In this format the package.xml file was inside the
- // Package-n.n directory
- $dp = opendir($tmpdir);
- do {
- $pkgdir = readdir($dp);
- } while ($pkgdir{0} == '.');
- $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml';
- $flag_old_format = true;
- $this->log(0, "warning : you are using an archive with an old format");
- }
- // }}}
- // <== XXX This part should be removed later on
- // }}}
- }
- if (!is_file($descfile)) {
- return $this->raiseError("no package.xml file after extracting the archive");
- }
- // Parse xml file -----------------------------------------------
- $pkginfo = $this->infoFromDescriptionFile($descfile);
- if (PEAR::isError($pkginfo)) {
- return $pkginfo;
- }
- $this->validatePackageInfo($pkginfo, $errors, $warnings);
- // XXX We allow warnings, do we have to do it?
- if (count($errors)) {
- if (empty($options['force'])) {
- return $this->raiseError("The following errors where found (use force option to install anyway):\n".
- implode("\n", $errors));
- } else {
- $this->log(0, "warning : the following errors were found:\n".
- implode("\n", $errors));
- }
- }
- $pkgname = $pkginfo['package'];
- // {{{ Check dependencies -------------------------------------------
- if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) {
- $dep_errors = '';
- $error = $this->checkDeps($pkginfo, $dep_errors);
- if ($error == true) {
- if (empty($options['soft'])) {
- $this->log(0, substr($dep_errors, 1));
- }
- return $this->raiseError("$pkgname: Dependencies failed");
- } else if (!empty($dep_errors)) {
- // Print optional dependencies
- if (empty($options['soft'])) {
- $this->log(0, $dep_errors);
- }
- }
- }
- // }}}
- // {{{ checks to do when not in "force" mode
- if (empty($options['force'])) {
- $test = $this->registry->checkFileMap($pkginfo);
- if (sizeof($test)) {
- $tmp = $test;
- foreach ($tmp as $file => $pkg) {
- if ($pkg == $pkgname) {
- unset($test[$file]);
- }
- }
- if (sizeof($test)) {
- $msg = "$pkgname: conflicting files found:\n";
- $longest = max(array_map("strlen", array_keys($test)));
- $fmt = "%${longest}s (%s)\n";
- foreach ($test as $file => $pkg) {
- $msg .= sprintf($fmt, $file, $pkg);
- }
- return $this->raiseError($msg);
- }
- }
- }
- // }}}
- $this->startFileTransaction();
- if (empty($options['upgrade'])) {
- // checks to do only when installing new packages
- if (empty($options['force']) && $this->registry->packageExists($pkgname)) {
- return $this->raiseError("$pkgname already installed");
- }
- } else {
- if ($this->registry->packageExists($pkgname)) {
- $v1 = $this->registry->packageInfo($pkgname, 'version');
- $v2 = $pkginfo['version'];
- $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)");
- }
- if (empty($options['register-only'])) {
- // when upgrading, remove old release's files first:
- if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
- return $this->raiseError($err);
- }
- }
- }
- }
- // {{{ Copy files to dest dir ---------------------------------------
- // info from the package it self we want to access from _installFile
- $this->pkginfo = &$pkginfo;
- // used to determine whether we should build any C code
- $this->source_files = 0;
- if (empty($options['register-only'])) {
- if (!is_dir($php_dir)) {
- return $this->raiseError("no script destination directory\n",
- null, PEAR_ERROR_DIE);
- }
- $tmp_path = dirname($descfile);
- if (substr($pkgfile, -4) != '.xml') {
- $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkginfo['version'];
- }
- // ==> XXX This part should be removed later on
- if ($flag_old_format) {
- $tmp_path = dirname($descfile);
- }
- // <== XXX This part should be removed later on
- // {{{ install files
- foreach ($pkginfo['filelist'] as $file => $atts) {
- $this->expectError(PEAR_INSTALLER_FAILED);
- $res = $this->_installFile($file, $atts, $tmp_path, $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);
- } else {
- $this->log(0, "Warning: " . $res->getMessage());
- }
- }
- if ($res != PEAR_INSTALLER_OK) {
- // Do not register files that were not installed
- unset($pkginfo['filelist'][$file]);
- }
- }
- // }}}
- // {{{ compile and install source files
- if ($this->source_files > 0 && empty($options['nobuild'])) {
- $this->log(1, "$this->source_files source files, building");
- $bob = &new PEAR_Builder($this->ui);
- $bob->debug = $this->debug;
- $built = $bob->build($descfile, array(&$this, '_buildCallback'));
- if (PEAR::isError($built)) {
- $this->rollbackFileTransaction();
- 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'];
- $this->log(1, "Installing '$ext[file]'");
- $copyto = $this->_prependPath($dest, $this->installroot);
- $copydir = dirname($copyto);
- if (!@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", PEAR_INSTALLER_FAILED);
- }
- $this->log(3, "+ cp $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");
- }
- }
- $this->addFileOperation('rename', array($ext['file'], $copyto));
- $pkginfo['filelist'][$bn] = array(
- 'role' => $role,
- 'installed_as' => $dest,
- 'php_api' => $ext['php_api'],
- 'zend_mod_api' => $ext['zend_mod_api'],
- 'zend_ext_api' => $ext['zend_ext_api'],
- );
- }
- }
- // }}}
- }
- if (!$this->commitFileTransaction()) {
- $this->rollbackFileTransaction();
- return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
- }
- // }}}
- $ret = false;
- // {{{ Register that the package is installed -----------------------
- if (empty($options['upgrade'])) {
- // if 'force' is used, replace the info in registry
- if (!empty($options['force']) && $this->registry->packageExists($pkgname)) {
- $this->registry->deletePackage($pkgname);
- }
- $ret = $this->registry->addPackage($pkgname, $pkginfo);
- } else {
- // new: upgrade installs a package if it isn't installed
- if (!$this->registry->packageExists($pkgname)) {
- $ret = $this->registry->addPackage($pkgname, $pkginfo);
- } else {
- $ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
- }
- }
- if (!$ret) {
- return $this->raiseError("Adding package $pkgname to registry failed");
- }
- // }}}
- return $pkginfo;
- }
- // }}}
- // {{{ 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
- * - nodeps: do not process dependencies of other packages to ensure
- * uninstallation does not break things
- */
- function uninstall($package, $options = array())
- {
- $php_dir = $this->config->get('php_dir');
- if (isset($options['installroot'])) {
- if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
- $options['installroot'] = substr($options['installroot'], 0, -1);
- }
- $this->installroot = $options['installroot'];
- $php_dir = $this->_prependPath($php_dir, $this->installroot);
- } else {
- $this->installroot = '';
- }
- $this->registry = &new PEAR_Registry($php_dir);
- $filelist = $this->registry->packageInfo($package, 'filelist');
- if ($filelist == null) {
- return $this->raiseError("$package not installed");
- }
- if (empty($options['nodeps'])) {
- $depchecker = &new PEAR_Dependency($this->registry);
- $error = $depchecker->checkPackageUninstall($errors, $warning, $package);
- if ($error) {
- return $this->raiseError($errors . 'uninstall failed');
- }
- if ($warning) {
- $this->log(0, $warning);
- }
- }
- // {{{ Delete the files
- $this->startFileTransaction();
- if (PEAR::isError($err = $this->_deletePackageFiles($package))) {
- $this->rollbackFileTransaction();
- return $this->raiseError($err);
- }
- if (!$this->commitFileTransaction()) {
- $this->rollbackFileTransaction();
- return $this->raiseError("uninstall failed");
- } else {
- $this->startFileTransaction();
- if (!isset($filelist['dirtree']) || !count($filelist['dirtree'])) {
- return $this->registry->deletePackage($package);
- }
- // attempt to delete empty directories
- uksort($filelist['dirtree'], array($this, '_sortDirs'));
- foreach($filelist['dirtree'] as $dir => $notused) {
- $this->addFileOperation('rmdir', array($dir));
- }
- if (!$this->commitFileTransaction()) {
- $this->rollbackFileTransaction();
- }
- }
- // }}}
- // Register that the package is no longer installed
- return $this->registry->deletePackage($package);
- }
- // }}}
- // {{{ _sortDirs()
- function _sortDirs($a, $b)
- {
- if (strnatcmp($a, $b) == -1) return 1;
- if (strnatcmp($a, $b) == 1) return -1;
- return 0;
- }
- // }}}
- // {{{ checkDeps()
- /**
- * Check if the package meets all dependencies
- *
- * @param array Package information (passed by reference)
- * @param string Error message (passed by reference)
- * @return boolean False when no error occured, otherwise true
- */
- function checkDeps(&$pkginfo, &$errors)
- {
- if (empty($this->registry)) {
- $this->registry = &new PEAR_Registry($this->config->get('php_dir'));
- }
- $depchecker = &new PEAR_Dependency($this->registry);
- $error = $errors = '';
- $failed_deps = $optional_deps = array();
- if (is_array($pkginfo['release_deps'])) {
- foreach($pkginfo['release_deps'] as $dep) {
- $code = $depchecker->callCheckMethod($error, $dep);
- if ($code) {
- if (isset($dep['optional']) && $dep['optional'] == 'yes') {
- $optional_deps[] = array($dep, $code, $error);
- } else {
- $failed_deps[] = array($dep, $code, $error);
- }
- }
- }
- // {{{ failed dependencies
- $n = count($failed_deps);
- if ($n > 0) {
- for ($i = 0; $i < $n; $i++) {
- if (isset($failed_deps[$i]['type'])) {
- $type = $failed_deps[$i]['type'];
- } else {
- $type = 'pkg';
- }
- switch ($failed_deps[$i][1]) {
- case PEAR_DEPENDENCY_MISSING:
- if ($type == 'pkg') {
- // install
- }
- $errors .= "\n" . $failed_deps[$i][2];
- break;
- case PEAR_DEPENDENCY_UPGRADE_MINOR:
- if ($type == 'pkg') {
- // upgrade
- }
- $errors .= "\n" . $failed_deps[$i][2];
- break;
- default:
- $errors .= "\n" . $failed_deps[$i][2];
- break;
- }
- }
- return true;
- }
- // }}}
- // {{{ optional dependencies
- $count_optional = count($optional_deps);
- if ($count_optional > 0) {
- $errors = "Optional dependencies:";
- for ($i = 0; $i < $count_optional; $i++) {
- if (isset($optional_deps[$i]['type'])) {
- $type = $optional_deps[$i]['type'];
- } else {
- $type = 'pkg';
- }
- switch ($optional_deps[$i][1]) {
- case PEAR_DEPENDENCY_MISSING:
- case PEAR_DEPENDENCY_UPGRADE_MINOR:
- default:
- $errors .= "\n" . $optional_deps[$i][2];
- break;
- }
- }
- return false;
- }
- // }}}
- }
- return false;
- }
- // }}}
- // {{{ _buildCallback()
- function _buildCallback($what, $data)
- {
- if (($what == 'cmdoutput' && $this->debug > 1) ||
- ($what == 'output' && $this->debug > 0)) {
- $this->ui->outputData(rtrim($data), 'build');
- }
- }
- // }}}
- }
- // {{{ md5_file() utility function
- if (!function_exists("md5_file")) {
- function md5_file($filename) {
- $fp = fopen($filename, "r");
- if (!$fp) return null;
- $contents = fread($fp, filesize($filename));
- fclose($fp);
- return md5($contents);
- }
- }
- // }}}
- ?>
|