source: root_commands.c @ be999a5

Last change on this file since be999a5 was be999a5, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-23T23:12:24Z

First step in this merge. Mostly a bzr merge and then a cleanup of conflicts
and parts I want to/have to redo (because of ui-fix).

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