source: root_commands.c @ 5613af7

Last change on this file since 5613af7 was 5613af7, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-21T17:59:03Z

Clearer error message when trying to read/write setting that don't exist
(or are not where the user's looking).

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