source: root_commands.c @ 50b8978

Last change on this file since 50b8978 was 65a4a64, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-04T20:48:47Z

To address bugs #77 and #661, add some code that should avoid triggering
infinite loops between keyboard and chair.

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