Scan media entities as well, not just url entities. This should expand more
[bitlbee.git] / root_commands.c
blobb46b076dcf1109462098dacfa26773f10484c3d8
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* User manager (root) commands */
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.
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.
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
26 #define BITLBEE_CORE
27 #include "commands.h"
28 #include "bitlbee.h"
29 #include "help.h"
30 #include "ipc.h"
32 void root_command_string( irc_t *irc, char *command )
34 root_command( irc, split_command_parts( command ) );
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_rootmsg( irc, "Not enough parameters given (need %d).", x ); \
45 return y; \
46 } \
47 } while( 0 )
49 void root_command( irc_t *irc, char *cmd[] )
51 int i, len;
53 if( !cmd[0] )
54 return;
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 )
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;
65 MIN_ARGS( root_commands[i].required_parameters );
67 root_commands[i].execute( irc, cmd );
68 return;
71 irc_rootmsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] );
74 static void cmd_help( irc_t *irc, char **cmd )
76 char param[80];
77 int i;
78 char *s;
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 );
87 s = help_get( &(global.help), param );
88 if( !s ) s = help_get( &(global.help), "" );
90 if( s )
92 irc_rootmsg( irc, "%s", s );
93 g_free( s );
95 else
97 irc_rootmsg( irc, "Error opening helpfile." );
101 static void cmd_account( irc_t *irc, char **cmd );
102 static void bitlbee_whatsnew( irc_t *irc );
104 static void cmd_identify( irc_t *irc, char **cmd )
106 storage_status_t status;
107 gboolean load = TRUE;
108 char *password = cmd[1];
110 if( irc->status & USTATUS_IDENTIFIED )
112 irc_rootmsg( irc, "You're already logged in." );
113 return;
116 if( cmd[1] == NULL )
119 else if( strncmp( cmd[1], "-no", 3 ) == 0 )
121 load = FALSE;
122 password = cmd[2];
123 if( password == NULL )
124 irc->status |= OPER_HACK_IDENTIFY_NOLOAD;
126 else if( strncmp( cmd[1], "-force", 6 ) == 0 )
128 password = cmd[2];
129 if( password == NULL )
130 irc->status |= OPER_HACK_IDENTIFY_FORCE;
132 else if( irc->b->accounts != NULL )
134 irc_rootmsg( irc,
135 "You're trying to identify yourself, but already have "
136 "at least one IM account set up. "
137 "Use \x02identify -noload\x02 or \x02identify -force\x02 "
138 "instead (see \x02help identify\x02)." );
139 return;
142 if( password == NULL )
144 irc_rootmsg( irc, "About to identify, use /OPER to enter the password" );
145 irc->status |= OPER_HACK_IDENTIFY;
146 return;
149 if( load )
150 status = storage_load( irc, password );
151 else
152 status = storage_check_pass( irc->user->nick, password );
154 switch (status) {
155 case STORAGE_INVALID_PASSWORD:
156 irc_rootmsg( irc, "Incorrect password" );
157 break;
158 case STORAGE_NO_SUCH_USER:
159 irc_rootmsg( irc, "The nick is (probably) not registered" );
160 break;
161 case STORAGE_OK:
162 irc_rootmsg( irc, "Password accepted%s",
163 load ? ", settings and accounts loaded" : "" );
164 irc_setpass( irc, password );
165 irc->status |= USTATUS_IDENTIFIED;
166 irc_umode_set( irc, "+R", 1 );
168 bitlbee_whatsnew( irc );
170 /* The following code is a bit hairy now. With takeover
171 support, we shouldn't immediately auto_connect in case
172 we're going to offer taking over an existing session.
173 Do it in 200ms since that should give the parent process
174 enough time to come back to us. */
175 if( load )
177 irc_channel_auto_joins( irc, NULL );
178 if( !set_getbool( &irc->default_channel->set, "auto_join" ) )
179 irc_channel_del_user( irc->default_channel, irc->user,
180 IRC_CDU_PART, "auto_join disabled "
181 "for this channel." );
182 if( set_getbool( &irc->b->set, "auto_connect" ) )
183 irc->login_source_id = b_timeout_add( 200,
184 cmd_identify_finish, irc );
187 /* If ipc_child_identify() returns FALSE, it means we're
188 already sure that there's no takeover target (only
189 possible in 1-process daemon mode). Start auto_connect
190 immediately. */
191 if( !ipc_child_identify( irc ) && load )
192 cmd_identify_finish( irc, 0, 0 );
194 break;
195 case STORAGE_OTHER_ERROR:
196 default:
197 irc_rootmsg( irc, "Unknown error while loading configuration" );
198 break;
202 gboolean cmd_identify_finish( gpointer data, gint fd, b_input_condition cond )
204 char *account_on[] = { "account", "on", NULL };
205 irc_t *irc = data;
207 if( set_getbool( &irc->b->set, "auto_connect" ) )
208 cmd_account( irc, account_on );
210 b_event_remove( irc->login_source_id );
211 irc->login_source_id = -1;
212 return FALSE;
215 static void cmd_register( irc_t *irc, char **cmd )
217 char s[16];
219 if( global.conf->authmode == AUTHMODE_REGISTERED )
221 irc_rootmsg( irc, "This server does not allow registering new accounts" );
222 return;
225 if( cmd[1] == NULL )
227 irc_rootmsg( irc, "About to register, use /OPER to enter the password" );
228 irc->status |= OPER_HACK_REGISTER;
229 return;
232 switch( storage_save( irc, cmd[1], FALSE ) ) {
233 case STORAGE_ALREADY_EXISTS:
234 irc_rootmsg( irc, "Nick is already registered" );
235 break;
237 case STORAGE_OK:
238 irc_rootmsg( irc, "Account successfully created" );
239 irc_setpass( irc, cmd[1] );
240 irc->status |= USTATUS_IDENTIFIED;
241 irc_umode_set( irc, "+R", 1 );
243 /* Set this var now, or anyone who logs in to his/her
244 newly created account for the first time gets the
245 whatsnew story. */
246 g_snprintf( s, sizeof( s ), "%d", BITLBEE_VERSION_CODE );
247 set_setstr( &irc->b->set, "last_version", s );
248 break;
250 default:
251 irc_rootmsg( irc, "Error registering" );
252 break;
256 static void cmd_drop( irc_t *irc, char **cmd )
258 storage_status_t status;
260 status = storage_remove (irc->user->nick, cmd[1]);
261 switch (status) {
262 case STORAGE_NO_SUCH_USER:
263 irc_rootmsg( irc, "That account does not exist" );
264 break;
265 case STORAGE_INVALID_PASSWORD:
266 irc_rootmsg( irc, "Password invalid" );
267 break;
268 case STORAGE_OK:
269 irc_setpass( irc, NULL );
270 irc->status &= ~USTATUS_IDENTIFIED;
271 irc_umode_set( irc, "-R", 1 );
272 irc_rootmsg( irc, "Account `%s' removed", irc->user->nick );
273 break;
274 default:
275 irc_rootmsg( irc, "Error: `%d'", status );
276 break;
280 static void cmd_save( irc_t *irc, char **cmd )
282 if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
283 irc_rootmsg( irc, "Please create an account first" );
284 else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
285 irc_rootmsg( irc, "Configuration saved" );
286 else
287 irc_rootmsg( irc, "Configuration could not be saved!" );
290 static void cmd_showset( irc_t *irc, set_t **head, char *key )
292 set_t *set;
293 char *val;
295 if( ( val = set_getstr( head, key ) ) )
296 irc_rootmsg( irc, "%s = `%s'", key, val );
297 else if( !( set = set_find( head, key ) ) )
299 irc_rootmsg( irc, "Setting `%s' does not exist.", key );
300 if( *head == irc->b->set )
301 irc_rootmsg( irc, "It might be an account or channel setting. "
302 "See \x02help account set\x02 and \x02help channel set\x02." );
304 else if( set->flags & SET_PASSWORD )
305 irc_rootmsg( irc, "%s = `********' (hidden)", key );
306 else
307 irc_rootmsg( irc, "%s is empty", key );
310 typedef set_t** (*cmd_set_findhead)( irc_t*, char* );
311 typedef int (*cmd_set_checkflags)( irc_t*, set_t *set );
313 static int cmd_set_real( irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags )
315 char *set_name = NULL, *value = NULL;
316 gboolean del = FALSE;
318 if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
320 MIN_ARGS( 2, 0 );
321 set_name = cmd[2];
322 del = TRUE;
324 else
326 set_name = cmd[1];
327 value = cmd[2];
330 if( set_name && ( value || del ) )
332 set_t *s = set_find( head, set_name );
333 int st;
335 if( s && checkflags && checkflags( irc, s ) == 0 )
336 return 0;
338 if( del )
339 st = set_reset( head, set_name );
340 else
341 st = set_setstr( head, set_name, value );
343 if( set_getstr( head, set_name ) == NULL &&
344 set_find( head, set_name ) )
346 /* This happens when changing the passwd, for example.
347 Showing these msgs instead gives slightly clearer
348 feedback. */
349 if( st )
350 irc_rootmsg( irc, "Setting changed successfully" );
351 else
352 irc_rootmsg( irc, "Failed to change setting" );
354 else
356 cmd_showset( irc, head, set_name );
359 else if( set_name )
361 cmd_showset( irc, head, set_name );
363 else
365 set_t *s = *head;
366 while( s )
368 if( set_isvisible( s ) )
369 cmd_showset( irc, &s, s->key );
370 s = s->next;
374 return 1;
377 static int cmd_account_set_checkflags( irc_t *irc, set_t *s )
379 account_t *a = s->data;
381 if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
383 irc_rootmsg( irc, "This setting can only be changed when the account is %s-line", "off" );
384 return 0;
386 else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
388 irc_rootmsg( irc, "This setting can only be changed when the account is %s-line", "on" );
389 return 0;
392 return 1;
395 static void cmd_account( irc_t *irc, char **cmd )
397 account_t *a;
398 int len;
400 if( global.conf->authmode == AUTHMODE_REGISTERED && !( irc->status & USTATUS_IDENTIFIED ) )
402 irc_rootmsg( irc, "This server only accepts registered users" );
403 return;
406 len = strlen( cmd[1] );
408 if( len >= 1 && g_strncasecmp( cmd[1], "add", len ) == 0 )
410 struct prpl *prpl;
412 MIN_ARGS( 3 );
414 if( cmd[4] == NULL )
416 for( a = irc->b->accounts; a; a = a->next )
417 if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
419 irc_rootmsg( irc, "Enter password for account %s "
420 "first (use /OPER)", a->tag );
421 return;
424 irc->status |= OPER_HACK_ACCOUNT_ADD;
427 prpl = find_protocol( cmd[2] );
429 if( prpl == NULL )
431 irc_rootmsg( irc, "Unknown protocol" );
432 return;
435 for( a = irc->b->accounts; a; a = a->next )
436 if( a->prpl == prpl && prpl->handle_cmp( a->user, cmd[3] ) == 0 )
437 irc_rootmsg( irc, "Warning: You already have an account with "
438 "protocol `%s' and username `%s'. Are you accidentally "
439 "trying to add it twice?", prpl->name, cmd[3] );
441 a = account_add( irc->b, prpl, cmd[3], cmd[4] ? cmd[4] : PASSWORD_PENDING );
442 if( cmd[5] )
444 irc_rootmsg( irc, "Warning: Passing a servername/other flags to `account add' "
445 "is now deprecated. Use `account set' instead." );
446 set_setstr( &a->set, "server", cmd[5] );
449 irc_rootmsg( irc, "Account successfully added with tag %s", a->tag );
451 if( cmd[4] == NULL )
453 set_t *oauth = set_find( &a->set, "oauth" );
454 if( oauth && bool2int( set_value( oauth ) ) )
456 *a->pass = '\0';
457 irc_rootmsg( irc, "No need to enter a password for this "
458 "account since it's using OAuth" );
460 else
462 irc_rootmsg( irc, "You can now use the /OPER command to "
463 "enter the password" );
464 if( oauth )
465 irc_rootmsg( irc, "Alternatively, enable OAuth if "
466 "the account supports it: account %s "
467 "set oauth on", a->tag );
471 return;
473 else if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
475 int i = 0;
477 if( strchr( irc->umode, 'b' ) )
478 irc_rootmsg( irc, "Account list:" );
480 for( a = irc->b->accounts; a; a = a->next )
482 char *con;
484 if( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) )
485 con = " (connected)";
486 else if( a->ic )
487 con = " (connecting)";
488 else if( a->reconnect )
489 con = " (awaiting reconnect)";
490 else
491 con = "";
493 irc_rootmsg( irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con );
495 i ++;
497 irc_rootmsg( irc, "End of account list" );
499 return;
501 else if( cmd[2] )
503 /* Try the following two only if cmd[2] == NULL */
505 else if( len >= 2 && g_strncasecmp( cmd[1], "on", len ) == 0 )
507 if ( irc->b->accounts )
509 irc_rootmsg( irc, "Trying to get all accounts connected..." );
511 for( a = irc->b->accounts; a; a = a->next )
512 if( !a->ic && a->auto_connect )
514 if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
515 irc_rootmsg( irc, "Enter password for account %s "
516 "first (use /OPER)", a->tag );
517 else
518 account_on( irc->b, a );
521 else
523 irc_rootmsg( irc, "No accounts known. Use `account add' to add one." );
526 return;
528 else if( len >= 2 && g_strncasecmp( cmd[1], "off", len ) == 0 )
530 irc_rootmsg( irc, "Deactivating all active (re)connections..." );
532 for( a = irc->b->accounts; a; a = a->next )
534 if( a->ic )
535 account_off( irc->b, a );
536 else if( a->reconnect )
537 cancel_auto_reconnect( a );
540 return;
543 MIN_ARGS( 2 );
544 len = strlen( cmd[2] );
546 /* At least right now, don't accept on/off/set/del as account IDs even
547 if they're a proper match, since people not familiar with the new
548 syntax yet may get a confusing/nasty surprise. */
549 if( g_strcasecmp( cmd[1], "on" ) == 0 ||
550 g_strcasecmp( cmd[1], "off" ) == 0 ||
551 g_strcasecmp( cmd[1], "set" ) == 0 ||
552 g_strcasecmp( cmd[1], "del" ) == 0 ||
553 ( a = account_get( irc->b, cmd[1] ) ) == NULL )
555 irc_rootmsg( irc, "Could not find account `%s'. Note that the syntax "
556 "of the account command changed, see \x02help account\x02.", cmd[1] );
558 return;
561 if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
563 if( a->ic )
565 irc_rootmsg( irc, "Account is still logged in, can't delete" );
567 else
569 account_del( irc->b, a );
570 irc_rootmsg( irc, "Account deleted" );
573 else if( len >= 2 && g_strncasecmp( cmd[2], "on", len ) == 0 )
575 if( a->ic )
576 irc_rootmsg( irc, "Account already online" );
577 else if( strcmp( a->pass, PASSWORD_PENDING ) == 0 )
578 irc_rootmsg( irc, "Enter password for account %s "
579 "first (use /OPER)", a->tag );
580 else
581 account_on( irc->b, a );
583 else if( len >= 2 && g_strncasecmp( cmd[2], "off", len ) == 0 )
585 if( a->ic )
587 account_off( irc->b, a );
589 else if( a->reconnect )
591 cancel_auto_reconnect( a );
592 irc_rootmsg( irc, "Reconnect cancelled" );
594 else
596 irc_rootmsg( irc, "Account already offline" );
599 else if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
601 cmd_set_real( irc, cmd + 2, &a->set, cmd_account_set_checkflags );
603 else
605 irc_rootmsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[2] );
609 static void cmd_channel( irc_t *irc, char **cmd )
611 irc_channel_t *ic;
612 int len;
614 len = strlen( cmd[1] );
616 if( len >= 1 && g_strncasecmp( cmd[1], "list", len ) == 0 )
618 GSList *l;
619 int i = 0;
621 if( strchr( irc->umode, 'b' ) )
622 irc_rootmsg( irc, "Channel list:" );
624 for( l = irc->channels; l; l = l->next )
626 irc_channel_t *ic = l->data;
628 irc_rootmsg( irc, "%2d. %s, %s channel%s", i, ic->name,
629 set_getstr( &ic->set, "type" ),
630 ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "" );
632 i ++;
634 irc_rootmsg( irc, "End of channel list" );
636 return;
639 if( ( ic = irc_channel_get( irc, cmd[1] ) ) == NULL )
641 /* If this doesn't match any channel, maybe this is the short
642 syntax (only works when used inside a channel). */
643 if( ( ic = irc->root->last_channel ) &&
644 ( len = strlen( cmd[1] ) ) &&
645 g_strncasecmp( cmd[1], "set", len ) == 0 )
646 cmd_set_real( irc, cmd + 1, &ic->set, NULL );
647 else
648 irc_rootmsg( irc, "Could not find channel `%s'", cmd[1] );
650 return;
653 MIN_ARGS( 2 );
654 len = strlen( cmd[2] );
656 if( len >= 1 && g_strncasecmp( cmd[2], "set", len ) == 0 )
658 cmd_set_real( irc, cmd + 2, &ic->set, NULL );
660 else if( len >= 1 && g_strncasecmp( cmd[2], "del", len ) == 0 )
662 if( !( ic->flags & IRC_CHANNEL_JOINED ) &&
663 ic != ic->irc->default_channel )
665 irc_rootmsg( irc, "Channel %s deleted.", ic->name );
666 irc_channel_free( ic );
668 else
669 irc_rootmsg( irc, "Couldn't remove channel (main channel %s or "
670 "channels you're still in cannot be deleted).",
671 irc->default_channel->name );
673 else
675 irc_rootmsg( irc, "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel", cmd[1] );
679 static void cmd_add( irc_t *irc, char **cmd )
681 account_t *a;
682 int add_on_server = 1;
684 if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
686 MIN_ARGS( 3 );
687 add_on_server = 0;
688 cmd ++;
691 if( !( a = account_get( irc->b, cmd[1] ) ) )
693 irc_rootmsg( irc, "Invalid account" );
694 return;
696 else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
698 irc_rootmsg( irc, "That account is not on-line" );
699 return;
702 if( cmd[3] )
704 if( !nick_ok( cmd[3] ) )
706 irc_rootmsg( irc, "The requested nick `%s' is invalid", cmd[3] );
707 return;
709 else if( irc_user_by_name( irc, cmd[3] ) )
711 irc_rootmsg( irc, "The requested nick `%s' already exists", cmd[3] );
712 return;
714 else
716 nick_set_raw( a, cmd[2], cmd[3] );
720 if( add_on_server )
722 irc_channel_t *ic;
723 char *s, *group = NULL;;
725 if( ( ic = irc->root->last_channel ) &&
726 ( s = set_getstr( &ic->set, "fill_by" ) ) &&
727 strcmp( s, "group" ) == 0 &&
728 ( group = set_getstr( &ic->set, "group" ) ) )
729 irc_rootmsg( irc, "Adding `%s' to contact list (group %s)",
730 cmd[2], group );
731 else
732 irc_rootmsg( irc, "Adding `%s' to contact list", cmd[2] );
734 a->prpl->add_buddy( a->ic, cmd[2], group );
736 else
738 bee_user_t *bu;
739 irc_user_t *iu;
741 /* Only for add -tmp. For regular adds, this callback will
742 be called once the IM server confirms. */
743 if( ( bu = bee_user_new( irc->b, a->ic, cmd[2], BEE_USER_LOCAL ) ) &&
744 ( iu = bu->ui_data ) )
745 irc_rootmsg( irc, "Temporarily assigned nickname `%s' "
746 "to contact `%s'", iu->nick, cmd[2] );
751 static void cmd_remove( irc_t *irc, char **cmd )
753 irc_user_t *iu;
754 bee_user_t *bu;
755 char *s;
757 if( !( iu = irc_user_by_name( irc, cmd[1] ) ) || !( bu = iu->bu ) )
759 irc_rootmsg( irc, "Buddy `%s' not found", cmd[1] );
760 return;
762 s = g_strdup( bu->handle );
764 bu->ic->acc->prpl->remove_buddy( bu->ic, bu->handle, NULL );
765 nick_del( bu );
766 if( g_slist_find( irc->users, iu ) )
767 bee_user_free( irc->b, bu );
769 irc_rootmsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] );
770 g_free( s );
772 return;
775 static void cmd_info( irc_t *irc, char **cmd )
777 struct im_connection *ic;
778 account_t *a;
780 if( !cmd[2] )
782 irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
783 if( !iu || !iu->bu )
785 irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
786 return;
788 ic = iu->bu->ic;
789 cmd[2] = iu->bu->handle;
791 else if( !( a = account_get( irc->b, cmd[1] ) ) )
793 irc_rootmsg( irc, "Invalid account" );
794 return;
796 else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
798 irc_rootmsg( irc, "That account is not on-line" );
799 return;
802 if( !ic->acc->prpl->get_info )
804 irc_rootmsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
806 else
808 ic->acc->prpl->get_info( ic, cmd[2] );
812 static void cmd_rename( irc_t *irc, char **cmd )
814 irc_user_t *iu, *old;
815 gboolean del = g_strcasecmp( cmd[1], "-del" ) == 0;
817 iu = irc_user_by_name( irc, cmd[del ? 2 : 1] );
819 if( iu == NULL )
821 irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
823 else if( del )
825 if( iu->bu )
826 bee_irc_user_nick_reset( iu );
827 irc_rootmsg( irc, "Nickname reset to `%s'", iu->nick );
829 else if( iu == irc->user )
831 irc_rootmsg( irc, "Use /nick to change your own nickname" );
833 else if( !nick_ok( cmd[2] ) )
835 irc_rootmsg( irc, "Nick `%s' is invalid", cmd[2] );
837 else if( ( old = irc_user_by_name( irc, cmd[2] ) ) && old != iu )
839 irc_rootmsg( irc, "Nick `%s' already exists", cmd[2] );
841 else
843 if( !irc_user_set_nick( iu, cmd[2] ) )
845 irc_rootmsg( irc, "Error while changing nick" );
846 return;
849 if( iu == irc->root )
851 /* If we're called internally (user did "set root_nick"),
852 let's not go O(INF). :-) */
853 if( strcmp( cmd[0], "set_rename" ) != 0 )
854 set_setstr( &irc->b->set, "root_nick", cmd[2] );
856 else if( iu->bu )
858 nick_set( iu->bu, cmd[2] );
861 irc_rootmsg( irc, "Nick successfully changed" );
865 char *set_eval_root_nick( set_t *set, char *new_nick )
867 irc_t *irc = set->data;
869 if( strcmp( irc->root->nick, new_nick ) != 0 )
871 char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL };
873 cmd_rename( irc, cmd );
876 return strcmp( irc->root->nick, new_nick ) == 0 ? new_nick : SET_INVALID;
879 static void cmd_block( irc_t *irc, char **cmd )
881 struct im_connection *ic;
882 account_t *a;
884 if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
886 char *format;
887 GSList *l;
889 if( strchr( irc->umode, 'b' ) != NULL )
890 format = "%s\t%s";
891 else
892 format = "%-32.32s %-16.16s";
894 irc_rootmsg( irc, format, "Handle", "Nickname" );
895 for( l = a->ic->deny; l; l = l->next )
897 bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data );
898 irc_user_t *iu = bu ? bu->ui_data : NULL;
899 irc_rootmsg( irc, format, l->data, iu ? iu->nick : "(none)" );
901 irc_rootmsg( irc, "End of list." );
903 return;
905 else if( !cmd[2] )
907 irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
908 if( !iu || !iu->bu )
910 irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
911 return;
913 ic = iu->bu->ic;
914 cmd[2] = iu->bu->handle;
916 else if( !( a = account_get( irc->b, cmd[1] ) ) )
918 irc_rootmsg( irc, "Invalid account" );
919 return;
921 else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
923 irc_rootmsg( irc, "That account is not on-line" );
924 return;
927 if( !ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit )
929 irc_rootmsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
931 else
933 imc_rem_allow( ic, cmd[2] );
934 imc_add_block( ic, cmd[2] );
935 irc_rootmsg( irc, "Buddy `%s' moved from allow- to block-list", cmd[2] );
939 static void cmd_allow( irc_t *irc, char **cmd )
941 struct im_connection *ic;
942 account_t *a;
944 if( !cmd[2] && ( a = account_get( irc->b, cmd[1] ) ) && a->ic )
946 char *format;
947 GSList *l;
949 if( strchr( irc->umode, 'b' ) != NULL )
950 format = "%s\t%s";
951 else
952 format = "%-32.32s %-16.16s";
954 irc_rootmsg( irc, format, "Handle", "Nickname" );
955 for( l = a->ic->permit; l; l = l->next )
957 bee_user_t *bu = bee_user_by_handle( irc->b, a->ic, l->data );
958 irc_user_t *iu = bu ? bu->ui_data : NULL;
959 irc_rootmsg( irc, format, l->data, iu ? iu->nick : "(none)" );
961 irc_rootmsg( irc, "End of list." );
963 return;
965 else if( !cmd[2] )
967 irc_user_t *iu = irc_user_by_name( irc, cmd[1] );
968 if( !iu || !iu->bu )
970 irc_rootmsg( irc, "Nick `%s' does not exist", cmd[1] );
971 return;
973 ic = iu->bu->ic;
974 cmd[2] = iu->bu->handle;
976 else if( !( a = account_get( irc->b, cmd[1] ) ) )
978 irc_rootmsg( irc, "Invalid account" );
979 return;
981 else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
983 irc_rootmsg( irc, "That account is not on-line" );
984 return;
987 if( !ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit )
989 irc_rootmsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
991 else
993 imc_rem_block( ic, cmd[2] );
994 imc_add_allow( ic, cmd[2] );
996 irc_rootmsg( irc, "Buddy `%s' moved from block- to allow-list", cmd[2] );
1000 static void cmd_yesno( irc_t *irc, char **cmd )
1002 query_t *q = NULL;
1003 int numq = 0;
1005 if( irc->queries == NULL )
1007 /* Alright, alright, let's add a tiny easter egg here. */
1008 static irc_t *last_irc = NULL;
1009 static time_t last_time = 0;
1010 static int times = 0;
1011 static const char *msg[] = {
1012 "Oh yeah, that's right.",
1013 "Alright, alright. Now go back to work.",
1014 "Buuuuuuuuuuuuuuuurp... Excuse me!",
1015 "Yes?",
1016 "No?",
1019 if( last_irc == irc && time( NULL ) - last_time < 15 )
1021 if( ( ++times >= 3 ) )
1023 irc_rootmsg( irc, "%s", msg[rand()%(sizeof(msg)/sizeof(char*))] );
1024 last_irc = NULL;
1025 times = 0;
1026 return;
1029 else
1031 last_time = time( NULL );
1032 last_irc = irc;
1033 times = 0;
1036 irc_rootmsg( irc, "Did I ask you something?" );
1037 return;
1040 /* If there's an argument, the user seems to want to answer another question than the
1041 first/last (depending on the query_order setting) one. */
1042 if( cmd[1] )
1044 if( sscanf( cmd[1], "%d", &numq ) != 1 )
1046 irc_rootmsg( irc, "Invalid query number" );
1047 return;
1050 for( q = irc->queries; q; q = q->next, numq -- )
1051 if( numq == 0 )
1052 break;
1054 if( !q )
1056 irc_rootmsg( irc, "Uhm, I never asked you something like that..." );
1057 return;
1061 if( g_strcasecmp( cmd[0], "yes" ) == 0 )
1062 query_answer( irc, q, 1 );
1063 else if( g_strcasecmp( cmd[0], "no" ) == 0 )
1064 query_answer( irc, q, 0 );
1067 static void cmd_set( irc_t *irc, char **cmd )
1069 cmd_set_real( irc, cmd, &irc->b->set, NULL );
1072 static void cmd_blist( irc_t *irc, char **cmd )
1074 int online = 0, away = 0, offline = 0;
1075 GSList *l;
1076 char s[256];
1077 char *format;
1078 int n_online = 0, n_away = 0, n_offline = 0;
1080 if( cmd[1] && g_strcasecmp( cmd[1], "all" ) == 0 )
1081 online = offline = away = 1;
1082 else if( cmd[1] && g_strcasecmp( cmd[1], "offline" ) == 0 )
1083 offline = 1;
1084 else if( cmd[1] && g_strcasecmp( cmd[1], "away" ) == 0 )
1085 away = 1;
1086 else if( cmd[1] && g_strcasecmp( cmd[1], "online" ) == 0 )
1087 online = 1;
1088 else
1089 online = away = 1;
1091 if( strchr( irc->umode, 'b' ) != NULL )
1092 format = "%s\t%s\t%s";
1093 else
1094 format = "%-16.16s %-40.40s %s";
1096 irc_rootmsg( irc, format, "Nick", "Handle/Account", "Status" );
1098 if( irc->root->last_channel &&
1099 strcmp( set_getstr( &irc->root->last_channel->set, "type" ), "control" ) != 0 )
1100 irc->root->last_channel = NULL;
1102 for( l = irc->users; l; l = l->next )
1104 irc_user_t *iu = l->data;
1105 bee_user_t *bu = iu->bu;
1107 if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1108 ( bu->flags & ( BEE_USER_ONLINE | BEE_USER_AWAY ) ) != BEE_USER_ONLINE )
1109 continue;
1111 if( online == 1 )
1113 char st[256] = "Online";
1115 if( bu->status_msg )
1116 g_snprintf( st, sizeof( st ) - 1, "Online (%s)", bu->status_msg );
1118 g_snprintf( s, sizeof( s ) - 1, "%s %s", bu->handle, bu->ic->acc->tag );
1119 irc_rootmsg( irc, format, iu->nick, s, st );
1122 n_online ++;
1125 for( l = irc->users; l; l = l->next )
1127 irc_user_t *iu = l->data;
1128 bee_user_t *bu = iu->bu;
1130 if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1131 !( bu->flags & BEE_USER_ONLINE ) || !( bu->flags & BEE_USER_AWAY ) )
1132 continue;
1134 if( away == 1 )
1136 g_snprintf( s, sizeof( s ) - 1, "%s %s", bu->handle, bu->ic->acc->tag );
1137 irc_rootmsg( irc, format, iu->nick, s, irc_user_get_away( iu ) );
1139 n_away ++;
1142 for( l = irc->users; l; l = l->next )
1144 irc_user_t *iu = l->data;
1145 bee_user_t *bu = iu->bu;
1147 if( !bu || ( irc->root->last_channel && !irc_channel_wants_user( irc->root->last_channel, iu ) ) ||
1148 bu->flags & BEE_USER_ONLINE )
1149 continue;
1151 if( offline == 1 )
1153 g_snprintf( s, sizeof( s ) - 1, "%s %s", bu->handle, bu->ic->acc->tag );
1154 irc_rootmsg( irc, format, iu->nick, s, "Offline" );
1156 n_offline ++;
1159 irc_rootmsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline );
1162 static void cmd_qlist( irc_t *irc, char **cmd )
1164 query_t *q = irc->queries;
1165 int num;
1167 if( !q )
1169 irc_rootmsg( irc, "There are no pending questions." );
1170 return;
1173 irc_rootmsg( irc, "Pending queries:" );
1175 for( num = 0; q; q = q->next, num ++ )
1176 if( q->ic ) /* Not necessary yet, but it might come later */
1177 irc_rootmsg( irc, "%d, %s: %s", num, q->ic->acc->tag, q->question );
1178 else
1179 irc_rootmsg( irc, "%d, BitlBee: %s", num, q->question );
1182 static void cmd_chat( irc_t *irc, char **cmd )
1184 account_t *acc;
1186 if( g_strcasecmp( cmd[1], "add" ) == 0 )
1188 char *channel, *s;
1189 struct irc_channel *ic;
1191 MIN_ARGS( 3 );
1193 if( !( acc = account_get( irc->b, cmd[2] ) ) )
1195 irc_rootmsg( irc, "Invalid account" );
1196 return;
1198 else if( !acc->prpl->chat_join )
1200 irc_rootmsg( irc, "Named chatrooms not supported on that account." );
1201 return;
1204 if( cmd[4] == NULL )
1206 channel = g_strdup( cmd[3] );
1207 if( ( s = strchr( channel, '@' ) ) )
1208 *s = 0;
1210 else
1212 channel = g_strdup( cmd[4] );
1215 if( strchr( CTYPES, channel[0] ) == NULL )
1217 s = g_strdup_printf( "#%s", channel );
1218 g_free( channel );
1219 channel = s;
1221 irc_channel_name_strip( channel );
1224 if( ( ic = irc_channel_new( irc, channel ) ) &&
1225 set_setstr( &ic->set, "type", "chat" ) &&
1226 set_setstr( &ic->set, "chat_type", "room" ) &&
1227 set_setstr( &ic->set, "account", cmd[2] ) &&
1228 set_setstr( &ic->set, "room", cmd[3] ) )
1230 irc_rootmsg( irc, "Chatroom successfully added." );
1232 else
1234 if( ic )
1235 irc_channel_free( ic );
1237 irc_rootmsg( irc, "Could not add chatroom." );
1239 g_free( channel );
1241 else if( g_strcasecmp( cmd[1], "with" ) == 0 )
1243 irc_user_t *iu;
1245 MIN_ARGS( 2 );
1247 if( ( iu = irc_user_by_name( irc, cmd[2] ) ) &&
1248 iu->bu && iu->bu->ic->acc->prpl->chat_with )
1250 if( !iu->bu->ic->acc->prpl->chat_with( iu->bu->ic, iu->bu->handle ) )
1252 irc_rootmsg( irc, "(Possible) failure while trying to open "
1253 "a groupchat with %s.", iu->nick );
1256 else
1258 irc_rootmsg( irc, "Can't open a groupchat with %s.", cmd[2] );
1261 else if( g_strcasecmp( cmd[1], "list" ) == 0 ||
1262 g_strcasecmp( cmd[1], "set" ) == 0 ||
1263 g_strcasecmp( cmd[1], "del" ) == 0 )
1265 irc_rootmsg( irc, "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command." );
1266 cmd_channel( irc, cmd );
1268 else
1270 irc_rootmsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
1274 static void cmd_group( irc_t *irc, char **cmd )
1276 GSList *l;
1277 int len;
1279 len = strlen( cmd[1] );
1280 if( g_strncasecmp( cmd[1], "list", len ) == 0 )
1282 int n = 0;
1284 if( strchr( irc->umode, 'b' ) )
1285 irc_rootmsg( irc, "Group list:" );
1287 for( l = irc->b->groups; l; l = l->next )
1289 bee_group_t *bg = l->data;
1290 irc_rootmsg( irc, "%d. %s", n ++, bg->name );
1292 irc_rootmsg( irc, "End of group list" );
1294 else
1296 irc_rootmsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group", cmd[1] );
1300 static void cmd_transfer( irc_t *irc, char **cmd )
1302 GSList *files = irc->file_transfers;
1303 enum { LIST, REJECT, CANCEL };
1304 int subcmd = LIST;
1305 int fid;
1307 if( !files )
1309 irc_rootmsg( irc, "No pending transfers" );
1310 return;
1313 if( cmd[1] && ( strcmp( cmd[1], "reject" ) == 0 ) )
1315 subcmd = REJECT;
1317 else if( cmd[1] && ( strcmp( cmd[1], "cancel" ) == 0 ) &&
1318 cmd[2] && ( sscanf( cmd[2], "%d", &fid ) == 1 ) )
1320 subcmd = CANCEL;
1323 for( ; files; files = g_slist_next( files ) )
1325 file_transfer_t *file = files->data;
1327 switch( subcmd ) {
1328 case LIST:
1329 if ( file->status == FT_STATUS_LISTENING )
1330 irc_rootmsg( irc,
1331 "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name);
1332 else
1334 int kb_per_s = 0;
1335 time_t diff = time( NULL ) - file->started ? : 1;
1336 if ( ( file->started > 0 ) && ( file->bytes_transferred > 0 ) )
1337 kb_per_s = file->bytes_transferred / 1024 / diff;
1339 irc_rootmsg( irc,
1340 "Pending file(id %d): %s (%10zd/%zd kb, %d kb/s)", file->local_id, file->file_name,
1341 file->bytes_transferred/1024, file->file_size/1024, kb_per_s);
1343 break;
1344 case REJECT:
1345 if( file->status == FT_STATUS_LISTENING )
1347 irc_rootmsg( irc, "Rejecting file transfer for %s", file->file_name );
1348 imcb_file_canceled( file->ic, file, "Denied by user" );
1350 break;
1351 case CANCEL:
1352 if( file->local_id == fid )
1354 irc_rootmsg( irc, "Canceling file transfer for %s", file->file_name );
1355 imcb_file_canceled( file->ic, file, "Canceled by user" );
1357 break;
1362 static void cmd_nick( irc_t *irc, char **cmd )
1364 irc_rootmsg( irc, "This command is deprecated. Try: account %s set display_name", cmd[1] );
1367 /* Maybe this should be a stand-alone command as well? */
1368 static void bitlbee_whatsnew( irc_t *irc )
1370 int last = set_getint( &irc->b->set, "last_version" );
1371 char s[16], *msg;
1373 if( last >= BITLBEE_VERSION_CODE )
1374 return;
1376 msg = help_get_whatsnew( &(global.help), last );
1378 if( msg )
1379 irc_rootmsg( irc, "%s: This seems to be your first time using this "
1380 "this version of BitlBee. Here's a list of new "
1381 "features you may like to know about:\n\n%s\n",
1382 irc->user->nick, msg );
1384 g_free( msg );
1386 g_snprintf( s, sizeof( s ), "%d", BITLBEE_VERSION_CODE );
1387 set_setstr( &irc->b->set, "last_version", s );
1390 /* IMPORTANT: Keep this list sorted! The short command logic needs that. */
1391 command_t root_commands[] = {
1392 { "account", 1, cmd_account, 0 },
1393 { "add", 2, cmd_add, 0 },
1394 { "allow", 1, cmd_allow, 0 },
1395 { "blist", 0, cmd_blist, 0 },
1396 { "block", 1, cmd_block, 0 },
1397 { "channel", 1, cmd_channel, 0 },
1398 { "chat", 1, cmd_chat, 0 },
1399 { "drop", 1, cmd_drop, 0 },
1400 { "ft", 0, cmd_transfer, 0 },
1401 { "group", 1, cmd_group, 0 },
1402 { "help", 0, cmd_help, 0 },
1403 { "identify", 0, cmd_identify, 0 },
1404 { "info", 1, cmd_info, 0 },
1405 { "nick", 1, cmd_nick, 0 },
1406 { "no", 0, cmd_yesno, 0 },
1407 { "qlist", 0, cmd_qlist, 0 },
1408 { "register", 0, cmd_register, 0 },
1409 { "remove", 1, cmd_remove, 0 },
1410 { "rename", 2, cmd_rename, 0 },
1411 { "save", 0, cmd_save, 0 },
1412 { "set", 0, cmd_set, 0 },
1413 { "transfer", 0, cmd_transfer, 0 },
1414 { "yes", 0, cmd_yesno, 0 },
1415 /* Not expecting too many plugins adding root commands so just make a
1416 dumb array with some empty entried at the end. */
1417 { NULL },
1418 { NULL },
1419 { NULL },
1420 { NULL },
1421 { NULL },
1422 { NULL },
1423 { NULL },
1424 { NULL },
1425 { NULL },
1427 static const int num_root_commands = sizeof( root_commands ) / sizeof( command_t );
1429 gboolean root_command_add( const char *command, int params, void (*func)(irc_t *, char **args), int flags )
1431 int i;
1433 if( root_commands[num_root_commands-2].command )
1434 /* Planning fail! List is full. */
1435 return FALSE;
1437 for( i = 0; root_commands[i].command; i++ )
1439 if( g_strcasecmp( root_commands[i].command, command ) == 0 )
1440 return FALSE;
1441 else if( g_strcasecmp( root_commands[i].command, command ) > 0 )
1442 break;
1444 memmove( root_commands + i + 1, root_commands + i,
1445 sizeof( command_t ) * ( num_root_commands - i - 1 ) );
1447 root_commands[i].command = g_strdup( command );
1448 root_commands[i].required_parameters = params;
1449 root_commands[i].execute = func;
1450 root_commands[i].flags = flags;
1452 return TRUE;