source: root_commands.c @ 060d066

Last change on this file since 060d066 was 060d066, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-02-01T13:05:58Z

More password paranoia: Allow omitting the identify/register password as
well (and enter it using /OPER instead).

This is a gross hack and indeed still not solid: In irssi one can still
use /RAWLOG SAVE to find the OPER line sent to BitlBee (and of course not
everyone uses SSL to talk to remote BitlBee servers). This only works
within 10-30 minutes after entering the password though.

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