source: root_commands.c @ 03e5fb7

Last change on this file since 03e5fb7 was f5c0d8e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-31T20:06:14Z

Merge mainline stuff.

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