source: root_commands.c @ 09d4922

Last change on this file since 09d4922 was 09d4922, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-11-21T19:34:59Z

Be clearer about password settings being intentionally hidden (and not
really empty). Bug #657 and confusing BitlBee users since probably 2002.

  • Property mode set to 100644
File size: 34.6 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* User manager (root) commands                                         */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
22  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
23  Suite 330, Boston, MA  02111-1307  USA
24*/
25
26#define BITLBEE_CORE
27#include "commands.h"
28#include "bitlbee.h"
29#include "help.h"
30#include "ipc.h"
31
32void root_command_string( irc_t *irc, char *command )
33{
34        root_command( irc, split_command_parts( command ) );
35}
36
37#define MIN_ARGS( x, y... )                                                    \
38        do                                                                     \
39        {                                                                      \
40                int blaat;                                                     \
41                for( blaat = 0; blaat <= x; blaat ++ )                         \
42                        if( cmd[blaat] == NULL )                               \
43                        {                                                      \
44                                irc_usermsg( irc, "Not enough parameters given (need %d).", x ); \
45                                return y;                                      \
46                        }                                                      \
47        } while( 0 )
48
49void root_command( irc_t *irc, char *cmd[] )
50{       
51        int i, len;
52       
53        if( !cmd[0] )
54                return;
55       
56        len = strlen( cmd[0] );
57        for( i = 0; root_commands[i].command; i++ )
58                if( g_strncasecmp( root_commands[i].command, cmd[0], len ) == 0 )
59                {
60                        if( root_commands[i+1].command &&
61                            g_strncasecmp( root_commands[i+1].command, cmd[0], len ) == 0 )
62                                /* Only match on the first letters if the match is unique. */
63                                break;
64                       
65                        MIN_ARGS( root_commands[i].required_parameters );
66                       
67                        root_commands[i].execute( irc, cmd );
68                        return;
69                }
70       
71        irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] );
72}
73
74static void cmd_help( irc_t *irc, char **cmd )
75{
76        char param[80];
77        int i;
78        char *s;
79       
80        memset( param, 0, sizeof(param) );
81        for ( i = 1; (cmd[i] != NULL && ( strlen(param) < (sizeof(param)-1) ) ); i++ ) {
82                if ( i != 1 )   // prepend space except for the first parameter
83                        strcat(param, " ");
84                strncat( param, cmd[i], sizeof(param) - strlen(param) - 1 );
85        }
86
87        s = help_get( &(global.help), param );
88        if( !s ) s = help_get( &(global.help), "" );
89       
90        if( s )
91        {
92                irc_usermsg( irc, "%s", s );
93                g_free( s );
94        }
95        else
96        {
97                irc_usermsg( irc, "Error opening helpfile." );
98        }
99}
100
101static void cmd_account( irc_t *irc, char **cmd );
102static void bitlbee_whatsnew( irc_t *irc );
103
104static void cmd_identify( irc_t *irc, char **cmd )
105{
106        storage_status_t status;
107        gboolean load = TRUE;
108        char *password = cmd[1];
109       
110        if( irc->status & USTATUS_IDENTIFIED )
111        {
112                irc_usermsg( irc, "You're already logged in." );
113                return;
114        }
115       
116        if( strncmp( cmd[1], "-no", 3 ) == 0 )
117        {
118                load = FALSE;
119                password = cmd[2];
120        }
121        else if( strncmp( cmd[1], "-force", 6 ) == 0 )
122        {
123                password = cmd[2];
124        }
125        else if( irc->b->accounts != NULL )
126        {
127                irc_usermsg( irc,
128                             "You're trying to identify yourself, but already have "
129                             "at least one IM account set up. "
130                             "Use \x02identify -noload\x02 or \x02identify -force\x02 "
131                             "instead (see \x02help identify\x02)." );
132                return;
133        }
134       
135        if( password == NULL )
136        {
137                MIN_ARGS( 2 );
138        }
139       
140        if( load )
141                status = storage_load( irc, password );
142        else
143                status = storage_check_pass( irc->user->nick, password );
144       
145        switch (status) {
146        case STORAGE_INVALID_PASSWORD:
147                irc_usermsg( irc, "Incorrect password" );
148                break;
149        case STORAGE_NO_SUCH_USER:
150                irc_usermsg( irc, "The nick is (probably) not registered" );
151                break;
152        case STORAGE_OK:
153                irc_usermsg( irc, "Password accepted%s",
154                             load ? ", settings and accounts loaded" : "" );
155                irc_setpass( irc, password );
156                irc->status |= USTATUS_IDENTIFIED;
157                irc_umode_set( irc, "+R", 1 );
158               
159                bitlbee_whatsnew( irc );
160               
161                /* The following code is a bit hairy now. With takeover
162                   support, we shouldn't immediately auto_connect in case
163                   we're going to offer taking over an existing session.
164                   Do it in 200ms since that should give the parent process
165                   enough time to come back to us. */
166                if( load )
167                {
168                        irc_channel_auto_joins( irc, NULL );
169                        if( !set_getbool( &irc->default_channel->set, "auto_join" ) )
170                                irc_channel_del_user( irc->default_channel, irc->user,
171                                                      IRC_CDU_PART, "auto_join disabled "
172                                                      "for this channel." );
173                        if( set_getbool( &irc->b->set, "auto_connect" ) )
174                                irc->login_source_id = b_timeout_add( 200,
175                                        cmd_identify_finish, irc );
176                }
177               
178                /* If ipc_child_identify() returns FALSE, it means we're
179                   already sure that there's no takeover target (only
180                   possible in 1-process daemon mode). Start auto_connect
181                   immediately. */
182                if( !ipc_child_identify( irc ) && load &&
183                    set_getbool( &irc->b->set, "auto_connect" ) )
184                        cmd_identify_finish( irc, 0, 0 );
185               
186                break;
187        case STORAGE_OTHER_ERROR:
188        default:
189                irc_usermsg( irc, "Unknown error while loading configuration" );
190                break;
191        }
192}
193
194gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond )
195{
196        char *account_on[] = { "account", "on", NULL };
197        irc_t *irc = data;
198       
199        cmd_account( irc, account_on );
200       
201        b_event_remove( irc->login_source_id );
202        irc->login_source_id = -1;
203        return FALSE;
204}
205
206static void cmd_register( irc_t *irc, char **cmd )
207{
208        char s[16];
209       
210        if( global.conf->authmode == AUTHMODE_REGISTERED )
211        {
212                irc_usermsg( irc, "This server does not allow registering new accounts" );
213                return;
214        }
215
216        switch( storage_save( irc, cmd[1], FALSE ) ) {
217                case STORAGE_ALREADY_EXISTS:
218                        irc_usermsg( irc, "Nick is already registered" );
219                        break;
220                       
221                case STORAGE_OK:
222                        irc_usermsg( irc, "Account successfully created" );
223                        irc_setpass( irc, cmd[1] );
224                        irc->status |= USTATUS_IDENTIFIED;
225                        irc_umode_set( irc, "+R", 1 );
226                       
227                        /* Set this var now, or anyone who logs in to his/her
228                           newly created account for the first time gets the
229                           whatsnew story. */
230                        g_snprintf( s, sizeof( s ), "%d", BITLBEE_VERSION_CODE );
231                        set_setstr( &irc->b->set, "last_version", s );
232                        break;
233
234                default:
235                        irc_usermsg( irc, "Error registering" );
236                        break;
237        }
238}
239
240static void cmd_drop( irc_t *irc, char **cmd )
241{
242        storage_status_t status;
243       
244        status = storage_remove (irc->user->nick, cmd[1]);
245        switch (status) {
246        case STORAGE_NO_SUCH_USER:
247                irc_usermsg( irc, "That account does not exist" );
248                break;
249        case STORAGE_INVALID_PASSWORD:
250                irc_usermsg( irc, "Password invalid" );
251                break;
252        case STORAGE_OK:
253                irc_setpass( irc, NULL );
254                irc->status &= ~USTATUS_IDENTIFIED;
255                irc_umode_set( irc, "-R", 1 );
256                irc_usermsg( irc, "Account `%s' removed", irc->user->nick );
257                break;
258        default:
259                irc_usermsg( irc, "Error: `%d'", status );
260                break;
261        }
262}
263
264static void cmd_save( irc_t *irc, char **cmd )
265{
266        if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
267                irc_usermsg( irc, "Please create an account first" );
268        else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
269                irc_usermsg( irc, "Configuration saved" );
270        else
271                irc_usermsg( irc, "Configuration could not be saved!" );
272}
273
274static void cmd_showset( irc_t *irc, set_t **head, char *key )
275{
276        set_t *set;
277        char *val;
278       
279        if( ( val = set_getstr( head, key ) ) )
280                irc_usermsg( irc, "%s = `%s'", key, val );
281        else if( !( set = set_find( head, key ) ) )
282        {
283                irc_usermsg( irc, "Setting `%s' does not exist.", key );
284                if( *head == irc->b->set )
285                        irc_usermsg( irc, "It might be an account or channel setting. "
286                                     "See \x02help account set\x02 and \x02help channel set\x02." );
287        }
288        else if( set->flags & SET_PASSWORD )
289                irc_usermsg( irc, "%s = `********' (hidden)", key );
290        else
291                irc_usermsg( irc, "%s is empty", key );
292}
293
294typedef set_t** (*cmd_set_findhead)( irc_t*, char* );
295typedef int (*cmd_set_checkflags)( irc_t*, set_t *set );
296
297static int cmd_set_real( irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags )
298{
299        char *set_name = NULL, *value = NULL;
300        gboolean del = FALSE;
301       
302        if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
303        {
304                MIN_ARGS( 2, 0 );
305                set_name = cmd[2];
306                del = TRUE;
307        }
308        else
309        {
310                set_name = cmd[1];
311                value = cmd[2];
312        }
313       
314        if( set_name && ( value || del ) )
315        {
316                set_t *s = set_find( head, set_name );
317                int st;
318               
319                if( s && checkflags && checkflags( irc, s ) == 0 )
320                        return 0;
321               
322                if( del )
323                        st = set_reset( head, set_name );
324                else
325                        st = set_setstr( head, set_name, value );
326               
327                if( set_getstr( head, set_name ) == NULL &&
328                    set_find( head, set_name ) )
329                {
330                        /* This happens when changing the passwd, for example.
331                           Showing these msgs instead gives slightly clearer
332                           feedback. */
333                        if( st )
334                                irc_usermsg( irc, "Setting changed successfully" );
335                        else
336                                irc_usermsg( irc, "Failed to change setting" );
337                }
338                else
339                {
340                        cmd_showset( irc, head, set_name );
341                }
342        }
343        else if( set_name )
344        {
345                cmd_showset( irc, head, set_name );
346        }
347        else
348        {
349                set_t *s = *head;
350                while( s )
351                {
352                        if( !( s->flags & SET_HIDDEN ) )
353                                cmd_showset( irc, &s, s->key );
354                        s = s->next;
355                }
356        }
357       
358        return 1;
359}
360
361static int cmd_account_set_checkflags( irc_t *irc, set_t *s )
362{
363        account_t *a = s->data;
364       
365        if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
366        {
367                irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" );
368                return 0;
369        }
370        else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
371        {
372                irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" );
373                return 0;
374        }
375       
376        return 1;
377}
378
379static void cmd_account( irc_t *irc, char **cmd )
380{
381        account_t *a;
382        int len;
383       
384        if( global.conf->authmode == AUTHMODE_REGISTERED && !( irc->status & USTATUS_IDENTIFIED ) )
385        {
386                irc_usermsg( irc, "This server only accepts registered users" );
387                return;
388        }
389       
390        len = strlen( cmd[1] );
391       
392        if( len >= 1 && g_strncasecmp( cmd[1], "add", len ) == 0 )
393        {
394                struct prpl *prpl;
395               
396                MIN_ARGS( 4 );
397               
398                prpl = find_protocol( cmd[2] );
399               
400                if( prpl == NULL )
401                {
402                        irc_usermsg( irc, "Unknown protocol" );
403                        return;
404                }
405               
406                for( a = irc->b->accounts; a; a = a->next )
407                        if( a->prpl == prpl && prpl->handle_cmp( a->user, cmd[3] ) == 0 )
408                                irc_usermsg( irc, "Warning: You already have an account with "
409                                             "protocol `%s' and username `%s'. Are you accidentally "
410                                             "trying to add it twice?", prpl->name, cmd[3] );
411               
412                a = account_add( irc->b, prpl, cmd[3], cmd[4] );
413                if( cmd[5] )
414                {
415                        irc_usermsg( irc, "Warning: Passing a servername/other flags to `account add' "
416                                          "is now deprecated. Use `account set' instead." );
417                        set_setstr( &a->set, "server", cmd[5] );
418                }
419               
420                irc_usermsg( irc, "Account successfully added" );
421               
422                return;
423        }
424        else if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
425        {
426                int i = 0;
427               
428                if( strchr( irc->umode, 'b' ) )
429                        irc_usermsg( irc, "Account list:" );
430               
431                for( a = irc->b->accounts; a; a = a->next )
432                {
433                        char *con;
434                       
435                        if( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) )
436                                con = " (connected)";
437                        else if( a->ic )
438                                con = " (connecting)";
439                        else if( a->reconnect )
440                                con = " (awaiting reconnect)";
441                        else
442                                con = "";
443                       
444                        irc_usermsg( irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con );
445                       
446                        i ++;
447                }
448                irc_usermsg( irc, "End of account list" );
449               
450                return;
451        }
452        else if( cmd[2] )
453        {
454                /* Try the following two only if cmd[2] == NULL */
455        }
456        else if( len >= 2 && g_strncasecmp( cmd[1], "on", len ) == 0 )
457        {
458                if ( irc->b->accounts )
459                {
460                        irc_usermsg( irc, "Trying to get all accounts connected..." );
461               
462                        for( a = irc->b->accounts; a; a = a->next )
463                                if( !a->ic && a->auto_connect )
464                                        account_on( irc->b, a );
465                } 
466                else
467                {
468                        irc_usermsg( irc, "No accounts known. Use `account add' to add one." );
469                }
470               
471                return;
472        }
473        else if( len >= 2 && g_strncasecmp( cmd[1], "off", len ) == 0 )
474        {
475                irc_usermsg( irc, "Deactivating all active (re)connections..." );
476               
477                for( a = irc->b->accounts; a; a = a->next )
478                {
479                        if( a->ic )
480                                account_off( irc->b, a );
481                        else if( a->reconnect )
482                                cancel_auto_reconnect( a );
483                }
484               
485                return;
486        }
487       
488        MIN_ARGS( 2 );
489        len = strlen( cmd[2] );
490       
491        /* At least right now, don't accept on/off/set/del as account IDs even
492           if they're a proper match, since people not familiar with the new
493           syntax yet may get a confusing/nasty surprise. */
494        if( g_strcasecmp( cmd[1], "on" ) == 0 ||
495            g_strcasecmp( cmd[1], "off" ) == 0 ||
496            g_strcasecmp( cmd[1], "set" ) == 0 ||
497            g_strcasecmp( cmd[1], "del" ) == 0 ||
498            ( a = account_get( irc->b, cmd[1] ) ) == NULL )
499        {
500                irc_usermsg( irc, "Could not find account `%s'. Note that the syntax "
501                             "of the account command changed, see \x02help account\x02.", cmd[1] );
502               
503                return;
504        }
505       
506        if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
507        {
508                if( a->ic )
509                {
510                        irc_usermsg( irc, "Account is still logged in, can't delete" );
511                }
512                else
513                {
514                        account_del( irc->b, a );
515                        irc_usermsg( irc, "Account deleted" );
516                }
517        }
518        else if( len >= 2 && g_strncasecmp( cmd[2], "on", len ) == 0 )
519        {
520                if( a->ic )
521                        irc_usermsg( irc, "Account already online" );
522                else
523                        account_on( irc->b, a );
524        }
525        else if( len >= 2 && g_strncasecmp( cmd[2], "off", len ) == 0 )
526        {
527                if( a->ic )
528                {
529                        account_off( irc->b, a );
530                }
531                else if( a->reconnect )
532                {
533                        cancel_auto_reconnect( a );
534                        irc_usermsg( irc, "Reconnect cancelled" );
535                }
536                else
537                {
538                        irc_usermsg( irc, "Account already offline" );
539                }
540        }
541        else if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
542        {
543                cmd_set_real( irc, cmd + 2, &a->set, cmd_account_set_checkflags );
544        }
545        else
546        {
547                irc_usermsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[2] );
548        }
549}
550
551static void cmd_channel( irc_t *irc, char **cmd )
552{
553        irc_channel_t *ic;
554        int len;
555       
556        len = strlen( cmd[1] );
557       
558        if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
559        {
560                GSList *l;
561                int i = 0;
562               
563                if( strchr( irc->umode, 'b' ) )
564                        irc_usermsg( irc, "Channel list:" );
565               
566                for( l = irc->channels; l; l = l->next )
567                {
568                        irc_channel_t *ic = l->data;
569                       
570                        irc_usermsg( irc, "%2d. %s, %s channel%s", i, ic->name,
571                                     set_getstr( &ic->set, "type" ),
572                                     ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "" );
573                       
574                        i ++;
575                }
576                irc_usermsg( irc, "End of channel list" );
577               
578                return;
579        }
580       
581        if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL )
582        {
583                /* If this doesn't match any channel, maybe this is the short
584                   syntax (only works when used inside a channel). */
585                if( ( ic = irc->root->last_channel ) &&
586                    ( len = strlen( cmd[1] ) ) &&
587                    g_strncasecmp( cmd[1], "set", len ) == 0 )
588                        cmd_set_real( irc, cmd + 1, &ic->set, NULL );
589                else
590                        irc_usermsg( irc, "Could not find channel `%s'", cmd[1] );
591               
592                return;
593        }
594       
595        MIN_ARGS( 2 );
596        len = strlen( cmd[2] );
597       
598        if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
599        {
600                cmd_set_real( irc, cmd + 2, &ic->set, NULL );
601        }
602        else if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
603        {
604                if( !( ic->flags & IRC_CHANNEL_JOINED ) &&
605                    ic != ic->irc->default_channel )
606                {
607                        irc_usermsg( irc, "Channel %s deleted.", ic->name );
608                        irc_channel_free( ic );
609                }
610                else
611                        irc_usermsg( irc, "Couldn't remove channel (main channel %s or "
612                                          "channels you're still in cannot be deleted).",
613                                          irc->default_channel->name );
614        }
615        else
616        {
617                irc_usermsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] );
618        }
619}
620
621static void cmd_add( irc_t *irc, char **cmd )
622{
623        account_t *a;
624        int add_on_server = 1;
625       
626        if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
627        {
628                MIN_ARGS( 3 );
629                add_on_server = 0;
630                cmd ++;
631        }
632       
633        if( !( a = account_get( irc->b, cmd[1] ) ) )
634        {
635                irc_usermsg( irc, "Invalid account" );
636                return;
637        }
638        else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
639        {
640                irc_usermsg( irc, "That account is not on-line" );
641                return;
642        }
643       
644        if( cmd[3] )
645        {
646                if( !nick_ok( cmd[3] ) )
647                {
648                        irc_usermsg( irc, "The requested nick `%s' is invalid", cmd[3] );
649                        return;
650                }
651                else if( irc_user_by_name( irc, cmd[3] ) )
652                {
653                        irc_usermsg( irc, "The requested nick `%s' already exists", cmd[3] );
654                        return;
655                }
656                else
657                {
658                        nick_set_raw( a, cmd[2], cmd[3] );
659                }
660        }
661       
662        if( add_on_server )
663        {
664                irc_channel_t *ic;
665                char *s, *group = NULL;;
666               
667                if( ( ic = irc->root->last_channel ) &&
668                    ( s = set_getstr( &ic->set, "fill_by" ) ) &&
669                    strcmp( s, "group" ) == 0 &&
670                    ( group = set_getstr( &ic->set, "group" ) ) )
671                        irc_usermsg( irc, "Adding `%s' to contact list (group %s)",
672                                     cmd[2], group );
673                else
674                        irc_usermsg( irc, "Adding `%s' to contact list", cmd[2] );
675               
676                a->prpl->add_buddy( a->ic, cmd[2], group );
677        }
678        else
679        {
680                bee_user_t *bu;
681                irc_user_t *iu;
682               
683                /* Only for add -tmp. For regular adds, this callback will
684                   be called once the IM server confirms. */
685                if( ( bu = bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ) ) &&
686                    ( iu = bu->ui_data ) )
687                        irc_usermsg( irc, "Temporarily assigned nickname `%s' "
688                                     "to contact `%s'", iu->nick, cmd[2] );
689        }
690       
691}
692
693static void cmd_remove( irc_t *irc, char **cmd )
694{
695        irc_user_t *iu;
696        bee_user_t *bu;
697        char *s;
698       
699        if( !( iu = irc_user_by_name( irc, cmd[1] ) ) || !( bu = iu->bu ) )
700        {
701                irc_usermsg( irc, "Buddy `%s' not found", cmd[1] );
702                return;
703        }
704        s = g_strdup( bu->handle );
705       
706        bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL );
707        nick_del( bu );
708        if( g_slist_find( irc->users, iu ) )
709                bee_user_free( irc->b, bu );
710       
711        irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] );
712        g_free( s );
713       
714        return;
715}
716
717static void cmd_info( irc_t *irc, char **cmd )
718{
719        struct im_connection *ic;
720        account_t *a;
721       
722        if( !cmd[2] )
723        {
724                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
725                if( !iu || !iu->bu )
726                {
727                        irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
728                        return;
729                }
730                ic = iu->bu->ic;
731                cmd[2] = iu->bu->handle;
732        }
733        else if( !( a = account_get( irc->b, cmd[1] ) ) )
734        {
735                irc_usermsg( irc, "Invalid account" );
736                return;
737        }
738        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
739        {
740                irc_usermsg( irc, "That account is not on-line" );
741                return;
742        }
743       
744        if( !ic->acc->prpl->get_info )
745        {
746                irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
747        }
748        else
749        {
750                ic->acc->prpl->get_info( ic, cmd[2] );
751        }
752}
753
754static void cmd_rename( irc_t *irc, char **cmd )
755{
756        irc_user_t *iu, *old;
757       
758        iu = irc_user_by_name( irc, cmd[1] );
759       
760        if( iu == NULL )
761        {
762                irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
763        }
764        else if( iu == irc->user )
765        {
766                irc_usermsg( irc, "Use /nick to change your own nickname" );
767        }
768        else if( !nick_ok( cmd[2] ) )
769        {
770                irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] );
771        }
772        else if( ( old = irc_user_by_name( irc, cmd[2] ) ) && old != iu )
773        {
774                irc_usermsg( irc, "Nick `%s' already exists", cmd[2] );
775        }
776        else
777        {
778                if( !irc_user_set_nick( iu, cmd[2] ) )
779                {
780                        irc_usermsg( irc, "Error while changing nick" );
781                        return;
782                }
783               
784                if( iu == irc->root )
785                {
786                        /* If we're called internally (user did "set root_nick"),
787                           let's not go O(INF). :-) */
788                        if( strcmp( cmd[0], "set_rename" ) != 0 )
789                                set_setstr( &irc->b->set, "root_nick", cmd[2] );
790                }
791                else if( iu->bu )
792                {
793                        nick_set( iu->bu, cmd[2] );
794                }
795               
796                irc_usermsg( irc, "Nick successfully changed" );
797        }
798}
799
800char *set_eval_root_nick( set_t *set, char *new_nick )
801{
802        irc_t *irc = set->data;
803       
804        if( strcmp( irc->root->nick, new_nick ) != 0 )
805        {
806                char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL };
807               
808                cmd_rename( irc, cmd );
809        }
810       
811        return strcmp( irc->root->nick, new_nick ) == 0 ? new_nick : SET_INVALID;
812}
813
814static void cmd_block( irc_t *irc, char **cmd )
815{
816        struct im_connection *ic;
817        account_t *a;
818       
819        if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
820        {
821                char *format;
822                GSList *l;
823               
824                if( strchr( irc->umode, 'b' ) != NULL )
825                        format = "%s\t%s";
826                else
827                        format = "%-32.32s  %-16.16s";
828               
829                irc_usermsg( irc, format, "Handle", "Nickname" );
830                for( l = a->ic->deny; l; l = l->next )
831                {
832                        bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data );
833                        irc_user_t *iu = bu ? bu->ui_data : NULL;
834                        irc_usermsg( irc, format, l->data, iu ? iu->nick : "(none)" );
835                }
836                irc_usermsg( irc, "End of list." );
837               
838                return;
839        }
840        else if( !cmd[2] )
841        {
842                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
843                if( !iu || !iu->bu )
844                {
845                        irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
846                        return;
847                }
848                ic = iu->bu->ic;
849                cmd[2] = iu->bu->handle;
850        }
851        else if( !( a = account_get( irc->b, cmd[1] ) ) )
852        {
853                irc_usermsg( irc, "Invalid account" );
854                return;
855        }
856        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
857        {
858                irc_usermsg( irc, "That account is not on-line" );
859                return;
860        }
861       
862        if( !ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit )
863        {
864                irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
865        }
866        else
867        {
868                imc_rem_allow( ic, cmd[2] );
869                imc_add_block( ic, cmd[2] );
870                irc_usermsg( irc, "Buddy `%s' moved from allow- to block-list", cmd[2] );
871        }
872}
873
874static void cmd_allow( irc_t *irc, char **cmd )
875{
876        struct im_connection *ic;
877        account_t *a;
878       
879        if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
880        {
881                char *format;
882                GSList *l;
883               
884                if( strchr( irc->umode, 'b' ) != NULL )
885                        format = "%s\t%s";
886                else
887                        format = "%-32.32s  %-16.16s";
888               
889                irc_usermsg( irc, format, "Handle", "Nickname" );
890                for( l = a->ic->permit; l; l = l->next )
891                {
892                        bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data );
893                        irc_user_t *iu = bu ? bu->ui_data : NULL;
894                        irc_usermsg( irc, format, l->data, iu ? iu->nick : "(none)" );
895                }
896                irc_usermsg( irc, "End of list." );
897               
898                return;
899        }
900        else if( !cmd[2] )
901        {
902                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
903                if( !iu || !iu->bu )
904                {
905                        irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
906                        return;
907                }
908                ic = iu->bu->ic;
909                cmd[2] = iu->bu->handle;
910        }
911        else if( !( a = account_get( irc->b, cmd[1] ) ) )
912        {
913                irc_usermsg( irc, "Invalid account" );
914                return;
915        }
916        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
917        {
918                irc_usermsg( irc, "That account is not on-line" );
919                return;
920        }
921       
922        if( !ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit )
923        {
924                irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
925        }
926        else
927        {
928                imc_rem_block( ic, cmd[2] );
929                imc_add_allow( ic, cmd[2] );
930               
931                irc_usermsg( irc, "Buddy `%s' moved from block- to allow-list", cmd[2] );
932        }
933}
934
935static void cmd_yesno( irc_t *irc, char **cmd )
936{
937        query_t *q = NULL;
938        int numq = 0;
939       
940        if( irc->queries == NULL )
941        {
942                /* Alright, alright, let's add a tiny easter egg here. */
943                static irc_t *last_irc = NULL;
944                static time_t last_time = 0;
945                static int times = 0;
946                static const char *msg[] = {
947                        "Oh yeah, that's right.",
948                        "Alright, alright. Now go back to work.",
949                        "Buuuuuuuuuuuuuuuurp... Excuse me!",
950                        "Yes?",
951                        "No?",
952                };
953               
954                if( last_irc == irc && time( NULL ) - last_time < 15 )
955                {
956                        if( ( ++times >= 3 ) )
957                        {
958                                irc_usermsg( irc, "%s", msg[rand()%(sizeof(msg)/sizeof(char*))] );
959                                last_irc = NULL;
960                                times = 0;
961                                return;
962                        }
963                }
964                else
965                {
966                        last_time = time( NULL );
967                        last_irc = irc;
968                        times = 0;
969                }
970               
971                irc_usermsg( irc, "Did I ask you something?" );
972                return;
973        }
974       
975        /* If there's an argument, the user seems to want to answer another question than the
976           first/last (depending on the query_order setting) one. */
977        if( cmd[1] )
978        {
979                if( sscanf( cmd[1], "%d", &numq ) != 1 )
980                {
981                        irc_usermsg( irc, "Invalid query number" );
982                        return;
983                }
984               
985                for( q = irc->queries; q; q = q->next, numq -- )
986                        if( numq == 0 )
987                                break;
988               
989                if( !q )
990                {
991                        irc_usermsg( irc, "Uhm, I never asked you something like that..." );
992                        return;
993                }
994        }
995       
996        if( g_strcasecmp( cmd[0], "yes" ) == 0 )
997                query_answer( irc, q, 1 );
998        else if( g_strcasecmp( cmd[0], "no" ) == 0 )
999                query_answer( irc, q, 0 );
1000}
1001
1002static void cmd_set( irc_t *irc, char **cmd )
1003{
1004        cmd_set_real( irc, cmd, &irc->b->set, NULL );
1005}
1006
1007static void cmd_blist( irc_t *irc, char **cmd )
1008{
1009        int online = 0, away = 0, offline = 0;
1010        GSList *l;
1011        char s[256];
1012        char *format;
1013        int n_online = 0, n_away = 0, n_offline = 0;
1014       
1015        if( cmd[1] && g_strcasecmp( cmd[1], "all" ) == 0 )
1016                online = offline = away = 1;
1017        else if( cmd[1] && g_strcasecmp( cmd[1], "offline" ) == 0 )
1018                offline = 1;
1019        else if( cmd[1] && g_strcasecmp( cmd[1], "away" ) == 0 )
1020                away = 1;
1021        else if( cmd[1] && g_strcasecmp( cmd[1], "online" ) == 0 )
1022                online = 1;
1023        else
1024                online = away = 1;
1025       
1026        if( strchr( irc->umode, 'b' ) != NULL )
1027                format = "%s\t%s\t%s";
1028        else
1029                format = "%-16.16s  %-40.40s  %s";
1030       
1031        irc_usermsg( irc, format, "Nick", "Handle/Account", "Status" );
1032       
1033        if( irc->root->last_channel &&
1034            strcmp( set_getstr( &irc->root->last_channel->set, "type" ), "control" ) != 0 )
1035                irc->root->last_channel = NULL;
1036       
1037        for( l = irc->users; l; l = l->next )
1038        {
1039                irc_user_t *iu = l->data;
1040                bee_user_t *bu = iu->bu;
1041               
1042                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1043                    ( bu->flags & ( BEE_USER_ONLINE | BEE_USER_AWAY ) ) != BEE_USER_ONLINE )
1044                        continue;
1045               
1046                if( online == 1 )
1047                {
1048                        char st[256] = "Online";
1049                       
1050                        if( bu->status_msg )
1051                                g_snprintf( st, sizeof( st ) - 1, "Online (%s)", bu->status_msg );
1052                       
1053                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
1054                        irc_usermsg( irc, format, iu->nick, s, st );
1055                }
1056               
1057                n_online ++;
1058        }
1059
1060        for( l = irc->users; l; l = l->next )
1061        {
1062                irc_user_t *iu = l->data;
1063                bee_user_t *bu = iu->bu;
1064               
1065                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1066                    !( bu->flags & BEE_USER_ONLINE ) || !( bu->flags & BEE_USER_AWAY ) )
1067                        continue;
1068               
1069                if( away == 1 )
1070                {
1071                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
1072                        irc_usermsg( irc, format, iu->nick, s, irc_user_get_away( iu ) );
1073                }
1074                n_away ++;
1075        }
1076       
1077        for( l = irc->users; l; l = l->next )
1078        {
1079                irc_user_t *iu = l->data;
1080                bee_user_t *bu = iu->bu;
1081               
1082                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1083                    bu->flags & BEE_USER_ONLINE )
1084                        continue;
1085               
1086                if( offline == 1 )
1087                {
1088                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
1089                        irc_usermsg( irc, format, iu->nick, s, "Offline" );
1090                }
1091                n_offline ++;
1092        }
1093       
1094        irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline );
1095}
1096
1097static void cmd_qlist( irc_t *irc, char **cmd )
1098{
1099        query_t *q = irc->queries;
1100        int num;
1101       
1102        if( !q )
1103        {
1104                irc_usermsg( irc, "There are no pending questions." );
1105                return;
1106        }
1107       
1108        irc_usermsg( irc, "Pending queries:" );
1109       
1110        for( num = 0; q; q = q->next, num ++ )
1111                if( q->ic ) /* Not necessary yet, but it might come later */
1112                        irc_usermsg( irc, "%d, %s(%s): %s", num, q->ic->acc->prpl->name, q->ic->acc->user, q->question );
1113                else
1114                        irc_usermsg( irc, "%d, BitlBee: %s", num, q->question );
1115}
1116
1117static void cmd_chat( irc_t *irc, char **cmd )
1118{
1119        account_t *acc;
1120       
1121        if( g_strcasecmp( cmd[1], "add" ) == 0 )
1122        {
1123                char *channel, *s;
1124                struct irc_channel *ic;
1125               
1126                MIN_ARGS( 3 );
1127               
1128                if( !( acc = account_get( irc->b, cmd[2] ) ) )
1129                {
1130                        irc_usermsg( irc, "Invalid account" );
1131                        return;
1132                }
1133                else if( !acc->prpl->chat_join )
1134                {
1135                        irc_usermsg( irc, "Named chatrooms not supported on that account." );
1136                        return;
1137                }
1138               
1139                if( cmd[4] == NULL )
1140                {
1141                        channel = g_strdup( cmd[3] );
1142                        if( ( s = strchr( channel, '@' ) ) )
1143                                *s = 0;
1144                }
1145                else
1146                {
1147                        channel = g_strdup( cmd[4] );
1148                }
1149               
1150                if( strchr( CTYPES, channel[0] ) == NULL )
1151                {
1152                        s = g_strdup_printf( "#%s", channel );
1153                        g_free( channel );
1154                        channel = s;
1155                       
1156                        irc_channel_name_strip( channel );
1157                }
1158               
1159                if( ( ic = irc_channel_new( irc, channel ) ) &&
1160                    set_setstr( &ic->set, "type", "chat" ) &&
1161                    set_setstr( &ic->set, "chat_type", "room" ) &&
1162                    set_setstr( &ic->set, "account", cmd[2] ) &&
1163                    set_setstr( &ic->set, "room", cmd[3] ) )
1164                {
1165                        irc_usermsg( irc, "Chatroom successfully added." );
1166                }
1167                else
1168                {
1169                        if( ic )
1170                                irc_channel_free( ic );
1171                       
1172                        irc_usermsg( irc, "Could not add chatroom." );
1173                }
1174                g_free( channel );
1175        }
1176        else if( g_strcasecmp( cmd[1], "with" ) == 0 )
1177        {
1178                irc_user_t *iu;
1179               
1180                MIN_ARGS( 2 );
1181               
1182                if( ( iu = irc_user_by_name( irc, cmd[2] ) ) &&
1183                    iu->bu && iu->bu->ic->acc->prpl->chat_with )
1184                {
1185                        if( !iu->bu->ic->acc->prpl->chat_with( iu->bu->ic, iu->bu->handle ) )
1186                        {
1187                                irc_usermsg( irc, "(Possible) failure while trying to open "
1188                                                  "a groupchat with %s.", iu->nick );
1189                        }
1190                }
1191                else
1192                {
1193                        irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] );
1194                }
1195        }
1196        else if( g_strcasecmp( cmd[1], "list" ) == 0 ||
1197                 g_strcasecmp( cmd[1], "set" ) == 0 ||
1198                 g_strcasecmp( cmd[1], "del" ) == 0 )
1199        {
1200                irc_usermsg( irc, "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command." );
1201                cmd_channel( irc, cmd );
1202        }
1203        else
1204        {
1205                irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
1206        }
1207}
1208
1209static void cmd_group( irc_t *irc, char **cmd )
1210{
1211        GSList *l;
1212        int len;
1213       
1214        len = strlen( cmd[1] );
1215        if( g_strncasecmp( cmd[1], "list", len ) == 0 )
1216        {
1217                int n = 0;
1218               
1219                if( strchr( irc->umode, 'b' ) )
1220                        irc_usermsg( irc, "Group list:" );
1221               
1222                for( l = irc->b->groups; l; l = l->next )
1223                {
1224                        bee_group_t *bg = l->data;
1225                        irc_usermsg( irc, "%d. %s", n ++, bg->name );
1226                }
1227                irc_usermsg( irc, "End of group list" );
1228        }
1229        else
1230        {
1231                irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group", cmd[1] );
1232        }
1233}
1234
1235static void cmd_transfer( irc_t *irc, char **cmd )
1236{
1237        GSList *files = irc->file_transfers;
1238        enum { LIST, REJECT, CANCEL };
1239        int subcmd = LIST;
1240        int fid;
1241
1242        if( !files )
1243        {
1244                irc_usermsg( irc, "No pending transfers" );
1245                return;
1246        }
1247
1248        if( cmd[1] && ( strcmp( cmd[1], "reject" ) == 0 ) )
1249        {
1250                subcmd = REJECT;
1251        }
1252        else if( cmd[1] && ( strcmp( cmd[1], "cancel" ) == 0 ) && 
1253                 cmd[2] && ( sscanf( cmd[2], "%d", &fid ) == 1 ) )
1254        {
1255                subcmd = CANCEL;
1256        }
1257
1258        for( ; files; files = g_slist_next( files ) )
1259        {
1260                file_transfer_t *file = files->data;
1261               
1262                switch( subcmd ) {
1263                case LIST:
1264                        if ( file->status == FT_STATUS_LISTENING )
1265                                irc_usermsg( irc, 
1266                                        "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name);
1267                        else 
1268                        {
1269                                int kb_per_s = 0;
1270                                time_t diff = time( NULL ) - file->started ? : 1;
1271                                if ( ( file->started > 0 ) && ( file->bytes_transferred > 0 ) )
1272                                        kb_per_s = file->bytes_transferred / 1024 / diff;
1273                                       
1274                                irc_usermsg( irc, 
1275                                        "Pending file(id %d): %s (%10zd/%zd kb, %d kb/s)", file->local_id, file->file_name, 
1276                                        file->bytes_transferred/1024, file->file_size/1024, kb_per_s);
1277                        }
1278                        break;
1279                case REJECT:
1280                        if( file->status == FT_STATUS_LISTENING )
1281                        {
1282                                irc_usermsg( irc, "Rejecting file transfer for %s", file->file_name );
1283                                imcb_file_canceled( file->ic, file, "Denied by user" );
1284                        }
1285                        break;
1286                case CANCEL:
1287                        if( file->local_id == fid )
1288                        {
1289                                irc_usermsg( irc, "Canceling file transfer for %s", file->file_name );
1290                                imcb_file_canceled( file->ic, file, "Canceled by user" );
1291                        }
1292                        break;
1293                }
1294        }
1295}
1296
1297static void cmd_nick( irc_t *irc, char **cmd )
1298{
1299        irc_usermsg( irc, "This command is deprecated. Try: account %s set display_name", cmd[1] );
1300}
1301
1302/* Maybe this should be a stand-alone command as well? */
1303static void bitlbee_whatsnew( irc_t *irc )
1304{
1305        int last = set_getint( &irc->b->set, "last_version" );
1306        char s[16], *msg;
1307       
1308        if( last >= BITLBEE_VERSION_CODE )
1309                return;
1310       
1311        msg = help_get_whatsnew( &(global.help), last );
1312       
1313        if( msg )
1314                irc_usermsg( irc, "%s: This seems to be your first time using this "
1315                                  "this version of BitlBee. Here's a list of new "
1316                                  "features you may like to know about:\n\n%s\n",
1317                                  irc->user->nick, msg );
1318       
1319        g_free( msg );
1320       
1321        g_snprintf( s, sizeof( s ), "%d", BITLBEE_VERSION_CODE );
1322        set_setstr( &irc->b->set, "last_version", s );
1323}
1324
1325/* IMPORTANT: Keep this list sorted! The short command logic needs that. */
1326command_t root_commands[] = {
1327        { "account",        1, cmd_account,        0 },
1328        { "add",            2, cmd_add,            0 },
1329        { "allow",          1, cmd_allow,          0 },
1330        { "blist",          0, cmd_blist,          0 },
1331        { "block",          1, cmd_block,          0 },
1332        { "channel",        1, cmd_channel,        0 },
1333        { "chat",           1, cmd_chat,           0 },
1334        { "drop",           1, cmd_drop,           0 },
1335        { "ft",             0, cmd_transfer,       0 },
1336        { "group",          1, cmd_group,          0 },
1337        { "help",           0, cmd_help,           0 }, 
1338        { "identify",       1, cmd_identify,       0 },
1339        { "info",           1, cmd_info,           0 },
1340        { "nick",           1, cmd_nick,           0 },
1341        { "no",             0, cmd_yesno,          0 },
1342        { "qlist",          0, cmd_qlist,          0 },
1343        { "register",       1, cmd_register,       0 },
1344        { "remove",         1, cmd_remove,         0 },
1345        { "rename",         2, cmd_rename,         0 },
1346        { "save",           0, cmd_save,           0 },
1347        { "set",            0, cmd_set,            0 },
1348        { "transfer",       0, cmd_transfer,       0 },
1349        { "yes",            0, cmd_yesno,          0 },
1350        /* Not expecting too many plugins adding root commands so just make a
1351           dumb array with some empty entried at the end. */
1352        { NULL },
1353        { NULL },
1354        { NULL },
1355        { NULL },
1356        { NULL },
1357        { NULL },
1358        { NULL },
1359        { NULL },
1360        { NULL },
1361};
1362static const int num_root_commands = sizeof( root_commands ) / sizeof( command_t );
1363
1364gboolean root_command_add( const char *command, int params, void (*func)(irc_t *, char **args), int flags )
1365{
1366        int i;
1367       
1368        if( root_commands[num_root_commands-2].command )
1369                /* Planning fail! List is full. */
1370                return FALSE;
1371       
1372        for( i = 0; root_commands[i].command; i++ )
1373        {
1374                if( g_strcasecmp( root_commands[i].command, command ) == 0 )
1375                        return FALSE;
1376                else if( g_strcasecmp( root_commands[i].command, command ) > 0 )
1377                        break;
1378        }
1379        memmove( root_commands + i + 1, root_commands + i,
1380                 sizeof( command_t ) * ( num_root_commands - i - 1 ) );
1381       
1382        root_commands[i].command = g_strdup( command );
1383        root_commands[i].required_parameters = params;
1384        root_commands[i].execute = func;
1385        root_commands[i].flags = flags;
1386       
1387        return TRUE;
1388}
Note: See TracBrowser for help on using the repository browser.