source: root_commands.c @ 693ff09

Last change on this file since 693ff09 was e67e513, checked in by unknown <pesco@…>, at 2011-10-03T14:56:58Z

rename irc_usermsg to irc_rootmsg.
add new irc_usermsg, irc_usernotice.
deliver user-specific messages from libotr as notices to that user.

  • Property mode set to 100644
File size: 35.8 KB
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0baed0d]4  * Copyright 2002-2010 Wilmer van der Gaast and others                *
[b7d3cc34]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"
[f1c2b21]30#include "ipc.h"
[b7d3cc34]31
[280c56a]32void root_command_string( irc_t *irc, char *command )
[7e563ed]33{
[24b8bbb]34        root_command( irc, split_command_parts( command ) );
[7e563ed]35}
36
[3b99524]37#define MIN_ARGS( x, y... )                                                    \
38        do                                                                     \
39        {                                                                      \
[07054a5]40                int blaat;                                                     \
41                for( blaat = 0; blaat <= x; blaat ++ )                         \
42                        if( cmd[blaat] == NULL )                               \
[3b99524]43                        {                                                      \
[e67e513]44                                irc_rootmsg( irc, "Not enough parameters given (need %d).", x ); \
[3b99524]45                                return y;                                      \
46                        }                                                      \
47        } while( 0 )
48
[f73b969]49void root_command( irc_t *irc, char *cmd[] )
[7e563ed]50{       
[6c56f42]51        int i, len;
[7e563ed]52       
53        if( !cmd[0] )
[f73b969]54                return;
[7e563ed]55       
[6c56f42]56        len = strlen( cmd[0] );
[8358691]57        for( i = 0; root_commands[i].command; i++ )
58                if( g_strncasecmp( root_commands[i].command, cmd[0], len ) == 0 )
[7e563ed]59                {
[8358691]60                        if( root_commands[i+1].command &&
61                            g_strncasecmp( root_commands[i+1].command, cmd[0], len ) == 0 )
[6c56f42]62                                /* Only match on the first letters if the match is unique. */
63                                break;
64                       
[8358691]65                        MIN_ARGS( root_commands[i].required_parameters );
[3b99524]66                       
[8358691]67                        root_commands[i].execute( irc, cmd );
[f73b969]68                        return;
[7e563ed]69                }
70       
[e67e513]71        irc_rootmsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] );
[7e563ed]72}
73
[f73b969]74static void cmd_help( irc_t *irc, char **cmd )
[b7d3cc34]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        {
[e67e513]92                irc_rootmsg( irc, "%s", s );
[b7d3cc34]93                g_free( s );
94        }
95        else
96        {
[e67e513]97                irc_rootmsg( irc, "Error opening helpfile." );
[b7d3cc34]98        }
99}
100
[90bbb0e]101static void cmd_account( irc_t *irc, char **cmd );
[180ab31]102static void bitlbee_whatsnew( irc_t *irc );
[90bbb0e]103
[f73b969]104static void cmd_identify( irc_t *irc, char **cmd )
[b7d3cc34]105{
[92cb8c4]106        storage_status_t status;
107        gboolean load = TRUE;
108        char *password = cmd[1];
[b7d3cc34]109       
[92cb8c4]110        if( irc->status & USTATUS_IDENTIFIED )
[e9cf291]111        {
[e67e513]112                irc_rootmsg( irc, "You're already logged in." );
[e9cf291]113                return;
114        }
115       
[060d066]116        if( cmd[1] == NULL )
117        {
118        }
119        else if( strncmp( cmd[1], "-no", 3 ) == 0 )
[92cb8c4]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        {
[e67e513]130                irc_rootmsg( irc,
[92cb8c4]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        {
[e67e513]140                irc_rootmsg( irc, "About to identify, use /OPER to enter the password" );
[060d066]141                irc->status |= OPER_HACK_IDENTIFY;
142                return;
[92cb8c4]143        }
144       
145        if( load )
146                status = storage_load( irc, password );
147        else
148                status = storage_check_pass( irc->user->nick, password );
149       
[09adf08]150        switch (status) {
151        case STORAGE_INVALID_PASSWORD:
[e67e513]152                irc_rootmsg( irc, "Incorrect password" );
[09adf08]153                break;
154        case STORAGE_NO_SUCH_USER:
[e67e513]155                irc_rootmsg( irc, "The nick is (probably) not registered" );
[09adf08]156                break;
157        case STORAGE_OK:
[e67e513]158                irc_rootmsg( irc, "Password accepted%s",
[92cb8c4]159                             load ? ", settings and accounts loaded" : "" );
160                irc_setpass( irc, password );
[3183c21]161                irc->status |= USTATUS_IDENTIFIED;
[238f828]162                irc_umode_set( irc, "+R", 1 );
[6c2404e]163               
[180ab31]164                bitlbee_whatsnew( irc );
165               
[e92c4f4]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 )
[6c2404e]172                {
[e92c4f4]173                        irc_channel_auto_joins( irc, NULL );
[fe4f28f]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." );
[e92c4f4]178                        if( set_getbool( &irc->b->set, "auto_connect" ) )
[6c2404e]179                                irc->login_source_id = b_timeout_add( 200,
180                                        cmd_identify_finish, irc );
181                }
[e92c4f4]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" ) )
[6c2404e]189                        cmd_identify_finish( irc, 0, 0 );
190               
[09adf08]191                break;
[c121f89]192        case STORAGE_OTHER_ERROR:
[09adf08]193        default:
[e67e513]194                irc_rootmsg( irc, "Unknown error while loading configuration" );
[09adf08]195                break;
[b7d3cc34]196        }
197}
198
[6c2404e]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       
[af9f2ca]206        b_event_remove( irc->login_source_id );
[f545372]207        irc->login_source_id = -1;
[6c2404e]208        return FALSE;
209}
210
[f73b969]211static void cmd_register( irc_t *irc, char **cmd )
[b7d3cc34]212{
[8d93b4a]213        char s[16];
214       
[b7d3cc34]215        if( global.conf->authmode == AUTHMODE_REGISTERED )
216        {
[e67e513]217                irc_rootmsg( irc, "This server does not allow registering new accounts" );
[f73b969]218                return;
[b7d3cc34]219        }
[060d066]220       
221        if( cmd[1] == NULL )
222        {
[e67e513]223                irc_rootmsg( irc, "About to register, use /OPER to enter the password" );
[060d066]224                irc->status |= OPER_HACK_REGISTER;
225                return;
226        }
[1ee6c18]227
[3183c21]228        switch( storage_save( irc, cmd[1], FALSE ) ) {
[a1f17d4]229                case STORAGE_ALREADY_EXISTS:
[e67e513]230                        irc_rootmsg( irc, "Nick is already registered" );
[a1f17d4]231                        break;
232                       
233                case STORAGE_OK:
[e67e513]234                        irc_rootmsg( irc, "Account successfully created" );
[3183c21]235                        irc_setpass( irc, cmd[1] );
[79e826a]236                        irc->status |= USTATUS_IDENTIFIED;
[238f828]237                        irc_umode_set( irc, "+R", 1 );
[8d93b4a]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 );
[a1f17d4]244                        break;
245
246                default:
[e67e513]247                        irc_rootmsg( irc, "Error registering" );
[a1f17d4]248                        break;
[b7d3cc34]249        }
250}
251
[f73b969]252static void cmd_drop( irc_t *irc, char **cmd )
[b7d3cc34]253{
[a1f17d4]254        storage_status_t status;
255       
[1f92a58]256        status = storage_remove (irc->user->nick, cmd[1]);
[a1f17d4]257        switch (status) {
258        case STORAGE_NO_SUCH_USER:
[e67e513]259                irc_rootmsg( irc, "That account does not exist" );
[f73b969]260                break;
[a1f17d4]261        case STORAGE_INVALID_PASSWORD:
[e67e513]262                irc_rootmsg( irc, "Password invalid" );
[f73b969]263                break;
[a1f17d4]264        case STORAGE_OK:
[7cad7b4]265                irc_setpass( irc, NULL );
[79e826a]266                irc->status &= ~USTATUS_IDENTIFIED;
[238f828]267                irc_umode_set( irc, "-R", 1 );
[e67e513]268                irc_rootmsg( irc, "Account `%s' removed", irc->user->nick );
[f73b969]269                break;
[a1f17d4]270        default:
[e67e513]271                irc_rootmsg( irc, "Error: `%d'", status );
[f73b969]272                break;
[b7d3cc34]273        }
274}
[1f92a58]275
276static void cmd_save( irc_t *irc, char **cmd )
277{
278        if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
[e67e513]279                irc_rootmsg( irc, "Please create an account first" );
[1f92a58]280        else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
[e67e513]281                irc_rootmsg( irc, "Configuration saved" );
[1f92a58]282        else
[e67e513]283                irc_rootmsg( irc, "Configuration could not be saved!" );
[1f92a58]284}
[b7d3cc34]285
[f536a99]286static void cmd_showset( irc_t *irc, set_t **head, char *key )
[f3579fd]287{
[09d4922]288        set_t *set;
[f536a99]289        char *val;
[f3579fd]290       
[f536a99]291        if( ( val = set_getstr( head, key ) ) )
[e67e513]292                irc_rootmsg( irc, "%s = `%s'", key, val );
[09d4922]293        else if( !( set = set_find( head, key ) ) )
[5613af7]294        {
[e67e513]295                irc_rootmsg( irc, "Setting `%s' does not exist.", key );
[5613af7]296                if( *head == irc->b->set )
[e67e513]297                        irc_rootmsg( irc, "It might be an account or channel setting. "
[5613af7]298                                     "See \x02help account set\x02 and \x02help channel set\x02." );
299        }
[09d4922]300        else if( set->flags & SET_PASSWORD )
[e67e513]301                irc_rootmsg( irc, "%s = `********' (hidden)", key );
[f3579fd]302        else
[e67e513]303                irc_rootmsg( irc, "%s is empty", key );
[f3579fd]304}
305
[e7bc722]306typedef set_t** (*cmd_set_findhead)( irc_t*, char* );
[c05eb5b]307typedef int (*cmd_set_checkflags)( irc_t*, set_t *set );
[e7bc722]308
[e907683]309static int cmd_set_real( irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags )
[e7bc722]310{
[e907683]311        char *set_name = NULL, *value = NULL;
312        gboolean del = FALSE;
[e7bc722]313       
314        if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
[d4810df]315        {
316                MIN_ARGS( 2, 0 );
[e907683]317                set_name = cmd[2];
318                del = TRUE;
[d4810df]319        }
[e7bc722]320        else
321        {
[e907683]322                set_name = cmd[1];
323                value = cmd[2];
[e7bc722]324        }
325       
[e907683]326        if( set_name && ( value || del ) )
[e7bc722]327        {
328                set_t *s = set_find( head, set_name );
329                int st;
330               
[8a08d92]331                if( s && checkflags && checkflags( irc, s ) == 0 )
[e7bc722]332                        return 0;
333               
[e907683]334                if( del )
[e7bc722]335                        st = set_reset( head, set_name );
336                else
[e907683]337                        st = set_setstr( head, set_name, value );
[e7bc722]338               
[5613af7]339                if( set_getstr( head, set_name ) == NULL &&
340                    set_find( head, set_name ) )
[e7bc722]341                {
[e907683]342                        /* This happens when changing the passwd, for example.
343                           Showing these msgs instead gives slightly clearer
344                           feedback. */
[e7bc722]345                        if( st )
[e67e513]346                                irc_rootmsg( irc, "Setting changed successfully" );
[e7bc722]347                        else
[e67e513]348                                irc_rootmsg( irc, "Failed to change setting" );
[e7bc722]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                {
[180ab31]364                        if( !( s->flags & SET_HIDDEN ) )
365                                cmd_showset( irc, &s, s->key );
[e7bc722]366                        s = s->next;
367                }
368        }
369       
370        return 1;
371}
372
[c05eb5b]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        {
[e67e513]379                irc_rootmsg( irc, "This setting can only be changed when the account is %s-line", "off" );
[c05eb5b]380                return 0;
381        }
382        else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
383        {
[e67e513]384                irc_rootmsg( irc, "This setting can only be changed when the account is %s-line", "on" );
[c05eb5b]385                return 0;
386        }
387       
388        return 1;
389}
390
[f73b969]391static void cmd_account( irc_t *irc, char **cmd )
[b7d3cc34]392{
393        account_t *a;
[c7eb771]394        int len;
[b7d3cc34]395       
[3af70b0]396        if( global.conf->authmode == AUTHMODE_REGISTERED && !( irc->status & USTATUS_IDENTIFIED ) )
[b7d3cc34]397        {
[e67e513]398                irc_rootmsg( irc, "This server only accepts registered users" );
[f73b969]399                return;
[b7d3cc34]400        }
401       
[c7eb771]402        len = strlen( cmd[1] );
403       
404        if( len >= 1 && g_strncasecmp( cmd[1], "add", len ) == 0 )
[b7d3cc34]405        {
[7b23afd]406                struct prpl *prpl;
[b7d3cc34]407               
[9564e55]408                MIN_ARGS( 3 );
409               
410                if( cmd[4] == NULL )
[da60f28]411                {
[9564e55]412                        for( a = irc->b->accounts; a; a = a->next )
413                                if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
414                                {
[e67e513]415                                        irc_rootmsg( irc, "Enter password for account %s(%s) "
[9564e55]416                                                     "first (use /OPER)", a->prpl->name, a->user );
417                                        return;
418                                }
[da60f28]419                       
420                        irc->status |= OPER_HACK_ACCOUNT_ADD;
421                }
[b7d3cc34]422               
[a9a7287]423                prpl = find_protocol( cmd[2] );
[b7d3cc34]424               
[7b23afd]425                if( prpl == NULL )
[b7d3cc34]426                {
[e67e513]427                        irc_rootmsg( irc, "Unknown protocol" );
[f73b969]428                        return;
[b7d3cc34]429                }
[593971d]430               
431                for( a = irc->b->accounts; a; a = a->next )
432                        if( a->prpl == prpl && prpl->handle_cmp( a->user, cmd[3] ) == 0 )
[e67e513]433                                irc_rootmsg( irc, "Warning: You already have an account with "
[593971d]434                                             "protocol `%s' and username `%s'. Are you accidentally "
435                                             "trying to add it twice?", prpl->name, cmd[3] );
436               
[9564e55]437                a = account_add( irc->b, prpl, cmd[3], cmd[4] ? cmd[4] : PASSWORD_PENDING );
[b7d3cc34]438                if( cmd[5] )
[30ce1ce]439                {
[e67e513]440                        irc_rootmsg( irc, "Warning: Passing a servername/other flags to `account add' "
[30ce1ce]441                                          "is now deprecated. Use `account set' instead." );
[5100caa]442                        set_setstr( &a->set, "server", cmd[5] );
[30ce1ce]443                }
[b7d3cc34]444               
[e67e513]445                irc_rootmsg( irc, "Account successfully added%s", cmd[4] ? "" :
[bedad20]446                             ", now use /OPER to enter the password" );
[9564e55]447               
[e907683]448                return;
[b7d3cc34]449        }
[c7eb771]450        else if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
[b7d3cc34]451        {
452                int i = 0;
453               
[e6e1f18]454                if( strchr( irc->umode, 'b' ) )
[e67e513]455                        irc_rootmsg( irc, "Account list:" );
[e6e1f18]456               
[d860a8d]457                for( a = irc->b->accounts; a; a = a->next )
[b7d3cc34]458                {
459                        char *con;
460                       
[0da65d5]461                        if( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) )
[b7d3cc34]462                                con = " (connected)";
[0da65d5]463                        else if( a->ic )
[b7d3cc34]464                                con = " (connecting)";
465                        else if( a->reconnect )
466                                con = " (awaiting reconnect)";
467                        else
468                                con = "";
469                       
[e67e513]470                        irc_rootmsg( irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con );
[b7d3cc34]471                       
472                        i ++;
473                }
[e67e513]474                irc_rootmsg( irc, "End of account list" );
[e907683]475               
476                return;
477        }
478        else if( cmd[2] )
479        {
480                /* Try the following two only if cmd[2] == NULL */
[b7d3cc34]481        }
[c7eb771]482        else if( len >= 2 && g_strncasecmp( cmd[1], "on", len ) == 0 )
[b7d3cc34]483        {
[e907683]484                if ( irc->b->accounts )
[b7d3cc34]485                {
[e67e513]486                        irc_rootmsg( irc, "Trying to get all accounts connected..." );
[e907683]487               
488                        for( a = irc->b->accounts; a; a = a->next )
489                                if( !a->ic && a->auto_connect )
[9564e55]490                                {
491                                        if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
[e67e513]492                                                irc_rootmsg( irc, "Enter password for account %s(%s) "
[9564e55]493                                                             "first (use /OPER)", a->prpl->name, a->user );
494                                        else
495                                                account_on( irc->b, a );
496                                }
[e907683]497                } 
[b7d3cc34]498                else
499                {
[e67e513]500                        irc_rootmsg( irc, "No accounts known. Use `account add' to add one." );
[b7d3cc34]501                }
[e907683]502               
503                return;
[b7d3cc34]504        }
[c7eb771]505        else if( len >= 2 && g_strncasecmp( cmd[1], "off", len ) == 0 )
[b7d3cc34]506        {
[e67e513]507                irc_rootmsg( irc, "Deactivating all active (re)connections..." );
[e907683]508               
509                for( a = irc->b->accounts; a; a = a->next )
[b7d3cc34]510                {
[0da65d5]511                        if( a->ic )
[d860a8d]512                                account_off( irc->b, a );
[b7d3cc34]513                        else if( a->reconnect )
514                                cancel_auto_reconnect( a );
[e907683]515                }
516               
517                return;
518        }
519       
520        MIN_ARGS( 2 );
[c7eb771]521        len = strlen( cmd[2] );
[e907683]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        {
[e67e513]532                irc_rootmsg( irc, "Could not find account `%s'. Note that the syntax "
[e907683]533                             "of the account command changed, see \x02help account\x02.", cmd[1] );
534               
535                return;
536        }
537       
[c7eb771]538        if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
[e907683]539        {
540                if( a->ic )
541                {
[e67e513]542                        irc_rootmsg( irc, "Account is still logged in, can't delete" );
[b7d3cc34]543                }
544                else
545                {
[e907683]546                        account_del( irc->b, a );
[e67e513]547                        irc_rootmsg( irc, "Account deleted" );
[b7d3cc34]548                }
549        }
[c7eb771]550        else if( len >= 2 && g_strncasecmp( cmd[2], "on", len ) == 0 )
[5100caa]551        {
[e907683]552                if( a->ic )
[e67e513]553                        irc_rootmsg( irc, "Account already online" );
[9564e55]554                else if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
[e67e513]555                        irc_rootmsg( irc, "Enter password for account %s(%s) "
[9564e55]556                                     "first (use /OPER)", a->prpl->name, a->user );
[e907683]557                else
558                        account_on( irc->b, a );
559        }
[c7eb771]560        else if( len >= 2 && g_strncasecmp( cmd[2], "off", len ) == 0 )
[e907683]561        {
562                if( a->ic )
563                {
564                        account_off( irc->b, a );
565                }
566                else if( a->reconnect )
567                {
568                        cancel_auto_reconnect( a );
[e67e513]569                        irc_rootmsg( irc, "Reconnect cancelled" );
[e907683]570                }
571                else
572                {
[e67e513]573                        irc_rootmsg( irc, "Account already offline" );
[e907683]574                }
575        }
[c7eb771]576        else if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
[e907683]577        {
578                cmd_set_real( irc, cmd + 2, &a->set, cmd_account_set_checkflags );
[5100caa]579        }
[b7d3cc34]580        else
581        {
[e67e513]582                irc_rootmsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[2] );
[b7d3cc34]583        }
584}
585
[e907683]586static void cmd_channel( irc_t *irc, char **cmd )
[c133d4b8]587{
588        irc_channel_t *ic;
[c7eb771]589        int len;
590       
591        len = strlen( cmd[1] );
[c133d4b8]592       
[c7eb771]593        if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
[36562b0]594        {
595                GSList *l;
596                int i = 0;
597               
598                if( strchr( irc->umode, 'b' ) )
[e67e513]599                        irc_rootmsg( irc, "Channel list:" );
[36562b0]600               
601                for( l = irc->channels; l; l = l->next )
602                {
603                        irc_channel_t *ic = l->data;
604                       
[e67e513]605                        irc_rootmsg( irc, "%2d. %s, %s channel%s", i, ic->name,
[36562b0]606                                     set_getstr( &ic->set, "type" ),
607                                     ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "" );
608                       
609                        i ++;
610                }
[e67e513]611                irc_rootmsg( irc, "End of channel list" );
[e907683]612               
613                return;
[36562b0]614        }
[e907683]615       
616        if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL )
[a4d920b]617        {
[4f22a68c]618                /* If this doesn't match any channel, maybe this is the short
619                   syntax (only works when used inside a channel). */
[f7ca587]620                if( ( ic = irc->root->last_channel ) &&
621                    ( len = strlen( cmd[1] ) ) &&
622                    g_strncasecmp( cmd[1], "set", len ) == 0 )
[4f22a68c]623                        cmd_set_real( irc, cmd + 1, &ic->set, NULL );
624                else
[e67e513]625                        irc_rootmsg( irc, "Could not find channel `%s'", cmd[1] );
[4f22a68c]626               
[e907683]627                return;
628        }
629       
[4f22a68c]630        MIN_ARGS( 2 );
631        len = strlen( cmd[2] );
632       
[c7eb771]633        if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
[e907683]634        {
635                cmd_set_real( irc, cmd + 2, &ic->set, NULL );
636        }
[c7eb771]637        else if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
[e907683]638        {
639                if( !( ic->flags & IRC_CHANNEL_JOINED ) &&
[a4d920b]640                    ic != ic->irc->default_channel )
641                {
[e67e513]642                        irc_rootmsg( irc, "Channel %s deleted.", ic->name );
[a4d920b]643                        irc_channel_free( ic );
644                }
645                else
[e67e513]646                        irc_rootmsg( irc, "Couldn't remove channel (main channel %s or "
[a4d920b]647                                          "channels you're still in cannot be deleted).",
[5266354]648                                          irc->default_channel->name );
[a4d920b]649        }
[c133d4b8]650        else
651        {
[e67e513]652                irc_rootmsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] );
[c133d4b8]653        }
654}
655
[f73b969]656static void cmd_add( irc_t *irc, char **cmd )
[b7d3cc34]657{
658        account_t *a;
[f0cb961]659        int add_on_server = 1;
[f8de26f]660       
661        if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
662        {
[77fc000c]663                MIN_ARGS( 3 );
[f0cb961]664                add_on_server = 0;
[7adc657]665                cmd ++;
[f8de26f]666        }
[b7d3cc34]667       
[dbb0ce3]668        if( !( a = account_get( irc->b, cmd[1] ) ) )
[b7d3cc34]669        {
[e67e513]670                irc_rootmsg( irc, "Invalid account" );
[f73b969]671                return;
[b7d3cc34]672        }
[0da65d5]673        else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
[b7d3cc34]674        {
[e67e513]675                irc_rootmsg( irc, "That account is not on-line" );
[f73b969]676                return;
[b7d3cc34]677        }
678       
679        if( cmd[3] )
680        {
681                if( !nick_ok( cmd[3] ) )
682                {
[e67e513]683                        irc_rootmsg( irc, "The requested nick `%s' is invalid", cmd[3] );
[f73b969]684                        return;
[b7d3cc34]685                }
[dbb0ce3]686                else if( irc_user_by_name( irc, cmd[3] ) )
[b7d3cc34]687                {
[e67e513]688                        irc_rootmsg( irc, "The requested nick `%s' already exists", cmd[3] );
[f73b969]689                        return;
[b7d3cc34]690                }
691                else
692                {
[b1f818b]693                        nick_set_raw( a, cmd[2], cmd[3] );
[b7d3cc34]694                }
695        }
[f8de26f]696       
[f0cb961]697        if( add_on_server )
[f1d488e]698        {
699                irc_channel_t *ic;
700                char *s, *group = NULL;;
701               
[f7ca587]702                if( ( ic = irc->root->last_channel ) &&
[f1d488e]703                    ( s = set_getstr( &ic->set, "fill_by" ) ) &&
704                    strcmp( s, "group" ) == 0 &&
705                    ( group = set_getstr( &ic->set, "group" ) ) )
[e67e513]706                        irc_rootmsg( irc, "Adding `%s' to contact list (group %s)",
[f1d488e]707                                     cmd[2], group );
708                else
[e67e513]709                        irc_rootmsg( irc, "Adding `%s' to contact list", cmd[2] );
[f1d488e]710               
711                a->prpl->add_buddy( a->ic, cmd[2], group );
712        }
[7adc657]713        else
[f1d488e]714        {
715                bee_user_t *bu;
716                irc_user_t *iu;
717               
[dbb0ce3]718                /* Only for add -tmp. For regular adds, this callback will
719                   be called once the IM server confirms. */
[f1d488e]720                if( ( bu = bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ) ) &&
721                    ( iu = bu->ui_data ) )
[e67e513]722                        irc_rootmsg( irc, "Temporarily assigned nickname `%s' "
[f1d488e]723                                     "to contact `%s'", iu->nick, cmd[2] );
724        }
[f0cb961]725       
[b7d3cc34]726}
727
[dbb0ce3]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        {
[e67e513]736                irc_rootmsg( irc, "Buddy `%s' not found", cmd[1] );
[dbb0ce3]737                return;
738        }
739        s = g_strdup( bu->handle );
740       
741        bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL );
[b1f818b]742        nick_del( bu );
[5e98ff0]743        if( g_slist_find( irc->users, iu ) )
744                bee_user_free( irc->b, bu );
[dbb0ce3]745       
[e67e513]746        irc_rootmsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] );
[dbb0ce3]747        g_free( s );
748       
749        return;
750}
751
[f73b969]752static void cmd_info( irc_t *irc, char **cmd )
[b7d3cc34]753{
[0da65d5]754        struct im_connection *ic;
[b7d3cc34]755        account_t *a;
756       
757        if( !cmd[2] )
758        {
[aa44bdd]759                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
760                if( !iu || !iu->bu )
[b7d3cc34]761                {
[e67e513]762                        irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
[f73b969]763                        return;
[b7d3cc34]764                }
[aa44bdd]765                ic = iu->bu->ic;
766                cmd[2] = iu->bu->handle;
[b7d3cc34]767        }
[aa44bdd]768        else if( !( a = account_get( irc->b, cmd[1] ) ) )
[b7d3cc34]769        {
[e67e513]770                irc_rootmsg( irc, "Invalid account" );
[f73b969]771                return;
[b7d3cc34]772        }
[0da65d5]773        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
[b7d3cc34]774        {
[e67e513]775                irc_rootmsg( irc, "That account is not on-line" );
[f73b969]776                return;
[b7d3cc34]777        }
778       
[0da65d5]779        if( !ic->acc->prpl->get_info )
[b7d3cc34]780        {
[e67e513]781                irc_rootmsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
[b7d3cc34]782        }
[f73b969]783        else
784        {
[0da65d5]785                ic->acc->prpl->get_info( ic, cmd[2] );
[f73b969]786        }
[b7d3cc34]787}
788
[f73b969]789static void cmd_rename( irc_t *irc, char **cmd )
[b7d3cc34]790{
[9a9b520]791        irc_user_t *iu, *old;
[a429907]792        gboolean del = g_strcasecmp( cmd[1], "-del" ) == 0;
[b7d3cc34]793       
[a429907]794        iu = irc_user_by_name( irc, cmd[del ? 2 : 1] );
[57c96f7]795       
796        if( iu == NULL )
[0baed0d]797        {
[e67e513]798                irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
[0baed0d]799        }
[a429907]800        else if( del )
801        {
802                if( iu->bu )
803                        bee_irc_user_nick_reset( iu );
[e67e513]804                irc_rootmsg( irc, "Nickname reset to `%s'", iu->nick );
[a429907]805        }
[57c96f7]806        else if( iu == irc->user )
[b7d3cc34]807        {
[e67e513]808                irc_rootmsg( irc, "Use /nick to change your own nickname" );
[b7d3cc34]809        }
[f73b969]810        else if( !nick_ok( cmd[2] ) )
[b7d3cc34]811        {
[e67e513]812                irc_rootmsg( irc, "Nick `%s' is invalid", cmd[2] );
[b7d3cc34]813        }
[9a9b520]814        else if( ( old = irc_user_by_name( irc, cmd[2] ) ) && old != iu )
[b7d3cc34]815        {
[e67e513]816                irc_rootmsg( irc, "Nick `%s' already exists", cmd[2] );
[b7d3cc34]817        }
[f73b969]818        else
[b7d3cc34]819        {
[57c96f7]820                if( !irc_user_set_nick( iu, cmd[2] ) )
821                {
[e67e513]822                        irc_rootmsg( irc, "Error while changing nick" );
[57c96f7]823                        return;
824                }
825               
826                if( iu == irc->root )
[f73b969]827                {
[7125cb3]828                        /* If we're called internally (user did "set root_nick"),
829                           let's not go O(INF). :-) */
[1195cec]830                        if( strcmp( cmd[0], "set_rename" ) != 0 )
[57c96f7]831                                set_setstr( &irc->b->set, "root_nick", cmd[2] );
[f73b969]832                }
[57c96f7]833                else if( iu->bu )
[f73b969]834                {
[b1f818b]835                        nick_set( iu->bu, cmd[2] );
[f73b969]836                }
837               
[e67e513]838                irc_rootmsg( irc, "Nick successfully changed" );
[b7d3cc34]839        }
840}
841
[1195cec]842char *set_eval_root_nick( set_t *set, char *new_nick )
843{
844        irc_t *irc = set->data;
845       
[0a6e5d1]846        if( strcmp( irc->root->nick, new_nick ) != 0 )
[1195cec]847        {
[0a6e5d1]848                char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL };
[1195cec]849               
850                cmd_rename( irc, cmd );
851        }
852       
[0a6e5d1]853        return strcmp( irc->root->nick, new_nick ) == 0 ? new_nick : SET_INVALID;
[1195cec]854}
[0baed0d]855
[f73b969]856static void cmd_block( irc_t *irc, char **cmd )
[b7d3cc34]857{
[0da65d5]858        struct im_connection *ic;
[b7d3cc34]859        account_t *a;
860       
[2272cb3]861        if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
[87b6a3e]862        {
863                char *format;
864                GSList *l;
865               
866                if( strchr( irc->umode, 'b' ) != NULL )
867                        format = "%s\t%s";
868                else
[57ef864]869                        format = "%-32.32s  %-16.16s";
[87b6a3e]870               
[e67e513]871                irc_rootmsg( irc, format, "Handle", "Nickname" );
[0da65d5]872                for( l = a->ic->deny; l; l = l->next )
[87b6a3e]873                {
[2272cb3]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;
[e67e513]876                        irc_rootmsg( irc, format, l->data, iu ? iu->nick : "(none)" );
[87b6a3e]877                }
[e67e513]878                irc_rootmsg( irc, "End of list." );
[87b6a3e]879               
880                return;
881        }
882        else if( !cmd[2] )
[b7d3cc34]883        {
[2272cb3]884                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
885                if( !iu || !iu->bu )
[b7d3cc34]886                {
[e67e513]887                        irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
[f73b969]888                        return;
[b7d3cc34]889                }
[2272cb3]890                ic = iu->bu->ic;
891                cmd[2] = iu->bu->handle;
[b7d3cc34]892        }
[2272cb3]893        else if( !( a = account_get( irc->b, cmd[1] ) ) )
[b7d3cc34]894        {
[e67e513]895                irc_rootmsg( irc, "Invalid account" );
[f73b969]896                return;
[b7d3cc34]897        }
[0da65d5]898        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
[b7d3cc34]899        {
[e67e513]900                irc_rootmsg( irc, "That account is not on-line" );
[f73b969]901                return;
[b7d3cc34]902        }
903       
[0da65d5]904        if( !ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit )
[b7d3cc34]905        {
[e67e513]906                irc_rootmsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
[b7d3cc34]907        }
908        else
909        {
[84b045d]910                imc_rem_allow( ic, cmd[2] );
911                imc_add_block( ic, cmd[2] );
[e67e513]912                irc_rootmsg( irc, "Buddy `%s' moved from allow- to block-list", cmd[2] );
[b7d3cc34]913        }
914}
915
[f73b969]916static void cmd_allow( irc_t *irc, char **cmd )
[b7d3cc34]917{
[0da65d5]918        struct im_connection *ic;
[b7d3cc34]919        account_t *a;
920       
[2272cb3]921        if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
[87b6a3e]922        {
923                char *format;
924                GSList *l;
925               
926                if( strchr( irc->umode, 'b' ) != NULL )
927                        format = "%s\t%s";
928                else
[57ef864]929                        format = "%-32.32s  %-16.16s";
[87b6a3e]930               
[e67e513]931                irc_rootmsg( irc, format, "Handle", "Nickname" );
[0da65d5]932                for( l = a->ic->permit; l; l = l->next )
[87b6a3e]933                {
[2272cb3]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;
[e67e513]936                        irc_rootmsg( irc, format, l->data, iu ? iu->nick : "(none)" );
[87b6a3e]937                }
[e67e513]938                irc_rootmsg( irc, "End of list." );
[87b6a3e]939               
940                return;
941        }
942        else if( !cmd[2] )
[b7d3cc34]943        {
[2272cb3]944                irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
945                if( !iu || !iu->bu )
[b7d3cc34]946                {
[e67e513]947                        irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
[f73b969]948                        return;
[b7d3cc34]949                }
[2272cb3]950                ic = iu->bu->ic;
951                cmd[2] = iu->bu->handle;
[b7d3cc34]952        }
[2272cb3]953        else if( !( a = account_get( irc->b, cmd[1] ) ) )
[b7d3cc34]954        {
[e67e513]955                irc_rootmsg( irc, "Invalid account" );
[f73b969]956                return;
[b7d3cc34]957        }
[0da65d5]958        else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
[b7d3cc34]959        {
[e67e513]960                irc_rootmsg( irc, "That account is not on-line" );
[f73b969]961                return;
[b7d3cc34]962        }
963       
[0da65d5]964        if( !ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit )
[b7d3cc34]965        {
[e67e513]966                irc_rootmsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
[b7d3cc34]967        }
968        else
969        {
[84b045d]970                imc_rem_block( ic, cmd[2] );
971                imc_add_allow( ic, cmd[2] );
[b7d3cc34]972               
[e67e513]973                irc_rootmsg( irc, "Buddy `%s' moved from block- to allow-list", cmd[2] );
[b7d3cc34]974        }
975}
976
[f73b969]977static void cmd_yesno( irc_t *irc, char **cmd )
[b7d3cc34]978{
979        query_t *q = NULL;
980        int numq = 0;
981       
982        if( irc->queries == NULL )
983        {
[65a4a64]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                        {
[e67e513]1000                                irc_rootmsg( irc, "%s", msg[rand()%(sizeof(msg)/sizeof(char*))] );
[65a4a64]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               
[e67e513]1013                irc_rootmsg( irc, "Did I ask you something?" );
[f73b969]1014                return;
[b7d3cc34]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                {
[e67e513]1023                        irc_rootmsg( irc, "Invalid query number" );
[f73b969]1024                        return;
[b7d3cc34]1025                }
1026               
1027                for( q = irc->queries; q; q = q->next, numq -- )
1028                        if( numq == 0 )
1029                                break;
1030               
1031                if( !q )
1032                {
[e67e513]1033                        irc_rootmsg( irc, "Uhm, I never asked you something like that..." );
[f73b969]1034                        return;
[b7d3cc34]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
[f73b969]1044static void cmd_set( irc_t *irc, char **cmd )
[b7d3cc34]1045{
[e907683]1046        cmd_set_real( irc, cmd, &irc->b->set, NULL );
[b7d3cc34]1047}
1048
[f73b969]1049static void cmd_blist( irc_t *irc, char **cmd )
[b7d3cc34]1050{
1051        int online = 0, away = 0, offline = 0;
[4c3519a]1052        GSList *l;
[aefa533e]1053        char s[256];
1054        char *format;
[b7d3cc34]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
[449a51d]1066                online = away = 1;
[b7d3cc34]1067       
[aefa533e]1068        if( strchr( irc->umode, 'b' ) != NULL )
1069                format = "%s\t%s\t%s";
1070        else
1071                format = "%-16.16s  %-40.40s  %s";
1072       
[e67e513]1073        irc_rootmsg( irc, format, "Nick", "Handle/Account", "Status" );
[b7d3cc34]1074       
[acd7959]1075        if( irc->root->last_channel &&
1076            strcmp( set_getstr( &irc->root->last_channel->set, "type" ), "control" ) != 0 )
[ac2717b]1077                irc->root->last_channel = NULL;
1078       
[4c3519a]1079        for( l = irc->users; l; l = l->next )
[b7d3cc34]1080        {
[4c3519a]1081                irc_user_t *iu = l->data;
1082                bee_user_t *bu = iu->bu;
1083               
[ac2717b]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 )
[4c3519a]1086                        continue;
1087               
[aefa533e]1088                if( online == 1 )
1089                {
[449a51d]1090                        char st[256] = "Online";
1091                       
[4c3519a]1092                        if( bu->status_msg )
1093                                g_snprintf( st, sizeof( st ) - 1, "Online (%s)", bu->status_msg );
[449a51d]1094                       
[4c3519a]1095                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
[e67e513]1096                        irc_rootmsg( irc, format, iu->nick, s, st );
[aefa533e]1097                }
1098               
[b7d3cc34]1099                n_online ++;
1100        }
1101
[4c3519a]1102        for( l = irc->users; l; l = l->next )
[b7d3cc34]1103        {
[4c3519a]1104                irc_user_t *iu = l->data;
1105                bee_user_t *bu = iu->bu;
1106               
[ac2717b]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 ) )
[4c3519a]1109                        continue;
1110               
[aefa533e]1111                if( away == 1 )
1112                {
[4c3519a]1113                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
[e67e513]1114                        irc_rootmsg( irc, format, iu->nick, s, irc_user_get_away( iu ) );
[aefa533e]1115                }
[b7d3cc34]1116                n_away ++;
1117        }
1118       
[4c3519a]1119        for( l = irc->users; l; l = l->next )
[b7d3cc34]1120        {
[4c3519a]1121                irc_user_t *iu = l->data;
1122                bee_user_t *bu = iu->bu;
1123               
[ac2717b]1124                if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1125                    bu->flags & BEE_USER_ONLINE )
[4c3519a]1126                        continue;
1127               
[aefa533e]1128                if( offline == 1 )
1129                {
[4c3519a]1130                        g_snprintf( s, sizeof( s ) - 1, "%s %s(%s)", bu->handle, bu->ic->acc->prpl->name, bu->ic->acc->user );
[e67e513]1131                        irc_rootmsg( irc, format, iu->nick, s, "Offline" );
[aefa533e]1132                }
[b7d3cc34]1133                n_offline ++;
1134        }
1135       
[e67e513]1136        irc_rootmsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline );
[b7d3cc34]1137}
1138
[f73b969]1139static void cmd_qlist( irc_t *irc, char **cmd )
[b7d3cc34]1140{
1141        query_t *q = irc->queries;
1142        int num;
1143       
1144        if( !q )
1145        {
[e67e513]1146                irc_rootmsg( irc, "There are no pending questions." );
[f73b969]1147                return;
[b7d3cc34]1148        }
1149       
[e67e513]1150        irc_rootmsg( irc, "Pending queries:" );
[b7d3cc34]1151       
1152        for( num = 0; q; q = q->next, num ++ )
[0da65d5]1153                if( q->ic ) /* Not necessary yet, but it might come later */
[e67e513]1154                        irc_rootmsg( irc, "%d, %s(%s): %s", num, q->ic->acc->prpl->name, q->ic->acc->user, q->question );
[5c09a59]1155                else
[e67e513]1156                        irc_rootmsg( irc, "%d, BitlBee: %s", num, q->question );
[b7d3cc34]1157}
1158
[a9a7287]1159static void cmd_chat( irc_t *irc, char **cmd )
1160{
1161        account_t *acc;
1162       
1163        if( g_strcasecmp( cmd[1], "add" ) == 0 )
1164        {
[07054a5]1165                char *channel, *s;
[7b71feb]1166                struct irc_channel *ic;
[07054a5]1167               
1168                MIN_ARGS( 3 );
[a9a7287]1169               
[7b71feb]1170                if( !( acc = account_get( irc->b, cmd[2] ) ) )
[a9a7287]1171                {
[e67e513]1172                        irc_rootmsg( irc, "Invalid account" );
[a9a7287]1173                        return;
1174                }
[5a75d15]1175                else if( !acc->prpl->chat_join )
1176                {
[e67e513]1177                        irc_rootmsg( irc, "Named chatrooms not supported on that account." );
[5a75d15]1178                        return;
1179                }
[a9a7287]1180               
[07054a5]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                {
[7b71feb]1194                        s = g_strdup_printf( "#%s", channel );
[07054a5]1195                        g_free( channel );
1196                        channel = s;
[134a02c]1197                       
1198                        irc_channel_name_strip( channel );
[07054a5]1199                }
1200               
[5a75d15]1201                if( ( ic = irc_channel_new( irc, channel ) ) &&
[547ea68]1202                    set_setstr( &ic->set, "type", "chat" ) &&
[5a75d15]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                {
[e67e513]1207                        irc_rootmsg( irc, "Chatroom successfully added." );
[5a75d15]1208                }
1209                else
[a9a7287]1210                {
[5a75d15]1211                        if( ic )
1212                                irc_channel_free( ic );
[a9a7287]1213                       
[e67e513]1214                        irc_rootmsg( irc, "Could not add chatroom." );
[d995c9b]1215                }
[d7db346]1216                g_free( channel );
[d995c9b]1217        }
[39f93f0]1218        else if( g_strcasecmp( cmd[1], "with" ) == 0 )
1219        {
[c1a8a16]1220                irc_user_t *iu;
[3b99524]1221               
1222                MIN_ARGS( 2 );
[39f93f0]1223               
[c1a8a16]1224                if( ( iu = irc_user_by_name( irc, cmd[2] ) ) &&
1225                    iu->bu && iu->bu->ic->acc->prpl->chat_with )
[39f93f0]1226                {
[c1a8a16]1227                        if( !iu->bu->ic->acc->prpl->chat_with( iu->bu->ic, iu->bu->handle ) )
[39f93f0]1228                        {
[e67e513]1229                                irc_rootmsg( irc, "(Possible) failure while trying to open "
[c1a8a16]1230                                                  "a groupchat with %s.", iu->nick );
[39f93f0]1231                        }
1232                }
1233                else
1234                {
[e67e513]1235                        irc_rootmsg( irc, "Can't open a groupchat with %s.", cmd[2] );
[39f93f0]1236                }
1237        }
[7cd2e8a]1238        else if( g_strcasecmp( cmd[1], "list" ) == 0 ||
1239                 g_strcasecmp( cmd[1], "set" ) == 0 ||
1240                 g_strcasecmp( cmd[1], "del" ) == 0 )
1241        {
[e67e513]1242                irc_rootmsg( irc, "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command." );
[7cd2e8a]1243                cmd_channel( irc, cmd );
1244        }
[a9a7287]1245        else
1246        {
[e67e513]1247                irc_rootmsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
[a9a7287]1248        }
[fa29d093]1249}
1250
[f1d488e]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' ) )
[e67e513]1262                        irc_rootmsg( irc, "Group list:" );
[f1d488e]1263               
1264                for( l = irc->b->groups; l; l = l->next )
1265                {
1266                        bee_group_t *bg = l->data;
[e67e513]1267                        irc_rootmsg( irc, "%d. %s", n ++, bg->name );
[f1d488e]1268                }
[e67e513]1269                irc_rootmsg( irc, "End of group list" );
[f1d488e]1270        }
1271        else
1272        {
[e67e513]1273                irc_rootmsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group", cmd[1] );
[f1d488e]1274        }
1275}
1276
[b8a491d]1277static void cmd_transfer( irc_t *irc, char **cmd )
[2c2df7d]1278{
1279        GSList *files = irc->file_transfers;
1280        enum { LIST, REJECT, CANCEL };
1281        int subcmd = LIST;
1282        int fid;
1283
1284        if( !files )
1285        {
[e67e513]1286                irc_rootmsg( irc, "No pending transfers" );
[2c2df7d]1287                return;
1288        }
1289
[b8a491d]1290        if( cmd[1] && ( strcmp( cmd[1], "reject" ) == 0 ) )
[2c2df7d]1291        {
1292                subcmd = REJECT;
1293        }
[b8a491d]1294        else if( cmd[1] && ( strcmp( cmd[1], "cancel" ) == 0 ) && 
1295                 cmd[2] && ( sscanf( cmd[2], "%d", &fid ) == 1 ) )
[2c2df7d]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 )
[e67e513]1307                                irc_rootmsg( irc, 
[2c2df7d]1308                                        "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name);
1309                        else 
1310                        {
1311                                int kb_per_s = 0;
[44961cb]1312                                time_t diff = time( NULL ) - file->started ? : 1;
[2c2df7d]1313                                if ( ( file->started > 0 ) && ( file->bytes_transferred > 0 ) )
1314                                        kb_per_s = file->bytes_transferred / 1024 / diff;
1315                                       
[e67e513]1316                                irc_rootmsg( irc, 
[2c2df7d]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                        {
[e67e513]1324                                irc_rootmsg( irc, "Rejecting file transfer for %s", file->file_name );
[9d4352c]1325                                imcb_file_canceled( file->ic, file, "Denied by user" );
[2c2df7d]1326                        }
1327                        break;
1328                case CANCEL:
1329                        if( file->local_id == fid )
1330                        {
[e67e513]1331                                irc_rootmsg( irc, "Canceling file transfer for %s", file->file_name );
[9d4352c]1332                                imcb_file_canceled( file->ic, file, "Canceled by user" );
[2c2df7d]1333                        }
1334                        break;
1335                }
1336        }
1337}
1338
[3cd4016]1339static void cmd_nick( irc_t *irc, char **cmd )
1340{
[e67e513]1341        irc_rootmsg( irc, "This command is deprecated. Try: account %s set display_name", cmd[1] );
[3cd4016]1342}
1343
[180ab31]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" );
[674a01d]1348        char s[16], *msg;
[180ab31]1349       
1350        if( last >= BITLBEE_VERSION_CODE )
1351                return;
1352       
[674a01d]1353        msg = help_get_whatsnew( &(global.help), last );
[180ab31]1354       
[674a01d]1355        if( msg )
[e67e513]1356                irc_rootmsg( irc, "%s: This seems to be your first time using this "
[180ab31]1357                                  "this version of BitlBee. Here's a list of new "
1358                                  "features you may like to know about:\n\n%s\n",
[674a01d]1359                                  irc->user->nick, msg );
1360       
1361        g_free( msg );
[180ab31]1362       
1363        g_snprintf( s, sizeof( s ), "%d", BITLBEE_VERSION_CODE );
1364        set_setstr( &irc->b->set, "last_version", s );
1365}
1366
[6c56f42]1367/* IMPORTANT: Keep this list sorted! The short command logic needs that. */
[8358691]1368command_t root_commands[] = {
[d860a8d]1369        { "account",        1, cmd_account,        0 },
[6c56f42]1370        { "add",            2, cmd_add,            0 },
[2272cb3]1371        { "allow",          1, cmd_allow,          0 },
[4c3519a]1372        { "blist",          0, cmd_blist,          0 },
[2272cb3]1373        { "block",          1, cmd_block,          0 },
[c133d4b8]1374        { "channel",        1, cmd_channel,        0 },
1375        { "chat",           1, cmd_chat,           0 },
[6c56f42]1376        { "drop",           1, cmd_drop,           0 },
[9d4352c]1377        { "ft",             0, cmd_transfer,       0 },
[f1d488e]1378        { "group",          1, cmd_group,          0 },
[6c56f42]1379        { "help",           0, cmd_help,           0 }, 
[060d066]1380        { "identify",       0, cmd_identify,       0 },
[aa44bdd]1381        { "info",           1, cmd_info,           0 },
[3cd4016]1382        { "nick",           1, cmd_nick,           0 },
[6c56f42]1383        { "no",             0, cmd_yesno,          0 },
[9d4352c]1384        { "qlist",          0, cmd_qlist,          0 },
[060d066]1385        { "register",       0, cmd_register,       0 },
[dbb0ce3]1386        { "remove",         1, cmd_remove,         0 },
[0298d11]1387        { "rename",         2, cmd_rename,         0 },
[6c56f42]1388        { "save",           0, cmd_save,           0 },
[0298d11]1389        { "set",            0, cmd_set,            0 },
[9d4352c]1390        { "transfer",       0, cmd_transfer,       0 },
[0298d11]1391        { "yes",            0, cmd_yesno,          0 },
[8358691]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 },
[0298d11]1403};
[8358691]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.