source: root_commands.c @ da60f28

Last change on this file since da60f28 was da60f28, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-02-01T18:17:16Z

Oops. Forgot to set OPER_HACK_ACCOUNT_ADD when adding an account.

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