Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

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