2010-03-10 15:03:40 +03:00
< ? php
2012-05-11 19:57:55 +04:00
/**
* 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 : 24 Z dufuz $
* @ link http :// pear . php . net / package / PEAR
* @ since File available since Release 0.1
*/
2010-03-10 15:03:40 +03:00
2012-05-11 19:57:55 +04:00
/**
* Used for installation groups in package . xml 2.0 and platform exceptions
*/
require_once 'OS/Guess.php' ;
2010-03-10 15:03:40 +03:00
require_once 'PEAR/Downloader.php' ;
2012-05-11 19:57:55 +04:00
define ( 'PEAR_INSTALLER_NOBINARY' , - 240 );
2010-03-10 15:03:40 +03:00
/**
* Administration class used to install PEAR packages and maintain the
* installed package database .
*
2012-05-11 19:57:55 +04:00
* @ 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
2010-03-10 15:03:40 +03:00
*/
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 ;
2012-05-11 19:57:55 +04:00
/**
* PEAR_Registry object used by the installer
* @ var PEAR_Registry
2010-03-10 15:03:40 +03:00
*/
var $registry ;
2012-05-11 19:57:55 +04:00
/**
* array of PEAR_Downloader_Packages
* @ var array
*/
var $_downloadedPackages ;
2010-03-10 15:03:40 +03:00
/** 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' );
2012-05-11 19:57:55 +04:00
}
function setOptions ( $options )
{
$this -> _options = $options ;
}
function setConfig ( & $config )
{
$this -> config = & $config ;
$this -> _registry = & $config -> getRegistry ();
2010-03-10 15:03:40 +03:00
}
// }}}
2012-05-11 19:57:55 +04:00
function _removeBackups ( $files )
{
foreach ( $files as $path ) {
$this -> addFileOperation ( 'removebackup' , array ( $path ));
}
}
2010-03-10 15:03:40 +03:00
// {{{ _deletePackageFiles()
/**
* Delete a package ' s installed files , does not remove empty directories .
*
2012-05-11 19:57:55 +04:00
* @ param string package name
* @ param string channel name
* @ param bool if true , then files are backed up first
2010-03-10 15:03:40 +03:00
* @ return bool TRUE on success , or a PEAR error on failure
2012-05-11 19:57:55 +04:00
* @ access protected
2010-03-10 15:03:40 +03:00
*/
2012-05-11 19:57:55 +04:00
function _deletePackageFiles ( $package , $channel = false , $backup = false )
2010-03-10 15:03:40 +03:00
{
2012-05-11 19:57:55 +04:00
if ( ! $channel ) {
$channel = 'pear.php.net' ;
}
2010-03-10 15:03:40 +03:00
if ( ! strlen ( $package )) {
return $this -> raiseError ( " No package to uninstall given " );
}
2012-05-11 19:57:55 +04:00
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 );
2010-03-10 15:03:40 +03:00
if ( $filelist == null ) {
2012-05-11 19:57:55 +04:00
return $this -> raiseError ( " $channel / $package not installed " );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
$ret = array ();
2010-03-10 15:03:40 +03:00
foreach ( $filelist as $file => $props ) {
if ( empty ( $props [ 'installed_as' ])) {
continue ;
}
2012-05-11 19:57:55 +04:00
$path = $props [ 'installed_as' ];
if ( $backup ) {
$this -> addFileOperation ( 'backup' , array ( $path ));
$ret [] = $path ;
}
2010-03-10 15:03:40 +03:00
$this -> addFileOperation ( 'delete' , array ( $path ));
}
2012-05-11 19:57:55 +04:00
if ( $backup ) {
return $ret ;
}
2010-03-10 15:03:40 +03:00
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 ;
2012-05-11 19:57:55 +04:00
if ( ! isset ( $this -> _registry )) {
$this -> _registry = & $this -> config -> getRegistry ();
}
2010-03-10 15:03:40 +03:00
if ( isset ( $atts [ 'platform' ])) {
if ( empty ( $os )) {
$os = new OS_Guess ();
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
if ( strlen ( $atts [ 'platform' ]) && $atts [ 'platform' ]{ 0 } == '!' ) {
2012-05-11 19:57:55 +04:00
$negate = true ;
2010-03-10 15:03:40 +03:00
$platform = substr ( $atts [ 'platform' ], 1 );
} else {
2012-05-11 19:57:55 +04:00
$negate = false ;
2010-03-10 15:03:40 +03:00
$platform = $atts [ 'platform' ];
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
if (( bool ) $os -> matchSignature ( $platform ) === $negate ) {
$this -> log ( 3 , " skipped $file (meant for $atts[platform] , we are " . $os -> getSignature () . " ) " );
return PEAR_INSTALLER_SKIPPED ;
}
}
// }}}
2012-05-11 19:57:55 +04:00
$channel = $this -> pkginfo -> getChannel ();
2010-03-10 15:03:40 +03:00
// {{{ assemble the destination paths
switch ( $atts [ 'role' ]) {
2012-05-11 19:57:55 +04:00
case 'src' :
case 'extsrc' :
$this -> source_files ++ ;
return ;
2010-03-10 15:03:40 +03:00
case 'doc' :
case 'data' :
case 'test' :
2012-05-11 19:57:55 +04:00
$dest_dir = $this -> config -> get ( $atts [ 'role' ] . '_dir' , null , $channel ) .
DIRECTORY_SEPARATOR . $this -> pkginfo -> getPackage ();
2010-03-10 15:03:40 +03:00
unset ( $atts [ 'baseinstalldir' ]);
break ;
case 'ext' :
case 'php' :
2012-05-11 19:57:55 +04:00
$dest_dir = $this -> config -> get ( $atts [ 'role' ] . '_dir' , null , $channel );
2010-03-10 15:03:40 +03:00
break ;
case 'script' :
2012-05-11 19:57:55 +04:00
$dest_dir = $this -> config -> get ( 'bin_dir' , null , $channel );
2010-03-10 15:03:40 +03:00
break ;
default :
return $this -> raiseError ( " Invalid role ` $atts[role] ' for file $file " );
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
$save_destdir = $dest_dir ;
if ( ! empty ( $atts [ 'baseinstalldir' ])) {
$dest_dir .= DIRECTORY_SEPARATOR . $atts [ 'baseinstalldir' ];
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
if ( dirname ( $file ) != '.' && empty ( $atts [ 'install-as' ])) {
$dest_dir .= DIRECTORY_SEPARATOR . dirname ( $file );
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
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 +! " ),
2012-05-11 19:57:55 +04:00
array ( DIRECTORY_SEPARATOR ,
DIRECTORY_SEPARATOR ,
DIRECTORY_SEPARATOR ),
2010-03-10 15:03:40 +03:00
array ( $dest_file , $orig_file ));
2012-05-11 19:57:55 +04:00
$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 );
2010-03-10 15:03:40 +03:00
$dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename ( $final_dest_file );
2012-05-11 19:57:55 +04:00
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 );
}
2010-03-10 15:03:40 +03:00
// }}}
2012-05-11 19:57:55 +04:00
if ( empty ( $this -> _options [ 'register-only' ]) &&
( ! file_exists ( $dest_dir ) || ! is_dir ( $dest_dir ))) {
2010-03-10 15:03:40 +03:00
if ( ! $this -> mkDirHier ( $dest_dir )) {
return $this -> raiseError ( " failed to mkdir $dest_dir " ,
PEAR_INSTALLER_FAILED );
}
$this -> log ( 3 , " + mkdir $dest_dir " );
}
2012-05-11 19:57:55 +04:00
// 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 );
// }}}
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
// {{{ 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 " );
}
}
}
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
// }}}
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' ));
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
}
// Store the full path where the file was installed for easy unistall
if ( $atts [ 'role' ] != 'script' ) {
$loc = $this -> config -> get ( $atts [ 'role' ] . '_dir' );
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
$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 " );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
}
$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
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
$wp = @ fopen ( $dest_file , " wb " );
if ( ! is_resource ( $wp )) {
return $this -> raiseError ( " failed to create $dest_file : $php_errormsg " ,
PEAR_INSTALLER_FAILED );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
if ( fwrite ( $wp , $contents ) === false ) {
return $this -> raiseError ( " failed writing to $dest_file : $php_errormsg " ,
PEAR_INSTALLER_FAILED );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
fclose ( $wp );
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
// {{{ 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 );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
// }}}
2012-05-11 19:57:55 +04:00
// {{{ set file permissions
if ( ! OS_WINDOWS ) {
if ( $role -> isExecutable ()) {
$mode = 0777 & ~ ( int ) octdec ( $this -> config -> get ( 'umask' ));
$this -> log ( 3 , " + chmod +x $dest_file " );
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
$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 " );
}
}
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
// }}}
if ( $attribs [ 'role' ] == 'src' ) {
rename ( $dest_file , $final_dest_file );
$this -> log ( 2 , " renamed source file $dest_file to $final_dest_file " );
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
$this -> addFileOperation ( " rename " , array ( $dest_file , $final_dest_file , $role -> isExtension ()));
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
// 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 )))));
}
2010-03-10 15:03:40 +03:00
//$this->log(2, "installed: $dest_file");
return PEAR_INSTALLER_OK ;
}
// }}}
// {{{ addFileOperation()
/**
* Add a file operation to the current file transaction .
*
* @ see startFileTransaction ()
2012-05-11 19:57:55 +04:00
* @ 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 )
2010-03-10 15:03:40 +03:00
* - 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 ) .
2012-05-11 19:57:55 +04:00
* @ param array $data For all file operations , this array must contain the
2010-03-10 15:03:40 +03:00
* 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 ,
2012-05-11 19:57:55 +04:00
* the second its new name , the third whether this is a PHP extension .
2010-03-10 15:03:40 +03:00
*
* 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 ));
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
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 ();
2012-05-11 19:57:55 +04:00
foreach ( $this -> file_operations as $key => $tr ) {
2010-03-10 15:03:40 +03:00
list ( $type , $data ) = $tr ;
switch ( $type ) {
case 'rename' :
if ( ! file_exists ( $data [ 0 ])) {
$errors [] = " cannot rename file $data[0] , doesn't exist " ;
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
// 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
2012-05-11 19:57:55 +04:00
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. " );
}
}
2010-03-10 15:03:40 +03:00
}
break ;
}
}
// }}}
2012-05-11 19:57:55 +04:00
$n = count ( $this -> file_operations );
$this -> log ( 2 , " about to commit $n file operations for " . $this -> pkginfo -> getName ());
$m = count ( $errors );
2010-03-10 15:03:40 +03:00
if ( $m > 0 ) {
foreach ( $errors as $error ) {
2012-05-11 19:57:55 +04:00
if ( ! isset ( $this -> _options [ 'soft' ])) {
$this -> log ( 1 , $error );
}
}
if ( ! isset ( $this -> _options [ 'ignore-errors' ])) {
return false ;
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
$this -> _dirtree = array ();
2010-03-10 15:03:40 +03:00
// {{{ really commit the transaction
2012-05-11 19:57:55 +04:00
foreach ( $this -> file_operations as $i => $tr ) {
if ( ! $tr ) {
// support removal of non-existing backups
continue ;
}
2010-03-10 15:03:40 +03:00
list ( $type , $data ) = $tr ;
switch ( $type ) {
2012-05-11 19:57:55 +04:00
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 " );
2010-03-10 15:03:40 +03:00
break ;
2012-05-11 19:57:55 +04:00
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) " );
}
2010-03-10 15:03:40 +03:00
break ;
2012-05-11 19:57:55 +04:00
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] " );
}
2010-03-10 15:03:40 +03:00
break ;
case 'rmdir' :
2012-05-11 19:57:55 +04:00
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 );
}
2010-03-10 15:03:40 +03:00
break ;
case 'installed_as' :
2012-05-11 19:57:55 +04:00
$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 ;
2010-03-10 15:03:40 +03:00
$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 ) {
2012-05-11 19:57:55 +04:00
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 ;
2010-03-10 15:03:40 +03:00
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' :
2012-05-11 19:57:55 +04:00
$this -> pkginfo -> setInstalledAs ( $data [ 0 ], false );
2010-03-10 15:03:40 +03:00
break ;
}
}
2012-05-11 19:57:55 +04:00
$this -> pkginfo -> resetDirtree ();
2010-03-10 15:03:40 +03:00
$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 );
2012-05-11 19:57:55 +04:00
$ret = parent :: download ( $packages );
$errors = $this -> getErrorMsgs ();
2010-03-10 15:03:40 +03:00
$installpackages = $this -> getDownloadedPackages ();
trigger_error ( " PEAR Warning: PEAR_Installer::download() is deprecated " .
" in favor of PEAR_Downloader class " , E_USER_WARNING );
return $ret ;
}
// }}}
2012-05-11 19:57:55 +04:00
// {{{ _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 ;
}
2010-03-10 15:03:40 +03:00
// {{{ install()
/**
* Installs the files within the package file specified .
*
2012-05-11 19:57:55 +04:00
* @ param string | PEAR_Downloader_Package $pkgfile path to the package file ,
* or a pre - initialized packagefile object
2010-03-10 15:03:40 +03:00
* @ 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 ())
{
2012-05-11 19:57:55 +04:00
$this -> _options = $options ;
$this -> _registry = & $this -> config -> getRegistry ();
if ( is_object ( $pkgfile )) {
$dlpkg = & $pkgfile ;
$pkg = $pkgfile -> getPackageFile ();
$pkgfile = $pkg -> getArchiveFile ();
$descfile = $pkg -> getPackageFile ();
2010-03-10 15:03:40 +03:00
} else {
$descfile = $pkgfile ;
2012-05-11 19:57:55 +04:00
$pkg = $this -> _parsePackageXml ( $descfile );
if ( PEAR :: isError ( $pkg )) {
return $pkg ;
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
}
$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 . '"' );
2010-03-10 15:03:40 +03:00
$tar = new Archive_Tar ( $pkgfile );
2012-05-11 19:57:55 +04:00
if ( ! $tar -> extract ( $tmpdir )) {
2010-03-10 15:03:40 +03:00
return $this -> raiseError ( " unable to unpack $pkgfile " );
}
}
2012-05-11 19:57:55 +04:00
$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' ]);
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
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 ;
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
$installregistry = & $this -> _registry ;
$php_dir = $this -> config -> get ( 'php_dir' , null , $channel );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
$this -> installroot = '' ;
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
// {{{ 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 ;
}
2010-03-10 15:03:40 +03:00
2012-05-11 19:57:55 +04:00
// ensure we have the most accurate registry
$installregistry -> flushFileMap ();
$test = $installregistry -> checkFileMap ( $instfilelist , $testp , '1.1' );
if ( PEAR :: isError ( $test )) {
return $test ;
2010-03-10 15:03:40 +03:00
}
if ( sizeof ( $test )) {
2012-05-11 19:57:55 +04:00
$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 ]);
}
}
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
if ( count ( $test )) {
$msg = " $channel / $pkgname : conflicting files found: \n " ;
2010-03-10 15:03:40 +03:00
$longest = max ( array_map ( " strlen " , array_keys ( $test )));
$fmt = " % ${ longest } s (%s) \n " ;
2012-05-11 19:57:55 +04:00
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 " );
2010-03-10 15:03:40 +03:00
}
}
}
}
// }}}
$this -> startFileTransaction ();
2012-05-11 19:57:55 +04:00
$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' ])) {
2010-03-10 15:03:40 +03:00
// checks to do only when installing new packages
2012-05-11 19:57:55 +04:00
if ( empty ( $options [ 'force' ]) && $test ) {
return $this -> raiseError ( " $channel / $pkgname is already installed " );
2010-03-10 15:03:40 +03:00
}
} else {
2012-05-11 19:57:55 +04:00
// Upgrade
if ( $test ) {
$v1 = $installregistry -> packageInfo ( $pkgname , 'version' , $usechannel );
$v2 = $pkg -> getVersion ();
2010-03-10 15:03:40 +03:00
$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 ) " );
}
2012-05-11 19:57:55 +04:00
}
}
// 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 ());
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
} else {
$backedup = $err ;
2010-03-10 15:03:40 +03:00
}
}
// {{{ Copy files to dest dir ---------------------------------------
// info from the package it self we want to access from _installFile
2012-05-11 19:57:55 +04:00
$this -> pkginfo = & $pkg ;
2010-03-10 15:03:40 +03:00
// used to determine whether we should build any C code
$this -> source_files = 0 ;
2012-05-11 19:57:55 +04:00
$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 " );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
}
2010-03-10 15:03:40 +03:00
2012-05-11 19:57:55 +04:00
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 ;
}
2010-03-10 15:03:40 +03:00
2012-05-11 19:57:55 +04:00
$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 );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
$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 " );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
return $this -> raiseError ( $res );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
if ( ! isset ( $options [ 'soft' ])) {
$this -> log ( 0 , " Warning: " . $res -> getMessage ());
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
$real = isset ( $atts [ 'attribs' ]) ? $atts [ 'attribs' ] : $atts ;
if ( $res == PEAR_INSTALLER_OK && $real [ 'role' ] != 'src' ) {
// Register files that were installed
$pkg -> installedFile ( $file , $atts );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
}
// }}}
// {{{ 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 );
2010-03-10 15:03:40 +03:00
}
if ( ! $this -> commitFileTransaction ()) {
$this -> rollbackFileTransaction ();
2012-05-11 19:57:55 +04:00
$this -> configSet ( 'default_channel' , $savechannel );
2010-03-10 15:03:40 +03:00
return $this -> raiseError ( " commit failed " , PEAR_INSTALLER_FAILED );
}
// }}}
2012-05-11 19:57:55 +04:00
$ret = false ;
$installphase = 'install' ;
$oldversion = false ;
2010-03-10 15:03:40 +03:00
// {{{ Register that the package is installed -----------------------
if ( empty ( $options [ 'upgrade' ])) {
// if 'force' is used, replace the info in registry
2012-05-11 19:57:55 +04:00
$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 );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
if ( ! empty ( $options [ 'force' ]) && $test ) {
$oldversion = $installregistry -> packageInfo ( $pkgname , 'version' , $usechannel );
$installregistry -> deletePackage ( $pkgname , $usechannel );
}
$ret = $installregistry -> addPackage2 ( $pkg );
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
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 );
}
2010-03-10 15:03:40 +03:00
// new: upgrade installs a package if it isn't installed
2012-05-11 19:57:55 +04:00
if ( ! $test ) {
$ret = $installregistry -> addPackage2 ( $pkg );
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
if ( $usechannel != $channel ) {
$installregistry -> deletePackage ( $pkgname , $usechannel );
$ret = $installregistry -> addPackage2 ( $pkg );
} else {
$ret = $installregistry -> updatePackage2 ( $pkg );
}
$installphase = 'upgrade' ;
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
if ( ! $ret ) {
2012-05-11 19:57:55 +04:00
$this -> configSet ( 'default_channel' , $savechannel );
return $this -> raiseError ( " Adding package $channel / $pkgname to registry failed " );
2010-03-10 15:03:40 +03:00
}
// }}}
2012-05-11 19:57:55 +04:00
$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 , " \n Build 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 ));
}
}
2010-03-10 15:03:40 +03:00
}
// }}}
2012-05-11 19:57:55 +04:00
function & getUninstallPackages ()
{
return $this -> _downloadedPackages ;
}
2010-03-10 15:03:40 +03:00
// {{{ 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
2012-05-11 19:57:55 +04:00
* - register - only : update registry but don ' t remove files
2010-03-10 15:03:40 +03:00
* - nodeps : do not process dependencies of other packages to ensure
* uninstallation does not break things
*/
function uninstall ( $package , $options = array ())
{
2012-05-11 19:57:55 +04:00
$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 ();
2010-03-10 15:03:40 +03:00
} else {
2012-05-11 19:57:55 +04:00
$pkg = false ;
$info = $this -> _registry -> parsePackageName ( $package ,
$this -> config -> get ( 'default_channel' ));
$channel = $info [ 'channel' ];
$package = $info [ 'package' ];
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
$savechannel = $this -> config -> get ( 'default_channel' );
$this -> configSet ( 'default_channel' , $channel );
if ( ! is_object ( $pkg )) {
$pkg = $this -> _registry -> getPackage ( $package , $channel );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
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 );
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
if ( ! isset ( $options [ 'soft' ])) {
$this -> log ( 0 , 'WARNING: ' . $e -> getMessage ());
}
} elseif ( is_array ( $e )) {
if ( ! isset ( $options [ 'soft' ])) {
$this -> log ( 0 , $e [ 0 ]);
2010-03-10 15:03:40 +03:00
}
}
2012-05-11 19:57:55 +04:00
$this -> pkginfo = & $pkg ;
// pretty much nothing happens if we are only registering the uninstall
if ( empty ( $options [ 'register-only' ])) {
// {{{ Delete the files
2010-03-10 15:03:40 +03:00
$this -> startFileTransaction ();
2012-05-11 19:57:55 +04:00
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 ();
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
2010-03-10 15:03:40 +03:00
if ( ! $this -> commitFileTransaction ()) {
$this -> rollbackFileTransaction ();
2012-05-11 19:57:55 +04:00
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' );
}
}
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
// }}}
2010-03-10 15:03:40 +03:00
}
2012-05-11 19:57:55 +04:00
$this -> configSet ( 'default_channel' , $savechannel );
2010-03-10 15:03:40 +03:00
// Register that the package is no longer installed
2012-05-11 19:57:55 +04:00
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 ;
2010-03-10 15:03:40 +03:00
}
// }}}
// {{{ _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' );
}
}
// }}}
2012-05-11 19:57:55 +04:00
}