Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * PEAR_Registry
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * @category   pear
8
 * @package    PEAR
9
 * @author     Stig Bakken <ssb@php.net>
10
 * @author     Tomas V. V. Cox <cox@idecnet.com>
11
 * @author     Greg Beaver <cellog@php.net>
12
 * @copyright  1997-2009 The Authors
13
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
14
 * @version    CVS: $Id: Registry.php 313023 2011-07-06 19:17:11Z dufuz $
15
 * @link       http://pear.php.net/package/PEAR
16
 * @since      File available since Release 0.1
17
 */
18
 
19
/**
20
 * for PEAR_Error
21
 */
22
require_once 'PEAR.php';
23
require_once 'PEAR/DependencyDB.php';
24
 
25
define('PEAR_REGISTRY_ERROR_LOCK',         -2);
26
define('PEAR_REGISTRY_ERROR_FORMAT',       -3);
27
define('PEAR_REGISTRY_ERROR_FILE',         -4);
28
define('PEAR_REGISTRY_ERROR_CONFLICT',     -5);
29
define('PEAR_REGISTRY_ERROR_CHANNEL_FILE', -6);
30
 
31
/**
32
 * Administration class used to maintain the installed package database.
33
 * @category   pear
34
 * @package    PEAR
35
 * @author     Stig Bakken <ssb@php.net>
36
 * @author     Tomas V. V. Cox <cox@idecnet.com>
37
 * @author     Greg Beaver <cellog@php.net>
38
 * @copyright  1997-2009 The Authors
39
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
40
 * @version    Release: 1.9.4
41
 * @link       http://pear.php.net/package/PEAR
42
 * @since      Class available since Release 1.4.0a1
43
 */
44
class PEAR_Registry extends PEAR
45
{
46
    /**
47
     * File containing all channel information.
48
     * @var string
49
     */
50
    var $channels = '';
51
 
52
    /** Directory where registry files are stored.
53
     * @var string
54
     */
55
    var $statedir = '';
56
 
57
    /** File where the file map is stored
58
     * @var string
59
     */
60
    var $filemap = '';
61
 
62
    /** Directory where registry files for channels are stored.
63
     * @var string
64
     */
65
    var $channelsdir = '';
66
 
67
    /** Name of file used for locking the registry
68
     * @var string
69
     */
70
    var $lockfile = '';
71
 
72
    /** File descriptor used during locking
73
     * @var resource
74
     */
75
    var $lock_fp = null;
76
 
77
    /** Mode used during locking
78
     * @var int
79
     */
80
    var $lock_mode = 0; // XXX UNUSED
81
 
82
    /** Cache of package information.  Structure:
83
     * array(
84
     *   'package' => array('id' => ... ),
85
     *   ... )
86
     * @var array
87
     */
88
    var $pkginfo_cache = array();
89
 
90
    /** Cache of file map.  Structure:
91
     * array( '/path/to/file' => 'package', ... )
92
     * @var array
93
     */
94
    var $filemap_cache = array();
95
 
96
    /**
97
     * @var false|PEAR_ChannelFile
98
     */
99
    var $_pearChannel;
100
 
101
    /**
102
     * @var false|PEAR_ChannelFile
103
     */
104
    var $_peclChannel;
105
 
106
    /**
107
     * @var false|PEAR_ChannelFile
108
     */
109
    var $_docChannel;
110
 
111
    /**
112
     * @var PEAR_DependencyDB
113
     */
114
    var $_dependencyDB;
115
 
116
    /**
117
     * @var PEAR_Config
118
     */
119
    var $_config;
120
 
121
    /**
122
     * PEAR_Registry constructor.
123
     *
124
     * @param string (optional) PEAR install directory (for .php files)
125
     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PEAR channel, if
126
     *        default values are not desired.  Only used the very first time a PEAR
127
     *        repository is initialized
128
     * @param PEAR_ChannelFile PEAR_ChannelFile object representing the PECL channel, if
129
     *        default values are not desired.  Only used the very first time a PEAR
130
     *        repository is initialized
131
     *
132
     * @access public
133
     */
134
    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR, $pear_channel = false,
135
                           $pecl_channel = false)
136
    {
137
        parent::PEAR();
138
        $this->setInstallDir($pear_install_dir);
139
        $this->_pearChannel = $pear_channel;
140
        $this->_peclChannel = $pecl_channel;
141
        $this->_config      = false;
142
    }
143
 
144
    function setInstallDir($pear_install_dir = PEAR_INSTALL_DIR)
145
    {
146
        $ds = DIRECTORY_SEPARATOR;
147
        $this->install_dir = $pear_install_dir;
148
        $this->channelsdir = $pear_install_dir.$ds.'.channels';
149
        $this->statedir    = $pear_install_dir.$ds.'.registry';
150
        $this->filemap     = $pear_install_dir.$ds.'.filemap';
151
        $this->lockfile    = $pear_install_dir.$ds.'.lock';
152
    }
153
 
154
    function hasWriteAccess()
155
    {
156
        if (!file_exists($this->install_dir)) {
157
            $dir = $this->install_dir;
158
            while ($dir && $dir != '.') {
159
                $olddir = $dir;
160
                $dir    = dirname($dir);
161
                if ($dir != '.' && file_exists($dir)) {
162
                    if (is_writeable($dir)) {
163
                        return true;
164
                    }
165
 
166
                    return false;
167
                }
168
 
169
                if ($dir == $olddir) { // this can happen in safe mode
170
                    return @is_writable($dir);
171
                }
172
            }
173
 
174
            return false;
175
        }
176
 
177
        return is_writeable($this->install_dir);
178
    }
179
 
180
    function setConfig(&$config, $resetInstallDir = true)
181
    {
182
        $this->_config = &$config;
183
        if ($resetInstallDir) {
184
            $this->setInstallDir($config->get('php_dir'));
185
        }
186
    }
187
 
188
    function _initializeChannelDirs()
189
    {
190
        static $running = false;
191
        if (!$running) {
192
            $running = true;
193
            $ds = DIRECTORY_SEPARATOR;
194
            if (!is_dir($this->channelsdir) ||
195
                  !file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
196
                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
197
                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
198
                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
199
                if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
200
                    $pear_channel = $this->_pearChannel;
201
                    if (!is_a($pear_channel, 'PEAR_ChannelFile') || !$pear_channel->validate()) {
202
                        if (!class_exists('PEAR_ChannelFile')) {
203
                            require_once 'PEAR/ChannelFile.php';
204
                        }
205
 
206
                        $pear_channel = new PEAR_ChannelFile;
207
                        $pear_channel->setAlias('pear');
208
                        $pear_channel->setServer('pear.php.net');
209
                        $pear_channel->setSummary('PHP Extension and Application Repository');
210
                        $pear_channel->setDefaultPEARProtocols();
211
                        $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
212
                        $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
213
                        $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
214
                        //$pear_channel->setBaseURL('REST1.4', 'http://pear.php.net/rest/');
215
                    } else {
216
                        $pear_channel->setServer('pear.php.net');
217
                        $pear_channel->setAlias('pear');
218
                    }
219
 
220
                    $pear_channel->validate();
221
                    $this->_addChannel($pear_channel);
222
                }
223
 
224
                if (!file_exists($this->channelsdir . $ds . 'pecl.php.net.reg')) {
225
                    $pecl_channel = $this->_peclChannel;
226
                    if (!is_a($pecl_channel, 'PEAR_ChannelFile') || !$pecl_channel->validate()) {
227
                        if (!class_exists('PEAR_ChannelFile')) {
228
                            require_once 'PEAR/ChannelFile.php';
229
                        }
230
 
231
                        $pecl_channel = new PEAR_ChannelFile;
232
                        $pecl_channel->setAlias('pecl');
233
                        $pecl_channel->setServer('pecl.php.net');
234
                        $pecl_channel->setSummary('PHP Extension Community Library');
235
                        $pecl_channel->setDefaultPEARProtocols();
236
                        $pecl_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
237
                        $pecl_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
238
                        $pecl_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
239
                    } else {
240
                        $pecl_channel->setServer('pecl.php.net');
241
                        $pecl_channel->setAlias('pecl');
242
                    }
243
 
244
                    $pecl_channel->validate();
245
                    $this->_addChannel($pecl_channel);
246
                }
247
 
248
                if (!file_exists($this->channelsdir . $ds . 'doc.php.net.reg')) {
249
                    $doc_channel = $this->_docChannel;
250
                    if (!is_a($doc_channel, 'PEAR_ChannelFile') || !$doc_channel->validate()) {
251
                        if (!class_exists('PEAR_ChannelFile')) {
252
                            require_once 'PEAR/ChannelFile.php';
253
                        }
254
 
255
                        $doc_channel = new PEAR_ChannelFile;
256
                        $doc_channel->setAlias('phpdocs');
257
                        $doc_channel->setServer('doc.php.net');
258
                        $doc_channel->setSummary('PHP Documentation Team');
259
                        $doc_channel->setDefaultPEARProtocols();
260
                        $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
261
                        $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
262
                        $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
263
                    } else {
264
                        $doc_channel->setServer('doc.php.net');
265
                        $doc_channel->setAlias('doc');
266
                    }
267
 
268
                    $doc_channel->validate();
269
                    $this->_addChannel($doc_channel);
270
                }
271
 
272
                if (!file_exists($this->channelsdir . $ds . '__uri.reg')) {
273
                    if (!class_exists('PEAR_ChannelFile')) {
274
                        require_once 'PEAR/ChannelFile.php';
275
                    }
276
 
277
                    $private = new PEAR_ChannelFile;
278
                    $private->setName('__uri');
279
                    $private->setDefaultPEARProtocols();
280
                    $private->setBaseURL('REST1.0', '****');
281
                    $private->setSummary('Pseudo-channel for static packages');
282
                    $this->_addChannel($private);
283
                }
284
                $this->_rebuildFileMap();
285
            }
286
 
287
            $running = false;
288
        }
289
    }
290
 
291
    function _initializeDirs()
292
    {
293
        $ds = DIRECTORY_SEPARATOR;
294
        // XXX Compatibility code should be removed in the future
295
        // rename all registry files if any to lowercase
296
        if (!OS_WINDOWS && file_exists($this->statedir) && is_dir($this->statedir) &&
297
              $handle = opendir($this->statedir)) {
298
            $dest = $this->statedir . $ds;
299
            while (false !== ($file = readdir($handle))) {
300
                if (preg_match('/^.*[A-Z].*\.reg\\z/', $file)) {
301
                    rename($dest . $file, $dest . strtolower($file));
302
                }
303
            }
304
            closedir($handle);
305
        }
306
 
307
        $this->_initializeChannelDirs();
308
        if (!file_exists($this->filemap)) {
309
            $this->_rebuildFileMap();
310
        }
311
        $this->_initializeDepDB();
312
    }
313
 
314
    function _initializeDepDB()
315
    {
316
        if (!isset($this->_dependencyDB)) {
317
            static $initializing = false;
318
            if (!$initializing) {
319
                $initializing = true;
320
                if (!$this->_config) { // never used?
321
                    $file = OS_WINDOWS ? 'pear.ini' : '.pearrc';
322
                    $this->_config = &new PEAR_Config($this->statedir . DIRECTORY_SEPARATOR .
323
                        $file);
324
                    $this->_config->setRegistry($this);
325
                    $this->_config->set('php_dir', $this->install_dir);
326
                }
327
 
328
                $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
329
                if (PEAR::isError($this->_dependencyDB)) {
330
                    // attempt to recover by removing the dep db
331
                    if (file_exists($this->_config->get('php_dir', null, 'pear.php.net') .
332
                        DIRECTORY_SEPARATOR . '.depdb')) {
333
                        @unlink($this->_config->get('php_dir', null, 'pear.php.net') .
334
                            DIRECTORY_SEPARATOR . '.depdb');
335
                    }
336
 
337
                    $this->_dependencyDB = &PEAR_DependencyDB::singleton($this->_config);
338
                    if (PEAR::isError($this->_dependencyDB)) {
339
                        echo $this->_dependencyDB->getMessage();
340
                        echo 'Unrecoverable error';
341
                        exit(1);
342
                    }
343
                }
344
 
345
                $initializing = false;
346
            }
347
        }
348
    }
349
 
350
    /**
351
     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
352
     *
353
     * @access private
354
     */
355
    function _PEAR_Registry()
356
    {
357
        parent::_PEAR();
358
        if (is_resource($this->lock_fp)) {
359
            $this->_unlock();
360
        }
361
    }
362
 
363
    /**
364
     * Make sure the directory where we keep registry files exists.
365
     *
366
     * @return bool TRUE if directory exists, FALSE if it could not be
367
     * created
368
     *
369
     * @access private
370
     */
371
    function _assertStateDir($channel = false)
372
    {
373
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
374
            return $this->_assertChannelStateDir($channel);
375
        }
376
 
377
        static $init = false;
378
        if (!file_exists($this->statedir)) {
379
            if (!$this->hasWriteAccess()) {
380
                return false;
381
            }
382
 
383
            require_once 'System.php';
384
            if (!System::mkdir(array('-p', $this->statedir))) {
385
                return $this->raiseError("could not create directory '{$this->statedir}'");
386
            }
387
            $init = true;
388
        } elseif (!is_dir($this->statedir)) {
389
            return $this->raiseError('Cannot create directory ' . $this->statedir . ', ' .
390
                'it already exists and is not a directory');
391
        }
392
 
393
        $ds = DIRECTORY_SEPARATOR;
394
        if (!file_exists($this->channelsdir)) {
395
            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg') ||
396
                  !file_exists($this->channelsdir . $ds . 'pecl.php.net.reg') ||
397
                  !file_exists($this->channelsdir . $ds . 'doc.php.net.reg') ||
398
                  !file_exists($this->channelsdir . $ds . '__uri.reg')) {
399
                $init = true;
400
            }
401
        } elseif (!is_dir($this->channelsdir)) {
402
            return $this->raiseError('Cannot create directory ' . $this->channelsdir . ', ' .
403
                'it already exists and is not a directory');
404
        }
405
 
406
        if ($init) {
407
            static $running = false;
408
            if (!$running) {
409
                $running = true;
410
                $this->_initializeDirs();
411
                $running = false;
412
                $init = false;
413
            }
414
        } else {
415
            $this->_initializeDepDB();
416
        }
417
 
418
        return true;
419
    }
420
 
421
    /**
422
     * Make sure the directory where we keep registry files exists for a non-standard channel.
423
     *
424
     * @param string channel name
425
     * @return bool TRUE if directory exists, FALSE if it could not be
426
     * created
427
     *
428
     * @access private
429
     */
430
    function _assertChannelStateDir($channel)
431
    {
432
        $ds = DIRECTORY_SEPARATOR;
433
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
434
            if (!file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
435
                $this->_initializeChannelDirs();
436
            }
437
            return $this->_assertStateDir($channel);
438
        }
439
 
440
        $channelDir = $this->_channelDirectoryName($channel);
441
        if (!is_dir($this->channelsdir) ||
442
              !file_exists($this->channelsdir . $ds . 'pear.php.net.reg')) {
443
            $this->_initializeChannelDirs();
444
        }
445
 
446
        if (!file_exists($channelDir)) {
447
            if (!$this->hasWriteAccess()) {
448
                return false;
449
            }
450
 
451
            require_once 'System.php';
452
            if (!System::mkdir(array('-p', $channelDir))) {
453
                return $this->raiseError("could not create directory '" . $channelDir .
454
                    "'");
455
            }
456
        } elseif (!is_dir($channelDir)) {
457
            return $this->raiseError("could not create directory '" . $channelDir .
458
                "', already exists and is not a directory");
459
        }
460
 
461
        return true;
462
    }
463
 
464
    /**
465
     * Make sure the directory where we keep registry files for channels exists
466
     *
467
     * @return bool TRUE if directory exists, FALSE if it could not be
468
     * created
469
     *
470
     * @access private
471
     */
472
    function _assertChannelDir()
473
    {
474
        if (!file_exists($this->channelsdir)) {
475
            if (!$this->hasWriteAccess()) {
476
                return false;
477
            }
478
 
479
            require_once 'System.php';
480
            if (!System::mkdir(array('-p', $this->channelsdir))) {
481
                return $this->raiseError("could not create directory '{$this->channelsdir}'");
482
            }
483
        } elseif (!is_dir($this->channelsdir)) {
484
            return $this->raiseError("could not create directory '{$this->channelsdir}" .
485
                "', it already exists and is not a directory");
486
        }
487
 
488
        if (!file_exists($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
489
            if (!$this->hasWriteAccess()) {
490
                return false;
491
            }
492
 
493
            require_once 'System.php';
494
            if (!System::mkdir(array('-p', $this->channelsdir . DIRECTORY_SEPARATOR . '.alias'))) {
495
                return $this->raiseError("could not create directory '{$this->channelsdir}/.alias'");
496
            }
497
        } elseif (!is_dir($this->channelsdir . DIRECTORY_SEPARATOR . '.alias')) {
498
            return $this->raiseError("could not create directory '{$this->channelsdir}" .
499
                "/.alias', it already exists and is not a directory");
500
        }
501
 
502
        return true;
503
    }
504
 
505
    /**
506
     * Get the name of the file where data for a given package is stored.
507
     *
508
     * @param string channel name, or false if this is a PEAR package
509
     * @param string package name
510
     *
511
     * @return string registry file name
512
     *
513
     * @access public
514
     */
515
    function _packageFileName($package, $channel = false)
516
    {
517
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
518
            return $this->_channelDirectoryName($channel) . DIRECTORY_SEPARATOR .
519
                strtolower($package) . '.reg';
520
        }
521
 
522
        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
523
    }
524
 
525
    /**
526
     * Get the name of the file where data for a given channel is stored.
527
     * @param string channel name
528
     * @return string registry file name
529
     */
530
    function _channelFileName($channel, $noaliases = false)
531
    {
532
        if (!$noaliases) {
533
            if (file_exists($this->_getChannelAliasFileName($channel))) {
534
                $channel = implode('', file($this->_getChannelAliasFileName($channel)));
535
            }
536
        }
537
        return $this->channelsdir . DIRECTORY_SEPARATOR . str_replace('/', '_',
538
            strtolower($channel)) . '.reg';
539
    }
540
 
541
    /**
542
     * @param string
543
     * @return string
544
     */
545
    function _getChannelAliasFileName($alias)
546
    {
547
        return $this->channelsdir . DIRECTORY_SEPARATOR . '.alias' .
548
              DIRECTORY_SEPARATOR . str_replace('/', '_', strtolower($alias)) . '.txt';
549
    }
550
 
551
    /**
552
     * Get the name of a channel from its alias
553
     */
554
    function _getChannelFromAlias($channel)
555
    {
556
        if (!$this->_channelExists($channel)) {
557
            if ($channel == 'pear.php.net') {
558
                return 'pear.php.net';
559
            }
560
 
561
            if ($channel == 'pecl.php.net') {
562
                return 'pecl.php.net';
563
            }
564
 
565
            if ($channel == 'doc.php.net') {
566
                return 'doc.php.net';
567
            }
568
 
569
            if ($channel == '__uri') {
570
                return '__uri';
571
            }
572
 
573
            return false;
574
        }
575
 
576
        $channel = strtolower($channel);
577
        if (file_exists($this->_getChannelAliasFileName($channel))) {
578
            // translate an alias to an actual channel
579
            return implode('', file($this->_getChannelAliasFileName($channel)));
580
        }
581
 
582
        return $channel;
583
    }
584
 
585
    /**
586
     * Get the alias of a channel from its alias or its name
587
     */
588
    function _getAlias($channel)
589
    {
590
        if (!$this->_channelExists($channel)) {
591
            if ($channel == 'pear.php.net') {
592
                return 'pear';
593
            }
594
 
595
            if ($channel == 'pecl.php.net') {
596
                return 'pecl';
597
            }
598
 
599
            if ($channel == 'doc.php.net') {
600
                return 'phpdocs';
601
            }
602
 
603
            return false;
604
        }
605
 
606
        $channel = $this->_getChannel($channel);
607
        if (PEAR::isError($channel)) {
608
            return $channel;
609
        }
610
 
611
        return $channel->getAlias();
612
    }
613
 
614
    /**
615
     * Get the name of the file where data for a given package is stored.
616
     *
617
     * @param string channel name, or false if this is a PEAR package
618
     * @param string package name
619
     *
620
     * @return string registry file name
621
     *
622
     * @access public
623
     */
624
    function _channelDirectoryName($channel)
625
    {
626
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
627
            return $this->statedir;
628
        }
629
 
630
        $ch = $this->_getChannelFromAlias($channel);
631
        if (!$ch) {
632
            $ch = $channel;
633
        }
634
 
635
        return $this->statedir . DIRECTORY_SEPARATOR . strtolower('.channel.' .
636
            str_replace('/', '_', $ch));
637
    }
638
 
639
    function _openPackageFile($package, $mode, $channel = false)
640
    {
641
        if (!$this->_assertStateDir($channel)) {
642
            return null;
643
        }
644
 
645
        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
646
            return null;
647
        }
648
 
649
        $file = $this->_packageFileName($package, $channel);
650
        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
651
            return null;
652
        }
653
 
654
        $fp = @fopen($file, $mode);
655
        if (!$fp) {
656
            return null;
657
        }
658
 
659
        return $fp;
660
    }
661
 
662
    function _closePackageFile($fp)
663
    {
664
        fclose($fp);
665
    }
666
 
667
    function _openChannelFile($channel, $mode)
668
    {
669
        if (!$this->_assertChannelDir()) {
670
            return null;
671
        }
672
 
673
        if (!in_array($mode, array('r', 'rb')) && !$this->hasWriteAccess()) {
674
            return null;
675
        }
676
 
677
        $file = $this->_channelFileName($channel);
678
        if (!file_exists($file) && $mode == 'r' || $mode == 'rb') {
679
            return null;
680
        }
681
 
682
        $fp = @fopen($file, $mode);
683
        if (!$fp) {
684
            return null;
685
        }
686
 
687
        return $fp;
688
    }
689
 
690
    function _closeChannelFile($fp)
691
    {
692
        fclose($fp);
693
    }
694
 
695
    function _rebuildFileMap()
696
    {
697
        if (!class_exists('PEAR_Installer_Role')) {
698
            require_once 'PEAR/Installer/Role.php';
699
        }
700
 
701
        $channels = $this->_listAllPackages();
702
        $files = array();
703
        foreach ($channels as $channel => $packages) {
704
            foreach ($packages as $package) {
705
                $version = $this->_packageInfo($package, 'version', $channel);
706
                $filelist = $this->_packageInfo($package, 'filelist', $channel);
707
                if (!is_array($filelist)) {
708
                    continue;
709
                }
710
 
711
                foreach ($filelist as $name => $attrs) {
712
                    if (isset($attrs['attribs'])) {
713
                        $attrs = $attrs['attribs'];
714
                    }
715
 
716
                    // it is possible for conflicting packages in different channels to
717
                    // conflict with data files/doc files
718
                    if ($name == 'dirtree') {
719
                        continue;
720
                    }
721
 
722
                    if (isset($attrs['role']) && !in_array($attrs['role'],
723
                          PEAR_Installer_Role::getInstallableRoles())) {
724
                        // these are not installed
725
                        continue;
726
                    }
727
 
728
                    if (isset($attrs['role']) && !in_array($attrs['role'],
729
                          PEAR_Installer_Role::getBaseinstallRoles())) {
730
                        $attrs['baseinstalldir'] = $package;
731
                    }
732
 
733
                    if (isset($attrs['baseinstalldir'])) {
734
                        $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
735
                    } else {
736
                        $file = $name;
737
                    }
738
 
739
                    $file = preg_replace(',^/+,', '', $file);
740
                    if ($channel != 'pear.php.net') {
741
                        if (!isset($files[$attrs['role']])) {
742
                            $files[$attrs['role']] = array();
743
                        }
744
                        $files[$attrs['role']][$file] = array(strtolower($channel),
745
                            strtolower($package));
746
                    } else {
747
                        if (!isset($files[$attrs['role']])) {
748
                            $files[$attrs['role']] = array();
749
                        }
750
                        $files[$attrs['role']][$file] = strtolower($package);
751
                    }
752
                }
753
            }
754
        }
755
 
756
 
757
        $this->_assertStateDir();
758
        if (!$this->hasWriteAccess()) {
759
            return false;
760
        }
761
 
762
        $fp = @fopen($this->filemap, 'wb');
763
        if (!$fp) {
764
            return false;
765
        }
766
 
767
        $this->filemap_cache = $files;
768
        fwrite($fp, serialize($files));
769
        fclose($fp);
770
        return true;
771
    }
772
 
773
    function _readFileMap()
774
    {
775
        if (!file_exists($this->filemap)) {
776
            return array();
777
        }
778
 
779
        $fp = @fopen($this->filemap, 'r');
780
        if (!$fp) {
781
            return $this->raiseError('PEAR_Registry: could not open filemap "' . $this->filemap . '"', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
782
        }
783
 
784
        clearstatcache();
785
        $rt = get_magic_quotes_runtime();
786
        set_magic_quotes_runtime(0);
787
        $fsize = filesize($this->filemap);
788
        fclose($fp);
789
        $data = file_get_contents($this->filemap);
790
        set_magic_quotes_runtime($rt);
791
        $tmp = unserialize($data);
792
        if (!$tmp && $fsize > 7) {
793
            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
794
        }
795
 
796
        $this->filemap_cache = $tmp;
797
        return true;
798
    }
799
 
800
    /**
801
     * Lock the registry.
802
     *
803
     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
804
     *                See flock manual for more information.
805
     *
806
     * @return bool TRUE on success, FALSE if locking failed, or a
807
     *              PEAR error if some other error occurs (such as the
808
     *              lock file not being writable).
809
     *
810
     * @access private
811
     */
812
    function _lock($mode = LOCK_EX)
813
    {
814
        if (stristr(php_uname(), 'Windows 9')) {
815
            return true;
816
        }
817
 
818
        if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
819
            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
820
            return true;
821
        }
822
 
823
        if (!$this->_assertStateDir()) {
824
            if ($mode == LOCK_EX) {
825
                return $this->raiseError('Registry directory is not writeable by the current user');
826
            }
827
 
828
            return true;
829
        }
830
 
831
        $open_mode = 'w';
832
        // XXX People reported problems with LOCK_SH and 'w'
833
        if ($mode === LOCK_SH || $mode === LOCK_UN) {
834
            if (!file_exists($this->lockfile)) {
835
                touch($this->lockfile);
836
            }
837
            $open_mode = 'r';
838
        }
839
 
840
        if (!is_resource($this->lock_fp)) {
841
            $this->lock_fp = @fopen($this->lockfile, $open_mode);
842
        }
843
 
844
        if (!is_resource($this->lock_fp)) {
845
            $this->lock_fp = null;
846
            return $this->raiseError("could not create lock file" .
847
                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
848
        }
849
 
850
        if (!(int)flock($this->lock_fp, $mode)) {
851
            switch ($mode) {
852
                case LOCK_SH: $str = 'shared';    break;
853
                case LOCK_EX: $str = 'exclusive'; break;
854
                case LOCK_UN: $str = 'unlock';    break;
855
                default:      $str = 'unknown';   break;
856
            }
857
 
858
            //is resource at this point, close it on error.
859
            fclose($this->lock_fp);
860
            $this->lock_fp = null;
861
            return $this->raiseError("could not acquire $str lock ($this->lockfile)",
862
                                     PEAR_REGISTRY_ERROR_LOCK);
863
        }
864
 
865
        return true;
866
    }
867
 
868
    function _unlock()
869
    {
870
        $ret = $this->_lock(LOCK_UN);
871
        if (is_resource($this->lock_fp)) {
872
            fclose($this->lock_fp);
873
        }
874
 
875
        $this->lock_fp = null;
876
        return $ret;
877
    }
878
 
879
    function _packageExists($package, $channel = false)
880
    {
881
        return file_exists($this->_packageFileName($package, $channel));
882
    }
883
 
884
    /**
885
     * Determine whether a channel exists in the registry
886
     *
887
     * @param string Channel name
888
     * @param bool if true, then aliases will be ignored
889
     * @return boolean
890
     */
891
    function _channelExists($channel, $noaliases = false)
892
    {
893
        $a = file_exists($this->_channelFileName($channel, $noaliases));
894
        if (!$a && $channel == 'pear.php.net') {
895
            return true;
896
        }
897
 
898
        if (!$a && $channel == 'pecl.php.net') {
899
            return true;
900
        }
901
 
902
        if (!$a && $channel == 'doc.php.net') {
903
            return true;
904
        }
905
 
906
        return $a;
907
    }
908
 
909
    /**
910
     * Determine whether a mirror exists within the deafult channel in the registry
911
     *
912
     * @param string Channel name
913
     * @param string Mirror name
914
     *
915
     * @return boolean
916
     */
917
    function _mirrorExists($channel, $mirror)
918
    {
919
        $data = $this->_channelInfo($channel);
920
        if (!isset($data['servers']['mirror'])) {
921
            return false;
922
        }
923
 
924
        foreach ($data['servers']['mirror'] as $m) {
925
            if ($m['attribs']['host'] == $mirror) {
926
                return true;
927
            }
928
        }
929
 
930
        return false;
931
    }
932
 
933
    /**
934
     * @param PEAR_ChannelFile Channel object
935
     * @param donotuse
936
     * @param string Last-Modified HTTP tag from remote request
937
     * @return boolean|PEAR_Error True on creation, false if it already exists
938
     */
939
    function _addChannel($channel, $update = false, $lastmodified = false)
940
    {
941
        if (!is_a($channel, 'PEAR_ChannelFile')) {
942
            return false;
943
        }
944
 
945
        if (!$channel->validate()) {
946
            return false;
947
        }
948
 
949
        if (file_exists($this->_channelFileName($channel->getName()))) {
950
            if (!$update) {
951
                return false;
952
            }
953
 
954
            $checker = $this->_getChannel($channel->getName());
955
            if (PEAR::isError($checker)) {
956
                return $checker;
957
            }
958
 
959
            if ($channel->getAlias() != $checker->getAlias()) {
960
                if (file_exists($this->_getChannelAliasFileName($checker->getAlias()))) {
961
                    @unlink($this->_getChannelAliasFileName($checker->getAlias()));
962
                }
963
            }
964
        } else {
965
            if ($update && !in_array($channel->getName(), array('pear.php.net', 'pecl.php.net', 'doc.php.net'))) {
966
                return false;
967
            }
968
        }
969
 
970
        $ret = $this->_assertChannelDir();
971
        if (PEAR::isError($ret)) {
972
            return $ret;
973
        }
974
 
975
        $ret = $this->_assertChannelStateDir($channel->getName());
976
        if (PEAR::isError($ret)) {
977
            return $ret;
978
        }
979
 
980
        if ($channel->getAlias() != $channel->getName()) {
981
            if (file_exists($this->_getChannelAliasFileName($channel->getAlias())) &&
982
                  $this->_getChannelFromAlias($channel->getAlias()) != $channel->getName()) {
983
                $channel->setAlias($channel->getName());
984
            }
985
 
986
            if (!$this->hasWriteAccess()) {
987
                return false;
988
            }
989
 
990
            $fp = @fopen($this->_getChannelAliasFileName($channel->getAlias()), 'w');
991
            if (!$fp) {
992
                return false;
993
            }
994
 
995
            fwrite($fp, $channel->getName());
996
            fclose($fp);
997
        }
998
 
999
        if (!$this->hasWriteAccess()) {
1000
            return false;
1001
        }
1002
 
1003
        $fp = @fopen($this->_channelFileName($channel->getName()), 'wb');
1004
        if (!$fp) {
1005
            return false;
1006
        }
1007
 
1008
        $info = $channel->toArray();
1009
        if ($lastmodified) {
1010
            $info['_lastmodified'] = $lastmodified;
1011
        } else {
1012
            $info['_lastmodified'] = date('r');
1013
        }
1014
 
1015
        fwrite($fp, serialize($info));
1016
        fclose($fp);
1017
        return true;
1018
    }
1019
 
1020
    /**
1021
     * Deletion fails if there are any packages installed from the channel
1022
     * @param string|PEAR_ChannelFile channel name
1023
     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
1024
     */
1025
    function _deleteChannel($channel)
1026
    {
1027
        if (!is_string($channel)) {
1028
            if (!is_a($channel, 'PEAR_ChannelFile')) {
1029
                return false;
1030
            }
1031
 
1032
            if (!$channel->validate()) {
1033
                return false;
1034
            }
1035
            $channel = $channel->getName();
1036
        }
1037
 
1038
        if ($this->_getChannelFromAlias($channel) == '__uri') {
1039
            return false;
1040
        }
1041
 
1042
        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
1043
            return false;
1044
        }
1045
 
1046
        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
1047
            return false;
1048
        }
1049
 
1050
        if (!$this->_channelExists($channel)) {
1051
            return false;
1052
        }
1053
 
1054
        if (!$channel || $this->_getChannelFromAlias($channel) == 'pear.php.net') {
1055
            return false;
1056
        }
1057
 
1058
        $channel = $this->_getChannelFromAlias($channel);
1059
        if ($channel == 'pear.php.net') {
1060
            return false;
1061
        }
1062
 
1063
        $test = $this->_listChannelPackages($channel);
1064
        if (count($test)) {
1065
            return false;
1066
        }
1067
 
1068
        $test = @rmdir($this->_channelDirectoryName($channel));
1069
        if (!$test) {
1070
            return false;
1071
        }
1072
 
1073
        $file = $this->_getChannelAliasFileName($this->_getAlias($channel));
1074
        if (file_exists($file)) {
1075
            $test = @unlink($file);
1076
            if (!$test) {
1077
                return false;
1078
            }
1079
        }
1080
 
1081
        $file = $this->_channelFileName($channel);
1082
        $ret = true;
1083
        if (file_exists($file)) {
1084
            $ret = @unlink($file);
1085
        }
1086
 
1087
        return $ret;
1088
    }
1089
 
1090
    /**
1091
     * Determine whether a channel exists in the registry
1092
     * @param string Channel Alias
1093
     * @return boolean
1094
     */
1095
    function _isChannelAlias($alias)
1096
    {
1097
        return file_exists($this->_getChannelAliasFileName($alias));
1098
    }
1099
 
1100
    /**
1101
     * @param string|null
1102
     * @param string|null
1103
     * @param string|null
1104
     * @return array|null
1105
     * @access private
1106
     */
1107
    function _packageInfo($package = null, $key = null, $channel = 'pear.php.net')
1108
    {
1109
        if ($package === null) {
1110
            if ($channel === null) {
1111
                $channels = $this->_listChannels();
1112
                $ret = array();
1113
                foreach ($channels as $channel) {
1114
                    $channel = strtolower($channel);
1115
                    $ret[$channel] = array();
1116
                    $packages = $this->_listPackages($channel);
1117
                    foreach ($packages as $package) {
1118
                        $ret[$channel][] = $this->_packageInfo($package, null, $channel);
1119
                    }
1120
                }
1121
 
1122
                return $ret;
1123
            }
1124
 
1125
            $ps = $this->_listPackages($channel);
1126
            if (!count($ps)) {
1127
                return array();
1128
            }
1129
            return array_map(array(&$this, '_packageInfo'),
1130
                             $ps, array_fill(0, count($ps), null),
1131
                             array_fill(0, count($ps), $channel));
1132
        }
1133
 
1134
        $fp = $this->_openPackageFile($package, 'r', $channel);
1135
        if ($fp === null) {
1136
            return null;
1137
        }
1138
 
1139
        $rt = get_magic_quotes_runtime();
1140
        set_magic_quotes_runtime(0);
1141
        clearstatcache();
1142
        $this->_closePackageFile($fp);
1143
        $data = file_get_contents($this->_packageFileName($package, $channel));
1144
        set_magic_quotes_runtime($rt);
1145
        $data = unserialize($data);
1146
        if ($key === null) {
1147
            return $data;
1148
        }
1149
 
1150
        // compatibility for package.xml version 2.0
1151
        if (isset($data['old'][$key])) {
1152
            return $data['old'][$key];
1153
        }
1154
 
1155
        if (isset($data[$key])) {
1156
            return $data[$key];
1157
        }
1158
 
1159
        return null;
1160
    }
1161
 
1162
    /**
1163
     * @param string Channel name
1164
     * @param bool whether to strictly retrieve info of channels, not just aliases
1165
     * @return array|null
1166
     */
1167
    function _channelInfo($channel, $noaliases = false)
1168
    {
1169
        if (!$this->_channelExists($channel, $noaliases)) {
1170
            return null;
1171
        }
1172
 
1173
        $fp = $this->_openChannelFile($channel, 'r');
1174
        if ($fp === null) {
1175
            return null;
1176
        }
1177
 
1178
        $rt = get_magic_quotes_runtime();
1179
        set_magic_quotes_runtime(0);
1180
        clearstatcache();
1181
        $this->_closeChannelFile($fp);
1182
        $data = file_get_contents($this->_channelFileName($channel));
1183
        set_magic_quotes_runtime($rt);
1184
        $data = unserialize($data);
1185
        return $data;
1186
    }
1187
 
1188
    function _listChannels()
1189
    {
1190
        $channellist = array();
1191
        if (!file_exists($this->channelsdir) || !is_dir($this->channelsdir)) {
1192
            return array('pear.php.net', 'pecl.php.net', 'doc.php.net', '__uri');
1193
        }
1194
 
1195
        $dp = opendir($this->channelsdir);
1196
        while ($ent = readdir($dp)) {
1197
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
1198
                continue;
1199
            }
1200
 
1201
            if ($ent == '__uri.reg') {
1202
                $channellist[] = '__uri';
1203
                continue;
1204
            }
1205
 
1206
            $channellist[] = str_replace('_', '/', substr($ent, 0, -4));
1207
        }
1208
 
1209
        closedir($dp);
1210
        if (!in_array('pear.php.net', $channellist)) {
1211
            $channellist[] = 'pear.php.net';
1212
        }
1213
 
1214
        if (!in_array('pecl.php.net', $channellist)) {
1215
            $channellist[] = 'pecl.php.net';
1216
        }
1217
 
1218
        if (!in_array('doc.php.net', $channellist)) {
1219
            $channellist[] = 'doc.php.net';
1220
        }
1221
 
1222
 
1223
        if (!in_array('__uri', $channellist)) {
1224
            $channellist[] = '__uri';
1225
        }
1226
 
1227
        natsort($channellist);
1228
        return $channellist;
1229
    }
1230
 
1231
    function _listPackages($channel = false)
1232
    {
1233
        if ($channel && $this->_getChannelFromAlias($channel) != 'pear.php.net') {
1234
            return $this->_listChannelPackages($channel);
1235
        }
1236
 
1237
        if (!file_exists($this->statedir) || !is_dir($this->statedir)) {
1238
            return array();
1239
        }
1240
 
1241
        $pkglist = array();
1242
        $dp = opendir($this->statedir);
1243
        if (!$dp) {
1244
            return $pkglist;
1245
        }
1246
 
1247
        while ($ent = readdir($dp)) {
1248
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
1249
                continue;
1250
            }
1251
 
1252
            $pkglist[] = substr($ent, 0, -4);
1253
        }
1254
        closedir($dp);
1255
        return $pkglist;
1256
    }
1257
 
1258
    function _listChannelPackages($channel)
1259
    {
1260
        $pkglist = array();
1261
        if (!file_exists($this->_channelDirectoryName($channel)) ||
1262
              !is_dir($this->_channelDirectoryName($channel))) {
1263
            return array();
1264
        }
1265
 
1266
        $dp = opendir($this->_channelDirectoryName($channel));
1267
        if (!$dp) {
1268
            return $pkglist;
1269
        }
1270
 
1271
        while ($ent = readdir($dp)) {
1272
            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
1273
                continue;
1274
            }
1275
            $pkglist[] = substr($ent, 0, -4);
1276
        }
1277
 
1278
        closedir($dp);
1279
        return $pkglist;
1280
    }
1281
 
1282
    function _listAllPackages()
1283
    {
1284
        $ret = array();
1285
        foreach ($this->_listChannels() as $channel) {
1286
            $ret[$channel] = $this->_listPackages($channel);
1287
        }
1288
 
1289
        return $ret;
1290
    }
1291
 
1292
    /**
1293
     * Add an installed package to the registry
1294
     * @param string package name
1295
     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
1296
     * @return bool success of saving
1297
     * @access private
1298
     */
1299
    function _addPackage($package, $info)
1300
    {
1301
        if ($this->_packageExists($package)) {
1302
            return false;
1303
        }
1304
 
1305
        $fp = $this->_openPackageFile($package, 'wb');
1306
        if ($fp === null) {
1307
            return false;
1308
        }
1309
 
1310
        $info['_lastmodified'] = time();
1311
        fwrite($fp, serialize($info));
1312
        $this->_closePackageFile($fp);
1313
        if (isset($info['filelist'])) {
1314
            $this->_rebuildFileMap();
1315
        }
1316
 
1317
        return true;
1318
    }
1319
 
1320
    /**
1321
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1322
     * @return bool
1323
     * @access private
1324
     */
1325
    function _addPackage2($info)
1326
    {
1327
        if (!is_a($info, 'PEAR_PackageFile_v1') && !is_a($info, 'PEAR_PackageFile_v2')) {
1328
            return false;
1329
        }
1330
 
1331
        if (!$info->validate()) {
1332
            if (class_exists('PEAR_Common')) {
1333
                $ui = PEAR_Frontend::singleton();
1334
                if ($ui) {
1335
                    foreach ($info->getValidationWarnings() as $err) {
1336
                        $ui->log($err['message'], true);
1337
                    }
1338
                }
1339
            }
1340
            return false;
1341
        }
1342
 
1343
        $channel = $info->getChannel();
1344
        $package = $info->getPackage();
1345
        $save = $info;
1346
        if ($this->_packageExists($package, $channel)) {
1347
            return false;
1348
        }
1349
 
1350
        if (!$this->_channelExists($channel, true)) {
1351
            return false;
1352
        }
1353
 
1354
        $info = $info->toArray(true);
1355
        if (!$info) {
1356
            return false;
1357
        }
1358
 
1359
        $fp = $this->_openPackageFile($package, 'wb', $channel);
1360
        if ($fp === null) {
1361
            return false;
1362
        }
1363
 
1364
        $info['_lastmodified'] = time();
1365
        fwrite($fp, serialize($info));
1366
        $this->_closePackageFile($fp);
1367
        $this->_rebuildFileMap();
1368
        return true;
1369
    }
1370
 
1371
    /**
1372
     * @param string Package name
1373
     * @param array parsed package.xml 1.0
1374
     * @param bool this parameter is only here for BC.  Don't use it.
1375
     * @access private
1376
     */
1377
    function _updatePackage($package, $info, $merge = true)
1378
    {
1379
        $oldinfo = $this->_packageInfo($package);
1380
        if (empty($oldinfo)) {
1381
            return false;
1382
        }
1383
 
1384
        $fp = $this->_openPackageFile($package, 'w');
1385
        if ($fp === null) {
1386
            return false;
1387
        }
1388
 
1389
        if (is_object($info)) {
1390
            $info = $info->toArray();
1391
        }
1392
        $info['_lastmodified'] = time();
1393
 
1394
        $newinfo = $info;
1395
        if ($merge) {
1396
            $info = array_merge($oldinfo, $info);
1397
        } else {
1398
            $diff = $info;
1399
        }
1400
 
1401
        fwrite($fp, serialize($info));
1402
        $this->_closePackageFile($fp);
1403
        if (isset($newinfo['filelist'])) {
1404
            $this->_rebuildFileMap();
1405
        }
1406
 
1407
        return true;
1408
    }
1409
 
1410
    /**
1411
     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1412
     * @return bool
1413
     * @access private
1414
     */
1415
    function _updatePackage2($info)
1416
    {
1417
        if (!$this->_packageExists($info->getPackage(), $info->getChannel())) {
1418
            return false;
1419
        }
1420
 
1421
        $fp = $this->_openPackageFile($info->getPackage(), 'w', $info->getChannel());
1422
        if ($fp === null) {
1423
            return false;
1424
        }
1425
 
1426
        $save = $info;
1427
        $info = $save->getArray(true);
1428
        $info['_lastmodified'] = time();
1429
        fwrite($fp, serialize($info));
1430
        $this->_closePackageFile($fp);
1431
        $this->_rebuildFileMap();
1432
        return true;
1433
    }
1434
 
1435
    /**
1436
     * @param string Package name
1437
     * @param string Channel name
1438
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
1439
     * @access private
1440
     */
1441
    function &_getPackage($package, $channel = 'pear.php.net')
1442
    {
1443
        $info = $this->_packageInfo($package, null, $channel);
1444
        if ($info === null) {
1445
            return $info;
1446
        }
1447
 
1448
        $a = $this->_config;
1449
        if (!$a) {
1450
            $this->_config = &new PEAR_Config;
1451
            $this->_config->set('php_dir', $this->statedir);
1452
        }
1453
 
1454
        if (!class_exists('PEAR_PackageFile')) {
1455
            require_once 'PEAR/PackageFile.php';
1456
        }
1457
 
1458
        $pkg = &new PEAR_PackageFile($this->_config);
1459
        $pf = &$pkg->fromArray($info);
1460
        return $pf;
1461
    }
1462
 
1463
    /**
1464
     * @param string channel name
1465
     * @param bool whether to strictly retrieve channel names
1466
     * @return PEAR_ChannelFile|PEAR_Error
1467
     * @access private
1468
     */
1469
    function &_getChannel($channel, $noaliases = false)
1470
    {
1471
        $ch = false;
1472
        if ($this->_channelExists($channel, $noaliases)) {
1473
            $chinfo = $this->_channelInfo($channel, $noaliases);
1474
            if ($chinfo) {
1475
                if (!class_exists('PEAR_ChannelFile')) {
1476
                    require_once 'PEAR/ChannelFile.php';
1477
                }
1478
 
1479
                $ch = &PEAR_ChannelFile::fromArrayWithErrors($chinfo);
1480
            }
1481
        }
1482
 
1483
        if ($ch) {
1484
            if ($ch->validate()) {
1485
                return $ch;
1486
            }
1487
 
1488
            foreach ($ch->getErrors(true) as $err) {
1489
                $message = $err['message'] . "\n";
1490
            }
1491
 
1492
            $ch = PEAR::raiseError($message);
1493
            return $ch;
1494
        }
1495
 
1496
        if ($this->_getChannelFromAlias($channel) == 'pear.php.net') {
1497
            // the registry is not properly set up, so use defaults
1498
            if (!class_exists('PEAR_ChannelFile')) {
1499
                require_once 'PEAR/ChannelFile.php';
1500
            }
1501
 
1502
            $pear_channel = new PEAR_ChannelFile;
1503
            $pear_channel->setServer('pear.php.net');
1504
            $pear_channel->setAlias('pear');
1505
            $pear_channel->setSummary('PHP Extension and Application Repository');
1506
            $pear_channel->setDefaultPEARProtocols();
1507
            $pear_channel->setBaseURL('REST1.0', 'http://pear.php.net/rest/');
1508
            $pear_channel->setBaseURL('REST1.1', 'http://pear.php.net/rest/');
1509
            $pear_channel->setBaseURL('REST1.3', 'http://pear.php.net/rest/');
1510
            return $pear_channel;
1511
        }
1512
 
1513
        if ($this->_getChannelFromAlias($channel) == 'pecl.php.net') {
1514
            // the registry is not properly set up, so use defaults
1515
            if (!class_exists('PEAR_ChannelFile')) {
1516
                require_once 'PEAR/ChannelFile.php';
1517
            }
1518
            $pear_channel = new PEAR_ChannelFile;
1519
            $pear_channel->setServer('pecl.php.net');
1520
            $pear_channel->setAlias('pecl');
1521
            $pear_channel->setSummary('PHP Extension Community Library');
1522
            $pear_channel->setDefaultPEARProtocols();
1523
            $pear_channel->setBaseURL('REST1.0', 'http://pecl.php.net/rest/');
1524
            $pear_channel->setBaseURL('REST1.1', 'http://pecl.php.net/rest/');
1525
            $pear_channel->setValidationPackage('PEAR_Validator_PECL', '1.0');
1526
            return $pear_channel;
1527
        }
1528
 
1529
        if ($this->_getChannelFromAlias($channel) == 'doc.php.net') {
1530
            // the registry is not properly set up, so use defaults
1531
            if (!class_exists('PEAR_ChannelFile')) {
1532
                require_once 'PEAR/ChannelFile.php';
1533
            }
1534
 
1535
            $doc_channel = new PEAR_ChannelFile;
1536
            $doc_channel->setServer('doc.php.net');
1537
            $doc_channel->setAlias('phpdocs');
1538
            $doc_channel->setSummary('PHP Documentation Team');
1539
            $doc_channel->setDefaultPEARProtocols();
1540
            $doc_channel->setBaseURL('REST1.0', 'http://doc.php.net/rest/');
1541
            $doc_channel->setBaseURL('REST1.1', 'http://doc.php.net/rest/');
1542
            $doc_channel->setBaseURL('REST1.3', 'http://doc.php.net/rest/');
1543
            return $doc_channel;
1544
        }
1545
 
1546
 
1547
        if ($this->_getChannelFromAlias($channel) == '__uri') {
1548
            // the registry is not properly set up, so use defaults
1549
            if (!class_exists('PEAR_ChannelFile')) {
1550
                require_once 'PEAR/ChannelFile.php';
1551
            }
1552
 
1553
            $private = new PEAR_ChannelFile;
1554
            $private->setName('__uri');
1555
            $private->setDefaultPEARProtocols();
1556
            $private->setBaseURL('REST1.0', '****');
1557
            $private->setSummary('Pseudo-channel for static packages');
1558
            return $private;
1559
        }
1560
 
1561
        return $ch;
1562
    }
1563
 
1564
    /**
1565
     * @param string Package name
1566
     * @param string Channel name
1567
     * @return bool
1568
     */
1569
    function packageExists($package, $channel = 'pear.php.net')
1570
    {
1571
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1572
            return $e;
1573
        }
1574
        $ret = $this->_packageExists($package, $channel);
1575
        $this->_unlock();
1576
        return $ret;
1577
    }
1578
 
1579
    // }}}
1580
 
1581
    // {{{ channelExists()
1582
 
1583
    /**
1584
     * @param string channel name
1585
     * @param bool if true, then aliases will be ignored
1586
     * @return bool
1587
     */
1588
    function channelExists($channel, $noaliases = false)
1589
    {
1590
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1591
            return $e;
1592
        }
1593
        $ret = $this->_channelExists($channel, $noaliases);
1594
        $this->_unlock();
1595
        return $ret;
1596
    }
1597
 
1598
    // }}}
1599
 
1600
    /**
1601
     * @param string channel name mirror is in
1602
     * @param string mirror name
1603
     *
1604
     * @return bool
1605
     */
1606
    function mirrorExists($channel, $mirror)
1607
    {
1608
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1609
            return $e;
1610
        }
1611
 
1612
        $ret = $this->_mirrorExists($channel, $mirror);
1613
        $this->_unlock();
1614
        return $ret;
1615
    }
1616
 
1617
    // {{{ isAlias()
1618
 
1619
    /**
1620
     * Determines whether the parameter is an alias of a channel
1621
     * @param string
1622
     * @return bool
1623
     */
1624
    function isAlias($alias)
1625
    {
1626
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1627
            return $e;
1628
        }
1629
        $ret = $this->_isChannelAlias($alias);
1630
        $this->_unlock();
1631
        return $ret;
1632
    }
1633
 
1634
    // }}}
1635
    // {{{ packageInfo()
1636
 
1637
    /**
1638
     * @param string|null
1639
     * @param string|null
1640
     * @param string
1641
     * @return array|null
1642
     */
1643
    function packageInfo($package = null, $key = null, $channel = 'pear.php.net')
1644
    {
1645
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1646
            return $e;
1647
        }
1648
        $ret = $this->_packageInfo($package, $key, $channel);
1649
        $this->_unlock();
1650
        return $ret;
1651
    }
1652
 
1653
    // }}}
1654
    // {{{ channelInfo()
1655
 
1656
    /**
1657
     * Retrieve a raw array of channel data.
1658
     *
1659
     * Do not use this, instead use {@link getChannel()} for normal
1660
     * operations.  Array structure is undefined in this method
1661
     * @param string channel name
1662
     * @param bool whether to strictly retrieve information only on non-aliases
1663
     * @return array|null|PEAR_Error
1664
     */
1665
    function channelInfo($channel = null, $noaliases = false)
1666
    {
1667
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1668
            return $e;
1669
        }
1670
        $ret = $this->_channelInfo($channel, $noaliases);
1671
        $this->_unlock();
1672
        return $ret;
1673
    }
1674
 
1675
    // }}}
1676
 
1677
    /**
1678
     * @param string
1679
     */
1680
    function channelName($channel)
1681
    {
1682
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1683
            return $e;
1684
        }
1685
        $ret = $this->_getChannelFromAlias($channel);
1686
        $this->_unlock();
1687
        return $ret;
1688
    }
1689
 
1690
    /**
1691
     * @param string
1692
     */
1693
    function channelAlias($channel)
1694
    {
1695
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1696
            return $e;
1697
        }
1698
        $ret = $this->_getAlias($channel);
1699
        $this->_unlock();
1700
        return $ret;
1701
    }
1702
    // {{{ listPackages()
1703
 
1704
    function listPackages($channel = false)
1705
    {
1706
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1707
            return $e;
1708
        }
1709
        $ret = $this->_listPackages($channel);
1710
        $this->_unlock();
1711
        return $ret;
1712
    }
1713
 
1714
    // }}}
1715
    // {{{ listAllPackages()
1716
 
1717
    function listAllPackages()
1718
    {
1719
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1720
            return $e;
1721
        }
1722
        $ret = $this->_listAllPackages();
1723
        $this->_unlock();
1724
        return $ret;
1725
    }
1726
 
1727
    // }}}
1728
    // {{{ listChannel()
1729
 
1730
    function listChannels()
1731
    {
1732
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1733
            return $e;
1734
        }
1735
        $ret = $this->_listChannels();
1736
        $this->_unlock();
1737
        return $ret;
1738
    }
1739
 
1740
    // }}}
1741
    // {{{ addPackage()
1742
 
1743
    /**
1744
     * Add an installed package to the registry
1745
     * @param string|PEAR_PackageFile_v1|PEAR_PackageFile_v2 package name or object
1746
     *               that will be passed to {@link addPackage2()}
1747
     * @param array package info (parsed by PEAR_Common::infoFrom*() methods)
1748
     * @return bool success of saving
1749
     */
1750
    function addPackage($package, $info)
1751
    {
1752
        if (is_object($info)) {
1753
            return $this->addPackage2($info);
1754
        }
1755
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1756
            return $e;
1757
        }
1758
        $ret = $this->_addPackage($package, $info);
1759
        $this->_unlock();
1760
        if ($ret) {
1761
            if (!class_exists('PEAR_PackageFile_v1')) {
1762
                require_once 'PEAR/PackageFile/v1.php';
1763
            }
1764
            $pf = new PEAR_PackageFile_v1;
1765
            $pf->setConfig($this->_config);
1766
            $pf->fromArray($info);
1767
            $this->_dependencyDB->uninstallPackage($pf);
1768
            $this->_dependencyDB->installPackage($pf);
1769
        }
1770
        return $ret;
1771
    }
1772
 
1773
    // }}}
1774
    // {{{ addPackage2()
1775
 
1776
    function addPackage2($info)
1777
    {
1778
        if (!is_object($info)) {
1779
            return $this->addPackage($info['package'], $info);
1780
        }
1781
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1782
            return $e;
1783
        }
1784
        $ret = $this->_addPackage2($info);
1785
        $this->_unlock();
1786
        if ($ret) {
1787
            $this->_dependencyDB->uninstallPackage($info);
1788
            $this->_dependencyDB->installPackage($info);
1789
        }
1790
        return $ret;
1791
    }
1792
 
1793
    // }}}
1794
    // {{{ updateChannel()
1795
 
1796
    /**
1797
     * For future expandibility purposes, separate this
1798
     * @param PEAR_ChannelFile
1799
     */
1800
    function updateChannel($channel, $lastmodified = null)
1801
    {
1802
        if ($channel->getName() == '__uri') {
1803
            return false;
1804
        }
1805
        return $this->addChannel($channel, $lastmodified, true);
1806
    }
1807
 
1808
    // }}}
1809
    // {{{ deleteChannel()
1810
 
1811
    /**
1812
     * Deletion fails if there are any packages installed from the channel
1813
     * @param string|PEAR_ChannelFile channel name
1814
     * @return boolean|PEAR_Error True on deletion, false if it doesn't exist
1815
     */
1816
    function deleteChannel($channel)
1817
    {
1818
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1819
            return $e;
1820
        }
1821
 
1822
        $ret = $this->_deleteChannel($channel);
1823
        $this->_unlock();
1824
        if ($ret && is_a($this->_config, 'PEAR_Config')) {
1825
            $this->_config->setChannels($this->listChannels());
1826
        }
1827
 
1828
        return $ret;
1829
    }
1830
 
1831
    // }}}
1832
    // {{{ addChannel()
1833
 
1834
    /**
1835
     * @param PEAR_ChannelFile Channel object
1836
     * @param string Last-Modified header from HTTP for caching
1837
     * @return boolean|PEAR_Error True on creation, false if it already exists
1838
     */
1839
    function addChannel($channel, $lastmodified = false, $update = false)
1840
    {
1841
        if (!is_a($channel, 'PEAR_ChannelFile') || !$channel->validate()) {
1842
            return false;
1843
        }
1844
 
1845
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1846
            return $e;
1847
        }
1848
 
1849
        $ret = $this->_addChannel($channel, $update, $lastmodified);
1850
        $this->_unlock();
1851
        if (!$update && $ret && is_a($this->_config, 'PEAR_Config')) {
1852
            $this->_config->setChannels($this->listChannels());
1853
        }
1854
 
1855
        return $ret;
1856
    }
1857
 
1858
    // }}}
1859
    // {{{ deletePackage()
1860
 
1861
    function deletePackage($package, $channel = 'pear.php.net')
1862
    {
1863
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1864
            return $e;
1865
        }
1866
 
1867
        $file = $this->_packageFileName($package, $channel);
1868
        $ret  = file_exists($file) ? @unlink($file) : false;
1869
        $this->_rebuildFileMap();
1870
        $this->_unlock();
1871
        $p = array('channel' => $channel, 'package' => $package);
1872
        $this->_dependencyDB->uninstallPackage($p);
1873
        return $ret;
1874
    }
1875
 
1876
    // }}}
1877
    // {{{ updatePackage()
1878
 
1879
    function updatePackage($package, $info, $merge = true)
1880
    {
1881
        if (is_object($info)) {
1882
            return $this->updatePackage2($info, $merge);
1883
        }
1884
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1885
            return $e;
1886
        }
1887
        $ret = $this->_updatePackage($package, $info, $merge);
1888
        $this->_unlock();
1889
        if ($ret) {
1890
            if (!class_exists('PEAR_PackageFile_v1')) {
1891
                require_once 'PEAR/PackageFile/v1.php';
1892
            }
1893
            $pf = new PEAR_PackageFile_v1;
1894
            $pf->setConfig($this->_config);
1895
            $pf->fromArray($this->packageInfo($package));
1896
            $this->_dependencyDB->uninstallPackage($pf);
1897
            $this->_dependencyDB->installPackage($pf);
1898
        }
1899
        return $ret;
1900
    }
1901
 
1902
    // }}}
1903
    // {{{ updatePackage2()
1904
 
1905
    function updatePackage2($info)
1906
    {
1907
 
1908
        if (!is_object($info)) {
1909
            return $this->updatePackage($info['package'], $info, $merge);
1910
        }
1911
 
1912
        if (!$info->validate(PEAR_VALIDATE_DOWNLOADING)) {
1913
            return false;
1914
        }
1915
 
1916
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
1917
            return $e;
1918
        }
1919
 
1920
        $ret = $this->_updatePackage2($info);
1921
        $this->_unlock();
1922
        if ($ret) {
1923
            $this->_dependencyDB->uninstallPackage($info);
1924
            $this->_dependencyDB->installPackage($info);
1925
        }
1926
 
1927
        return $ret;
1928
    }
1929
 
1930
    // }}}
1931
    // {{{ getChannel()
1932
    /**
1933
     * @param string channel name
1934
     * @param bool whether to strictly return raw channels (no aliases)
1935
     * @return PEAR_ChannelFile|PEAR_Error
1936
     */
1937
    function &getChannel($channel, $noaliases = false)
1938
    {
1939
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1940
            return $e;
1941
        }
1942
        $ret = &$this->_getChannel($channel, $noaliases);
1943
        $this->_unlock();
1944
        if (!$ret) {
1945
            return PEAR::raiseError('Unknown channel: ' . $channel);
1946
        }
1947
        return $ret;
1948
    }
1949
 
1950
    // }}}
1951
    // {{{ getPackage()
1952
    /**
1953
     * @param string package name
1954
     * @param string channel name
1955
     * @return PEAR_PackageFile_v1|PEAR_PackageFile_v2|null
1956
     */
1957
    function &getPackage($package, $channel = 'pear.php.net')
1958
    {
1959
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
1960
            return $e;
1961
        }
1962
        $pf = &$this->_getPackage($package, $channel);
1963
        $this->_unlock();
1964
        return $pf;
1965
    }
1966
 
1967
    // }}}
1968
 
1969
    /**
1970
     * Get PEAR_PackageFile_v[1/2] objects representing the contents of
1971
     * a dependency group that are installed.
1972
     *
1973
     * This is used at uninstall-time
1974
     * @param array
1975
     * @return array|false
1976
     */
1977
    function getInstalledGroup($group)
1978
    {
1979
        $ret = array();
1980
        if (isset($group['package'])) {
1981
            if (!isset($group['package'][0])) {
1982
                $group['package'] = array($group['package']);
1983
            }
1984
            foreach ($group['package'] as $package) {
1985
                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
1986
                $p = &$this->getPackage($package['name'], $depchannel);
1987
                if ($p) {
1988
                    $save = &$p;
1989
                    $ret[] = &$save;
1990
                }
1991
            }
1992
        }
1993
        if (isset($group['subpackage'])) {
1994
            if (!isset($group['subpackage'][0])) {
1995
                $group['subpackage'] = array($group['subpackage']);
1996
            }
1997
            foreach ($group['subpackage'] as $package) {
1998
                $depchannel = isset($package['channel']) ? $package['channel'] : '__uri';
1999
                $p = &$this->getPackage($package['name'], $depchannel);
2000
                if ($p) {
2001
                    $save = &$p;
2002
                    $ret[] = &$save;
2003
                }
2004
            }
2005
        }
2006
        if (!count($ret)) {
2007
            return false;
2008
        }
2009
        return $ret;
2010
    }
2011
 
2012
    // {{{ getChannelValidator()
2013
    /**
2014
     * @param string channel name
2015
     * @return PEAR_Validate|false
2016
     */
2017
    function &getChannelValidator($channel)
2018
    {
2019
        $chan = $this->getChannel($channel);
2020
        if (PEAR::isError($chan)) {
2021
            return $chan;
2022
        }
2023
        $val = $chan->getValidationObject();
2024
        return $val;
2025
    }
2026
    // }}}
2027
    // {{{ getChannels()
2028
    /**
2029
     * @param string channel name
2030
     * @return array an array of PEAR_ChannelFile objects representing every installed channel
2031
     */
2032
    function &getChannels()
2033
    {
2034
        $ret = array();
2035
        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
2036
            return $e;
2037
        }
2038
        foreach ($this->_listChannels() as $channel) {
2039
            $e = &$this->_getChannel($channel);
2040
            if (!$e || PEAR::isError($e)) {
2041
                continue;
2042
            }
2043
            $ret[] = $e;
2044
        }
2045
        $this->_unlock();
2046
        return $ret;
2047
    }
2048
 
2049
    // }}}
2050
    // {{{ checkFileMap()
2051
 
2052
    /**
2053
     * Test whether a file or set of files belongs to a package.
2054
     *
2055
     * If an array is passed in
2056
     * @param string|array file path, absolute or relative to the pear
2057
     *                     install dir
2058
     * @param string|array name of PEAR package or array('package' => name, 'channel' =>
2059
     *                     channel) of a package that will be ignored
2060
     * @param string API version - 1.1 will exclude any files belonging to a package
2061
     * @param array private recursion variable
2062
     * @return array|false which package and channel the file belongs to, or an empty
2063
     *                     string if the file does not belong to an installed package,
2064
     *                     or belongs to the second parameter's package
2065
     */
2066
    function checkFileMap($path, $package = false, $api = '1.0', $attrs = false)
2067
    {
2068
        if (is_array($path)) {
2069
            static $notempty;
2070
            if (empty($notempty)) {
2071
                if (!class_exists('PEAR_Installer_Role')) {
2072
                    require_once 'PEAR/Installer/Role.php';
2073
                }
2074
                $notempty = create_function('$a','return !empty($a);');
2075
            }
2076
            $package = is_array($package) ? array(strtolower($package[0]), strtolower($package[1]))
2077
                : strtolower($package);
2078
            $pkgs = array();
2079
            foreach ($path as $name => $attrs) {
2080
                if (is_array($attrs)) {
2081
                    if (isset($attrs['install-as'])) {
2082
                        $name = $attrs['install-as'];
2083
                    }
2084
                    if (!in_array($attrs['role'], PEAR_Installer_Role::getInstallableRoles())) {
2085
                        // these are not installed
2086
                        continue;
2087
                    }
2088
                    if (!in_array($attrs['role'], PEAR_Installer_Role::getBaseinstallRoles())) {
2089
                        $attrs['baseinstalldir'] = is_array($package) ? $package[1] : $package;
2090
                    }
2091
                    if (isset($attrs['baseinstalldir'])) {
2092
                        $name = $attrs['baseinstalldir'] . DIRECTORY_SEPARATOR . $name;
2093
                    }
2094
                }
2095
                $pkgs[$name] = $this->checkFileMap($name, $package, $api, $attrs);
2096
                if (PEAR::isError($pkgs[$name])) {
2097
                    return $pkgs[$name];
2098
                }
2099
            }
2100
            return array_filter($pkgs, $notempty);
2101
        }
2102
        if (empty($this->filemap_cache)) {
2103
            if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
2104
                return $e;
2105
            }
2106
            $err = $this->_readFileMap();
2107
            $this->_unlock();
2108
            if (PEAR::isError($err)) {
2109
                return $err;
2110
            }
2111
        }
2112
        if (!$attrs) {
2113
            $attrs = array('role' => 'php'); // any old call would be for PHP role only
2114
        }
2115
        if (isset($this->filemap_cache[$attrs['role']][$path])) {
2116
            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
2117
                return false;
2118
            }
2119
            return $this->filemap_cache[$attrs['role']][$path];
2120
        }
2121
        $l = strlen($this->install_dir);
2122
        if (substr($path, 0, $l) == $this->install_dir) {
2123
            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
2124
        }
2125
        if (isset($this->filemap_cache[$attrs['role']][$path])) {
2126
            if ($api >= '1.1' && $this->filemap_cache[$attrs['role']][$path] == $package) {
2127
                return false;
2128
            }
2129
            return $this->filemap_cache[$attrs['role']][$path];
2130
        }
2131
        return false;
2132
    }
2133
 
2134
    // }}}
2135
    // {{{ flush()
2136
    /**
2137
     * Force a reload of the filemap
2138
     * @since 1.5.0RC3
2139
     */
2140
    function flushFileMap()
2141
    {
2142
        $this->filemap_cache = null;
2143
        clearstatcache(); // ensure that the next read gets the full, current filemap
2144
    }
2145
 
2146
    // }}}
2147
    // {{{ apiVersion()
2148
    /**
2149
     * Get the expected API version.  Channels API is version 1.1, as it is backwards
2150
     * compatible with 1.0
2151
     * @return string
2152
     */
2153
    function apiVersion()
2154
    {
2155
        return '1.1';
2156
    }
2157
    // }}}
2158
 
2159
 
2160
    /**
2161
     * Parse a package name, or validate a parsed package name array
2162
     * @param string|array pass in an array of format
2163
     *                     array(
2164
     *                      'package' => 'pname',
2165
     *                     ['channel' => 'channame',]
2166
     *                     ['version' => 'version',]
2167
     *                     ['state' => 'state',]
2168
     *                     ['group' => 'groupname'])
2169
     *                     or a string of format
2170
     *                     [channel://][channame/]pname[-version|-state][/group=groupname]
2171
     * @return array|PEAR_Error
2172
     */
2173
    function parsePackageName($param, $defaultchannel = 'pear.php.net')
2174
    {
2175
        $saveparam = $param;
2176
        if (is_array($param)) {
2177
            // convert to string for error messages
2178
            $saveparam = $this->parsedPackageNameToString($param);
2179
            // process the array
2180
            if (!isset($param['package'])) {
2181
                return PEAR::raiseError('parsePackageName(): array $param ' .
2182
                    'must contain a valid package name in index "param"',
2183
                    'package', null, null, $param);
2184
            }
2185
            if (!isset($param['uri'])) {
2186
                if (!isset($param['channel'])) {
2187
                    $param['channel'] = $defaultchannel;
2188
                }
2189
            } else {
2190
                $param['channel'] = '__uri';
2191
            }
2192
        } else {
2193
            $components = @parse_url((string) $param);
2194
            if (isset($components['scheme'])) {
2195
                if ($components['scheme'] == 'http') {
2196
                    // uri package
2197
                    $param = array('uri' => $param, 'channel' => '__uri');
2198
                } elseif($components['scheme'] != 'channel') {
2199
                    return PEAR::raiseError('parsePackageName(): only channel:// uris may ' .
2200
                        'be downloaded, not "' . $param . '"', 'invalid', null, null, $param);
2201
                }
2202
            }
2203
            if (!isset($components['path'])) {
2204
                return PEAR::raiseError('parsePackageName(): array $param ' .
2205
                    'must contain a valid package name in "' . $param . '"',
2206
                    'package', null, null, $param);
2207
            }
2208
            if (isset($components['host'])) {
2209
                // remove the leading "/"
2210
                $components['path'] = substr($components['path'], 1);
2211
            }
2212
            if (!isset($components['scheme'])) {
2213
                if (strpos($components['path'], '/') !== false) {
2214
                    if ($components['path']{0} == '/') {
2215
                        return PEAR::raiseError('parsePackageName(): this is not ' .
2216
                            'a package name, it begins with "/" in "' . $param . '"',
2217
                            'invalid', null, null, $param);
2218
                    }
2219
                    $parts = explode('/', $components['path']);
2220
                    $components['host'] = array_shift($parts);
2221
                    if (count($parts) > 1) {
2222
                        $components['path'] = array_pop($parts);
2223
                        $components['host'] .= '/' . implode('/', $parts);
2224
                    } else {
2225
                        $components['path'] = implode('/', $parts);
2226
                    }
2227
                } else {
2228
                    $components['host'] = $defaultchannel;
2229
                }
2230
            } else {
2231
                if (strpos($components['path'], '/')) {
2232
                    $parts = explode('/', $components['path']);
2233
                    $components['path'] = array_pop($parts);
2234
                    $components['host'] .= '/' . implode('/', $parts);
2235
                }
2236
            }
2237
 
2238
            if (is_array($param)) {
2239
                $param['package'] = $components['path'];
2240
            } else {
2241
                $param = array(
2242
                    'package' => $components['path']
2243
                    );
2244
                if (isset($components['host'])) {
2245
                    $param['channel'] = $components['host'];
2246
                }
2247
            }
2248
            if (isset($components['fragment'])) {
2249
                $param['group'] = $components['fragment'];
2250
            }
2251
            if (isset($components['user'])) {
2252
                $param['user'] = $components['user'];
2253
            }
2254
            if (isset($components['pass'])) {
2255
                $param['pass'] = $components['pass'];
2256
            }
2257
            if (isset($components['query'])) {
2258
                parse_str($components['query'], $param['opts']);
2259
            }
2260
            // check for extension
2261
            $pathinfo = pathinfo($param['package']);
2262
            if (isset($pathinfo['extension']) &&
2263
                  in_array(strtolower($pathinfo['extension']), array('tgz', 'tar'))) {
2264
                $param['extension'] = $pathinfo['extension'];
2265
                $param['package'] = substr($pathinfo['basename'], 0,
2266
                    strlen($pathinfo['basename']) - 4);
2267
            }
2268
            // check for version
2269
            if (strpos($param['package'], '-')) {
2270
                $test = explode('-', $param['package']);
2271
                if (count($test) != 2) {
2272
                    return PEAR::raiseError('parsePackageName(): only one version/state ' .
2273
                        'delimiter "-" is allowed in "' . $saveparam . '"',
2274
                        'version', null, null, $param);
2275
                }
2276
                list($param['package'], $param['version']) = $test;
2277
            }
2278
        }
2279
        // validation
2280
        $info = $this->channelExists($param['channel']);
2281
        if (PEAR::isError($info)) {
2282
            return $info;
2283
        }
2284
        if (!$info) {
2285
            return PEAR::raiseError('unknown channel "' . $param['channel'] .
2286
                '" in "' . $saveparam . '"', 'channel', null, null, $param);
2287
        }
2288
        $chan = $this->getChannel($param['channel']);
2289
        if (PEAR::isError($chan)) {
2290
            return $chan;
2291
        }
2292
        if (!$chan) {
2293
            return PEAR::raiseError("Exception: corrupt registry, could not " .
2294
                "retrieve channel " . $param['channel'] . " information",
2295
                'registry', null, null, $param);
2296
        }
2297
        $param['channel'] = $chan->getName();
2298
        $validate = $chan->getValidationObject();
2299
        $vpackage = $chan->getValidationPackage();
2300
        // validate package name
2301
        if (!$validate->validPackageName($param['package'], $vpackage['_content'])) {
2302
            return PEAR::raiseError('parsePackageName(): invalid package name "' .
2303
                $param['package'] . '" in "' . $saveparam . '"',
2304
                'package', null, null, $param);
2305
        }
2306
        if (isset($param['group'])) {
2307
            if (!PEAR_Validate::validGroupName($param['group'])) {
2308
                return PEAR::raiseError('parsePackageName(): dependency group "' . $param['group'] .
2309
                    '" is not a valid group name in "' . $saveparam . '"', 'group', null, null,
2310
                    $param);
2311
            }
2312
        }
2313
        if (isset($param['state'])) {
2314
            if (!in_array(strtolower($param['state']), $validate->getValidStates())) {
2315
                return PEAR::raiseError('parsePackageName(): state "' . $param['state']
2316
                    . '" is not a valid state in "' . $saveparam . '"',
2317
                    'state', null, null, $param);
2318
            }
2319
        }
2320
        if (isset($param['version'])) {
2321
            if (isset($param['state'])) {
2322
                return PEAR::raiseError('parsePackageName(): cannot contain both ' .
2323
                    'a version and a stability (state) in "' . $saveparam . '"',
2324
                    'version/state', null, null, $param);
2325
            }
2326
            // check whether version is actually a state
2327
            if (in_array(strtolower($param['version']), $validate->getValidStates())) {
2328
                $param['state'] = strtolower($param['version']);
2329
                unset($param['version']);
2330
            } else {
2331
                if (!$validate->validVersion($param['version'])) {
2332
                    return PEAR::raiseError('parsePackageName(): "' . $param['version'] .
2333
                        '" is neither a valid version nor a valid state in "' .
2334
                        $saveparam . '"', 'version/state', null, null, $param);
2335
                }
2336
            }
2337
        }
2338
        return $param;
2339
    }
2340
 
2341
    /**
2342
     * @param array
2343
     * @return string
2344
     */
2345
    function parsedPackageNameToString($parsed, $brief = false)
2346
    {
2347
        if (is_string($parsed)) {
2348
            return $parsed;
2349
        }
2350
        if (is_object($parsed)) {
2351
            $p = $parsed;
2352
            $parsed = array(
2353
                'package' => $p->getPackage(),
2354
                'channel' => $p->getChannel(),
2355
                'version' => $p->getVersion(),
2356
            );
2357
        }
2358
        if (isset($parsed['uri'])) {
2359
            return $parsed['uri'];
2360
        }
2361
        if ($brief) {
2362
            if ($channel = $this->channelAlias($parsed['channel'])) {
2363
                return $channel . '/' . $parsed['package'];
2364
            }
2365
        }
2366
        $upass = '';
2367
        if (isset($parsed['user'])) {
2368
            $upass = $parsed['user'];
2369
            if (isset($parsed['pass'])) {
2370
                $upass .= ':' . $parsed['pass'];
2371
            }
2372
            $upass = "$upass@";
2373
        }
2374
        $ret = 'channel://' . $upass . $parsed['channel'] . '/' . $parsed['package'];
2375
        if (isset($parsed['version']) || isset($parsed['state'])) {
2376
            $ver = isset($parsed['version']) ? $parsed['version'] : '';
2377
            $ver .= isset($parsed['state']) ? $parsed['state'] : '';
2378
            $ret .= '-' . $ver;
2379
        }
2380
        if (isset($parsed['extension'])) {
2381
            $ret .= '.' . $parsed['extension'];
2382
        }
2383
        if (isset($parsed['opts'])) {
2384
            $ret .= '?';
2385
            foreach ($parsed['opts'] as $name => $value) {
2386
                $parsed['opts'][$name] = "$name=$value";
2387
            }
2388
            $ret .= implode('&', $parsed['opts']);
2389
        }
2390
        if (isset($parsed['group'])) {
2391
            $ret .= '#' . $parsed['group'];
2392
        }
2393
        return $ret;
2394
    }
2395
}