Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// /* vim: set expandtab tabstop=4 shiftwidth=4: */
3
/**
4
 * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
5
 * channel-update, channel-info, channel-alias, channel-discover commands)
6
 *
7
 * PHP versions 4 and 5
8
 *
9
 * @category   pear
10
 * @package    PEAR
11
 * @author     Stig Bakken <ssb@php.net>
12
 * @author     Greg Beaver <cellog@php.net>
13
 * @copyright  1997-2009 The Authors
14
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
15
 * @version    CVS: $Id: Channels.php 313023 2011-07-06 19:17:11Z dufuz $
16
 * @link       http://pear.php.net/package/PEAR
17
 * @since      File available since Release 1.4.0a1
18
 */
19
 
20
/**
21
 * base class
22
 */
23
require_once 'PEAR/Command/Common.php';
24
 
25
define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500);
26
 
27
/**
28
 * PEAR commands for managing channels.
29
 *
30
 * @category   pear
31
 * @package    PEAR
32
 * @author     Greg Beaver <cellog@php.net>
33
 * @copyright  1997-2009 The Authors
34
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
35
 * @version    Release: 1.9.4
36
 * @link       http://pear.php.net/package/PEAR
37
 * @since      Class available since Release 1.4.0a1
38
 */
39
class PEAR_Command_Channels extends PEAR_Command_Common
40
{
41
    var $commands = array(
42
        'list-channels' => array(
43
            'summary' => 'List Available Channels',
44
            'function' => 'doList',
45
            'shortcut' => 'lc',
46
            'options' => array(),
47
            'doc' => '
48
List all available channels for installation.
49
',
50
            ),
51
        'update-channels' => array(
52
            'summary' => 'Update the Channel List',
53
            'function' => 'doUpdateAll',
54
            'shortcut' => 'uc',
55
            'options' => array(),
56
            'doc' => '
57
List all installed packages in all channels.
58
'
59
            ),
60
        'channel-delete' => array(
61
            'summary' => 'Remove a Channel From the List',
62
            'function' => 'doDelete',
63
            'shortcut' => 'cde',
64
            'options' => array(),
65
            'doc' => '<channel name>
66
Delete a channel from the registry.  You may not
67
remove any channel that has installed packages.
68
'
69
            ),
70
        'channel-add' => array(
71
            'summary' => 'Add a Channel',
72
            'function' => 'doAdd',
73
            'shortcut' => 'ca',
74
            'options' => array(),
75
            'doc' => '<channel.xml>
76
Add a private channel to the channel list.  Note that all
77
public channels should be synced using "update-channels".
78
Parameter may be either a local file or remote URL to a
79
channel.xml.
80
'
81
            ),
82
        'channel-update' => array(
83
            'summary' => 'Update an Existing Channel',
84
            'function' => 'doUpdate',
85
            'shortcut' => 'cu',
86
            'options' => array(
87
                'force' => array(
88
                    'shortopt' => 'f',
89
                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
90
                    ),
91
                'channel' => array(
92
                    'shortopt' => 'c',
93
                    'arg' => 'CHANNEL',
94
                    'doc' => 'will force download of new channel.xml if an existing channel name is used',
95
                    ),
96
),
97
            'doc' => '[<channel.xml>|<channel name>]
98
Update a channel in the channel list directly.  Note that all
99
public channels can be synced using "update-channels".
100
Parameter may be a local or remote channel.xml, or the name of
101
an existing channel.
102
'
103
            ),
104
        'channel-info' => array(
105
            'summary' => 'Retrieve Information on a Channel',
106
            'function' => 'doInfo',
107
            'shortcut' => 'ci',
108
            'options' => array(),
109
            'doc' => '<package>
110
List the files in an installed package.
111
'
112
            ),
113
        'channel-alias' => array(
114
            'summary' => 'Specify an alias to a channel name',
115
            'function' => 'doAlias',
116
            'shortcut' => 'cha',
117
            'options' => array(),
118
            'doc' => '<channel> <alias>
119
Specify a specific alias to use for a channel name.
120
The alias may not be an existing channel name or
121
alias.
122
'
123
            ),
124
        'channel-discover' => array(
125
            'summary' => 'Initialize a Channel from its server',
126
            'function' => 'doDiscover',
127
            'shortcut' => 'di',
128
            'options' => array(),
129
            'doc' => '[<channel.xml>|<channel name>]
130
Initialize a channel from its server and create a local channel.xml.
131
If <channel name> is in the format "<username>:<password>@<channel>" then
132
<username> and <password> will be set as the login username/password for
133
<channel>. Use caution when passing the username/password in this way, as
134
it may allow other users on your computer to briefly view your username/
135
password via the system\'s process list.
136
'
137
            ),
138
        'channel-login' => array(
139
            'summary' => 'Connects and authenticates to remote channel server',
140
            'shortcut' => 'cli',
141
            'function' => 'doLogin',
142
            'options' => array(),
143
            'doc' => '<channel name>
144
Log in to a remote channel server.  If <channel name> is not supplied,
145
the default channel is used. To use remote functions in the installer
146
that require any kind of privileges, you need to log in first.  The
147
username and password you enter here will be stored in your per-user
148
PEAR configuration (~/.pearrc on Unix-like systems).  After logging
149
in, your username and password will be sent along in subsequent
150
operations on the remote server.',
151
            ),
152
        'channel-logout' => array(
153
            'summary' => 'Logs out from the remote channel server',
154
            'shortcut' => 'clo',
155
            'function' => 'doLogout',
156
            'options' => array(),
157
            'doc' => '<channel name>
158
Logs out from a remote channel server.  If <channel name> is not supplied,
159
the default channel is used. This command does not actually connect to the
160
remote server, it only deletes the stored username and password from your user
161
configuration.',
162
            ),
163
        );
164
 
165
    /**
166
     * PEAR_Command_Registry constructor.
167
     *
168
     * @access public
169
     */
170
    function PEAR_Command_Channels(&$ui, &$config)
171
    {
172
        parent::PEAR_Command_Common($ui, $config);
173
    }
174
 
175
    function _sortChannels($a, $b)
176
    {
177
        return strnatcasecmp($a->getName(), $b->getName());
178
    }
179
 
180
    function doList($command, $options, $params)
181
    {
182
        $reg = &$this->config->getRegistry();
183
        $registered = $reg->getChannels();
184
        usort($registered, array(&$this, '_sortchannels'));
185
        $i = $j = 0;
186
        $data = array(
187
            'caption' => 'Registered Channels:',
188
            'border' => true,
189
            'headline' => array('Channel', 'Alias', 'Summary')
190
            );
191
        foreach ($registered as $channel) {
192
            $data['data'][] = array($channel->getName(),
193
                                    $channel->getAlias(),
194
                                    $channel->getSummary());
195
        }
196
 
197
        if (count($registered) === 0) {
198
            $data = '(no registered channels)';
199
        }
200
        $this->ui->outputData($data, $command);
201
        return true;
202
    }
203
 
204
    function doUpdateAll($command, $options, $params)
205
    {
206
        $reg = &$this->config->getRegistry();
207
        $channels = $reg->getChannels();
208
 
209
        $success = true;
210
        foreach ($channels as $channel) {
211
            if ($channel->getName() != '__uri') {
212
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
213
                $err = $this->doUpdate('channel-update',
214
                                          $options,
215
                                          array($channel->getName()));
216
                if (PEAR::isError($err)) {
217
                    $this->ui->outputData($err->getMessage(), $command);
218
                    $success = false;
219
                } else {
220
                    $success &= $err;
221
                }
222
            }
223
        }
224
        return $success;
225
    }
226
 
227
    function doInfo($command, $options, $params)
228
    {
229
        if (count($params) !== 1) {
230
            return $this->raiseError("No channel specified");
231
        }
232
 
233
        $reg     = &$this->config->getRegistry();
234
        $channel = strtolower($params[0]);
235
        if ($reg->channelExists($channel)) {
236
            $chan = $reg->getChannel($channel);
237
            if (PEAR::isError($chan)) {
238
                return $this->raiseError($chan);
239
            }
240
        } else {
241
            if (strpos($channel, '://')) {
242
                $downloader = &$this->getDownloader();
243
                $tmpdir = $this->config->get('temp_dir');
244
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
245
                $loc = $downloader->downloadHttp($channel, $this->ui, $tmpdir);
246
                PEAR::staticPopErrorHandling();
247
                if (PEAR::isError($loc)) {
248
                    return $this->raiseError('Cannot open "' . $channel .
249
                        '" (' . $loc->getMessage() . ')');
250
                } else {
251
                    $contents = implode('', file($loc));
252
                }
253
            } else {
254
                if (!file_exists($params[0])) {
255
                    return $this->raiseError('Unknown channel "' . $channel . '"');
256
                }
257
 
258
                $fp = fopen($params[0], 'r');
259
                if (!$fp) {
260
                    return $this->raiseError('Cannot open "' . $params[0] . '"');
261
                }
262
 
263
                $contents = '';
264
                while (!feof($fp)) {
265
                    $contents .= fread($fp, 1024);
266
                }
267
                fclose($fp);
268
            }
269
 
270
            if (!class_exists('PEAR_ChannelFile')) {
271
                require_once 'PEAR/ChannelFile.php';
272
            }
273
 
274
            $chan = new PEAR_ChannelFile;
275
            $chan->fromXmlString($contents);
276
            $chan->validate();
277
            if ($errs = $chan->getErrors(true)) {
278
                foreach ($errs as $err) {
279
                    $this->ui->outputData($err['level'] . ': ' . $err['message']);
280
                }
281
                return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
282
            }
283
        }
284
 
285
        if (!$chan) {
286
            return $this->raiseError('Serious error: Channel "' . $params[0] .
287
                '" has a corrupted registry entry');
288
        }
289
 
290
        $channel = $chan->getName();
291
        $caption = 'Channel ' . $channel . ' Information:';
292
        $data1 = array(
293
            'caption' => $caption,
294
            'border' => true);
295
        $data1['data']['server'] = array('Name and Server', $chan->getName());
296
        if ($chan->getAlias() != $chan->getName()) {
297
            $data1['data']['alias'] = array('Alias', $chan->getAlias());
298
        }
299
 
300
        $data1['data']['summary'] = array('Summary', $chan->getSummary());
301
        $validate = $chan->getValidationPackage();
302
        $data1['data']['vpackage'] = array('Validation Package Name', $validate['_content']);
303
        $data1['data']['vpackageversion'] =
304
            array('Validation Package Version', $validate['attribs']['version']);
305
        $d = array();
306
        $d['main'] = $data1;
307
 
308
        $data['data'] = array();
309
        $data['caption'] = 'Server Capabilities';
310
        $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
311
        if ($chan->supportsREST()) {
312
            if ($chan->supportsREST()) {
313
                $funcs = $chan->getFunctions('rest');
314
                if (!isset($funcs[0])) {
315
                    $funcs = array($funcs);
316
                }
317
                foreach ($funcs as $protocol) {
318
                    $data['data'][] = array('rest', $protocol['attribs']['type'],
319
                        $protocol['_content']);
320
                }
321
            }
322
        } else {
323
            $data['data'][] = array('No supported protocols');
324
        }
325
 
326
        $d['protocols'] = $data;
327
        $data['data'] = array();
328
        $mirrors = $chan->getMirrors();
329
        if ($mirrors) {
330
            $data['caption'] = 'Channel ' . $channel . ' Mirrors:';
331
            unset($data['headline']);
332
            foreach ($mirrors as $mirror) {
333
                $data['data'][] = array($mirror['attribs']['host']);
334
                $d['mirrors'] = $data;
335
            }
336
 
337
            foreach ($mirrors as $i => $mirror) {
338
                $data['data'] = array();
339
                $data['caption'] = 'Mirror ' . $mirror['attribs']['host'] . ' Capabilities';
340
                $data['headline'] = array('Type', 'Version/REST type', 'Function Name/REST base');
341
                if ($chan->supportsREST($mirror['attribs']['host'])) {
342
                    if ($chan->supportsREST($mirror['attribs']['host'])) {
343
                        $funcs = $chan->getFunctions('rest', $mirror['attribs']['host']);
344
                        if (!isset($funcs[0])) {
345
                            $funcs = array($funcs);
346
                        }
347
 
348
                        foreach ($funcs as $protocol) {
349
                            $data['data'][] = array('rest', $protocol['attribs']['type'],
350
                                $protocol['_content']);
351
                        }
352
                    }
353
                } else {
354
                    $data['data'][] = array('No supported protocols');
355
                }
356
                $d['mirrorprotocols' . $i] = $data;
357
            }
358
        }
359
        $this->ui->outputData($d, 'channel-info');
360
    }
361
 
362
    // }}}
363
 
364
    function doDelete($command, $options, $params)
365
    {
366
        if (count($params) !== 1) {
367
            return $this->raiseError('channel-delete: no channel specified');
368
        }
369
 
370
        $reg = &$this->config->getRegistry();
371
        if (!$reg->channelExists($params[0])) {
372
            return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
373
        }
374
 
375
        $channel = $reg->channelName($params[0]);
376
        if ($channel == 'pear.php.net') {
377
            return $this->raiseError('Cannot delete the pear.php.net channel');
378
        }
379
 
380
        if ($channel == 'pecl.php.net') {
381
            return $this->raiseError('Cannot delete the pecl.php.net channel');
382
        }
383
 
384
        if ($channel == 'doc.php.net') {
385
            return $this->raiseError('Cannot delete the doc.php.net channel');
386
        }
387
 
388
        if ($channel == '__uri') {
389
            return $this->raiseError('Cannot delete the __uri pseudo-channel');
390
        }
391
 
392
        if (PEAR::isError($err = $reg->listPackages($channel))) {
393
            return $err;
394
        }
395
 
396
        if (count($err)) {
397
            return $this->raiseError('Channel "' . $channel .
398
                '" has installed packages, cannot delete');
399
        }
400
 
401
        if (!$reg->deleteChannel($channel)) {
402
            return $this->raiseError('Channel "' . $channel . '" deletion failed');
403
        } else {
404
            $this->config->deleteChannel($channel);
405
            $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
406
        }
407
    }
408
 
409
    function doAdd($command, $options, $params)
410
    {
411
        if (count($params) !== 1) {
412
            return $this->raiseError('channel-add: no channel file specified');
413
        }
414
 
415
        if (strpos($params[0], '://')) {
416
            $downloader = &$this->getDownloader();
417
            $tmpdir = $this->config->get('temp_dir');
418
            if (!file_exists($tmpdir)) {
419
                require_once 'System.php';
420
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
421
                $err = System::mkdir(array('-p', $tmpdir));
422
                PEAR::staticPopErrorHandling();
423
                if (PEAR::isError($err)) {
424
                    return $this->raiseError('channel-add: temp_dir does not exist: "' .
425
                        $tmpdir .
426
                        '" - You can change this location with "pear config-set temp_dir"');
427
                }
428
            }
429
 
430
            if (!is_writable($tmpdir)) {
431
                return $this->raiseError('channel-add: temp_dir is not writable: "' .
432
                    $tmpdir .
433
                    '" - You can change this location with "pear config-set temp_dir"');
434
            }
435
 
436
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
437
            $loc = $downloader->downloadHttp($params[0], $this->ui, $tmpdir, null, false);
438
            PEAR::staticPopErrorHandling();
439
            if (PEAR::isError($loc)) {
440
                return $this->raiseError('channel-add: Cannot open "' . $params[0] .
441
                    '" (' . $loc->getMessage() . ')');
442
            }
443
 
444
            list($loc, $lastmodified) = $loc;
445
            $contents = implode('', file($loc));
446
        } else {
447
            $lastmodified = $fp = false;
448
            if (file_exists($params[0])) {
449
                $fp = fopen($params[0], 'r');
450
            }
451
 
452
            if (!$fp) {
453
                return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
454
            }
455
 
456
            $contents = '';
457
            while (!feof($fp)) {
458
                $contents .= fread($fp, 1024);
459
            }
460
            fclose($fp);
461
        }
462
 
463
        if (!class_exists('PEAR_ChannelFile')) {
464
            require_once 'PEAR/ChannelFile.php';
465
        }
466
 
467
        $channel = new PEAR_ChannelFile;
468
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
469
        $result = $channel->fromXmlString($contents);
470
        PEAR::staticPopErrorHandling();
471
        if (!$result) {
472
            $exit = false;
473
            if (count($errors = $channel->getErrors(true))) {
474
                foreach ($errors as $error) {
475
                    $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
476
                    if (!$exit) {
477
                        $exit = $error['level'] == 'error' ? true : false;
478
                    }
479
                }
480
                if ($exit) {
481
                    return $this->raiseError('channel-add: invalid channel.xml file');
482
                }
483
            }
484
        }
485
 
486
        $reg = &$this->config->getRegistry();
487
        if ($reg->channelExists($channel->getName())) {
488
            return $this->raiseError('channel-add: Channel "' . $channel->getName() .
489
                '" exists, use channel-update to update entry', PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
490
        }
491
 
492
        $ret = $reg->addChannel($channel, $lastmodified);
493
        if (PEAR::isError($ret)) {
494
            return $ret;
495
        }
496
 
497
        if (!$ret) {
498
            return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
499
                '" to registry failed');
500
        }
501
 
502
        $this->config->setChannels($reg->listChannels());
503
        $this->config->writeConfigFile();
504
        $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
505
    }
506
 
507
    function doUpdate($command, $options, $params)
508
    {
509
        if (count($params) !== 1) {
510
            return $this->raiseError("No channel file specified");
511
        }
512
 
513
        $tmpdir = $this->config->get('temp_dir');
514
        if (!file_exists($tmpdir)) {
515
            require_once 'System.php';
516
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
517
            $err = System::mkdir(array('-p', $tmpdir));
518
            PEAR::staticPopErrorHandling();
519
            if (PEAR::isError($err)) {
520
                return $this->raiseError('channel-add: temp_dir does not exist: "' .
521
                    $tmpdir .
522
                    '" - You can change this location with "pear config-set temp_dir"');
523
            }
524
        }
525
 
526
        if (!is_writable($tmpdir)) {
527
            return $this->raiseError('channel-add: temp_dir is not writable: "' .
528
                $tmpdir .
529
                '" - You can change this location with "pear config-set temp_dir"');
530
        }
531
 
532
        $reg = &$this->config->getRegistry();
533
        $lastmodified = false;
534
        if ((!file_exists($params[0]) || is_dir($params[0]))
535
              && $reg->channelExists(strtolower($params[0]))) {
536
            $c = $reg->getChannel(strtolower($params[0]));
537
            if (PEAR::isError($c)) {
538
                return $this->raiseError($c);
539
            }
540
 
541
            $this->ui->outputData("Updating channel \"$params[0]\"", $command);
542
            $dl = &$this->getDownloader(array());
543
            // if force is specified, use a timestamp of "1" to force retrieval
544
            $lastmodified = isset($options['force']) ? false : $c->lastModified();
545
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
546
            $contents = $dl->downloadHttp('http://' . $c->getName() . '/channel.xml',
547
                $this->ui, $tmpdir, null, $lastmodified);
548
            PEAR::staticPopErrorHandling();
549
            if (PEAR::isError($contents)) {
550
                // Attempt to fall back to https
551
                $this->ui->outputData("Channel \"$params[0]\" is not responding over http://, failed with message: " . $contents->getMessage());
552
                $this->ui->outputData("Trying channel \"$params[0]\" over https:// instead");
553
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
554
                $contents = $dl->downloadHttp('https://' . $c->getName() . '/channel.xml',
555
                    $this->ui, $tmpdir, null, $lastmodified);
556
                PEAR::staticPopErrorHandling();
557
                if (PEAR::isError($contents)) {
558
                    return $this->raiseError('Cannot retrieve channel.xml for channel "' .
559
                        $c->getName() . '" (' . $contents->getMessage() . ')');
560
                }
561
            }
562
 
563
            list($contents, $lastmodified) = $contents;
564
            if (!$contents) {
565
                $this->ui->outputData("Channel \"$params[0]\" is up to date");
566
                return;
567
            }
568
 
569
            $contents = implode('', file($contents));
570
            if (!class_exists('PEAR_ChannelFile')) {
571
                require_once 'PEAR/ChannelFile.php';
572
            }
573
 
574
            $channel = new PEAR_ChannelFile;
575
            $channel->fromXmlString($contents);
576
            if (!$channel->getErrors()) {
577
                // security check: is the downloaded file for the channel we got it from?
578
                if (strtolower($channel->getName()) != strtolower($c->getName())) {
579
                    if (!isset($options['force'])) {
580
                        return $this->raiseError('ERROR: downloaded channel definition file' .
581
                            ' for channel "' . $channel->getName() . '" from channel "' .
582
                            strtolower($c->getName()) . '"');
583
                    }
584
 
585
                    $this->ui->log(0, 'WARNING: downloaded channel definition file' .
586
                        ' for channel "' . $channel->getName() . '" from channel "' .
587
                        strtolower($c->getName()) . '"');
588
                }
589
            }
590
        } else {
591
            if (strpos($params[0], '://')) {
592
                $dl = &$this->getDownloader();
593
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
594
                $loc = $dl->downloadHttp($params[0],
595
                    $this->ui, $tmpdir, null, $lastmodified);
596
                PEAR::staticPopErrorHandling();
597
                if (PEAR::isError($loc)) {
598
                    return $this->raiseError("Cannot open " . $params[0] .
599
                         ' (' . $loc->getMessage() . ')');
600
                }
601
 
602
                list($loc, $lastmodified) = $loc;
603
                $contents = implode('', file($loc));
604
            } else {
605
                $fp = false;
606
                if (file_exists($params[0])) {
607
                    $fp = fopen($params[0], 'r');
608
                }
609
 
610
                if (!$fp) {
611
                    return $this->raiseError("Cannot open " . $params[0]);
612
                }
613
 
614
                $contents = '';
615
                while (!feof($fp)) {
616
                    $contents .= fread($fp, 1024);
617
                }
618
                fclose($fp);
619
            }
620
 
621
            if (!class_exists('PEAR_ChannelFile')) {
622
                require_once 'PEAR/ChannelFile.php';
623
            }
624
 
625
            $channel = new PEAR_ChannelFile;
626
            $channel->fromXmlString($contents);
627
        }
628
 
629
        $exit = false;
630
        if (count($errors = $channel->getErrors(true))) {
631
            foreach ($errors as $error) {
632
                $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
633
                if (!$exit) {
634
                    $exit = $error['level'] == 'error' ? true : false;
635
                }
636
            }
637
            if ($exit) {
638
                return $this->raiseError('Invalid channel.xml file');
639
            }
640
        }
641
 
642
        if (!$reg->channelExists($channel->getName())) {
643
            return $this->raiseError('Error: Channel "' . $channel->getName() .
644
                '" does not exist, use channel-add to add an entry');
645
        }
646
 
647
        $ret = $reg->updateChannel($channel, $lastmodified);
648
        if (PEAR::isError($ret)) {
649
            return $ret;
650
        }
651
 
652
        if (!$ret) {
653
            return $this->raiseError('Updating Channel "' . $channel->getName() .
654
                '" in registry failed');
655
        }
656
 
657
        $this->config->setChannels($reg->listChannels());
658
        $this->config->writeConfigFile();
659
        $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
660
    }
661
 
662
    function &getDownloader()
663
    {
664
        if (!class_exists('PEAR_Downloader')) {
665
            require_once 'PEAR/Downloader.php';
666
        }
667
        $a = new PEAR_Downloader($this->ui, array(), $this->config);
668
        return $a;
669
    }
670
 
671
    function doAlias($command, $options, $params)
672
    {
673
        if (count($params) === 1) {
674
            return $this->raiseError('No channel alias specified');
675
        }
676
 
677
        if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) {
678
            return $this->raiseError(
679
                'Invalid format, correct is: channel-alias channel alias');
680
        }
681
 
682
        $reg = &$this->config->getRegistry();
683
        if (!$reg->channelExists($params[0], true)) {
684
            $extra = '';
685
            if ($reg->isAlias($params[0])) {
686
                $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
687
                    strtolower($params[1]) . '")';
688
            }
689
 
690
            return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
691
        }
692
 
693
        if ($reg->isAlias($params[1])) {
694
            return $this->raiseError('Channel "' . $reg->channelName($params[1]) . '" is ' .
695
                'already aliased to "' . strtolower($params[1]) . '", cannot re-alias');
696
        }
697
 
698
        $chan = &$reg->getChannel($params[0]);
699
        if (PEAR::isError($chan)) {
700
            return $this->raiseError('Corrupt registry?  Error retrieving channel "' . $params[0] .
701
                '" information (' . $chan->getMessage() . ')');
702
        }
703
 
704
        // make it a local alias
705
        if (!$chan->setAlias(strtolower($params[1]), true)) {
706
            return $this->raiseError('Alias "' . strtolower($params[1]) .
707
                '" is not a valid channel alias');
708
        }
709
 
710
        $reg->updateChannel($chan);
711
        $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
712
            strtolower($params[1]) . '"');
713
    }
714
 
715
    /**
716
     * The channel-discover command
717
     *
718
     * @param string $command command name
719
     * @param array  $options option_name => value
720
     * @param array  $params  list of additional parameters.
721
     *               $params[0] should contain a string with either:
722
     *               - <channel name> or
723
     *               - <username>:<password>@<channel name>
724
     * @return null|PEAR_Error
725
     */
726
    function doDiscover($command, $options, $params)
727
    {
728
        if (count($params) !== 1) {
729
            return $this->raiseError("No channel server specified");
730
        }
731
 
732
        // Look for the possible input format "<username>:<password>@<channel>"
733
        if (preg_match('/^(.+):(.+)@(.+)\\z/', $params[0], $matches)) {
734
            $username = $matches[1];
735
            $password = $matches[2];
736
            $channel  = $matches[3];
737
        } else {
738
            $channel = $params[0];
739
        }
740
 
741
        $reg = &$this->config->getRegistry();
742
        if ($reg->channelExists($channel)) {
743
            if (!$reg->isAlias($channel)) {
744
                return $this->raiseError("Channel \"$channel\" is already initialized", PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS);
745
            }
746
 
747
            return $this->raiseError("A channel alias named \"$channel\" " .
748
                'already exists, aliasing channel "' . $reg->channelName($channel)
749
                . '"');
750
        }
751
 
752
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
753
        $err = $this->doAdd($command, $options, array('http://' . $channel . '/channel.xml'));
754
        $this->popErrorHandling();
755
        if (PEAR::isError($err)) {
756
            if ($err->getCode() === PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS) {
757
                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
758
                    $err->getMessage() . ')');
759
            }
760
            // Attempt fetch via https
761
            $this->ui->outputData("Discovering channel $channel over http:// failed with message: " . $err->getMessage());
762
            $this->ui->outputData("Trying to discover channel $channel over https:// instead");
763
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
764
            $err = $this->doAdd($command, $options, array('https://' . $channel . '/channel.xml'));
765
            $this->popErrorHandling();
766
            if (PEAR::isError($err)) {
767
                return $this->raiseError("Discovery of channel \"$channel\" failed (" .
768
                    $err->getMessage() . ')');
769
            }
770
        }
771
 
772
        // Store username/password if they were given
773
        // Arguably we should do a logintest on the channel here, but since
774
        // that's awkward on a REST-based channel (even "pear login" doesn't
775
        // do it for those), and XML-RPC is deprecated, it's fairly pointless.
776
        if (isset($username)) {
777
            $this->config->set('username', $username, 'user', $channel);
778
            $this->config->set('password', $password, 'user', $channel);
779
            $this->config->store();
780
            $this->ui->outputData("Stored login for channel \"$channel\" using username \"$username\"", $command);
781
        }
782
 
783
        $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
784
    }
785
 
786
    /**
787
     * Execute the 'login' command.
788
     *
789
     * @param string $command command name
790
     * @param array $options option_name => value
791
     * @param array $params list of additional parameters
792
     *
793
     * @return bool TRUE on success or
794
     * a PEAR error on failure
795
     *
796
     * @access public
797
     */
798
    function doLogin($command, $options, $params)
799
    {
800
        $reg = &$this->config->getRegistry();
801
 
802
        // If a parameter is supplied, use that as the channel to log in to
803
        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
804
 
805
        $chan = $reg->getChannel($channel);
806
        if (PEAR::isError($chan)) {
807
            return $this->raiseError($chan);
808
        }
809
 
810
        $server   = $this->config->get('preferred_mirror', null, $channel);
811
        $username = $this->config->get('username',         null, $channel);
812
        if (empty($username)) {
813
            $username = isset($_ENV['USER']) ? $_ENV['USER'] : null;
814
        }
815
        $this->ui->outputData("Logging in to $server.", $command);
816
 
817
        list($username, $password) = $this->ui->userDialog(
818
            $command,
819
            array('Username', 'Password'),
820
            array('text',     'password'),
821
            array($username,  '')
822
            );
823
        $username = trim($username);
824
        $password = trim($password);
825
 
826
        $ourfile = $this->config->getConfFile('user');
827
        if (!$ourfile) {
828
            $ourfile = $this->config->getConfFile('system');
829
        }
830
 
831
        $this->config->set('username', $username, 'user', $channel);
832
        $this->config->set('password', $password, 'user', $channel);
833
 
834
        if ($chan->supportsREST()) {
835
            $ok = true;
836
        }
837
 
838
        if ($ok !== true) {
839
            return $this->raiseError('Login failed!');
840
        }
841
 
842
        $this->ui->outputData("Logged in.", $command);
843
        // avoid changing any temporary settings changed with -d
844
        $ourconfig = new PEAR_Config($ourfile, $ourfile);
845
        $ourconfig->set('username', $username, 'user', $channel);
846
        $ourconfig->set('password', $password, 'user', $channel);
847
        $ourconfig->store();
848
 
849
        return true;
850
    }
851
 
852
    /**
853
     * Execute the 'logout' command.
854
     *
855
     * @param string $command command name
856
     * @param array $options option_name => value
857
     * @param array $params list of additional parameters
858
     *
859
     * @return bool TRUE on success or
860
     * a PEAR error on failure
861
     *
862
     * @access public
863
     */
864
    function doLogout($command, $options, $params)
865
    {
866
        $reg     = &$this->config->getRegistry();
867
 
868
        // If a parameter is supplied, use that as the channel to log in to
869
        $channel = isset($params[0]) ? $params[0] : $this->config->get('default_channel');
870
 
871
        $chan    = $reg->getChannel($channel);
872
        if (PEAR::isError($chan)) {
873
            return $this->raiseError($chan);
874
        }
875
 
876
        $server = $this->config->get('preferred_mirror', null, $channel);
877
        $this->ui->outputData("Logging out from $server.", $command);
878
        $this->config->remove('username', 'user', $channel);
879
        $this->config->remove('password', 'user', $channel);
880
        $this->config->store();
881
        return true;
882
    }
883
}