source: root_commands.c @ 6e0e283

Last change on this file since 6e0e283 was 8d93b4a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-10-22T00:50:34Z

Oops. Just in time I noticed the whatsnew code doesn't kick in for pre-1.3
accounts. Fixed now.

  • Property mode set to 100644
File size: 34.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; 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        char *val;
277       
278        if( ( val = set_getstr( head, key ) ) )
279                irc_usermsg( irc, "%s = `%s'", key, val );
280        else if( !set_find( head, key ) )
281        {
282                irc_usermsg( irc, "Setting `%s' does not exist.", key );
283                if( *head == irc->b->set )
284                        irc_usermsg( irc, "It might be an account or channel setting. "
285                                     "See \x02help account set\x02 and \x02help channel set\x02." );
286        }
287        else
288                irc_usermsg( irc, "%s is empty", key );
289}
290
291typedef set_t** (*cmd_set_findhead)( irc_t*, char* );
292typedef int (*cmd_set_checkflags)( irc_t*, set_t *set );
293
294static int cmd_set_real( irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags )
295{
296        char *set_name = NULL, *value = NULL;
297        gboolean del = FALSE;
298       
299        if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
300        {
301                MIN_ARGS( 2, 0 );
302                set_name = cmd[2];
303                del = TRUE;
304        }
305        else
306        {
307                set_name = cmd[1];
308                value = cmd[2];
309        }
310       
311        if( set_name && ( value || del ) )
312        {
313                set_t *s = set_find( head, set_name );
314                int st;
315               
316                if( s && checkflags && checkflags( irc, s ) == 0 )
317                        return 0;
318               
319                if( del )
320                        st = set_reset( head, set_name );
321                else
322                        st = set_setstr( head, set_name, value );
323               
324                if( set_getstr( head, set_name ) == NULL &&
325                    set_find( head, set_name ) )
326                {
327                        /* This happens when changing the passwd, for example.
328                           Showing these msgs instead gives slightly clearer
329                           feedback. */
330                        if( st )
331                                irc_usermsg( irc, "Setting changed successfully" );
332                        else
333                                irc_usermsg( irc, "Failed to change setting" );
334                }
335                else
336                {
337                        cmd_showset( irc, head, set_name );
338                }
339        }
340        else if( set_name )
341        {
342                cmd_showset( irc, head, set_name );
343        }
344        else
345        {
346                set_t *s = *head;
347                while( s )
348                {
349                        if( !( s->flags & SET_HIDDEN ) )
350                                cmd_showset( irc, &s, s->key );
351                        s = s->next;
352                }
353        }
354       
355        return 1;
356}
357
358static int cmd_account_set_checkflags( irc_t *irc, set_t *s )
359{
360        account_t *a = s->data;
361       
362        if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
363        {
364                irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" );
365                return 0;
366        }
367        else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
368        {
369                irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" );
370                return 0;
371        }
372       
373        return 1;
374}
375
376static void cmd_account( irc_t *irc, char **cmd )
377{
378        account_t *a;
379        int len;
380       
381        if( global.conf->authmode == AUTHMODE_REGISTERED && !( irc->status & USTATUS_IDENTIFIED ) )
382        {
383                irc_usermsg( irc, "This server only accepts registered users" );
384                return;
385        }
386       
387        len = strlen( cmd[1] );
388       
389        if( len >= 1 && g_strncasecmp( cmd[1], "add", len ) == 0 )
390        {
391                struct prpl *prpl;
392               
393                MIN_ARGS( 4 );
394               
395                prpl = find_protocol( cmd[2] );
396               
397                if( prpl == NULL )
398                {
399                        irc_usermsg( irc, "Unknown protocol" );
400                        return;
401                }
402               
403                for( a = irc->b->accounts; a; a = a->next )
404                        if( a->prpl == prpl && prpl->handle_cmp( a->user, cmd[3] ) == 0 )
405                                irc_usermsg( irc, "Warning: You already have an account with "
406                                             "protocol `%s' and username `%s'. Are you accidentally "
407                                             "trying to add it twice?", prpl->name, cmd[3] );
408               
409                a = account_add( irc->b, prpl, cmd[3], cmd[4] );
410                if( cmd[5] )
411                {
412                        irc_usermsg( irc, "Warning: Passing a servername/other flags to `account add' "
413                                          "is now deprecated. Use `account set' instead." );
414                        set_setstr( &a->set, "server", cmd[5] );
415                }
416               
417                irc_usermsg( irc, "Account successfully added" );
418               
419                return;
420        }
421        else if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
422        {
423                int i = 0;
424               
425                if( strchr( irc->umode, 'b' ) )
426                        irc_usermsg( irc, "Account list:" );
427               
428                for( a = irc->b->accounts; a; a = a->next )
429                {
430                        char *con;
431                       
432                        if( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) )
433                                con = " (connected)";
434                        else if( a->ic )
435                                con = " (connecting)";
436                        else if( a->reconnect )
437                                con = " (awaiting reconnect)";
438                        else
439                                con = "";
440                       
441                        irc_usermsg( irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con );
442                       
443                        i ++;
444                }
445                irc_usermsg( irc, "End of account list" );
446               
447                return;
448        }
449        else if( cmd[2] )
450        {
451                /* Try the following two only if cmd[2] == NULL */
452        }
453        else if( len >= 2 && g_strncasecmp( cmd[1], "on", len ) == 0 )
454        {
455                if ( irc->b->accounts )
456                {
457                        irc_usermsg( irc, "Trying to get all accounts connected..." );
458               
459                        for( a = irc->b->accounts; a; a = a->next )
460                                if( !a->ic && a->auto_connect )
461                                        account_on( irc->b, a );
462                } 
463                else
464                {
465                        irc_usermsg( irc, "No accounts known. Use `account add' to add one." );
466                }
467               
468                return;
469        }
470        else if( len >= 2 && g_strncasecmp( cmd[1], "off", len ) == 0 )
471        {
472                irc_usermsg( irc, "Deactivating all active (re)connections..." );
473               
474                for( a = irc->b->accounts; a; a = a->next )
475                {
476                        if( a->ic )
477                                account_off( irc->b, a );
478                        else if( a->reconnect )
479                                cancel_auto_reconnect( a );
480                }
481               
482                return;
483        }
484       
485        MIN_ARGS( 2 );
486        len = strlen( cmd[2] );
487       
488        /* At least right now, don't accept on/off/set/del as account IDs even
489           if they're a proper match, since people not familiar with the new
490           syntax yet may get a confusing/nasty surprise. */
491        if( g_strcasecmp( cmd[1], "on" ) == 0 ||
492            g_strcasecmp( cmd[1], "off" ) == 0 ||
493            g_strcasecmp( cmd[1], "set" ) == 0 ||
494            g_strcasecmp( cmd[1], "del" ) == 0 ||
495            ( a = account_get( irc->b, cmd[1] ) ) == NULL )
496        {
497                irc_usermsg( irc, "Could not find account `%s'. Note that the syntax "
498                             "of the account command changed, see \x02help account\x02.", cmd[1] );
499               
500                return;
501        }
502       
503        if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
504        {
505                if( a->ic )
506                {
507                        irc_usermsg( irc, "Account is still logged in, can't delete" );
508                }
509                else
510                {
511                        account_del( irc->b, a );
512                        irc_usermsg( irc, "Account deleted" );
513                }
514        }
515        else if( len >= 2 && g_strncasecmp( cmd[2], "on", len ) == 0 )
516        {
517                if( a->ic )
518                        irc_usermsg( irc, "Account already online" );
519                else
520                        account_on( irc->b, a );
521        }
522        else if( len >= 2 && g_strncasecmp( cmd[2], "off", len ) == 0 )
523        {
524                if( a->ic )
525                {
526                        account_off( irc->b, a );
527                }
528                else if( a->reconnect )
529                {
530                        cancel_auto_reconnect( a );
531                        irc_usermsg( irc, "Reconnect cancelled" );
532                }
533                else
534                {
535                        irc_usermsg( irc, "Account already offline" );
536                }
537        }
538        else if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
539        {
540                cmd_set_real( irc, cmd + 2, &a->set, cmd_account_set_checkflags );
541        }
542        else
543        {
544                irc_usermsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[2] );
545        }
546}
547
548static void cmd_channel( irc_t *irc, char **cmd )
549{
550        irc_channel_t *ic;
551        int len;
552       
553        len = strlen( cmd[1] );
554       
555        if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
556        {
557                GSList *l;
558                int i = 0;
559               
560                if( strchr( irc->umode, 'b' ) )
561                        irc_usermsg( irc, "Channel list:" );
562               
563                for( l = irc->channels; l; l = l->next )
564                {
565                        irc_channel_t *ic = l->data;
566                       
567                        irc_usermsg( irc, "%2d. %s, %s channel%s", i, ic->name,
568                                     set_getstr( &ic->set, "type" ),
569                                     ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "" );
570                       
571                        i ++;
572                }
573                irc_usermsg( irc, "End of channel list" );
574               
575                return;
576        }
577       
578        if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL )
579        {
580                /* If this doesn't match any channel, maybe this is the short
581                   syntax (only works when used inside a channel). */
582                if( ( ic = irc->root->last_channel ) &&
583                    ( len = strlen( cmd[1] ) ) &&
584                    g_strncasecmp( cmd[1], "set", len ) == 0 )
585                        cmd_set_real( irc, cmd + 1, &ic->set, NULL );
586                else
587                        irc_usermsg( irc, "Could not find channel `%s'", cmd[1] );
588               
589                return;
590        }
591       
592        MIN_ARGS( 2 );
593        len = strlen( cmd[2] );
594       
595        if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
596        {
597                cmd_set_real( irc, cmd + 2, &ic->set, NULL );
598        }
599        else if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
600        {
601                if( !( ic->flags & IRC_CHANNEL_JOINED ) &&
602                    ic != ic->irc->default_channel )
603                {
604                        irc_usermsg( irc, "Channel %s deleted.", ic->name );
605                        irc_channel_free( ic );
606                }
607                else
608                        irc_usermsg( irc, "Couldn't remove channel (main channel %s or "
609                                          "channels you're still in cannot be deleted).",
610                                          irc->default_channel->name );
611        }
612        else
613        {
614                irc_usermsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] );
615        }
616}
617
618static void cmd_add( irc_t *irc, char **cmd )
619{
620        account_t *a;
621        int add_on_server = 1;
622       
623        if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
624        {
625                MIN_ARGS( 3 );
626                add_on_server = 0;
627                cmd ++;
628        }
629       
630        if( !( a = account_get( irc->b, cmd[1] ) ) )
631        {
632                irc_usermsg( irc, "Invalid account" );
633                return;
634        }
635        else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
636        {
637                irc_usermsg( irc, "That account is not on-line" );
638                return;
639        }
640       
641        if( cmd[3] )
642        {
643                if( !nick_ok( cmd[3] ) )
644                {
645                        irc_usermsg( irc, "The requested nick `%s' is invalid", cmd[3] );
646                        return;
647                }
648                else if( irc_user_by_name( irc, cmd[3] ) )
649                {
650                        irc_usermsg( irc, "The requested nick `%s' already exists", cmd[3] );
651                        return;
652                }
653                else
654                {
655                        nick_set_raw( a, cmd[2], cmd[3] );
656                }
657        }
658       
659        if( add_on_server )
660        {
661                irc_channel_t *ic;
662                char *s, *group = NULL;;
663               
664                if( ( ic = irc->root->last_channel ) &&
665                    ( s = set_getstr( &ic->set, "fill_by" ) ) &&
666                    strcmp( s, "group" ) == 0 &&
667                    ( group = set_getstr( &ic->set, "group" ) ) )
668                        irc_usermsg( irc, "Adding `%s' to contact list (group %s)",
669                                     cmd[2], group );
670                else
671                        irc_usermsg( irc, "Adding `%s' to contact list", cmd[2] );
672               
673                a->prpl->add_buddy( a->ic, cmd[2], group );
674        }
675        else
676        {
677                bee_user_t *bu;
678                irc_user_t *iu;
679               
680                /* Only for add -tmp. For regular adds, this callback will
681                   be called once the IM server confirms. */
682                if( ( bu = bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ) ) &&
683                    ( iu = bu->ui_data ) )
684                        irc_usermsg( irc, "Temporarily assigned nickname `%s' "
685                                     "to contact `%s'", iu->nick, cmd[2] );
686        }
687       
688}
689
690static void cmd_remove( irc_t *irc, char **cmd )
691{
692        irc_user_t *iu;
693        bee_user_t *bu;
694        char *s;
695       
696        if( !( iu = irc_user_by_name( irc, cmd[1] ) ) || !( bu = iu->bu ) )
697        {
698                irc_usermsg( irc, "Buddy `%s' not found", cmd[1] );
699                return;
700        }
701        s = g_strdup( bu->handle );
702       
703        bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL );
704        nick_del( bu );
705        if( g_slist_find( irc->users, iu ) )
706                bee_user_free( irc->b, bu );
707       
708        irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] );
709        g_free( s );
710       
711        return;
712}
713
714static void cmd_info( irc_t *irc, char **cmd )
715{
716        struct im_connection *ic;
717        account_t *a;
718       
719        if( !cmd[2] )
720        {
721                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
722                if( !iu || !iu->bu )
723                {
724                        irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
725                        return;
726                }
727                ic = iu->bu->ic;
728                cmd[2] = iu->bu->handle;
729        }
730        else if( !( a = account_get( irc->b, cmd[1] ) ) )
731        {
732                irc_usermsg( irc, "Invalid account" );
733                return;
734        }
735        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
736        {
737                irc_usermsg( irc, "That account is not on-line" );
738                return;
739        }
740       
741        if( !ic->acc->prpl->get_info )
742        {
743                irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
744        }
745        else
746        {
747                ic->acc->prpl->get_info( ic, cmd[2] );
748        }
749}
750
751static void cmd_rename( irc_t *irc, char **cmd )
752{
753        irc_user_t *iu, *old;
754       
755        iu = irc_user_by_name( irc, cmd[1] );
756       
757        if( iu == NULL )
758        {
759                irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
760        }
761        else if( iu == irc->user )
762        {
763                irc_usermsg( irc, "Use /nick to change your own nickname" );
764        }
765        else if( !nick_ok( cmd[2] ) )
766        {
767                irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] );
768        }
769        else if( ( old = irc_user_by_name( irc, cmd[2] ) ) && old != iu )
770        {
771                irc_usermsg( irc, "Nick `%s' already exists", cmd[2] );
772        }
773        else
774        {
775                if( !irc_user_set_nick( iu, cmd[2] ) )
776                {
777                        irc_usermsg( irc, "Error while changing nick" );
778                        return;
779                }
780               
781                if( iu == irc->root )
782                {
783                        /* If we're called internally (user did "set root_nick"),
784                           let's not go O(INF). :-) */
785                        if( strcmp( cmd[0], "set_rename" ) != 0 )
786                                set_setstr( &irc->b->set, "root_nick", cmd[2] );
787                }
788                else if( iu->bu )
789                {
790                        nick_set( iu->bu, cmd[2] );
791                }
792               
793                irc_usermsg( irc, "Nick successfully changed" );
794        }
795}
796
797char *set_eval_root_nick( set_t *set, char *new_nick )
798{
799        irc_t *irc = set->data;
800       
801        if( strcmp( irc->root->nick, new_nick ) != 0 )
802        {
803                char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL };
804               
805                cmd_rename( irc, cmd );
806        }
807       
808        return strcmp( irc->root->nick, new_nick ) == 0 ? new_nick : SET_INVALID;
809}
810
811static void cmd_block( irc_t *irc, char **cmd )
812{
813        struct im_connection *ic;
814        account_t *a;
815       
816        if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
817        {
818                char *format;
819                GSList *l;
820               
821                if( strchr( irc->umode, 'b' ) != NULL )
822                        format = "%s\t%s";
823                else
824                        format = "%-32.32s  %-16.16s";
825               
826                irc_usermsg( irc, format, "Handle", "Nickname" );
827                for( l = a->ic->deny; l; l = l->next )
828                {
829                        bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data );
830                        irc_user_t *iu = bu ? bu->ui_data : NULL;
831                        irc_usermsg( irc, format, l->data, iu ? iu->nick : "(none)" );
832                }
833                irc_usermsg( irc, "End of list." );
834               
835                return;
836        }
837        else if( !cmd[2] )
838        {
839                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
840                if( !iu || !iu->bu )
841                {
842                        irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
843                        return;
844                }
845                ic = iu->bu->ic;
846                cmd[2] = iu->bu->handle;
847        }
848        else if( !( a = account_get( irc->b, cmd[1] ) ) )
849        {
850                irc_usermsg( irc, "Invalid account" );
851                return;
852        }
853        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
854        {
855                irc_usermsg( irc, "That account is not on-line" );
856                return;
857        }
858       
859        if( !ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit )
860        {
861                irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
862        }
863        else
864        {
865                imc_rem_allow( ic, cmd[2] );
866                imc_add_block( ic, cmd[2] );
867                irc_usermsg( irc, "Buddy `%s' moved from allow- to block-list", cmd[2] );
868        }
869}
870
871static void cmd_allow( irc_t *irc, char **cmd )
872{
873        struct im_connection *ic;
874        account_t *a;
875       
876        if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
877        {
878                char *format;
879                GSList *l;
880               
881                if( strchr( irc->umode, 'b' ) != NULL )
882                        format = "%s\t%s";
883                else
884                        format = "%-32.32s  %-16.16s";
885               
886                irc_usermsg( irc, format, "Handle", "Nickname" );
887                for( l = a->ic->permit; l; l = l->next )
888                {
889                        bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data );
890                        irc_user_t *iu = bu ? bu->ui_data : NULL;
891                        irc_usermsg( irc, format, l->data, iu ? iu->nick : "(none)" );
892                }
893                irc_usermsg( irc, "End of list." );
894               
895                return;
896        }
897        else if( !cmd[2] )
898        {
899                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
900                if( !iu || !iu->bu )
901                {
902                        irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
903                        return;
904                }
905                ic = iu->bu->ic;
906                cmd[2] = iu->bu->handle;
907        }
908        else if( !( a = account_get( irc->b, cmd[1] ) ) )
909        {
910                irc_usermsg( irc, "Invalid account" );
911                return;
912        }
913        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
914        {
915                irc_usermsg( irc, "That account is not on-line" );
916                return;
917        }
918       
919        if( !ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit )
920        {
921                irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
922        }
923        else
924        {
925                imc_rem_block( ic, cmd[2] );
926                imc_add_allow( ic, cmd[2] );
927               
928                irc_usermsg( irc, "Buddy `%s' moved from block- to allow-list", cmd[2] );
929        }
930}
931
932static void cmd_yesno( irc_t *irc, char **cmd )
933{
934        query_t *q = NULL;
935        int numq = 0;
936       
937        if( irc->queries == NULL )
938        {
939                /* Alright, alright, let's add a tiny easter egg here. */
940                static irc_t *last_irc = NULL;
941                static time_t last_time = 0;
942                static int times = 0;
943                static const char *msg[] = {
944                        "Oh yeah, that's right.",
945                        "Alright, alright. Now go back to work.",
946                        "Buuuuuuuuuuuuuuuurp... Excuse me!",
947                        "Yes?",
948                        "No?",
949                };
950               
951                if( last_irc == irc && time( NULL ) - last_time < 15 )
952                {
953                        if( ( ++times >= 3 ) )
954                        {
955                                irc_usermsg( irc, "%s", msg[rand()%(sizeof(msg)/sizeof(char*))] );
956                                last_irc = NULL;
957                                times = 0;
958                                return;
959                        }
960                }
961                else
962                {
963                        last_time = time( NULL );
964                        last_irc = irc;
965                        times = 0;
966                }
967               
968                irc_usermsg( irc, "Did I ask you something?" );
969                return;
970        }
971       
972        /* If there's an argument, the user seems to want to answer another question than the
973           first/last (depending on the query_order setting) one. */
974        if( cmd[1] )
975        {
976                if( sscanf( cmd[1], "%d", &numq ) != 1 )
977                {
978                        irc_usermsg( irc, "Invalid query number" );
979                        return;
980                }
981               
982                for( q = irc->queries; q; q = q->next, numq -- )
983                        if( numq == 0 )
984                                break;
985               
986                if( !q )
987                {
988                        irc_usermsg( irc, "Uhm, I never asked you something like that..." );
989                        return;
990                }
991        }
992       
993        if( g_strcasecmp( cmd[0], "yes" ) == 0 )
994                query_answer( irc, q, 1 );
995        else if( g_strcasecmp( cmd[0], "no" ) == 0 )
996                query_answer( irc, q, 0 );
997}
998
999static void cmd_set( irc_t *irc, char **cmd )
1000{
1001        cmd_set_real( irc, cmd, &irc->b->set, NULL );
1002}
1003
1004static void cmd_blist( irc_t *irc, char **cmd )
1005{
1006        int online = 0, away = 0, offline = 0;
1007        GSList *l;
1008        char s[256];
1009        char *format;
1010        int n_online = 0, n_away = 0, n_offline = 0;
1011       
1012        if( cmd[1] && g_strcasecmp( cmd[1], "all" ) == 0 )
1013                online = offline = away = 1;
1014        else if( cmd[1] && g_strcasecmp( cmd[1], "offline" ) == 0 )
1015                offline = 1;
1016        else if( cmd[1] && g_strcasecmp( cmd[1], "away" ) == 0 )
1017                away = 1;
1018        else if( cmd[1] && g_strcasecmp( cmd[1], "online" ) == 0 )
1019                online = 1;
1020        else
1021                online = away = 1;
1022       
1023        if( strchr( irc->umode, 'b' ) != NULL )
1024                format = "%s\t%s\t%s";
1025        else
1026                format = "%-16.16s  %-40.40s  %s";
1027       
1028        irc_usermsg( irc, format, "Nick", "Handle/Account", "Status" );
1029       
1030        if( irc->root->last_channel &&
1031            strcmp( set_getstr( &irc->root->last_channel->set, "type" ), "control" ) != 0 )
1032                irc->root->last_channel = NULL;
1033       
1034        for( l = irc->users; l; l = l->next )
1035        {
1036                irc_user_t *iu = l->data;
1037                bee_user_t *bu = iu->bu;
1038               
1039                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1040                    ( bu->flags & ( BEE_USER_ONLINE | BEE_USER_AWAY ) ) != BEE_USER_ONLINE )
1041                        continue;
1042               
1043                if( online == 1 )
1044                {
1045                        char st[256] = "Online";
1046                       
1047                        if( bu->status_msg )
1048                                g_snprintf( st, sizeof( st ) - 1, "Online (%s)", bu->status_msg );
1049                       
1050                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
1051                        irc_usermsg( irc, format, iu->nick, s, st );
1052                }
1053               
1054                n_online ++;
1055        }
1056
1057        for( l = irc->users; l; l = l->next )
1058        {
1059                irc_user_t *iu = l->data;
1060                bee_user_t *bu = iu->bu;
1061               
1062                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1063                    !( bu->flags & BEE_USER_ONLINE ) || !( bu->flags & BEE_USER_AWAY ) )
1064                        continue;
1065               
1066                if( away == 1 )
1067                {
1068                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
1069                        irc_usermsg( irc, format, iu->nick, s, irc_user_get_away( iu ) );
1070                }
1071                n_away ++;
1072        }
1073       
1074        for( l = irc->users; l; l = l->next )
1075        {
1076                irc_user_t *iu = l->data;
1077                bee_user_t *bu = iu->bu;
1078               
1079                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1080                    bu->flags & BEE_USER_ONLINE )
1081                        continue;
1082               
1083                if( offline == 1 )
1084                {
1085                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
1086                        irc_usermsg( irc, format, iu->nick, s, "Offline" );
1087                }
1088                n_offline ++;
1089        }
1090       
1091        irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline );
1092}
1093
1094static void cmd_qlist( irc_t *irc, char **cmd )
1095{
1096        query_t *q = irc->queries;
1097        int num;
1098       
1099        if( !q )
1100        {
1101                irc_usermsg( irc, "There are no pending questions." );
1102                return;
1103        }
1104       
1105        irc_usermsg( irc, "Pending queries:" );
1106       
1107        for( num = 0; q; q = q->next, num ++ )
1108                if( q->ic ) /* Not necessary yet, but it might come later */
1109                        irc_usermsg( irc, "%d, %s(%s): %s", num, q->ic->acc->prpl->name, q->ic->acc->user, q->question );
1110                else
1111                        irc_usermsg( irc, "%d, BitlBee: %s", num, q->question );
1112}
1113
1114static void cmd_chat( irc_t *irc, char **cmd )
1115{
1116        account_t *acc;
1117       
1118        if( g_strcasecmp( cmd[1], "add" ) == 0 )
1119        {
1120                char *channel, *s;
1121                struct irc_channel *ic;
1122               
1123                MIN_ARGS( 3 );
1124               
1125                if( !( acc = account_get( irc->b, cmd[2] ) ) )
1126                {
1127                        irc_usermsg( irc, "Invalid account" );
1128                        return;
1129                }
1130                else if( !acc->prpl->chat_join )
1131                {
1132                        irc_usermsg( irc, "Named chatrooms not supported on that account." );
1133                        return;
1134                }
1135               
1136                if( cmd[4] == NULL )
1137                {
1138                        channel = g_strdup( cmd[3] );
1139                        if( ( s = strchr( channel, '@' ) ) )
1140                                *s = 0;
1141                }
1142                else
1143                {
1144                        channel = g_strdup( cmd[4] );
1145                }
1146               
1147                if( strchr( CTYPES, channel[0] ) == NULL )
1148                {
1149                        s = g_strdup_printf( "#%s", channel );
1150                        g_free( channel );
1151                        channel = s;
1152                       
1153                        irc_channel_name_strip( channel );
1154                }
1155               
1156                if( ( ic = irc_channel_new( irc, channel ) ) &&
1157                    set_setstr( &ic->set, "type", "chat" ) &&
1158                    set_setstr( &ic->set, "chat_type", "room" ) &&
1159                    set_setstr( &ic->set, "account", cmd[2] ) &&
1160                    set_setstr( &ic->set, "room", cmd[3] ) )
1161                {
1162                        irc_usermsg( irc, "Chatroom successfully added." );
1163                }
1164                else
1165                {
1166                        if( ic )
1167                                irc_channel_free( ic );
1168                       
1169                        irc_usermsg( irc, "Could not add chatroom." );
1170                }
1171                g_free( channel );
1172        }
1173        else if( g_strcasecmp( cmd[1], "with" ) == 0 )
1174        {
1175                irc_user_t *iu;
1176               
1177                MIN_ARGS( 2 );
1178               
1179                if( ( iu = irc_user_by_name( irc, cmd[2] ) ) &&
1180                    iu->bu && iu->bu->ic->acc->prpl->chat_with )
1181                {
1182                        if( !iu->bu->ic->acc->prpl->chat_with( iu->bu->ic, iu->bu->handle ) )
1183                        {
1184                                irc_usermsg( irc, "(Possible) failure while trying to open "
1185                                                  "a groupchat with %s.", iu->nick );
1186                        }
1187                }
1188                else
1189                {
1190                        irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] );
1191                }
1192        }
1193        else if( g_strcasecmp( cmd[1], "list" ) == 0 ||
1194                 g_strcasecmp( cmd[1], "set" ) == 0 ||
1195                 g_strcasecmp( cmd[1], "del" ) == 0 )
1196        {
1197                irc_usermsg( irc, "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command." );
1198                cmd_channel( irc, cmd );
1199        }
1200        else
1201        {
1202                irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
1203        }
1204}
1205
1206static void cmd_group( irc_t *irc, char **cmd )
1207{
1208        GSList *l;
1209        int len;
1210       
1211        len = strlen( cmd[1] );
1212        if( g_strncasecmp( cmd[1], "list", len ) == 0 )
1213        {
1214                int n = 0;
1215               
1216                if( strchr( irc->umode, 'b' ) )
1217                        irc_usermsg( irc, "Group list:" );
1218               
1219                for( l = irc->b->groups; l; l = l->next )
1220                {
1221                        bee_group_t *bg = l->data;
1222                        irc_usermsg( irc, "%d. %s", n ++, bg->name );
1223                }
1224                irc_usermsg( irc, "End of group list" );
1225        }
1226        else
1227        {
1228                irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group", cmd[1] );
1229        }
1230}
1231
1232static void cmd_transfer( irc_t *irc, char **cmd )
1233{
1234        GSList *files = irc->file_transfers;
1235        enum { LIST, REJECT, CANCEL };
1236        int subcmd = LIST;
1237        int fid;
1238
1239        if( !files )
1240        {
1241                irc_usermsg( irc, "No pending transfers" );
1242                return;
1243        }
1244
1245        if( cmd[1] && ( strcmp( cmd[1], "reject" ) == 0 ) )
1246        {
1247                subcmd = REJECT;
1248        }
1249        else if( cmd[1] && ( strcmp( cmd[1], "cancel" ) == 0 ) && 
1250                 cmd[2] && ( sscanf( cmd[2], "%d", &fid ) == 1 ) )
1251        {
1252                subcmd = CANCEL;
1253        }
1254
1255        for( ; files; files = g_slist_next( files ) )
1256        {
1257                file_transfer_t *file = files->data;
1258               
1259                switch( subcmd ) {
1260                case LIST:
1261                        if ( file->status == FT_STATUS_LISTENING )
1262                                irc_usermsg( irc, 
1263                                        "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name);
1264                        else 
1265                        {
1266                                int kb_per_s = 0;
1267                                time_t diff = time( NULL ) - file->started ? : 1;
1268                                if ( ( file->started > 0 ) && ( file->bytes_transferred > 0 ) )
1269                                        kb_per_s = file->bytes_transferred / 1024 / diff;
1270                                       
1271                                irc_usermsg( irc, 
1272                                        "Pending file(id %d): %s (%10zd/%zd kb, %d kb/s)", file->local_id, file->file_name, 
1273                                        file->bytes_transferred/1024, file->file_size/1024, kb_per_s);
1274                        }
1275                        break;
1276                case REJECT:
1277                        if( file->status == FT_STATUS_LISTENING )
1278                        {
1279                                irc_usermsg( irc, "Rejecting file transfer for %s", file->file_name );
1280                                imcb_file_canceled( file->ic, file, "Denied by user" );
1281                        }
1282                        break;
1283                case CANCEL:
1284                        if( file->local_id == fid )
1285                        {
1286                                irc_usermsg( irc, "Canceling file transfer for %s", file->file_name );
1287                                imcb_file_canceled( file->ic, file, "Canceled by user" );
1288                        }
1289                        break;
1290                }
1291        }
1292}
1293
1294static void cmd_nick( irc_t *irc, char **cmd )
1295{
1296        irc_usermsg( irc, "This command is deprecated. Try: account %s set display_name", cmd[1] );
1297}
1298
1299/* Maybe this should be a stand-alone command as well? */
1300static void bitlbee_whatsnew( irc_t *irc )
1301{
1302        int last = set_getint( &irc->b->set, "last_version" );
1303        char s[16], *msg;
1304       
1305        if( last >= BITLBEE_VERSION_CODE )
1306                return;
1307       
1308        msg = help_get_whatsnew( &(global.help), last );
1309       
1310        if( msg )
1311                irc_usermsg( irc, "%s: This seems to be your first time using this "
1312                                  "this version of BitlBee. Here's a list of new "
1313                                  "features you may like to know about:\n\n%s\n",
1314                                  irc->user->nick, msg );
1315       
1316        g_free( msg );
1317       
1318        g_snprintf( s, sizeof( s ), "%d", BITLBEE_VERSION_CODE );
1319        set_setstr( &irc->b->set, "last_version", s );
1320}
1321
1322/* IMPORTANT: Keep this list sorted! The short command logic needs that. */
1323command_t root_commands[] = {
1324        { "account",        1, cmd_account,        0 },
1325        { "add",            2, cmd_add,            0 },
1326        { "allow",          1, cmd_allow,          0 },
1327        { "blist",          0, cmd_blist,          0 },
1328        { "block",          1, cmd_block,          0 },
1329        { "channel",        1, cmd_channel,        0 },
1330        { "chat",           1, cmd_chat,           0 },
1331        { "drop",           1, cmd_drop,           0 },
1332        { "ft",             0, cmd_transfer,       0 },
1333        { "group",          1, cmd_group,          0 },
1334        { "help",           0, cmd_help,           0 }, 
1335        { "identify",       1, cmd_identify,       0 },
1336        { "info",           1, cmd_info,           0 },
1337        { "nick",           1, cmd_nick,           0 },
1338        { "no",             0, cmd_yesno,          0 },
1339        { "qlist",          0, cmd_qlist,          0 },
1340        { "register",       1, cmd_register,       0 },
1341        { "remove",         1, cmd_remove,         0 },
1342        { "rename",         2, cmd_rename,         0 },
1343        { "save",           0, cmd_save,           0 },
1344        { "set",            0, cmd_set,            0 },
1345        { "transfer",       0, cmd_transfer,       0 },
1346        { "yes",            0, cmd_yesno,          0 },
1347        /* Not expecting too many plugins adding root commands so just make a
1348           dumb array with some empty entried at the end. */
1349        { NULL },
1350        { NULL },
1351        { NULL },
1352        { NULL },
1353        { NULL },
1354        { NULL },
1355        { NULL },
1356        { NULL },
1357        { NULL },
1358};
1359static const int num_root_commands = sizeof( root_commands ) / sizeof( command_t );
1360
1361gboolean root_command_add( const char *command, int params, void (*func)(irc_t *, char **args), int flags )
1362{
1363        int i;
1364       
1365        if( root_commands[num_root_commands-2].command )
1366                /* Planning fail! List is full. */
1367                return FALSE;
1368       
1369        for( i = 0; root_commands[i].command; i++ )
1370        {
1371                if( g_strcasecmp( root_commands[i].command, command ) == 0 )
1372                        return FALSE;
1373                else if( g_strcasecmp( root_commands[i].command, command ) > 0 )
1374                        break;
1375        }
1376        memmove( root_commands + i + 1, root_commands + i,
1377                 sizeof( command_t ) * ( num_root_commands - i - 1 ) );
1378       
1379        root_commands[i].command = g_strdup( command );
1380        root_commands[i].required_parameters = params;
1381        root_commands[i].execute = func;
1382        root_commands[i].flags = flags;
1383       
1384        return TRUE;
1385}
Note: See TracBrowser for help on using the repository browser.