aboutsummaryrefslogtreecommitdiffstats
path: root/3dparty/PEAR
diff options
context:
space:
mode:
authorJakob Sack <mail@jakobsack.de>2011-03-01 23:20:16 +0100
committerJakob Sack <mail@jakobsack.de>2011-03-01 23:20:16 +0100
commit132695ceb1d7ab0e4bfbb141e9e9639111dd25b5 (patch)
treef53fabcefd89a1e5bbeda9a2c2d2fff6683139d4 /3dparty/PEAR
parent1fd39a52fa750878e7d70fba63c099f252095762 (diff)
downloadnextcloud-server-132695ceb1d7ab0e4bfbb141e9e9639111dd25b5.tar.gz
nextcloud-server-132695ceb1d7ab0e4bfbb141e9e9639111dd25b5.zip
Start of the refactoring. Commit is quite big because I forgot to use git right from the beginning. Sorry.
Diffstat (limited to '3dparty/PEAR')
-rw-r--r--3dparty/PEAR/Autoloader.php208
-rw-r--r--3dparty/PEAR/Builder.php426
-rw-r--r--3dparty/PEAR/Command.php398
-rw-r--r--3dparty/PEAR/Command/Auth.php155
-rw-r--r--3dparty/PEAR/Command/Build.php89
-rw-r--r--3dparty/PEAR/Command/Common.php249
-rw-r--r--3dparty/PEAR/Command/Config.php225
-rw-r--r--3dparty/PEAR/Command/Install.php470
-rw-r--r--3dparty/PEAR/Command/Mirror.php101
-rw-r--r--3dparty/PEAR/Command/Package.php819
-rw-r--r--3dparty/PEAR/Command/Registry.php351
-rw-r--r--3dparty/PEAR/Command/Remote.php435
-rw-r--r--3dparty/PEAR/Common.php2094
-rw-r--r--3dparty/PEAR/Config.php1169
-rw-r--r--3dparty/PEAR/Dependency.php487
-rw-r--r--3dparty/PEAR/Downloader.php680
-rw-r--r--3dparty/PEAR/ErrorStack.php981
-rw-r--r--3dparty/PEAR/Exception.php359
-rw-r--r--3dparty/PEAR/Frontend/CLI.php509
-rw-r--r--3dparty/PEAR/Installer.php1068
-rw-r--r--3dparty/PEAR/Packager.php165
-rw-r--r--3dparty/PEAR/Registry.php538
-rw-r--r--3dparty/PEAR/Remote.php394
-rw-r--r--3dparty/PEAR/RunTest.php363
24 files changed, 12733 insertions, 0 deletions
diff --git a/3dparty/PEAR/Autoloader.php b/3dparty/PEAR/Autoloader.php
new file mode 100644
index 00000000000..de0278d6191
--- /dev/null
+++ b/3dparty/PEAR/Autoloader.php
@@ -0,0 +1,208 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | 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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Autoloader.php,v 1.11 2004/02/27 02:21:29 cellog Exp $
+
+if (!extension_loaded("overload")) {
+ // die hard without ext/overload
+ die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
+}
+
+require_once "PEAR.php";
+
+/**
+ * This class is for objects where you want to separate the code for
+ * some methods into separate classes. This is useful if you have a
+ * class with not-frequently-used methods that contain lots of code
+ * that you would like to avoid always parsing.
+ *
+ * The PEAR_Autoloader class provides autoloading and aggregation.
+ * The autoloading lets you set up in which classes the separated
+ * methods are found. Aggregation is the technique used to import new
+ * methods, an instance of each class providing separated methods is
+ * stored and called every time the aggregated method is called.
+ *
+ * @author Stig Sæther Bakken <ssb@php.net>
+ */
+class PEAR_Autoloader extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * Map of methods and classes where they are defined
+ *
+ * @var array
+ *
+ * @access private
+ */
+ var $_autoload_map = array();
+
+ /**
+ * Map of methods and aggregate objects
+ *
+ * @var array
+ *
+ * @access private
+ */
+ var $_method_map = array();
+
+ // }}}
+ // {{{ addAutoload()
+
+ /**
+ * Add one or more autoload entries.
+ *
+ * @param string $method which method to autoload
+ *
+ * @param string $classname (optional) which class to find the method in.
+ * If the $method parameter is an array, this
+ * parameter may be omitted (and will be ignored
+ * if not), and the $method parameter will be
+ * treated as an associative array with method
+ * names as keys and class names as values.
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function addAutoload($method, $classname = null)
+ {
+ if (is_array($method)) {
+ array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
+ $this->_autoload_map = array_merge($this->_autoload_map, $method);
+ } else {
+ $this->_autoload_map[strtolower($method)] = $classname;
+ }
+ }
+
+ // }}}
+ // {{{ removeAutoload()
+
+ /**
+ * Remove an autoload entry.
+ *
+ * @param string $method which method to remove the autoload entry for
+ *
+ * @return bool TRUE if an entry was removed, FALSE if not
+ *
+ * @access public
+ */
+ function removeAutoload($method)
+ {
+ $method = strtolower($method);
+ $ok = isset($this->_autoload_map[$method]);
+ unset($this->_autoload_map[$method]);
+ return $ok;
+ }
+
+ // }}}
+ // {{{ addAggregateObject()
+
+ /**
+ * Add an aggregate object to this object. If the specified class
+ * is not defined, loading it will be attempted following PEAR's
+ * file naming scheme. All the methods in the class will be
+ * aggregated, except private ones (name starting with an
+ * underscore) and constructors.
+ *
+ * @param string $classname what class to instantiate for the object.
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function addAggregateObject($classname)
+ {
+ $classname = strtolower($classname);
+ if (!class_exists($classname)) {
+ $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
+ include_once $include_file;
+ }
+ $obj =& new $classname;
+ $methods = get_class_methods($classname);
+ foreach ($methods as $method) {
+ // don't import priviate methods and constructors
+ if ($method{0} != '_' && $method != $classname) {
+ $this->_method_map[$method] = $obj;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ removeAggregateObject()
+
+ /**
+ * Remove an aggregate object.
+ *
+ * @param string $classname the class of the object to remove
+ *
+ * @return bool TRUE if an object was removed, FALSE if not
+ *
+ * @access public
+ */
+ function removeAggregateObject($classname)
+ {
+ $ok = false;
+ $classname = strtolower($classname);
+ reset($this->_method_map);
+ while (list($method, $obj) = each($this->_method_map)) {
+ if (is_a($obj, $classname)) {
+ unset($this->_method_map[$method]);
+ $ok = true;
+ }
+ }
+ return $ok;
+ }
+
+ // }}}
+ // {{{ __call()
+
+ /**
+ * Overloaded object call handler, called each time an
+ * undefined/aggregated method is invoked. This method repeats
+ * the call in the right aggregate object and passes on the return
+ * value.
+ *
+ * @param string $method which method that was called
+ *
+ * @param string $args An array of the parameters passed in the
+ * original call
+ *
+ * @return mixed The return value from the aggregated method, or a PEAR
+ * error if the called method was unknown.
+ */
+ function __call($method, $args, &$retval)
+ {
+ $method = strtolower($method);
+ if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
+ $this->addAggregateObject($this->_autoload_map[$method]);
+ }
+ if (isset($this->_method_map[$method])) {
+ $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+}
+
+overload("PEAR_Autoloader");
+
+?>
diff --git a/3dparty/PEAR/Builder.php b/3dparty/PEAR/Builder.php
new file mode 100644
index 00000000000..4f6cc135d1e
--- /dev/null
+++ b/3dparty/PEAR/Builder.php
@@ -0,0 +1,426 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Sæther Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Builder.php,v 1.16.2.3 2005/02/17 17:55:01 cellog Exp $
+
+require_once 'PEAR/Common.php';
+
+/**
+ * Class to handle building (compiling) extensions.
+ *
+ * @author Stig Sæther Bakken <ssb@php.net>
+ */
+class PEAR_Builder extends PEAR_Common
+{
+ // {{{ properties
+
+ var $php_api_version = 0;
+ var $zend_module_api_no = 0;
+ var $zend_extension_api_no = 0;
+
+ var $extensions_built = array();
+
+ var $current_callback = null;
+
+ // used for msdev builds
+ var $_lastline = null;
+ var $_firstline = null;
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Builder constructor.
+ *
+ * @param object $ui user interface object (instance of PEAR_Frontend_*)
+ *
+ * @access public
+ */
+ function PEAR_Builder(&$ui)
+ {
+ parent::PEAR_Common();
+ $this->setFrontendObject($ui);
+ }
+
+ // }}}
+
+ // {{{ _build_win32()
+
+ /**
+ * Build an extension from source on windows.
+ * requires msdev
+ */
+ function _build_win32($descfile, $callback = null)
+ {
+ if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) {
+ return $info;
+ }
+ $dir = dirname($descfile);
+ $old_cwd = getcwd();
+
+ if (!@chdir($dir)) {
+ return $this->raiseError("could not chdir to $dir");
+ }
+ $this->log(2, "building in $dir");
+
+ $dsp = $info['package'].'.dsp';
+ if (!@is_file("$dir/$dsp")) {
+ return $this->raiseError("The DSP $dsp does not exist.");
+ }
+ // XXX TODO: make release build type configurable
+ $command = 'msdev '.$dsp.' /MAKE "'.$info['package']. ' - Release"';
+
+ $this->current_callback = $callback;
+ $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+
+ // figure out the build platform and type
+ $platform = 'Win32';
+ $buildtype = 'Release';
+ if (preg_match('/.*?'.$info['package'].'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
+ $platform = $matches[1];
+ $buildtype = $matches[2];
+ }
+
+ if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) {
+ if ($matches[2]) {
+ // there were errors in the build
+ return $this->raiseError("There were errors during compilation.");
+ }
+ $out = $matches[1];
+ } else {
+ return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
+ }
+
+ // msdev doesn't tell us the output directory :/
+ // open the dsp, find /out and use that directory
+ $dsptext = join(file($dsp),'');
+
+ // this regex depends on the build platform and type having been
+ // correctly identified above.
+ $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
+ $info['package'].'\s-\s'.
+ $platform.'\s'.
+ $buildtype.'").*?'.
+ '\/out:"(.*?)"/is';
+
+ if ($dsptext && preg_match($regex,$dsptext,$matches)) {
+ // what we get back is a relative path to the output file itself.
+ $outfile = realpath($matches[2]);
+ } else {
+ return $this->raiseError("Could not retrieve output information from $dsp.");
+ }
+ if (@copy($outfile, "$dir/$out")) {
+ $outfile = "$dir/$out";
+ }
+
+ $built_files[] = array(
+ 'file' => "$outfile",
+ 'php_api' => $this->php_api_version,
+ 'zend_mod_api' => $this->zend_module_api_no,
+ 'zend_ext_api' => $this->zend_extension_api_no,
+ );
+
+ return $built_files;
+ }
+ // }}}
+
+ // {{{ msdevCallback()
+ function msdevCallback($what, $data)
+ {
+ if (!$this->_firstline)
+ $this->_firstline = $data;
+ $this->_lastline = $data;
+ }
+ // }}}
+
+ // {{{ _harventInstDir
+ /**
+ * @param string
+ * @param string
+ * @param array
+ * @access private
+ */
+ function _harvestInstDir($dest_prefix, $dirname, &$built_files)
+ {
+ $d = opendir($dirname);
+ if (!$d)
+ return false;
+
+ $ret = true;
+ while (($ent = readdir($d)) !== false) {
+ if ($ent{0} == '.')
+ continue;
+
+ $full = $dirname . DIRECTORY_SEPARATOR . $ent;
+ if (is_dir($full)) {
+ if (!$this->_harvestInstDir(
+ $dest_prefix . DIRECTORY_SEPARATOR . $ent,
+ $full, $built_files)) {
+ $ret = false;
+ break;
+ }
+ } else {
+ $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
+ $built_files[] = array(
+ 'file' => $full,
+ 'dest' => $dest,
+ 'php_api' => $this->php_api_version,
+ 'zend_mod_api' => $this->zend_module_api_no,
+ 'zend_ext_api' => $this->zend_extension_api_no,
+ );
+ }
+ }
+ closedir($d);
+ return $ret;
+ }
+
+ // }}}
+
+ // {{{ build()
+
+ /**
+ * Build an extension from source. Runs "phpize" in the source
+ * directory, but compiles in a temporary directory
+ * (/var/tmp/pear-build-USER/PACKAGE-VERSION).
+ *
+ * @param string $descfile path to XML package description file
+ *
+ * @param mixed $callback callback function used to report output,
+ * see PEAR_Builder::_runCommand for details
+ *
+ * @return array an array of associative arrays with built files,
+ * format:
+ * array( array( 'file' => '/path/to/ext.so',
+ * 'php_api' => YYYYMMDD,
+ * 'zend_mod_api' => YYYYMMDD,
+ * 'zend_ext_api' => YYYYMMDD ),
+ * ... )
+ *
+ * @access public
+ *
+ * @see PEAR_Builder::_runCommand
+ * @see PEAR_Common::infoFromDescriptionFile
+ */
+ function build($descfile, $callback = null)
+ {
+ if (PEAR_OS == "Windows") {
+ return $this->_build_win32($descfile,$callback);
+ }
+ if (PEAR_OS != 'Unix') {
+ return $this->raiseError("building extensions not supported on this platform");
+ }
+ if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) {
+ return $info;
+ }
+ $dir = dirname($descfile);
+ $old_cwd = getcwd();
+ if (!@chdir($dir)) {
+ return $this->raiseError("could not chdir to $dir");
+ }
+ $vdir = "$info[package]-$info[version]";
+ if (is_dir($vdir)) {
+ chdir($vdir);
+ }
+ $dir = getcwd();
+ $this->log(2, "building in $dir");
+ $this->current_callback = $callback;
+ putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
+ $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback'));
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ if (!$err) {
+ return $this->raiseError("`phpize' failed");
+ }
+
+ // {{{ start of interactive part
+ $configure_command = "$dir/configure";
+ if (isset($info['configure_options'])) {
+ foreach ($info['configure_options'] as $o) {
+ list($r) = $this->ui->userDialog('build',
+ array($o['prompt']),
+ array('text'),
+ array(@$o['default']));
+ if (substr($o['name'], 0, 5) == 'with-' &&
+ ($r == 'yes' || $r == 'autodetect')) {
+ $configure_command .= " --$o[name]";
+ } else {
+ $configure_command .= " --$o[name]=".trim($r);
+ }
+ }
+ }
+ // }}} end of interactive part
+
+ // FIXME make configurable
+ if(!$user=getenv('USER')){
+ $user='defaultuser';
+ }
+ $build_basedir = "/var/tmp/pear-build-$user";
+ $build_dir = "$build_basedir/$info[package]-$info[version]";
+ $inst_dir = "$build_basedir/install-$info[package]-$info[version]";
+ $this->log(1, "building in $build_dir");
+ if (is_dir($build_dir)) {
+ System::rm('-rf', $build_dir);
+ }
+ if (!System::mkDir(array('-p', $build_dir))) {
+ return $this->raiseError("could not create build dir: $build_dir");
+ }
+ $this->addTempFile($build_dir);
+ if (!System::mkDir(array('-p', $inst_dir))) {
+ return $this->raiseError("could not create temporary install dir: $inst_dir");
+ }
+ $this->addTempFile($inst_dir);
+
+ if (getenv('MAKE')) {
+ $make_command = getenv('MAKE');
+ } else {
+ $make_command = 'make';
+ }
+ $to_run = array(
+ $configure_command,
+ $make_command,
+ "$make_command INSTALL_ROOT=\"$inst_dir\" install",
+ "find \"$inst_dir\" -ls"
+ );
+ if (!@chdir($build_dir)) {
+ return $this->raiseError("could not chdir to $build_dir");
+ }
+ putenv('PHP_PEAR_VERSION=@PEAR-VER@');
+ foreach ($to_run as $cmd) {
+ $err = $this->_runCommand($cmd, $callback);
+ if (PEAR::isError($err)) {
+ chdir($old_cwd);
+ return $err;
+ }
+ if (!$err) {
+ chdir($old_cwd);
+ return $this->raiseError("`$cmd' failed");
+ }
+ }
+ if (!($dp = opendir("modules"))) {
+ chdir($old_cwd);
+ return $this->raiseError("no `modules' directory found");
+ }
+ $built_files = array();
+ $prefix = exec("php-config --prefix");
+ $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
+ chdir($old_cwd);
+ return $built_files;
+ }
+
+ // }}}
+ // {{{ phpizeCallback()
+
+ /**
+ * Message callback function used when running the "phpize"
+ * program. Extracts the API numbers used. Ignores other message
+ * types than "cmdoutput".
+ *
+ * @param string $what the type of message
+ * @param mixed $data the message
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function phpizeCallback($what, $data)
+ {
+ if ($what != 'cmdoutput') {
+ return;
+ }
+ $this->log(1, rtrim($data));
+ if (preg_match('/You should update your .aclocal.m4/', $data)) {
+ return;
+ }
+ $matches = array();
+ if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
+ $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
+ $apino = (int)$matches[2];
+ if (isset($this->$member)) {
+ $this->$member = $apino;
+ //$msg = sprintf("%-22s : %d", $matches[1], $apino);
+ //$this->log(1, $msg);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _runCommand()
+
+ /**
+ * Run an external command, using a message callback to report
+ * output. The command will be run through popen and output is
+ * reported for every line with a "cmdoutput" message with the
+ * line string, including newlines, as payload.
+ *
+ * @param string $command the command to run
+ *
+ * @param mixed $callback (optional) function to use as message
+ * callback
+ *
+ * @return bool whether the command was successful (exit code 0
+ * means success, any other means failure)
+ *
+ * @access private
+ */
+ function _runCommand($command, $callback = null)
+ {
+ $this->log(1, "running: $command");
+ $pp = @popen("$command 2>&1", "r");
+ if (!$pp) {
+ return $this->raiseError("failed to run `$command'");
+ }
+ if ($callback && $callback[0]->debug == 1) {
+ $olddbg = $callback[0]->debug;
+ $callback[0]->debug = 2;
+ }
+
+ while ($line = fgets($pp, 1024)) {
+ if ($callback) {
+ call_user_func($callback, 'cmdoutput', $line);
+ } else {
+ $this->log(2, rtrim($line));
+ }
+ }
+ if ($callback && isset($olddbg)) {
+ $callback[0]->debug = $olddbg;
+ }
+ $exitcode = @pclose($pp);
+ return ($exitcode == 0);
+ }
+
+ // }}}
+ // {{{ log()
+
+ function log($level, $msg)
+ {
+ if ($this->current_callback) {
+ if ($this->debug >= $level) {
+ call_user_func($this->current_callback, 'output', $msg);
+ }
+ return;
+ }
+ return PEAR_Common::log($level, $msg);
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Command.php b/3dparty/PEAR/Command.php
new file mode 100644
index 00000000000..2ea68743d20
--- /dev/null
+++ b/3dparty/PEAR/Command.php
@@ -0,0 +1,398 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Command.php,v 1.24 2004/05/16 15:43:30 pajoye Exp $
+
+
+require_once "PEAR.php";
+
+/**
+ * List of commands and what classes they are implemented in.
+ * @var array command => implementing class
+ */
+$GLOBALS['_PEAR_Command_commandlist'] = array();
+
+/**
+ * List of shortcuts to common commands.
+ * @var array shortcut => command
+ */
+$GLOBALS['_PEAR_Command_shortcuts'] = array();
+
+/**
+ * Array of command objects
+ * @var array class => object
+ */
+$GLOBALS['_PEAR_Command_objects'] = array();
+
+/**
+ * Which user interface class is being used.
+ * @var string class name
+ */
+$GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI';
+
+/**
+ * Instance of $_PEAR_Command_uiclass.
+ * @var object
+ */
+$GLOBALS['_PEAR_Command_uiobject'] = null;
+
+/**
+ * PEAR command class, a simple factory class for administrative
+ * commands.
+ *
+ * How to implement command classes:
+ *
+ * - The class must be called PEAR_Command_Nnn, installed in the
+ * "PEAR/Common" subdir, with a method called getCommands() that
+ * returns an array of the commands implemented by the class (see
+ * PEAR/Command/Install.php for an example).
+ *
+ * - The class must implement a run() function that is called with three
+ * params:
+ *
+ * (string) command name
+ * (array) assoc array with options, freely defined by each
+ * command, for example:
+ * array('force' => true)
+ * (array) list of the other parameters
+ *
+ * The run() function returns a PEAR_CommandResponse object. Use
+ * these methods to get information:
+ *
+ * int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
+ * *_PARTIAL means that you need to issue at least
+ * one more command to complete the operation
+ * (used for example for validation steps).
+ *
+ * string getMessage() Returns a message for the user. Remember,
+ * no HTML or other interface-specific markup.
+ *
+ * If something unexpected happens, run() returns a PEAR error.
+ *
+ * - DON'T OUTPUT ANYTHING! Return text for output instead.
+ *
+ * - DON'T USE HTML! The text you return will be used from both Gtk,
+ * web and command-line interfaces, so for now, keep everything to
+ * plain text.
+ *
+ * - DON'T USE EXIT OR DIE! Always use pear errors. From static
+ * classes do PEAR::raiseError(), from other classes do
+ * $this->raiseError().
+ */
+class PEAR_Command
+{
+ // {{{ factory()
+
+ /**
+ * Get the right object for executing a command.
+ *
+ * @param string $command The name of the command
+ * @param object $config Instance of PEAR_Config object
+ *
+ * @return object the command object or a PEAR error
+ *
+ * @access public
+ * @static
+ */
+ function factory($command, &$config)
+ {
+ if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+ PEAR_Command::registerCommands();
+ }
+ if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
+ $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
+ }
+ if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+ return PEAR::raiseError("unknown command `$command'");
+ }
+ $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+ if (!class_exists($class)) {
+ return PEAR::raiseError("unknown command `$command'");
+ }
+ $ui =& PEAR_Command::getFrontendObject();
+ $obj = &new $class($ui, $config);
+ return $obj;
+ }
+
+ // }}}
+ // {{{ & getFrontendObject()
+
+ /**
+ * Get instance of frontend object.
+ *
+ * @return object
+ * @static
+ */
+ function &getFrontendObject()
+ {
+ if (empty($GLOBALS['_PEAR_Command_uiobject'])) {
+ $GLOBALS['_PEAR_Command_uiobject'] = &new $GLOBALS['_PEAR_Command_uiclass'];
+ }
+ return $GLOBALS['_PEAR_Command_uiobject'];
+ }
+
+ // }}}
+ // {{{ & setFrontendClass()
+
+ /**
+ * Load current frontend class.
+ *
+ * @param string $uiclass Name of class implementing the frontend
+ *
+ * @return object the frontend object, or a PEAR error
+ * @static
+ */
+ function &setFrontendClass($uiclass)
+ {
+ if (is_object($GLOBALS['_PEAR_Command_uiobject']) &&
+ is_a($GLOBALS['_PEAR_Command_uiobject'], $uiclass)) {
+ return $GLOBALS['_PEAR_Command_uiobject'];
+ }
+ if (!class_exists($uiclass)) {
+ $file = str_replace('_', '/', $uiclass) . '.php';
+ if (PEAR_Command::isIncludeable($file)) {
+ include_once $file;
+ }
+ }
+ if (class_exists($uiclass)) {
+ $obj = &new $uiclass;
+ // quick test to see if this class implements a few of the most
+ // important frontend methods
+ if (method_exists($obj, 'userConfirm')) {
+ $GLOBALS['_PEAR_Command_uiobject'] = &$obj;
+ $GLOBALS['_PEAR_Command_uiclass'] = $uiclass;
+ return $obj;
+ } else {
+ $err = PEAR::raiseError("not a frontend class: $uiclass");
+ return $err;
+ }
+ }
+ $err = PEAR::raiseError("no such class: $uiclass");
+ return $err;
+ }
+
+ // }}}
+ // {{{ setFrontendType()
+
+ // }}}
+ // {{{ isIncludeable()
+
+ /**
+ * @param string $path relative or absolute include path
+ * @return boolean
+ * @static
+ */
+ function isIncludeable($path)
+ {
+ if (file_exists($path) && is_readable($path)) {
+ return true;
+ }
+ $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
+ foreach ($ipath as $include) {
+ $test = realpath($include . DIRECTORY_SEPARATOR . $path);
+ if (file_exists($test) && is_readable($test)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set current frontend.
+ *
+ * @param string $uitype Name of the frontend type (for example "CLI")
+ *
+ * @return object the frontend object, or a PEAR error
+ * @static
+ */
+ function setFrontendType($uitype)
+ {
+ $uiclass = 'PEAR_Frontend_' . $uitype;
+ return PEAR_Command::setFrontendClass($uiclass);
+ }
+
+ // }}}
+ // {{{ registerCommands()
+
+ /**
+ * Scan through the Command directory looking for classes
+ * and see what commands they implement.
+ *
+ * @param bool (optional) if FALSE (default), the new list of
+ * commands should replace the current one. If TRUE,
+ * new entries will be merged with old.
+ *
+ * @param string (optional) where (what directory) to look for
+ * classes, defaults to the Command subdirectory of
+ * the directory from where this file (__FILE__) is
+ * included.
+ *
+ * @return bool TRUE on success, a PEAR error on failure
+ *
+ * @access public
+ * @static
+ */
+ function registerCommands($merge = false, $dir = null)
+ {
+ if ($dir === null) {
+ $dir = dirname(__FILE__) . '/Command';
+ }
+ $dp = @opendir($dir);
+ if (empty($dp)) {
+ return PEAR::raiseError("registerCommands: opendir($dir) failed");
+ }
+ if (!$merge) {
+ $GLOBALS['_PEAR_Command_commandlist'] = array();
+ }
+ while ($entry = readdir($dp)) {
+ if ($entry{0} == '.' || substr($entry, -4) != '.php' || $entry == 'Common.php') {
+ continue;
+ }
+ $class = "PEAR_Command_".substr($entry, 0, -4);
+ $file = "$dir/$entry";
+ include_once $file;
+ // List of commands
+ if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
+ $GLOBALS['_PEAR_Command_objects'][$class] = &new $class($ui, $config);
+ }
+ $implements = $GLOBALS['_PEAR_Command_objects'][$class]->getCommands();
+ foreach ($implements as $command => $desc) {
+ $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
+ $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc;
+ }
+ $shortcuts = $GLOBALS['_PEAR_Command_objects'][$class]->getShortcuts();
+ foreach ($shortcuts as $shortcut => $command) {
+ $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
+ }
+ }
+ @closedir($dp);
+ return true;
+ }
+
+ // }}}
+ // {{{ getCommands()
+
+ /**
+ * Get the list of currently supported commands, and what
+ * classes implement them.
+ *
+ * @return array command => implementing class
+ *
+ * @access public
+ * @static
+ */
+ function getCommands()
+ {
+ if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+ PEAR_Command::registerCommands();
+ }
+ return $GLOBALS['_PEAR_Command_commandlist'];
+ }
+
+ // }}}
+ // {{{ getShortcuts()
+
+ /**
+ * Get the list of command shortcuts.
+ *
+ * @return array shortcut => command
+ *
+ * @access public
+ * @static
+ */
+ function getShortcuts()
+ {
+ if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
+ PEAR_Command::registerCommands();
+ }
+ return $GLOBALS['_PEAR_Command_shortcuts'];
+ }
+
+ // }}}
+ // {{{ getGetoptArgs()
+
+ /**
+ * Compiles arguments for getopt.
+ *
+ * @param string $command command to get optstring for
+ * @param string $short_args (reference) short getopt format
+ * @param array $long_args (reference) long getopt format
+ *
+ * @return void
+ *
+ * @access public
+ * @static
+ */
+ function getGetoptArgs($command, &$short_args, &$long_args)
+ {
+ if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+ PEAR_Command::registerCommands();
+ }
+ if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+ return null;
+ }
+ $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+ $obj = &$GLOBALS['_PEAR_Command_objects'][$class];
+ return $obj->getGetoptArgs($command, $short_args, $long_args);
+ }
+
+ // }}}
+ // {{{ getDescription()
+
+ /**
+ * Get description for a command.
+ *
+ * @param string $command Name of the command
+ *
+ * @return string command description
+ *
+ * @access public
+ * @static
+ */
+ function getDescription($command)
+ {
+ if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
+ return null;
+ }
+ return $GLOBALS['_PEAR_Command_commanddesc'][$command];
+ }
+
+ // }}}
+ // {{{ getHelp()
+
+ /**
+ * Get help for command.
+ *
+ * @param string $command Name of the command to return help for
+ *
+ * @access public
+ * @static
+ */
+ function getHelp($command)
+ {
+ $cmds = PEAR_Command::getCommands();
+ if (isset($cmds[$command])) {
+ $class = $cmds[$command];
+ return $GLOBALS['_PEAR_Command_objects'][$class]->getHelp($command);
+ }
+ return false;
+ }
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Command/Auth.php b/3dparty/PEAR/Command/Auth.php
new file mode 100644
index 00000000000..0b9d3d3965d
--- /dev/null
+++ b/3dparty/PEAR/Command/Auth.php
@@ -0,0 +1,155 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Auth.php,v 1.15 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Remote.php";
+require_once "PEAR/Config.php";
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ */
+class PEAR_Command_Auth extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'login' => array(
+ 'summary' => 'Connects and authenticates to remote server',
+ 'shortcut' => 'li',
+ 'function' => 'doLogin',
+ 'options' => array(),
+ 'doc' => '
+Log in to the remote server. To use remote functions in the installer
+that require any kind of privileges, you need to log in first. The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems). After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.',
+ ),
+ 'logout' => array(
+ 'summary' => 'Logs out from the remote server',
+ 'shortcut' => 'lo',
+ 'function' => 'doLogout',
+ 'options' => array(),
+ 'doc' => '
+Logs out from the remote server. This command does not actually
+connect to the remote server, it only deletes the stored username and
+password from your user configuration.',
+ )
+
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Auth constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Auth(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doLogin()
+
+ /**
+ * Execute the 'login' command.
+ *
+ * @param string $command command name
+ *
+ * @param array $options option_name => value
+ *
+ * @param array $params list of additional parameters
+ *
+ * @return bool TRUE on success, FALSE for unknown commands, or
+ * a PEAR error on failure
+ *
+ * @access public
+ */
+ function doLogin($command, $options, $params)
+ {
+ $server = $this->config->get('master_server');
+ $remote = new PEAR_Remote($this->config);
+ $username = $this->config->get('username');
+ if (empty($username)) {
+ $username = @$_ENV['USER'];
+ }
+ $this->ui->outputData("Logging in to $server.", $command);
+
+ list($username, $password) = $this->ui->userDialog(
+ $command,
+ array('Username', 'Password'),
+ array('text', 'password'),
+ array($username, '')
+ );
+ $username = trim($username);
+ $password = trim($password);
+
+ $this->config->set('username', $username);
+ $this->config->set('password', $password);
+
+ $remote->expectError(401);
+ $ok = $remote->call('logintest');
+ $remote->popExpect();
+ if ($ok === true) {
+ $this->ui->outputData("Logged in.", $command);
+ $this->config->store();
+ } else {
+ return $this->raiseError("Login failed!");
+ }
+
+ }
+
+ // }}}
+ // {{{ doLogout()
+
+ /**
+ * Execute the 'logout' command.
+ *
+ * @param string $command command name
+ *
+ * @param array $options option_name => value
+ *
+ * @param array $params list of additional parameters
+ *
+ * @return bool TRUE on success, FALSE for unknown commands, or
+ * a PEAR error on failure
+ *
+ * @access public
+ */
+ function doLogout($command, $options, $params)
+ {
+ $server = $this->config->get('master_server');
+ $this->ui->outputData("Logging out from $server.", $command);
+ $this->config->remove('username');
+ $this->config->remove('password');
+ $this->config->store();
+ }
+
+ // }}}
+}
+
+?> \ No newline at end of file
diff --git a/3dparty/PEAR/Command/Build.php b/3dparty/PEAR/Command/Build.php
new file mode 100644
index 00000000000..2ecbbc92f5f
--- /dev/null
+++ b/3dparty/PEAR/Command/Build.php
@@ -0,0 +1,89 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Build.php,v 1.9 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Builder.php";
+
+/**
+ * PEAR commands for building extensions.
+ *
+ */
+class PEAR_Command_Build extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'build' => array(
+ 'summary' => 'Build an Extension From C Source',
+ 'function' => 'doBuild',
+ 'shortcut' => 'b',
+ 'options' => array(),
+ 'doc' => '[package.xml]
+Builds one or more extensions contained in a package.'
+ ),
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Build constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Build(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doBuild()
+
+ function doBuild($command, $options, $params)
+ {
+ if (sizeof($params) < 1) {
+ $params[0] = 'package.xml';
+ }
+ $builder = &new PEAR_Builder($this->ui);
+ $this->debug = $this->config->get('verbose');
+ $err = $builder->build($params[0], array(&$this, 'buildCallback'));
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ buildCallback()
+
+ function buildCallback($what, $data)
+ {
+ if (($what == 'cmdoutput' && $this->debug > 1) ||
+ ($what == 'output' && $this->debug > 0)) {
+ $this->ui->outputData(rtrim($data), 'build');
+ }
+ }
+
+ // }}}
+}
diff --git a/3dparty/PEAR/Command/Common.php b/3dparty/PEAR/Command/Common.php
new file mode 100644
index 00000000000..c6ace694caf
--- /dev/null
+++ b/3dparty/PEAR/Command/Common.php
@@ -0,0 +1,249 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Sæther Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.24 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR.php";
+
+class PEAR_Command_Common extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * PEAR_Config object used to pass user system and configuration
+ * on when executing commands
+ *
+ * @var object
+ */
+ var $config;
+
+ /**
+ * User Interface object, for all interaction with the user.
+ * @var object
+ */
+ var $ui;
+
+ var $_deps_rel_trans = array(
+ 'lt' => '<',
+ 'le' => '<=',
+ 'eq' => '=',
+ 'ne' => '!=',
+ 'gt' => '>',
+ 'ge' => '>=',
+ 'has' => '=='
+ );
+
+ var $_deps_type_trans = array(
+ 'pkg' => 'package',
+ 'extension' => 'extension',
+ 'php' => 'PHP',
+ 'prog' => 'external program',
+ 'ldlib' => 'external library for linking',
+ 'rtlib' => 'external runtime library',
+ 'os' => 'operating system',
+ 'websrv' => 'web server',
+ 'sapi' => 'SAPI backend'
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Common constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Common(&$ui, &$config)
+ {
+ parent::PEAR();
+ $this->config = &$config;
+ $this->ui = &$ui;
+ }
+
+ // }}}
+
+ // {{{ getCommands()
+
+ /**
+ * Return a list of all the commands defined by this class.
+ * @return array list of commands
+ * @access public
+ */
+ function getCommands()
+ {
+ $ret = array();
+ foreach (array_keys($this->commands) as $command) {
+ $ret[$command] = $this->commands[$command]['summary'];
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getShortcuts()
+
+ /**
+ * Return a list of all the command shortcuts defined by this class.
+ * @return array shortcut => command
+ * @access public
+ */
+ function getShortcuts()
+ {
+ $ret = array();
+ foreach (array_keys($this->commands) as $command) {
+ if (isset($this->commands[$command]['shortcut'])) {
+ $ret[$this->commands[$command]['shortcut']] = $command;
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ getOptions()
+
+ function getOptions($command)
+ {
+ return @$this->commands[$command]['options'];
+ }
+
+ // }}}
+ // {{{ getGetoptArgs()
+
+ function getGetoptArgs($command, &$short_args, &$long_args)
+ {
+ $short_args = "";
+ $long_args = array();
+ if (empty($this->commands[$command])) {
+ return;
+ }
+ reset($this->commands[$command]);
+ while (list($option, $info) = each($this->commands[$command]['options'])) {
+ $larg = $sarg = '';
+ if (isset($info['arg'])) {
+ if ($info['arg']{0} == '(') {
+ $larg = '==';
+ $sarg = '::';
+ $arg = substr($info['arg'], 1, -1);
+ } else {
+ $larg = '=';
+ $sarg = ':';
+ $arg = $info['arg'];
+ }
+ }
+ if (isset($info['shortopt'])) {
+ $short_args .= $info['shortopt'] . $sarg;
+ }
+ $long_args[] = $option . $larg;
+ }
+ }
+
+ // }}}
+ // {{{ getHelp()
+ /**
+ * Returns the help message for the given command
+ *
+ * @param string $command The command
+ * @return mixed A fail string if the command does not have help or
+ * a two elements array containing [0]=>help string,
+ * [1]=> help string for the accepted cmd args
+ */
+ function getHelp($command)
+ {
+ $config = &PEAR_Config::singleton();
+ $help = @$this->commands[$command]['doc'];
+ if (empty($help)) {
+ // XXX (cox) Fallback to summary if there is no doc (show both?)
+ if (!$help = @$this->commands[$command]['summary']) {
+ return "No help for command \"$command\"";
+ }
+ }
+ if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
+ foreach($matches[0] as $k => $v) {
+ $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
+ }
+ }
+ return array($help, $this->getHelpArgs($command));
+ }
+
+ // }}}
+ // {{{ getHelpArgs()
+ /**
+ * Returns the help for the accepted arguments of a command
+ *
+ * @param string $command
+ * @return string The help string
+ */
+ function getHelpArgs($command)
+ {
+ if (isset($this->commands[$command]['options']) &&
+ count($this->commands[$command]['options']))
+ {
+ $help = "Options:\n";
+ foreach ($this->commands[$command]['options'] as $k => $v) {
+ if (isset($v['arg'])) {
+ if ($v['arg']{0} == '(') {
+ $arg = substr($v['arg'], 1, -1);
+ $sapp = " [$arg]";
+ $lapp = "[=$arg]";
+ } else {
+ $sapp = " $v[arg]";
+ $lapp = "=$v[arg]";
+ }
+ } else {
+ $sapp = $lapp = "";
+ }
+ if (isset($v['shortopt'])) {
+ $s = $v['shortopt'];
+ @$help .= " -$s$sapp, --$k$lapp\n";
+ } else {
+ @$help .= " --$k$lapp\n";
+ }
+ $p = " ";
+ $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
+ $help .= " $doc\n";
+ }
+ return $help;
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ run()
+
+ function run($command, $options, $params)
+ {
+ $func = @$this->commands[$command]['function'];
+ if (empty($func)) {
+ // look for shortcuts
+ foreach (array_keys($this->commands) as $cmd) {
+ if (@$this->commands[$cmd]['shortcut'] == $command) {
+ $command = $cmd;
+ $func = @$this->commands[$command]['function'];
+ if (empty($func)) {
+ return $this->raiseError("unknown command `$command'");
+ }
+ break;
+ }
+ }
+ }
+ return $this->$func($command, $options, $params);
+ }
+
+ // }}}
+}
+
+?> \ No newline at end of file
diff --git a/3dparty/PEAR/Command/Config.php b/3dparty/PEAR/Command/Config.php
new file mode 100644
index 00000000000..474a2345170
--- /dev/null
+++ b/3dparty/PEAR/Command/Config.php
@@ -0,0 +1,225 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Config.php,v 1.27 2004/06/15 16:48:49 pajoye Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Config.php";
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ */
+class PEAR_Command_Config extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'config-show' => array(
+ 'summary' => 'Show All Settings',
+ 'function' => 'doConfigShow',
+ 'shortcut' => 'csh',
+ 'options' => array(),
+ 'doc' => '
+Displays all configuration values. An optional argument
+may be used to tell which configuration layer to display. Valid
+configuration layers are "user", "system" and "default".
+',
+ ),
+ 'config-get' => array(
+ 'summary' => 'Show One Setting',
+ 'function' => 'doConfigGet',
+ 'shortcut' => 'cg',
+ 'options' => array(),
+ 'doc' => '<parameter> [layer]
+Displays the value of one configuration parameter. The
+first argument is the name of the parameter, an optional second argument
+may be used to tell which configuration layer to look in. Valid configuration
+layers are "user", "system" and "default". If no layer is specified, a value
+will be picked from the first layer that defines the parameter, in the order
+just specified.
+',
+ ),
+ 'config-set' => array(
+ 'summary' => 'Change Setting',
+ 'function' => 'doConfigSet',
+ 'shortcut' => 'cs',
+ 'options' => array(),
+ 'doc' => '<parameter> <value> [layer]
+Sets the value of one configuration parameter. The first argument is
+the name of the parameter, the second argument is the new value. Some
+parameters are subject to validation, and the command will fail with
+an error message if the new value does not make sense. An optional
+third argument may be used to specify in which layer to set the
+configuration parameter. The default layer is "user".
+',
+ ),
+ 'config-help' => array(
+ 'summary' => 'Show Information About Setting',
+ 'function' => 'doConfigHelp',
+ 'shortcut' => 'ch',
+ 'options' => array(),
+ 'doc' => '[parameter]
+Displays help for a configuration parameter. Without arguments it
+displays help for all configuration parameters.
+',
+ ),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Config constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Config(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doConfigShow()
+
+ function doConfigShow($command, $options, $params)
+ {
+ // $params[0] -> the layer
+ if ($error = $this->_checkLayer(@$params[0])) {
+ return $this->raiseError($error);
+ }
+ $keys = $this->config->getKeys();
+ sort($keys);
+ $data = array('caption' => 'Configuration:');
+ foreach ($keys as $key) {
+ $type = $this->config->getType($key);
+ $value = $this->config->get($key, @$params[0]);
+ if ($type == 'password' && $value) {
+ $value = '********';
+ }
+ if ($value === false) {
+ $value = 'false';
+ } elseif ($value === true) {
+ $value = 'true';
+ }
+ $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
+ }
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doConfigGet()
+
+ function doConfigGet($command, $options, $params)
+ {
+ // $params[0] -> the parameter
+ // $params[1] -> the layer
+ if ($error = $this->_checkLayer(@$params[1])) {
+ return $this->raiseError($error);
+ }
+ if (sizeof($params) < 1 || sizeof($params) > 2) {
+ return $this->raiseError("config-get expects 1 or 2 parameters");
+ } elseif (sizeof($params) == 1) {
+ $this->ui->outputData($this->config->get($params[0]), $command);
+ } else {
+ $data = $this->config->get($params[0], $params[1]);
+ $this->ui->outputData($data, $command);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doConfigSet()
+
+ function doConfigSet($command, $options, $params)
+ {
+ // $param[0] -> a parameter to set
+ // $param[1] -> the value for the parameter
+ // $param[2] -> the layer
+ $failmsg = '';
+ if (sizeof($params) < 2 || sizeof($params) > 3) {
+ $failmsg .= "config-set expects 2 or 3 parameters";
+ return PEAR::raiseError($failmsg);
+ }
+ if ($error = $this->_checkLayer(@$params[2])) {
+ $failmsg .= $error;
+ return PEAR::raiseError($failmsg);
+ }
+ if (!call_user_func_array(array(&$this->config, 'set'), $params))
+ {
+ $failmsg = "config-set (" . implode(", ", $params) . ") failed";
+ } else {
+ $this->config->store();
+ }
+ if ($failmsg) {
+ return $this->raiseError($failmsg);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doConfigHelp()
+
+ function doConfigHelp($command, $options, $params)
+ {
+ if (empty($params)) {
+ $params = $this->config->getKeys();
+ }
+ $data['caption'] = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
+ $data['headline'] = array('Name', 'Type', 'Description');
+ $data['border'] = true;
+ foreach ($params as $name) {
+ $type = $this->config->getType($name);
+ $docs = $this->config->getDocs($name);
+ if ($type == 'set') {
+ $docs = rtrim($docs) . "\nValid set: " .
+ implode(' ', $this->config->getSetValues($name));
+ }
+ $data['data'][] = array($name, $type, $docs);
+ }
+ $this->ui->outputData($data, $command);
+ }
+
+ // }}}
+ // {{{ _checkLayer()
+
+ /**
+ * Checks if a layer is defined or not
+ *
+ * @param string $layer The layer to search for
+ * @return mixed False on no error or the error message
+ */
+ function _checkLayer($layer = null)
+ {
+ if (!empty($layer) && $layer != 'default') {
+ $layers = $this->config->getLayers();
+ if (!in_array($layer, $layers)) {
+ return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
+ }
+ }
+ return false;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Command/Install.php b/3dparty/PEAR/Command/Install.php
new file mode 100644
index 00000000000..dce52f017e2
--- /dev/null
+++ b/3dparty/PEAR/Command/Install.php
@@ -0,0 +1,470 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Sæther Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Install.php,v 1.53.2.1 2004/10/19 04:08:42 cellog Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Installer.php";
+
+/**
+ * PEAR commands for installation or deinstallation/upgrading of
+ * packages.
+ *
+ */
+class PEAR_Command_Install extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'install' => array(
+ 'summary' => 'Install Package',
+ 'function' => 'doInstall',
+ 'shortcut' => 'i',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'will overwrite newer installed packages',
+ ),
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, install anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as installed',
+ ),
+ 'soft' => array(
+ 'shortopt' => 's',
+ 'doc' => 'soft install, fail silently, or upgrade if already installed',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
+ ),
+ 'doc' => '<package> ...
+Installs one or more PEAR packages. You can specify a package to
+install in four ways:
+
+"Package-1.0.tgz" : installs from a local file
+
+"http://example.com/Package-1.0.tgz" : installs from
+anywhere on the net.
+
+"package.xml" : installs the package described in
+package.xml. Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+"Package" : queries your configured server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+More than one package may be specified at once. It is ok to mix these
+four ways of specifying packages.
+'),
+ 'upgrade' => array(
+ 'summary' => 'Upgrade Package',
+ 'function' => 'doInstall',
+ 'shortcut' => 'up',
+ 'options' => array(
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'overwrite newer installed packages',
+ ),
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, upgrade anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as upgraded',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ 'alldeps' => array(
+ 'shortopt' => 'a',
+ 'doc' => 'install all required and optional dependencies',
+ ),
+ 'onlyreqdeps' => array(
+ 'shortopt' => 'o',
+ 'doc' => 'install all required dependencies',
+ ),
+ ),
+ 'doc' => '<package> ...
+Upgrades one or more PEAR packages. See documentation for the
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+ 'upgrade-all' => array(
+ 'summary' => 'Upgrade All Packages',
+ 'function' => 'doInstall',
+ 'shortcut' => 'ua',
+ 'options' => array(
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, upgrade anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not install files, only register the package as upgraded',
+ ),
+ 'nobuild' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'don\'t build C extensions',
+ ),
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'request uncompressed files when downloading',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ ),
+ 'doc' => '
+Upgrades all packages that have a newer release available. Upgrades are
+done only if there is a release available of the state specified in
+"preferred_state" (currently {config preferred_state}), or a state considered
+more stable.
+'),
+ 'uninstall' => array(
+ 'summary' => 'Un-install Package',
+ 'function' => 'doUninstall',
+ 'shortcut' => 'un',
+ 'options' => array(
+ 'nodeps' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'ignore dependencies, uninstall anyway',
+ ),
+ 'register-only' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'do not remove files, only register the packages as not installed',
+ ),
+ 'installroot' => array(
+ 'shortopt' => 'R',
+ 'arg' => 'DIR',
+ 'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+ ),
+ 'ignore-errors' => array(
+ 'doc' => 'force install even if there were errors',
+ ),
+ ),
+ 'doc' => '<package> ...
+Uninstalls one or more PEAR packages. More than one package may be
+specified at once.
+'),
+ 'bundle' => array(
+ 'summary' => 'Unpacks a Pecl Package',
+ 'function' => 'doBundle',
+ 'shortcut' => 'bun',
+ 'options' => array(
+ 'destination' => array(
+ 'shortopt' => 'd',
+ 'arg' => 'DIR',
+ 'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
+ ),
+ 'force' => array(
+ 'shortopt' => 'f',
+ 'doc' => 'Force the unpacking even if there were errors in the package',
+ ),
+ ),
+ 'doc' => '<package>
+Unpacks a Pecl Package into the selected location. It will download the
+package if needed.
+'),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Install constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Install(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doInstall()
+
+ function doInstall($command, $options, $params)
+ {
+ require_once 'PEAR/Downloader.php';
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ if ($command == 'upgrade') {
+ $options['upgrade'] = true;
+ }
+ if ($command == 'upgrade-all') {
+ include_once "PEAR/Remote.php";
+ $options['upgrade'] = true;
+ $remote = &new PEAR_Remote($this->config);
+ $state = $this->config->get('preferred_state');
+ if (empty($state) || $state == 'any') {
+ $latest = $remote->call("package.listLatestReleases");
+ } else {
+ $latest = $remote->call("package.listLatestReleases", $state);
+ }
+ if (PEAR::isError($latest)) {
+ return $latest;
+ }
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $installed = array_flip($reg->listPackages());
+ $params = array();
+ foreach ($latest as $package => $info) {
+ $package = strtolower($package);
+ if (!isset($installed[$package])) {
+ // skip packages we don't have installed
+ continue;
+ }
+ $inst_version = $reg->packageInfo($package, 'version');
+ if (version_compare("$info[version]", "$inst_version", "le")) {
+ // installed version is up-to-date
+ continue;
+ }
+ $params[] = $package;
+ $this->ui->outputData(array('data' => "Will upgrade $package"), $command);
+ }
+ }
+ $this->downloader = &new PEAR_Downloader($this->ui, $options, $this->config);
+ $errors = array();
+ $downloaded = array();
+ $this->downloader->download($params);
+ $errors = $this->downloader->getErrorMsgs();
+ if (count($errors)) {
+ $err['data'] = array($errors);
+ $err['headline'] = 'Install Errors';
+ $this->ui->outputData($err);
+ return $this->raiseError("$command failed");
+ }
+ $downloaded = $this->downloader->getDownloadedPackages();
+ $this->installer->sortPkgDeps($downloaded);
+ foreach ($downloaded as $pkg) {
+ PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+ $info = $this->installer->install($pkg['file'], $options, $this->config);
+ PEAR::popErrorHandling();
+ if (PEAR::isError($info)) {
+ $this->ui->outputData('ERROR: ' .$info->getMessage());
+ continue;
+ }
+ if (is_array($info)) {
+ if ($this->config->get('verbose') > 0) {
+ $label = "$info[package] $info[version]";
+ $out = array('data' => "$command ok: $label");
+ if (isset($info['release_warnings'])) {
+ $out['release_warnings'] = $info['release_warnings'];
+ }
+ $this->ui->outputData($out, $command);
+ }
+ } else {
+ return $this->raiseError("$command failed");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doUninstall()
+
+ function doUninstall($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Installer($this->ui);
+ }
+ if (sizeof($params) < 1) {
+ return $this->raiseError("Please supply the package(s) you want to uninstall");
+ }
+ include_once 'PEAR/Registry.php';
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $newparams = array();
+ $badparams = array();
+ foreach ($params as $pkg) {
+ $info = $reg->packageInfo($pkg);
+ if ($info === null) {
+ $badparams[] = $pkg;
+ } else {
+ $newparams[] = $info;
+ }
+ }
+ $this->installer->sortPkgDeps($newparams, true);
+ $params = array();
+ foreach($newparams as $info) {
+ $params[] = $info['info']['package'];
+ }
+ $params = array_merge($params, $badparams);
+ foreach ($params as $pkg) {
+ if ($this->installer->uninstall($pkg, $options)) {
+ if ($this->config->get('verbose') > 0) {
+ $this->ui->outputData("uninstall ok: $pkg", $command);
+ }
+ } else {
+ return $this->raiseError("uninstall failed: $pkg");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+
+
+ // }}}
+ // {{{ doBundle()
+ /*
+ (cox) It just downloads and untars the package, does not do
+ any check that the PEAR_Installer::_installFile() does.
+ */
+
+ function doBundle($command, $options, $params)
+ {
+ if (empty($this->installer)) {
+ $this->installer = &new PEAR_Downloader($this->ui);
+ }
+ $installer = &$this->installer;
+ if (sizeof($params) < 1) {
+ return $this->raiseError("Please supply the package you want to bundle");
+ }
+ $pkgfile = $params[0];
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($installer->validPackageName($pkgfile)) {
+ $pkgfile = $installer->getPackageDownloadUrl($pkgfile);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ return $this->raiseError("Could not open the package file: $pkgfile");
+ } else {
+ return $this->raiseError("No package file given");
+ }
+ }
+ }
+
+ // Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $installer->config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $installer->log(2, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$installer, '_downloadCallback') : null;
+ $file = $installer->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ if (PEAR::isError($file)) {
+ return $this->raiseError($file);
+ }
+ $pkgfile = $file;
+ }
+
+ // Parse xml file -----------------------------------------------
+ $pkginfo = $installer->infoFromTgzFile($pkgfile);
+ if (PEAR::isError($pkginfo)) {
+ return $this->raiseError($pkginfo);
+ }
+ $installer->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:\n".
+ implode("\n", $errors));
+ } else {
+ $this->log(0, "warning : the following errors were found:\n".
+ implode("\n", $errors));
+ }
+ }
+ $pkgname = $pkginfo['package'];
+
+ // Unpacking -------------------------------------------------
+
+ if (isset($options['destination'])) {
+ if (!is_dir($options['destination'])) {
+ System::mkdir('-p ' . $options['destination']);
+ }
+ $dest = realpath($options['destination']);
+ } else {
+ $pwd = getcwd();
+ if (is_dir($pwd . DIRECTORY_SEPARATOR . 'ext')) {
+ $dest = $pwd . DIRECTORY_SEPARATOR . 'ext';
+ } else {
+ $dest = $pwd;
+ }
+ }
+ $dest .= DIRECTORY_SEPARATOR . $pkgname;
+ $orig = $pkgname . '-' . $pkginfo['version'];
+
+ $tar = new Archive_Tar($pkgfile);
+ if (!@$tar->extractModify($dest, $orig)) {
+ return $this->raiseError("unable to unpack $pkgfile");
+ }
+ $this->ui->outputData("Package ready at '$dest'");
+ // }}}
+ }
+
+ // }}}
+
+}
+?>
diff --git a/3dparty/PEAR/Command/Mirror.php b/3dparty/PEAR/Command/Mirror.php
new file mode 100644
index 00000000000..bf56c3db640
--- /dev/null
+++ b/3dparty/PEAR/Command/Mirror.php
@@ -0,0 +1,101 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Alexander Merz <alexmerz@php.net> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Mirror.php,v 1.5 2004/03/18 12:23:57 mj Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Command.php";
+require_once "PEAR/Remote.php";
+require_once "PEAR.php";
+
+/**
+ * PEAR commands for providing file mirrors
+ *
+ */
+class PEAR_Command_Mirror extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'download-all' => array(
+ 'summary' => 'Downloads each available package from master_server',
+ 'function' => 'doDownloadAll',
+ 'shortcut' => 'da',
+ 'options' => array(),
+ 'doc' => '
+ Requests a list of available packages from the package server
+ (master_server) and downloads them to current working directory'
+ ),
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Mirror constructor.
+ *
+ * @access public
+ * @param object PEAR_Frontend a reference to an frontend
+ * @param object PEAR_Config a reference to the configuration data
+ */
+ function PEAR_Command_Mirror(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doDownloadAll()
+ /**
+ * retrieves a list of avaible Packages from master server
+ * and downloads them
+ *
+ * @access public
+ * @param string $command the command
+ * @param array $options the command options before the command
+ * @param array $params the stuff after the command name
+ * @return bool true if succesful
+ * @throw PEAR_Error
+ */
+ function doDownloadAll($command, $options, $params)
+ {
+ $this->config->set("php_dir", ".");
+ $remote = &new PEAR_Remote($this->config);
+ $remoteInfo = $remote->call("package.listAll");
+ if (PEAR::isError($remoteInfo)) {
+ return $remoteInfo;
+ }
+ $cmd = &PEAR_Command::factory("download", $this->config);
+ if (PEAR::isError($cmd)) {
+ return $cmd;
+ }
+ foreach ($remoteInfo as $pkgn => $pkg) {
+ /**
+ * Error handling not neccesary, because already done by
+ * the download command
+ */
+ $cmd->run("download", array(), array($pkgn));
+ }
+
+ return true;
+ }
+
+ // }}}
+}
diff --git a/3dparty/PEAR/Command/Package.php b/3dparty/PEAR/Command/Package.php
new file mode 100644
index 00000000000..aca87111118
--- /dev/null
+++ b/3dparty/PEAR/Command/Package.php
@@ -0,0 +1,819 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Martin Jansen <mj@php.net> |
+// | Greg Beaver <cellog@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Package.php,v 1.61.2.7 2005/02/17 17:47:55 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Command/Common.php';
+
+class PEAR_Command_Package extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'package' => array(
+ 'summary' => 'Build Package',
+ 'function' => 'doPackage',
+ 'shortcut' => 'p',
+ 'options' => array(
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'Do not gzip the package file'
+ ),
+ 'showname' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'Print the name of the packaged file.',
+ ),
+ ),
+ 'doc' => '[descfile]
+Creates a PEAR package from its description file (usually called
+package.xml).
+'
+ ),
+ 'package-validate' => array(
+ 'summary' => 'Validate Package Consistency',
+ 'function' => 'doPackageValidate',
+ 'shortcut' => 'pv',
+ 'options' => array(),
+ 'doc' => '
+',
+ ),
+ 'cvsdiff' => array(
+ 'summary' => 'Run a "cvs diff" for all files in a package',
+ 'function' => 'doCvsDiff',
+ 'shortcut' => 'cd',
+ 'options' => array(
+ 'quiet' => array(
+ 'shortopt' => 'q',
+ 'doc' => 'Be quiet',
+ ),
+ 'reallyquiet' => array(
+ 'shortopt' => 'Q',
+ 'doc' => 'Be really quiet',
+ ),
+ 'date' => array(
+ 'shortopt' => 'D',
+ 'doc' => 'Diff against revision of DATE',
+ 'arg' => 'DATE',
+ ),
+ 'release' => array(
+ 'shortopt' => 'R',
+ 'doc' => 'Diff against tag for package release REL',
+ 'arg' => 'REL',
+ ),
+ 'revision' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'Diff against revision REV',
+ 'arg' => 'REV',
+ ),
+ 'context' => array(
+ 'shortopt' => 'c',
+ 'doc' => 'Generate context diff',
+ ),
+ 'unified' => array(
+ 'shortopt' => 'u',
+ 'doc' => 'Generate unified diff',
+ ),
+ 'ignore-case' => array(
+ 'shortopt' => 'i',
+ 'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
+ ),
+ 'ignore-whitespace' => array(
+ 'shortopt' => 'b',
+ 'doc' => 'Ignore changes in amount of white space',
+ ),
+ 'ignore-blank-lines' => array(
+ 'shortopt' => 'B',
+ 'doc' => 'Ignore changes that insert or delete blank lines',
+ ),
+ 'brief' => array(
+ 'doc' => 'Report only whether the files differ, no details',
+ ),
+ 'dry-run' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'Don\'t do anything, just pretend',
+ ),
+ ),
+ 'doc' => '<package.xml>
+Compares all the files in a package. Without any options, this
+command will compare the current code with the last checked-in code.
+Using the -r or -R option you may compare the current code with that
+of a specific release.
+',
+ ),
+ 'cvstag' => array(
+ 'summary' => 'Set CVS Release Tag',
+ 'function' => 'doCvsTag',
+ 'shortcut' => 'ct',
+ 'options' => array(
+ 'quiet' => array(
+ 'shortopt' => 'q',
+ 'doc' => 'Be quiet',
+ ),
+ 'reallyquiet' => array(
+ 'shortopt' => 'Q',
+ 'doc' => 'Be really quiet',
+ ),
+ 'slide' => array(
+ 'shortopt' => 'F',
+ 'doc' => 'Move (slide) tag if it exists',
+ ),
+ 'delete' => array(
+ 'shortopt' => 'd',
+ 'doc' => 'Remove tag',
+ ),
+ 'dry-run' => array(
+ 'shortopt' => 'n',
+ 'doc' => 'Don\'t do anything, just pretend',
+ ),
+ ),
+ 'doc' => '<package.xml>
+Sets a CVS tag on all files in a package. Use this command after you have
+packaged a distribution tarball with the "package" command to tag what
+revisions of what files were in that release. If need to fix something
+after running cvstag once, but before the tarball is released to the public,
+use the "slide" option to move the release tag.
+',
+ ),
+ 'run-tests' => array(
+ 'summary' => 'Run Regression Tests',
+ 'function' => 'doRunTests',
+ 'shortcut' => 'rt',
+ 'options' => array(
+ 'recur' => array(
+ 'shortopt' => 'r',
+ 'doc' => 'Run tests in child directories, recursively. 4 dirs deep maximum',
+ ),
+ 'ini' => array(
+ 'shortopt' => 'i',
+ 'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
+ 'arg' => 'SETTINGS'
+ ),
+ 'realtimelog' => array(
+ 'shortopt' => 'l',
+ 'doc' => 'Log test runs/results as they are run',
+ ),
+ ),
+ 'doc' => '[testfile|dir ...]
+Run regression tests with PHP\'s regression testing script (run-tests.php).',
+ ),
+ 'package-dependencies' => array(
+ 'summary' => 'Show package dependencies',
+ 'function' => 'doPackageDependencies',
+ 'shortcut' => 'pd',
+ 'options' => array(),
+ 'doc' => '
+List all depencies the package has.'
+ ),
+ 'sign' => array(
+ 'summary' => 'Sign a package distribution file',
+ 'function' => 'doSign',
+ 'shortcut' => 'si',
+ 'options' => array(),
+ 'doc' => '<package-file>
+Signs a package distribution (.tar or .tgz) file with GnuPG.',
+ ),
+ 'makerpm' => array(
+ 'summary' => 'Builds an RPM spec file from a PEAR package',
+ 'function' => 'doMakeRPM',
+ 'shortcut' => 'rpm',
+ 'options' => array(
+ 'spec-template' => array(
+ 'shortopt' => 't',
+ 'arg' => 'FILE',
+ 'doc' => 'Use FILE as RPM spec file template'
+ ),
+ 'rpm-pkgname' => array(
+ 'shortopt' => 'p',
+ 'arg' => 'FORMAT',
+ 'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
+by the PEAR package name, defaults to "PEAR::%s".',
+ ),
+ ),
+ 'doc' => '<package-file>
+
+Creates an RPM .spec file for wrapping a PEAR package inside an RPM
+package. Intended to be used from the SPECS directory, with the PEAR
+package tarball in the SOURCES directory:
+
+$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
+Wrote RPM spec file PEAR::Net_Geo-1.0.spec
+$ rpm -bb PEAR::Net_Socket-1.0.spec
+...
+Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
+',
+ ),
+ );
+
+ var $output;
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Package constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Package(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ _displayValidationResults()
+
+ function _displayValidationResults($err, $warn, $strict = false)
+ {
+ foreach ($err as $e) {
+ $this->output .= "Error: $e\n";
+ }
+ foreach ($warn as $w) {
+ $this->output .= "Warning: $w\n";
+ }
+ $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
+ sizeof($err), sizeof($warn));
+ if ($strict && sizeof($err) > 0) {
+ $this->output .= "Fix these errors and try again.";
+ return false;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doPackage()
+
+ function doPackage($command, $options, $params)
+ {
+ $this->output = '';
+ include_once 'PEAR/Packager.php';
+ if (sizeof($params) < 1) {
+ $params[0] = "package.xml";
+ }
+ $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
+ $packager =& new PEAR_Packager();
+ $err = $warn = array();
+ $dir = dirname($pkginfofile);
+ $compress = empty($options['nocompress']) ? true : false;
+ $result = $packager->package($pkginfofile, $compress);
+ if (PEAR::isError($result)) {
+ $this->ui->outputData($this->output, $command);
+ return $this->raiseError($result);
+ }
+ // Don't want output, only the package file name just created
+ if (isset($options['showname'])) {
+ $this->output = $result;
+ }
+ if (PEAR::isError($result)) {
+ $this->output .= "Package failed: ".$result->getMessage();
+ }
+ $this->ui->outputData($this->output, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doPackageValidate()
+
+ function doPackageValidate($command, $options, $params)
+ {
+ $this->output = '';
+ if (sizeof($params) < 1) {
+ $params[0] = "package.xml";
+ }
+ $obj = new PEAR_Common;
+ $info = null;
+ if ($fp = @fopen($params[0], "r")) {
+ $test = fread($fp, 5);
+ fclose($fp);
+ if ($test == "<?xml") {
+ $info = $obj->infoFromDescriptionFile($params[0]);
+ }
+ }
+ if (empty($info)) {
+ $info = $obj->infoFromTgzFile($params[0]);
+ }
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ $obj->validatePackageInfo($info, $err, $warn);
+ $this->_displayValidationResults($err, $warn);
+ $this->ui->outputData($this->output, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doCvsTag()
+
+ function doCvsTag($command, $options, $params)
+ {
+ $this->output = '';
+ $_cmd = $command;
+ if (sizeof($params) < 1) {
+ $help = $this->getHelp($command);
+ return $this->raiseError("$command: missing parameter: $help[0]");
+ }
+ $obj = new PEAR_Common;
+ $info = $obj->infoFromDescriptionFile($params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ $err = $warn = array();
+ $obj->validatePackageInfo($info, $err, $warn);
+ if (!$this->_displayValidationResults($err, $warn, true)) {
+ $this->ui->outputData($this->output, $command);
+ break;
+ }
+ $version = $info['version'];
+ $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
+ $cvstag = "RELEASE_$cvsversion";
+ $files = array_keys($info['filelist']);
+ $command = "cvs";
+ if (isset($options['quiet'])) {
+ $command .= ' -q';
+ }
+ if (isset($options['reallyquiet'])) {
+ $command .= ' -Q';
+ }
+ $command .= ' tag';
+ if (isset($options['slide'])) {
+ $command .= ' -F';
+ }
+ if (isset($options['delete'])) {
+ $command .= ' -d';
+ }
+ $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
+ foreach ($files as $file) {
+ $command .= ' ' . escapeshellarg($file);
+ }
+ if ($this->config->get('verbose') > 1) {
+ $this->output .= "+ $command\n";
+ }
+ $this->output .= "+ $command\n";
+ if (empty($options['dry-run'])) {
+ $fp = popen($command, "r");
+ while ($line = fgets($fp, 1024)) {
+ $this->output .= rtrim($line)."\n";
+ }
+ pclose($fp);
+ }
+ $this->ui->outputData($this->output, $_cmd);
+ return true;
+ }
+
+ // }}}
+ // {{{ doCvsDiff()
+
+ function doCvsDiff($command, $options, $params)
+ {
+ $this->output = '';
+ if (sizeof($params) < 1) {
+ $help = $this->getHelp($command);
+ return $this->raiseError("$command: missing parameter: $help[0]");
+ }
+ $obj = new PEAR_Common;
+ $info = $obj->infoFromDescriptionFile($params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ $files = array_keys($info['filelist']);
+ $cmd = "cvs";
+ if (isset($options['quiet'])) {
+ $cmd .= ' -q';
+ unset($options['quiet']);
+ }
+ if (isset($options['reallyquiet'])) {
+ $cmd .= ' -Q';
+ unset($options['reallyquiet']);
+ }
+ if (isset($options['release'])) {
+ $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
+ $cvstag = "RELEASE_$cvsversion";
+ $options['revision'] = $cvstag;
+ unset($options['release']);
+ }
+ $execute = true;
+ if (isset($options['dry-run'])) {
+ $execute = false;
+ unset($options['dry-run']);
+ }
+ $cmd .= ' diff';
+ // the rest of the options are passed right on to "cvs diff"
+ foreach ($options as $option => $optarg) {
+ $arg = @$this->commands[$command]['options'][$option]['arg'];
+ $short = @$this->commands[$command]['options'][$option]['shortopt'];
+ $cmd .= $short ? " -$short" : " --$option";
+ if ($arg && $optarg) {
+ $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
+ }
+ }
+ foreach ($files as $file) {
+ $cmd .= ' ' . escapeshellarg($file);
+ }
+ if ($this->config->get('verbose') > 1) {
+ $this->output .= "+ $cmd\n";
+ }
+ if ($execute) {
+ $fp = popen($cmd, "r");
+ while ($line = fgets($fp, 1024)) {
+ $this->output .= rtrim($line)."\n";
+ }
+ pclose($fp);
+ }
+ $this->ui->outputData($this->output, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doRunTests()
+
+ function doRunTests($command, $options, $params)
+ {
+ include_once 'PEAR/RunTest.php';
+ $log = new PEAR_Common;
+ $log->ui = &$this->ui; // slightly hacky, but it will work
+ $run = new PEAR_RunTest($log);
+ $tests = array();
+ if (isset($options['recur'])) {
+ $depth = 4;
+ } else {
+ $depth = 1;
+ }
+ if (!count($params)) {
+ $params[] = '.';
+ }
+ foreach ($params as $p) {
+ if (is_dir($p)) {
+ $dir = System::find(array($p, '-type', 'f',
+ '-maxdepth', $depth,
+ '-name', '*.phpt'));
+ $tests = array_merge($tests, $dir);
+ } else {
+ if (!@file_exists($p)) {
+ if (!preg_match('/\.phpt$/', $p)) {
+ $p .= '.phpt';
+ }
+ $dir = System::find(array(dirname($p), '-type', 'f',
+ '-maxdepth', $depth,
+ '-name', $p));
+ $tests = array_merge($tests, $dir);
+ } else {
+ $tests[] = $p;
+ }
+ }
+ }
+ $ini_settings = '';
+ if (isset($options['ini'])) {
+ $ini_settings .= $options['ini'];
+ }
+ if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
+ $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
+ }
+ if ($ini_settings) {
+ $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
+ }
+ $skipped = $passed = $failed = array();
+ $this->ui->outputData('Running ' . count($tests) . ' tests', $command);
+ $start = time();
+ if (isset($options['realtimelog'])) {
+ @unlink('run-tests.log');
+ }
+ foreach ($tests as $t) {
+ if (isset($options['realtimelog'])) {
+ $fp = @fopen('run-tests.log', 'a');
+ if ($fp) {
+ fwrite($fp, "Running test $t...");
+ fclose($fp);
+ }
+ }
+ $result = $run->run($t, $ini_settings);
+ if (OS_WINDOWS) {
+ for($i=0;$i<2000;$i++) {
+ $i = $i; // delay - race conditions on windows
+ }
+ }
+ if (isset($options['realtimelog'])) {
+ $fp = @fopen('run-tests.log', 'a');
+ if ($fp) {
+ fwrite($fp, "$result\n");
+ fclose($fp);
+ }
+ }
+ if ($result == 'FAILED') {
+ $failed[] = $t;
+ }
+ if ($result == 'PASSED') {
+ $passed[] = $t;
+ }
+ if ($result == 'SKIPPED') {
+ $skipped[] = $t;
+ }
+ }
+ $total = date('i:s', time() - $start);
+ if (count($failed)) {
+ $output = "TOTAL TIME: $total\n";
+ $output .= count($passed) . " PASSED TESTS\n";
+ $output .= count($skipped) . " SKIPPED TESTS\n";
+ $output .= count($failed) . " FAILED TESTS:\n";
+ foreach ($failed as $failure) {
+ $output .= $failure . "\n";
+ }
+ if (isset($options['realtimelog'])) {
+ $fp = @fopen('run-tests.log', 'a');
+ } else {
+ $fp = @fopen('run-tests.log', 'w');
+ }
+ if ($fp) {
+ fwrite($fp, $output, strlen($output));
+ fclose($fp);
+ $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
+ }
+ } elseif (@file_exists('run-tests.log') && !@is_dir('run-tests.log')) {
+ @unlink('run-tests.log');
+ }
+ $this->ui->outputData('TOTAL TIME: ' . $total);
+ $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
+ $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
+ if (count($failed)) {
+ $this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
+ foreach ($failed as $failure) {
+ $this->ui->outputData($failure, $command);
+ }
+ }
+
+ return true;
+ }
+
+ // }}}
+ // {{{ doPackageDependencies()
+
+ function doPackageDependencies($command, $options, $params)
+ {
+ // $params[0] -> the PEAR package to list its information
+ if (sizeof($params) != 1) {
+ return $this->raiseError("bad parameter(s), try \"help $command\"");
+ }
+
+ $obj = new PEAR_Common();
+ if (PEAR::isError($info = $obj->infoFromAny($params[0]))) {
+ return $this->raiseError($info);
+ }
+
+ if (is_array($info['release_deps'])) {
+ $data = array(
+ 'caption' => 'Dependencies for ' . $info['package'],
+ 'border' => true,
+ 'headline' => array("Type", "Name", "Relation", "Version"),
+ );
+
+ foreach ($info['release_deps'] as $d) {
+
+ if (isset($this->_deps_rel_trans[$d['rel']])) {
+ $rel = $this->_deps_rel_trans[$d['rel']];
+ } else {
+ $rel = $d['rel'];
+ }
+
+ if (isset($this->_deps_type_trans[$d['type']])) {
+ $type = ucfirst($this->_deps_type_trans[$d['type']]);
+ } else {
+ $type = $d['type'];
+ }
+
+ if (isset($d['name'])) {
+ $name = $d['name'];
+ } else {
+ $name = '';
+ }
+
+ if (isset($d['version'])) {
+ $version = $d['version'];
+ } else {
+ $version = '';
+ }
+
+ $data['data'][] = array($type, $name, $rel, $version);
+ }
+
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // Fallback
+ $this->ui->outputData("This package does not have any dependencies.", $command);
+ }
+
+ // }}}
+ // {{{ doSign()
+
+ function doSign($command, $options, $params)
+ {
+ // should move most of this code into PEAR_Packager
+ // so it'll be easy to implement "pear package --sign"
+ if (sizeof($params) != 1) {
+ return $this->raiseError("bad parameter(s), try \"help $command\"");
+ }
+ if (!file_exists($params[0])) {
+ return $this->raiseError("file does not exist: $params[0]");
+ }
+ $obj = new PEAR_Common;
+ $info = $obj->infoFromTgzFile($params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ include_once "Archive/Tar.php";
+ include_once "System.php";
+ $tar = new Archive_Tar($params[0]);
+ $tmpdir = System::mktemp('-d pearsign');
+ if (!$tar->extractList('package.xml package.sig', $tmpdir)) {
+ return $this->raiseError("failed to extract tar file");
+ }
+ if (file_exists("$tmpdir/package.sig")) {
+ return $this->raiseError("package already signed");
+ }
+ @unlink("$tmpdir/package.sig");
+ $input = $this->ui->userDialog($command,
+ array('GnuPG Passphrase'),
+ array('password'));
+ $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/package.xml 2>/dev/null", "w");
+ if (!$gpg) {
+ return $this->raiseError("gpg command failed");
+ }
+ fwrite($gpg, "$input[0]\r");
+ if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
+ return $this->raiseError("gpg sign failed");
+ }
+ $tar->addModify("$tmpdir/package.sig", '', $tmpdir);
+ return true;
+ }
+
+ // }}}
+ // {{{ doMakeRPM()
+
+ /*
+
+ (cox)
+
+ TODO:
+
+ - Fill the rpm dependencies in the template file.
+
+ IDEAS:
+
+ - Instead of mapping the role to rpm vars, perhaps it's better
+
+ to use directly the pear cmd to install the files by itself
+
+ in %postrun so:
+
+ pear -d php_dir=%{_libdir}/php/pear -d test_dir=.. <package>
+
+ */
+
+ function doMakeRPM($command, $options, $params)
+ {
+ if (sizeof($params) != 1) {
+ return $this->raiseError("bad parameter(s), try \"help $command\"");
+ }
+ if (!file_exists($params[0])) {
+ return $this->raiseError("file does not exist: $params[0]");
+ }
+ include_once "Archive/Tar.php";
+ include_once "PEAR/Installer.php";
+ include_once "System.php";
+ $tar = new Archive_Tar($params[0]);
+ $tmpdir = System::mktemp('-d pear2rpm');
+ $instroot = System::mktemp('-d pear2rpm');
+ $tmp = $this->config->get('verbose');
+ $this->config->set('verbose', 0);
+ $installer = new PEAR_Installer($this->ui);
+ $info = $installer->install($params[0],
+ array('installroot' => $instroot,
+ 'nodeps' => true));
+ $pkgdir = "$info[package]-$info[version]";
+ $info['rpm_xml_dir'] = '/var/lib/pear';
+ $this->config->set('verbose', $tmp);
+ if (!$tar->extractList("package.xml", $tmpdir, $pkgdir)) {
+ return $this->raiseError("failed to extract $params[0]");
+ }
+ if (!file_exists("$tmpdir/package.xml")) {
+ return $this->raiseError("no package.xml found in $params[0]");
+ }
+ if (isset($options['spec-template'])) {
+ $spec_template = $options['spec-template'];
+ } else {
+ $spec_template = $this->config->get('data_dir') .
+ '/PEAR/template.spec';
+ }
+ if (isset($options['rpm-pkgname'])) {
+ $rpm_pkgname_format = $options['rpm-pkgname'];
+ } else {
+ $rpm_pkgname_format = "PEAR::%s";
+ }
+
+ $info['extra_headers'] = '';
+ $info['doc_files'] = '';
+ $info['files'] = '';
+ $info['rpm_package'] = sprintf($rpm_pkgname_format, $info['package']);
+ $srcfiles = 0;
+ foreach ($info['filelist'] as $name => $attr) {
+
+ if (!isset($attr['role'])) {
+ continue;
+ }
+ $name = preg_replace('![/:\\\\]!', '/', $name);
+ if ($attr['role'] == 'doc') {
+ $info['doc_files'] .= " $name";
+
+ // Map role to the rpm vars
+ } else {
+
+ $c_prefix = '%{_libdir}/php/pear';
+
+ switch ($attr['role']) {
+
+ case 'php':
+
+ $prefix = $c_prefix; break;
+
+ case 'ext':
+
+ $prefix = '%{_libdir}/php'; break; // XXX good place?
+
+ case 'src':
+
+ $srcfiles++;
+
+ $prefix = '%{_includedir}/php'; break; // XXX good place?
+
+ case 'test':
+
+ $prefix = "$c_prefix/tests/" . $info['package']; break;
+
+ case 'data':
+
+ $prefix = "$c_prefix/data/" . $info['package']; break;
+
+ case 'script':
+
+ $prefix = '%{_bindir}'; break;
+
+ }
+
+ $name = str_replace('\\', '/', $name);
+ $info['files'] .= "$prefix/$name\n";
+
+ }
+ }
+ if ($srcfiles > 0) {
+ include_once "OS/Guess.php";
+ $os = new OS_Guess;
+ $arch = $os->getCpu();
+ } else {
+ $arch = 'noarch';
+ }
+ $cfg = array('master_server', 'php_dir', 'ext_dir', 'doc_dir',
+ 'bin_dir', 'data_dir', 'test_dir');
+ foreach ($cfg as $k) {
+ $info[$k] = $this->config->get($k);
+ }
+ $info['arch'] = $arch;
+ $fp = @fopen($spec_template, "r");
+ if (!$fp) {
+ return $this->raiseError("could not open RPM spec file template $spec_template: $php_errormsg");
+ }
+ $spec_contents = preg_replace('/@([a-z0-9_-]+)@/e', '$info["\1"]', fread($fp, filesize($spec_template)));
+ fclose($fp);
+ $spec_file = "$info[rpm_package]-$info[version].spec";
+ $wp = fopen($spec_file, "wb");
+ if (!$wp) {
+ return $this->raiseError("could not write RPM spec file $spec_file: $php_errormsg");
+ }
+ fwrite($wp, $spec_contents);
+ fclose($wp);
+ $this->ui->outputData("Wrote RPM spec file $spec_file", $command);
+
+ return true;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Command/Registry.php b/3dparty/PEAR/Command/Registry.php
new file mode 100644
index 00000000000..62cb4b00e28
--- /dev/null
+++ b/3dparty/PEAR/Command/Registry.php
@@ -0,0 +1,351 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | 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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Registry.php,v 1.36 2004/01/08 17:33:13 sniper Exp $
+
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Config.php';
+
+class PEAR_Command_Registry extends PEAR_Command_Common
+{
+ // {{{ properties
+
+ var $commands = array(
+ 'list' => array(
+ 'summary' => 'List Installed Packages',
+ 'function' => 'doList',
+ 'shortcut' => 'l',
+ 'options' => array(),
+ 'doc' => '[package]
+If invoked without parameters, this command lists the PEAR packages
+installed in your php_dir ({config php_dir)). With a parameter, it
+lists the files in that package.
+',
+ ),
+ 'shell-test' => array(
+ 'summary' => 'Shell Script Test',
+ 'function' => 'doShellTest',
+ 'shortcut' => 'st',
+ 'options' => array(),
+ 'doc' => '<package> [[relation] version]
+Tests if a package is installed in the system. Will exit(1) if it is not.
+ <relation> The version comparison operator. One of:
+ <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
+ <version> The version to compare with
+'),
+ 'info' => array(
+ 'summary' => 'Display information about a package',
+ 'function' => 'doInfo',
+ 'shortcut' => 'in',
+ 'options' => array(),
+ 'doc' => '<package>
+Displays information about a package. The package argument may be a
+local package file, an URL to a package file, or the name of an
+installed package.'
+ )
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Registry constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Registry(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doList()
+
+ function _sortinfo($a, $b)
+ {
+ return strcmp($a['package'], $b['package']);
+ }
+
+ function doList($command, $options, $params)
+ {
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ if (sizeof($params) == 0) {
+ $installed = $reg->packageInfo();
+ usort($installed, array(&$this, '_sortinfo'));
+ $i = $j = 0;
+ $data = array(
+ 'caption' => 'Installed packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Version', 'State')
+ );
+ foreach ($installed as $package) {
+ $data['data'][] = array($package['package'],
+ $package['version'],
+ @$package['release_state']);
+ }
+ if (count($installed)==0) {
+ $data = '(no packages installed)';
+ }
+ $this->ui->outputData($data, $command);
+ } else {
+ if (file_exists($params[0]) && !is_dir($params[0])) {
+ include_once "PEAR/Common.php";
+ $obj = &new PEAR_Common;
+ $info = $obj->infoFromAny($params[0]);
+ $headings = array('Package File', 'Install Path');
+ $installed = false;
+ } else {
+ $info = $reg->packageInfo($params[0]);
+ $headings = array('Type', 'Install Path');
+ $installed = true;
+ }
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ if ($info === null) {
+ return $this->raiseError("`$params[0]' not installed");
+ }
+ $list = $info['filelist'];
+ if ($installed) {
+ $caption = 'Installed Files For ' . $params[0];
+ } else {
+ $caption = 'Contents of ' . basename($params[0]);
+ }
+ $data = array(
+ 'caption' => $caption,
+ 'border' => true,
+ 'headline' => $headings);
+ foreach ($list as $file => $att) {
+ if ($installed) {
+ if (empty($att['installed_as'])) {
+ continue;
+ }
+ $data['data'][] = array($att['role'], $att['installed_as']);
+ } else {
+ if (isset($att['baseinstalldir'])) {
+ $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
+ $file;
+ } else {
+ $dest = $file;
+ }
+ switch ($att['role']) {
+ case 'test':
+ case 'data':
+ if ($installed) {
+ break 2;
+ }
+ $dest = '-- will not be installed --';
+ break;
+ case 'doc':
+ $dest = $this->config->get('doc_dir') . DIRECTORY_SEPARATOR .
+ $dest;
+ break;
+ case 'php':
+ default:
+ $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
+ $dest;
+ }
+ $dest = preg_replace('!/+!', '/', $dest);
+ $file = preg_replace('!/+!', '/', $file);
+ $data['data'][] = array($file, $dest);
+ }
+ }
+ $this->ui->outputData($data, $command);
+
+
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doShellTest()
+
+ function doShellTest($command, $options, $params)
+ {
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $reg = &new PEAR_Registry($this->config->get('php_dir'));
+ // "pear shell-test Foo"
+ if (sizeof($params) == 1) {
+ if (!$reg->packageExists($params[0])) {
+ exit(1);
+ }
+ // "pear shell-test Foo 1.0"
+ } elseif (sizeof($params) == 2) {
+ $v = $reg->packageInfo($params[0], 'version');
+ if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
+ exit(1);
+ }
+ // "pear shell-test Foo ge 1.0"
+ } elseif (sizeof($params) == 3) {
+ $v = $reg->packageInfo($params[0], 'version');
+ if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
+ exit(1);
+ }
+ } else {
+ $this->popErrorHandling();
+ $this->raiseError("$command: expects 1 to 3 parameters");
+ exit(1);
+ }
+ }
+
+ // }}}
+ // {{{ doInfo
+
+ function doInfo($command, $options, $params)
+ {
+ // $params[0] The package for showing info
+ if (sizeof($params) != 1) {
+ return $this->raiseError("This command only accepts one param: ".
+ "the package you want information");
+ }
+ if (@is_file($params[0])) {
+ $obj = &new PEAR_Common();
+ $info = $obj->infoFromAny($params[0]);
+ } else {
+ $reg = &new PEAR_Registry($this->config->get('php_dir'));
+ $info = $reg->packageInfo($params[0]);
+ }
+ if (PEAR::isError($info)) {
+ return $info;
+ }
+ if (empty($info)) {
+ $this->raiseError("Nothing found for `$params[0]'");
+ return;
+ }
+ unset($info['filelist']);
+ unset($info['changelog']);
+ $keys = array_keys($info);
+ $longtext = array('description', 'summary');
+ foreach ($keys as $key) {
+ if (is_array($info[$key])) {
+ switch ($key) {
+ case 'maintainers': {
+ $i = 0;
+ $mstr = '';
+ foreach ($info[$key] as $m) {
+ if ($i++ > 0) {
+ $mstr .= "\n";
+ }
+ $mstr .= $m['name'] . " <";
+ if (isset($m['email'])) {
+ $mstr .= $m['email'];
+ } else {
+ $mstr .= $m['handle'] . '@php.net';
+ }
+ $mstr .= "> ($m[role])";
+ }
+ $info[$key] = $mstr;
+ break;
+ }
+ case 'release_deps': {
+ $i = 0;
+ $dstr = '';
+ foreach ($info[$key] as $d) {
+ if (isset($this->_deps_rel_trans[$d['rel']])) {
+ $rel = $this->_deps_rel_trans[$d['rel']];
+ } else {
+ $rel = $d['rel'];
+ }
+ if (isset($this->_deps_type_trans[$d['type']])) {
+ $type = ucfirst($this->_deps_type_trans[$d['type']]);
+ } else {
+ $type = $d['type'];
+ }
+ if (isset($d['name'])) {
+ $name = $d['name'] . ' ';
+ } else {
+ $name = '';
+ }
+ if (isset($d['version'])) {
+ $version = $d['version'] . ' ';
+ } else {
+ $version = '';
+ }
+ $dstr .= "$type $name$rel $version\n";
+ }
+ $info[$key] = $dstr;
+ break;
+ }
+ case 'provides' : {
+ $debug = $this->config->get('verbose');
+ if ($debug < 2) {
+ $pstr = 'Classes: ';
+ } else {
+ $pstr = '';
+ }
+ $i = 0;
+ foreach ($info[$key] as $p) {
+ if ($debug < 2 && $p['type'] != "class") {
+ continue;
+ }
+ // Only print classes when verbosity mode is < 2
+ if ($debug < 2) {
+ if ($i++ > 0) {
+ $pstr .= ", ";
+ }
+ $pstr .= $p['name'];
+ } else {
+ if ($i++ > 0) {
+ $pstr .= "\n";
+ }
+ $pstr .= ucfirst($p['type']) . " " . $p['name'];
+ if (isset($p['explicit']) && $p['explicit'] == 1) {
+ $pstr .= " (explicit)";
+ }
+ }
+ }
+ $info[$key] = $pstr;
+ break;
+ }
+ default: {
+ $info[$key] = implode(", ", $info[$key]);
+ break;
+ }
+ }
+ }
+ if ($key == '_lastmodified') {
+ $hdate = date('Y-m-d', $info[$key]);
+ unset($info[$key]);
+ $info['Last Modified'] = $hdate;
+ } else {
+ $info[$key] = trim($info[$key]);
+ if (in_array($key, $longtext)) {
+ $info[$key] = preg_replace('/ +/', ' ', $info[$key]);
+ }
+ }
+ }
+ $caption = 'About ' . $info['package'] . '-' . $info['version'];
+ $data = array(
+ 'caption' => $caption,
+ 'border' => true);
+ foreach ($info as $key => $value) {
+ $key = ucwords(trim(str_replace('_', ' ', $key)));
+ $data['data'][] = array($key, $value);
+ }
+ $data['raw'] = $info;
+
+ $this->ui->outputData($data, 'package-info');
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Command/Remote.php b/3dparty/PEAR/Command/Remote.php
new file mode 100644
index 00000000000..bbd16093f5d
--- /dev/null
+++ b/3dparty/PEAR/Command/Remote.php
@@ -0,0 +1,435 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | 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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Remote.php,v 1.39 2004/04/03 15:56:00 cellog Exp $
+
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Remote.php';
+require_once 'PEAR/Registry.php';
+
+class PEAR_Command_Remote extends PEAR_Command_Common
+{
+ // {{{ command definitions
+
+ var $commands = array(
+ 'remote-info' => array(
+ 'summary' => 'Information About Remote Packages',
+ 'function' => 'doRemoteInfo',
+ 'shortcut' => 'ri',
+ 'options' => array(),
+ 'doc' => '<package>
+Get details on a package from the server.',
+ ),
+ 'list-upgrades' => array(
+ 'summary' => 'List Available Upgrades',
+ 'function' => 'doListUpgrades',
+ 'shortcut' => 'lu',
+ 'options' => array(),
+ 'doc' => '
+List releases on the server of packages you have installed where
+a newer version is available with the same release state (stable etc.).'
+ ),
+ 'remote-list' => array(
+ 'summary' => 'List Remote Packages',
+ 'function' => 'doRemoteList',
+ 'shortcut' => 'rl',
+ 'options' => array(),
+ 'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+ ),
+ 'search' => array(
+ 'summary' => 'Search remote package database',
+ 'function' => 'doSearch',
+ 'shortcut' => 'sp',
+ 'options' => array(),
+ 'doc' => '
+Lists all packages which match the search parameters (first param
+is package name, second package info)',
+ ),
+ 'list-all' => array(
+ 'summary' => 'List All Packages',
+ 'function' => 'doListAll',
+ 'shortcut' => 'la',
+ 'options' => array(),
+ 'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+ ),
+ 'download' => array(
+ 'summary' => 'Download Package',
+ 'function' => 'doDownload',
+ 'shortcut' => 'd',
+ 'options' => array(
+ 'nocompress' => array(
+ 'shortopt' => 'Z',
+ 'doc' => 'download an uncompressed (.tar) file',
+ ),
+ ),
+ 'doc' => '{package|package-version}
+Download a package tarball. The file will be named as suggested by the
+server, for example if you download the DB package and the latest stable
+version of DB is 1.2, the downloaded file will be DB-1.2.tgz.',
+ ),
+ 'clear-cache' => array(
+ 'summary' => 'Clear XML-RPC Cache',
+ 'function' => 'doClearCache',
+ 'shortcut' => 'cc',
+ 'options' => array(),
+ 'doc' => '
+Clear the XML-RPC cache. See also the cache_ttl configuration
+parameter.
+',
+ ),
+ );
+
+ // }}}
+ // {{{ constructor
+
+ /**
+ * PEAR_Command_Remote constructor.
+ *
+ * @access public
+ */
+ function PEAR_Command_Remote(&$ui, &$config)
+ {
+ parent::PEAR_Command_Common($ui, $config);
+ }
+
+ // }}}
+
+ // {{{ doRemoteInfo()
+
+ function doRemoteInfo($command, $options, $params)
+ {
+ if (sizeof($params) != 1) {
+ return $this->raiseError("$command expects one param: the remote package name");
+ }
+ $r = new PEAR_Remote($this->config);
+ $info = $r->call('package.info', $params[0]);
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $installed = $reg->packageInfo($info['name']);
+ $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
+
+ $this->ui->outputData($info, $command);
+
+ return true;
+ }
+
+ // }}}
+ // {{{ doRemoteList()
+
+ function doRemoteList($command, $options, $params)
+ {
+ $r = new PEAR_Remote($this->config);
+ $list_options = false;
+ if ($this->config->get('preferred_state') == 'stable')
+ $list_options = true;
+ $available = $r->call('package.listAll', $list_options);
+ if (PEAR::isError($available)) {
+ return $this->raiseError($available);
+ }
+ $i = $j = 0;
+ $data = array(
+ 'caption' => 'Available packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Version'),
+ );
+ foreach ($available as $name => $info) {
+ $data['data'][] = array($name, isset($info['stable']) ? $info['stable'] : '-n/a-');
+ }
+ if (count($available)==0) {
+ $data = '(no packages installed yet)';
+ }
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doListAll()
+
+ function doListAll($command, $options, $params)
+ {
+ $r = new PEAR_Remote($this->config);
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $list_options = false;
+ if ($this->config->get('preferred_state') == 'stable')
+ $list_options = true;
+ $available = $r->call('package.listAll', $list_options);
+ if (PEAR::isError($available)) {
+ return $this->raiseError($available);
+ }
+ if (!is_array($available)) {
+ return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "'.$available.'")');
+ }
+ $data = array(
+ 'caption' => 'All packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Latest', 'Local'),
+ );
+ $local_pkgs = $reg->listPackages();
+
+ foreach ($available as $name => $info) {
+ $installed = $reg->packageInfo($name);
+ $desc = $info['summary'];
+ if (isset($params[$name]))
+ $desc .= "\n\n".$info['description'];
+
+ if (isset($options['mode']))
+ {
+ if ($options['mode'] == 'installed' && !isset($installed['version']))
+ continue;
+ if ($options['mode'] == 'notinstalled' && isset($installed['version']))
+ continue;
+ if ($options['mode'] == 'upgrades'
+ && (!isset($installed['version']) || $installed['version'] == $info['stable']))
+ {
+ continue;
+ }
+ }
+ $pos = array_search(strtolower($name), $local_pkgs);
+ if ($pos !== false) {
+ unset($local_pkgs[$pos]);
+ }
+
+ $data['data'][$info['category']][] = array(
+ $name,
+ @$info['stable'],
+ @$installed['version'],
+ @$desc,
+ @$info['deps'],
+ );
+ }
+
+ foreach ($local_pkgs as $name) {
+ $info = $reg->packageInfo($name);
+ $data['data']['Local'][] = array(
+ $info['package'],
+ '',
+ $info['version'],
+ $info['summary'],
+ @$info['release_deps']
+ );
+ }
+
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doSearch()
+
+ function doSearch($command, $options, $params)
+ {
+ if ((!isset($params[0]) || empty($params[0]))
+ && (!isset($params[1]) || empty($params[1])))
+ {
+ return $this->raiseError('no valid search string supplied');
+ };
+
+ $r = new PEAR_Remote($this->config);
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $available = $r->call('package.listAll', true, false);
+ if (PEAR::isError($available)) {
+ return $this->raiseError($available);
+ }
+ $data = array(
+ 'caption' => 'Matched packages:',
+ 'border' => true,
+ 'headline' => array('Package', 'Stable/(Latest)', 'Local'),
+ );
+
+ foreach ($available as $name => $info) {
+ $found = (!empty($params[0]) && stristr($name, $params[0]) !== false);
+ if (!$found && !(isset($params[1]) && !empty($params[1])
+ && (stristr($info['summary'], $params[1]) !== false
+ || stristr($info['description'], $params[1]) !== false)))
+ {
+ continue;
+ };
+
+ $installed = $reg->packageInfo($name);
+ $desc = $info['summary'];
+ if (isset($params[$name]))
+ $desc .= "\n\n".$info['description'];
+
+ $unstable = '';
+ if ($info['unstable']) {
+ $unstable = '/(' . $info['unstable'] . $info['state'] . ')';
+ }
+ if (!isset($info['stable']) || !$info['stable']) {
+ $info['stable'] = 'none';
+ }
+ $data['data'][$info['category']][] = array(
+ $name,
+ $info['stable'] . $unstable,
+ $installed['version'],
+ $desc,
+ );
+ }
+ if (!isset($data['data'])) {
+ return $this->raiseError('no packages found');
+ }
+ $this->ui->outputData($data, $command);
+ return true;
+ }
+
+ // }}}
+ // {{{ doDownload()
+
+ function doDownload($command, $options, $params)
+ {
+ //$params[0] -> The package to download
+ if (count($params) != 1) {
+ return PEAR::raiseError("download expects one argument: the package to download");
+ }
+ $server = $this->config->get('master_server');
+ if (!ereg('^http://', $params[0])) {
+ $getoption = isset($options['nocompress'])&&$options['nocompress']==1?'?uncompress=on':'';
+ $pkgfile = "http://$server/get/$params[0]".$getoption;
+ } else {
+ $pkgfile = $params[0];
+ }
+ $this->bytes_downloaded = 0;
+ $saved = PEAR_Common::downloadHttp($pkgfile, $this->ui, '.',
+ array(&$this, 'downloadCallback'));
+ if (PEAR::isError($saved)) {
+ return $this->raiseError($saved);
+ }
+ $fname = basename($saved);
+ $this->ui->outputData("File $fname downloaded ($this->bytes_downloaded bytes)", $command);
+ return true;
+ }
+
+ function downloadCallback($msg, $params = null)
+ {
+ if ($msg == 'done') {
+ $this->bytes_downloaded = $params;
+ }
+ }
+
+ // }}}
+ // {{{ doListUpgrades()
+
+ function doListUpgrades($command, $options, $params)
+ {
+ include_once "PEAR/Registry.php";
+ $remote = new PEAR_Remote($this->config);
+ if (empty($params[0])) {
+ $state = $this->config->get('preferred_state');
+ } else {
+ $state = $params[0];
+ }
+ $caption = 'Available Upgrades';
+ if (empty($state) || $state == 'any') {
+ $latest = $remote->call("package.listLatestReleases");
+ } else {
+ $latest = $remote->call("package.listLatestReleases", $state);
+ $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
+ }
+ $caption .= ':';
+ if (PEAR::isError($latest)) {
+ return $latest;
+ }
+ $reg = new PEAR_Registry($this->config->get('php_dir'));
+ $inst = array_flip($reg->listPackages());
+ $data = array(
+ 'caption' => $caption,
+ 'border' => 1,
+ 'headline' => array('Package', 'Local', 'Remote', 'Size'),
+ );
+ foreach ((array)$latest as $pkg => $info) {
+ $package = strtolower($pkg);
+ if (!isset($inst[$package])) {
+ // skip packages we don't have installed
+ continue;
+ }
+ extract($info);
+ $pkginfo = $reg->packageInfo($package);
+ $inst_version = $pkginfo['version'];
+ $inst_state = $pkginfo['release_state'];
+ if (version_compare("$version", "$inst_version", "le")) {
+ // installed version is up-to-date
+ continue;
+ }
+ if ($filesize >= 20480) {
+ $filesize += 1024 - ($filesize % 1024);
+ $fs = sprintf("%dkB", $filesize / 1024);
+ } elseif ($filesize > 0) {
+ $filesize += 103 - ($filesize % 103);
+ $fs = sprintf("%.1fkB", $filesize / 1024.0);
+ } else {
+ $fs = " -"; // XXX center instead
+ }
+ $data['data'][] = array($pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
+ }
+ if (empty($data['data'])) {
+ $this->ui->outputData('No upgrades available');
+ } else {
+ $this->ui->outputData($data, $command);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ doClearCache()
+
+ function doClearCache($command, $options, $params)
+ {
+ $cache_dir = $this->config->get('cache_dir');
+ $verbose = $this->config->get('verbose');
+ $output = '';
+ if (!($dp = @opendir($cache_dir))) {
+ return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
+ }
+ if ($verbose >= 1) {
+ $output .= "reading directory $cache_dir\n";
+ }
+ $num = 0;
+ while ($ent = readdir($dp)) {
+ if (preg_match('/^xmlrpc_cache_[a-z0-9]{32}$/', $ent)) {
+ $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
+ $ok = @unlink($path);
+ if ($ok) {
+ if ($verbose >= 2) {
+ $output .= "deleted $path\n";
+ }
+ $num++;
+ } elseif ($verbose >= 1) {
+ $output .= "failed to delete $path\n";
+ }
+ }
+ }
+ closedir($dp);
+ if ($verbose >= 1) {
+ $output .= "$num cache entries cleared\n";
+ }
+ $this->ui->outputData(rtrim($output), $command);
+ return $num;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Common.php b/3dparty/PEAR/Common.php
new file mode 100644
index 00000000000..fba53a37799
--- /dev/null
+++ b/3dparty/PEAR/Common.php
@@ -0,0 +1,2094 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.126.2.2 2004/12/27 07:04:19 cellog Exp $
+
+require_once 'PEAR.php';
+require_once 'Archive/Tar.php';
+require_once 'System.php';
+require_once 'PEAR/Config.php';
+
+// {{{ constants and globals
+
+/**
+ * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
+ */
+define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
+define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
+define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/');
+
+// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
+define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
+define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i');
+
+// XXX far from perfect :-)
+define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?$/');
+
+/**
+ * List of temporary files and directories registered by
+ * PEAR_Common::addTempFile().
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_tempfiles'] = array();
+
+/**
+ * Valid maintainer roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
+
+/**
+ * Valid release states
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
+
+/**
+ * Valid dependency types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
+
+/**
+ * Valid dependency relations
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
+
+/**
+ * Valid file roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
+
+/**
+ * Valid replacement types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
+
+// }}}
+
+/**
+ * Class providing common functionality for PEAR administration classes.
+ * @deprecated This class will disappear, and its components will be spread
+ * into smaller classes, like the AT&T breakup
+ */
+class PEAR_Common extends PEAR
+{
+ // {{{ properties
+
+ /** stack of elements, gives some sort of XML context */
+ var $element_stack = array();
+
+ /** name of currently parsed XML element */
+ var $current_element;
+
+ /** array of attributes of the currently parsed XML element */
+ var $current_attributes = array();
+
+ /** assoc with information about a package */
+ var $pkginfo = array();
+
+ /**
+ * User Interface object (PEAR_Frontend_* class). If null,
+ * the log() method uses print.
+ * @var object
+ */
+ var $ui = null;
+
+ /**
+ * Configuration object (PEAR_Config).
+ * @var object
+ */
+ var $config = null;
+
+ var $current_path = null;
+
+ /**
+ * PEAR_SourceAnalyzer instance
+ * @var object
+ */
+ var $source_analyzer = null;
+ /**
+ * Flag variable used to mark a valid package file
+ * @var boolean
+ * @access private
+ */
+ var $_validPackageFile;
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Common constructor
+ *
+ * @access public
+ */
+ function PEAR_Common()
+ {
+ parent::PEAR();
+ $this->config = &PEAR_Config::singleton();
+ $this->debug = $this->config->get('verbose');
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * PEAR_Common destructor
+ *
+ * @access private
+ */
+ function _PEAR_Common()
+ {
+ // doesn't work due to bug #14744
+ //$tempfiles = $this->_tempfiles;
+ $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
+ while ($file = array_shift($tempfiles)) {
+ if (@is_dir($file)) {
+ System::rm(array('-rf', $file));
+ } elseif (file_exists($file)) {
+ unlink($file);
+ }
+ }
+ }
+
+ // }}}
+ // {{{ addTempFile()
+
+ /**
+ * Register a temporary file or directory. When the destructor is
+ * executed, all registered temporary files and directories are
+ * removed.
+ *
+ * @param string $file name of file or directory
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function addTempFile($file)
+ {
+ $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
+ }
+
+ // }}}
+ // {{{ mkDirHier()
+
+ /**
+ * Wrapper to System::mkDir(), creates a directory as well as
+ * any necessary parent directories.
+ *
+ * @param string $dir directory name
+ *
+ * @return bool TRUE on success, or a PEAR error
+ *
+ * @access public
+ */
+ function mkDirHier($dir)
+ {
+ $this->log(2, "+ create dir $dir");
+ return System::mkDir(array('-p', $dir));
+ }
+
+ // }}}
+ // {{{ log()
+
+ /**
+ * Logging method.
+ *
+ * @param int $level log level (0 is quiet, higher is noisier)
+ * @param string $msg message to write to the log
+ *
+ * @return void
+ *
+ * @access public
+ */
+ function log($level, $msg, $append_crlf = true)
+ {
+ if ($this->debug >= $level) {
+ if (is_object($this->ui)) {
+ $this->ui->log($msg, $append_crlf);
+ } else {
+ print "$msg\n";
+ }
+ }
+ }
+
+ // }}}
+ // {{{ mkTempDir()
+
+ /**
+ * Create and register a temporary directory.
+ *
+ * @param string $tmpdir (optional) Directory to use as tmpdir.
+ * Will use system defaults (for example
+ * /tmp or c:\windows\temp) if not specified
+ *
+ * @return string name of created directory
+ *
+ * @access public
+ */
+ function mkTempDir($tmpdir = '')
+ {
+ if ($tmpdir) {
+ $topt = array('-t', $tmpdir);
+ } else {
+ $topt = array();
+ }
+ $topt = array_merge($topt, array('-d', 'pear'));
+ if (!$tmpdir = System::mktemp($topt)) {
+ return false;
+ }
+ $this->addTempFile($tmpdir);
+ return $tmpdir;
+ }
+
+ // }}}
+ // {{{ setFrontendObject()
+
+ /**
+ * Set object that represents the frontend to be used.
+ *
+ * @param object Reference of the frontend object
+ * @return void
+ * @access public
+ */
+ function setFrontendObject(&$ui)
+ {
+ $this->ui = &$ui;
+ }
+
+ // }}}
+
+ // {{{ _unIndent()
+
+ /**
+ * Unindent given string (?)
+ *
+ * @param string $str The string that has to be unindented.
+ * @return string
+ * @access private
+ */
+ function _unIndent($str)
+ {
+ // remove leading newlines
+ $str = preg_replace('/^[\r\n]+/', '', $str);
+ // find whitespace at the beginning of the first line
+ $indent_len = strspn($str, " \t");
+ $indent = substr($str, 0, $indent_len);
+ $data = '';
+ // remove the same amount of whitespace from following lines
+ foreach (explode("\n", $str) as $line) {
+ if (substr($line, 0, $indent_len) == $indent) {
+ $data .= substr($line, $indent_len) . "\n";
+ }
+ }
+ return $data;
+ }
+
+ // }}}
+ // {{{ _element_start()
+
+ /**
+ * XML parser callback for starting elements. Used while package
+ * format version is not yet known.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of starting element
+ * @param array $attribs element attributes, name => value
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_start($xp, $name, $attribs)
+ {
+ array_push($this->element_stack, $name);
+ $this->current_element = $name;
+ $spos = sizeof($this->element_stack) - 2;
+ $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+ $this->current_attributes = $attribs;
+ switch ($name) {
+ case 'package': {
+ $this->_validPackageFile = true;
+ if (isset($attribs['version'])) {
+ $vs = preg_replace('/[^0-9a-z]/', '_', $attribs['version']);
+ } else {
+ $vs = '1_0';
+ }
+ $elem_start = '_element_start_'. $vs;
+ $elem_end = '_element_end_'. $vs;
+ $cdata = '_pkginfo_cdata_'. $vs;
+ if (!method_exists($this, $elem_start) ||
+ !method_exists($this, $elem_end) ||
+ !method_exists($this, $cdata)) {
+ $this->raiseError("No handlers for package.xml version $attribs[version]");
+ return;
+ }
+ xml_set_element_handler($xp, $elem_start, $elem_end);
+ xml_set_character_data_handler($xp, $cdata);
+ break;
+ }
+ }
+ }
+
+ // }}}
+ // {{{ _element_end()
+
+ /**
+ * XML parser callback for ending elements. Used while package
+ * format version is not yet known.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of ending element
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_end($xp, $name)
+ {
+ }
+
+ // }}}
+
+ // Support for package DTD v1.0:
+ // {{{ _element_start_1_0()
+
+ /**
+ * XML parser callback for ending elements. Used for version 1.0
+ * packages.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of ending element
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_start_1_0($xp, $name, $attribs)
+ {
+ array_push($this->element_stack, $name);
+ $this->current_element = $name;
+ $spos = sizeof($this->element_stack) - 2;
+ $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+ $this->current_attributes = $attribs;
+ $this->cdata = '';
+ switch ($name) {
+ case 'dir':
+ if ($this->in_changelog) {
+ break;
+ }
+ if ($attribs['name'] != '/') {
+ $this->dir_names[] = $attribs['name'];
+ }
+ if (isset($attribs['baseinstalldir'])) {
+ $this->dir_install = $attribs['baseinstalldir'];
+ }
+ if (isset($attribs['role'])) {
+ $this->dir_role = $attribs['role'];
+ }
+ break;
+ case 'file':
+ if ($this->in_changelog) {
+ break;
+ }
+ if (isset($attribs['name'])) {
+ $path = '';
+ if (count($this->dir_names)) {
+ foreach ($this->dir_names as $dir) {
+ $path .= $dir . DIRECTORY_SEPARATOR;
+ }
+ }
+ $path .= $attribs['name'];
+ unset($attribs['name']);
+ $this->current_path = $path;
+ $this->filelist[$path] = $attribs;
+ // Set the baseinstalldir only if the file don't have this attrib
+ if (!isset($this->filelist[$path]['baseinstalldir']) &&
+ isset($this->dir_install))
+ {
+ $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+ }
+ // Set the Role
+ if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+ $this->filelist[$path]['role'] = $this->dir_role;
+ }
+ }
+ break;
+ case 'replace':
+ if (!$this->in_changelog) {
+ $this->filelist[$this->current_path]['replacements'][] = $attribs;
+ }
+ break;
+ case 'maintainers':
+ $this->pkginfo['maintainers'] = array();
+ $this->m_i = 0; // maintainers array index
+ break;
+ case 'maintainer':
+ // compatibility check
+ if (!isset($this->pkginfo['maintainers'])) {
+ $this->pkginfo['maintainers'] = array();
+ $this->m_i = 0;
+ }
+ $this->pkginfo['maintainers'][$this->m_i] = array();
+ $this->current_maintainer =& $this->pkginfo['maintainers'][$this->m_i];
+ break;
+ case 'changelog':
+ $this->pkginfo['changelog'] = array();
+ $this->c_i = 0; // changelog array index
+ $this->in_changelog = true;
+ break;
+ case 'release':
+ if ($this->in_changelog) {
+ $this->pkginfo['changelog'][$this->c_i] = array();
+ $this->current_release = &$this->pkginfo['changelog'][$this->c_i];
+ } else {
+ $this->current_release = &$this->pkginfo;
+ }
+ break;
+ case 'deps':
+ if (!$this->in_changelog) {
+ $this->pkginfo['release_deps'] = array();
+ }
+ break;
+ case 'dep':
+ // dependencies array index
+ if (!$this->in_changelog) {
+ $this->d_i++;
+ $this->pkginfo['release_deps'][$this->d_i] = $attribs;
+ }
+ break;
+ case 'configureoptions':
+ if (!$this->in_changelog) {
+ $this->pkginfo['configure_options'] = array();
+ }
+ break;
+ case 'configureoption':
+ if (!$this->in_changelog) {
+ $this->pkginfo['configure_options'][] = $attribs;
+ }
+ break;
+ case 'provides':
+ if (empty($attribs['type']) || empty($attribs['name'])) {
+ break;
+ }
+ $attribs['explicit'] = true;
+ $this->pkginfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
+ break;
+ }
+ }
+
+ // }}}
+ // {{{ _element_end_1_0()
+
+ /**
+ * XML parser callback for ending elements. Used for version 1.0
+ * packages.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name name of ending element
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _element_end_1_0($xp, $name)
+ {
+ $data = trim($this->cdata);
+ switch ($name) {
+ case 'name':
+ switch ($this->prev_element) {
+ case 'package':
+ // XXX should we check the package name here?
+ $this->pkginfo['package'] = ereg_replace('[^a-zA-Z0-9._]', '_', $data);
+ break;
+ case 'maintainer':
+ $this->current_maintainer['name'] = $data;
+ break;
+ }
+ break;
+ case 'summary':
+ $this->pkginfo['summary'] = $data;
+ break;
+ case 'description':
+ $data = $this->_unIndent($this->cdata);
+ $this->pkginfo['description'] = $data;
+ break;
+ case 'user':
+ $this->current_maintainer['handle'] = $data;
+ break;
+ case 'email':
+ $this->current_maintainer['email'] = $data;
+ break;
+ case 'role':
+ $this->current_maintainer['role'] = $data;
+ break;
+ case 'version':
+ $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
+ if ($this->in_changelog) {
+ $this->current_release['version'] = $data;
+ } else {
+ $this->pkginfo['version'] = $data;
+ }
+ break;
+ case 'date':
+ if ($this->in_changelog) {
+ $this->current_release['release_date'] = $data;
+ } else {
+ $this->pkginfo['release_date'] = $data;
+ }
+ break;
+ case 'notes':
+ // try to "de-indent" release notes in case someone
+ // has been over-indenting their xml ;-)
+ $data = $this->_unIndent($this->cdata);
+ if ($this->in_changelog) {
+ $this->current_release['release_notes'] = $data;
+ } else {
+ $this->pkginfo['release_notes'] = $data;
+ }
+ break;
+ case 'warnings':
+ if ($this->in_changelog) {
+ $this->current_release['release_warnings'] = $data;
+ } else {
+ $this->pkginfo['release_warnings'] = $data;
+ }
+ break;
+ case 'state':
+ if ($this->in_changelog) {
+ $this->current_release['release_state'] = $data;
+ } else {
+ $this->pkginfo['release_state'] = $data;
+ }
+ break;
+ case 'license':
+ if ($this->in_changelog) {
+ $this->current_release['release_license'] = $data;
+ } else {
+ $this->pkginfo['release_license'] = $data;
+ }
+ break;
+ case 'dep':
+ if ($data && !$this->in_changelog) {
+ $this->pkginfo['release_deps'][$this->d_i]['name'] = $data;
+ }
+ break;
+ case 'dir':
+ if ($this->in_changelog) {
+ break;
+ }
+ array_pop($this->dir_names);
+ break;
+ case 'file':
+ if ($this->in_changelog) {
+ break;
+ }
+ if ($data) {
+ $path = '';
+ if (count($this->dir_names)) {
+ foreach ($this->dir_names as $dir) {
+ $path .= $dir . DIRECTORY_SEPARATOR;
+ }
+ }
+ $path .= $data;
+ $this->filelist[$path] = $this->current_attributes;
+ // Set the baseinstalldir only if the file don't have this attrib
+ if (!isset($this->filelist[$path]['baseinstalldir']) &&
+ isset($this->dir_install))
+ {
+ $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+ }
+ // Set the Role
+ if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+ $this->filelist[$path]['role'] = $this->dir_role;
+ }
+ }
+ break;
+ case 'maintainer':
+ if (empty($this->pkginfo['maintainers'][$this->m_i]['role'])) {
+ $this->pkginfo['maintainers'][$this->m_i]['role'] = 'lead';
+ }
+ $this->m_i++;
+ break;
+ case 'release':
+ if ($this->in_changelog) {
+ $this->c_i++;
+ }
+ break;
+ case 'changelog':
+ $this->in_changelog = false;
+ break;
+ }
+ array_pop($this->element_stack);
+ $spos = sizeof($this->element_stack) - 1;
+ $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
+ $this->cdata = '';
+ }
+
+ // }}}
+ // {{{ _pkginfo_cdata_1_0()
+
+ /**
+ * XML parser callback for character data. Used for version 1.0
+ * packages.
+ *
+ * @param resource $xp XML parser resource
+ * @param string $name character data
+ *
+ * @return void
+ *
+ * @access private
+ */
+ function _pkginfo_cdata_1_0($xp, $data)
+ {
+ if (isset($this->cdata)) {
+ $this->cdata .= $data;
+ }
+ }
+
+ // }}}
+
+ // {{{ infoFromTgzFile()
+
+ /**
+ * Returns information about a package file. Expects the name of
+ * a gzipped tar file as input.
+ *
+ * @param string $file name of .tgz file
+ *
+ * @return array array with package information
+ *
+ * @access public
+ *
+ */
+ function infoFromTgzFile($file)
+ {
+ if (!@is_file($file)) {
+ return $this->raiseError("could not open file \"$file\"");
+ }
+ $tar = new Archive_Tar($file);
+ if ($this->debug <= 1) {
+ $tar->pushErrorHandling(PEAR_ERROR_RETURN);
+ }
+ $content = $tar->listContent();
+ if ($this->debug <= 1) {
+ $tar->popErrorHandling();
+ }
+ if (!is_array($content)) {
+ $file = realpath($file);
+ return $this->raiseError("Could not get contents of package \"$file\"".
+ '. Invalid tgz file.');
+ }
+ $xml = null;
+ foreach ($content as $file) {
+ $name = $file['filename'];
+ if ($name == 'package.xml') {
+ $xml = $name;
+ break;
+ } elseif (ereg('package.xml$', $name, $match)) {
+ $xml = $match[0];
+ break;
+ }
+ }
+ $tmpdir = System::mkTemp(array('-d', 'pear'));
+ $this->addTempFile($tmpdir);
+ if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
+ return $this->raiseError('could not extract the package.xml file');
+ }
+ return $this->infoFromDescriptionFile("$tmpdir/$xml");
+ }
+
+ // }}}
+ // {{{ infoFromDescriptionFile()
+
+ /**
+ * Returns information about a package file. Expects the name of
+ * a package xml file as input.
+ *
+ * @param string $descfile name of package xml file
+ *
+ * @return array array with package information
+ *
+ * @access public
+ *
+ */
+ function infoFromDescriptionFile($descfile)
+ {
+ if (!@is_file($descfile) || !is_readable($descfile) ||
+ (!$fp = @fopen($descfile, 'r'))) {
+ return $this->raiseError("Unable to open $descfile");
+ }
+
+ // read the whole thing so we only get one cdata callback
+ // for each block of cdata
+ $data = fread($fp, filesize($descfile));
+ return $this->infoFromString($data);
+ }
+
+ // }}}
+ // {{{ infoFromString()
+
+ /**
+ * Returns information about a package file. Expects the contents
+ * of a package xml file as input.
+ *
+ * @param string $data name of package xml file
+ *
+ * @return array array with package information
+ *
+ * @access public
+ *
+ */
+ function infoFromString($data)
+ {
+ require_once('PEAR/Dependency.php');
+ if (PEAR_Dependency::checkExtension($error, 'xml')) {
+ return $this->raiseError($error);
+ }
+ $xp = @xml_parser_create();
+ if (!$xp) {
+ return $this->raiseError('Unable to create XML parser');
+ }
+ xml_set_object($xp, $this);
+ xml_set_element_handler($xp, '_element_start', '_element_end');
+ xml_set_character_data_handler($xp, '_pkginfo_cdata');
+ xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
+
+ $this->element_stack = array();
+ $this->pkginfo = array('provides' => array());
+ $this->current_element = false;
+ unset($this->dir_install);
+ $this->pkginfo['filelist'] = array();
+ $this->filelist =& $this->pkginfo['filelist'];
+ $this->dir_names = array();
+ $this->in_changelog = false;
+ $this->d_i = 0;
+ $this->cdata = '';
+ $this->_validPackageFile = false;
+
+ if (!xml_parse($xp, $data, 1)) {
+ $code = xml_get_error_code($xp);
+ $msg = sprintf("XML error: %s at line %d",
+ xml_error_string($code),
+ xml_get_current_line_number($xp));
+ xml_parser_free($xp);
+ return $this->raiseError($msg, $code);
+ }
+
+ xml_parser_free($xp);
+
+ if (!$this->_validPackageFile) {
+ return $this->raiseError('Invalid Package File, no <package> tag');
+ }
+ foreach ($this->pkginfo as $k => $v) {
+ if (!is_array($v)) {
+ $this->pkginfo[$k] = trim($v);
+ }
+ }
+ return $this->pkginfo;
+ }
+ // }}}
+ // {{{ infoFromAny()
+
+ /**
+ * Returns package information from different sources
+ *
+ * This method is able to extract information about a package
+ * from a .tgz archive or from a XML package definition file.
+ *
+ * @access public
+ * @param string Filename of the source ('package.xml', '<package>.tgz')
+ * @return string
+ */
+ function infoFromAny($info)
+ {
+ if (is_string($info) && file_exists($info)) {
+ $tmp = substr($info, -4);
+ if ($tmp == '.xml') {
+ $info = $this->infoFromDescriptionFile($info);
+ } elseif ($tmp == '.tar' || $tmp == '.tgz') {
+ $info = $this->infoFromTgzFile($info);
+ } else {
+ $fp = fopen($info, "r");
+ $test = fread($fp, 5);
+ fclose($fp);
+ if ($test == "<?xml") {
+ $info = $this->infoFromDescriptionFile($info);
+ } else {
+ $info = $this->infoFromTgzFile($info);
+ }
+ }
+ if (PEAR::isError($info)) {
+ return $this->raiseError($info);
+ }
+ }
+ return $info;
+ }
+
+ // }}}
+ // {{{ xmlFromInfo()
+
+ /**
+ * Return an XML document based on the package info (as returned
+ * by the PEAR_Common::infoFrom* methods).
+ *
+ * @param array $pkginfo package info
+ *
+ * @return string XML data
+ *
+ * @access public
+ */
+ function xmlFromInfo($pkginfo)
+ {
+ static $maint_map = array(
+ "handle" => "user",
+ "name" => "name",
+ "email" => "email",
+ "role" => "role",
+ );
+ $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
+ $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
+ $ret .= "<package version=\"1.0\">
+ <name>$pkginfo[package]</name>
+ <summary>".htmlspecialchars($pkginfo['summary'])."</summary>
+ <description>".htmlspecialchars($pkginfo['description'])."</description>
+ <maintainers>
+";
+ foreach ($pkginfo['maintainers'] as $maint) {
+ $ret .= " <maintainer>\n";
+ foreach ($maint_map as $idx => $elm) {
+ $ret .= " <$elm>";
+ $ret .= htmlspecialchars($maint[$idx]);
+ $ret .= "</$elm>\n";
+ }
+ $ret .= " </maintainer>\n";
+ }
+ $ret .= " </maintainers>\n";
+ $ret .= $this->_makeReleaseXml($pkginfo);
+ if (@sizeof($pkginfo['changelog']) > 0) {
+ $ret .= " <changelog>\n";
+ foreach ($pkginfo['changelog'] as $oldrelease) {
+ $ret .= $this->_makeReleaseXml($oldrelease, true);
+ }
+ $ret .= " </changelog>\n";
+ }
+ $ret .= "</package>\n";
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _makeReleaseXml()
+
+ /**
+ * Generate part of an XML description with release information.
+ *
+ * @param array $pkginfo array with release information
+ * @param bool $changelog whether the result will be in a changelog element
+ *
+ * @return string XML data
+ *
+ * @access private
+ */
+ function _makeReleaseXml($pkginfo, $changelog = false)
+ {
+ // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
+ $indent = $changelog ? " " : "";
+ $ret = "$indent <release>\n";
+ if (!empty($pkginfo['version'])) {
+ $ret .= "$indent <version>$pkginfo[version]</version>\n";
+ }
+ if (!empty($pkginfo['release_date'])) {
+ $ret .= "$indent <date>$pkginfo[release_date]</date>\n";
+ }
+ if (!empty($pkginfo['release_license'])) {
+ $ret .= "$indent <license>$pkginfo[release_license]</license>\n";
+ }
+ if (!empty($pkginfo['release_state'])) {
+ $ret .= "$indent <state>$pkginfo[release_state]</state>\n";
+ }
+ if (!empty($pkginfo['release_notes'])) {
+ $ret .= "$indent <notes>".htmlspecialchars($pkginfo['release_notes'])."</notes>\n";
+ }
+ if (!empty($pkginfo['release_warnings'])) {
+ $ret .= "$indent <warnings>".htmlspecialchars($pkginfo['release_warnings'])."</warnings>\n";
+ }
+ if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
+ $ret .= "$indent <deps>\n";
+ foreach ($pkginfo['release_deps'] as $dep) {
+ $ret .= "$indent <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
+ if (isset($dep['version'])) {
+ $ret .= " version=\"$dep[version]\"";
+ }
+ if (isset($dep['optional'])) {
+ $ret .= " optional=\"$dep[optional]\"";
+ }
+ if (isset($dep['name'])) {
+ $ret .= ">$dep[name]</dep>\n";
+ } else {
+ $ret .= "/>\n";
+ }
+ }
+ $ret .= "$indent </deps>\n";
+ }
+ if (isset($pkginfo['configure_options'])) {
+ $ret .= "$indent <configureoptions>\n";
+ foreach ($pkginfo['configure_options'] as $c) {
+ $ret .= "$indent <configureoption name=\"".
+ htmlspecialchars($c['name']) . "\"";
+ if (isset($c['default'])) {
+ $ret .= " default=\"" . htmlspecialchars($c['default']) . "\"";
+ }
+ $ret .= " prompt=\"" . htmlspecialchars($c['prompt']) . "\"";
+ $ret .= "/>\n";
+ }
+ $ret .= "$indent </configureoptions>\n";
+ }
+ if (isset($pkginfo['provides'])) {
+ foreach ($pkginfo['provides'] as $key => $what) {
+ $ret .= "$indent <provides type=\"$what[type]\" ";
+ $ret .= "name=\"$what[name]\" ";
+ if (isset($what['extends'])) {
+ $ret .= "extends=\"$what[extends]\" ";
+ }
+ $ret .= "/>\n";
+ }
+ }
+ if (isset($pkginfo['filelist'])) {
+ $ret .= "$indent <filelist>\n";
+ foreach ($pkginfo['filelist'] as $file => $fa) {
+ @$ret .= "$indent <file role=\"$fa[role]\"";
+ if (isset($fa['baseinstalldir'])) {
+ $ret .= ' baseinstalldir="' .
+ htmlspecialchars($fa['baseinstalldir']) . '"';
+ }
+ if (isset($fa['md5sum'])) {
+ $ret .= " md5sum=\"$fa[md5sum]\"";
+ }
+ if (isset($fa['platform'])) {
+ $ret .= " platform=\"$fa[platform]\"";
+ }
+ if (!empty($fa['install-as'])) {
+ $ret .= ' install-as="' .
+ htmlspecialchars($fa['install-as']) . '"';
+ }
+ $ret .= ' name="' . htmlspecialchars($file) . '"';
+ if (empty($fa['replacements'])) {
+ $ret .= "/>\n";
+ } else {
+ $ret .= ">\n";
+ foreach ($fa['replacements'] as $r) {
+ $ret .= "$indent <replace";
+ foreach ($r as $k => $v) {
+ $ret .= " $k=\"" . htmlspecialchars($v) .'"';
+ }
+ $ret .= "/>\n";
+ }
+ @$ret .= "$indent </file>\n";
+ }
+ }
+ $ret .= "$indent </filelist>\n";
+ }
+ $ret .= "$indent </release>\n";
+ return $ret;
+ }
+
+ // }}}
+ // {{{ validatePackageInfo()
+
+ /**
+ * Validate XML package definition file.
+ *
+ * @param string $info Filename of the package archive or of the
+ * package definition file
+ * @param array $errors Array that will contain the errors
+ * @param array $warnings Array that will contain the warnings
+ * @param string $dir_prefix (optional) directory where source files
+ * may be found, or empty if they are not available
+ * @access public
+ * @return boolean
+ */
+ function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
+ {
+ if (PEAR::isError($info = $this->infoFromAny($info))) {
+ return $this->raiseError($info);
+ }
+ if (!is_array($info)) {
+ return false;
+ }
+
+ $errors = array();
+ $warnings = array();
+ if (!isset($info['package'])) {
+ $errors[] = 'missing package name';
+ } elseif (!$this->validPackageName($info['package'])) {
+ $errors[] = 'invalid package name';
+ }
+ $this->_packageName = $pn = $info['package'];
+
+ if (empty($info['summary'])) {
+ $errors[] = 'missing summary';
+ } elseif (strpos(trim($info['summary']), "\n") !== false) {
+ $warnings[] = 'summary should be on a single line';
+ }
+ if (empty($info['description'])) {
+ $errors[] = 'missing description';
+ }
+ if (empty($info['release_license'])) {
+ $errors[] = 'missing license';
+ }
+ if (!isset($info['version'])) {
+ $errors[] = 'missing version';
+ } elseif (!$this->validPackageVersion($info['version'])) {
+ $errors[] = 'invalid package release version';
+ }
+ if (empty($info['release_state'])) {
+ $errors[] = 'missing release state';
+ } elseif (!in_array($info['release_state'], PEAR_Common::getReleaseStates())) {
+ $errors[] = "invalid release state `$info[release_state]', should be one of: "
+ . implode(' ', PEAR_Common::getReleaseStates());
+ }
+ if (empty($info['release_date'])) {
+ $errors[] = 'missing release date';
+ } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) {
+ $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD";
+ }
+ if (empty($info['release_notes'])) {
+ $errors[] = "missing release notes";
+ }
+ if (empty($info['maintainers'])) {
+ $errors[] = 'no maintainer(s)';
+ } else {
+ $i = 1;
+ foreach ($info['maintainers'] as $m) {
+ if (empty($m['handle'])) {
+ $errors[] = "maintainer $i: missing handle";
+ }
+ if (empty($m['role'])) {
+ $errors[] = "maintainer $i: missing role";
+ } elseif (!in_array($m['role'], PEAR_Common::getUserRoles())) {
+ $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: "
+ . implode(' ', PEAR_Common::getUserRoles());
+ }
+ if (empty($m['name'])) {
+ $errors[] = "maintainer $i: missing name";
+ }
+ if (empty($m['email'])) {
+ $errors[] = "maintainer $i: missing email";
+ }
+ $i++;
+ }
+ }
+ if (!empty($info['release_deps'])) {
+ $i = 1;
+ foreach ($info['release_deps'] as $d) {
+ if (empty($d['type'])) {
+ $errors[] = "dependency $i: missing type";
+ } elseif (!in_array($d['type'], PEAR_Common::getDependencyTypes())) {
+ $errors[] = "dependency $i: invalid type '$d[type]', should be one of: " .
+ implode(' ', PEAR_Common::getDependencyTypes());
+ }
+ if (empty($d['rel'])) {
+ $errors[] = "dependency $i: missing relation";
+ } elseif (!in_array($d['rel'], PEAR_Common::getDependencyRelations())) {
+ $errors[] = "dependency $i: invalid relation '$d[rel]', should be one of: "
+ . implode(' ', PEAR_Common::getDependencyRelations());
+ }
+ if (!empty($d['optional'])) {
+ if (!in_array($d['optional'], array('yes', 'no'))) {
+ $errors[] = "dependency $i: invalid relation optional attribute '$d[optional]', should be one of: yes no";
+ } else {
+ if (($d['rel'] == 'not' || $d['rel'] == 'ne') && $d['optional'] == 'yes') {
+ $errors[] = "dependency $i: 'not' and 'ne' dependencies cannot be " .
+ "optional";
+ }
+ }
+ }
+ if ($d['rel'] != 'not' && $d['rel'] != 'has' && empty($d['version'])) {
+ $warnings[] = "dependency $i: missing version";
+ } elseif (($d['rel'] == 'not' || $d['rel'] == 'has') && !empty($d['version'])) {
+ $warnings[] = "dependency $i: version ignored for `$d[rel]' dependencies";
+ }
+ if ($d['rel'] == 'not' && !empty($d['version'])) {
+ $warnings[] = "dependency $i: 'not' defines a total conflict, to exclude " .
+ "specific versions, use 'ne'";
+ }
+ if ($d['type'] == 'php' && !empty($d['name'])) {
+ $warnings[] = "dependency $i: name ignored for php type dependencies";
+ } elseif ($d['type'] != 'php' && empty($d['name'])) {
+ $errors[] = "dependency $i: missing name";
+ }
+ if ($d['type'] == 'php' && $d['rel'] == 'not') {
+ $errors[] = "dependency $i: PHP dependencies cannot use 'not' " .
+ "rel, use 'ne' to exclude versions";
+ }
+ $i++;
+ }
+ }
+ if (!empty($info['configure_options'])) {
+ $i = 1;
+ foreach ($info['configure_options'] as $c) {
+ if (empty($c['name'])) {
+ $errors[] = "configure option $i: missing name";
+ }
+ if (empty($c['prompt'])) {
+ $errors[] = "configure option $i: missing prompt";
+ }
+ $i++;
+ }
+ }
+ if (empty($info['filelist'])) {
+ $errors[] = 'no files';
+ } else {
+ foreach ($info['filelist'] as $file => $fa) {
+ if (empty($fa['role'])) {
+ $errors[] = "file $file: missing role";
+ continue;
+ } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
+ $errors[] = "file $file: invalid role, should be one of: "
+ . implode(' ', PEAR_Common::getFileRoles());
+ }
+ if ($fa['role'] == 'php' && $dir_prefix) {
+ $this->log(1, "Analyzing $file");
+ $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
+ if ($srcinfo) {
+ $this->buildProvidesArray($srcinfo);
+ }
+ }
+
+ // (ssb) Any checks we can do for baseinstalldir?
+ // (cox) Perhaps checks that either the target dir and
+ // baseInstall doesn't cointain "../../"
+ }
+ }
+ $this->_packageName = $pn = $info['package'];
+ $pnl = strlen($pn);
+ foreach ((array)$this->pkginfo['provides'] as $key => $what) {
+ if (isset($what['explicit'])) {
+ // skip conformance checks if the provides entry is
+ // specified in the package.xml file
+ continue;
+ }
+ extract($what);
+ if ($type == 'class') {
+ if (!strncasecmp($name, $pn, $pnl)) {
+ continue;
+ }
+ $warnings[] = "in $file: class \"$name\" not prefixed with package name \"$pn\"";
+ } elseif ($type == 'function') {
+ if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
+ continue;
+ }
+ $warnings[] = "in $file: function \"$name\" not prefixed with package name \"$pn\"";
+ }
+ }
+
+
+ return true;
+ }
+
+ // }}}
+ // {{{ buildProvidesArray()
+
+ /**
+ * Build a "provides" array from data returned by
+ * analyzeSourceCode(). The format of the built array is like
+ * this:
+ *
+ * array(
+ * 'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
+ * ...
+ * )
+ *
+ *
+ * @param array $srcinfo array with information about a source file
+ * as returned by the analyzeSourceCode() method.
+ *
+ * @return void
+ *
+ * @access public
+ *
+ */
+ function buildProvidesArray($srcinfo)
+ {
+ $file = basename($srcinfo['source_file']);
+ $pn = '';
+ if (isset($this->_packageName)) {
+ $pn = $this->_packageName;
+ }
+ $pnl = strlen($pn);
+ foreach ($srcinfo['declared_classes'] as $class) {
+ $key = "class;$class";
+ if (isset($this->pkginfo['provides'][$key])) {
+ continue;
+ }
+ $this->pkginfo['provides'][$key] =
+ array('file'=> $file, 'type' => 'class', 'name' => $class);
+ if (isset($srcinfo['inheritance'][$class])) {
+ $this->pkginfo['provides'][$key]['extends'] =
+ $srcinfo['inheritance'][$class];
+ }
+ }
+ foreach ($srcinfo['declared_methods'] as $class => $methods) {
+ foreach ($methods as $method) {
+ $function = "$class::$method";
+ $key = "function;$function";
+ if ($method{0} == '_' || !strcasecmp($method, $class) ||
+ isset($this->pkginfo['provides'][$key])) {
+ continue;
+ }
+ $this->pkginfo['provides'][$key] =
+ array('file'=> $file, 'type' => 'function', 'name' => $function);
+ }
+ }
+
+ foreach ($srcinfo['declared_functions'] as $function) {
+ $key = "function;$function";
+ if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
+ continue;
+ }
+ if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
+ $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
+ }
+ $this->pkginfo['provides'][$key] =
+ array('file'=> $file, 'type' => 'function', 'name' => $function);
+ }
+ }
+
+ // }}}
+ // {{{ analyzeSourceCode()
+
+ /**
+ * Analyze the source code of the given PHP file
+ *
+ * @param string Filename of the PHP file
+ * @return mixed
+ * @access public
+ */
+ function analyzeSourceCode($file)
+ {
+ if (!function_exists("token_get_all")) {
+ return false;
+ }
+ if (!defined('T_DOC_COMMENT')) {
+ define('T_DOC_COMMENT', T_COMMENT);
+ }
+ if (!defined('T_INTERFACE')) {
+ define('T_INTERFACE', -1);
+ }
+ if (!defined('T_IMPLEMENTS')) {
+ define('T_IMPLEMENTS', -1);
+ }
+ if (!$fp = @fopen($file, "r")) {
+ return false;
+ }
+ $contents = fread($fp, filesize($file));
+ $tokens = token_get_all($contents);
+/*
+ for ($i = 0; $i < sizeof($tokens); $i++) {
+ @list($token, $data) = $tokens[$i];
+ if (is_string($token)) {
+ var_dump($token);
+ } else {
+ print token_name($token) . ' ';
+ var_dump(rtrim($data));
+ }
+ }
+*/
+ $look_for = 0;
+ $paren_level = 0;
+ $bracket_level = 0;
+ $brace_level = 0;
+ $lastphpdoc = '';
+ $current_class = '';
+ $current_interface = '';
+ $current_class_level = -1;
+ $current_function = '';
+ $current_function_level = -1;
+ $declared_classes = array();
+ $declared_interfaces = array();
+ $declared_functions = array();
+ $declared_methods = array();
+ $used_classes = array();
+ $used_functions = array();
+ $extends = array();
+ $implements = array();
+ $nodeps = array();
+ $inquote = false;
+ $interface = false;
+ for ($i = 0; $i < sizeof($tokens); $i++) {
+ if (is_array($tokens[$i])) {
+ list($token, $data) = $tokens[$i];
+ } else {
+ $token = $tokens[$i];
+ $data = '';
+ }
+ if ($inquote) {
+ if ($token != '"') {
+ continue;
+ } else {
+ $inquote = false;
+ }
+ }
+ switch ($token) {
+ case T_WHITESPACE:
+ continue;
+ case ';':
+ if ($interface) {
+ $current_function = '';
+ $current_function_level = -1;
+ }
+ break;
+ case '"':
+ $inquote = true;
+ break;
+ case T_CURLY_OPEN:
+ case T_DOLLAR_OPEN_CURLY_BRACES:
+ case '{': $brace_level++; continue 2;
+ case '}':
+ $brace_level--;
+ if ($current_class_level == $brace_level) {
+ $current_class = '';
+ $current_class_level = -1;
+ }
+ if ($current_function_level == $brace_level) {
+ $current_function = '';
+ $current_function_level = -1;
+ }
+ continue 2;
+ case '[': $bracket_level++; continue 2;
+ case ']': $bracket_level--; continue 2;
+ case '(': $paren_level++; continue 2;
+ case ')': $paren_level--; continue 2;
+ case T_INTERFACE:
+ $interface = true;
+ case T_CLASS:
+ if (($current_class_level != -1) || ($current_function_level != -1)) {
+ PEAR::raiseError("Parser error: Invalid PHP file $file",
+ PEAR_COMMON_ERROR_INVALIDPHP);
+ return false;
+ }
+ case T_FUNCTION:
+ case T_NEW:
+ case T_EXTENDS:
+ case T_IMPLEMENTS:
+ $look_for = $token;
+ continue 2;
+ case T_STRING:
+ if (version_compare(zend_version(), '2.0', '<')) {
+ if (in_array(strtolower($data),
+ array('public', 'private', 'protected', 'abstract',
+ 'interface', 'implements', 'clone', 'throw')
+ )) {
+ PEAR::raiseError('Error: PHP5 packages must be packaged by php 5 PEAR');
+ return false;
+ }
+ }
+ if ($look_for == T_CLASS) {
+ $current_class = $data;
+ $current_class_level = $brace_level;
+ $declared_classes[] = $current_class;
+ } elseif ($look_for == T_INTERFACE) {
+ $current_interface = $data;
+ $current_class_level = $brace_level;
+ $declared_interfaces[] = $current_interface;
+ } elseif ($look_for == T_IMPLEMENTS) {
+ $implements[$current_class] = $data;
+ } elseif ($look_for == T_EXTENDS) {
+ $extends[$current_class] = $data;
+ } elseif ($look_for == T_FUNCTION) {
+ if ($current_class) {
+ $current_function = "$current_class::$data";
+ $declared_methods[$current_class][] = $data;
+ } elseif ($current_interface) {
+ $current_function = "$current_interface::$data";
+ $declared_methods[$current_interface][] = $data;
+ } else {
+ $current_function = $data;
+ $declared_functions[] = $current_function;
+ }
+ $current_function_level = $brace_level;
+ $m = array();
+ } elseif ($look_for == T_NEW) {
+ $used_classes[$data] = true;
+ }
+ $look_for = 0;
+ continue 2;
+ case T_VARIABLE:
+ $look_for = 0;
+ continue 2;
+ case T_DOC_COMMENT:
+ case T_COMMENT:
+ if (preg_match('!^/\*\*\s!', $data)) {
+ $lastphpdoc = $data;
+ if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
+ $nodeps = array_merge($nodeps, $m[1]);
+ }
+ }
+ continue 2;
+ case T_DOUBLE_COLON:
+ if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
+ PEAR::raiseError("Parser error: Invalid PHP file $file",
+ PEAR_COMMON_ERROR_INVALIDPHP);
+ return false;
+ }
+ $class = $tokens[$i - 1][1];
+ if (strtolower($class) != 'parent') {
+ $used_classes[$class] = true;
+ }
+ continue 2;
+ }
+ }
+ return array(
+ "source_file" => $file,
+ "declared_classes" => $declared_classes,
+ "declared_interfaces" => $declared_interfaces,
+ "declared_methods" => $declared_methods,
+ "declared_functions" => $declared_functions,
+ "used_classes" => array_diff(array_keys($used_classes), $nodeps),
+ "inheritance" => $extends,
+ "implements" => $implements,
+ );
+ }
+
+ // }}}
+ // {{{ betterStates()
+
+ /**
+ * Return an array containing all of the states that are more stable than
+ * or equal to the passed in state
+ *
+ * @param string Release state
+ * @param boolean Determines whether to include $state in the list
+ * @return false|array False if $state is not a valid release state
+ */
+ function betterStates($state, $include = false)
+ {
+ static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+ $i = array_search($state, $states);
+ if ($i === false) {
+ return false;
+ }
+ if ($include) {
+ $i--;
+ }
+ return array_slice($states, $i + 1);
+ }
+
+ // }}}
+ // {{{ detectDependencies()
+
+ function detectDependencies($any, $status_callback = null)
+ {
+ if (!function_exists("token_get_all")) {
+ return false;
+ }
+ if (PEAR::isError($info = $this->infoFromAny($any))) {
+ return $this->raiseError($info);
+ }
+ if (!is_array($info)) {
+ return false;
+ }
+ $deps = array();
+ $used_c = $decl_c = $decl_f = $decl_m = array();
+ foreach ($info['filelist'] as $file => $fa) {
+ $tmp = $this->analyzeSourceCode($file);
+ $used_c = @array_merge($used_c, $tmp['used_classes']);
+ $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
+ $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
+ $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
+ $inheri = @array_merge($inheri, $tmp['inheritance']);
+ }
+ $used_c = array_unique($used_c);
+ $decl_c = array_unique($decl_c);
+ $undecl_c = array_diff($used_c, $decl_c);
+ return array('used_classes' => $used_c,
+ 'declared_classes' => $decl_c,
+ 'declared_methods' => $decl_m,
+ 'declared_functions' => $decl_f,
+ 'undeclared_classes' => $undecl_c,
+ 'inheritance' => $inheri,
+ );
+ }
+
+ // }}}
+ // {{{ getUserRoles()
+
+ /**
+ * Get the valid roles for a PEAR package maintainer
+ *
+ * @return array
+ * @static
+ */
+ function getUserRoles()
+ {
+ return $GLOBALS['_PEAR_Common_maintainer_roles'];
+ }
+
+ // }}}
+ // {{{ getReleaseStates()
+
+ /**
+ * Get the valid package release states of packages
+ *
+ * @return array
+ * @static
+ */
+ function getReleaseStates()
+ {
+ return $GLOBALS['_PEAR_Common_release_states'];
+ }
+
+ // }}}
+ // {{{ getDependencyTypes()
+
+ /**
+ * Get the implemented dependency types (php, ext, pkg etc.)
+ *
+ * @return array
+ * @static
+ */
+ function getDependencyTypes()
+ {
+ return $GLOBALS['_PEAR_Common_dependency_types'];
+ }
+
+ // }}}
+ // {{{ getDependencyRelations()
+
+ /**
+ * Get the implemented dependency relations (has, lt, ge etc.)
+ *
+ * @return array
+ * @static
+ */
+ function getDependencyRelations()
+ {
+ return $GLOBALS['_PEAR_Common_dependency_relations'];
+ }
+
+ // }}}
+ // {{{ getFileRoles()
+
+ /**
+ * Get the implemented file roles
+ *
+ * @return array
+ * @static
+ */
+ function getFileRoles()
+ {
+ return $GLOBALS['_PEAR_Common_file_roles'];
+ }
+
+ // }}}
+ // {{{ getReplacementTypes()
+
+ /**
+ * Get the implemented file replacement types in
+ *
+ * @return array
+ * @static
+ */
+ function getReplacementTypes()
+ {
+ return $GLOBALS['_PEAR_Common_replacement_types'];
+ }
+
+ // }}}
+ // {{{ getProvideTypes()
+
+ /**
+ * Get the implemented file replacement types in
+ *
+ * @return array
+ * @static
+ */
+ function getProvideTypes()
+ {
+ return $GLOBALS['_PEAR_Common_provide_types'];
+ }
+
+ // }}}
+ // {{{ getScriptPhases()
+
+ /**
+ * Get the implemented file replacement types in
+ *
+ * @return array
+ * @static
+ */
+ function getScriptPhases()
+ {
+ return $GLOBALS['_PEAR_Common_script_phases'];
+ }
+
+ // }}}
+ // {{{ validPackageName()
+
+ /**
+ * Test whether a string contains a valid package name.
+ *
+ * @param string $name the package name to test
+ *
+ * @return bool
+ *
+ * @access public
+ */
+ function validPackageName($name)
+ {
+ return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
+ }
+
+
+ // }}}
+ // {{{ validPackageVersion()
+
+ /**
+ * Test whether a string contains a valid package version.
+ *
+ * @param string $ver the package version to test
+ *
+ * @return bool
+ *
+ * @access public
+ */
+ function validPackageVersion($ver)
+ {
+ return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
+ }
+
+
+ // }}}
+
+ // {{{ downloadHttp()
+
+ /**
+ * Download a file through HTTP. Considers suggested file name in
+ * Content-disposition: header and can run a callback function for
+ * different events. The callback will be called with two
+ * parameters: the callback type, and parameters. The implemented
+ * callback types are:
+ *
+ * 'setup' called at the very beginning, parameter is a UI object
+ * that should be used for all output
+ * 'message' the parameter is a string with an informational message
+ * 'saveas' may be used to save with a different file name, the
+ * parameter is the filename that is about to be used.
+ * If a 'saveas' callback returns a non-empty string,
+ * that file name will be used as the filename instead.
+ * Note that $save_dir will not be affected by this, only
+ * the basename of the file.
+ * 'start' download is starting, parameter is number of bytes
+ * that are expected, or -1 if unknown
+ * 'bytesread' parameter is the number of bytes read so far
+ * 'done' download is complete, parameter is the total number
+ * of bytes read
+ * 'connfailed' if the TCP connection fails, this callback is called
+ * with array(host,port,errno,errmsg)
+ * 'writefailed' if writing to disk fails, this callback is called
+ * with array(destfile,errmsg)
+ *
+ * If an HTTP proxy has been configured (http_proxy PEAR_Config
+ * setting), the proxy will be used.
+ *
+ * @param string $url the URL to download
+ * @param object $ui PEAR_Frontend_* instance
+ * @param object $config PEAR_Config instance
+ * @param string $save_dir (optional) directory to save file in
+ * @param mixed $callback (optional) function/method to call for status
+ * updates
+ *
+ * @return string Returns the full path of the downloaded file or a PEAR
+ * error on failure. If the error is caused by
+ * socket-related errors, the error object will
+ * have the fsockopen error code available through
+ * getCode().
+ *
+ * @access public
+ */
+ function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
+ {
+ if ($callback) {
+ call_user_func($callback, 'setup', array(&$ui));
+ }
+ if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) {
+ list(,$host,,$port,$path) = $matches;
+ }
+ if (isset($this)) {
+ $config = &$this->config;
+ } else {
+ $config = &PEAR_Config::singleton();
+ }
+ $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+ if ($proxy = parse_url($config->get('http_proxy'))) {
+ $proxy_host = @$proxy['host'];
+ $proxy_port = @$proxy['port'];
+ $proxy_user = @$proxy['user'];
+ $proxy_pass = @$proxy['pass'];
+
+ if ($proxy_port == '') {
+ $proxy_port = 8080;
+ }
+ if ($callback) {
+ call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
+ }
+ }
+ if (empty($port)) {
+ $port = 80;
+ }
+ if ($proxy_host != '') {
+ $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
+ if (!$fp) {
+ if ($callback) {
+ call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
+ $errno, $errstr));
+ }
+ return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
+ }
+ $request = "GET $url HTTP/1.0\r\n";
+ } else {
+ $fp = @fsockopen($host, $port, $errno, $errstr);
+ if (!$fp) {
+ if ($callback) {
+ call_user_func($callback, 'connfailed', array($host, $port,
+ $errno, $errstr));
+ }
+ return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
+ }
+ $request = "GET $path HTTP/1.0\r\n";
+ }
+ $request .= "Host: $host:$port\r\n".
+ "User-Agent: PHP/".PHP_VERSION."\r\n";
+ if ($proxy_host != '' && $proxy_user != '') {
+ $request .= 'Proxy-Authorization: Basic ' .
+ base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
+ }
+ $request .= "\r\n";
+ fwrite($fp, $request);
+ $headers = array();
+ while (trim($line = fgets($fp, 1024))) {
+ if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
+ $headers[strtolower($matches[1])] = trim($matches[2]);
+ } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
+ if ($matches[1] != 200) {
+ return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
+ }
+ }
+ }
+ if (isset($headers['content-disposition']) &&
+ preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|$)/', $headers['content-disposition'], $matches)) {
+ $save_as = basename($matches[1]);
+ } else {
+ $save_as = basename($url);
+ }
+ if ($callback) {
+ $tmp = call_user_func($callback, 'saveas', $save_as);
+ if ($tmp) {
+ $save_as = $tmp;
+ }
+ }
+ $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
+ if (!$wp = @fopen($dest_file, 'wb')) {
+ fclose($fp);
+ if ($callback) {
+ call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+ }
+ return PEAR::raiseError("could not open $dest_file for writing");
+ }
+ if (isset($headers['content-length'])) {
+ $length = $headers['content-length'];
+ } else {
+ $length = -1;
+ }
+ $bytes = 0;
+ if ($callback) {
+ call_user_func($callback, 'start', array(basename($dest_file), $length));
+ }
+ while ($data = @fread($fp, 1024)) {
+ $bytes += strlen($data);
+ if ($callback) {
+ call_user_func($callback, 'bytesread', $bytes);
+ }
+ if (!@fwrite($wp, $data)) {
+ fclose($fp);
+ if ($callback) {
+ call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+ }
+ return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
+ }
+ }
+ fclose($fp);
+ fclose($wp);
+ if ($callback) {
+ call_user_func($callback, 'done', $bytes);
+ }
+ return $dest_file;
+ }
+
+ // }}}
+ // {{{ sortPkgDeps()
+
+ /**
+ * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+ *
+ * It also removes duplicate dependencies
+ * @param array
+ * @param boolean Sort packages in reverse order if true
+ * @return array array of array(packagefilename, package.xml contents)
+ */
+ function sortPkgDeps(&$packages, $uninstall = false)
+ {
+ $ret = array();
+ if ($uninstall) {
+ foreach($packages as $packageinfo) {
+ $ret[] = array('info' => $packageinfo);
+ }
+ } else {
+ foreach($packages as $packagefile) {
+ if (!is_array($packagefile)) {
+ $ret[] = array('file' => $packagefile,
+ 'info' => $a = $this->infoFromAny($packagefile),
+ 'pkg' => $a['package']);
+ } else {
+ $ret[] = $packagefile;
+ }
+ }
+ }
+ $checkdupes = array();
+ $newret = array();
+ foreach($ret as $i => $p) {
+ if (!isset($checkdupes[$p['info']['package']])) {
+ $checkdupes[$p['info']['package']][] = $i;
+ $newret[] = $p;
+ }
+ }
+ $this->_packageSortTree = $this->_getPkgDepTree($newret);
+
+ $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps';
+ usort($newret, array(&$this, $func));
+ $this->_packageSortTree = null;
+ $packages = $newret;
+ }
+
+ // }}}
+ // {{{ _sortPkgDeps()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are installed first
+ *
+ * This is a crude compare, real dependency checking is done on install.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependent package first, and
+ * installation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDeps($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = $this->_getPkgDeps($p1);
+ $p2deps = $this->_getPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return 1; // package 1 has dependencies, package 2 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return -1; // put package 1 first: package 2 depends on package 1
+ }
+ if (in_array($p2name, $p1deps)) {
+ return 1; // put package 2 first: package 1 depends on package 2
+ }
+ if ($this->_removedDependency($p1name, $p2name)) {
+ return -1; // put package 1 first: package 2 depends on packages that depend on package 1
+ }
+ if ($this->_removedDependency($p2name, $p1name)) {
+ return 1; // put package 2 first: package 1 depends on packages that depend on package 2
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _sortPkgDepsRev()
+
+ /**
+ * Compare two package's package.xml, and sort
+ * so that dependencies are uninstalled last
+ *
+ * This is a crude compare, real dependency checking is done on uninstall.
+ * The only purpose this serves is to make the command-line
+ * order-independent (you can list a dependency first, and
+ * uninstallation occurs in the order required)
+ * @access private
+ */
+ function _sortPkgDepsRev($p1, $p2)
+ {
+ $p1name = $p1['info']['package'];
+ $p2name = $p2['info']['package'];
+ $p1deps = $this->_getRevPkgDeps($p1);
+ $p2deps = $this->_getRevPkgDeps($p2);
+ if (!count($p1deps) && !count($p2deps)) {
+ return 0; // order makes no difference
+ }
+ if (!count($p1deps)) {
+ return 1; // package 2 has dependencies, package 1 doesn't
+ }
+ if (!count($p2deps)) {
+ return -1; // package 2 has dependencies, package 1 doesn't
+ }
+ // both have dependencies
+ if (in_array($p1name, $p2deps)) {
+ return 1; // put package 1 last
+ }
+ if (in_array($p2name, $p1deps)) {
+ return -1; // put package 2 last
+ }
+ if ($this->_removedDependency($p1name, $p2name)) {
+ return 1; // put package 1 last: package 2 depends on packages that depend on package 1
+ }
+ if ($this->_removedDependency($p2name, $p1name)) {
+ return -1; // put package 2 last: package 1 depends on packages that depend on package 2
+ }
+ // doesn't really matter if neither depends on the other
+ return 0;
+ }
+
+ // }}}
+ // {{{ _getPkgDeps()
+
+ /**
+ * get an array of package dependency names
+ * @param array
+ * @return array
+ * @access private
+ */
+ function _getPkgDeps($p)
+ {
+ if (!isset($p['info']['releases'])) {
+ return $this->_getRevPkgDeps($p);
+ }
+ $rel = array_shift($p['info']['releases']);
+ if (!isset($rel['deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($rel['deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _getPkgDeps()
+
+ /**
+ * get an array representation of the package dependency tree
+ * @return array
+ * @access private
+ */
+ function _getPkgDepTree($packages)
+ {
+ $tree = array();
+ foreach ($packages as $p) {
+ $package = $p['info']['package'];
+ $deps = $this->_getPkgDeps($p);
+ $tree[$package] = $deps;
+ }
+ return $tree;
+ }
+
+ // }}}
+ // {{{ _removedDependency($p1, $p2)
+
+ /**
+ * get an array of package dependency names for uninstall
+ * @param string package 1 name
+ * @param string package 2 name
+ * @return bool
+ * @access private
+ */
+ function _removedDependency($p1, $p2)
+ {
+ if (empty($this->_packageSortTree[$p2])) {
+ return false;
+ }
+ if (!in_array($p1, $this->_packageSortTree[$p2])) {
+ foreach ($this->_packageSortTree[$p2] as $potential) {
+ if ($this->_removedDependency($p1, $potential)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _getRevPkgDeps()
+
+ /**
+ * get an array of package dependency names for uninstall
+ * @param array
+ * @return array
+ * @access private
+ */
+ function _getRevPkgDeps($p)
+ {
+ if (!isset($p['info']['release_deps'])) {
+ return array();
+ }
+ $ret = array();
+ foreach($p['info']['release_deps'] as $dep) {
+ if ($dep['type'] == 'pkg') {
+ $ret[] = $dep['name'];
+ }
+ }
+ return $ret;
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Config.php b/3dparty/PEAR/Config.php
new file mode 100644
index 00000000000..ba641735250
--- /dev/null
+++ b/3dparty/PEAR/Config.php
@@ -0,0 +1,1169 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Config.php,v 1.52 2004/01/08 17:33:12 sniper Exp $
+
+require_once 'PEAR.php';
+require_once 'System.php';
+
+/**
+ * Last created PEAR_Config instance.
+ * @var object
+ */
+$GLOBALS['_PEAR_Config_instance'] = null;
+if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
+ $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
+} else {
+ $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
+}
+
+// Below we define constants with default values for all configuration
+// parameters except username/password. All of them can have their
+// defaults set through environment variables. The reason we use the
+// PHP_ prefix is for some security, PHP protects environment
+// variables starting with PHP_*.
+
+if (getenv('PHP_PEAR_SYSCONF_DIR')) {
+ define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
+} elseif (getenv('SystemRoot')) {
+ define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
+} else {
+ define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
+}
+
+// Default for master_server
+if (getenv('PHP_PEAR_MASTER_SERVER')) {
+ define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
+}
+
+// Default for http_proxy
+if (getenv('PHP_PEAR_HTTP_PROXY')) {
+ define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
+} elseif (getenv('http_proxy')) {
+ define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
+}
+
+// Default for php_dir
+if (getenv('PHP_PEAR_INSTALL_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
+} else {
+ if (@is_dir($PEAR_INSTALL_DIR)) {
+ define('PEAR_CONFIG_DEFAULT_PHP_DIR',
+ $PEAR_INSTALL_DIR);
+ } else {
+ define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
+ }
+}
+
+// Default for ext_dir
+if (getenv('PHP_PEAR_EXTENSION_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
+} else {
+ if (ini_get('extension_dir')) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
+ } elseif (defined('PEAR_EXTENSION_DIR') && @is_dir(PEAR_EXTENSION_DIR)) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
+ } elseif (defined('PHP_EXTENSION_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
+ } else {
+ define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
+ }
+}
+
+// Default for doc_dir
+if (getenv('PHP_PEAR_DOC_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_DOC_DIR',
+ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
+}
+
+// Default for bin_dir
+if (getenv('PHP_PEAR_BIN_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
+}
+
+// Default for data_dir
+if (getenv('PHP_PEAR_DATA_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_DATA_DIR',
+ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
+}
+
+// Default for test_dir
+if (getenv('PHP_PEAR_TEST_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_TEST_DIR',
+ $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
+}
+
+// Default for cache_dir
+if (getenv('PHP_PEAR_CACHE_DIR')) {
+ define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
+ System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
+ DIRECTORY_SEPARATOR . 'cache');
+}
+
+// Default for php_bin
+if (getenv('PHP_PEAR_PHP_BIN')) {
+ define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
+ DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
+}
+
+// Default for verbose
+if (getenv('PHP_PEAR_VERBOSE')) {
+ define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
+}
+
+// Default for preferred_state
+if (getenv('PHP_PEAR_PREFERRED_STATE')) {
+ define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
+}
+
+// Default for umask
+if (getenv('PHP_PEAR_UMASK')) {
+ define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
+}
+
+// Default for cache_ttl
+if (getenv('PHP_PEAR_CACHE_TTL')) {
+ define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
+}
+
+// Default for sig_type
+if (getenv('PHP_PEAR_SIG_TYPE')) {
+ define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
+}
+
+// Default for sig_bin
+if (getenv('PHP_PEAR_SIG_BIN')) {
+ define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_SIG_BIN',
+ System::which(
+ 'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
+}
+
+// Default for sig_keydir
+if (getenv('PHP_PEAR_SIG_KEYDIR')) {
+ define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
+} else {
+ define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
+ PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
+}
+
+/**
+ * This is a class for storing configuration data, keeping track of
+ * which are system-defined, user-defined or defaulted.
+ */
+class PEAR_Config extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * Array of config files used.
+ *
+ * @var array layer => config file
+ */
+ var $files = array(
+ 'system' => '',
+ 'user' => '',
+ );
+
+ var $layers = array();
+
+ /**
+ * Configuration data, two-dimensional array where the first
+ * dimension is the config layer ('user', 'system' and 'default'),
+ * and the second dimension is keyname => value.
+ *
+ * The order in the first dimension is important! Earlier
+ * layers will shadow later ones when a config value is
+ * requested (if a 'user' value exists, it will be returned first,
+ * then 'system' and finally 'default').
+ *
+ * @var array layer => array(keyname => value, ...)
+ */
+ var $configuration = array(
+ 'user' => array(),
+ 'system' => array(),
+ 'default' => array(),
+ );
+
+ /**
+ * Information about the configuration data. Stores the type,
+ * default value and a documentation string for each configuration
+ * value.
+ *
+ * @var array layer => array(infotype => value, ...)
+ */
+ var $configuration_info = array(
+ // Internet Access
+ 'master_server' => array(
+ 'type' => 'string',
+ 'default' => 'pear.php.net',
+ 'doc' => 'name of the main PEAR server',
+ 'prompt' => 'PEAR server',
+ 'group' => 'Internet Access',
+ ),
+ 'http_proxy' => array(
+ 'type' => 'string',
+ 'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
+ 'doc' => 'HTTP proxy (host:port) to use when downloading packages',
+ 'prompt' => 'HTTP Proxy Server Address',
+ 'group' => 'Internet Access',
+ ),
+ // File Locations
+ 'php_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
+ 'doc' => 'directory where .php files are installed',
+ 'prompt' => 'PEAR directory',
+ 'group' => 'File Locations',
+ ),
+ 'ext_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
+ 'doc' => 'directory where loadable extensions are installed',
+ 'prompt' => 'PHP extension directory',
+ 'group' => 'File Locations',
+ ),
+ 'doc_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
+ 'doc' => 'directory where documentation is installed',
+ 'prompt' => 'PEAR documentation directory',
+ 'group' => 'File Locations',
+ ),
+ 'bin_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
+ 'doc' => 'directory where executables are installed',
+ 'prompt' => 'PEAR executables directory',
+ 'group' => 'File Locations',
+ ),
+ 'data_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
+ 'doc' => 'directory where data files are installed',
+ 'prompt' => 'PEAR data directory',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ 'test_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
+ 'doc' => 'directory where regression tests are installed',
+ 'prompt' => 'PEAR test directory',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ 'cache_dir' => array(
+ 'type' => 'directory',
+ 'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
+ 'doc' => 'directory which is used for XMLRPC cache',
+ 'prompt' => 'PEAR Installer cache directory',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ 'php_bin' => array(
+ 'type' => 'file',
+ 'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
+ 'doc' => 'PHP CLI/CGI binary for executing scripts',
+ 'prompt' => 'PHP CLI/CGI binary',
+ 'group' => 'File Locations (Advanced)',
+ ),
+ // Maintainers
+ 'username' => array(
+ 'type' => 'string',
+ 'default' => '',
+ 'doc' => '(maintainers) your PEAR account name',
+ 'prompt' => 'PEAR username (for maintainers)',
+ 'group' => 'Maintainers',
+ ),
+ 'password' => array(
+ 'type' => 'password',
+ 'default' => '',
+ 'doc' => '(maintainers) your PEAR account password',
+ 'prompt' => 'PEAR password (for maintainers)',
+ 'group' => 'Maintainers',
+ ),
+ // Advanced
+ 'verbose' => array(
+ 'type' => 'integer',
+ 'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
+ 'doc' => 'verbosity level
+0: really quiet
+1: somewhat quiet
+2: verbose
+3: debug',
+ 'prompt' => 'Debug Log Level',
+ 'group' => 'Advanced',
+ ),
+ 'preferred_state' => array(
+ 'type' => 'set',
+ 'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
+ 'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
+ 'valid_set' => array(
+ 'stable', 'beta', 'alpha', 'devel', 'snapshot'),
+ 'prompt' => 'Preferred Package State',
+ 'group' => 'Advanced',
+ ),
+ 'umask' => array(
+ 'type' => 'mask',
+ 'default' => PEAR_CONFIG_DEFAULT_UMASK,
+ 'doc' => 'umask used when creating files (Unix-like systems only)',
+ 'prompt' => 'Unix file mask',
+ 'group' => 'Advanced',
+ ),
+ 'cache_ttl' => array(
+ 'type' => 'integer',
+ 'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
+ 'doc' => 'amount of secs where the local cache is used and not updated',
+ 'prompt' => 'Cache TimeToLive',
+ 'group' => 'Advanced',
+ ),
+ 'sig_type' => array(
+ 'type' => 'set',
+ 'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
+ 'doc' => 'which package signature mechanism to use',
+ 'valid_set' => array('gpg'),
+ 'prompt' => 'Package Signature Type',
+ 'group' => 'Maintainers',
+ ),
+ 'sig_bin' => array(
+ 'type' => 'string',
+ 'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
+ 'doc' => 'which package signature mechanism to use',
+ 'prompt' => 'Signature Handling Program',
+ 'group' => 'Maintainers',
+ ),
+ 'sig_keyid' => array(
+ 'type' => 'string',
+ 'default' => '',
+ 'doc' => 'which key to use for signing with',
+ 'prompt' => 'Signature Key Id',
+ 'group' => 'Maintainers',
+ ),
+ 'sig_keydir' => array(
+ 'type' => 'string',
+ 'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
+ 'doc' => 'which package signature mechanism to use',
+ 'prompt' => 'Signature Key Directory',
+ 'group' => 'Maintainers',
+ ),
+ );
+
+ // }}}
+
+ // {{{ PEAR_Config([file], [defaults_file])
+
+ /**
+ * Constructor.
+ *
+ * @param string (optional) file to read user-defined options from
+ * @param string (optional) file to read system-wide defaults from
+ *
+ * @access public
+ *
+ * @see PEAR_Config::singleton
+ */
+ function PEAR_Config($user_file = '', $system_file = '')
+ {
+ $this->PEAR();
+ $sl = DIRECTORY_SEPARATOR;
+ if (empty($user_file)) {
+ if (OS_WINDOWS) {
+ $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
+ } else {
+ $user_file = getenv('HOME') . $sl . '.pearrc';
+ }
+ }
+ if (empty($system_file)) {
+ if (OS_WINDOWS) {
+ $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini';
+ } else {
+ $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf';
+ }
+ }
+ $this->layers = array_keys($this->configuration);
+ $this->files['user'] = $user_file;
+ $this->files['system'] = $system_file;
+ if ($user_file && file_exists($user_file)) {
+ $this->readConfigFile($user_file);
+ }
+ if ($system_file && file_exists($system_file)) {
+ $this->mergeConfigFile($system_file, false, 'system');
+ }
+ foreach ($this->configuration_info as $key => $info) {
+ $this->configuration['default'][$key] = $info['default'];
+ }
+ //$GLOBALS['_PEAR_Config_instance'] = &$this;
+ }
+
+ // }}}
+ // {{{ singleton([file], [defaults_file])
+
+ /**
+ * Static singleton method. If you want to keep only one instance
+ * of this class in use, this method will give you a reference to
+ * the last created PEAR_Config object if one exists, or create a
+ * new object.
+ *
+ * @param string (optional) file to read user-defined options from
+ * @param string (optional) file to read system-wide defaults from
+ *
+ * @return object an existing or new PEAR_Config instance
+ *
+ * @access public
+ *
+ * @see PEAR_Config::PEAR_Config
+ */
+ function &singleton($user_file = '', $system_file = '')
+ {
+ if (is_object($GLOBALS['_PEAR_Config_instance'])) {
+ return $GLOBALS['_PEAR_Config_instance'];
+ }
+ $GLOBALS['_PEAR_Config_instance'] =
+ &new PEAR_Config($user_file, $system_file);
+ return $GLOBALS['_PEAR_Config_instance'];
+ }
+
+ // }}}
+ // {{{ readConfigFile([file], [layer])
+
+ /**
+ * Reads configuration data from a file. All existing values in
+ * the config layer are discarded and replaced with data from the
+ * file.
+ *
+ * @param string (optional) file to read from, if NULL or not
+ * specified, the last-used file for the same layer (second param)
+ * is used
+ *
+ * @param string (optional) config layer to insert data into
+ * ('user' or 'system')
+ *
+ * @return bool TRUE on success or a PEAR error on failure
+ *
+ * @access public
+ */
+ function readConfigFile($file = null, $layer = 'user')
+ {
+ if (empty($this->files[$layer])) {
+ return $this->raiseError("unknown config file type `$layer'");
+ }
+ if ($file === null) {
+ $file = $this->files[$layer];
+ }
+ $data = $this->_readConfigDataFrom($file);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ $this->_decodeInput($data);
+ $this->configuration[$layer] = $data;
+ return true;
+ }
+
+ // }}}
+ // {{{ mergeConfigFile(file, [override], [layer])
+
+ /**
+ * Merges data into a config layer from a file. Does the same
+ * thing as readConfigFile, except it does not replace all
+ * existing values in the config layer.
+ *
+ * @param string file to read from
+ *
+ * @param bool (optional) whether to overwrite existing data
+ * (default TRUE)
+ *
+ * @param string config layer to insert data into ('user' or
+ * 'system')
+ *
+ * @return bool TRUE on success or a PEAR error on failure
+ *
+ * @access public.
+ */
+ function mergeConfigFile($file, $override = true, $layer = 'user')
+ {
+ if (empty($this->files[$layer])) {
+ return $this->raiseError("unknown config file type `$layer'");
+ }
+ if ($file === null) {
+ $file = $this->files[$layer];
+ }
+ $data = $this->_readConfigDataFrom($file);
+ if (PEAR::isError($data)) {
+ return $data;
+ }
+ $this->_decodeInput($data);
+ if ($override) {
+ $this->configuration[$layer] = array_merge($this->configuration[$layer], $data);
+ } else {
+ $this->configuration[$layer] = array_merge($data, $this->configuration[$layer]);
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ writeConfigFile([file], [layer])
+
+ /**
+ * Writes data into a config layer from a file.
+ *
+ * @param string file to read from
+ *
+ * @param bool (optional) whether to overwrite existing data
+ * (default TRUE)
+ *
+ * @param string config layer to insert data into ('user' or
+ * 'system')
+ *
+ * @return bool TRUE on success or a PEAR error on failure
+ *
+ * @access public.
+ */
+ function writeConfigFile($file = null, $layer = 'user', $data = null)
+ {
+ if ($layer == 'both' || $layer == 'all') {
+ foreach ($this->files as $type => $file) {
+ $err = $this->writeConfigFile($file, $type, $data);
+ if (PEAR::isError($err)) {
+ return $err;
+ }
+ }
+ return true;
+ }
+ if (empty($this->files[$layer])) {
+ return $this->raiseError("unknown config file type `$layer'");
+ }
+ if ($file === null) {
+ $file = $this->files[$layer];
+ }
+ $data = ($data === null) ? $this->configuration[$layer] : $data;
+ $this->_encodeOutput($data);
+ $opt = array('-p', dirname($file));
+ if (!@System::mkDir($opt)) {
+ return $this->raiseError("could not create directory: " . dirname($file));
+ }
+ if (@is_file($file) && !@is_writeable($file)) {
+ return $this->raiseError("no write access to $file!");
+ }
+ $fp = @fopen($file, "w");
+ if (!$fp) {
+ return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed");
+ }
+ $contents = "#PEAR_Config 0.9\n" . serialize($data);
+ if (!@fwrite($fp, $contents)) {
+ return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed");
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _readConfigDataFrom(file)
+
+ /**
+ * Reads configuration data from a file and returns the parsed data
+ * in an array.
+ *
+ * @param string file to read from
+ *
+ * @return array configuration data or a PEAR error on failure
+ *
+ * @access private
+ */
+ function _readConfigDataFrom($file)
+ {
+ $fp = @fopen($file, "r");
+ if (!$fp) {
+ return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
+ }
+ $size = filesize($file);
+ $rt = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ $contents = fread($fp, $size);
+ set_magic_quotes_runtime($rt);
+ fclose($fp);
+ $version = '0.1';
+ if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
+ $version = $matches[1];
+ $contents = substr($contents, strlen($matches[0]));
+ }
+ if (version_compare("$version", '1', '<')) {
+ $data = unserialize($contents);
+ if (!is_array($data)) {
+ if (strlen(trim($contents)) > 0) {
+ $error = "PEAR_Config: bad data in $file";
+// if (isset($this)) {
+ return $this->raiseError($error);
+// } else {
+// return PEAR::raiseError($error);
+ } else {
+ $data = array();
+ }
+ }
+ // add parsing of newer formats here...
+ } else {
+ return $this->raiseError("$file: unknown version `$version'");
+ }
+ return $data;
+ }
+
+ // }}}
+ // {{{ getConfFile(layer)
+ /**
+ * Gets the file used for storing the config for a layer
+ *
+ * @param string $layer 'user' or 'system'
+ */
+
+ function getConfFile($layer)
+ {
+ return $this->files[$layer];
+ }
+
+ // }}}
+ // {{{ _encodeOutput(&data)
+
+ /**
+ * Encodes/scrambles configuration data before writing to files.
+ * Currently, 'password' values will be base64-encoded as to avoid
+ * that people spot cleartext passwords by accident.
+ *
+ * @param array (reference) array to encode values in
+ *
+ * @return bool TRUE on success
+ *
+ * @access private
+ */
+ function _encodeOutput(&$data)
+ {
+ foreach ($data as $key => $value) {
+ if (!isset($this->configuration_info[$key])) {
+ continue;
+ }
+ $type = $this->configuration_info[$key]['type'];
+ switch ($type) {
+ // we base64-encode passwords so they are at least
+ // not shown in plain by accident
+ case 'password': {
+ $data[$key] = base64_encode($data[$key]);
+ break;
+ }
+ case 'mask': {
+ $data[$key] = octdec($data[$key]);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _decodeInput(&data)
+
+ /**
+ * Decodes/unscrambles configuration data after reading from files.
+ *
+ * @param array (reference) array to encode values in
+ *
+ * @return bool TRUE on success
+ *
+ * @access private
+ *
+ * @see PEAR_Config::_encodeOutput
+ */
+ function _decodeInput(&$data)
+ {
+ if (!is_array($data)) {
+ return true;
+ }
+ foreach ($data as $key => $value) {
+ if (!isset($this->configuration_info[$key])) {
+ continue;
+ }
+ $type = $this->configuration_info[$key]['type'];
+ switch ($type) {
+ case 'password': {
+ $data[$key] = base64_decode($data[$key]);
+ break;
+ }
+ case 'mask': {
+ $data[$key] = decoct($data[$key]);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ get(key, [layer])
+
+ /**
+ * Returns a configuration value, prioritizing layers as per the
+ * layers property.
+ *
+ * @param string config key
+ *
+ * @return mixed the config value, or NULL if not found
+ *
+ * @access public
+ */
+ function get($key, $layer = null)
+ {
+ if ($layer === null) {
+ foreach ($this->layers as $layer) {
+ if (isset($this->configuration[$layer][$key])) {
+ return $this->configuration[$layer][$key];
+ }
+ }
+ } elseif (isset($this->configuration[$layer][$key])) {
+ return $this->configuration[$layer][$key];
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ set(key, value, [layer])
+
+ /**
+ * Set a config value in a specific layer (defaults to 'user').
+ * Enforces the types defined in the configuration_info array. An
+ * integer config variable will be cast to int, and a set config
+ * variable will be validated against its legal values.
+ *
+ * @param string config key
+ *
+ * @param string config value
+ *
+ * @param string (optional) config layer
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function set($key, $value, $layer = 'user')
+ {
+ if (empty($this->configuration_info[$key])) {
+ return false;
+ }
+ extract($this->configuration_info[$key]);
+ switch ($type) {
+ case 'integer':
+ $value = (int)$value;
+ break;
+ case 'set': {
+ // If a valid_set is specified, require the value to
+ // be in the set. If there is no valid_set, accept
+ // any value.
+ if ($valid_set) {
+ reset($valid_set);
+ if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
+ (key($valid_set) !== 0 && empty($valid_set[$value])))
+ {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+ $this->configuration[$layer][$key] = $value;
+ return true;
+ }
+
+ // }}}
+ // {{{ getType(key)
+
+ /**
+ * Get the type of a config value.
+ *
+ * @param string config key
+ *
+ * @return string type, one of "string", "integer", "file",
+ * "directory", "set" or "password".
+ *
+ * @access public
+ *
+ */
+ function getType($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['type'];
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ getDocs(key)
+
+ /**
+ * Get the documentation for a config value.
+ *
+ * @param string config key
+ *
+ * @return string documentation string
+ *
+ * @access public
+ *
+ */
+ function getDocs($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['doc'];
+ }
+ return false;
+ }
+ // }}}
+ // {{{ getPrompt(key)
+
+ /**
+ * Get the short documentation for a config value.
+ *
+ * @param string config key
+ *
+ * @return string short documentation string
+ *
+ * @access public
+ *
+ */
+ function getPrompt($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['prompt'];
+ }
+ return false;
+ }
+ // }}}
+ // {{{ getGroup(key)
+
+ /**
+ * Get the parameter group for a config key.
+ *
+ * @param string config key
+ *
+ * @return string parameter group
+ *
+ * @access public
+ *
+ */
+ function getGroup($key)
+ {
+ if (isset($this->configuration_info[$key])) {
+ return $this->configuration_info[$key]['group'];
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ getGroups()
+
+ /**
+ * Get the list of parameter groups.
+ *
+ * @return array list of parameter groups
+ *
+ * @access public
+ *
+ */
+ function getGroups()
+ {
+ $tmp = array();
+ foreach ($this->configuration_info as $key => $info) {
+ $tmp[$info['group']] = 1;
+ }
+ return array_keys($tmp);
+ }
+
+ // }}}
+ // {{{ getGroupKeys()
+
+ /**
+ * Get the list of the parameters in a group.
+ *
+ * @param string $group parameter group
+ *
+ * @return array list of parameters in $group
+ *
+ * @access public
+ *
+ */
+ function getGroupKeys($group)
+ {
+ $keys = array();
+ foreach ($this->configuration_info as $key => $info) {
+ if ($info['group'] == $group) {
+ $keys[] = $key;
+ }
+ }
+ return $keys;
+ }
+
+ // }}}
+ // {{{ getSetValues(key)
+
+ /**
+ * Get the list of allowed set values for a config value. Returns
+ * NULL for config values that are not sets.
+ *
+ * @param string config key
+ *
+ * @return array enumerated array of set values, or NULL if the
+ * config key is unknown or not a set
+ *
+ * @access public
+ *
+ */
+ function getSetValues($key)
+ {
+ if (isset($this->configuration_info[$key]) &&
+ isset($this->configuration_info[$key]['type']) &&
+ $this->configuration_info[$key]['type'] == 'set')
+ {
+ $valid_set = $this->configuration_info[$key]['valid_set'];
+ reset($valid_set);
+ if (key($valid_set) === 0) {
+ return $valid_set;
+ }
+ return array_keys($valid_set);
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ getKeys()
+
+ /**
+ * Get all the current config keys.
+ *
+ * @return array simple array of config keys
+ *
+ * @access public
+ */
+ function getKeys()
+ {
+ $keys = array();
+ foreach ($this->layers as $layer) {
+ $keys = array_merge($keys, $this->configuration[$layer]);
+ }
+ return array_keys($keys);
+ }
+
+ // }}}
+ // {{{ remove(key, [layer])
+
+ /**
+ * Remove the a config key from a specific config layer.
+ *
+ * @param string config key
+ *
+ * @param string (optional) config layer
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function remove($key, $layer = 'user')
+ {
+ if (isset($this->configuration[$layer][$key])) {
+ unset($this->configuration[$layer][$key]);
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ removeLayer(layer)
+
+ /**
+ * Temporarily remove an entire config layer. USE WITH CARE!
+ *
+ * @param string config key
+ *
+ * @param string (optional) config layer
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function removeLayer($layer)
+ {
+ if (isset($this->configuration[$layer])) {
+ $this->configuration[$layer] = array();
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ store([layer])
+
+ /**
+ * Stores configuration data in a layer.
+ *
+ * @param string config layer to store
+ *
+ * @return bool TRUE on success, or PEAR error on failure
+ *
+ * @access public
+ */
+ function store($layer = 'user', $data = null)
+ {
+ return $this->writeConfigFile(null, $layer, $data);
+ }
+
+ // }}}
+ // {{{ toDefault(key)
+
+ /**
+ * Unset the user-defined value of a config key, reverting the
+ * value to the system-defined one.
+ *
+ * @param string config key
+ *
+ * @return bool TRUE on success, FALSE on failure
+ *
+ * @access public
+ */
+ function toDefault($key)
+ {
+ trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE);
+ return $this->remove($key, 'user');
+ }
+
+ // }}}
+ // {{{ definedBy(key)
+
+ /**
+ * Tells what config layer that gets to define a key.
+ *
+ * @param string config key
+ *
+ * @return string the config layer, or an empty string if not found
+ *
+ * @access public
+ */
+ function definedBy($key)
+ {
+ foreach ($this->layers as $layer) {
+ if (isset($this->configuration[$layer][$key])) {
+ return $layer;
+ }
+ }
+ return '';
+ }
+
+ // }}}
+ // {{{ isDefaulted(key)
+
+ /**
+ * Tells whether a config value has a system-defined value.
+ *
+ * @param string config key
+ *
+ * @return bool
+ *
+ * @access public
+ *
+ * @deprecated
+ */
+ function isDefaulted($key)
+ {
+ trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE);
+ return $this->definedBy($key) == 'system';
+ }
+
+ // }}}
+ // {{{ isDefined(key)
+
+ /**
+ * Tells whether a given key exists as a config value.
+ *
+ * @param string config key
+ *
+ * @return bool whether <config key> exists in this object
+ *
+ * @access public
+ */
+ function isDefined($key)
+ {
+ foreach ($this->layers as $layer) {
+ if (isset($this->configuration[$layer][$key])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ isDefinedLayer(key)
+
+ /**
+ * Tells whether a given config layer exists.
+ *
+ * @param string config layer
+ *
+ * @return bool whether <config layer> exists in this object
+ *
+ * @access public
+ */
+ function isDefinedLayer($layer)
+ {
+ return isset($this->configuration[$layer]);
+ }
+
+ // }}}
+ // {{{ getLayers()
+
+ /**
+ * Returns the layers defined (except the 'default' one)
+ *
+ * @return array of the defined layers
+ */
+ function getLayers()
+ {
+ $cf = $this->configuration;
+ unset($cf['default']);
+ return array_keys($cf);
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Dependency.php b/3dparty/PEAR/Dependency.php
new file mode 100644
index 00000000000..705167aa70f
--- /dev/null
+++ b/3dparty/PEAR/Dependency.php
@@ -0,0 +1,487 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
+// | Stig Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Dependency.php,v 1.36.4.1 2004/12/27 07:04:19 cellog Exp $
+
+require_once "PEAR.php";
+
+define('PEAR_DEPENDENCY_MISSING', -1);
+define('PEAR_DEPENDENCY_CONFLICT', -2);
+define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3);
+define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4);
+define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5);
+define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6);
+define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL', -7);
+define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL', -8);
+define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL', -9);
+
+/**
+ * Dependency check for PEAR packages
+ *
+ * The class is based on the dependency RFC that can be found at
+ * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1
+ *
+ * @author Tomas V.V.Vox <cox@idecnet.com>
+ * @author Stig Bakken <ssb@php.net>
+ */
+class PEAR_Dependency
+{
+ // {{{ constructor
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param object Registry object
+ * @return void
+ */
+ function PEAR_Dependency(&$registry)
+ {
+ $this->registry = &$registry;
+ }
+
+ // }}}
+ // {{{ callCheckMethod()
+
+ /**
+ * This method maps the XML dependency definition to the
+ * corresponding one from PEAR_Dependency
+ *
+ * <pre>
+ * $opts => Array
+ * (
+ * [type] => pkg
+ * [rel] => ge
+ * [version] => 3.4
+ * [name] => HTML_Common
+ * [optional] => false
+ * )
+ * </pre>
+ *
+ * @param string Error message
+ * @param array Options
+ * @return boolean
+ */
+ function callCheckMethod(&$errmsg, $opts)
+ {
+ $rel = isset($opts['rel']) ? $opts['rel'] : 'has';
+ $req = isset($opts['version']) ? $opts['version'] : null;
+ $name = isset($opts['name']) ? $opts['name'] : null;
+ $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ?
+ $opts['optional'] : null;
+ $errmsg = '';
+ switch ($opts['type']) {
+ case 'pkg':
+ return $this->checkPackage($errmsg, $name, $req, $rel, $opt);
+ break;
+ case 'ext':
+ return $this->checkExtension($errmsg, $name, $req, $rel, $opt);
+ break;
+ case 'php':
+ return $this->checkPHP($errmsg, $req, $rel);
+ break;
+ case 'prog':
+ return $this->checkProgram($errmsg, $name);
+ break;
+ case 'os':
+ return $this->checkOS($errmsg, $name);
+ break;
+ case 'sapi':
+ return $this->checkSAPI($errmsg, $name);
+ break;
+ case 'zend':
+ return $this->checkZend($errmsg, $name);
+ break;
+ default:
+ return "'{$opts['type']}' dependency type not supported";
+ }
+ }
+
+ // }}}
+ // {{{ checkPackage()
+
+ /**
+ * Package dependencies check method
+ *
+ * @param string $errmsg Empty string, it will be populated with an error message, if any
+ * @param string $name Name of the package to test
+ * @param string $req The package version required
+ * @param string $relation How to compare versions with each other
+ * @param bool $opt Whether the relationship is optional
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkPackage(&$errmsg, $name, $req = null, $relation = 'has',
+ $opt = false)
+ {
+ if (is_string($req) && substr($req, 0, 2) == 'v.') {
+ $req = substr($req, 2);
+ }
+ switch ($relation) {
+ case 'has':
+ if (!$this->registry->packageExists($name)) {
+ if ($opt) {
+ $errmsg = "package `$name' is recommended to utilize some features.";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
+ $errmsg = "requires package `$name'";
+ return PEAR_DEPENDENCY_MISSING;
+ }
+ return false;
+ case 'not':
+ if ($this->registry->packageExists($name)) {
+ $errmsg = "conflicts with package `$name'";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+ return false;
+ case 'lt':
+ case 'le':
+ case 'eq':
+ case 'ne':
+ case 'ge':
+ case 'gt':
+ $version = $this->registry->packageInfo($name, 'version');
+ if (!$this->registry->packageExists($name)
+ || !version_compare("$version", "$req", $relation))
+ {
+ $code = $this->codeFromRelation($relation, $version, $req, $opt);
+ if ($opt) {
+ $errmsg = "package `$name' version " . $this->signOperator($relation) .
+ " $req is recommended to utilize some features.";
+ if ($version) {
+ $errmsg .= " Installed version is $version";
+ }
+ return $code;
+ }
+ $errmsg = "requires package `$name' " .
+ $this->signOperator($relation) . " $req";
+ return $code;
+ }
+ return false;
+ }
+ $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$name)";
+ return PEAR_DEPENDENCY_BAD_DEPENDENCY;
+ }
+
+ // }}}
+ // {{{ checkPackageUninstall()
+
+ /**
+ * Check package dependencies on uninstall
+ *
+ * @param string $error The resultant error string
+ * @param string $warning The resultant warning string
+ * @param string $name Name of the package to test
+ *
+ * @return bool true if there were errors
+ */
+ function checkPackageUninstall(&$error, &$warning, $package)
+ {
+ $error = null;
+ $packages = $this->registry->listPackages();
+ foreach ($packages as $pkg) {
+ if ($pkg == $package) {
+ continue;
+ }
+ $deps = $this->registry->packageInfo($pkg, 'release_deps');
+ if (empty($deps)) {
+ continue;
+ }
+ foreach ($deps as $dep) {
+ if ($dep['type'] == 'pkg' && strcasecmp($dep['name'], $package) == 0) {
+ if ($dep['rel'] == 'ne' || $dep['rel'] == 'not') {
+ continue;
+ }
+ if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+ $warning .= "\nWarning: Package '$pkg' optionally depends on '$package'";
+ } else {
+ $error .= "Package '$pkg' depends on '$package'\n";
+ }
+ }
+ }
+ }
+ return ($error) ? true : false;
+ }
+
+ // }}}
+ // {{{ checkExtension()
+
+ /**
+ * Extension dependencies check method
+ *
+ * @param string $name Name of the extension to test
+ * @param string $req_ext_ver Required extension version to compare with
+ * @param string $relation How to compare versions with eachother
+ * @param bool $opt Whether the relationship is optional
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkExtension(&$errmsg, $name, $req = null, $relation = 'has',
+ $opt = false)
+ {
+ if ($relation == 'not') {
+ if (extension_loaded($name)) {
+ $errmsg = "conflicts with PHP extension '$name'";
+ return PEAR_DEPENDENCY_CONFLICT;
+ } else {
+ return false;
+ }
+ }
+
+ if (!extension_loaded($name)) {
+ if ($relation == 'not') {
+ return false;
+ }
+ if ($opt) {
+ $errmsg = "'$name' PHP extension is recommended to utilize some features";
+ return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+ }
+ $errmsg = "'$name' PHP extension is not installed";
+ return PEAR_DEPENDENCY_MISSING;
+ }
+ if ($relation == 'has') {
+ return false;
+ }
+ $code = false;
+ if (is_string($req) && substr($req, 0, 2) == 'v.') {
+ $req = substr($req, 2);
+ }
+ $ext_ver = phpversion($name);
+ $operator = $relation;
+ // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90)
+ if (!version_compare("$ext_ver", "$req", $operator)) {
+ $errmsg = "'$name' PHP extension version " .
+ $this->signOperator($operator) . " $req is required";
+ $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt);
+ if ($opt) {
+ $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) .
+ " $req is recommended to utilize some features";
+ return $code;
+ }
+ }
+ return $code;
+ }
+
+ // }}}
+ // {{{ checkOS()
+
+ /**
+ * Operating system dependencies check method
+ *
+ * @param string $os Name of the operating system
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkOS(&$errmsg, $os)
+ {
+ // XXX Fixme: Implement a more flexible way, like
+ // comma separated values or something similar to PEAR_OS
+ static $myos;
+ if (empty($myos)) {
+ include_once "OS/Guess.php";
+ $myos = new OS_Guess();
+ }
+ // only 'has' relation is currently supported
+ if ($myos->matchSignature($os)) {
+ return false;
+ }
+ $errmsg = "'$os' operating system not supported";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+
+ // }}}
+ // {{{ checkPHP()
+
+ /**
+ * PHP version check method
+ *
+ * @param string $req which version to compare
+ * @param string $relation how to compare the version
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkPHP(&$errmsg, $req, $relation = 'ge')
+ {
+ // this would be a bit stupid, but oh well :)
+ if ($relation == 'has') {
+ return false;
+ }
+ if ($relation == 'not') {
+ $errmsg = 'Invalid dependency - "not" is not allowed for php dependencies, ' .
+ 'php cannot conflict with itself';
+ return PEAR_DEPENDENCY_BAD_DEPENDENCY;
+ }
+ if (substr($req, 0, 2) == 'v.') {
+ $req = substr($req,2, strlen($req) - 2);
+ }
+ $php_ver = phpversion();
+ $operator = $relation;
+ if (!version_compare("$php_ver", "$req", $operator)) {
+ $errmsg = "PHP version " . $this->signOperator($operator) .
+ " $req is required";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ checkProgram()
+
+ /**
+ * External program check method. Looks for executable files in
+ * directories listed in the PATH environment variable.
+ *
+ * @param string $program which program to look for
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkProgram(&$errmsg, $program)
+ {
+ // XXX FIXME honor safe mode
+ $exe_suffix = OS_WINDOWS ? '.exe' : '';
+ $path_elements = explode(PATH_SEPARATOR, getenv('PATH'));
+ foreach ($path_elements as $dir) {
+ $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix;
+ if (@file_exists($file) && @is_executable($file)) {
+ return false;
+ }
+ }
+ $errmsg = "'$program' program is not present in the PATH";
+ return PEAR_DEPENDENCY_MISSING;
+ }
+
+ // }}}
+ // {{{ checkSAPI()
+
+ /**
+ * SAPI backend check method. Version comparison is not yet
+ * available here.
+ *
+ * @param string $name name of SAPI backend
+ * @param string $req which version to compare
+ * @param string $relation how to compare versions (currently
+ * hardcoded to 'has')
+ * @return mixed bool false if no error or the error string
+ */
+ function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has')
+ {
+ // XXX Fixme: There is no way to know if the user has or
+ // not other SAPI backends installed than the installer one
+
+ $sapi_backend = php_sapi_name();
+ // Version comparisons not supported, sapi backends don't have
+ // version information yet.
+ if ($sapi_backend == $name) {
+ return false;
+ }
+ $errmsg = "'$sapi_backend' SAPI backend not supported";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+
+ // }}}
+ // {{{ checkZend()
+
+ /**
+ * Zend version check method
+ *
+ * @param string $req which version to compare
+ * @param string $relation how to compare the version
+ *
+ * @return mixed bool false if no error or the error string
+ */
+ function checkZend(&$errmsg, $req, $relation = 'ge')
+ {
+ if (substr($req, 0, 2) == 'v.') {
+ $req = substr($req,2, strlen($req) - 2);
+ }
+ $zend_ver = zend_version();
+ $operator = substr($relation,0,2);
+ if (!version_compare("$zend_ver", "$req", $operator)) {
+ $errmsg = "Zend version " . $this->signOperator($operator) .
+ " $req is required";
+ return PEAR_DEPENDENCY_CONFLICT;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ signOperator()
+
+ /**
+ * Converts text comparing operators to them sign equivalents
+ *
+ * Example: 'ge' to '>='
+ *
+ * @access public
+ * @param string Operator
+ * @return string Sign equivalent
+ */
+ function signOperator($operator)
+ {
+ switch($operator) {
+ case 'lt': return '<';
+ case 'le': return '<=';
+ case 'gt': return '>';
+ case 'ge': return '>=';
+ case 'eq': return '==';
+ case 'ne': return '!=';
+ default:
+ return $operator;
+ }
+ }
+
+ // }}}
+ // {{{ codeFromRelation()
+
+ /**
+ * Convert relation into corresponding code
+ *
+ * @access public
+ * @param string Relation
+ * @param string Version
+ * @param string Requirement
+ * @param bool Optional dependency indicator
+ * @return integer
+ */
+ function codeFromRelation($relation, $version, $req, $opt = false)
+ {
+ $code = PEAR_DEPENDENCY_BAD_DEPENDENCY;
+ switch ($relation) {
+ case 'gt': case 'ge': case 'eq':
+ // upgrade
+ $have_major = preg_replace('/\D.*/', '', $version);
+ $need_major = preg_replace('/\D.*/', '', $req);
+ if ($need_major > $have_major) {
+ $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL :
+ PEAR_DEPENDENCY_UPGRADE_MAJOR;
+ } else {
+ $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL :
+ PEAR_DEPENDENCY_UPGRADE_MINOR;
+ }
+ break;
+ case 'lt': case 'le': case 'ne':
+ $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL :
+ PEAR_DEPENDENCY_CONFLICT;
+ break;
+ }
+ return $code;
+ }
+
+ // }}}
+}
+?>
diff --git a/3dparty/PEAR/Downloader.php b/3dparty/PEAR/Downloader.php
new file mode 100644
index 00000000000..5e16dc5ffb1
--- /dev/null
+++ b/3dparty/PEAR/Downloader.php
@@ -0,0 +1,680 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | Martin Jansen <mj@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Dependency.php';
+require_once 'PEAR/Remote.php';
+require_once 'System.php';
+
+
+define('PEAR_INSTALLER_OK', 1);
+define('PEAR_INSTALLER_FAILED', 0);
+define('PEAR_INSTALLER_SKIPPED', -1);
+define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
+
+/**
+ * Administration class used to download PEAR packages and maintain the
+ * installed package database.
+ *
+ * @since PEAR 1.4
+ * @author Greg Beaver <cellog@php.net>
+ */
+class PEAR_Downloader extends PEAR_Common
+{
+ /**
+ * @var PEAR_Config
+ * @access private
+ */
+ var $_config;
+
+ /**
+ * @var PEAR_Registry
+ * @access private
+ */
+ var $_registry;
+
+ /**
+ * @var PEAR_Remote
+ * @access private
+ */
+ var $_remote;
+
+ /**
+ * Preferred Installation State (snapshot, devel, alpha, beta, stable)
+ * @var string|null
+ * @access private
+ */
+ var $_preferredState;
+
+ /**
+ * Options from command-line passed to Install.
+ *
+ * Recognized options:<br />
+ * - onlyreqdeps : install all required dependencies as well
+ * - alldeps : install all dependencies, including optional
+ * - installroot : base relative path to install files in
+ * - force : force a download even if warnings would prevent it
+ * @see PEAR_Command_Install
+ * @access private
+ * @var array
+ */
+ var $_options;
+
+ /**
+ * Downloaded Packages after a call to download().
+ *
+ * Format of each entry:
+ *
+ * <code>
+ * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+ * 'info' => array() // parsed package.xml
+ * );
+ * </code>
+ * @access private
+ * @var array
+ */
+ var $_downloadedPackages = array();
+
+ /**
+ * Packages slated for download.
+ *
+ * This is used to prevent downloading a package more than once should it be a dependency
+ * for two packages to be installed.
+ * Format of each entry:
+ *
+ * <pre>
+ * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+ * );
+ * </pre>
+ * @access private
+ * @var array
+ */
+ var $_toDownload = array();
+
+ /**
+ * Array of every package installed, with names lower-cased.
+ *
+ * Format:
+ * <code>
+ * array('package1' => 0, 'package2' => 1, );
+ * </code>
+ * @var array
+ */
+ var $_installed = array();
+
+ /**
+ * @var array
+ * @access private
+ */
+ var $_errorStack = array();
+
+ // {{{ PEAR_Downloader()
+
+ function PEAR_Downloader(&$ui, $options, &$config)
+ {
+ $this->_options = $options;
+ $this->_config = &$config;
+ $this->_preferredState = $this->_config->get('preferred_state');
+ $this->ui = &$ui;
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+
+ $php_dir = $this->_config->get('php_dir');
+ if (isset($this->_options['installroot'])) {
+ if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+ $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1);
+ }
+ $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']);
+ }
+ $this->_registry = &new PEAR_Registry($php_dir);
+ $this->_remote = &new PEAR_Remote($config);
+
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $this->_installed = $this->_registry->listPackages();
+ array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);'));
+ $this->_installed = array_flip($this->_installed);
+ }
+ parent::PEAR_Common();
+ }
+
+ // }}}
+ // {{{ configSet()
+ function configSet($key, $value, $layer = 'user')
+ {
+ $this->_config->set($key, $value, $layer);
+ $this->_preferredState = $this->_config->get('preferred_state');
+ if (!$this->_preferredState) {
+ // don't inadvertantly use a non-set preferred_state
+ $this->_preferredState = null;
+ }
+ }
+
+ // }}}
+ // {{{ setOptions()
+ function setOptions($options)
+ {
+ $this->_options = $options;
+ }
+
+ // }}}
+ // {{{ _downloadFile()
+ /**
+ * @param string filename to download
+ * @param string version/state
+ * @param string original value passed to command-line
+ * @param string|null preferred state (snapshot/devel/alpha/beta/stable)
+ * Defaults to configuration preferred state
+ * @return null|PEAR_Error|string
+ * @access private
+ */
+ function _downloadFile($pkgfile, $version, $origpkgfile, $state = null)
+ {
+ if (is_null($state)) {
+ $state = $this->_preferredState;
+ }
+ // {{{ check the package filename, and whether it's already installed
+ $need_download = false;
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ } elseif (!@is_file($pkgfile)) {
+ if ($this->validPackageName($pkgfile)) {
+ if ($this->_registry->packageExists($pkgfile)) {
+ if (empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $errors[] = "$pkgfile already installed";
+ return;
+ }
+ }
+ $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
+ $need_download = true;
+ } else {
+ if (strlen($pkgfile)) {
+ $errors[] = "Could not open the package file: $pkgfile";
+ } else {
+ $errors[] = "No package file given";
+ }
+ return;
+ }
+ }
+ // }}}
+
+ // {{{ Download package -----------------------------------------------
+ if ($need_download) {
+ $downloaddir = $this->_config->get('download_dir');
+ if (empty($downloaddir)) {
+ if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+ return $downloaddir;
+ }
+ $this->log(3, '+ tmp dir created at ' . $downloaddir);
+ }
+ $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+ $this->pushErrorHandling(PEAR_ERROR_RETURN);
+ $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+ $this->popErrorHandling();
+ if (PEAR::isError($file)) {
+ if ($this->validPackageName($origpkgfile)) {
+ if (!PEAR::isError($info = $this->_remote->call('package.info',
+ $origpkgfile))) {
+ if (!count($info['releases'])) {
+ return $this->raiseError('Package ' . $origpkgfile .
+ ' has no releases');
+ } else {
+ return $this->raiseError('No releases of preferred state "'
+ . $state . '" exist for package ' . $origpkgfile .
+ '. Use ' . $origpkgfile . '-state to install another' .
+ ' state (like ' . $origpkgfile .'-beta)',
+ PEAR_INSTALLER_ERROR_NO_PREF_STATE);
+ }
+ } else {
+ return $pkgfile;
+ }
+ } else {
+ return $this->raiseError($file);
+ }
+ }
+ $pkgfile = $file;
+ }
+ // }}}
+ return $pkgfile;
+ }
+ // }}}
+ // {{{ getPackageDownloadUrl()
+
+ function getPackageDownloadUrl($package, $version = null)
+ {
+ if ($version) {
+ $package .= "-$version";
+ }
+ if ($this === null || $this->_config === null) {
+ $package = "http://pear.php.net/get/$package";
+ } else {
+ $package = "http://" . $this->_config->get('master_server') .
+ "/get/$package";
+ }
+ if (!extension_loaded("zlib")) {
+ $package .= '?uncompress=yes';
+ }
+ return $package;
+ }
+
+ // }}}
+ // {{{ extractDownloadFileName($pkgfile, &$version)
+
+ function extractDownloadFileName($pkgfile, &$version)
+ {
+ if (@is_file($pkgfile)) {
+ return $pkgfile;
+ }
+ // regex defined in Common.php
+ if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) {
+ $version = (isset($m[3])) ? $m[3] : null;
+ return $m[1];
+ }
+ $version = null;
+ return $pkgfile;
+ }
+
+ // }}}
+
+ // }}}
+ // {{{ getDownloadedPackages()
+
+ /**
+ * Retrieve a list of downloaded packages after a call to {@link download()}.
+ *
+ * Also resets the list of downloaded packages.
+ * @return array
+ */
+ function getDownloadedPackages()
+ {
+ $ret = $this->_downloadedPackages;
+ $this->_downloadedPackages = array();
+ $this->_toDownload = array();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ download()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * BC-compatible method name
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function download($packages)
+ {
+ return $this->doDownload($packages);
+ }
+
+ // }}}
+ // {{{ doDownload()
+
+ /**
+ * Download any files and their dependencies, if necessary
+ *
+ * @param array a mixed list of package names, local files, or package.xml
+ */
+ function doDownload($packages)
+ {
+ $mywillinstall = array();
+ $state = $this->_preferredState;
+
+ // {{{ download files in this list if necessary
+ foreach($packages as $pkgfile) {
+ $need_download = false;
+ if (!is_file($pkgfile)) {
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ $need_download = true;
+ }
+ $pkgfile = $this->_downloadNonFile($pkgfile);
+ if (PEAR::isError($pkgfile)) {
+ return $pkgfile;
+ }
+ if ($pkgfile === false) {
+ continue;
+ }
+ } // end is_file()
+
+ $tempinfo = $this->infoFromAny($pkgfile);
+ if ($need_download) {
+ $this->_toDownload[] = $tempinfo['package'];
+ }
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ // ignore dependencies if there are any errors
+ if (!PEAR::isError($tempinfo)) {
+ $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+ }
+ }
+ $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
+ 'file' => $pkgfile, 'info' => $tempinfo);
+ } // end foreach($packages)
+ // }}}
+
+ // {{{ extract dependencies from downloaded files and then download
+ // them if necessary
+ if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+ $deppackages = array();
+ foreach ($mywillinstall as $package => $alldeps) {
+ if (!is_array($alldeps)) {
+ // there are no dependencies
+ continue;
+ }
+ $fail = false;
+ foreach ($alldeps as $info) {
+ if ($info['type'] != 'pkg') {
+ continue;
+ }
+ $ret = $this->_processDependency($package, $info, $mywillinstall);
+ if ($ret === false) {
+ continue;
+ }
+ if ($ret === 0) {
+ $fail = true;
+ continue;
+ }
+ if (PEAR::isError($ret)) {
+ return $ret;
+ }
+ $deppackages[] = $ret;
+ } // foreach($alldeps
+ if ($fail) {
+ $deppackages = array();
+ }
+ }
+
+ if (count($deppackages)) {
+ $this->doDownload($deppackages);
+ }
+ } // }}} if --alldeps or --onlyreqdeps
+ }
+
+ // }}}
+ // {{{ _downloadNonFile($pkgfile)
+
+ /**
+ * @return false|PEAR_Error|string false if loop should be broken out of,
+ * string if the file was downloaded,
+ * PEAR_Error on exception
+ * @access private
+ */
+ function _downloadNonFile($pkgfile)
+ {
+ $origpkgfile = $pkgfile;
+ $state = null;
+ $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
+ if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile);
+ }
+ if (!$this->validPackageName($pkgfile)) {
+ return $this->raiseError("Package name '$pkgfile' not valid");
+ }
+ // ignore packages that are installed unless we are upgrading
+ $curinfo = $this->_registry->packageInfo($pkgfile);
+ if ($this->_registry->packageExists($pkgfile)
+ && empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+ $this->log(0, "Package '{$curinfo['package']}' already installed, skipping");
+ return false;
+ }
+ if (in_array($pkgfile, $this->_toDownload)) {
+ return false;
+ }
+ $releases = $this->_remote->call('package.info', $pkgfile, 'releases', true);
+ if (!count($releases)) {
+ return $this->raiseError("No releases found for package '$pkgfile'");
+ }
+ // Want a specific version/state
+ if ($version !== null) {
+ // Passed Foo-1.2
+ if ($this->validPackageVersion($version)) {
+ if (!isset($releases[$version])) {
+ return $this->raiseError("No release with version '$version' found for '$pkgfile'");
+ }
+ // Passed Foo-alpha
+ } elseif (in_array($version, $this->getReleaseStates())) {
+ $state = $version;
+ $version = 0;
+ foreach ($releases as $ver => $inf) {
+ if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ break;
+ }
+ }
+ if ($version == 0) {
+ return $this->raiseError("No release with state '$state' found for '$pkgfile'");
+ }
+ // invalid suffix passed
+ } else {
+ return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ".
+ "version number or release state");
+ }
+ // Guess what to download
+ } else {
+ $states = $this->betterStates($this->_preferredState, true);
+ $possible = false;
+ $version = 0;
+ $higher_version = 0;
+ $prev_hi_ver = 0;
+ foreach ($releases as $ver => $inf) {
+ if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) {
+ $version = $ver;
+ break;
+ } else {
+ $ver = (string)$ver;
+ if (version_compare($prev_hi_ver, $ver) < 0) {
+ $prev_hi_ver = $higher_version = $ver;
+ }
+ }
+ }
+ if ($version === 0 && !isset($this->_options['force'])) {
+ return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) .
+ "' found for '$pkgfile'");
+ } elseif ($version === 0) {
+ $this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " .
+ "than state '$this->_preferredState'");
+ }
+ }
+ // Check if we haven't already the version
+ if (empty($this->_options['force']) && !is_null($curinfo)) {
+ if ($curinfo['version'] == $version) {
+ $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
+ return false;
+ } elseif (version_compare("$version", "{$curinfo['version']}") < 0) {
+ $this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " .
+ " is installed and {$curinfo['version']} is > requested '$version', skipping");
+ return false;
+ }
+ }
+ $this->_toDownload[] = $pkgfile;
+ return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state);
+ }
+
+ // }}}
+ // {{{ _processDependency($package, $info, $mywillinstall)
+
+ /**
+ * Process a dependency, download if necessary
+ * @param array dependency information from PEAR_Remote call
+ * @param array packages that will be installed in this iteration
+ * @return false|string|PEAR_Error
+ * @access private
+ * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
+ * in fact lower than the required value. This will be very important for BC dependencies
+ */
+ function _processDependency($package, $info, $mywillinstall)
+ {
+ $state = $this->_preferredState;
+ if (!isset($this->_options['alldeps']) && isset($info['optional']) &&
+ $info['optional'] == 'yes') {
+ // skip optional deps
+ $this->log(0, "skipping Package '$package' optional dependency '$info[name]'");
+ return false;
+ }
+ // {{{ get releases
+ $releases = $this->_remote->call('package.info', $info['name'], 'releases', true);
+ if (PEAR::isError($releases)) {
+ return $releases;
+ }
+ if (!count($releases)) {
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ $this->pushError("Package '$package' dependency '$info[name]' ".
+ "has no releases");
+ }
+ return false;
+ }
+ $found = false;
+ $save = $releases;
+ while(count($releases) && !$found) {
+ if (!empty($state) && $state != 'any') {
+ list($release_version, $release) = each($releases);
+ if ($state != $release['state'] &&
+ !in_array($release['state'], $this->betterStates($state)))
+ {
+ // drop this release - it ain't stable enough
+ array_shift($releases);
+ } else {
+ $found = true;
+ }
+ } else {
+ $found = true;
+ }
+ }
+ if (!count($releases) && !$found) {
+ $get = array();
+ foreach($save as $release) {
+ $get = array_merge($get,
+ $this->betterStates($release['state'], true));
+ }
+ $savestate = array_shift($get);
+ $this->pushError( "Release for '$package' dependency '$info[name]' " .
+ "has state '$savestate', requires '$state'");
+ return 0;
+ }
+ if (in_array(strtolower($info['name']), $this->_toDownload) ||
+ isset($mywillinstall[strtolower($info['name'])])) {
+ // skip upgrade check for packages we will install
+ return false;
+ }
+ if (!isset($this->_installed[strtolower($info['name'])])) {
+ // check to see if we can install the specific version required
+ if ($info['rel'] == 'eq') {
+ return $info['name'] . '-' . $info['version'];
+ }
+ // skip upgrade check for packages we don't have installed
+ return $info['name'];
+ }
+ // }}}
+
+ // {{{ see if a dependency must be upgraded
+ $inst_version = $this->_registry->packageInfo($info['name'], 'version');
+ if (!isset($info['version'])) {
+ // this is a rel='has' dependency, check against latest
+ if (version_compare($release_version, $inst_version, 'le')) {
+ return false;
+ } else {
+ return $info['name'];
+ }
+ }
+ if (version_compare($info['version'], $inst_version, 'le')) {
+ // installed version is up-to-date
+ return false;
+ }
+ return $info['name'];
+ }
+
+ // }}}
+ // {{{ _downloadCallback()
+
+ function _downloadCallback($msg, $params = null)
+ {
+ switch ($msg) {
+ case 'saveas':
+ $this->log(1, "downloading $params ...");
+ break;
+ case 'done':
+ $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
+ break;
+ case 'bytesread':
+ static $bytes;
+ if (empty($bytes)) {
+ $bytes = 0;
+ }
+ if (!($bytes % 10240)) {
+ $this->log(1, '.', false);
+ }
+ $bytes += $params;
+ break;
+ case 'start':
+ $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)");
+ break;
+ }
+ if (method_exists($this->ui, '_downloadCallback'))
+ $this->ui->_downloadCallback($msg, $params);
+ }
+
+ // }}}
+ // {{{ _prependPath($path, $prepend)
+
+ function _prependPath($path, $prepend)
+ {
+ if (strlen($prepend) > 0) {
+ if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+ $path = $prepend . substr($path, 2);
+ } else {
+ $path = $prepend . $path;
+ }
+ }
+ return $path;
+ }
+ // }}}
+ // {{{ pushError($errmsg, $code)
+
+ /**
+ * @param string
+ * @param integer
+ */
+ function pushError($errmsg, $code = -1)
+ {
+ array_push($this->_errorStack, array($errmsg, $code));
+ }
+
+ // }}}
+ // {{{ getErrorMsgs()
+
+ function getErrorMsgs()
+ {
+ $msgs = array();
+ $errs = $this->_errorStack;
+ foreach ($errs as $err) {
+ $msgs[] = $err[0];
+ }
+ $this->_errorStack = array();
+ return $msgs;
+ }
+
+ // }}}
+}
+// }}}
+
+?>
diff --git a/3dparty/PEAR/ErrorStack.php b/3dparty/PEAR/ErrorStack.php
new file mode 100644
index 00000000000..da52f08d345
--- /dev/null
+++ b/3dparty/PEAR/ErrorStack.php
@@ -0,0 +1,981 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Gregory Beaver <cellog@php.net> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: ErrorStack.php,v 1.7.2.5 2005/01/01 21:26:51 cellog Exp $
+
+/**
+ * Error Stack Implementation
+ *
+ * This is an incredibly simple implementation of a very complex error handling
+ * facility. It contains the ability
+ * to track multiple errors from multiple packages simultaneously. In addition,
+ * it can track errors of many levels, save data along with the error, context
+ * information such as the exact file, line number, class and function that
+ * generated the error, and if necessary, it can raise a traditional PEAR_Error.
+ * It has built-in support for PEAR::Log, to log errors as they occur
+ *
+ * Since version 0.2alpha, it is also possible to selectively ignore errors,
+ * through the use of an error callback, see {@link pushCallback()}
+ *
+ * Since version 0.3alpha, it is possible to specify the exception class
+ * returned from {@link push()}
+ *
+ * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can
+ * still be done quite handily in an error callback or by manipulating the returned array
+ * @author Greg Beaver <cellog@php.net>
+ * @version PEAR1.3.2 (beta)
+ * @package PEAR_ErrorStack
+ * @category Debugging
+ * @license http://www.php.net/license/3_0.txt PHP License v3.0
+ */
+
+/**
+ * Singleton storage
+ *
+ * Format:
+ * <pre>
+ * array(
+ * 'package1' => PEAR_ErrorStack object,
+ * 'package2' => PEAR_ErrorStack object,
+ * ...
+ * )
+ * </pre>
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
+
+/**
+ * Global error callback (default)
+ *
+ * This is only used if set to non-false. * is the default callback for
+ * all packages, whereas specific packages may set a default callback
+ * for all instances, regardless of whether they are a singleton or not.
+ *
+ * To exclude non-singletons, only set the local callback for the singleton
+ * @see PEAR_ErrorStack::setDefaultCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
+ '*' => false,
+);
+
+/**
+ * Global Log object (default)
+ *
+ * This is only used if set to non-false. Use to set a default log object for
+ * all stacks, regardless of instantiation order or location
+ * @see PEAR_ErrorStack::setDefaultLogger()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
+
+/**
+ * Global Overriding Callback
+ *
+ * This callback will override any error callbacks that specific loggers have set.
+ * Use with EXTREME caution
+ * @see PEAR_ErrorStack::staticPushCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+
+/**#@+
+ * One of four possible return values from the error Callback
+ * @see PEAR_ErrorStack::_errorCallback()
+ */
+/**
+ * If this is returned, then the error will be both pushed onto the stack
+ * and logged.
+ */
+define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
+/**
+ * If this is returned, then the error will only be pushed onto the stack,
+ * and not logged.
+ */
+define('PEAR_ERRORSTACK_PUSH', 2);
+/**
+ * If this is returned, then the error will only be logged, but not pushed
+ * onto the error stack.
+ */
+define('PEAR_ERRORSTACK_LOG', 3);
+/**
+ * If this is returned, then the error is completely ignored.
+ */
+define('PEAR_ERRORSTACK_IGNORE', 4);
+/**
+ * If this is returned, then the error is logged and die() is called.
+ */
+define('PEAR_ERRORSTACK_DIE', 5);
+/**#@-*/
+
+/**
+ * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
+ * the singleton method.
+ */
+define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
+
+/**
+ * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
+ * that has no __toString() method
+ */
+define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
+/**
+ * Error Stack Implementation
+ *
+ * Usage:
+ * <code>
+ * // global error stack
+ * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
+ * // local error stack
+ * $local_stack = new PEAR_ErrorStack('MyPackage');
+ * </code>
+ * @copyright 2004 Gregory Beaver
+ * @package PEAR_ErrorStack
+ * @license http://www.php.net/license/3_0.txt PHP License
+ */
+class PEAR_ErrorStack {
+ /**
+ * Errors are stored in the order that they are pushed on the stack.
+ * @since 0.4alpha Errors are no longer organized by error level.
+ * This renders pop() nearly unusable, and levels could be more easily
+ * handled in a callback anyway
+ * @var array
+ * @access private
+ */
+ var $_errors = array();
+
+ /**
+ * Storage of errors by level.
+ *
+ * Allows easy retrieval and deletion of only errors from a particular level
+ * @since PEAR 1.4.0dev
+ * @var array
+ * @access private
+ */
+ var $_errorsByLevel = array();
+
+ /**
+ * Package name this error stack represents
+ * @var string
+ * @access protected
+ */
+ var $_package;
+
+ /**
+ * Determines whether a PEAR_Error is thrown upon every error addition
+ * @var boolean
+ * @access private
+ */
+ var $_compat = false;
+
+ /**
+ * If set to a valid callback, this will be used to generate the error
+ * message from the error code, otherwise the message passed in will be
+ * used
+ * @var false|string|array
+ * @access private
+ */
+ var $_msgCallback = false;
+
+ /**
+ * If set to a valid callback, this will be used to generate the error
+ * context for an error. For PHP-related errors, this will be a file
+ * and line number as retrieved from debug_backtrace(), but can be
+ * customized for other purposes. The error might actually be in a separate
+ * configuration file, or in a database query.
+ * @var false|string|array
+ * @access protected
+ */
+ var $_contextCallback = false;
+
+ /**
+ * If set to a valid callback, this will be called every time an error
+ * is pushed onto the stack. The return value will be used to determine
+ * whether to allow an error to be pushed or logged.
+ *
+ * The return value must be one an PEAR_ERRORSTACK_* constant
+ * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+ * @var false|string|array
+ * @access protected
+ */
+ var $_errorCallback = array();
+
+ /**
+ * PEAR::Log object for logging errors
+ * @var false|Log
+ * @access protected
+ */
+ var $_logger = false;
+
+ /**
+ * Error messages - designed to be overridden
+ * @var array
+ * @abstract
+ */
+ var $_errorMsgs = array();
+
+ /**
+ * Set up a new error stack
+ *
+ * @param string $package name of the package this error stack represents
+ * @param callback $msgCallback callback used for error message generation
+ * @param callback $contextCallback callback used for context generation,
+ * defaults to {@link getFileLine()}
+ * @param boolean $throwPEAR_Error
+ */
+ function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
+ $throwPEAR_Error = false)
+ {
+ $this->_package = $package;
+ $this->setMessageCallback($msgCallback);
+ $this->setContextCallback($contextCallback);
+ $this->_compat = $throwPEAR_Error;
+ }
+
+ /**
+ * Return a single error stack for this package.
+ *
+ * Note that all parameters are ignored if the stack for package $package
+ * has already been instantiated
+ * @param string $package name of the package this error stack represents
+ * @param callback $msgCallback callback used for error message generation
+ * @param callback $contextCallback callback used for context generation,
+ * defaults to {@link getFileLine()}
+ * @param boolean $throwPEAR_Error
+ * @param string $stackClass class to instantiate
+ * @static
+ * @return PEAR_ErrorStack
+ */
+ function &singleton($package, $msgCallback = false, $contextCallback = false,
+ $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
+ {
+ if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+ return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
+ }
+ if (!class_exists($stackClass)) {
+ if (function_exists('debug_backtrace')) {
+ $trace = debug_backtrace();
+ }
+ PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
+ 'exception', array('stackclass' => $stackClass),
+ 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
+ false, $trace);
+ }
+ return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
+ &new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
+ }
+
+ /**
+ * Internal error handler for PEAR_ErrorStack class
+ *
+ * Dies if the error is an exception (and would have died anyway)
+ * @access private
+ */
+ function _handleError($err)
+ {
+ if ($err['level'] == 'exception') {
+ $message = $err['message'];
+ if (isset($_SERVER['REQUEST_URI'])) {
+ echo '<br />';
+ } else {
+ echo "\n";
+ }
+ var_dump($err['context']);
+ die($message);
+ }
+ }
+
+ /**
+ * Set up a PEAR::Log object for all error stacks that don't have one
+ * @param Log $log
+ * @static
+ */
+ function setDefaultLogger(&$log)
+ {
+ if (is_object($log) && method_exists($log, 'log') ) {
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+ } elseif (is_callable($log)) {
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+ }
+ }
+
+ /**
+ * Set up a PEAR::Log object for this error stack
+ * @param Log $log
+ */
+ function setLogger(&$log)
+ {
+ if (is_object($log) && method_exists($log, 'log') ) {
+ $this->_logger = &$log;
+ } elseif (is_callable($log)) {
+ $this->_logger = &$log;
+ }
+ }
+
+ /**
+ * Set an error code => error message mapping callback
+ *
+ * This method sets the callback that can be used to generate error
+ * messages for any instance
+ * @param array|string Callback function/method
+ */
+ function setMessageCallback($msgCallback)
+ {
+ if (!$msgCallback) {
+ $this->_msgCallback = array(&$this, 'getErrorMessage');
+ } else {
+ if (is_callable($msgCallback)) {
+ $this->_msgCallback = $msgCallback;
+ }
+ }
+ }
+
+ /**
+ * Get an error code => error message mapping callback
+ *
+ * This method returns the current callback that can be used to generate error
+ * messages
+ * @return array|string|false Callback function/method or false if none
+ */
+ function getMessageCallback()
+ {
+ return $this->_msgCallback;
+ }
+
+ /**
+ * Sets a default callback to be used by all error stacks
+ *
+ * This method sets the callback that can be used to generate error
+ * messages for a singleton
+ * @param array|string Callback function/method
+ * @param string Package name, or false for all packages
+ * @static
+ */
+ function setDefaultCallback($callback = false, $package = false)
+ {
+ if (!is_callable($callback)) {
+ $callback = false;
+ }
+ $package = $package ? $package : '*';
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
+ }
+
+ /**
+ * Set a callback that generates context information (location of error) for an error stack
+ *
+ * This method sets the callback that can be used to generate context
+ * information for an error. Passing in NULL will disable context generation
+ * and remove the expensive call to debug_backtrace()
+ * @param array|string|null Callback function/method
+ */
+ function setContextCallback($contextCallback)
+ {
+ if ($contextCallback === null) {
+ return $this->_contextCallback = false;
+ }
+ if (!$contextCallback) {
+ $this->_contextCallback = array(&$this, 'getFileLine');
+ } else {
+ if (is_callable($contextCallback)) {
+ $this->_contextCallback = $contextCallback;
+ }
+ }
+ }
+
+ /**
+ * Set an error Callback
+ * If set to a valid callback, this will be called every time an error
+ * is pushed onto the stack. The return value will be used to determine
+ * whether to allow an error to be pushed or logged.
+ *
+ * The return value must be one of the ERRORSTACK_* constants.
+ *
+ * This functionality can be used to emulate PEAR's pushErrorHandling, and
+ * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
+ * the error stack or logging
+ * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+ * @see popCallback()
+ * @param string|array $cb
+ */
+ function pushCallback($cb)
+ {
+ array_push($this->_errorCallback, $cb);
+ }
+
+ /**
+ * Remove a callback from the error callback stack
+ * @see pushCallback()
+ * @return array|string|false
+ */
+ function popCallback()
+ {
+ if (!count($this->_errorCallback)) {
+ return false;
+ }
+ return array_pop($this->_errorCallback);
+ }
+
+ /**
+ * Set a temporary overriding error callback for every package error stack
+ *
+ * Use this to temporarily disable all existing callbacks (can be used
+ * to emulate the @ operator, for instance)
+ * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+ * @see staticPopCallback(), pushCallback()
+ * @param string|array $cb
+ * @static
+ */
+ function staticPushCallback($cb)
+ {
+ array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
+ }
+
+ /**
+ * Remove a temporary overriding error callback
+ * @see staticPushCallback()
+ * @return array|string|false
+ * @static
+ */
+ function staticPopCallback()
+ {
+ $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
+ if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
+ $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+ }
+ return $ret;
+ }
+
+ /**
+ * Add an error to the stack
+ *
+ * If the message generator exists, it is called with 2 parameters.
+ * - the current Error Stack object
+ * - an array that is in the same format as an error. Available indices
+ * are 'code', 'package', 'time', 'params', 'level', and 'context'
+ *
+ * Next, if the error should contain context information, this is
+ * handled by the context grabbing method.
+ * Finally, the error is pushed onto the proper error stack
+ * @param int $code Package-specific error code
+ * @param string $level Error level. This is NOT spell-checked
+ * @param array $params associative array of error parameters
+ * @param string $msg Error message, or a portion of it if the message
+ * is to be generated
+ * @param array $repackage If this error re-packages an error pushed by
+ * another package, place the array returned from
+ * {@link pop()} in this parameter
+ * @param array $backtrace Protected parameter: use this to pass in the
+ * {@link debug_backtrace()} that should be used
+ * to find error context
+ * @return PEAR_Error|array|Exception
+ * if compatibility mode is on, a PEAR_Error is also
+ * thrown. If the class Exception exists, then one
+ * is returned to allow code like:
+ * <code>
+ * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
+ * </code>
+ *
+ * The errorData property of the exception class will be set to the array
+ * that would normally be returned. If a PEAR_Error is returned, the userinfo
+ * property is set to the array
+ *
+ * Otherwise, an array is returned in this format:
+ * <code>
+ * array(
+ * 'code' => $code,
+ * 'params' => $params,
+ * 'package' => $this->_package,
+ * 'level' => $level,
+ * 'time' => time(),
+ * 'context' => $context,
+ * 'message' => $msg,
+ * //['repackage' => $err] repackaged error array/Exception class
+ * );
+ * </code>
+ */
+ function push($code, $level = 'error', $params = array(), $msg = false,
+ $repackage = false, $backtrace = false)
+ {
+ $context = false;
+ // grab error context
+ if ($this->_contextCallback) {
+ if (!$backtrace) {
+ $backtrace = debug_backtrace();
+ }
+ $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
+ }
+
+ // save error
+ $time = explode(' ', microtime());
+ $time = $time[1] + $time[0];
+ $err = array(
+ 'code' => $code,
+ 'params' => $params,
+ 'package' => $this->_package,
+ 'level' => $level,
+ 'time' => $time,
+ 'context' => $context,
+ 'message' => $msg,
+ );
+
+ // set up the error message, if necessary
+ if ($this->_msgCallback) {
+ $msg = call_user_func_array($this->_msgCallback,
+ array(&$this, $err));
+ $err['message'] = $msg;
+ }
+
+ if ($repackage) {
+ $err['repackage'] = $repackage;
+ }
+ $push = $log = true;
+ $die = false;
+ // try the overriding callback first
+ $callback = $this->staticPopCallback();
+ if ($callback) {
+ $this->staticPushCallback($callback);
+ }
+ if (!is_callable($callback)) {
+ // try the local callback next
+ $callback = $this->popCallback();
+ if (is_callable($callback)) {
+ $this->pushCallback($callback);
+ } else {
+ // try the default callback
+ $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
+ $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
+ }
+ }
+ if (is_callable($callback)) {
+ switch(call_user_func($callback, $err)){
+ case PEAR_ERRORSTACK_IGNORE:
+ return $err;
+ break;
+ case PEAR_ERRORSTACK_PUSH:
+ $log = false;
+ break;
+ case PEAR_ERRORSTACK_LOG:
+ $push = false;
+ break;
+ case PEAR_ERRORSTACK_DIE:
+ $die = true;
+ break;
+ // anything else returned has the same effect as pushandlog
+ }
+ }
+ if ($push) {
+ array_unshift($this->_errors, $err);
+ $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
+ }
+ if ($log) {
+ if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
+ $this->_log($err);
+ }
+ }
+ if ($die) {
+ die();
+ }
+ if ($this->_compat && $push) {
+ return $this->raiseError($msg, $code, null, null, $err);
+ }
+ return $err;
+ }
+
+ /**
+ * Static version of {@link push()}
+ *
+ * @param string $package Package name this error belongs to
+ * @param int $code Package-specific error code
+ * @param string $level Error level. This is NOT spell-checked
+ * @param array $params associative array of error parameters
+ * @param string $msg Error message, or a portion of it if the message
+ * is to be generated
+ * @param array $repackage If this error re-packages an error pushed by
+ * another package, place the array returned from
+ * {@link pop()} in this parameter
+ * @param array $backtrace Protected parameter: use this to pass in the
+ * {@link debug_backtrace()} that should be used
+ * to find error context
+ * @return PEAR_Error|null|Exception
+ * if compatibility mode is on, a PEAR_Error is also
+ * thrown. If the class Exception exists, then one
+ * is returned to allow code like:
+ * <code>
+ * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
+ * </code>
+ * @static
+ */
+ function staticPush($package, $code, $level = 'error', $params = array(),
+ $msg = false, $repackage = false, $backtrace = false)
+ {
+ $s = &PEAR_ErrorStack::singleton($package);
+ if ($s->_contextCallback) {
+ if (!$backtrace) {
+ if (function_exists('debug_backtrace')) {
+ $backtrace = debug_backtrace();
+ }
+ }
+ }
+ return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
+ }
+
+ /**
+ * Log an error using PEAR::Log
+ * @param array $err Error array
+ * @param array $levels Error level => Log constant map
+ * @access protected
+ */
+ function _log($err)
+ {
+ if ($this->_logger) {
+ $logger = &$this->_logger;
+ } else {
+ $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
+ }
+ if (is_a($logger, 'Log')) {
+ $levels = array(
+ 'exception' => PEAR_LOG_CRIT,
+ 'alert' => PEAR_LOG_ALERT,
+ 'critical' => PEAR_LOG_CRIT,
+ 'error' => PEAR_LOG_ERR,
+ 'warning' => PEAR_LOG_WARNING,
+ 'notice' => PEAR_LOG_NOTICE,
+ 'info' => PEAR_LOG_INFO,
+ 'debug' => PEAR_LOG_DEBUG);
+ if (isset($levels[$err['level']])) {
+ $level = $levels[$err['level']];
+ } else {
+ $level = PEAR_LOG_INFO;
+ }
+ $logger->log($err['message'], $level, $err);
+ } else { // support non-standard logs
+ call_user_func($logger, $err);
+ }
+ }
+
+
+ /**
+ * Pop an error off of the error stack
+ *
+ * @return false|array
+ * @since 0.4alpha it is no longer possible to specify a specific error
+ * level to return - the last error pushed will be returned, instead
+ */
+ function pop()
+ {
+ return @array_shift($this->_errors);
+ }
+
+ /**
+ * Determine whether there are any errors on the stack
+ * @param string|array Level name. Use to determine if any errors
+ * of level (string), or levels (array) have been pushed
+ * @return boolean
+ */
+ function hasErrors($level = false)
+ {
+ if ($level) {
+ return isset($this->_errorsByLevel[$level]);
+ }
+ return count($this->_errors);
+ }
+
+ /**
+ * Retrieve all errors since last purge
+ *
+ * @param boolean set in order to empty the error stack
+ * @param string level name, to return only errors of a particular severity
+ * @return array
+ */
+ function getErrors($purge = false, $level = false)
+ {
+ if (!$purge) {
+ if ($level) {
+ if (!isset($this->_errorsByLevel[$level])) {
+ return array();
+ } else {
+ return $this->_errorsByLevel[$level];
+ }
+ } else {
+ return $this->_errors;
+ }
+ }
+ if ($level) {
+ $ret = $this->_errorsByLevel[$level];
+ foreach ($this->_errorsByLevel[$level] as $i => $unused) {
+ // entries are references to the $_errors array
+ $this->_errorsByLevel[$level][$i] = false;
+ }
+ // array_filter removes all entries === false
+ $this->_errors = array_filter($this->_errors);
+ unset($this->_errorsByLevel[$level]);
+ return $ret;
+ }
+ $ret = $this->_errors;
+ $this->_errors = array();
+ $this->_errorsByLevel = array();
+ return $ret;
+ }
+
+ /**
+ * Determine whether there are any errors on a single error stack, or on any error stack
+ *
+ * The optional parameter can be used to test the existence of any errors without the need of
+ * singleton instantiation
+ * @param string|false Package name to check for errors
+ * @param string Level name to check for a particular severity
+ * @return boolean
+ * @static
+ */
+ function staticHasErrors($package = false, $level = false)
+ {
+ if ($package) {
+ if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+ return false;
+ }
+ return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
+ }
+ foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+ if ($obj->hasErrors($level)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get a list of all errors since last purge, organized by package
+ * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
+ * @param boolean $purge Set to purge the error stack of existing errors
+ * @param string $level Set to a level name in order to retrieve only errors of a particular level
+ * @param boolean $merge Set to return a flat array, not organized by package
+ * @param array $sortfunc Function used to sort a merged array - default
+ * sorts by time, and should be good for most cases
+ * @static
+ * @return array
+ */
+ function staticGetErrors($purge = false, $level = false, $merge = false,
+ $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
+ {
+ $ret = array();
+ if (!is_callable($sortfunc)) {
+ $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
+ }
+ foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+ $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
+ if ($test) {
+ if ($merge) {
+ $ret = array_merge($ret, $test);
+ } else {
+ $ret[$package] = $test;
+ }
+ }
+ }
+ if ($merge) {
+ usort($ret, $sortfunc);
+ }
+ return $ret;
+ }
+
+ /**
+ * Error sorting function, sorts by time
+ * @access private
+ */
+ function _sortErrors($a, $b)
+ {
+ if ($a['time'] == $b['time']) {
+ return 0;
+ }
+ if ($a['time'] < $b['time']) {
+ return 1;
+ }
+ return -1;
+ }
+
+ /**
+ * Standard file/line number/function/class context callback
+ *
+ * This function uses a backtrace generated from {@link debug_backtrace()}
+ * and so will not work at all in PHP < 4.3.0. The frame should
+ * reference the frame that contains the source of the error.
+ * @return array|false either array('file' => file, 'line' => line,
+ * 'function' => function name, 'class' => class name) or
+ * if this doesn't work, then false
+ * @param unused
+ * @param integer backtrace frame.
+ * @param array Results of debug_backtrace()
+ * @static
+ */
+ function getFileLine($code, $params, $backtrace = null)
+ {
+ if ($backtrace === null) {
+ return false;
+ }
+ $frame = 0;
+ $functionframe = 1;
+ if (!isset($backtrace[1])) {
+ $functionframe = 0;
+ } else {
+ while (isset($backtrace[$functionframe]['function']) &&
+ $backtrace[$functionframe]['function'] == 'eval' &&
+ isset($backtrace[$functionframe + 1])) {
+ $functionframe++;
+ }
+ }
+ if (isset($backtrace[$frame])) {
+ if (!isset($backtrace[$frame]['file'])) {
+ $frame++;
+ }
+ $funcbacktrace = $backtrace[$functionframe];
+ $filebacktrace = $backtrace[$frame];
+ $ret = array('file' => $filebacktrace['file'],
+ 'line' => $filebacktrace['line']);
+ // rearrange for eval'd code or create function errors
+ if (strpos($filebacktrace['file'], '(') &&
+ preg_match(';^(.*?)\((\d+)\) : (.*?)$;', $filebacktrace['file'],
+ $matches)) {
+ $ret['file'] = $matches[1];
+ $ret['line'] = $matches[2] + 0;
+ }
+ if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
+ if ($funcbacktrace['function'] != 'eval') {
+ if ($funcbacktrace['function'] == '__lambda_func') {
+ $ret['function'] = 'create_function() code';
+ } else {
+ $ret['function'] = $funcbacktrace['function'];
+ }
+ }
+ }
+ if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
+ $ret['class'] = $funcbacktrace['class'];
+ }
+ return $ret;
+ }
+ return false;
+ }
+
+ /**
+ * Standard error message generation callback
+ *
+ * This method may also be called by a custom error message generator
+ * to fill in template values from the params array, simply
+ * set the third parameter to the error message template string to use
+ *
+ * The special variable %__msg% is reserved: use it only to specify
+ * where a message passed in by the user should be placed in the template,
+ * like so:
+ *
+ * Error message: %msg% - internal error
+ *
+ * If the message passed like so:
+ *
+ * <code>
+ * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
+ * </code>
+ *
+ * The returned error message will be "Error message: server error 500 -
+ * internal error"
+ * @param PEAR_ErrorStack
+ * @param array
+ * @param string|false Pre-generated error message template
+ * @static
+ * @return string
+ */
+ function getErrorMessage(&$stack, $err, $template = false)
+ {
+ if ($template) {
+ $mainmsg = $template;
+ } else {
+ $mainmsg = $stack->getErrorMessageTemplate($err['code']);
+ }
+ $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
+ if (count($err['params'])) {
+ foreach ($err['params'] as $name => $val) {
+ if (is_array($val)) {
+ // @ is needed in case $val is a multi-dimensional array
+ $val = @implode(', ', $val);
+ }
+ if (is_object($val)) {
+ if (method_exists($val, '__toString')) {
+ $val = $val->__toString();
+ } else {
+ PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
+ 'warning', array('obj' => get_class($val)),
+ 'object %obj% passed into getErrorMessage, but has no __toString() method');
+ $val = 'Object';
+ }
+ }
+ $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
+ }
+ }
+ return $mainmsg;
+ }
+
+ /**
+ * Standard Error Message Template generator from code
+ * @return string
+ */
+ function getErrorMessageTemplate($code)
+ {
+ if (!isset($this->_errorMsgs[$code])) {
+ return '%__msg%';
+ }
+ return $this->_errorMsgs[$code];
+ }
+
+ /**
+ * Set the Error Message Template array
+ *
+ * The array format must be:
+ * <pre>
+ * array(error code => 'message template',...)
+ * </pre>
+ *
+ * Error message parameters passed into {@link push()} will be used as input
+ * for the error message. If the template is 'message %foo% was %bar%', and the
+ * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
+ * be 'message one was six'
+ * @return string
+ */
+ function setErrorMessageTemplate($template)
+ {
+ $this->_errorMsgs = $template;
+ }
+
+
+ /**
+ * emulate PEAR::raiseError()
+ *
+ * @return PEAR_Error
+ */
+ function raiseError()
+ {
+ require_once 'PEAR.php';
+ $args = func_get_args();
+ return call_user_func_array(array('PEAR', 'raiseError'), $args);
+ }
+}
+$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
+$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
+?> \ No newline at end of file
diff --git a/3dparty/PEAR/Exception.php b/3dparty/PEAR/Exception.php
new file mode 100644
index 00000000000..c735c16b398
--- /dev/null
+++ b/3dparty/PEAR/Exception.php
@@ -0,0 +1,359 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
+// +----------------------------------------------------------------------+
+// | PEAR_Exception |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 The PEAR 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 at through the world-wide-web at |
+// | 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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
+// | Hans Lellelid <hans@velum.net> |
+// | Bertrand Mansion <bmansion@mamasam.com> |
+// | Greg Beaver <cellog@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Exception.php,v 1.4.2.2 2004/12/31 19:01:52 cellog Exp $
+
+
+/**
+ * Base PEAR_Exception Class
+ *
+ * WARNING: This code should be considered stable, but the API is
+ * subject to immediate and drastic change, so API stability is
+ * at best alpha
+ *
+ * 1) Features:
+ *
+ * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
+ * - Definable triggers, shot when exceptions occur
+ * - Pretty and informative error messages
+ * - Added more context info available (like class, method or cause)
+ * - cause can be a PEAR_Exception or an array of mixed
+ * PEAR_Exceptions/PEAR_ErrorStack warnings
+ * - callbacks for specific exception classes and their children
+ *
+ * 2) Ideas:
+ *
+ * - Maybe a way to define a 'template' for the output
+ *
+ * 3) Inherited properties from PHP Exception Class:
+ *
+ * protected $message
+ * protected $code
+ * protected $line
+ * protected $file
+ * private $trace
+ *
+ * 4) Inherited methods from PHP Exception Class:
+ *
+ * __clone
+ * __construct
+ * getMessage
+ * getCode
+ * getFile
+ * getLine
+ * getTraceSafe
+ * getTraceSafeAsString
+ * __toString
+ *
+ * 5) Usage example
+ *
+ * <code>
+ * require_once 'PEAR/Exception.php';
+ *
+ * class Test {
+ * function foo() {
+ * throw new PEAR_Exception('Error Message', ERROR_CODE);
+ * }
+ * }
+ *
+ * function myLogger($pear_exception) {
+ * echo $pear_exception->getMessage();
+ * }
+ * // each time a exception is thrown the 'myLogger' will be called
+ * // (its use is completely optional)
+ * PEAR_Exception::addObserver('myLogger');
+ * $test = new Test;
+ * try {
+ * $test->foo();
+ * } catch (PEAR_Exception $e) {
+ * print $e;
+ * }
+ * </code>
+ *
+ * @since PHP 5
+ * @package PEAR
+ * @version $Revision: 1.4.2.2 $
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Hans Lellelid <hans@velum.net>
+ * @author Bertrand Mansion <bmansion@mamasam.com>
+ *
+ */
+class PEAR_Exception extends Exception
+{
+ const OBSERVER_PRINT = -2;
+ const OBSERVER_TRIGGER = -4;
+ const OBSERVER_DIE = -8;
+ protected $cause;
+ private static $_observers = array();
+ private static $_uniqueid = 0;
+ private $_trace;
+
+ /**
+ * Supported signatures:
+ * PEAR_Exception(string $message);
+ * PEAR_Exception(string $message, int $code);
+ * PEAR_Exception(string $message, Exception $cause);
+ * PEAR_Exception(string $message, Exception $cause, int $code);
+ * PEAR_Exception(string $message, array $causes);
+ * PEAR_Exception(string $message, array $causes, int $code);
+ */
+ public function __construct($message, $p2 = null, $p3 = null)
+ {
+ if (is_int($p2)) {
+ $code = $p2;
+ $this->cause = null;
+ } elseif ($p2 instanceof Exception || is_array($p2)) {
+ $code = $p3;
+ if (is_array($p2) && isset($p2['message'])) {
+ // fix potential problem of passing in a single warning
+ $p2 = array($p2);
+ }
+ $this->cause = $p2;
+ } else {
+ $code = null;
+ $this->cause = null;
+ }
+ parent::__construct($message, $code);
+ $this->signal();
+ }
+
+ /**
+ * @param mixed $callback - A valid php callback, see php func is_callable()
+ * - A PEAR_Exception::OBSERVER_* constant
+ * - An array(const PEAR_Exception::OBSERVER_*,
+ * mixed $options)
+ * @param string $label The name of the observer. Use this if you want
+ * to remove it later with removeObserver()
+ */
+ public static function addObserver($callback, $label = 'default')
+ {
+ self::$_observers[$label] = $callback;
+ }
+
+ public static function removeObserver($label = 'default')
+ {
+ unset(self::$_observers[$label]);
+ }
+
+ /**
+ * @return int unique identifier for an observer
+ */
+ public static function getUniqueId()
+ {
+ return self::$_uniqueid++;
+ }
+
+ private function signal()
+ {
+ foreach (self::$_observers as $func) {
+ if (is_callable($func)) {
+ call_user_func($func, $this);
+ continue;
+ }
+ settype($func, 'array');
+ switch ($func[0]) {
+ case self::OBSERVER_PRINT :
+ $f = (isset($func[1])) ? $func[1] : '%s';
+ printf($f, $this->getMessage());
+ break;
+ case self::OBSERVER_TRIGGER :
+ $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
+ trigger_error($this->getMessage(), $f);
+ break;
+ case self::OBSERVER_DIE :
+ $f = (isset($func[1])) ? $func[1] : '%s';
+ die(printf($f, $this->getMessage()));
+ break;
+ default:
+ trigger_error('invalid observer type', E_USER_WARNING);
+ }
+ }
+ }
+
+ /**
+ * Return specific error information that can be used for more detailed
+ * error messages or translation.
+ *
+ * This method may be overridden in child exception classes in order
+ * to add functionality not present in PEAR_Exception and is a placeholder
+ * to define API
+ *
+ * The returned array must be an associative array of parameter => value like so:
+ * <pre>
+ * array('name' => $name, 'context' => array(...))
+ * </pre>
+ * @return array
+ */
+ public function getErrorData()
+ {
+ return array();
+ }
+
+ /**
+ * Returns the exception that caused this exception to be thrown
+ * @access public
+ * @return Exception|array The context of the exception
+ */
+ public function getCause()
+ {
+ return $this->cause;
+ }
+
+ /**
+ * Function must be public to call on caused exceptions
+ * @param array
+ */
+ public function getCauseMessage(&$causes)
+ {
+ $trace = $this->getTraceSafe();
+ $cause = array('class' => get_class($this),
+ 'message' => $this->message,
+ 'file' => 'unknown',
+ 'line' => 'unknown');
+ if (isset($trace[0])) {
+ if (isset($trace[0]['file'])) {
+ $cause['file'] = $trace[0]['file'];
+ $cause['line'] = $trace[0]['line'];
+ }
+ }
+ if ($this->cause instanceof PEAR_Exception) {
+ $this->cause->getCauseMessage($causes);
+ }
+ if (is_array($this->cause)) {
+ foreach ($this->cause as $cause) {
+ if ($cause instanceof PEAR_Exception) {
+ $cause->getCauseMessage($causes);
+ } elseif (is_array($cause) && isset($cause['message'])) {
+ // PEAR_ErrorStack warning
+ $causes[] = array(
+ 'class' => $cause['package'],
+ 'message' => $cause['message'],
+ 'file' => isset($cause['context']['file']) ?
+ $cause['context']['file'] :
+ 'unknown',
+ 'line' => isset($cause['context']['line']) ?
+ $cause['context']['line'] :
+ 'unknown',
+ );
+ }
+ }
+ }
+ }
+
+ public function getTraceSafe()
+ {
+ if (!isset($this->_trace)) {
+ $this->_trace = $this->getTrace();
+ if (empty($this->_trace)) {
+ $backtrace = debug_backtrace();
+ $this->_trace = array($backtrace[count($backtrace)-1]);
+ }
+ }
+ return $this->_trace;
+ }
+
+ public function getErrorClass()
+ {
+ $trace = $this->getTraceSafe();
+ return $trace[0]['class'];
+ }
+
+ public function getErrorMethod()
+ {
+ $trace = $this->getTraceSafe();
+ return $trace[0]['function'];
+ }
+
+ public function __toString()
+ {
+ if (isset($_SERVER['REQUEST_URI'])) {
+ return $this->toHtml();
+ }
+ return $this->toText();
+ }
+
+ public function toHtml()
+ {
+ $trace = $this->getTraceSafe();
+ $causes = array();
+ $this->getCauseMessage($causes);
+ $html = '<table border="1" cellspacing="0">' . "\n";
+ foreach ($causes as $i => $cause) {
+ $html .= '<tr><td colspan="3" bgcolor="#ff9999">'
+ . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
+ . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
+ . 'on line <b>' . $cause['line'] . '</b>'
+ . "</td></tr>\n";
+ }
+ $html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
+ . '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
+ . '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
+ . '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";
+
+ foreach ($trace as $k => $v) {
+ $html .= '<tr><td align="center">' . $k . '</td>'
+ . '<td>';
+ if (!empty($v['class'])) {
+ $html .= $v['class'] . $v['type'];
+ }
+ $html .= $v['function'];
+ $args = array();
+ if (!empty($v['args'])) {
+ foreach ($v['args'] as $arg) {
+ if (is_null($arg)) $args[] = 'null';
+ elseif (is_array($arg)) $args[] = 'Array';
+ elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
+ elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
+ elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
+ else {
+ $arg = (string)$arg;
+ $str = htmlspecialchars(substr($arg, 0, 16));
+ if (strlen($arg) > 16) $str .= '&hellip;';
+ $args[] = "'" . $str . "'";
+ }
+ }
+ }
+ $html .= '(' . implode(', ',$args) . ')'
+ . '</td>'
+ . '<td>' . $v['file'] . ':' . $v['line'] . '</td></tr>' . "\n";
+ }
+ $html .= '<tr><td align="center">' . ($k+1) . '</td>'
+ . '<td>{main}</td>'
+ . '<td>&nbsp;</td></tr>' . "\n"
+ . '</table>';
+ return $html;
+ }
+
+ public function toText()
+ {
+ $causes = array();
+ $this->getCauseMessage($causes);
+ $causeMsg = '';
+ foreach ($causes as $i => $cause) {
+ $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
+ . $cause['message'] . ' in ' . $cause['file']
+ . ' on line ' . $cause['line'] . "\n";
+ }
+ return $causeMsg . $this->getTraceAsString();
+ }
+}
+
+?> \ No newline at end of file
diff --git a/3dparty/PEAR/Frontend/CLI.php b/3dparty/PEAR/Frontend/CLI.php
new file mode 100644
index 00000000000..832ddf09b3f
--- /dev/null
+++ b/3dparty/PEAR/Frontend/CLI.php
@@ -0,0 +1,509 @@
+<?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 |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: Stig Sæther Bakken <ssb@php.net> |
+ +----------------------------------------------------------------------+
+
+ $Id: CLI.php,v 1.41 2004/02/17 05:49:16 ssb Exp $
+*/
+
+require_once "PEAR.php";
+
+class PEAR_Frontend_CLI extends PEAR
+{
+ // {{{ properties
+
+ /**
+ * What type of user interface this frontend is for.
+ * @var string
+ * @access public
+ */
+ var $type = 'CLI';
+ var $lp = ''; // line prefix
+
+ var $params = array();
+ var $term = array(
+ 'bold' => '',
+ 'normal' => '',
+ );
+
+ // }}}
+
+ // {{{ constructor
+
+ function PEAR_Frontend_CLI()
+ {
+ parent::PEAR();
+ $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
+ if (function_exists('posix_isatty') && !posix_isatty(1)) {
+ // output is being redirected to a file or through a pipe
+ } elseif ($term) {
+ // XXX can use ncurses extension here, if available
+ if (preg_match('/^(xterm|vt220|linux)/', $term)) {
+ $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
+ $this->term['normal']=sprintf("%c%c%c", 27, 91, 109);
+ } elseif (preg_match('/^vt100/', $term)) {
+ $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
+ $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
+ }
+ } elseif (OS_WINDOWS) {
+ // XXX add ANSI codes here
+ }
+ }
+
+ // }}}
+
+ // {{{ displayLine(text)
+
+ function displayLine($text)
+ {
+ trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR);
+ }
+
+ function _displayLine($text)
+ {
+ print "$this->lp$text\n";
+ }
+
+ // }}}
+ // {{{ display(text)
+
+ function display($text)
+ {
+ trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR);
+ }
+
+ function _display($text)
+ {
+ print $text;
+ }
+
+ // }}}
+ // {{{ displayError(eobj)
+
+ /**
+ * @param object PEAR_Error object
+ */
+ function displayError($eobj)
+ {
+ return $this->_displayLine($eobj->getMessage());
+ }
+
+ // }}}
+ // {{{ displayFatalError(eobj)
+
+ /**
+ * @param object PEAR_Error object
+ */
+ function displayFatalError($eobj)
+ {
+ $this->displayError($eobj);
+ exit(1);
+ }
+
+ // }}}
+ // {{{ displayHeading(title)
+
+ function displayHeading($title)
+ {
+ trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR);
+ }
+
+ function _displayHeading($title)
+ {
+ print $this->lp.$this->bold($title)."\n";
+ print $this->lp.str_repeat("=", strlen($title))."\n";
+ }
+
+ // }}}
+ // {{{ userDialog(prompt, [type], [default])
+
+ function userDialog($command, $prompts, $types = array(), $defaults = array())
+ {
+ $result = array();
+ if (is_array($prompts)) {
+ $fp = fopen("php://stdin", "r");
+ foreach ($prompts as $key => $prompt) {
+ $type = $types[$key];
+ $default = @$defaults[$key];
+ if ($type == 'password') {
+ system('stty -echo');
+ }
+ print "$this->lp$prompt ";
+ if ($default) {
+ print "[$default] ";
+ }
+ print ": ";
+ $line = fgets($fp, 2048);
+ if ($type == 'password') {
+ system('stty echo');
+ print "\n";
+ }
+ if ($default && trim($line) == "") {
+ $result[$key] = $default;
+ } else {
+ $result[$key] = $line;
+ }
+ }
+ fclose($fp);
+ }
+ return $result;
+ }
+
+ // }}}
+ // {{{ userConfirm(prompt, [default])
+
+ function userConfirm($prompt, $default = 'yes')
+ {
+ trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
+ static $positives = array('y', 'yes', 'on', '1');
+ static $negatives = array('n', 'no', 'off', '0');
+ print "$this->lp$prompt [$default] : ";
+ $fp = fopen("php://stdin", "r");
+ $line = fgets($fp, 2048);
+ fclose($fp);
+ $answer = strtolower(trim($line));
+ if (empty($answer)) {
+ $answer = $default;
+ }
+ if (in_array($answer, $positives)) {
+ return true;
+ }
+ if (in_array($answer, $negatives)) {
+ return false;
+ }
+ if (in_array($default, $positives)) {
+ return true;
+ }
+ return false;
+ }
+
+ // }}}
+ // {{{ startTable([params])
+
+ function startTable($params = array())
+ {
+ trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR);
+ }
+
+ function _startTable($params = array())
+ {
+ $params['table_data'] = array();
+ $params['widest'] = array(); // indexed by column
+ $params['highest'] = array(); // indexed by row
+ $params['ncols'] = 0;
+ $this->params = $params;
+ }
+
+ // }}}
+ // {{{ tableRow(columns, [rowparams], [colparams])
+
+ function tableRow($columns, $rowparams = array(), $colparams = array())
+ {
+ trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR);
+ }
+
+ function _tableRow($columns, $rowparams = array(), $colparams = array())
+ {
+ $highest = 1;
+ for ($i = 0; $i < sizeof($columns); $i++) {
+ $col = &$columns[$i];
+ if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
+ $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0);
+ }
+ if (strpos($col, "\n") !== false) {
+ $multiline = explode("\n", $col);
+ $w = 0;
+ foreach ($multiline as $n => $line) {
+ if (strlen($line) > $w) {
+ $w = strlen($line);
+ }
+ }
+ $lines = sizeof($multiline);
+ } else {
+ $w = strlen($col);
+ }
+ if ($w > @$this->params['widest'][$i]) {
+ $this->params['widest'][$i] = $w;
+ }
+ $tmp = count_chars($columns[$i], 1);
+ // handle unix, mac and windows formats
+ $lines = (isset($tmp[10]) ? $tmp[10] : @$tmp[13]) + 1;
+ if ($lines > $highest) {
+ $highest = $lines;
+ }
+ }
+ if (sizeof($columns) > $this->params['ncols']) {
+ $this->params['ncols'] = sizeof($columns);
+ }
+ $new_row = array(
+ 'data' => $columns,
+ 'height' => $highest,
+ 'rowparams' => $rowparams,
+ 'colparams' => $colparams,
+ );
+ $this->params['table_data'][] = $new_row;
+ }
+
+ // }}}
+ // {{{ endTable()
+
+ function endTable()
+ {
+ trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR);
+ }
+
+ function _endTable()
+ {
+ extract($this->params);
+ if (!empty($caption)) {
+ $this->_displayHeading($caption);
+ }
+ if (count($table_data) == 0) {
+ return;
+ }
+ if (!isset($width)) {
+ $width = $widest;
+ } else {
+ for ($i = 0; $i < $ncols; $i++) {
+ if (!isset($width[$i])) {
+ $width[$i] = $widest[$i];
+ }
+ }
+ }
+ $border = false;
+ if (empty($border)) {
+ $cellstart = '';
+ $cellend = ' ';
+ $rowend = '';
+ $padrowend = false;
+ $borderline = '';
+ } else {
+ $cellstart = '| ';
+ $cellend = ' ';
+ $rowend = '|';
+ $padrowend = true;
+ $borderline = '+';
+ foreach ($width as $w) {
+ $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
+ $borderline .= '+';
+ }
+ }
+ if ($borderline) {
+ $this->_displayLine($borderline);
+ }
+ for ($i = 0; $i < sizeof($table_data); $i++) {
+ extract($table_data[$i]);
+ if (!is_array($rowparams)) {
+ $rowparams = array();
+ }
+ if (!is_array($colparams)) {
+ $colparams = array();
+ }
+ $rowlines = array();
+ if ($height > 1) {
+ for ($c = 0; $c < sizeof($data); $c++) {
+ $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
+ if (sizeof($rowlines[$c]) < $height) {
+ $rowlines[$c] = array_pad($rowlines[$c], $height, '');
+ }
+ }
+ } else {
+ for ($c = 0; $c < sizeof($data); $c++) {
+ $rowlines[$c] = array($data[$c]);
+ }
+ }
+ for ($r = 0; $r < $height; $r++) {
+ $rowtext = '';
+ for ($c = 0; $c < sizeof($data); $c++) {
+ if (isset($colparams[$c])) {
+ $attribs = array_merge($rowparams, $colparams);
+ } else {
+ $attribs = $rowparams;
+ }
+ $w = isset($width[$c]) ? $width[$c] : 0;
+ //$cell = $data[$c];
+ $cell = $rowlines[$c][$r];
+ $l = strlen($cell);
+ if ($l > $w) {
+ $cell = substr($cell, 0, $w);
+ }
+ if (isset($attribs['bold'])) {
+ $cell = $this->bold($cell);
+ }
+ if ($l < $w) {
+ // not using str_pad here because we may
+ // add bold escape characters to $cell
+ $cell .= str_repeat(' ', $w - $l);
+ }
+
+ $rowtext .= $cellstart . $cell . $cellend;
+ }
+ if (!$border) {
+ $rowtext = rtrim($rowtext);
+ }
+ $rowtext .= $rowend;
+ $this->_displayLine($rowtext);
+ }
+ }
+ if ($borderline) {
+ $this->_displayLine($borderline);
+ }
+ }
+
+ // }}}
+ // {{{ outputData()
+
+ function outputData($data, $command = '_default')
+ {
+ switch ($command) {
+ case 'install':
+ case 'upgrade':
+ case 'upgrade-all':
+ if (isset($data['release_warnings'])) {
+ $this->_displayLine('');
+ $this->_startTable(array(
+ 'border' => false,
+ 'caption' => 'Release Warnings'
+ ));
+ $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
+ $this->_endTable();
+ $this->_displayLine('');
+ }
+ $this->_displayLine($data['data']);
+ break;
+ case 'search':
+ $this->_startTable($data);
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+ }
+
+ foreach($data['data'] as $category) {
+ foreach($category as $pkg) {
+ $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+ }
+ };
+ $this->_endTable();
+ break;
+ case 'list-all':
+ $this->_startTable($data);
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+ }
+
+ foreach($data['data'] as $category) {
+ foreach($category as $pkg) {
+ unset($pkg[3]);
+ unset($pkg[4]);
+ $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+ }
+ };
+ $this->_endTable();
+ break;
+ case 'config-show':
+ $data['border'] = false;
+ $opts = array(0 => array('wrap' => 30),
+ 1 => array('wrap' => 20),
+ 2 => array('wrap' => 35));
+ $this->_startTable($data);
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'],
+ array('bold' => true),
+ $opts);
+ }
+ foreach($data['data'] as $group) {
+ foreach($group as $value) {
+ if ($value[2] == '') {
+ $value[2] = "<not set>";
+ }
+ $this->_tableRow($value, null, $opts);
+ }
+ }
+ $this->_endTable();
+ break;
+ case 'remote-info':
+ $data = array(
+ 'caption' => 'Package details:',
+ 'border' => false,
+ 'data' => array(
+ array("Latest", $data['stable']),
+ array("Installed", $data['installed']),
+ array("Package", $data['name']),
+ array("License", $data['license']),
+ array("Category", $data['category']),
+ array("Summary", $data['summary']),
+ array("Description", $data['description']),
+ ),
+ );
+ default: {
+ if (is_array($data)) {
+ $this->_startTable($data);
+ $count = count($data['data'][0]);
+ if ($count == 2) {
+ $opts = array(0 => array('wrap' => 25),
+ 1 => array('wrap' => 48)
+ );
+ } elseif ($count == 3) {
+ $opts = array(0 => array('wrap' => 30),
+ 1 => array('wrap' => 20),
+ 2 => array('wrap' => 35)
+ );
+ } else {
+ $opts = null;
+ }
+ if (isset($data['headline']) && is_array($data['headline'])) {
+ $this->_tableRow($data['headline'],
+ array('bold' => true),
+ $opts);
+ }
+ foreach($data['data'] as $row) {
+ $this->_tableRow($row, null, $opts);
+ }
+ $this->_endTable();
+ } else {
+ $this->_displayLine($data);
+ }
+ }
+ }
+ }
+
+ // }}}
+ // {{{ log(text)
+
+
+ function log($text, $append_crlf = true)
+ {
+ if ($append_crlf) {
+ return $this->_displayLine($text);
+ }
+ return $this->_display($text);
+ }
+
+
+ // }}}
+ // {{{ bold($text)
+
+ function bold($text)
+ {
+ if (empty($this->term['bold'])) {
+ return strtoupper($text);
+ }
+ return $this->term['bold'] . $text . $this->term['normal'];
+ }
+
+ // }}}
+}
+
+?>
diff --git a/3dparty/PEAR/Installer.php b/3dparty/PEAR/Installer.php
new file mode 100644
index 00000000000..31e2cff81ee
--- /dev/null
+++ b/3dparty/PEAR/Installer.php
@@ -0,0 +1,1068 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | Martin Jansen <mj@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $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 <ssb@php.net>
+ * @author Martin Jansen <mj@php.net>
+ * @author Greg Beaver <cellog@php.net>
+ */
+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);
+ }
+}
+// }}}
+
+?>
diff --git a/3dparty/PEAR/Packager.php b/3dparty/PEAR/Packager.php
new file mode 100644
index 00000000000..3ed209be8b2
--- /dev/null
+++ b/3dparty/PEAR/Packager.php
@@ -0,0 +1,165 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Packager.php,v 1.53 2004/06/13 14:06:01 pajoye Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'System.php';
+
+/**
+ * Administration class used to make a PEAR release tarball.
+ *
+ * TODO:
+ * - add an extra param the dir where to place the created package
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <ssb@php.net>
+ */
+class PEAR_Packager extends PEAR_Common
+{
+ // {{{ constructor
+
+ function PEAR_Packager()
+ {
+ parent::PEAR_Common();
+ }
+
+ // }}}
+ // {{{ destructor
+
+ function _PEAR_Packager()
+ {
+ parent::_PEAR_Common();
+ }
+
+ // }}}
+
+ // {{{ package()
+
+ function package($pkgfile = null, $compress = true)
+ {
+ // {{{ validate supplied package.xml file
+ if (empty($pkgfile)) {
+ $pkgfile = 'package.xml';
+ }
+ // $this->pkginfo gets populated inside
+ $pkginfo = $this->infoFromDescriptionFile($pkgfile);
+ if (PEAR::isError($pkginfo)) {
+ return $this->raiseError($pkginfo);
+ }
+
+ $pkgdir = dirname(realpath($pkgfile));
+ $pkgfile = basename($pkgfile);
+
+ $errors = $warnings = array();
+ $this->validatePackageInfo($pkginfo, $errors, $warnings, $pkgdir);
+ foreach ($warnings as $w) {
+ $this->log(1, "Warning: $w");
+ }
+ foreach ($errors as $e) {
+ $this->log(0, "Error: $e");
+ }
+ if (sizeof($errors) > 0) {
+ return $this->raiseError('Errors in package');
+ }
+ // }}}
+
+ $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
+
+ // {{{ Create the package file list
+ $filelist = array();
+ $i = 0;
+
+ foreach ($pkginfo['filelist'] as $fname => $atts) {
+ $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
+ if (!file_exists($file)) {
+ return $this->raiseError("File does not exist: $fname");
+ } else {
+ $filelist[$i++] = $file;
+ if (empty($pkginfo['filelist'][$fname]['md5sum'])) {
+ $md5sum = md5_file($file);
+ $pkginfo['filelist'][$fname]['md5sum'] = $md5sum;
+ }
+ $this->log(2, "Adding file $fname");
+ }
+ }
+ // }}}
+
+ // {{{ regenerate package.xml
+ $new_xml = $this->xmlFromInfo($pkginfo);
+ if (PEAR::isError($new_xml)) {
+ return $this->raiseError($new_xml);
+ }
+ if (!($tmpdir = System::mktemp(array('-d')))) {
+ return $this->raiseError("PEAR_Packager: mktemp failed");
+ }
+ $newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
+ $np = @fopen($newpkgfile, 'wb');
+ if (!$np) {
+ return $this->raiseError("PEAR_Packager: unable to rewrite $pkgfile as $newpkgfile");
+ }
+ fwrite($np, $new_xml);
+ fclose($np);
+ // }}}
+
+ // {{{ TAR the Package -------------------------------------------
+ $ext = $compress ? '.tgz' : '.tar';
+ $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
+ $tar =& new Archive_Tar($dest_package, $compress);
+ $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
+ // ----- Creates with the package.xml file
+ $ok = $tar->createModify(array($newpkgfile), '', $tmpdir);
+ if (PEAR::isError($ok)) {
+ return $this->raiseError($ok);
+ } elseif (!$ok) {
+ return $this->raiseError('PEAR_Packager: tarball creation failed');
+ }
+ // ----- Add the content of the package
+ if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
+ return $this->raiseError('PEAR_Packager: tarball creation failed');
+ }
+ $this->log(1, "Package $dest_package done");
+ if (file_exists("$pkgdir/CVS/Root")) {
+ $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pkginfo['version']);
+ $cvstag = "RELEASE_$cvsversion";
+ $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
+ $this->log(1, "(or set the CVS tag $cvstag by hand)");
+ }
+ // }}}
+
+ return $dest_package;
+ }
+
+ // }}}
+}
+
+// {{{ md5_file() utility function
+if (!function_exists('md5_file')) {
+ function md5_file($file) {
+ if (!$fd = @fopen($file, 'r')) {
+ return false;
+ }
+ $md5 = md5(fread($fd, filesize($file)));
+ fclose($fd);
+ return $md5;
+ }
+}
+// }}}
+
+?>
diff --git a/3dparty/PEAR/Registry.php b/3dparty/PEAR/Registry.php
new file mode 100644
index 00000000000..f2510a38f15
--- /dev/null
+++ b/3dparty/PEAR/Registry.php
@@ -0,0 +1,538 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// | Tomas V.V.Cox <cox@idecnet.com> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: Registry.php,v 1.50.4.3 2004/10/26 19:19:56 cellog Exp $
+
+/*
+TODO:
+ - Transform into singleton()
+ - Add application level lock (avoid change the registry from the cmdline
+ while using the GTK interface, for ex.)
+*/
+require_once "System.php";
+require_once "PEAR.php";
+
+define('PEAR_REGISTRY_ERROR_LOCK', -2);
+define('PEAR_REGISTRY_ERROR_FORMAT', -3);
+define('PEAR_REGISTRY_ERROR_FILE', -4);
+
+/**
+ * Administration class used to maintain the installed package database.
+ */
+class PEAR_Registry extends PEAR
+{
+ // {{{ properties
+
+ /** Directory where registry files are stored.
+ * @var string
+ */
+ var $statedir = '';
+
+ /** File where the file map is stored
+ * @var string
+ */
+ var $filemap = '';
+
+ /** Name of file used for locking the registry
+ * @var string
+ */
+ var $lockfile = '';
+
+ /** File descriptor used during locking
+ * @var resource
+ */
+ var $lock_fp = null;
+
+ /** Mode used during locking
+ * @var int
+ */
+ var $lock_mode = 0; // XXX UNUSED
+
+ /** Cache of package information. Structure:
+ * array(
+ * 'package' => array('id' => ... ),
+ * ... )
+ * @var array
+ */
+ var $pkginfo_cache = array();
+
+ /** Cache of file map. Structure:
+ * array( '/path/to/file' => 'package', ... )
+ * @var array
+ */
+ var $filemap_cache = array();
+
+ // }}}
+
+ // {{{ constructor
+
+ /**
+ * PEAR_Registry constructor.
+ *
+ * @param string (optional) PEAR install directory (for .php files)
+ *
+ * @access public
+ */
+ function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR)
+ {
+ parent::PEAR();
+ $ds = DIRECTORY_SEPARATOR;
+ $this->install_dir = $pear_install_dir;
+ $this->statedir = $pear_install_dir.$ds.'.registry';
+ $this->filemap = $pear_install_dir.$ds.'.filemap';
+ $this->lockfile = $pear_install_dir.$ds.'.lock';
+
+ // XXX Compatibility code should be removed in the future
+ // rename all registry files if any to lowercase
+ if (!OS_WINDOWS && $handle = @opendir($this->statedir)) {
+ $dest = $this->statedir . DIRECTORY_SEPARATOR;
+ while (false !== ($file = readdir($handle))) {
+ if (preg_match('/^.*[A-Z].*\.reg$/', $file)) {
+ rename($dest . $file, $dest . strtolower($file));
+ }
+ }
+ closedir($handle);
+ }
+ if (!file_exists($this->filemap)) {
+ $this->rebuildFileMap();
+ }
+ }
+
+ // }}}
+ // {{{ destructor
+
+ /**
+ * PEAR_Registry destructor. Makes sure no locks are forgotten.
+ *
+ * @access private
+ */
+ function _PEAR_Registry()
+ {
+ parent::_PEAR();
+ if (is_resource($this->lock_fp)) {
+ $this->_unlock();
+ }
+ }
+
+ // }}}
+
+ // {{{ _assertStateDir()
+
+ /**
+ * Make sure the directory where we keep registry files exists.
+ *
+ * @return bool TRUE if directory exists, FALSE if it could not be
+ * created
+ *
+ * @access private
+ */
+ function _assertStateDir()
+ {
+ if (!@is_dir($this->statedir)) {
+ if (!System::mkdir(array('-p', $this->statedir))) {
+ return $this->raiseError("could not create directory '{$this->statedir}'");
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _packageFileName()
+
+ /**
+ * Get the name of the file where data for a given package is stored.
+ *
+ * @param string package name
+ *
+ * @return string registry file name
+ *
+ * @access public
+ */
+ function _packageFileName($package)
+ {
+ return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
+ }
+
+ // }}}
+ // {{{ _openPackageFile()
+
+ function _openPackageFile($package, $mode)
+ {
+ $this->_assertStateDir();
+ $file = $this->_packageFileName($package);
+ $fp = @fopen($file, $mode);
+ if (!$fp) {
+ return null;
+ }
+ return $fp;
+ }
+
+ // }}}
+ // {{{ _closePackageFile()
+
+ function _closePackageFile($fp)
+ {
+ fclose($fp);
+ }
+
+ // }}}
+ // {{{ rebuildFileMap()
+
+ function rebuildFileMap()
+ {
+ $packages = $this->listPackages();
+ $files = array();
+ foreach ($packages as $package) {
+ $version = $this->packageInfo($package, 'version');
+ $filelist = $this->packageInfo($package, 'filelist');
+ if (!is_array($filelist)) {
+ continue;
+ }
+ foreach ($filelist as $name => $attrs) {
+ if (isset($attrs['role']) && $attrs['role'] != 'php') {
+ continue;
+ }
+ if (isset($attrs['baseinstalldir'])) {
+ $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+ } else {
+ $file = $name;
+ }
+ $file = preg_replace(',^/+,', '', $file);
+ $files[$file] = $package;
+ }
+ }
+ $this->_assertStateDir();
+ $fp = @fopen($this->filemap, 'wb');
+ if (!$fp) {
+ return false;
+ }
+ $this->filemap_cache = $files;
+ fwrite($fp, serialize($files));
+ fclose($fp);
+ return true;
+ }
+
+ // }}}
+ // {{{ readFileMap()
+
+ function readFileMap()
+ {
+ $fp = @fopen($this->filemap, 'r');
+ if (!$fp) {
+ return $this->raiseError('PEAR_Registry: could not open filemap', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
+ }
+ $fsize = filesize($this->filemap);
+ $rt = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ $data = fread($fp, $fsize);
+ set_magic_quotes_runtime($rt);
+ fclose($fp);
+ $tmp = unserialize($data);
+ if (!$tmp && $fsize > 7) {
+ return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
+ }
+ $this->filemap_cache = $tmp;
+ return true;
+ }
+
+ // }}}
+ // {{{ _lock()
+
+ /**
+ * Lock the registry.
+ *
+ * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
+ * See flock manual for more information.
+ *
+ * @return bool TRUE on success, FALSE if locking failed, or a
+ * PEAR error if some other error occurs (such as the
+ * lock file not being writable).
+ *
+ * @access private
+ */
+ function _lock($mode = LOCK_EX)
+ {
+ if (!eregi('Windows 9', php_uname())) {
+ if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
+ // XXX does not check type of lock (LOCK_SH/LOCK_EX)
+ return true;
+ }
+ if (PEAR::isError($err = $this->_assertStateDir())) {
+ return $err;
+ }
+ $open_mode = 'w';
+ // XXX People reported problems with LOCK_SH and 'w'
+ if ($mode === LOCK_SH || $mode === LOCK_UN) {
+ if (@!is_file($this->lockfile)) {
+ touch($this->lockfile);
+ }
+ $open_mode = 'r';
+ }
+
+ if (!is_resource($this->lock_fp)) {
+ $this->lock_fp = @fopen($this->lockfile, $open_mode);
+ }
+
+ if (!is_resource($this->lock_fp)) {
+ return $this->raiseError("could not create lock file" .
+ (isset($php_errormsg) ? ": " . $php_errormsg : ""));
+ }
+ if (!(int)flock($this->lock_fp, $mode)) {
+ switch ($mode) {
+ case LOCK_SH: $str = 'shared'; break;
+ case LOCK_EX: $str = 'exclusive'; break;
+ case LOCK_UN: $str = 'unlock'; break;
+ default: $str = 'unknown'; break;
+ }
+ return $this->raiseError("could not acquire $str lock ($this->lockfile)",
+ PEAR_REGISTRY_ERROR_LOCK);
+ }
+ }
+ return true;
+ }
+
+ // }}}
+ // {{{ _unlock()
+
+ function _unlock()
+ {
+ $ret = $this->_lock(LOCK_UN);
+ if (is_resource($this->lock_fp)) {
+ fclose($this->lock_fp);
+ }
+ $this->lock_fp = null;
+ return $ret;
+ }
+
+ // }}}
+ // {{{ _packageExists()
+
+ function _packageExists($package)
+ {
+ return file_exists($this->_packageFileName($package));
+ }
+
+ // }}}
+ // {{{ _packageInfo()
+
+ function _packageInfo($package = null, $key = null)
+ {
+ if ($package === null) {
+ return array_map(array($this, '_packageInfo'),
+ $this->_listPackages());
+ }
+ $fp = $this->_openPackageFile($package, 'r');
+ if ($fp === null) {
+ return null;
+ }
+ $rt = get_magic_quotes_runtime();
+ set_magic_quotes_runtime(0);
+ $data = fread($fp, filesize($this->_packageFileName($package)));
+ set_magic_quotes_runtime($rt);
+ $this->_closePackageFile($fp);
+ $data = unserialize($data);
+ if ($key === null) {
+ return $data;
+ }
+ if (isset($data[$key])) {
+ return $data[$key];
+ }
+ return null;
+ }
+
+ // }}}
+ // {{{ _listPackages()
+
+ function _listPackages()
+ {
+ $pkglist = array();
+ $dp = @opendir($this->statedir);
+ if (!$dp) {
+ return $pkglist;
+ }
+ while ($ent = readdir($dp)) {
+ if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
+ continue;
+ }
+ $pkglist[] = substr($ent, 0, -4);
+ }
+ return $pkglist;
+ }
+
+ // }}}
+
+ // {{{ packageExists()
+
+ function packageExists($package)
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+ return $e;
+ }
+ $ret = $this->_packageExists($package);
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ packageInfo()
+
+ function packageInfo($package = null, $key = null)
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+ return $e;
+ }
+ $ret = $this->_packageInfo($package, $key);
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ listPackages()
+
+ function listPackages()
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+ return $e;
+ }
+ $ret = $this->_listPackages();
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ addPackage()
+
+ function addPackage($package, $info)
+ {
+ if ($this->packageExists($package)) {
+ return false;
+ }
+ if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+ return $e;
+ }
+ $fp = $this->_openPackageFile($package, 'wb');
+ if ($fp === null) {
+ $this->_unlock();
+ return false;
+ }
+ $info['_lastmodified'] = time();
+ fwrite($fp, serialize($info));
+ $this->_closePackageFile($fp);
+ $this->_unlock();
+ return true;
+ }
+
+ // }}}
+ // {{{ deletePackage()
+
+ function deletePackage($package)
+ {
+ if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+ return $e;
+ }
+ $file = $this->_packageFileName($package);
+ $ret = @unlink($file);
+ $this->rebuildFileMap();
+ $this->_unlock();
+ return $ret;
+ }
+
+ // }}}
+ // {{{ updatePackage()
+
+ function updatePackage($package, $info, $merge = true)
+ {
+ $oldinfo = $this->packageInfo($package);
+ if (empty($oldinfo)) {
+ return false;
+ }
+ if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+ return $e;
+ }
+ $fp = $this->_openPackageFile($package, 'w');
+ if ($fp === null) {
+ $this->_unlock();
+ return false;
+ }
+ $info['_lastmodified'] = time();
+ if ($merge) {
+ fwrite($fp, serialize(array_merge($oldinfo, $info)));
+ } else {
+ fwrite($fp, serialize($info));
+ }
+ $this->_closePackageFile($fp);
+ if (isset($info['filelist'])) {
+ $this->rebuildFileMap();
+ }
+ $this->_unlock();
+ return true;
+ }
+
+ // }}}
+ // {{{ checkFileMap()
+
+ /**
+ * Test whether a file belongs to a package.
+ *
+ * @param string $path file path, absolute or relative to the pear
+ * install dir
+ *
+ * @return string which package the file belongs to, or an empty
+ * string if the file does not belong to an installed package
+ *
+ * @access public
+ */
+ function checkFileMap($path)
+ {
+ if (is_array($path)) {
+ static $notempty;
+ if (empty($notempty)) {
+ $notempty = create_function('$a','return !empty($a);');
+ }
+ $pkgs = array();
+ foreach ($path as $name => $attrs) {
+ if (is_array($attrs) && isset($attrs['baseinstalldir'])) {
+ $name = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+ }
+ $pkgs[$name] = $this->checkFileMap($name);
+ }
+ return array_filter($pkgs, $notempty);
+ }
+ if (empty($this->filemap_cache) && PEAR::isError($this->readFileMap())) {
+ return $err;
+ }
+ if (isset($this->filemap_cache[$path])) {
+ return $this->filemap_cache[$path];
+ }
+ $l = strlen($this->install_dir);
+ if (substr($path, 0, $l) == $this->install_dir) {
+ $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
+ }
+ if (isset($this->filemap_cache[$path])) {
+ return $this->filemap_cache[$path];
+ }
+ return '';
+ }
+
+ // }}}
+
+}
+
+?>
diff --git a/3dparty/PEAR/Remote.php b/3dparty/PEAR/Remote.php
new file mode 100644
index 00000000000..7b1e314f903
--- /dev/null
+++ b/3dparty/PEAR/Remote.php
@@ -0,0 +1,394 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net> |
+// +----------------------------------------------------------------------+
+//
+// $Id: Remote.php,v 1.50 2004/06/08 18:03:46 cellog Exp $
+
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+/**
+ * This is a class for doing remote operations against the central
+ * PEAR database.
+ *
+ * @nodep XML_RPC_Value
+ * @nodep XML_RPC_Message
+ * @nodep XML_RPC_Client
+ */
+class PEAR_Remote extends PEAR
+{
+ // {{{ properties
+
+ var $config = null;
+ var $cache = null;
+
+ // }}}
+
+ // {{{ PEAR_Remote(config_object)
+
+ function PEAR_Remote(&$config)
+ {
+ $this->PEAR();
+ $this->config = &$config;
+ }
+
+ // }}}
+
+ // {{{ getCache()
+
+
+ function getCache($args)
+ {
+ $id = md5(serialize($args));
+ $cachedir = $this->config->get('cache_dir');
+ $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id;
+ if (!file_exists($filename)) {
+ return null;
+ };
+
+ $fp = fopen($filename, 'rb');
+ if (!$fp) {
+ return null;
+ }
+ $content = fread($fp, filesize($filename));
+ fclose($fp);
+ $result = array(
+ 'age' => time() - filemtime($filename),
+ 'lastChange' => filemtime($filename),
+ 'content' => unserialize($content),
+ );
+ return $result;
+ }
+
+ // }}}
+
+ // {{{ saveCache()
+
+ function saveCache($args, $data)
+ {
+ $id = md5(serialize($args));
+ $cachedir = $this->config->get('cache_dir');
+ if (!file_exists($cachedir)) {
+ System::mkdir(array('-p', $cachedir));
+ }
+ $filename = $cachedir.'/xmlrpc_cache_'.$id;
+
+ $fp = @fopen($filename, "wb");
+ if ($fp) {
+ fwrite($fp, serialize($data));
+ fclose($fp);
+ };
+ }
+
+ // }}}
+
+ // {{{ call(method, [args...])
+
+ function call($method)
+ {
+ $_args = $args = func_get_args();
+
+ $this->cache = $this->getCache($args);
+ $cachettl = $this->config->get('cache_ttl');
+ // If cache is newer than $cachettl seconds, we use the cache!
+ if ($this->cache !== null && $this->cache['age'] < $cachettl) {
+ return $this->cache['content'];
+ };
+
+ if (extension_loaded("xmlrpc")) {
+ $result = call_user_func_array(array(&$this, 'call_epi'), $args);
+ if (!PEAR::isError($result)) {
+ $this->saveCache($_args, $result);
+ };
+ return $result;
+ }
+ if (!@include_once("XML/RPC.php")) {
+ return $this->raiseError("For this remote PEAR operation you need to install the XML_RPC package");
+ }
+ array_shift($args);
+ $server_host = $this->config->get('master_server');
+ $username = $this->config->get('username');
+ $password = $this->config->get('password');
+ $eargs = array();
+ foreach($args as $arg) $eargs[] = $this->_encode($arg);
+ $f = new XML_RPC_Message($method, $eargs);
+ if ($this->cache !== null) {
+ $maxAge = '?maxAge='.$this->cache['lastChange'];
+ } else {
+ $maxAge = '';
+ };
+ $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+ if ($proxy = parse_url($this->config->get('http_proxy'))) {
+ $proxy_host = @$proxy['host'];
+ $proxy_port = @$proxy['port'];
+ $proxy_user = @urldecode(@$proxy['user']);
+ $proxy_pass = @urldecode(@$proxy['pass']);
+ }
+ $c = new XML_RPC_Client('/xmlrpc.php'.$maxAge, $server_host, 80, $proxy_host, $proxy_port, $proxy_user, $proxy_pass);
+ if ($username && $password) {
+ $c->setCredentials($username, $password);
+ }
+ if ($this->config->get('verbose') >= 3) {
+ $c->setDebug(1);
+ }
+ $r = $c->send($f);
+ if (!$r) {
+ return $this->raiseError("XML_RPC send failed");
+ }
+ $v = $r->value();
+ if ($e = $r->faultCode()) {
+ if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) {
+ return $this->cache['content'];
+ }
+ return $this->raiseError($r->faultString(), $e);
+ }
+
+ $result = XML_RPC_decode($v);
+ $this->saveCache($_args, $result);
+ return $result;
+ }
+
+ // }}}
+
+ // {{{ call_epi(method, [args...])
+
+ function call_epi($method)
+ {
+ do {
+ if (extension_loaded("xmlrpc")) {
+ break;
+ }
+ if (OS_WINDOWS) {
+ $ext = 'dll';
+ } elseif (PHP_OS == 'HP-UX') {
+ $ext = 'sl';
+ } elseif (PHP_OS == 'AIX') {
+ $ext = 'a';
+ } else {
+ $ext = 'so';
+ }
+ $ext = OS_WINDOWS ? 'dll' : 'so';
+ @dl("xmlrpc-epi.$ext");
+ if (extension_loaded("xmlrpc")) {
+ break;
+ }
+ @dl("xmlrpc.$ext");
+ if (extension_loaded("xmlrpc")) {
+ break;
+ }
+ return $this->raiseError("unable to load xmlrpc extension");
+ } while (false);
+ $params = func_get_args();
+ array_shift($params);
+ $method = str_replace("_", ".", $method);
+ $request = xmlrpc_encode_request($method, $params);
+ $server_host = $this->config->get("master_server");
+ if (empty($server_host)) {
+ return $this->raiseError("PEAR_Remote::call: no master_server configured");
+ }
+ $server_port = 80;
+ if ($http_proxy = $this->config->get('http_proxy')) {
+ $proxy = parse_url($http_proxy);
+ $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+ $proxy_host = @$proxy['host'];
+ $proxy_port = @$proxy['port'];
+ $proxy_user = @urldecode(@$proxy['user']);
+ $proxy_pass = @urldecode(@$proxy['pass']);
+ $fp = @fsockopen($proxy_host, $proxy_port);
+ $use_proxy = true;
+ } else {
+ $use_proxy = false;
+ $fp = @fsockopen($server_host, $server_port);
+ }
+ if (!$fp && $http_proxy) {
+ return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed");
+ } elseif (!$fp) {
+ return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed");
+ }
+ $len = strlen($request);
+ $req_headers = "Host: $server_host:$server_port\r\n" .
+ "Content-type: text/xml\r\n" .
+ "Content-length: $len\r\n";
+ $username = $this->config->get('username');
+ $password = $this->config->get('password');
+ if ($username && $password) {
+ $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n";
+ $tmp = base64_encode("$username:$password");
+ $req_headers .= "Authorization: Basic $tmp\r\n";
+ }
+ if ($this->cache !== null) {
+ $maxAge = '?maxAge='.$this->cache['lastChange'];
+ } else {
+ $maxAge = '';
+ };
+
+ if ($use_proxy && $proxy_host != '' && $proxy_user != '') {
+ $req_headers .= 'Proxy-Authorization: Basic '
+ .base64_encode($proxy_user.':'.$proxy_pass)
+ ."\r\n";
+ }
+
+ if ($this->config->get('verbose') > 3) {
+ print "XMLRPC REQUEST HEADERS:\n";
+ var_dump($req_headers);
+ print "XMLRPC REQUEST BODY:\n";
+ var_dump($request);
+ }
+
+ if ($use_proxy && $proxy_host != '') {
+ $post_string = "POST http://".$server_host;
+ if ($proxy_port > '') {
+ $post_string .= ':'.$server_port;
+ }
+ } else {
+ $post_string = "POST ";
+ }
+
+ fwrite($fp, ($post_string."/xmlrpc.php$maxAge HTTP/1.0\r\n$req_headers\r\n$request"));
+ $response = '';
+ $line1 = fgets($fp, 2048);
+ if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) {
+ return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server");
+ }
+ switch ($matches[1]) {
+ case "200": // OK
+ break;
+ case "304": // Not Modified
+ return $this->cache['content'];
+ case "401": // Unauthorized
+ if ($username && $password) {
+ return $this->raiseError("PEAR_Remote: authorization failed", 401);
+ } else {
+ return $this->raiseError("PEAR_Remote: authorization required, please log in first", 401);
+ }
+ default:
+ return $this->raiseError("PEAR_Remote: unexpected HTTP response", (int)$matches[1], null, null, "$matches[1] $matches[2]");
+ }
+ while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
+ while ($chunk = fread($fp, 10240)) {
+ $response .= $chunk;
+ }
+ fclose($fp);
+ if ($this->config->get('verbose') > 3) {
+ print "XMLRPC RESPONSE:\n";
+ var_dump($response);
+ }
+ $ret = xmlrpc_decode($response);
+ if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) {
+ if ($ret['__PEAR_TYPE__'] == 'error') {
+ if (isset($ret['__PEAR_CLASS__'])) {
+ $class = $ret['__PEAR_CLASS__'];
+ } else {
+ $class = "PEAR_Error";
+ }
+ if ($ret['code'] === '') $ret['code'] = null;
+ if ($ret['message'] === '') $ret['message'] = null;
+ if ($ret['userinfo'] === '') $ret['userinfo'] = null;
+ if (strtolower($class) == 'db_error') {
+ $ret = $this->raiseError(PEAR::errorMessage($ret['code']),
+ $ret['code'], null, null,
+ $ret['userinfo']);
+ } else {
+ $ret = $this->raiseError($ret['message'], $ret['code'],
+ null, null, $ret['userinfo']);
+ }
+ }
+ } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0])
+ && is_array($ret[0]) &&
+ !empty($ret[0]['faultString']) &&
+ !empty($ret[0]['faultCode'])) {
+ extract($ret[0]);
+ $faultString = "XML-RPC Server Fault: " .
+ str_replace("\n", " ", $faultString);
+ return $this->raiseError($faultString, $faultCode);
+ }
+ return $ret;
+ }
+
+ // }}}
+
+ // {{{ _encode
+
+ // a slightly extended version of XML_RPC_encode
+ function _encode($php_val)
+ {
+ global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double;
+ global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct;
+
+ $type = gettype($php_val);
+ $xmlrpcval = new XML_RPC_Value;
+
+ switch($type) {
+ case "array":
+ reset($php_val);
+ $firstkey = key($php_val);
+ end($php_val);
+ $lastkey = key($php_val);
+ if ($firstkey === 0 && is_int($lastkey) &&
+ ($lastkey + 1) == count($php_val)) {
+ $is_continuous = true;
+ reset($php_val);
+ $size = count($php_val);
+ for ($expect = 0; $expect < $size; $expect++, next($php_val)) {
+ if (key($php_val) !== $expect) {
+ $is_continuous = false;
+ break;
+ }
+ }
+ if ($is_continuous) {
+ reset($php_val);
+ $arr = array();
+ while (list($k, $v) = each($php_val)) {
+ $arr[$k] = $this->_encode($v);
+ }
+ $xmlrpcval->addArray($arr);
+ break;
+ }
+ }
+ // fall though if not numerical and continuous
+ case "object":
+ $arr = array();
+ while (list($k, $v) = each($php_val)) {
+ $arr[$k] = $this->_encode($v);
+ }
+ $xmlrpcval->addStruct($arr);
+ break;
+ case "integer":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_Int);
+ break;
+ case "double":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_Double);
+ break;
+ case "string":
+ case "NULL":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_String);
+ break;
+ case "boolean":
+ $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean);
+ break;
+ case "unknown type":
+ default:
+ return null;
+ }
+ return $xmlrpcval;
+ }
+
+ // }}}
+
+}
+
+?>
diff --git a/3dparty/PEAR/RunTest.php b/3dparty/PEAR/RunTest.php
new file mode 100644
index 00000000000..1aa02aab9df
--- /dev/null
+++ b/3dparty/PEAR/RunTest.php
@@ -0,0 +1,363 @@
+<?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 |
+// | license@php.net so we can mail you a copy immediately. |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
+// | Greg Beaver <cellog@php.net> |
+// | |
+// +----------------------------------------------------------------------+
+//
+// $Id: RunTest.php,v 1.3.2.4 2005/02/17 17:47:55 cellog Exp $
+//
+
+/**
+ * Simplified version of PHP's test suite
+ * -- EXPERIMENTAL --
+
+ Try it with:
+
+ $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
+
+
+TODO:
+
+Actually finish the development and testing
+
+ */
+
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+define('DETAILED', 1);
+putenv("PHP_PEAR_RUNTESTS=1");
+
+class PEAR_RunTest
+{
+ var $_logger;
+
+ /**
+ * An object that supports the PEAR_Common->log() signature, or null
+ * @param PEAR_Common|null
+ */
+ function PEAR_RunTest($logger = null)
+ {
+ $this->_logger = $logger;
+ }
+
+ //
+ // Run an individual test case.
+ //
+
+ function run($file, $ini_settings = '')
+ {
+ $cwd = getcwd();
+ $conf = &PEAR_Config::singleton();
+ $php = $conf->get('php_bin');
+ //var_dump($php);exit;
+ global $log_format, $info_params, $ini_overwrites;
+
+ $info_params = '';
+ $log_format = 'LEOD';
+
+ // Load the sections of the test file.
+ $section_text = array(
+ 'TEST' => '(unnamed test)',
+ 'SKIPIF' => '',
+ 'GET' => '',
+ 'ARGS' => '',
+ );
+
+ if (!is_file($file) || !$fp = fopen($file, "r")) {
+ return PEAR::raiseError("Cannot open test file: $file");
+ }
+
+ $section = '';
+ while (!feof($fp)) {
+ $line = fgets($fp);
+
+ // Match the beginning of a section.
+ if (ereg('^--([A-Z]+)--',$line,$r)) {
+ $section = $r[1];
+ $section_text[$section] = '';
+ continue;
+ }
+
+ // Add to the section text.
+ $section_text[$section] .= $line;
+ }
+ fclose($fp);
+
+ $shortname = str_replace($cwd.'/', '', $file);
+ $tested = trim($section_text['TEST'])." [$shortname]";
+
+ $tmp = realpath(dirname($file));
+ $tmp_skipif = $tmp . uniqid('/phpt.');
+ $tmp_file = ereg_replace('\.phpt$','.php',$file);
+ $tmp_post = $tmp . uniqid('/phpt.');
+
+ @unlink($tmp_skipif);
+ @unlink($tmp_file);
+ @unlink($tmp_post);
+
+ // unlink old test results
+ @unlink(ereg_replace('\.phpt$','.diff',$file));
+ @unlink(ereg_replace('\.phpt$','.log',$file));
+ @unlink(ereg_replace('\.phpt$','.exp',$file));
+ @unlink(ereg_replace('\.phpt$','.out',$file));
+
+ // Check if test should be skipped.
+ $info = '';
+ $warn = false;
+ if (array_key_exists('SKIPIF', $section_text)) {
+ if (trim($section_text['SKIPIF'])) {
+ $this->save_text($tmp_skipif, $section_text['SKIPIF']);
+ //$extra = substr(PHP_OS, 0, 3) !== "WIN" ?
+ // "unset REQUEST_METHOD;": "";
+
+ //$output = `$extra $php $info_params -f $tmp_skipif`;
+ $output = `$php $info_params -f $tmp_skipif`;
+ unlink($tmp_skipif);
+ if (eregi("^skip", trim($output))) {
+ $skipreason = "SKIP $tested";
+ $reason = (eregi("^skip[[:space:]]*(.+)\$", trim($output))) ? eregi_replace("^skip[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
+ if ($reason) {
+ $skipreason .= " (reason: $reason)";
+ }
+ $this->_logger->log(0, $skipreason);
+ if (isset($old_php)) {
+ $php = $old_php;
+ }
+ return 'SKIPPED';
+ }
+ if (eregi("^info", trim($output))) {
+ $reason = (ereg("^info[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^info[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
+ if ($reason) {
+ $info = " (info: $reason)";
+ }
+ }
+ if (eregi("^warn", trim($output))) {
+ $reason = (ereg("^warn[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^warn[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
+ if ($reason) {
+ $warn = true; /* only if there is a reason */
+ $info = " (warn: $reason)";
+ }
+ }
+ }
+ }
+
+ // We've satisfied the preconditions - run the test!
+ $this->save_text($tmp_file,$section_text['FILE']);
+
+ $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
+
+ $cmd = "$php$ini_settings -f $tmp_file$args 2>&1";
+ if (isset($this->_logger)) {
+ $this->_logger->log(2, 'Running command "' . $cmd . '"');
+ }
+
+ $savedir = getcwd(); // in case the test moves us around
+ if (isset($section_text['RETURNS'])) {
+ ob_start();
+ system($cmd, $return_value);
+ $out = ob_get_contents();
+ ob_end_clean();
+ @unlink($tmp_post);
+ $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
+ $returnfail = ($return_value != $section_text['RETURNS']);
+ } else {
+ $out = `$cmd`;
+ $returnfail = false;
+ }
+ chdir($savedir);
+ // Does the output match what is expected?
+ $output = trim($out);
+ $output = preg_replace('/\r\n/', "\n", $output);
+
+ if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
+ if (isset($section_text['EXPECTF'])) {
+ $wanted = trim($section_text['EXPECTF']);
+ } else {
+ $wanted = trim($section_text['EXPECTREGEX']);
+ }
+ $wanted_re = preg_replace('/\r\n/',"\n",$wanted);
+ if (isset($section_text['EXPECTF'])) {
+ $wanted_re = preg_quote($wanted_re, '/');
+ // Stick to basics
+ $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
+ $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
+ $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
+ $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
+ $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
+ $wanted_re = str_replace("%c", ".", $wanted_re);
+ // %f allows two points "-.0.0" but that is the best *simple* expression
+ }
+ /* DEBUG YOUR REGEX HERE
+ var_dump($wanted_re);
+ print(str_repeat('=', 80) . "\n");
+ var_dump($output);
+ */
+ if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
+ @unlink($tmp_file);
+ $this->_logger->log(0, "PASS $tested$info");
+ if (isset($old_php)) {
+ $php = $old_php;
+ }
+ return 'PASSED';
+ }
+
+ } else {
+ $wanted = trim($section_text['EXPECT']);
+ $wanted = preg_replace('/\r\n/',"\n",$wanted);
+ // compare and leave on success
+ $ok = (0 == strcmp($output,$wanted));
+ if (!$returnfail && $ok) {
+ @unlink($tmp_file);
+ $this->_logger->log(0, "PASS $tested$info");
+ if (isset($old_php)) {
+ $php = $old_php;
+ }
+ return 'PASSED';
+ }
+ }
+
+ // Test failed so we need to report details.
+ if ($warn) {
+ $this->_logger->log(0, "WARN $tested$info");
+ } else {
+ $this->_logger->log(0, "FAIL $tested$info");
+ }
+
+ if (isset($section_text['RETURNS'])) {
+ $GLOBALS['__PHP_FAILED_TESTS__'][] = array(
+ 'name' => $file,
+ 'test_name' => $tested,
+ 'output' => ereg_replace('\.phpt$','.log', $file),
+ 'diff' => ereg_replace('\.phpt$','.diff', $file),
+ 'info' => $info,
+ 'return' => $return_value
+ );
+ } else {
+ $GLOBALS['__PHP_FAILED_TESTS__'][] = array(
+ 'name' => $file,
+ 'test_name' => $tested,
+ 'output' => ereg_replace('\.phpt$','.log', $file),
+ 'diff' => ereg_replace('\.phpt$','.diff', $file),
+ 'info' => $info,
+ );
+ }
+
+ // write .exp
+ if (strpos($log_format,'E') !== FALSE) {
+ $logname = ereg_replace('\.phpt$','.exp',$file);
+ if (!$log = fopen($logname,'w')) {
+ return PEAR::raiseError("Cannot create test log - $logname");
+ }
+ fwrite($log,$wanted);
+ fclose($log);
+ }
+
+ // write .out
+ if (strpos($log_format,'O') !== FALSE) {
+ $logname = ereg_replace('\.phpt$','.out',$file);
+ if (!$log = fopen($logname,'w')) {
+ return PEAR::raiseError("Cannot create test log - $logname");
+ }
+ fwrite($log,$output);
+ fclose($log);
+ }
+
+ // write .diff
+ if (strpos($log_format,'D') !== FALSE) {
+ $logname = ereg_replace('\.phpt$','.diff',$file);
+ if (!$log = fopen($logname,'w')) {
+ return PEAR::raiseError("Cannot create test log - $logname");
+ }
+ fwrite($log, $this->generate_diff($wanted, $output,
+ isset($section_text['RETURNS']) ? array(trim($section_text['RETURNS']),
+ $return_value) : null));
+ fclose($log);
+ }
+
+ // write .log
+ if (strpos($log_format,'L') !== FALSE) {
+ $logname = ereg_replace('\.phpt$','.log',$file);
+ if (!$log = fopen($logname,'w')) {
+ return PEAR::raiseError("Cannot create test log - $logname");
+ }
+ fwrite($log,"
+---- EXPECTED OUTPUT
+$wanted
+---- ACTUAL OUTPUT
+$output
+---- FAILED
+");
+ if ($returnfail) {
+ fwrite($log,"
+---- EXPECTED RETURN
+$section_text[RETURNS]
+---- ACTUAL RETURN
+$return_value
+");
+ }
+ fclose($log);
+ //error_report($file,$logname,$tested);
+ }
+
+ if (isset($old_php)) {
+ $php = $old_php;
+ }
+
+ return $warn ? 'WARNED' : 'FAILED';
+ }
+
+ function generate_diff($wanted, $output, $return_value)
+ {
+ $w = explode("\n", $wanted);
+ $o = explode("\n", $output);
+ $w1 = array_diff_assoc($w,$o);
+ $o1 = array_diff_assoc($o,$w);
+ $w2 = array();
+ $o2 = array();
+ foreach($w1 as $idx => $val) $w2[sprintf("%03d<",$idx)] = sprintf("%03d- ", $idx+1).$val;
+ foreach($o1 as $idx => $val) $o2[sprintf("%03d>",$idx)] = sprintf("%03d+ ", $idx+1).$val;
+ $diff = array_merge($w2, $o2);
+ ksort($diff);
+ if ($return_value) {
+ $extra = "##EXPECTED: $return_value[0]\r\n##RETURNED: $return_value[1]";
+ } else {
+ $extra = '';
+ }
+ return implode("\r\n", $diff) . $extra;
+ }
+
+ //
+ // Write the given text to a temporary file, and return the filename.
+ //
+
+ function save_text($filename, $text)
+ {
+ if (!$fp = fopen($filename, 'w')) {
+ return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
+ }
+ fwrite($fp,$text);
+ fclose($fp);
+ if (1 < DETAILED) echo "
+FILE $filename {{{
+$text
+}}}
+";
+ }
+
+}
+?> \ No newline at end of file