1<?php
2/**
3 * PEAR_Config, customized configuration handling for the PEAR Installer
4 *
5 * PHP versions 4 and 5
6 *
7 * LICENSE: This source file is subject to version 3.0 of the PHP license
8 * that is available through the world-wide-web at the following URI:
9 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10 * the PHP License and are unable to obtain it through the web, please
11 * send a note to license@php.net so we can mail you a copy immediately.
12 *
13 * @category   pear
14 * @package    PEAR
15 * @author     Stig Bakken <ssb@php.net>
16 * @author     Greg Beaver <cellog@php.net>
17 * @copyright  1997-2006 The PHP Group
18 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
19 * @version    CVS: $Id: Config.php,v 1.124.2.3 2006/07/15 00:38:27 pajoye Exp $
20 * @link       http://pear.php.net/package/PEAR
21 * @since      File available since Release 0.1
22 */
23
24/**
25 * Required for error handling
26 */
27require_once 'PEAR.php';
28require_once 'PEAR/Registry.php';
29require_once 'PEAR/Installer/Role.php';
30require_once 'System.php';
31require_once 'PEAR/Remote.php';
32
33/**
34 * Last created PEAR_Config instance.
35 * @var object
36 */
37$GLOBALS['_PEAR_Config_instance'] = null;
38if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
39    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
40} else {
41    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
42}
43
44// Below we define constants with default values for all configuration
45// parameters except username/password.  All of them can have their
46// defaults set through environment variables.  The reason we use the
47// PHP_ prefix is for some security, PHP protects environment
48// variables starting with PHP_*.
49
50// default channel and preferred mirror is based on whether we are invoked through
51// the "pear" or the "pecl" command
52
53if (!defined('PEAR_RUNTYPE') || PEAR_RUNTYPE == 'pear') {
54    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pear.php.net');
55} else {
56    define('PEAR_CONFIG_DEFAULT_CHANNEL', 'pecl.php.net');
57}
58
59if (getenv('PHP_PEAR_SYSCONF_DIR')) {
60    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
61} elseif (getenv('SystemRoot')) {
62    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
63} else {
64    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
65}
66
67// Default for master_server
68if (getenv('PHP_PEAR_MASTER_SERVER')) {
69    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
70} else {
71    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
72}
73
74// Default for http_proxy
75if (getenv('PHP_PEAR_HTTP_PROXY')) {
76    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
77} elseif (getenv('http_proxy')) {
78    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
79} else {
80    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
81}
82
83// Default for php_dir
84if (getenv('PHP_PEAR_INSTALL_DIR')) {
85    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
86} else {
87    if (@is_dir($PEAR_INSTALL_DIR)) {
88        define('PEAR_CONFIG_DEFAULT_PHP_DIR',
89               $PEAR_INSTALL_DIR);
90    } else {
91        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
92    }
93}
94
95// Default for ext_dir
96if (getenv('PHP_PEAR_EXTENSION_DIR')) {
97    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
98} else {
99    if (ini_get('extension_dir')) {
100        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
101    } elseif (defined('PEAR_EXTENSION_DIR') && @is_dir(PEAR_EXTENSION_DIR)) {
102        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
103    } elseif (defined('PHP_EXTENSION_DIR')) {
104        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
105    } else {
106        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
107    }
108}
109
110// Default for doc_dir
111if (getenv('PHP_PEAR_DOC_DIR')) {
112    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
113} else {
114    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
115           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
116}
117
118// Default for bin_dir
119if (getenv('PHP_PEAR_BIN_DIR')) {
120    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
121} else {
122    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
123}
124
125// Default for data_dir
126if (getenv('PHP_PEAR_DATA_DIR')) {
127    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
128} else {
129    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
130           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
131}
132
133// Default for test_dir
134if (getenv('PHP_PEAR_TEST_DIR')) {
135    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
136} else {
137    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
138           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
139}
140
141// Default for temp_dir
142if (getenv('PHP_PEAR_TEMP_DIR')) {
143    define('PEAR_CONFIG_DEFAULT_TEMP_DIR', getenv('PHP_PEAR_TEMP_DIR'));
144} else {
145    define('PEAR_CONFIG_DEFAULT_TEMP_DIR',
146           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
147           DIRECTORY_SEPARATOR . 'temp');
148}
149
150// Default for cache_dir
151if (getenv('PHP_PEAR_CACHE_DIR')) {
152    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
153} else {
154    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
155           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
156           DIRECTORY_SEPARATOR . 'cache');
157}
158
159// Default for download_dir
160if (getenv('PHP_PEAR_DOWNLOAD_DIR')) {
161    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR', getenv('PHP_PEAR_DOWNLOAD_DIR'));
162} else {
163    define('PEAR_CONFIG_DEFAULT_DOWNLOAD_DIR',
164           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
165           DIRECTORY_SEPARATOR . 'download');
166}
167
168// Default for php_bin
169if (getenv('PHP_PEAR_PHP_BIN')) {
170    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
171} else {
172    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
173           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
174}
175
176// Default for verbose
177if (getenv('PHP_PEAR_VERBOSE')) {
178    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
179} else {
180    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
181}
182
183// Default for preferred_state
184if (getenv('PHP_PEAR_PREFERRED_STATE')) {
185    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
186} else {
187    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
188}
189
190// Default for umask
191if (getenv('PHP_PEAR_UMASK')) {
192    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
193} else {
194    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
195}
196
197// Default for cache_ttl
198if (getenv('PHP_PEAR_CACHE_TTL')) {
199    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
200} else {
201    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
202}
203
204// Default for sig_type
205if (getenv('PHP_PEAR_SIG_TYPE')) {
206    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
207} else {
208    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
209}
210
211// Default for sig_bin
212if (getenv('PHP_PEAR_SIG_BIN')) {
213    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
214} else {
215    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
216           System::which(
217               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
218}
219
220// Default for sig_keydir
221if (getenv('PHP_PEAR_SIG_KEYDIR')) {
222    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
223} else {
224    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
225           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
226}
227
228/**
229 * This is a class for storing configuration data, keeping track of
230 * which are system-defined, user-defined or defaulted.
231 * @category   pear
232 * @package    PEAR
233 * @author     Stig Bakken <ssb@php.net>
234 * @author     Greg Beaver <cellog@php.net>
235 * @copyright  1997-2006 The PHP Group
236 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
237 * @version    Release: 1.4.11
238 * @link       http://pear.php.net/package/PEAR
239 * @since      Class available since Release 0.1
240 */
241class PEAR_Config extends PEAR
242{
243    // {{{ properties
244
245    /**
246     * Array of config files used.
247     *
248     * @var array layer => config file
249     */
250    var $files = array(
251        'system' => '',
252        'user' => '',
253        );
254
255    var $layers = array();
256
257    /**
258     * Configuration data, two-dimensional array where the first
259     * dimension is the config layer ('user', 'system' and 'default'),
260     * and the second dimension is keyname => value.
261     *
262     * The order in the first dimension is important!  Earlier
263     * layers will shadow later ones when a config value is
264     * requested (if a 'user' value exists, it will be returned first,
265     * then 'system' and finally 'default').
266     *
267     * @var array layer => array(keyname => value, ...)
268     */
269    var $configuration = array(
270        'user' => array(),
271        'system' => array(),
272        'default' => array(),
273        );
274
275    /**
276     * Configuration values that can be set for a channel
277     *
278     * All other configuration values can only have a global value
279     * @var array
280     * @access private
281     */
282    var $_channelConfigInfo = array(
283        'php_dir', 'ext_dir', 'doc_dir', 'bin_dir', 'data_dir',
284        'test_dir', 'php_bin', 'username', 'password', 'verbose',
285        'preferred_state', 'umask', 'preferred_mirror',
286        );
287
288    /**
289     * Channels that can be accessed
290     * @see setChannels()
291     * @var array
292     * @access private
293     */
294    var $_channels = array('pear.php.net', 'pecl.php.net', '__uri');
295
296    /**
297     * This variable is used to control the directory values returned
298     * @see setInstallRoot();
299     * @var string|false
300     * @access private
301     */
302    var $_installRoot = false;
303
304    /**
305     * If requested, this will always refer to the registry
306     * contained in php_dir
307     * @var PEAR_Registry
308     */
309    var $_registry = array();
310
311    /**
312     * @var array
313     * @access private
314     */
315    var $_regInitialized = array();
316
317    /**
318     * @var bool
319     * @access private
320     */
321    var $_noRegistry = false;
322
323    /**
324     * amount of errors found while parsing config
325     * @var integer
326     * @access private
327     */
328    var $_errorsFound = 0;
329    var $_lastError = null;
330
331    /**
332     * Information about the configuration data.  Stores the type,
333     * default value and a documentation string for each configuration
334     * value.
335     *
336     * @var array layer => array(infotype => value, ...)
337     */
338    var $configuration_info = array(
339        // Channels/Internet Access
340        'default_channel' => array(
341            'type' => 'string',
342            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
343            'doc' => 'the default channel to use for all non explicit commands',
344            'prompt' => 'Default Channel',
345            'group' => 'Internet Access',
346            ),
347        'preferred_mirror' => array(
348            'type' => 'string',
349            'default' => PEAR_CONFIG_DEFAULT_CHANNEL,
350            'doc' => 'the default server or mirror to use for channel actions',
351            'prompt' => 'Default Channel Mirror',
352            'group' => 'Internet Access',
353            ),
354        'remote_config' => array(
355            'type' => 'password',
356            'default' => '',
357            'doc' => 'ftp url of remote configuration file to use for synchronized install',
358            'prompt' => 'Remote Configuration File',
359            'group' => 'Internet Access',
360            ),
361        'auto_discover' => array(
362            'type' => 'integer',
363            'default' => 0,
364            'doc' => 'whether to automatically discover new channels',
365            'prompt' => 'Auto-discover new Channels',
366            'group' => 'Internet Access',
367            ),
368        // Internet Access
369        'master_server' => array(
370            'type' => 'string',
371            'default' => 'pear.php.net',
372            'doc' => 'name of the main PEAR server [NOT USED IN THIS VERSION]',
373            'prompt' => 'PEAR server [DEPRECATED]',
374            'group' => 'Internet Access',
375            ),
376        'http_proxy' => array(
377            'type' => 'string',
378            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
379            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
380            'prompt' => 'HTTP Proxy Server Address',
381            'group' => 'Internet Access',
382            ),
383        // File Locations
384        'php_dir' => array(
385            'type' => 'directory',
386            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
387            'doc' => 'directory where .php files are installed',
388            'prompt' => 'PEAR directory',
389            'group' => 'File Locations',
390            ),
391        'ext_dir' => array(
392            'type' => 'directory',
393            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
394            'doc' => 'directory where loadable extensions are installed',
395            'prompt' => 'PHP extension directory',
396            'group' => 'File Locations',
397            ),
398        'doc_dir' => array(
399            'type' => 'directory',
400            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
401            'doc' => 'directory where documentation is installed',
402            'prompt' => 'PEAR documentation directory',
403            'group' => 'File Locations',
404            ),
405        'bin_dir' => array(
406            'type' => 'directory',
407            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
408            'doc' => 'directory where executables are installed',
409            'prompt' => 'PEAR executables directory',
410            'group' => 'File Locations',
411            ),
412        'data_dir' => array(
413            'type' => 'directory',
414            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
415            'doc' => 'directory where data files are installed',
416            'prompt' => 'PEAR data directory',
417            'group' => 'File Locations (Advanced)',
418            ),
419        'test_dir' => array(
420            'type' => 'directory',
421            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
422            'doc' => 'directory where regression tests are installed',
423            'prompt' => 'PEAR test directory',
424            'group' => 'File Locations (Advanced)',
425            ),
426        'cache_dir' => array(
427            'type' => 'directory',
428            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
429            'doc' => 'directory which is used for XMLRPC cache',
430            'prompt' => 'PEAR Installer cache directory',
431            'group' => 'File Locations (Advanced)',
432            ),
433        'temp_dir' => array(
434            'type' => 'directory',
435            'default' => PEAR_CONFIG_DEFAULT_TEMP_DIR,
436            'doc' => 'directory which is used for all temp files',
437            'prompt' => 'PEAR Installer temp directory',
438            'group' => 'File Locations (Advanced)',
439            ),
440        'download_dir' => array(
441            'type' => 'directory',
442            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
443            'doc' => 'directory which is used for all downloaded files',
444            'prompt' => 'PEAR Installer download directory',
445            'group' => 'File Locations (Advanced)',
446            ),
447        'php_bin' => array(
448            'type' => 'file',
449            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
450            'doc' => 'PHP CLI/CGI binary for executing scripts',
451            'prompt' => 'PHP CLI/CGI binary',
452            'group' => 'File Locations (Advanced)',
453            ),
454        // Maintainers
455        'username' => array(
456            'type' => 'string',
457            'default' => '',
458            'doc' => '(maintainers) your PEAR account name',
459            'prompt' => 'PEAR username (for maintainers)',
460            'group' => 'Maintainers',
461            ),
462        'password' => array(
463            'type' => 'password',
464            'default' => '',
465            'doc' => '(maintainers) your PEAR account password',
466            'prompt' => 'PEAR password (for maintainers)',
467            'group' => 'Maintainers',
468            ),
469        // Advanced
470        'verbose' => array(
471            'type' => 'integer',
472            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
473            'doc' => 'verbosity level
4740: really quiet
4751: somewhat quiet
4762: verbose
4773: debug',
478            'prompt' => 'Debug Log Level',
479            'group' => 'Advanced',
480            ),
481        'preferred_state' => array(
482            'type' => 'set',
483            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
484            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
485            'valid_set' => array(
486                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
487            'prompt' => 'Preferred Package State',
488            'group' => 'Advanced',
489            ),
490        'umask' => array(
491            'type' => 'mask',
492            'default' => PEAR_CONFIG_DEFAULT_UMASK,
493            'doc' => 'umask used when creating files (Unix-like systems only)',
494            'prompt' => 'Unix file mask',
495            'group' => 'Advanced',
496            ),
497        'cache_ttl' => array(
498            'type' => 'integer',
499            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
500            'doc' => 'amount of secs where the local cache is used and not updated',
501            'prompt' => 'Cache TimeToLive',
502            'group' => 'Advanced',
503            ),
504        'sig_type' => array(
505            'type' => 'set',
506            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
507            'doc' => 'which package signature mechanism to use',
508            'valid_set' => array('gpg'),
509            'prompt' => 'Package Signature Type',
510            'group' => 'Maintainers',
511            ),
512        'sig_bin' => array(
513            'type' => 'string',
514            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
515            'doc' => 'which package signature mechanism to use',
516            'prompt' => 'Signature Handling Program',
517            'group' => 'Maintainers',
518            ),
519        'sig_keyid' => array(
520            'type' => 'string',
521            'default' => '',
522            'doc' => 'which key to use for signing with',
523            'prompt' => 'Signature Key Id',
524            'group' => 'Maintainers',
525            ),
526        'sig_keydir' => array(
527            'type' => 'directory',
528            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
529            'doc' => 'directory where signature keys are located',
530            'prompt' => 'Signature Key Directory',
531            'group' => 'Maintainers',
532            ),
533        // __channels is reserved - used for channel-specific configuration
534        );
535
536    // }}}
537
538    // {{{ PEAR_Config([file], [defaults_file])
539
540    /**
541     * Constructor.
542     *
543     * @param string file to read user-defined options from
544     * @param string file to read system-wide defaults from
545     * @param bool   determines whether a registry object "follows"
546     *               the value of php_dir (is automatically created
547     *               and moved when php_dir is changed)
548     * @param bool   if true, fails if configuration files cannot be loaded
549     *
550     * @access public
551     *
552     * @see PEAR_Config::singleton
553     */
554    function PEAR_Config($user_file = '', $system_file = '', $ftp_file = false,
555                         $strict = true)
556    {
557        $this->PEAR();
558        PEAR_Installer_Role::initializeConfig($this);
559        $sl = DIRECTORY_SEPARATOR;
560        if (empty($user_file)) {
561            if (OS_WINDOWS) {
562                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
563            } else {
564                $user_file = getenv('HOME') . $sl . '.pearrc';
565            }
566        }
567        if (empty($system_file)) {
568            if (OS_WINDOWS) {
569                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini';
570            } else {
571                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf';
572            }
573        }
574
575        $this->layers = array_keys($this->configuration);
576        $this->files['user'] = $user_file;
577        $this->files['system'] = $system_file;
578        if ($user_file && @file_exists($user_file)) {
579            $this->pushErrorHandling(PEAR_ERROR_RETURN);
580            $this->readConfigFile($user_file, 'user', $strict);
581            $this->popErrorHandling();
582            if ($this->_errorsFound > 0) {
583                return;
584            }
585        }
586
587        if ($system_file && @file_exists($system_file)) {
588            $this->mergeConfigFile($system_file, false, 'system', $strict);
589            if ($this->_errorsFound > 0) {
590                return;
591            }
592
593        }
594
595        if (!$ftp_file) {
596            $ftp_file = $this->get('remote_config');
597        }
598
599        if ($ftp_file && defined('PEAR_REMOTEINSTALL_OK')) {
600            $this->readFTPConfigFile($ftp_file);
601        }
602
603        foreach ($this->configuration_info as $key => $info) {
604            $this->configuration['default'][$key] = $info['default'];
605        }
606
607        $this->_registry['default'] = &new PEAR_Registry($this->configuration['default']['php_dir']);
608        $this->_registry['default']->setConfig($this);
609        $this->_regInitialized['default'] = false;
610        //$GLOBALS['_PEAR_Config_instance'] = &$this;
611    }
612
613    // }}}
614    // {{{ singleton([file], [defaults_file])
615
616    /**
617     * Static singleton method.  If you want to keep only one instance
618     * of this class in use, this method will give you a reference to
619     * the last created PEAR_Config object if one exists, or create a
620     * new object.
621     *
622     * @param string (optional) file to read user-defined options from
623     * @param string (optional) file to read system-wide defaults from
624     *
625     * @return object an existing or new PEAR_Config instance
626     *
627     * @access public
628     *
629     * @see PEAR_Config::PEAR_Config
630     */
631    function &singleton($user_file = '', $system_file = '', $strict = true)
632    {
633        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
634            return $GLOBALS['_PEAR_Config_instance'];
635        }
636
637        $t_conf = &new PEAR_Config($user_file, $system_file, false, $strict);
638        if ($t_conf->_errorsFound > 0) {
639             return $t_conf->lastError;
640        }
641
642        $GLOBALS['_PEAR_Config_instance'] = &$t_conf;
643        return $GLOBALS['_PEAR_Config_instance'];
644    }
645
646    // }}}
647    // {{{ validConfiguration()
648
649    /**
650     * Determine whether any configuration files have been detected, and whether a
651     * registry object can be retrieved from this configuration.
652     * @return bool
653     * @since PEAR 1.4.0a1
654     */
655    function validConfiguration()
656    {
657        if ($this->isDefinedLayer('user') || $this->isDefinedLayer('system')) {
658            return true;
659        }
660        return false;
661    }
662
663    // }}}
664    // {{{ readConfigFile([file], [layer])
665
666    /**
667     * Reads configuration data from a file.  All existing values in
668     * the config layer are discarded and replaced with data from the
669     * file.
670     * @param string file to read from, if NULL or not specified, the
671     *               last-used file for the same layer (second param) is used
672     * @param string config layer to insert data into ('user' or 'system')
673     * @return bool TRUE on success or a PEAR error on failure
674     */
675    function readConfigFile($file = null, $layer = 'user', $strict = true)
676    {
677        if (empty($this->files[$layer])) {
678            return $this->raiseError("unknown config layer `$layer'");
679        }
680
681        if ($file === null) {
682            $file = $this->files[$layer];
683        }
684
685        $data = $this->_readConfigDataFrom($file);
686
687        if (PEAR::isError($data)) {
688            if ($strict) {
689                $this->_errorsFound++;
690                $this->lastError = $data;
691
692                return $data;
693            } else {
694                return true;
695            }
696        } else {
697            $this->files[$layer] = $file;
698        }
699
700        $this->_decodeInput($data);
701        $this->configuration[$layer] = $data;
702        $this->_setupChannels();
703        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
704            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
705            $this->_registry[$layer]->setConfig($this);
706            $this->_regInitialized[$layer] = false;
707        } else {
708            unset($this->_registry[$layer]);
709        }
710        return true;
711    }
712
713    // }}}
714
715    /**
716     * @param string url to the remote config file, like ftp://www.example.com/pear/config.ini
717     * @return true|PEAR_Error
718     */
719    function readFTPConfigFile($path)
720    {
721        do { // poor man's try
722            if (!class_exists('Net_FTP')) {
723                if (!class_exists('PEAR_Common')) {
724                    require_once 'PEAR/Common.php';
725                }
726                if (PEAR_Common::isIncludeable('Net/FTP.php')) {
727                    include_once 'Net/FTP.php';
728                }
729            }
730            if (class_exists('Net_FTP') &&
731                  (class_exists('PEAR_FTP') || PEAR_Common::isIncludeable('PEAR/FTP.php'))) {
732                require_once 'PEAR/FTP.php';
733                $this->_ftp = &new PEAR_FTP;
734                $this->_ftp->pushErrorHandling(PEAR_ERROR_RETURN);
735                $e = $this->_ftp->init($path);
736                if (PEAR::isError($e)) {
737                    $this->_ftp->popErrorHandling();
738                    return $e;
739                }
740                $tmp = System::mktemp('-d');
741                PEAR_Common::addTempFile($tmp);
742                $e = $this->_ftp->get(basename($path), $tmp . DIRECTORY_SEPARATOR .
743                    'pear.ini', false, FTP_BINARY);
744                if (PEAR::isError($e)) {
745                    $this->_ftp->popErrorHandling();
746                    return $e;
747                }
748                PEAR_Common::addTempFile($tmp . DIRECTORY_SEPARATOR . 'pear.ini');
749                $this->_ftp->disconnect();
750                $this->_ftp->popErrorHandling();
751                $this->files['ftp'] = $tmp . DIRECTORY_SEPARATOR . 'pear.ini';
752                $e = $this->readConfigFile(null, 'ftp');
753                if (PEAR::isError($e)) {
754                    return $e;
755                }
756                $fail = array();
757                foreach ($this->configuration_info as $key => $val) {
758                    if (in_array($this->getGroup($key),
759                          array('File Locations', 'File Locations (Advanced)')) &&
760                          $this->getType($key) == 'directory') {
761                        // any directory configs must be set for this to work
762                        if (!isset($this->configuration['ftp'][$key])) {
763                            $fail[] = $key;
764                        }
765                    }
766                }
767                if (count($fail)) {
768                    $fail = '"' . implode('", "', $fail) . '"';
769                    unset($this->files['ftp']);
770                    unset($this->configuration['ftp']);
771                    return PEAR::raiseError('ERROR: Ftp configuration file must set all ' .
772                        'directory configuration variables.  These variables were not set: ' .
773                        $fail);
774                } else {
775                    return true;
776                }
777            } else {
778                return PEAR::raiseError('Net_FTP must be installed to use remote config');
779            }
780        } while (false); // poor man's catch
781        unset($this->files['ftp']);
782        return PEAR::raiseError('no remote host specified');
783    }
784
785    // {{{ _setupChannels()
786
787    /**
788     * Reads the existing configurations and creates the _channels array from it
789     */
790    function _setupChannels()
791    {
792        $set = array_flip(array_values($this->_channels));
793        foreach ($this->configuration as $layer => $data) {
794            $i = 1000;
795            if (isset($data['__channels'])) {
796                foreach ($data['__channels'] as $channel => $info) {
797                    $set[$channel] = $i++;
798                }
799            }
800        }
801        $this->_channels = array_values(array_flip($set));
802        $this->setChannels($this->_channels);
803    }
804
805    // }}}
806    // {{{ deleteChannel(channel)
807
808    function deleteChannel($channel)
809    {
810        foreach ($this->configuration as $layer => $data) {
811            if (isset($data['__channels'])) {
812                if (isset($data['__channels'][strtolower($channel)])) {
813                    unset($this->configuration[$layer]['__channels'][strtolower($channel)]);
814                }
815            }
816        }
817        $this->_channels = array_flip($this->_channels);
818        unset($this->_channels[strtolower($channel)]);
819        $this->_channels = array_flip($this->_channels);
820    }
821
822    // }}}
823    // {{{ mergeConfigFile(file, [override], [layer])
824
825    /**
826     * Merges data into a config layer from a file.  Does the same
827     * thing as readConfigFile, except it does not replace all
828     * existing values in the config layer.
829     * @param string file to read from
830     * @param bool whether to overwrite existing data (default TRUE)
831     * @param string config layer to insert data into ('user' or 'system')
832     * @param string if true, errors are returned if file opening fails
833     * @return bool TRUE on success or a PEAR error on failure
834     */
835    function mergeConfigFile($file, $override = true, $layer = 'user', $strict = true)
836    {
837        if (empty($this->files[$layer])) {
838            return $this->raiseError("unknown config layer `$layer'");
839        }
840        if ($file === null) {
841            $file = $this->files[$layer];
842        }
843        $data = $this->_readConfigDataFrom($file);
844        if (PEAR::isError($data)) {
845            if ($strict) {
846                $this->_errorsFound++;
847                $this->lastError = $data;
848
849                return $data;
850            } else {
851                return true;
852            }
853        }
854        $this->_decodeInput($data);
855        if ($override) {
856            $this->configuration[$layer] =
857                PEAR_Config::arrayMergeRecursive($this->configuration[$layer], $data);
858        } else {
859            $this->configuration[$layer] =
860                PEAR_Config::arrayMergeRecursive($data, $this->configuration[$layer]);
861        }
862        $this->_setupChannels();
863        if (!$this->_noRegistry && ($phpdir = $this->get('php_dir', $layer, 'pear.php.net'))) {
864            $this->_registry[$layer] = &new PEAR_Registry($phpdir);
865            $this->_registry[$layer]->setConfig($this);
866            $this->_regInitialized[$layer] = false;
867        } else {
868            unset($this->_registry[$layer]);
869        }
870        return true;
871    }
872
873    // }}}
874    // {{{ arrayMergeRecursive($arr2, $arr1)
875    /**
876     * @param array
877     * @param array
878     * @return array
879     * @static
880     */
881    function arrayMergeRecursive($arr2, $arr1)
882    {
883        $ret = array();
884        foreach ($arr2 as $key => $data) {
885            if (!isset($arr1[$key])) {
886                $ret[$key] = $data;
887                unset($arr1[$key]);
888                continue;
889            }
890            if (is_array($data)) {
891                if (!is_array($arr1[$key])) {
892                    $ret[$key] = $arr1[$key];
893                    unset($arr1[$key]);
894                    continue;
895                }
896                $ret[$key] = PEAR_Config::arrayMergeRecursive($arr1[$key], $arr2[$key]);
897                unset($arr1[$key]);
898            }
899        }
900        return array_merge($ret, $arr1);
901    }
902
903    // }}}
904    // {{{ writeConfigFile([file], [layer])
905
906    /**
907     * Writes data into a config layer from a file.
908     *
909     * @param string|null file to read from, or null for default
910     * @param string config layer to insert data into ('user' or
911     *               'system')
912     * @param string|null data to write to config file or null for internal data [DEPRECATED]
913     * @return bool TRUE on success or a PEAR error on failure
914     */
915    function writeConfigFile($file = null, $layer = 'user', $data = null)
916    {
917        $this->_lazyChannelSetup($layer);
918        if ($layer == 'both' || $layer == 'all') {
919            foreach ($this->files as $type => $file) {
920                $err = $this->writeConfigFile($file, $type, $data);
921                if (PEAR::isError($err)) {
922                    return $err;
923                }
924            }
925            return true;
926        }
927        if (empty($this->files[$layer])) {
928            return $this->raiseError("unknown config file type `$layer'");
929        }
930        if ($file === null) {
931            $file = $this->files[$layer];
932        }
933        $data = ($data === null) ? $this->configuration[$layer] : $data;
934        $this->_encodeOutput($data);
935        $opt = array('-p', dirname($file));
936        if (!@System::mkDir($opt)) {
937            return $this->raiseError("could not create directory: " . dirname($file));
938        }
939        if (@is_file($file) && !@is_writeable($file)) {
940            return $this->raiseError("no write access to $file!");
941        }
942        $fp = @fopen($file, "w");
943        if (!$fp) {
944            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed");
945        }
946        $contents = "#PEAR_Config 0.9\n" . serialize($data);
947        if (!@fwrite($fp, $contents)) {
948            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed");
949        }
950        return true;
951    }
952
953    // }}}
954    // {{{ _readConfigDataFrom(file)
955
956    /**
957     * Reads configuration data from a file and returns the parsed data
958     * in an array.
959     *
960     * @param string file to read from
961     *
962     * @return array configuration data or a PEAR error on failure
963     *
964     * @access private
965     */
966    function _readConfigDataFrom($file)
967    {
968        $fp = @fopen($file, "r");
969        if (!$fp) {
970            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
971        }
972        $size = filesize($file);
973        $rt = get_magic_quotes_runtime();
974        set_magic_quotes_runtime(0);
975        if (function_exists('file_get_contents')) {
976            fclose($fp);
977            $contents = file_get_contents($file);
978        } else {
979            $contents = @fread($fp, $size);
980            fclose($fp);
981        }
982        if (empty($contents)) {
983            return $this->raiseError('Configuration file "' . $file . '" is empty');
984        }
985
986        set_magic_quotes_runtime($rt);
987
988        $version = false;
989        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
990            $version = $matches[1];
991            $contents = substr($contents, strlen($matches[0]));
992        } else {
993            // Museum config file
994            if (substr($contents,0,2) == 'a:') {
995                $version = '0.1';
996            }
997        }
998        if ($version && version_compare("$version", '1', '<')) {
999
1000            // no '@', it is possible that unserialize
1001            // raises a notice but it seems to block IO to
1002            // STDOUT if a '@' is used and a notice is raise
1003            $data = unserialize($contents);
1004
1005            if (!is_array($data) && !$data) {
1006                if ($contents == serialize(false)) {
1007                    $data = array();
1008                } else {
1009                    $err = $this->raiseError("PEAR_Config: bad data in $file");
1010                    return $err;
1011                }
1012            }
1013            if (!is_array($data)) {
1014                if (strlen(trim($contents)) > 0) {
1015                    $error = "PEAR_Config: bad data in $file";
1016                    $err = $this->raiseError($error);
1017                    return $err;
1018                } else {
1019                    $data = array();
1020                }
1021            }
1022        // add parsing of newer formats here...
1023        } else {
1024            $err = $this->raiseError("$file: unknown version `$version'");
1025            return $err;
1026        }
1027        return $data;
1028    }
1029
1030    // }}}
1031    // {{{ getConfFile(layer)
1032    /**
1033    * Gets the file used for storing the config for a layer
1034    *
1035    * @param string $layer 'user' or 'system'
1036    */
1037
1038    function getConfFile($layer)
1039    {
1040        return $this->files[$layer];
1041    }
1042
1043    // }}}
1044
1045    /**
1046     * @param array information on a role as parsed from its xml file
1047     * @return true|PEAR_Error
1048     * @access private
1049     */
1050    function _addConfigVars($vars)
1051    {
1052        if (count($vars) > 3) {
1053            return $this->raiseError('Roles can only define 3 new config variables or less');
1054        }
1055        foreach ($vars as $name => $var) {
1056            if (!is_array($var)) {
1057                return $this->raiseError('Configuration information must be an array');
1058            }
1059            if (!isset($var['type'])) {
1060                return $this->raiseError('Configuration information must contain a type');
1061            } else {
1062                if (!in_array($var['type'],
1063                      array('string', 'mask', 'password', 'directory', 'file', 'set'))) {
1064                    return $this->raiseError(
1065                        'Configuration type must be one of directory, file, string, ' .
1066                        'mask, set, or password');
1067                }
1068            }
1069            if (!isset($var['default'])) {
1070                return $this->raiseError(
1071                    'Configuration information must contain a default value ("default" index)');
1072            } else {
1073                if (is_array($var['default'])) {
1074                    $real_default = '';
1075                    foreach ($var['default'] as $config_var => $val) {
1076                        if (strpos($config_var, 'text') === 0) {
1077                            $real_default .= $val;
1078                        } elseif (strpos($config_var, 'constant') === 0) {
1079                            if (defined($val)) {
1080                                $real_default .= constant($val);
1081                            } else {
1082                                return $this->raiseError(
1083                                    'Unknown constant "' . $val . '" requested in ' .
1084                                    'default value for configuration variable "' .
1085                                    $name . '"');
1086                            }
1087                        } elseif (isset($this->configuration_info[$config_var])) {
1088                            $real_default .=
1089                                $this->configuration_info[$config_var]['default'];
1090                        } else {
1091                            return $this->raiseError(
1092                                'Unknown request for "' . $config_var . '" value in ' .
1093                                'default value for configuration variable "' .
1094                                $name . '"');
1095                        }
1096                    }
1097                    $var['default'] = $real_default;
1098                }
1099                if ($var['type'] == 'integer') {
1100                    $var['default'] = (integer) $var['default'];
1101                }
1102            }
1103            if (!isset($var['doc'])) {
1104                return $this->raiseError(
1105                    'Configuration information must contain a summary ("doc" index)');
1106            }
1107            if (!isset($var['prompt'])) {
1108                return $this->raiseError(
1109                    'Configuration information must contain a simple prompt ("prompt" index)');
1110            }
1111            if (!isset($var['group'])) {
1112                return $this->raiseError(
1113                    'Configuration information must contain a simple group ("group" index)');
1114            }
1115            if (isset($this->configuration_info[$name])) {
1116                return $this->raiseError('Configuration variable "' . $name .
1117                    '" already exists');
1118            }
1119            $this->configuration_info[$name] = $var;
1120        }
1121        return true;
1122    }
1123
1124    // {{{ _encodeOutput(&data)
1125
1126    /**
1127     * Encodes/scrambles configuration data before writing to files.
1128     * Currently, 'password' values will be base64-encoded as to avoid
1129     * that people spot cleartext passwords by accident.
1130     *
1131     * @param array (reference) array to encode values in
1132     *
1133     * @return bool TRUE on success
1134     *
1135     * @access private
1136     */
1137    function _encodeOutput(&$data)
1138    {
1139        foreach ($data as $key => $value) {
1140            if ($key == '__channels') {
1141                foreach ($data['__channels'] as $channel => $blah) {
1142                    $this->_encodeOutput($data['__channels'][$channel]);
1143                }
1144            }
1145            if (!isset($this->configuration_info[$key])) {
1146                continue;
1147            }
1148            $type = $this->configuration_info[$key]['type'];
1149            switch ($type) {
1150                // we base64-encode passwords so they are at least
1151                // not shown in plain by accident
1152                case 'password': {
1153                    $data[$key] = base64_encode($data[$key]);
1154                    break;
1155                }
1156                case 'mask': {
1157                    $data[$key] = octdec($data[$key]);
1158                    break;
1159                }
1160            }
1161        }
1162        return true;
1163    }
1164
1165    // }}}
1166    // {{{ _decodeInput(&data)
1167
1168    /**
1169     * Decodes/unscrambles configuration data after reading from files.
1170     *
1171     * @param array (reference) array to encode values in
1172     *
1173     * @return bool TRUE on success
1174     *
1175     * @access private
1176     *
1177     * @see PEAR_Config::_encodeOutput
1178     */
1179    function _decodeInput(&$data)
1180    {
1181        if (!is_array($data)) {
1182            return true;
1183        }
1184        foreach ($data as $key => $value) {
1185            if ($key == '__channels') {
1186                foreach ($data['__channels'] as $channel => $blah) {
1187                    $this->_decodeInput($data['__channels'][$channel]);
1188                }
1189            }
1190            if (!isset($this->configuration_info[$key])) {
1191                continue;
1192            }
1193            $type = $this->configuration_info[$key]['type'];
1194            switch ($type) {
1195                case 'password': {
1196                    $data[$key] = base64_decode($data[$key]);
1197                    break;
1198                }
1199                case 'mask': {
1200                    $data[$key] = decoct($data[$key]);
1201                    break;
1202                }
1203            }
1204        }
1205        return true;
1206    }
1207
1208    // }}}
1209    // {{{ getDefaultChannel([layer])
1210    /**
1211     * Retrieve the default channel.
1212     *
1213     * On startup, channels are not initialized, so if the default channel is not
1214     * pear.php.net, then initialize the config.
1215     * @param string registry layer
1216     * @return string|false
1217     */
1218    function getDefaultChannel($layer = null)
1219    {
1220        $ret = false;
1221        if ($layer === null) {
1222            foreach ($this->layers as $layer) {
1223                if (isset($this->configuration[$layer]['default_channel'])) {
1224                    $ret = $this->configuration[$layer]['default_channel'];
1225                    break;
1226                }
1227            }
1228        } elseif (isset($this->configuration[$layer]['default_channel'])) {
1229            $ret = $this->configuration[$layer]['default_channel'];
1230        }
1231        if ($ret == 'pear.php.net' && defined('PEAR_RUNTYPE') && PEAR_RUNTYPE == 'pecl') {
1232            $ret = 'pecl.php.net';
1233        }
1234        if ($ret) {
1235            if ($ret != 'pear.php.net') {
1236                $this->_lazyChannelSetup();
1237            }
1238            return $ret;
1239        }
1240        return PEAR_CONFIG_DEFAULT_CHANNEL;
1241    }
1242
1243    // {{{ get(key, [layer])
1244    /**
1245     * Returns a configuration value, prioritizing layers as per the
1246     * layers property.
1247     *
1248     * @param string config key
1249     *
1250     * @return mixed the config value, or NULL if not found
1251     *
1252     * @access public
1253     */
1254    function get($key, $layer = null, $channel = false)
1255    {
1256        if (!isset($this->configuration_info[$key])) {
1257            return null;
1258        }
1259        if ($key == '__channels') {
1260            return null;
1261        }
1262        if ($key == 'default_channel') {
1263            return $this->getDefaultChannel($layer);
1264        }
1265        if (!$channel) {
1266            $channel = $this->getDefaultChannel();
1267        } elseif ($channel != 'pear.php.net') {
1268            $this->_lazyChannelSetup();
1269        }
1270        $channel = strtolower($channel);
1271
1272        $test = (in_array($key, $this->_channelConfigInfo)) ?
1273            $this->_getChannelValue($key, $layer, $channel) :
1274            null;
1275        if ($test !== null) {
1276            if ($this->_installRoot) {
1277                if (in_array($this->getGroup($key),
1278                      array('File Locations', 'File Locations (Advanced)')) &&
1279                      $this->getType($key) == 'directory') {
1280                    return $this->_prependPath($test, $this->_installRoot);
1281                }
1282            }
1283            return $test;
1284        }
1285        if ($layer === null) {
1286            foreach ($this->layers as $layer) {
1287                if (isset($this->configuration[$layer][$key])) {
1288                    $test = $this->configuration[$layer][$key];
1289                    if ($this->_installRoot) {
1290                        if (in_array($this->getGroup($key),
1291                              array('File Locations', 'File Locations (Advanced)')) &&
1292                              $this->getType($key) == 'directory') {
1293                            return $this->_prependPath($test, $this->_installRoot);
1294                        }
1295                    }
1296                    if ($key == 'preferred_mirror') {
1297                        $reg = &$this->getRegistry();
1298                        if (is_object($reg)) {
1299                            $chan = &$reg->getChannel($channel);
1300                            if (PEAR::isError($chan)) {
1301                                return $channel;
1302                            }
1303                            if (!$chan->getMirror($test) && $chan->getName() != $test) {
1304                                return $channel; // mirror does not exist
1305                            }
1306                        }
1307                    }
1308                    return $test;
1309                }
1310            }
1311        } elseif (isset($this->configuration[$layer][$key])) {
1312            $test = $this->configuration[$layer][$key];
1313            if ($this->_installRoot) {
1314                if (in_array($this->getGroup($key),
1315                      array('File Locations', 'File Locations (Advanced)')) &&
1316                      $this->getType($key) == 'directory') {
1317                    return $this->_prependPath($test, $this->_installRoot);
1318                }
1319            }
1320            if ($key == 'preferred_mirror') {
1321                $reg = &$this->getRegistry();
1322                if (is_object($reg)) {
1323                    $chan = &$reg->getChannel($channel);
1324                    if (PEAR::isError($chan)) {
1325                        return $channel;
1326                    }
1327                    if (!$chan->getMirror($test) && $chan->getName() != $test) {
1328                        return $channel; // mirror does not exist
1329                    }
1330                }
1331            }
1332            return $test;
1333        }
1334        return null;
1335    }
1336
1337    // }}}
1338    // {{{ _getChannelValue(key, value, [layer])
1339    /**
1340     * Returns a channel-specific configuration value, prioritizing layers as per the
1341     * layers property.
1342     *
1343     * @param string config key
1344     *
1345     * @return mixed the config value, or NULL if not found
1346     *
1347     * @access private
1348     */
1349    function _getChannelValue($key, $layer, $channel)
1350    {
1351        if ($key == '__channels' || $channel == 'pear.php.net') {
1352            return null;
1353        }
1354        $ret = null;
1355        if ($layer === null) {
1356            foreach ($this->layers as $ilayer) {
1357                if (isset($this->configuration[$ilayer]['__channels'][$channel][$key])) {
1358                    $ret = $this->configuration[$ilayer]['__channels'][$channel][$key];
1359                    break;
1360                }
1361            }
1362        } elseif (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
1363            $ret = $this->configuration[$layer]['__channels'][$channel][$key];
1364        }
1365        if ($key == 'preferred_mirror') {
1366            if ($ret !== null) {
1367                $reg = &$this->getRegistry($layer);
1368                if (is_object($reg)) {
1369                    $chan = &$reg->getChannel($channel);
1370                    if (PEAR::isError($chan)) {
1371                        return $channel;
1372                    }
1373                    if (!$chan->getMirror($ret) && $chan->getName() != $ret) {
1374                        return $channel; // mirror does not exist
1375                    }
1376                }
1377                return $ret;
1378            }
1379            if ($channel == $this->getDefaultChannel($layer)) {
1380                return $channel; // we must use the channel name as the preferred mirror
1381                                 // if the user has not chosen an alternate
1382            } else {
1383                return $this->getDefaultChannel($layer);
1384            }
1385        }
1386        return $ret;
1387    }
1388
1389
1390    // }}}
1391    // {{{ set(key, value, [layer])
1392
1393    /**
1394     * Set a config value in a specific layer (defaults to 'user').
1395     * Enforces the types defined in the configuration_info array.  An
1396     * integer config variable will be cast to int, and a set config
1397     * variable will be validated against its legal values.
1398     *
1399     * @param string config key
1400     * @param string config value
1401     * @param string (optional) config layer
1402     * @param string channel to set this value for, or null for global value
1403     * @return bool TRUE on success, FALSE on failure
1404     */
1405    function set($key, $value, $layer = 'user', $channel = false)
1406    {
1407        if ($key == '__channels') {
1408            return false;
1409        }
1410        if (!isset($this->configuration[$layer])) {
1411            return false;
1412        }
1413        if ($key == 'default_channel') {
1414            // can only set this value globally
1415            $channel = 'pear.php.net';
1416            if ($value != 'pear.php.net') {
1417                $this->_lazyChannelSetup($layer);
1418            }
1419        }
1420        if ($key == 'preferred_mirror') {
1421            if ($channel == '__uri') {
1422                return false; // can't set the __uri pseudo-channel's mirror
1423            }
1424            $reg = &$this->getRegistry($layer);
1425            if (is_object($reg)) {
1426                $chan = &$reg->getChannel($channel ? $channel : 'pear.php.net');
1427                if (PEAR::isError($chan)) {
1428                    return false;
1429                }
1430                if (!$chan->getMirror($value) && $chan->getName() != $value) {
1431                    return false; // mirror does not exist
1432                }
1433            }
1434        }
1435        if (empty($this->configuration_info[$key])) {
1436            return false;
1437        }
1438        extract($this->configuration_info[$key]);
1439        switch ($type) {
1440            case 'integer':
1441                $value = (int)$value;
1442                break;
1443            case 'set': {
1444                // If a valid_set is specified, require the value to
1445                // be in the set.  If there is no valid_set, accept
1446                // any value.
1447                if ($valid_set) {
1448                    reset($valid_set);
1449                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
1450                        (key($valid_set) !== 0 && empty($valid_set[$value])))
1451                    {
1452                        return false;
1453                    }
1454                }
1455                break;
1456            }
1457        }
1458        if (!$channel) {
1459            $channel = $this->get('default_channel', null, 'pear.php.net');
1460        }
1461        if (!in_array($channel, $this->_channels)) {
1462            $this->_lazyChannelSetup($layer);
1463            $reg = &$this->getRegistry($layer);
1464            if ($reg) {
1465                $channel = $reg->channelName($channel);
1466            }
1467            if (!in_array($channel, $this->_channels)) {
1468                return false;
1469            }
1470        }
1471        if ($channel != 'pear.php.net') {
1472            if (in_array($key, $this->_channelConfigInfo)) {
1473                $this->configuration[$layer]['__channels'][$channel][$key] = $value;
1474                return true;
1475            } else {
1476                return false;
1477            }
1478        } else {
1479            if ($key == 'default_channel') {
1480                if (!isset($reg)) {
1481                    $reg = &$this->getRegistry($layer);
1482                    if (!$reg) {
1483                        $reg = &$this->getRegistry();
1484                    }
1485                }
1486                if ($reg) {
1487                    $value = $reg->channelName($value);
1488                }
1489                if (!$value) {
1490                    return false;
1491                }
1492            }
1493        }
1494        $this->configuration[$layer][$key] = $value;
1495        if ($key == 'php_dir' && !$this->_noRegistry) {
1496            if (!isset($this->_registry[$layer]) ||
1497                  $value != $this->_registry[$layer]->install_dir) {
1498                $this->_registry[$layer] = &new PEAR_Registry($value);
1499                $this->_regInitialized[$layer] = false;
1500                $this->_registry[$layer]->setConfig($this);
1501            }
1502        }
1503        return true;
1504    }
1505
1506    // }}}
1507    function _lazyChannelSetup($uselayer = false)
1508    {
1509        if ($this->_noRegistry) {
1510            return;
1511        }
1512        $merge = false;
1513        foreach ($this->_registry as $layer => $p) {
1514            if ($uselayer && $uselayer != $layer) {
1515                continue;
1516            }
1517            if (!$this->_regInitialized[$layer]) {
1518                if ($layer == 'default' && isset($this->_registry['user']) ||
1519                      isset($this->_registry['system'])) {
1520                    // only use the default registry if there are no alternatives
1521                    continue;
1522                }
1523                if (!is_object($this->_registry[$layer])) {
1524                    if ($phpdir = $this->get('php_dir', $layer, 'pear.php.net')) {
1525                        $this->_registry[$layer] = &new PEAR_Registry($phpdir);
1526                        $this->_registry[$layer]->setConfig($this);
1527                        $this->_regInitialized[$layer] = false;
1528                    } else {
1529                        unset($this->_registry[$layer]);
1530                        return;
1531                    }
1532                }
1533                $this->setChannels($this->_registry[$layer]->listChannels(), $merge);
1534                $this->_regInitialized[$layer] = true;
1535                $merge = true;
1536            }
1537        }
1538    }
1539    // {{{ setChannels()
1540
1541    /**
1542     * Set the list of channels.
1543     *
1544     * This should be set via a call to {@link PEAR_Registry::listChannels()}
1545     * @param array
1546     * @param bool
1547     * @return bool success of operation
1548     */
1549    function setChannels($channels, $merge = false)
1550    {
1551        if (!is_array($channels)) {
1552            return false;
1553        }
1554        if ($merge) {
1555            $this->_channels = array_merge($this->_channels, $channels);
1556        } else {
1557            $this->_channels = $channels;
1558        }
1559        foreach ($channels as $channel) {
1560            $channel = strtolower($channel);
1561            if ($channel == 'pear.php.net') {
1562                continue;
1563            }
1564            foreach ($this->layers as $layer) {
1565                if (!isset($this->configuration[$layer]['__channels'][$channel])
1566                      || !is_array($this->configuration[$layer]['__channels'][$channel])) {
1567                    $this->configuration[$layer]['__channels'][$channel] = array();
1568                }
1569            }
1570        }
1571        return true;
1572    }
1573
1574    // }}}
1575    // {{{ getType(key)
1576
1577    /**
1578     * Get the type of a config value.
1579     *
1580     * @param string  config key
1581     *
1582     * @return string type, one of "string", "integer", "file",
1583     * "directory", "set" or "password".
1584     *
1585     * @access public
1586     *
1587     */
1588    function getType($key)
1589    {
1590        if (isset($this->configuration_info[$key])) {
1591            return $this->configuration_info[$key]['type'];
1592        }
1593        return false;
1594    }
1595
1596    // }}}
1597    // {{{ getDocs(key)
1598
1599    /**
1600     * Get the documentation for a config value.
1601     *
1602     * @param string  config key
1603     *
1604     * @return string documentation string
1605     *
1606     * @access public
1607     *
1608     */
1609    function getDocs($key)
1610    {
1611        if (isset($this->configuration_info[$key])) {
1612            return $this->configuration_info[$key]['doc'];
1613        }
1614        return false;
1615    }
1616       // }}}
1617    // {{{ getPrompt(key)
1618
1619    /**
1620     * Get the short documentation for a config value.
1621     *
1622     * @param string  config key
1623     *
1624     * @return string short documentation string
1625     *
1626     * @access public
1627     *
1628     */
1629    function getPrompt($key)
1630    {
1631        if (isset($this->configuration_info[$key])) {
1632            return $this->configuration_info[$key]['prompt'];
1633        }
1634        return false;
1635    }
1636    // }}}
1637    // {{{ getGroup(key)
1638
1639    /**
1640     * Get the parameter group for a config key.
1641     *
1642     * @param string  config key
1643     *
1644     * @return string parameter group
1645     *
1646     * @access public
1647     *
1648     */
1649    function getGroup($key)
1650    {
1651        if (isset($this->configuration_info[$key])) {
1652            return $this->configuration_info[$key]['group'];
1653        }
1654        return false;
1655    }
1656
1657    // }}}
1658    // {{{ getGroups()
1659
1660    /**
1661     * Get the list of parameter groups.
1662     *
1663     * @return array list of parameter groups
1664     *
1665     * @access public
1666     *
1667     */
1668    function getGroups()
1669    {
1670        $tmp = array();
1671        foreach ($this->configuration_info as $key => $info) {
1672            $tmp[$info['group']] = 1;
1673        }
1674        return array_keys($tmp);
1675    }
1676
1677    // }}}
1678    // {{{ getGroupKeys()
1679
1680    /**
1681     * Get the list of the parameters in a group.
1682     *
1683     * @param string $group parameter group
1684     *
1685     * @return array list of parameters in $group
1686     *
1687     * @access public
1688     *
1689     */
1690    function getGroupKeys($group)
1691    {
1692        $keys = array();
1693        foreach ($this->configuration_info as $key => $info) {
1694            if ($info['group'] == $group) {
1695                $keys[] = $key;
1696            }
1697        }
1698        return $keys;
1699    }
1700
1701    // }}}
1702    // {{{ getSetValues(key)
1703
1704    /**
1705     * Get the list of allowed set values for a config value.  Returns
1706     * NULL for config values that are not sets.
1707     *
1708     * @param string  config key
1709     *
1710     * @return array enumerated array of set values, or NULL if the
1711     *               config key is unknown or not a set
1712     *
1713     * @access public
1714     *
1715     */
1716    function getSetValues($key)
1717    {
1718        if (isset($this->configuration_info[$key]) &&
1719            isset($this->configuration_info[$key]['type']) &&
1720            $this->configuration_info[$key]['type'] == 'set')
1721        {
1722            $valid_set = $this->configuration_info[$key]['valid_set'];
1723            reset($valid_set);
1724            if (key($valid_set) === 0) {
1725                return $valid_set;
1726            }
1727            return array_keys($valid_set);
1728        }
1729        return null;
1730    }
1731
1732    // }}}
1733    // {{{ getKeys()
1734
1735    /**
1736     * Get all the current config keys.
1737     *
1738     * @return array simple array of config keys
1739     *
1740     * @access public
1741     */
1742    function getKeys()
1743    {
1744        $keys = array();
1745        foreach ($this->layers as $layer) {
1746            $test = $this->configuration[$layer];
1747            if (isset($test['__channels'])) {
1748                foreach ($test['__channels'] as $channel => $configs) {
1749                    $keys = array_merge($keys, $configs);
1750                }
1751            }
1752            unset($test['__channels']);
1753            $keys = array_merge($keys, $test);
1754        }
1755        return array_keys($keys);
1756    }
1757
1758    // }}}
1759    // {{{ remove(key, [layer])
1760
1761    /**
1762     * Remove the a config key from a specific config layer.
1763     *
1764     * @param string config key
1765     *
1766     * @param string (optional) config layer
1767     *
1768     * @return bool TRUE on success, FALSE on failure
1769     *
1770     * @access public
1771     */
1772    function remove($key, $layer = 'user')
1773    {
1774        $channel = $this->getDefaultChannel();
1775        if ($channel !== 'pear.php.net') {
1776            if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
1777                unset($this->configuration[$layer]['__channels'][$channel][$key]);
1778                return true;
1779            }
1780        }
1781        if (isset($this->configuration[$layer][$key])) {
1782            unset($this->configuration[$layer][$key]);
1783            return true;
1784        }
1785        return false;
1786    }
1787
1788    // }}}
1789    // {{{ removeLayer(layer)
1790
1791    /**
1792     * Temporarily remove an entire config layer.  USE WITH CARE!
1793     *
1794     * @param string config key
1795     *
1796     * @param string (optional) config layer
1797     *
1798     * @return bool TRUE on success, FALSE on failure
1799     *
1800     * @access public
1801     */
1802    function removeLayer($layer)
1803    {
1804        if (isset($this->configuration[$layer])) {
1805            $this->configuration[$layer] = array();
1806            return true;
1807        }
1808        return false;
1809    }
1810
1811    // }}}
1812    // {{{ store([layer])
1813
1814    /**
1815     * Stores configuration data in a layer.
1816     *
1817     * @param string config layer to store
1818     *
1819     * @return bool TRUE on success, or PEAR error on failure
1820     *
1821     * @access public
1822     */
1823    function store($layer = 'user', $data = null)
1824    {
1825        return $this->writeConfigFile(null, $layer, $data);
1826    }
1827
1828    // }}}
1829    // {{{ toDefault(key)
1830
1831    /**
1832     * Unset the user-defined value of a config key, reverting the
1833     * value to the system-defined one.
1834     *
1835     * @param string config key
1836     *
1837     * @return bool TRUE on success, FALSE on failure
1838     *
1839     * @access public
1840     */
1841    function toDefault($key)
1842    {
1843        trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE);
1844        return $this->remove($key, 'user');
1845    }
1846
1847    // }}}
1848    // {{{ definedBy(key)
1849
1850    /**
1851     * Tells what config layer that gets to define a key.
1852     *
1853     * @param string config key
1854     * @param boolean return the defining channel
1855     *
1856     * @return string|array the config layer, or an empty string if not found.
1857     *
1858     *         if $returnchannel, the return is an array array('layer' => layername,
1859     *         'channel' => channelname), or an empty string if not found
1860     *
1861     * @access public
1862     */
1863    function definedBy($key, $returnchannel = false)
1864    {
1865        foreach ($this->layers as $layer) {
1866            $channel = $this->getDefaultChannel();
1867            if ($channel !== 'pear.php.net') {
1868                if (isset($this->configuration[$layer]['__channels'][$channel][$key])) {
1869                    if ($returnchannel) {
1870                        return array('layer' => $layer, 'channel' => $channel);
1871                    }
1872                    return $layer;
1873                }
1874            }
1875            if (isset($this->configuration[$layer][$key])) {
1876                if ($returnchannel) {
1877                    return array('layer' => $layer, 'channel' => 'pear.php.net');
1878                }
1879                return $layer;
1880            }
1881        }
1882        return '';
1883    }
1884
1885    // }}}
1886    // {{{ isDefaulted(key)
1887
1888    /**
1889     * Tells whether a config value has a system-defined value.
1890     *
1891     * @param string   config key
1892     *
1893     * @return bool
1894     *
1895     * @access public
1896     *
1897     * @deprecated
1898     */
1899    function isDefaulted($key)
1900    {
1901        trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE);
1902        return $this->definedBy($key) == 'system';
1903    }
1904
1905    // }}}
1906    // {{{ isDefined(key)
1907
1908    /**
1909     * Tells whether a given key exists as a config value.
1910     *
1911     * @param string config key
1912     *
1913     * @return bool whether <config key> exists in this object
1914     *
1915     * @access public
1916     */
1917    function isDefined($key)
1918    {
1919        foreach ($this->layers as $layer) {
1920            if (isset($this->configuration[$layer][$key])) {
1921                return true;
1922            }
1923        }
1924        return false;
1925    }
1926
1927    // }}}
1928    // {{{ isDefinedLayer(key)
1929
1930    /**
1931     * Tells whether a given config layer exists.
1932     *
1933     * @param string config layer
1934     *
1935     * @return bool whether <config layer> exists in this object
1936     *
1937     * @access public
1938     */
1939    function isDefinedLayer($layer)
1940    {
1941        return isset($this->configuration[$layer]);
1942    }
1943
1944    // }}}
1945    // {{{ getLayers()
1946
1947    /**
1948     * Returns the layers defined (except the 'default' one)
1949     *
1950     * @return array of the defined layers
1951     */
1952    function getLayers()
1953    {
1954        $cf = $this->configuration;
1955        unset($cf['default']);
1956        return array_keys($cf);
1957    }
1958
1959    // }}}
1960    // {{{ apiVersion()
1961    function apiVersion()
1962    {
1963        return '1.1';
1964    }
1965    // }}}
1966
1967    /**
1968     * @return PEAR_Registry
1969     */
1970    function &getRegistry($use = null)
1971    {
1972        if ($use === null) {
1973            $layer = 'user';
1974        } else {
1975            $layer = $use;
1976        }
1977        if (isset($this->_registry[$layer])) {
1978            return $this->_registry[$layer];
1979        } elseif ($use === null && isset($this->_registry['system'])) {
1980            return $this->_registry['system'];
1981        } elseif ($use === null && isset($this->_registry['default'])) {
1982            return $this->_registry['default'];
1983        } elseif ($use) {
1984            $a = false;
1985            return $a;
1986        } else {
1987            // only go here if null was passed in
1988            die("CRITICAL ERROR: Registry could not be initialized from any value");
1989        }
1990    }
1991    /**
1992     * This is to allow customization like the use of installroot
1993     * @param PEAR_Registry
1994     * @return bool
1995     */
1996    function setRegistry(&$reg, $layer = 'user')
1997    {
1998        if ($this->_noRegistry) {
1999            return false;
2000        }
2001        if (!in_array($layer, array('user', 'system'))) {
2002            return false;
2003        }
2004        $this->_registry[$layer] = &$reg;
2005        if (is_object($reg)) {
2006            $this->_registry[$layer]->setConfig($this);
2007        }
2008        return true;
2009    }
2010
2011    function noRegistry()
2012    {
2013        $this->_noRegistry = true;
2014    }
2015
2016    /**
2017     * @return PEAR_Remote
2018     */
2019    function &getRemote()
2020    {
2021        $remote = &new PEAR_Remote($this);
2022        return $remote;
2023    }
2024
2025    /**
2026     * @return PEAR_REST
2027     */
2028    function &getREST($version, $options = array())
2029    {
2030        $version = str_replace('.', '', $version);
2031        if (!class_exists($class = 'PEAR_REST_' . $version)) {
2032            require_once 'PEAR/REST/' . $version . '.php';
2033        }
2034        $remote = &new $class($this, $options);
2035        return $remote;
2036    }
2037
2038    /**
2039     * The ftp server is set in {@link readFTPConfigFile()}.  It exists only if a
2040     * remote configuration file has been specified
2041     * @return PEAR_FTP|false
2042     */
2043    function &getFTP()
2044    {
2045        if (isset($this->_ftp)) {
2046            return $this->_ftp;
2047        } else {
2048            $a = false;
2049            return $a;
2050        }
2051    }
2052
2053    // {{{ _prependPath($path, $prepend)
2054
2055    function _prependPath($path, $prepend)
2056    {
2057        if (strlen($prepend) > 0) {
2058            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
2059                if (preg_match('/^[a-z]:/i', $prepend)) {
2060                    $prepend = substr($prepend, 2);
2061                } elseif ($prepend{0} != '\\') {
2062                    $prepend = "\\$prepend";
2063                }
2064                $path = substr($path, 0, 2) . $prepend . substr($path, 2);
2065            } else {
2066                $path = $prepend . $path;
2067            }
2068        }
2069        return $path;
2070    }
2071    // }}}
2072
2073    /**
2074     * @param string|false installation directory to prepend to all _dir variables, or false to
2075     *                     disable
2076     */
2077    function setInstallRoot($root)
2078    {
2079        if (substr($root, -1) == DIRECTORY_SEPARATOR) {
2080            $root = substr($root, 0, -1);
2081        }
2082        $old = $this->_installRoot;
2083        $this->_installRoot = $root;
2084        if (($old != $root) && !$this->_noRegistry) {
2085            foreach (array_keys($this->_registry) as $layer) {
2086                if ($layer == 'ftp' || !isset($this->_registry[$layer])) {
2087                    continue;
2088                }
2089                $this->_registry[$layer] =
2090                    &new PEAR_Registry($this->get('php_dir', $layer, 'pear.php.net'));
2091                $this->_registry[$layer]->setConfig($this);
2092                $this->_regInitialized[$layer] = false;
2093            }
2094        }
2095    }
2096}
2097
2098?>
2099