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