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_ChannelFile, the channel handling class
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * @category   pear
8
 * @package    PEAR
9
 * @author     Greg Beaver <cellog@php.net>
10
 * @copyright  1997-2009 The Authors
11
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12
 * @version    CVS: $Id: ChannelFile.php 313023 2011-07-06 19:17:11Z dufuz $
13
 * @link       http://pear.php.net/package/PEAR
14
 * @since      File available since Release 1.4.0a1
15
 */
16
 
17
/**
18
 * Needed for error handling
19
 */
20
require_once 'PEAR/ErrorStack.php';
21
require_once 'PEAR/XMLParser.php';
22
require_once 'PEAR/Common.php';
23
 
24
/**
25
 * Error code if the channel.xml <channel> tag does not contain a valid version
26
 */
27
define('PEAR_CHANNELFILE_ERROR_NO_VERSION', 1);
28
/**
29
 * Error code if the channel.xml <channel> tag version is not supported (version 1.0 is the only supported version,
30
 * currently
31
 */
32
define('PEAR_CHANNELFILE_ERROR_INVALID_VERSION', 2);
33
 
34
/**
35
 * Error code if parsing is attempted with no xml extension
36
 */
37
define('PEAR_CHANNELFILE_ERROR_NO_XML_EXT', 3);
38
 
39
/**
40
 * Error code if creating the xml parser resource fails
41
 */
42
define('PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER', 4);
43
 
44
/**
45
 * Error code used for all sax xml parsing errors
46
 */
47
define('PEAR_CHANNELFILE_ERROR_PARSER_ERROR', 5);
48
 
49
/**#@+
50
 * Validation errors
51
 */
52
/**
53
 * Error code when channel name is missing
54
 */
55
define('PEAR_CHANNELFILE_ERROR_NO_NAME', 6);
56
/**
57
 * Error code when channel name is invalid
58
 */
59
define('PEAR_CHANNELFILE_ERROR_INVALID_NAME', 7);
60
/**
61
 * Error code when channel summary is missing
62
 */
63
define('PEAR_CHANNELFILE_ERROR_NO_SUMMARY', 8);
64
/**
65
 * Error code when channel summary is multi-line
66
 */
67
define('PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY', 9);
68
/**
69
 * Error code when channel server is missing for protocol
70
 */
71
define('PEAR_CHANNELFILE_ERROR_NO_HOST', 10);
72
/**
73
 * Error code when channel server is invalid for protocol
74
 */
75
define('PEAR_CHANNELFILE_ERROR_INVALID_HOST', 11);
76
/**
77
 * Error code when a mirror name is invalid
78
 */
79
define('PEAR_CHANNELFILE_ERROR_INVALID_MIRROR', 21);
80
/**
81
 * Error code when a mirror type is invalid
82
 */
83
define('PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE', 22);
84
/**
85
 * Error code when an attempt is made to generate xml, but the parsed content is invalid
86
 */
87
define('PEAR_CHANNELFILE_ERROR_INVALID', 23);
88
/**
89
 * Error code when an empty package name validate regex is passed in
90
 */
91
define('PEAR_CHANNELFILE_ERROR_EMPTY_REGEX', 24);
92
/**
93
 * Error code when a <function> tag has no version
94
 */
95
define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION', 25);
96
/**
97
 * Error code when a <function> tag has no name
98
 */
99
define('PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME', 26);
100
/**
101
 * Error code when a <validatepackage> tag has no name
102
 */
103
define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME', 27);
104
/**
105
 * Error code when a <validatepackage> tag has no version attribute
106
 */
107
define('PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION', 28);
108
/**
109
 * Error code when a mirror does not exist but is called for in one of the set*
110
 * methods.
111
 */
112
define('PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND', 32);
113
/**
114
 * Error code when a server port is not numeric
115
 */
116
define('PEAR_CHANNELFILE_ERROR_INVALID_PORT', 33);
117
/**
118
 * Error code when <static> contains no version attribute
119
 */
120
define('PEAR_CHANNELFILE_ERROR_NO_STATICVERSION', 34);
121
/**
122
 * Error code when <baseurl> contains no type attribute in a <rest> protocol definition
123
 */
124
define('PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE', 35);
125
/**
126
 * Error code when a mirror is defined and the channel.xml represents the __uri pseudo-channel
127
 */
128
define('PEAR_CHANNELFILE_URI_CANT_MIRROR', 36);
129
/**
130
 * Error code when ssl attribute is present and is not "yes"
131
 */
132
define('PEAR_CHANNELFILE_ERROR_INVALID_SSL', 37);
133
/**#@-*/
134
 
135
/**
136
 * Mirror types allowed.  Currently only internet servers are recognized.
137
 */
138
$GLOBALS['_PEAR_CHANNELS_MIRROR_TYPES'] =  array('server');
139
 
140
 
141
/**
142
 * The Channel handling class
143
 *
144
 * @category   pear
145
 * @package    PEAR
146
 * @author     Greg Beaver <cellog@php.net>
147
 * @copyright  1997-2009 The Authors
148
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
149
 * @version    Release: 1.9.4
150
 * @link       http://pear.php.net/package/PEAR
151
 * @since      Class available since Release 1.4.0a1
152
 */
153
class PEAR_ChannelFile
154
{
155
    /**
156
     * @access private
157
     * @var PEAR_ErrorStack
158
     * @access private
159
     */
160
    var $_stack;
161
 
162
    /**
163
     * Supported channel.xml versions, for parsing
164
     * @var array
165
     * @access private
166
     */
167
    var $_supportedVersions = array('1.0');
168
 
169
    /**
170
     * Parsed channel information
171
     * @var array
172
     * @access private
173
     */
174
    var $_channelInfo;
175
 
176
    /**
177
     * index into the subchannels array, used for parsing xml
178
     * @var int
179
     * @access private
180
     */
181
    var $_subchannelIndex;
182
 
183
    /**
184
     * index into the mirrors array, used for parsing xml
185
     * @var int
186
     * @access private
187
     */
188
    var $_mirrorIndex;
189
 
190
    /**
191
     * Flag used to determine the validity of parsed content
192
     * @var boolean
193
     * @access private
194
     */
195
    var $_isValid = false;
196
 
197
    function PEAR_ChannelFile()
198
    {
199
        $this->_stack = &new PEAR_ErrorStack('PEAR_ChannelFile');
200
        $this->_stack->setErrorMessageTemplate($this->_getErrorMessage());
201
        $this->_isValid = false;
202
    }
203
 
204
    /**
205
     * @return array
206
     * @access protected
207
     */
208
    function _getErrorMessage()
209
    {
210
        return
211
            array(
212
                PEAR_CHANNELFILE_ERROR_INVALID_VERSION =>
213
                    'While parsing channel.xml, an invalid version number "%version% was passed in, expecting one of %versions%',
214
                PEAR_CHANNELFILE_ERROR_NO_VERSION =>
215
                    'No version number found in <channel> tag',
216
                PEAR_CHANNELFILE_ERROR_NO_XML_EXT =>
217
                    '%error%',
218
                PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER =>
219
                    'Unable to create XML parser',
220
                PEAR_CHANNELFILE_ERROR_PARSER_ERROR =>
221
                    '%error%',
222
                PEAR_CHANNELFILE_ERROR_NO_NAME =>
223
                    'Missing channel name',
224
                PEAR_CHANNELFILE_ERROR_INVALID_NAME =>
225
                    'Invalid channel %tag% "%name%"',
226
                PEAR_CHANNELFILE_ERROR_NO_SUMMARY =>
227
                    'Missing channel summary',
228
                PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY =>
229
                    'Channel summary should be on one line, but is multi-line',
230
                PEAR_CHANNELFILE_ERROR_NO_HOST =>
231
                    'Missing channel server for %type% server',
232
                PEAR_CHANNELFILE_ERROR_INVALID_HOST =>
233
                    'Server name "%server%" is invalid for %type% server',
234
                PEAR_CHANNELFILE_ERROR_INVALID_MIRROR =>
235
                    'Invalid mirror name "%name%", mirror type %type%',
236
                PEAR_CHANNELFILE_ERROR_INVALID_MIRRORTYPE =>
237
                    'Invalid mirror type "%type%"',
238
                PEAR_CHANNELFILE_ERROR_INVALID =>
239
                    'Cannot generate xml, contents are invalid',
240
                PEAR_CHANNELFILE_ERROR_EMPTY_REGEX =>
241
                    'packagenameregex cannot be empty',
242
                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION =>
243
                    '%parent% %protocol% function has no version',
244
                PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME =>
245
                    '%parent% %protocol% function has no name',
246
                PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE =>
247
                    '%parent% rest baseurl has no type',
248
                PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME =>
249
                    'Validation package has no name in <validatepackage> tag',
250
                PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION =>
251
                    'Validation package "%package%" has no version',
252
                PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND =>
253
                    'Mirror "%mirror%" does not exist',
254
                PEAR_CHANNELFILE_ERROR_INVALID_PORT =>
255
                    'Port "%port%" must be numeric',
256
                PEAR_CHANNELFILE_ERROR_NO_STATICVERSION =>
257
                    '<static> tag must contain version attribute',
258
                PEAR_CHANNELFILE_URI_CANT_MIRROR =>
259
                    'The __uri pseudo-channel cannot have mirrors',
260
                PEAR_CHANNELFILE_ERROR_INVALID_SSL =>
261
                    '%server% has invalid ssl attribute "%ssl%" can only be yes or not present',
262
            );
263
    }
264
 
265
    /**
266
     * @param string contents of package.xml file
267
     * @return bool success of parsing
268
     */
269
    function fromXmlString($data)
270
    {
271
        if (preg_match('/<channel\s+version="([0-9]+\.[0-9]+)"/', $data, $channelversion)) {
272
            if (!in_array($channelversion[1], $this->_supportedVersions)) {
273
                $this->_stack->push(PEAR_CHANNELFILE_ERROR_INVALID_VERSION, 'error',
274
                    array('version' => $channelversion[1]));
275
                return false;
276
            }
277
            $parser = new PEAR_XMLParser;
278
            $result = $parser->parse($data);
279
            if ($result !== true) {
280
                if ($result->getCode() == 1) {
281
                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_XML_EXT, 'error',
282
                        array('error' => $result->getMessage()));
283
                } else {
284
                    $this->_stack->push(PEAR_CHANNELFILE_ERROR_CANT_MAKE_PARSER, 'error');
285
                }
286
                return false;
287
            }
288
            $this->_channelInfo = $parser->getData();
289
            return true;
290
        } else {
291
            $this->_stack->push(PEAR_CHANNELFILE_ERROR_NO_VERSION, 'error', array('xml' => $data));
292
            return false;
293
        }
294
    }
295
 
296
    /**
297
     * @return array
298
     */
299
    function toArray()
300
    {
301
        if (!$this->_isValid && !$this->validate()) {
302
            return false;
303
        }
304
        return $this->_channelInfo;
305
    }
306
 
307
    /**
308
     * @param array
309
     * @static
310
     * @return PEAR_ChannelFile|false false if invalid
311
     */
312
    function &fromArray($data, $compatibility = false, $stackClass = 'PEAR_ErrorStack')
313
    {
314
        $a = new PEAR_ChannelFile($compatibility, $stackClass);
315
        $a->_fromArray($data);
316
        if (!$a->validate()) {
317
            $a = false;
318
            return $a;
319
        }
320
        return $a;
321
    }
322
 
323
    /**
324
     * Unlike {@link fromArray()} this does not do any validation
325
     * @param array
326
     * @static
327
     * @return PEAR_ChannelFile
328
     */
329
    function &fromArrayWithErrors($data, $compatibility = false,
330
                                  $stackClass = 'PEAR_ErrorStack')
331
    {
332
        $a = new PEAR_ChannelFile($compatibility, $stackClass);
333
        $a->_fromArray($data);
334
        return $a;
335
    }
336
 
337
    /**
338
     * @param array
339
     * @access private
340
     */
341
    function _fromArray($data)
342
    {
343
        $this->_channelInfo = $data;
344
    }
345
 
346
    /**
347
     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
348
     * @param boolean determines whether to purge the error stack after retrieving
349
     * @return array
350
     */
351
    function getErrors($purge = false)
352
    {
353
        return $this->_stack->getErrors($purge);
354
    }
355
 
356
    /**
357
     * Unindent given string (?)
358
     *
359
     * @param string $str The string that has to be unindented.
360
     * @return string
361
     * @access private
362
     */
363
    function _unIndent($str)
364
    {
365
        // remove leading newlines
366
        $str = preg_replace('/^[\r\n]+/', '', $str);
367
        // find whitespace at the beginning of the first line
368
        $indent_len = strspn($str, " \t");
369
        $indent = substr($str, 0, $indent_len);
370
        $data = '';
371
        // remove the same amount of whitespace from following lines
372
        foreach (explode("\n", $str) as $line) {
373
            if (substr($line, 0, $indent_len) == $indent) {
374
                $data .= substr($line, $indent_len) . "\n";
375
            }
376
        }
377
        return $data;
378
    }
379
 
380
    /**
381
     * Parse a channel.xml file.  Expects the name of
382
     * a channel xml file as input.
383
     *
384
     * @param string  $descfile  name of channel xml file
385
     * @return bool success of parsing
386
     */
387
    function fromXmlFile($descfile)
388
    {
389
        if (!file_exists($descfile) || !is_file($descfile) || !is_readable($descfile) ||
390
             (!$fp = fopen($descfile, 'r'))) {
391
            require_once 'PEAR.php';
392
            return PEAR::raiseError("Unable to open $descfile");
393
        }
394
 
395
        // read the whole thing so we only get one cdata callback
396
        // for each block of cdata
397
        fclose($fp);
398
        $data = file_get_contents($descfile);
399
        return $this->fromXmlString($data);
400
    }
401
 
402
    /**
403
     * Parse channel information from different sources
404
     *
405
     * This method is able to extract information about a channel
406
     * from an .xml file or a string
407
     *
408
     * @access public
409
     * @param  string Filename of the source or the source itself
410
     * @return bool
411
     */
412
    function fromAny($info)
413
    {
414
        if (is_string($info) && file_exists($info) && strlen($info) < 255) {
415
            $tmp = substr($info, -4);
416
            if ($tmp == '.xml') {
417
                $info = $this->fromXmlFile($info);
418
            } else {
419
                $fp = fopen($info, "r");
420
                $test = fread($fp, 5);
421
                fclose($fp);
422
                if ($test == "<?xml") {
423
                    $info = $this->fromXmlFile($info);
424
                }
425
            }
426
            if (PEAR::isError($info)) {
427
                require_once 'PEAR.php';
428
                return PEAR::raiseError($info);
429
            }
430
        }
431
        if (is_string($info)) {
432
            $info = $this->fromXmlString($info);
433
        }
434
        return $info;
435
    }
436
 
437
    /**
438
     * Return an XML document based on previous parsing and modifications
439
     *
440
     * @return string XML data
441
     *
442
     * @access public
443
     */
444
    function toXml()
445
    {
446
        if (!$this->_isValid && !$this->validate()) {
447
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID);
448
            return false;
449
        }
450
        if (!isset($this->_channelInfo['attribs']['version'])) {
451
            $this->_channelInfo['attribs']['version'] = '1.0';
452
        }
453
        $channelInfo = $this->_channelInfo;
454
        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
455
        $ret .= "<channel version=\"" .
456
            $channelInfo['attribs']['version'] . "\" xmlns=\"http://pear.php.net/channel-1.0\"
457
  xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"
458
  xsi:schemaLocation=\"http://pear.php.net/dtd/channel-"
459
            . $channelInfo['attribs']['version'] . " http://pear.php.net/dtd/channel-" .
460
            $channelInfo['attribs']['version'] . ".xsd\">
461
 <name>$channelInfo[name]</name>
462
 <summary>" . htmlspecialchars($channelInfo['summary'])."</summary>
463
";
464
        if (isset($channelInfo['suggestedalias'])) {
465
            $ret .= ' <suggestedalias>' . $channelInfo['suggestedalias'] . "</suggestedalias>\n";
466
        }
467
        if (isset($channelInfo['validatepackage'])) {
468
            $ret .= ' <validatepackage version="' .
469
                $channelInfo['validatepackage']['attribs']['version']. '">' .
470
                htmlspecialchars($channelInfo['validatepackage']['_content']) .
471
                "</validatepackage>\n";
472
        }
473
        $ret .= " <servers>\n";
474
        $ret .= '  <primary';
475
        if (isset($channelInfo['servers']['primary']['attribs']['ssl'])) {
476
            $ret .= ' ssl="' . $channelInfo['servers']['primary']['attribs']['ssl'] . '"';
477
        }
478
        if (isset($channelInfo['servers']['primary']['attribs']['port'])) {
479
            $ret .= ' port="' . $channelInfo['servers']['primary']['attribs']['port'] . '"';
480
        }
481
        $ret .= ">\n";
482
        if (isset($channelInfo['servers']['primary']['rest'])) {
483
            $ret .= $this->_makeRestXml($channelInfo['servers']['primary']['rest'], '   ');
484
        }
485
        $ret .= "  </primary>\n";
486
        if (isset($channelInfo['servers']['mirror'])) {
487
            $ret .= $this->_makeMirrorsXml($channelInfo);
488
        }
489
        $ret .= " </servers>\n";
490
        $ret .= "</channel>";
491
        return str_replace("\r", "\n", str_replace("\r\n", "\n", $ret));
492
    }
493
 
494
    /**
495
     * Generate the <rest> tag
496
     * @access private
497
     */
498
    function _makeRestXml($info, $indent)
499
    {
500
        $ret = $indent . "<rest>\n";
501
        if (isset($info['baseurl']) && !isset($info['baseurl'][0])) {
502
            $info['baseurl'] = array($info['baseurl']);
503
        }
504
 
505
        if (isset($info['baseurl'])) {
506
            foreach ($info['baseurl'] as $url) {
507
                $ret .= "$indent <baseurl type=\"" . $url['attribs']['type'] . "\"";
508
                $ret .= ">" . $url['_content'] . "</baseurl>\n";
509
            }
510
        }
511
        $ret .= $indent . "</rest>\n";
512
        return $ret;
513
    }
514
 
515
    /**
516
     * Generate the <mirrors> tag
517
     * @access private
518
     */
519
    function _makeMirrorsXml($channelInfo)
520
    {
521
        $ret = "";
522
        if (!isset($channelInfo['servers']['mirror'][0])) {
523
            $channelInfo['servers']['mirror'] = array($channelInfo['servers']['mirror']);
524
        }
525
        foreach ($channelInfo['servers']['mirror'] as $mirror) {
526
            $ret .= '  <mirror host="' . $mirror['attribs']['host'] . '"';
527
            if (isset($mirror['attribs']['port'])) {
528
                $ret .= ' port="' . $mirror['attribs']['port'] . '"';
529
            }
530
            if (isset($mirror['attribs']['ssl'])) {
531
                $ret .= ' ssl="' . $mirror['attribs']['ssl'] . '"';
532
            }
533
            $ret .= ">\n";
534
            if (isset($mirror['rest'])) {
535
                if (isset($mirror['rest'])) {
536
                    $ret .= $this->_makeRestXml($mirror['rest'], '   ');
537
                }
538
                $ret .= "  </mirror>\n";
539
            } else {
540
                $ret .= "/>\n";
541
            }
542
        }
543
        return $ret;
544
    }
545
 
546
    /**
547
     * Generate the <functions> tag
548
     * @access private
549
     */
550
    function _makeFunctionsXml($functions, $indent, $rest = false)
551
    {
552
        $ret = '';
553
        if (!isset($functions[0])) {
554
            $functions = array($functions);
555
        }
556
        foreach ($functions as $function) {
557
            $ret .= "$indent<function version=\"" . $function['attribs']['version'] . "\"";
558
            if ($rest) {
559
                $ret .= ' uri="' . $function['attribs']['uri'] . '"';
560
            }
561
            $ret .= ">" . $function['_content'] . "</function>\n";
562
        }
563
        return $ret;
564
    }
565
 
566
    /**
567
     * Validation error.  Also marks the object contents as invalid
568
     * @param error code
569
     * @param array error information
570
     * @access private
571
     */
572
    function _validateError($code, $params = array())
573
    {
574
        $this->_stack->push($code, 'error', $params);
575
        $this->_isValid = false;
576
    }
577
 
578
    /**
579
     * Validation warning.  Does not mark the object contents invalid.
580
     * @param error code
581
     * @param array error information
582
     * @access private
583
     */
584
    function _validateWarning($code, $params = array())
585
    {
586
        $this->_stack->push($code, 'warning', $params);
587
    }
588
 
589
    /**
590
     * Validate parsed file.
591
     *
592
     * @access public
593
     * @return boolean
594
     */
595
    function validate()
596
    {
597
        $this->_isValid = true;
598
        $info = $this->_channelInfo;
599
        if (empty($info['name'])) {
600
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_NAME);
601
        } elseif (!$this->validChannelServer($info['name'])) {
602
            if ($info['name'] != '__uri') {
603
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME, array('tag' => 'name',
604
                    'name' => $info['name']));
605
            }
606
        }
607
        if (empty($info['summary'])) {
608
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
609
        } elseif (strpos(trim($info['summary']), "\n") !== false) {
610
            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
611
                array('summary' => $info['summary']));
612
        }
613
        if (isset($info['suggestedalias'])) {
614
            if (!$this->validChannelServer($info['suggestedalias'])) {
615
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
616
                    array('tag' => 'suggestedalias', 'name' =>$info['suggestedalias']));
617
            }
618
        }
619
        if (isset($info['localalias'])) {
620
            if (!$this->validChannelServer($info['localalias'])) {
621
                $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
622
                    array('tag' => 'localalias', 'name' =>$info['localalias']));
623
            }
624
        }
625
        if (isset($info['validatepackage'])) {
626
            if (!isset($info['validatepackage']['_content'])) {
627
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_NAME);
628
            }
629
            if (!isset($info['validatepackage']['attribs']['version'])) {
630
                $content = isset($info['validatepackage']['_content']) ?
631
                    $info['validatepackage']['_content'] :
632
                    null;
633
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NOVALIDATE_VERSION,
634
                    array('package' => $content));
635
            }
636
        }
637
 
638
        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['port']) &&
639
              !is_numeric($info['servers']['primary']['attribs']['port'])) {
640
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_PORT,
641
                array('port' => $info['servers']['primary']['attribs']['port']));
642
        }
643
 
644
        if (isset($info['servers']['primary']['attribs'], $info['servers']['primary']['attribs']['ssl']) &&
645
              $info['servers']['primary']['attribs']['ssl'] != 'yes') {
646
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
647
                array('ssl' => $info['servers']['primary']['attribs']['ssl'],
648
                    'server' => $info['name']));
649
        }
650
 
651
        if (isset($info['servers']['primary']['rest']) &&
652
              isset($info['servers']['primary']['rest']['baseurl'])) {
653
            $this->_validateFunctions('rest', $info['servers']['primary']['rest']['baseurl']);
654
        }
655
        if (isset($info['servers']['mirror'])) {
656
            if ($this->_channelInfo['name'] == '__uri') {
657
                $this->_validateError(PEAR_CHANNELFILE_URI_CANT_MIRROR);
658
            }
659
            if (!isset($info['servers']['mirror'][0])) {
660
                $info['servers']['mirror'] = array($info['servers']['mirror']);
661
            }
662
            foreach ($info['servers']['mirror'] as $mirror) {
663
                if (!isset($mirror['attribs']['host'])) {
664
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_HOST,
665
                      array('type' => 'mirror'));
666
                } elseif (!$this->validChannelServer($mirror['attribs']['host'])) {
667
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_HOST,
668
                        array('server' => $mirror['attribs']['host'], 'type' => 'mirror'));
669
                }
670
                if (isset($mirror['attribs']['ssl']) && $mirror['attribs']['ssl'] != 'yes') {
671
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_SSL,
672
                        array('ssl' => $info['ssl'], 'server' => $mirror['attribs']['host']));
673
                }
674
                if (isset($mirror['rest'])) {
675
                    $this->_validateFunctions('rest', $mirror['rest']['baseurl'],
676
                        $mirror['attribs']['host']);
677
                }
678
            }
679
        }
680
        return $this->_isValid;
681
    }
682
 
683
    /**
684
     * @param string  rest - protocol name this function applies to
685
     * @param array the functions
686
     * @param string the name of the parent element (mirror name, for instance)
687
     */
688
    function _validateFunctions($protocol, $functions, $parent = '')
689
    {
690
        if (!isset($functions[0])) {
691
            $functions = array($functions);
692
        }
693
 
694
        foreach ($functions as $function) {
695
            if (!isset($function['_content']) || empty($function['_content'])) {
696
                $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONNAME,
697
                    array('parent' => $parent, 'protocol' => $protocol));
698
            }
699
 
700
            if ($protocol == 'rest') {
701
                if (!isset($function['attribs']['type']) ||
702
                      empty($function['attribs']['type'])) {
703
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NOBASEURLTYPE,
704
                        array('parent' => $parent, 'protocol' => $protocol));
705
                }
706
            } else {
707
                if (!isset($function['attribs']['version']) ||
708
                      empty($function['attribs']['version'])) {
709
                    $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_FUNCTIONVERSION,
710
                        array('parent' => $parent, 'protocol' => $protocol));
711
                }
712
            }
713
        }
714
    }
715
 
716
    /**
717
     * Test whether a string contains a valid channel server.
718
     * @param string $ver the package version to test
719
     * @return bool
720
     */
721
    function validChannelServer($server)
722
    {
723
        if ($server == '__uri') {
724
            return true;
725
        }
726
        return (bool) preg_match(PEAR_CHANNELS_SERVER_PREG, $server);
727
    }
728
 
729
    /**
730
     * @return string|false
731
     */
732
    function getName()
733
    {
734
        if (isset($this->_channelInfo['name'])) {
735
            return $this->_channelInfo['name'];
736
        }
737
 
738
        return false;
739
    }
740
 
741
    /**
742
     * @return string|false
743
     */
744
    function getServer()
745
    {
746
        if (isset($this->_channelInfo['name'])) {
747
            return $this->_channelInfo['name'];
748
        }
749
 
750
        return false;
751
    }
752
 
753
    /**
754
     * @return int|80 port number to connect to
755
     */
756
    function getPort($mirror = false)
757
    {
758
        if ($mirror) {
759
            if ($mir = $this->getMirror($mirror)) {
760
                if (isset($mir['attribs']['port'])) {
761
                    return $mir['attribs']['port'];
762
                }
763
 
764
                if ($this->getSSL($mirror)) {
765
                    return 443;
766
                }
767
 
768
                return 80;
769
            }
770
 
771
            return false;
772
        }
773
 
774
        if (isset($this->_channelInfo['servers']['primary']['attribs']['port'])) {
775
            return $this->_channelInfo['servers']['primary']['attribs']['port'];
776
        }
777
 
778
        if ($this->getSSL()) {
779
            return 443;
780
        }
781
 
782
        return 80;
783
    }
784
 
785
    /**
786
     * @return bool Determines whether secure sockets layer (SSL) is used to connect to this channel
787
     */
788
    function getSSL($mirror = false)
789
    {
790
        if ($mirror) {
791
            if ($mir = $this->getMirror($mirror)) {
792
                if (isset($mir['attribs']['ssl'])) {
793
                    return true;
794
                }
795
 
796
                return false;
797
            }
798
 
799
            return false;
800
        }
801
 
802
        if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
803
            return true;
804
        }
805
 
806
        return false;
807
    }
808
 
809
    /**
810
     * @return string|false
811
     */
812
    function getSummary()
813
    {
814
        if (isset($this->_channelInfo['summary'])) {
815
            return $this->_channelInfo['summary'];
816
        }
817
 
818
        return false;
819
    }
820
 
821
    /**
822
     * @param string protocol type
823
     * @param string Mirror name
824
     * @return array|false
825
     */
826
    function getFunctions($protocol, $mirror = false)
827
    {
828
        if ($this->getName() == '__uri') {
829
            return false;
830
        }
831
 
832
        $function = $protocol == 'rest' ? 'baseurl' : 'function';
833
        if ($mirror) {
834
            if ($mir = $this->getMirror($mirror)) {
835
                if (isset($mir[$protocol][$function])) {
836
                    return $mir[$protocol][$function];
837
                }
838
            }
839
 
840
            return false;
841
        }
842
 
843
        if (isset($this->_channelInfo['servers']['primary'][$protocol][$function])) {
844
            return $this->_channelInfo['servers']['primary'][$protocol][$function];
845
        }
846
 
847
        return false;
848
    }
849
 
850
    /**
851
     * @param string Protocol type
852
     * @param string Function name (null to return the
853
     *               first protocol of the type requested)
854
     * @param string Mirror name, if any
855
     * @return array
856
     */
857
     function getFunction($type, $name = null, $mirror = false)
858
     {
859
        $protocols = $this->getFunctions($type, $mirror);
860
        if (!$protocols) {
861
            return false;
862
        }
863
 
864
        foreach ($protocols as $protocol) {
865
            if ($name === null) {
866
                return $protocol;
867
            }
868
 
869
            if ($protocol['_content'] != $name) {
870
                continue;
871
            }
872
 
873
            return $protocol;
874
        }
875
 
876
        return false;
877
     }
878
 
879
    /**
880
     * @param string protocol type
881
     * @param string protocol name
882
     * @param string version
883
     * @param string mirror name
884
     * @return boolean
885
     */
886
    function supports($type, $name = null, $mirror = false, $version = '1.0')
887
    {
888
        $protocols = $this->getFunctions($type, $mirror);
889
        if (!$protocols) {
890
            return false;
891
        }
892
 
893
        foreach ($protocols as $protocol) {
894
            if ($protocol['attribs']['version'] != $version) {
895
                continue;
896
            }
897
 
898
            if ($name === null) {
899
                return true;
900
            }
901
 
902
            if ($protocol['_content'] != $name) {
903
                continue;
904
            }
905
 
906
            return true;
907
        }
908
 
909
        return false;
910
    }
911
 
912
    /**
913
     * Determines whether a channel supports Representational State Transfer (REST) protocols
914
     * for retrieving channel information
915
     * @param string
916
     * @return bool
917
     */
918
    function supportsREST($mirror = false)
919
    {
920
        if ($mirror == $this->_channelInfo['name']) {
921
            $mirror = false;
922
        }
923
 
924
        if ($mirror) {
925
            if ($mir = $this->getMirror($mirror)) {
926
                return isset($mir['rest']);
927
            }
928
 
929
            return false;
930
        }
931
 
932
        return isset($this->_channelInfo['servers']['primary']['rest']);
933
    }
934
 
935
    /**
936
     * Get the URL to access a base resource.
937
     *
938
     * Hyperlinks in the returned xml will be used to retrieve the proper information
939
     * needed.  This allows extreme extensibility and flexibility in implementation
940
     * @param string Resource Type to retrieve
941
     */
942
    function getBaseURL($resourceType, $mirror = false)
943
    {
944
        if ($mirror == $this->_channelInfo['name']) {
945
            $mirror = false;
946
        }
947
 
948
        if ($mirror) {
949
            $mir = $this->getMirror($mirror);
950
            if (!$mir) {
951
                return false;
952
            }
953
 
954
            $rest = $mir['rest'];
955
        } else {
956
            $rest = $this->_channelInfo['servers']['primary']['rest'];
957
        }
958
 
959
        if (!isset($rest['baseurl'][0])) {
960
            $rest['baseurl'] = array($rest['baseurl']);
961
        }
962
 
963
        foreach ($rest['baseurl'] as $baseurl) {
964
            if (strtolower($baseurl['attribs']['type']) == strtolower($resourceType)) {
965
                return $baseurl['_content'];
966
            }
967
        }
968
 
969
        return false;
970
    }
971
 
972
    /**
973
     * Since REST does not implement RPC, provide this as a logical wrapper around
974
     * resetFunctions for REST
975
     * @param string|false mirror name, if any
976
     */
977
    function resetREST($mirror = false)
978
    {
979
        return $this->resetFunctions('rest', $mirror);
980
    }
981
 
982
    /**
983
     * Empty all protocol definitions
984
     * @param string protocol type
985
     * @param string|false mirror name, if any
986
     */
987
    function resetFunctions($type, $mirror = false)
988
    {
989
        if ($mirror) {
990
            if (isset($this->_channelInfo['servers']['mirror'])) {
991
                $mirrors = $this->_channelInfo['servers']['mirror'];
992
                if (!isset($mirrors[0])) {
993
                    $mirrors = array($mirrors);
994
                }
995
 
996
                foreach ($mirrors as $i => $mir) {
997
                    if ($mir['attribs']['host'] == $mirror) {
998
                        if (isset($this->_channelInfo['servers']['mirror'][$i][$type])) {
999
                            unset($this->_channelInfo['servers']['mirror'][$i][$type]);
1000
                        }
1001
 
1002
                        return true;
1003
                    }
1004
                }
1005
 
1006
                return false;
1007
            }
1008
 
1009
            return false;
1010
        }
1011
 
1012
        if (isset($this->_channelInfo['servers']['primary'][$type])) {
1013
            unset($this->_channelInfo['servers']['primary'][$type]);
1014
        }
1015
 
1016
        return true;
1017
    }
1018
 
1019
    /**
1020
     * Set a channel's protocols to the protocols supported by pearweb
1021
     */
1022
    function setDefaultPEARProtocols($version = '1.0', $mirror = false)
1023
    {
1024
        switch ($version) {
1025
            case '1.0' :
1026
                $this->resetREST($mirror);
1027
 
1028
                if (!isset($this->_channelInfo['servers'])) {
1029
                    $this->_channelInfo['servers'] = array('primary' =>
1030
                        array('rest' => array()));
1031
                } elseif (!isset($this->_channelInfo['servers']['primary'])) {
1032
                    $this->_channelInfo['servers']['primary'] = array('rest' => array());
1033
                }
1034
 
1035
                return true;
1036
            break;
1037
            default :
1038
                return false;
1039
            break;
1040
        }
1041
    }
1042
 
1043
    /**
1044
     * @return array
1045
     */
1046
    function getMirrors()
1047
    {
1048
        if (isset($this->_channelInfo['servers']['mirror'])) {
1049
            $mirrors = $this->_channelInfo['servers']['mirror'];
1050
            if (!isset($mirrors[0])) {
1051
                $mirrors = array($mirrors);
1052
            }
1053
 
1054
            return $mirrors;
1055
        }
1056
 
1057
        return array();
1058
    }
1059
 
1060
    /**
1061
     * Get the unserialized XML representing a mirror
1062
     * @return array|false
1063
     */
1064
    function getMirror($server)
1065
    {
1066
        foreach ($this->getMirrors() as $mirror) {
1067
            if ($mirror['attribs']['host'] == $server) {
1068
                return $mirror;
1069
            }
1070
        }
1071
 
1072
        return false;
1073
    }
1074
 
1075
    /**
1076
     * @param string
1077
     * @return string|false
1078
     * @error PEAR_CHANNELFILE_ERROR_NO_NAME
1079
     * @error PEAR_CHANNELFILE_ERROR_INVALID_NAME
1080
     */
1081
    function setName($name)
1082
    {
1083
        return $this->setServer($name);
1084
    }
1085
 
1086
    /**
1087
     * Set the socket number (port) that is used to connect to this channel
1088
     * @param integer
1089
     * @param string|false name of the mirror server, or false for the primary
1090
     */
1091
    function setPort($port, $mirror = false)
1092
    {
1093
        if ($mirror) {
1094
            if (!isset($this->_channelInfo['servers']['mirror'])) {
1095
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
1096
                    array('mirror' => $mirror));
1097
                return false;
1098
            }
1099
 
1100
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
1101
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
1102
                    if ($mirror == $mir['attribs']['host']) {
1103
                        $this->_channelInfo['servers']['mirror'][$i]['attribs']['port'] = $port;
1104
                        return true;
1105
                    }
1106
                }
1107
 
1108
                return false;
1109
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
1110
                $this->_channelInfo['servers']['mirror']['attribs']['port'] = $port;
1111
                $this->_isValid = false;
1112
                return true;
1113
            }
1114
        }
1115
 
1116
        $this->_channelInfo['servers']['primary']['attribs']['port'] = $port;
1117
        $this->_isValid = false;
1118
        return true;
1119
    }
1120
 
1121
    /**
1122
     * Set the socket number (port) that is used to connect to this channel
1123
     * @param bool Determines whether to turn on SSL support or turn it off
1124
     * @param string|false name of the mirror server, or false for the primary
1125
     */
1126
    function setSSL($ssl = true, $mirror = false)
1127
    {
1128
        if ($mirror) {
1129
            if (!isset($this->_channelInfo['servers']['mirror'])) {
1130
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
1131
                    array('mirror' => $mirror));
1132
                return false;
1133
            }
1134
 
1135
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
1136
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
1137
                    if ($mirror == $mir['attribs']['host']) {
1138
                        if (!$ssl) {
1139
                            if (isset($this->_channelInfo['servers']['mirror'][$i]
1140
                                  ['attribs']['ssl'])) {
1141
                                unset($this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl']);
1142
                            }
1143
                        } else {
1144
                            $this->_channelInfo['servers']['mirror'][$i]['attribs']['ssl'] = 'yes';
1145
                        }
1146
 
1147
                        return true;
1148
                    }
1149
                }
1150
 
1151
                return false;
1152
            } elseif ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
1153
                if (!$ssl) {
1154
                    if (isset($this->_channelInfo['servers']['mirror']['attribs']['ssl'])) {
1155
                        unset($this->_channelInfo['servers']['mirror']['attribs']['ssl']);
1156
                    }
1157
                } else {
1158
                    $this->_channelInfo['servers']['mirror']['attribs']['ssl'] = 'yes';
1159
                }
1160
 
1161
                $this->_isValid = false;
1162
                return true;
1163
            }
1164
        }
1165
 
1166
        if ($ssl) {
1167
            $this->_channelInfo['servers']['primary']['attribs']['ssl'] = 'yes';
1168
        } else {
1169
            if (isset($this->_channelInfo['servers']['primary']['attribs']['ssl'])) {
1170
                unset($this->_channelInfo['servers']['primary']['attribs']['ssl']);
1171
            }
1172
        }
1173
 
1174
        $this->_isValid = false;
1175
        return true;
1176
    }
1177
 
1178
    /**
1179
     * @param string
1180
     * @return string|false
1181
     * @error PEAR_CHANNELFILE_ERROR_NO_SERVER
1182
     * @error PEAR_CHANNELFILE_ERROR_INVALID_SERVER
1183
     */
1184
    function setServer($server, $mirror = false)
1185
    {
1186
        if (empty($server)) {
1187
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SERVER);
1188
            return false;
1189
        } elseif (!$this->validChannelServer($server)) {
1190
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
1191
                array('tag' => 'name', 'name' => $server));
1192
            return false;
1193
        }
1194
 
1195
        if ($mirror) {
1196
            $found = false;
1197
            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
1198
                if ($mirror == $mir['attribs']['host']) {
1199
                    $found = true;
1200
                    break;
1201
                }
1202
            }
1203
 
1204
            if (!$found) {
1205
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
1206
                    array('mirror' => $mirror));
1207
                return false;
1208
            }
1209
 
1210
            $this->_channelInfo['mirror'][$i]['attribs']['host'] = $server;
1211
            return true;
1212
        }
1213
 
1214
        $this->_channelInfo['name'] = $server;
1215
        return true;
1216
    }
1217
 
1218
    /**
1219
     * @param string
1220
     * @return boolean success
1221
     * @error PEAR_CHANNELFILE_ERROR_NO_SUMMARY
1222
     * @warning PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY
1223
     */
1224
    function setSummary($summary)
1225
    {
1226
        if (empty($summary)) {
1227
            $this->_validateError(PEAR_CHANNELFILE_ERROR_NO_SUMMARY);
1228
            return false;
1229
        } elseif (strpos(trim($summary), "\n") !== false) {
1230
            $this->_validateWarning(PEAR_CHANNELFILE_ERROR_MULTILINE_SUMMARY,
1231
                array('summary' => $summary));
1232
        }
1233
 
1234
        $this->_channelInfo['summary'] = $summary;
1235
        return true;
1236
    }
1237
 
1238
    /**
1239
     * @param string
1240
     * @param boolean determines whether the alias is in channel.xml or local
1241
     * @return boolean success
1242
     */
1243
    function setAlias($alias, $local = false)
1244
    {
1245
        if (!$this->validChannelServer($alias)) {
1246
            $this->_validateError(PEAR_CHANNELFILE_ERROR_INVALID_NAME,
1247
                array('tag' => 'suggestedalias', 'name' => $alias));
1248
            return false;
1249
        }
1250
 
1251
        if ($local) {
1252
            $this->_channelInfo['localalias'] = $alias;
1253
        } else {
1254
            $this->_channelInfo['suggestedalias'] = $alias;
1255
        }
1256
 
1257
        return true;
1258
    }
1259
 
1260
    /**
1261
     * @return string
1262
     */
1263
    function getAlias()
1264
    {
1265
        if (isset($this->_channelInfo['localalias'])) {
1266
            return $this->_channelInfo['localalias'];
1267
        }
1268
        if (isset($this->_channelInfo['suggestedalias'])) {
1269
            return $this->_channelInfo['suggestedalias'];
1270
        }
1271
        if (isset($this->_channelInfo['name'])) {
1272
            return $this->_channelInfo['name'];
1273
        }
1274
        return '';
1275
    }
1276
 
1277
    /**
1278
     * Set the package validation object if it differs from PEAR's default
1279
     * The class must be includeable via changing _ in the classname to path separator,
1280
     * but no checking of this is made.
1281
     * @param string|false pass in false to reset to the default packagename regex
1282
     * @return boolean success
1283
     */
1284
    function setValidationPackage($validateclass, $version)
1285
    {
1286
        if (empty($validateclass)) {
1287
            unset($this->_channelInfo['validatepackage']);
1288
        }
1289
        $this->_channelInfo['validatepackage'] = array('_content' => $validateclass);
1290
        $this->_channelInfo['validatepackage']['attribs'] = array('version' => $version);
1291
    }
1292
 
1293
    /**
1294
     * Add a protocol to the provides section
1295
     * @param string protocol type
1296
     * @param string protocol version
1297
     * @param string protocol name, if any
1298
     * @param string mirror name, if this is a mirror's protocol
1299
     * @return bool
1300
     */
1301
    function addFunction($type, $version, $name = '', $mirror = false)
1302
    {
1303
        if ($mirror) {
1304
            return $this->addMirrorFunction($mirror, $type, $version, $name);
1305
        }
1306
 
1307
        $set = array('attribs' => array('version' => $version), '_content' => $name);
1308
        if (!isset($this->_channelInfo['servers']['primary'][$type]['function'])) {
1309
            if (!isset($this->_channelInfo['servers'])) {
1310
                $this->_channelInfo['servers'] = array('primary' =>
1311
                    array($type => array()));
1312
            } elseif (!isset($this->_channelInfo['servers']['primary'])) {
1313
                $this->_channelInfo['servers']['primary'] = array($type => array());
1314
            }
1315
 
1316
            $this->_channelInfo['servers']['primary'][$type]['function'] = $set;
1317
            $this->_isValid = false;
1318
            return true;
1319
        } elseif (!isset($this->_channelInfo['servers']['primary'][$type]['function'][0])) {
1320
            $this->_channelInfo['servers']['primary'][$type]['function'] = array(
1321
                $this->_channelInfo['servers']['primary'][$type]['function']);
1322
        }
1323
 
1324
        $this->_channelInfo['servers']['primary'][$type]['function'][] = $set;
1325
        return true;
1326
    }
1327
    /**
1328
     * Add a protocol to a mirror's provides section
1329
     * @param string mirror name (server)
1330
     * @param string protocol type
1331
     * @param string protocol version
1332
     * @param string protocol name, if any
1333
     */
1334
    function addMirrorFunction($mirror, $type, $version, $name = '')
1335
    {
1336
        if (!isset($this->_channelInfo['servers']['mirror'])) {
1337
            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
1338
                array('mirror' => $mirror));
1339
            return false;
1340
        }
1341
 
1342
        $setmirror = false;
1343
        if (isset($this->_channelInfo['servers']['mirror'][0])) {
1344
            foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
1345
                if ($mirror == $mir['attribs']['host']) {
1346
                    $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
1347
                    break;
1348
                }
1349
            }
1350
        } else {
1351
            if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
1352
                $setmirror = &$this->_channelInfo['servers']['mirror'];
1353
            }
1354
        }
1355
 
1356
        if (!$setmirror) {
1357
            $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
1358
                array('mirror' => $mirror));
1359
            return false;
1360
        }
1361
 
1362
        $set = array('attribs' => array('version' => $version), '_content' => $name);
1363
        if (!isset($setmirror[$type]['function'])) {
1364
            $setmirror[$type]['function'] = $set;
1365
            $this->_isValid = false;
1366
            return true;
1367
        } elseif (!isset($setmirror[$type]['function'][0])) {
1368
            $setmirror[$type]['function'] = array($setmirror[$type]['function']);
1369
        }
1370
 
1371
        $setmirror[$type]['function'][] = $set;
1372
        $this->_isValid = false;
1373
        return true;
1374
    }
1375
 
1376
    /**
1377
     * @param string Resource Type this url links to
1378
     * @param string URL
1379
     * @param string|false mirror name, if this is not a primary server REST base URL
1380
     */
1381
    function setBaseURL($resourceType, $url, $mirror = false)
1382
    {
1383
        if ($mirror) {
1384
            if (!isset($this->_channelInfo['servers']['mirror'])) {
1385
                $this->_validateError(PEAR_CHANNELFILE_ERROR_MIRROR_NOT_FOUND,
1386
                    array('mirror' => $mirror));
1387
                return false;
1388
            }
1389
 
1390
            $setmirror = false;
1391
            if (isset($this->_channelInfo['servers']['mirror'][0])) {
1392
                foreach ($this->_channelInfo['servers']['mirror'] as $i => $mir) {
1393
                    if ($mirror == $mir['attribs']['host']) {
1394
                        $setmirror = &$this->_channelInfo['servers']['mirror'][$i];
1395
                        break;
1396
                    }
1397
                }
1398
            } else {
1399
                if ($this->_channelInfo['servers']['mirror']['attribs']['host'] == $mirror) {
1400
                    $setmirror = &$this->_channelInfo['servers']['mirror'];
1401
                }
1402
            }
1403
        } else {
1404
            $setmirror = &$this->_channelInfo['servers']['primary'];
1405
        }
1406
 
1407
        $set = array('attribs' => array('type' => $resourceType), '_content' => $url);
1408
        if (!isset($setmirror['rest'])) {
1409
            $setmirror['rest'] = array();
1410
        }
1411
 
1412
        if (!isset($setmirror['rest']['baseurl'])) {
1413
            $setmirror['rest']['baseurl'] = $set;
1414
            $this->_isValid = false;
1415
            return true;
1416
        } elseif (!isset($setmirror['rest']['baseurl'][0])) {
1417
            $setmirror['rest']['baseurl'] = array($setmirror['rest']['baseurl']);
1418
        }
1419
 
1420
        foreach ($setmirror['rest']['baseurl'] as $i => $url) {
1421
            if ($url['attribs']['type'] == $resourceType) {
1422
                $this->_isValid = false;
1423
                $setmirror['rest']['baseurl'][$i] = $set;
1424
                return true;
1425
            }
1426
        }
1427
 
1428
        $setmirror['rest']['baseurl'][] = $set;
1429
        $this->_isValid = false;
1430
        return true;
1431
    }
1432
 
1433
    /**
1434
     * @param string mirror server
1435
     * @param int mirror http port
1436
     * @return boolean
1437
     */
1438
    function addMirror($server, $port = null)
1439
    {
1440
        if ($this->_channelInfo['name'] == '__uri') {
1441
            return false; // the __uri channel cannot have mirrors by definition
1442
        }
1443
 
1444
        $set = array('attribs' => array('host' => $server));
1445
        if (is_numeric($port)) {
1446
            $set['attribs']['port'] = $port;
1447
        }
1448
 
1449
        if (!isset($this->_channelInfo['servers']['mirror'])) {
1450
            $this->_channelInfo['servers']['mirror'] = $set;
1451
            return true;
1452
        }
1453
 
1454
        if (!isset($this->_channelInfo['servers']['mirror'][0])) {
1455
            $this->_channelInfo['servers']['mirror'] =
1456
                array($this->_channelInfo['servers']['mirror']);
1457
        }
1458
 
1459
        $this->_channelInfo['servers']['mirror'][] = $set;
1460
        return true;
1461
    }
1462
 
1463
    /**
1464
     * Retrieve the name of the validation package for this channel
1465
     * @return string|false
1466
     */
1467
    function getValidationPackage()
1468
    {
1469
        if (!$this->_isValid && !$this->validate()) {
1470
            return false;
1471
        }
1472
 
1473
        if (!isset($this->_channelInfo['validatepackage'])) {
1474
            return array('attribs' => array('version' => 'default'),
1475
                '_content' => 'PEAR_Validate');
1476
        }
1477
 
1478
        return $this->_channelInfo['validatepackage'];
1479
    }
1480
 
1481
    /**
1482
     * Retrieve the object that can be used for custom validation
1483
     * @param string|false the name of the package to validate.  If the package is
1484
     *                     the channel validation package, PEAR_Validate is returned
1485
     * @return PEAR_Validate|false false is returned if the validation package
1486
     *         cannot be located
1487
     */
1488
    function &getValidationObject($package = false)
1489
    {
1490
        if (!class_exists('PEAR_Validate')) {
1491
            require_once 'PEAR/Validate.php';
1492
        }
1493
 
1494
        if (!$this->_isValid) {
1495
            if (!$this->validate()) {
1496
                $a = false;
1497
                return $a;
1498
            }
1499
        }
1500
 
1501
        if (isset($this->_channelInfo['validatepackage'])) {
1502
            if ($package == $this->_channelInfo['validatepackage']) {
1503
                // channel validation packages are always validated by PEAR_Validate
1504
                $val = &new PEAR_Validate;
1505
                return $val;
1506
            }
1507
 
1508
            if (!class_exists(str_replace('.', '_',
1509
                  $this->_channelInfo['validatepackage']['_content']))) {
1510
                if ($this->isIncludeable(str_replace('_', '/',
1511
                      $this->_channelInfo['validatepackage']['_content']) . '.php')) {
1512
                    include_once str_replace('_', '/',
1513
                        $this->_channelInfo['validatepackage']['_content']) . '.php';
1514
                    $vclass = str_replace('.', '_',
1515
                        $this->_channelInfo['validatepackage']['_content']);
1516
                    $val = &new $vclass;
1517
                } else {
1518
                    $a = false;
1519
                    return $a;
1520
                }
1521
            } else {
1522
                $vclass = str_replace('.', '_',
1523
                    $this->_channelInfo['validatepackage']['_content']);
1524
                $val = &new $vclass;
1525
            }
1526
        } else {
1527
            $val = &new PEAR_Validate;
1528
        }
1529
 
1530
        return $val;
1531
    }
1532
 
1533
    function isIncludeable($path)
1534
    {
1535
        $possibilities = explode(PATH_SEPARATOR, ini_get('include_path'));
1536
        foreach ($possibilities as $dir) {
1537
            if (file_exists($dir . DIRECTORY_SEPARATOR . $path)
1538
                  && is_readable($dir . DIRECTORY_SEPARATOR . $path)) {
1539
                return true;
1540
            }
1541
        }
1542
 
1543
        return false;
1544
    }
1545
 
1546
    /**
1547
     * This function is used by the channel updater and retrieves a value set by
1548
     * the registry, or the current time if it has not been set
1549
     * @return string
1550
     */
1551
    function lastModified()
1552
    {
1553
        if (isset($this->_channelInfo['_lastmodified'])) {
1554
            return $this->_channelInfo['_lastmodified'];
1555
        }
1556
 
1557
        return time();
1558
    }
1559
}