2 // /* vim: set expandtab tabstop=4 shiftwidth=4: */
4 * PEAR_Command_Channels (list-channels, update-channels, channel-delete, channel-add,
5 * channel-update, channel-info, channel-alias, channel-discover commands)
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
23 require_once 'PEAR/Command/Common.php';
25 define('PEAR_COMMAND_CHANNELS_CHANNEL_EXISTS', -500);
28 * PEAR commands for managing channels.
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
39 class PEAR_Command_Channels extends PEAR_Command_Common
41 var $commands = array(
42 'list-channels' => array(
43 'summary' => 'List Available Channels',
44 'function' => 'doList',
48 List all available channels for installation.
51 'update-channels' => array(
52 'summary' => 'Update the Channel List',
53 'function' => 'doUpdateAll',
57 List all installed packages in all channels.
60 'channel-delete' => array(
61 'summary' => 'Remove a Channel From the List',
62 'function' => 'doDelete',
65 'doc' => '<channel name>
66 Delete a channel from the registry. You may not
67 remove any channel that has installed packages.
70 'channel-add' => array(
71 'summary' => 'Add a Channel',
72 'function' => 'doAdd',
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
82 'channel-update' => array(
83 'summary' => 'Update an Existing Channel',
84 'function' => 'doUpdate',
89 'doc' => 'will force download of new channel.xml if an existing channel name is used',
94 'doc' => 'will force download of new channel.xml if an existing channel name is used',
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
104 'channel-info' => array(
105 'summary' => 'Retrieve Information on a Channel',
106 'function' => 'doInfo',
108 'options' => array(),
110 List the files in an installed package.
113 'channel-alias' => array(
114 'summary' => 'Specify an alias to a channel name',
115 'function' => 'doAlias',
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
124 'channel-discover' => array(
125 'summary' => 'Initialize a Channel from its server',
126 'function' => 'doDiscover',
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.
138 'channel-login' => array(
139 'summary' => 'Connects and authenticates to remote channel server',
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.',
152 'channel-logout' => array(
153 'summary' => 'Logs out from the remote channel server',
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
166 * PEAR_Command_Registry constructor.
170 function PEAR_Command_Channels(&$ui, &$config)
172 parent::PEAR_Command_Common($ui, $config);
175 function _sortChannels($a, $b)
177 return strnatcasecmp($a->getName(), $b->getName());
180 function doList($command, $options, $params)
182 $reg = &$this->config->getRegistry();
183 $registered = $reg->getChannels();
184 usort($registered, array(&$this, '_sortchannels'));
187 'caption' => 'Registered Channels:',
189 'headline' => array('Channel', 'Alias', 'Summary')
191 foreach ($registered as $channel) {
192 $data['data'][] = array($channel->getName(),
193 $channel->getAlias(),
194 $channel->getSummary());
197 if (count($registered) === 0) {
198 $data = '(no registered channels)';
200 $this->ui->outputData($data, $command);
204 function doUpdateAll($command, $options, $params)
206 $reg = &$this->config->getRegistry();
207 $channels = $reg->getChannels();
210 foreach ($channels as $channel) {
211 if ($channel->getName() != '__uri') {
212 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
213 $err = $this->doUpdate('channel-update',
215 array($channel->getName()));
216 if (PEAR::isError($err)) {
217 $this->ui->outputData($err->getMessage(), $command);
227 function doInfo($command, $options, $params)
229 if (count($params) !== 1) {
230 return $this->raiseError("No channel specified");
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);
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() . ')');
251 $contents = implode('', file($loc));
254 if (!file_exists($params[0])) {
255 return $this->raiseError('Unknown channel "' . $channel . '"');
258 $fp = fopen($params[0], 'r');
260 return $this->raiseError('Cannot open "' . $params[0] . '"');
265 $contents .= fread($fp, 1024);
270 if (!class_exists('PEAR_ChannelFile')) {
271 require_once 'PEAR/ChannelFile.php';
274 $chan = new PEAR_ChannelFile;
275 $chan->fromXmlString($contents);
277 if ($errs = $chan->getErrors(true)) {
278 foreach ($errs as $err) {
279 $this->ui->outputData($err['level'] . ': ' . $err['message']);
281 return $this->raiseError('Channel file "' . $params[0] . '" is not valid');
286 return $this->raiseError('Serious error: Channel "' . $params[0] .
287 '" has a corrupted registry entry');
290 $channel = $chan->getName();
291 $caption = 'Channel ' . $channel . ' Information:';
293 'caption' => $caption,
295 $data1['data']['server'] = array('Name and Server', $chan->getName());
296 if ($chan->getAlias() != $chan->getName()) {
297 $data1['data']['alias'] = array('Alias', $chan->getAlias());
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']);
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);
317 foreach ($funcs as $protocol) {
318 $data['data'][] = array('rest', $protocol['attribs']['type'],
319 $protocol['_content']);
323 $data['data'][] = array('No supported protocols');
326 $d['protocols'] = $data;
327 $data['data'] = array();
328 $mirrors = $chan->getMirrors();
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;
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);
348 foreach ($funcs as $protocol) {
349 $data['data'][] = array('rest', $protocol['attribs']['type'],
350 $protocol['_content']);
354 $data['data'][] = array('No supported protocols');
356 $d['mirrorprotocols' . $i] = $data;
359 $this->ui->outputData($d, 'channel-info');
364 function doDelete($command, $options, $params)
366 if (count($params) !== 1) {
367 return $this->raiseError('channel-delete: no channel specified');
370 $reg = &$this->config->getRegistry();
371 if (!$reg->channelExists($params[0])) {
372 return $this->raiseError('channel-delete: channel "' . $params[0] . '" does not exist');
375 $channel = $reg->channelName($params[0]);
376 if ($channel == 'pear.php.net') {
377 return $this->raiseError('Cannot delete the pear.php.net channel');
380 if ($channel == 'pecl.php.net') {
381 return $this->raiseError('Cannot delete the pecl.php.net channel');
384 if ($channel == 'doc.php.net') {
385 return $this->raiseError('Cannot delete the doc.php.net channel');
388 if ($channel == '__uri') {
389 return $this->raiseError('Cannot delete the __uri pseudo-channel');
392 if (PEAR::isError($err = $reg->listPackages($channel))) {
397 return $this->raiseError('Channel "' . $channel .
398 '" has installed packages, cannot delete');
401 if (!$reg->deleteChannel($channel)) {
402 return $this->raiseError('Channel "' . $channel . '" deletion failed');
404 $this->config->deleteChannel($channel);
405 $this->ui->outputData('Channel "' . $channel . '" deleted', $command);
409 function doAdd($command, $options, $params)
411 if (count($params) !== 1) {
412 return $this->raiseError('channel-add: no channel file specified');
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: "' .
426 '" - You can change this location with "pear config-set temp_dir"');
430 if (!is_writable($tmpdir)) {
431 return $this->raiseError('channel-add: temp_dir is not writable: "' .
433 '" - You can change this location with "pear config-set temp_dir"');
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() . ')');
444 list($loc, $lastmodified) = $loc;
445 $contents = implode('', file($loc));
447 $lastmodified = $fp = false;
448 if (file_exists($params[0])) {
449 $fp = fopen($params[0], 'r');
453 return $this->raiseError('channel-add: cannot open "' . $params[0] . '"');
458 $contents .= fread($fp, 1024);
463 if (!class_exists('PEAR_ChannelFile')) {
464 require_once 'PEAR/ChannelFile.php';
467 $channel = new PEAR_ChannelFile;
468 PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
469 $result = $channel->fromXmlString($contents);
470 PEAR::staticPopErrorHandling();
473 if (count($errors = $channel->getErrors(true))) {
474 foreach ($errors as $error) {
475 $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
477 $exit = $error['level'] == 'error' ? true : false;
481 return $this->raiseError('channel-add: invalid channel.xml file');
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);
492 $ret = $reg->addChannel($channel, $lastmodified);
493 if (PEAR::isError($ret)) {
498 return $this->raiseError('channel-add: adding Channel "' . $channel->getName() .
499 '" to registry failed');
502 $this->config->setChannels($reg->listChannels());
503 $this->config->writeConfigFile();
504 $this->ui->outputData('Adding Channel "' . $channel->getName() . '" succeeded', $command);
507 function doUpdate($command, $options, $params)
509 if (count($params) !== 1) {
510 return $this->raiseError("No channel file specified");
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: "' .
522 '" - You can change this location with "pear config-set temp_dir"');
526 if (!is_writable($tmpdir)) {
527 return $this->raiseError('channel-add: temp_dir is not writable: "' .
529 '" - You can change this location with "pear config-set temp_dir"');
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);
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() . ')');
563 list($contents, $lastmodified) = $contents;
565 $this->ui->outputData("Channel \"$params[0]\" is up to date");
569 $contents = implode('', file($contents));
570 if (!class_exists('PEAR_ChannelFile')) {
571 require_once 'PEAR/ChannelFile.php';
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()) . '"');
585 $this->ui->log(0, 'WARNING: downloaded channel definition file' .
586 ' for channel "' . $channel->getName() . '" from channel "' .
587 strtolower($c->getName()) . '"');
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() . ')');
602 list($loc, $lastmodified) = $loc;
603 $contents = implode('', file($loc));
606 if (file_exists($params[0])) {
607 $fp = fopen($params[0], 'r');
611 return $this->raiseError("Cannot open " . $params[0]);
616 $contents .= fread($fp, 1024);
621 if (!class_exists('PEAR_ChannelFile')) {
622 require_once 'PEAR/ChannelFile.php';
625 $channel = new PEAR_ChannelFile;
626 $channel->fromXmlString($contents);
630 if (count($errors = $channel->getErrors(true))) {
631 foreach ($errors as $error) {
632 $this->ui->outputData(ucfirst($error['level'] . ': ' . $error['message']));
634 $exit = $error['level'] == 'error' ? true : false;
638 return $this->raiseError('Invalid channel.xml file');
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');
647 $ret = $reg->updateChannel($channel, $lastmodified);
648 if (PEAR::isError($ret)) {
653 return $this->raiseError('Updating Channel "' . $channel->getName() .
654 '" in registry failed');
657 $this->config->setChannels($reg->listChannels());
658 $this->config->writeConfigFile();
659 $this->ui->outputData('Update of Channel "' . $channel->getName() . '" succeeded');
662 function &getDownloader()
664 if (!class_exists('PEAR_Downloader')) {
665 require_once 'PEAR/Downloader.php';
667 $a = new PEAR_Downloader($this->ui, array(), $this->config);
671 function doAlias($command, $options, $params)
673 if (count($params) === 1) {
674 return $this->raiseError('No channel alias specified');
677 if (count($params) !== 2 || (!empty($params[1]) && $params[1]{0} == '-')) {
678 return $this->raiseError(
679 'Invalid format, correct is: channel-alias channel alias');
682 $reg = &$this->config->getRegistry();
683 if (!$reg->channelExists($params[0], true)) {
685 if ($reg->isAlias($params[0])) {
686 $extra = ' (use "channel-alias ' . $reg->channelName($params[0]) . ' ' .
687 strtolower($params[1]) . '")';
690 return $this->raiseError('"' . $params[0] . '" is not a valid channel' . $extra);
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');
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() . ')');
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');
710 $reg->updateChannel($chan);
711 $this->ui->outputData('Channel "' . $chan->getName() . '" aliased successfully to "' .
712 strtolower($params[1]) . '"');
716 * The channel-discover command
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
726 function doDiscover($command, $options, $params)
728 if (count($params) !== 1) {
729 return $this->raiseError("No channel server specified");
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];
738 $channel = $params[0];
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);
747 return $this->raiseError("A channel alias named \"$channel\" " .
748 'already exists, aliasing channel "' . $reg->channelName($channel)
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() . ')');
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() . ')');
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);
783 $this->ui->outputData("Discovery of channel \"$channel\" succeeded", $command);
787 * Execute the 'login' command.
789 * @param string $command command name
790 * @param array $options option_name => value
791 * @param array $params list of additional parameters
793 * @return bool TRUE on success or
794 * a PEAR error on failure
798 function doLogin($command, $options, $params)
800 $reg = &$this->config->getRegistry();
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');
805 $chan = $reg->getChannel($channel);
806 if (PEAR::isError($chan)) {
807 return $this->raiseError($chan);
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;
815 $this->ui->outputData("Logging in to $server.", $command);
817 list($username, $password) = $this->ui->userDialog(
819 array('Username', 'Password'),
820 array('text', 'password'),
823 $username = trim($username);
824 $password = trim($password);
826 $ourfile = $this->config->getConfFile('user');
828 $ourfile = $this->config->getConfFile('system');
831 $this->config->set('username', $username, 'user', $channel);
832 $this->config->set('password', $password, 'user', $channel);
834 if ($chan->supportsREST()) {
839 return $this->raiseError('Login failed!');
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);
853 * Execute the 'logout' command.
855 * @param string $command command name
856 * @param array $options option_name => value
857 * @param array $params list of additional parameters
859 * @return bool TRUE on success or
860 * a PEAR error on failure
864 function doLogout($command, $options, $params)
866 $reg = &$this->config->getRegistry();
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');
871 $chan = $reg->getChannel($channel);
872 if (PEAR::isError($chan)) {
873 return $this->raiseError($chan);
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();