1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /* User manager (root) commands */
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
32 void root_command_string( irc_t
*irc
, char *command
)
34 root_command( irc
, split_command_parts( command
) );
37 #define MIN_ARGS( x, y... ) \
41 for( blaat = 0; blaat <= x; blaat ++ ) \
42 if( cmd[blaat] == NULL ) \
44 irc_rootmsg( irc, "Not enough parameters given (need %d).", x ); \
49 void root_command( irc_t
*irc
, char *cmd
[] )
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. */
65 MIN_ARGS( root_commands
[i
].required_parameters
);
67 root_commands
[i
].execute( irc
, cmd
);
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
)
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
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
), "" );
92 irc_rootmsg( irc
, "%s", s
);
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." );
119 else if( strncmp( cmd
[1], "-no", 3 ) == 0 )
123 if( password
== NULL
)
124 irc
->status
|= OPER_HACK_IDENTIFY_NOLOAD
;
126 else if( strncmp( cmd
[1], "-force", 6 ) == 0 )
129 if( password
== NULL
)
130 irc
->status
|= OPER_HACK_IDENTIFY_FORCE
;
132 else if( irc
->b
->accounts
!= NULL
)
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)." );
142 if( password
== NULL
)
144 irc_rootmsg( irc
, "About to identify, use /OPER to enter the password" );
145 irc
->status
|= OPER_HACK_IDENTIFY
;
150 status
= storage_load( irc
, password
);
152 status
= storage_check_pass( irc
->user
->nick
, password
);
155 case STORAGE_INVALID_PASSWORD
:
156 irc_rootmsg( irc
, "Incorrect password" );
158 case STORAGE_NO_SUCH_USER
:
159 irc_rootmsg( irc
, "The nick is (probably) not registered" );
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. */
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
191 if( !ipc_child_identify( irc
) && load
)
192 cmd_identify_finish( irc
, 0, 0 );
195 case STORAGE_OTHER_ERROR
:
197 irc_rootmsg( irc
, "Unknown error while loading configuration" );
202 gboolean
cmd_identify_finish( gpointer data
, gint fd
, b_input_condition cond
)
204 char *account_on
[] = { "account", "on", NULL
};
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;
215 static void cmd_register( irc_t
*irc
, char **cmd
)
219 if( global
.conf
->authmode
== AUTHMODE_REGISTERED
)
221 irc_rootmsg( irc
, "This server does not allow registering new accounts" );
227 irc_rootmsg( irc
, "About to register, use /OPER to enter the password" );
228 irc
->status
|= OPER_HACK_REGISTER
;
232 switch( storage_save( irc
, cmd
[1], FALSE
) ) {
233 case STORAGE_ALREADY_EXISTS
:
234 irc_rootmsg( irc
, "Nick is already registered" );
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
246 g_snprintf( s
, sizeof( s
), "%d", BITLBEE_VERSION_CODE
);
247 set_setstr( &irc
->b
->set
, "last_version", s
);
251 irc_rootmsg( irc
, "Error registering" );
256 static void cmd_drop( irc_t
*irc
, char **cmd
)
258 storage_status_t status
;
260 status
= storage_remove (irc
->user
->nick
, cmd
[1]);
262 case STORAGE_NO_SUCH_USER
:
263 irc_rootmsg( irc
, "That account does not exist" );
265 case STORAGE_INVALID_PASSWORD
:
266 irc_rootmsg( irc
, "Password invalid" );
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
);
275 irc_rootmsg( irc
, "Error: `%d'", status
);
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" );
287 irc_rootmsg( irc
, "Configuration could not be saved!" );
290 static void cmd_showset( irc_t
*irc
, set_t
**head
, char *key
)
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
);
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 )
330 if( set_name
&& ( value
|| del
) )
332 set_t
*s
= set_find( head
, set_name
);
335 if( s
&& checkflags
&& checkflags( irc
, s
) == 0 )
339 st
= set_reset( head
, set_name
);
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
350 irc_rootmsg( irc
, "Setting changed successfully" );
352 irc_rootmsg( irc
, "Failed to change setting" );
356 cmd_showset( irc
, head
, set_name
);
361 cmd_showset( irc
, head
, set_name
);
368 if( set_isvisible( s
) )
369 cmd_showset( irc
, &s
, s
->key
);
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" );
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" );
395 static void cmd_account( irc_t
*irc
, char **cmd
)
400 if( global
.conf
->authmode
== AUTHMODE_REGISTERED
&& !( irc
->status
& USTATUS_IDENTIFIED
) )
402 irc_rootmsg( irc
, "This server only accepts registered users" );
406 len
= strlen( cmd
[1] );
408 if( len
>= 1 && g_strncasecmp( cmd
[1], "add", len
) == 0 )
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
);
424 irc
->status
|= OPER_HACK_ACCOUNT_ADD
;
427 prpl
= find_protocol( cmd
[2] );
431 irc_rootmsg( irc
, "Unknown protocol" );
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
);
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
);
453 set_t
*oauth
= set_find( &a
->set
, "oauth" );
454 if( oauth
&& bool2int( set_value( oauth
) ) )
457 irc_rootmsg( irc
, "No need to enter a password for this "
458 "account since it's using OAuth" );
462 irc_rootmsg( irc
, "You can now use the /OPER command to "
463 "enter the password" );
465 irc_rootmsg( irc
, "Alternatively, enable OAuth if "
466 "the account supports it: account %s "
467 "set oauth on", a
->tag
);
473 else if( len
>= 1 && g_strncasecmp( cmd
[1], "list", len
) == 0 )
477 if( strchr( irc
->umode
, 'b' ) )
478 irc_rootmsg( irc
, "Account list:" );
480 for( a
= irc
->b
->accounts
; a
; a
= a
->next
)
484 if( a
->ic
&& ( a
->ic
->flags
& OPT_LOGGED_IN
) )
485 con
= " (connected)";
487 con
= " (connecting)";
488 else if( a
->reconnect
)
489 con
= " (awaiting reconnect)";
493 irc_rootmsg( irc
, "%2d (%s): %s, %s%s", i
, a
->tag
, a
->prpl
->name
, a
->user
, con
);
497 irc_rootmsg( irc
, "End of account list" );
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
);
518 account_on( irc
->b
, a
);
523 irc_rootmsg( irc
, "No accounts known. Use `account add' to add one." );
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
)
535 account_off( irc
->b
, a
);
536 else if( a
->reconnect
)
537 cancel_auto_reconnect( a
);
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] );
561 if( len
>= 1 && g_strncasecmp( cmd
[2], "del", len
) == 0 )
565 irc_rootmsg( irc
, "Account is still logged in, can't delete" );
569 account_del( irc
->b
, a
);
570 irc_rootmsg( irc
, "Account deleted" );
573 else if( len
>= 2 && g_strncasecmp( cmd
[2], "on", len
) == 0 )
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
);
581 account_on( irc
->b
, a
);
583 else if( len
>= 2 && g_strncasecmp( cmd
[2], "off", len
) == 0 )
587 account_off( irc
->b
, a
);
589 else if( a
->reconnect
)
591 cancel_auto_reconnect( a
);
592 irc_rootmsg( irc
, "Reconnect cancelled" );
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
);
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
)
614 len
= strlen( cmd
[1] );
616 if( len
>= 1 && g_strncasecmp( cmd
[1], "list", len
) == 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)" : "" );
634 irc_rootmsg( irc
, "End of channel list" );
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
);
648 irc_rootmsg( irc
, "Could not find channel `%s'", cmd
[1] );
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
);
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
);
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
)
682 int add_on_server
= 1;
684 if( g_strcasecmp( cmd
[1], "-tmp" ) == 0 )
691 if( !( a
= account_get( irc
->b
, cmd
[1] ) ) )
693 irc_rootmsg( irc
, "Invalid account" );
696 else if( !( a
->ic
&& ( a
->ic
->flags
& OPT_LOGGED_IN
) ) )
698 irc_rootmsg( irc
, "That account is not on-line" );
704 if( !nick_ok( cmd
[3] ) )
706 irc_rootmsg( irc
, "The requested nick `%s' is invalid", cmd
[3] );
709 else if( irc_user_by_name( irc
, cmd
[3] ) )
711 irc_rootmsg( irc
, "The requested nick `%s' already exists", cmd
[3] );
716 nick_set_raw( a
, cmd
[2], cmd
[3] );
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)",
732 irc_rootmsg( irc
, "Adding `%s' to contact list", cmd
[2] );
734 a
->prpl
->add_buddy( a
->ic
, cmd
[2], group
);
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
)
757 if( !( iu
= irc_user_by_name( irc
, cmd
[1] ) ) || !( bu
= iu
->bu
) )
759 irc_rootmsg( irc
, "Buddy `%s' not found", cmd
[1] );
762 s
= g_strdup( bu
->handle
);
764 bu
->ic
->acc
->prpl
->remove_buddy( bu
->ic
, bu
->handle
, NULL
);
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] );
775 static void cmd_info( irc_t
*irc
, char **cmd
)
777 struct im_connection
*ic
;
782 irc_user_t
*iu
= irc_user_by_name( irc
, cmd
[1] );
785 irc_rootmsg( irc
, "Nick `%s' does not exist", cmd
[1] );
789 cmd
[2] = iu
->bu
->handle
;
791 else if( !( a
= account_get( irc
->b
, cmd
[1] ) ) )
793 irc_rootmsg( irc
, "Invalid account" );
796 else if( !( ( ic
= a
->ic
) && ( a
->ic
->flags
& OPT_LOGGED_IN
) ) )
798 irc_rootmsg( irc
, "That account is not on-line" );
802 if( !ic
->acc
->prpl
->get_info
)
804 irc_rootmsg( irc
, "Command `%s' not supported by this protocol", cmd
[0] );
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] );
821 irc_rootmsg( irc
, "Nick `%s' does not exist", cmd
[1] );
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] );
843 if( !irc_user_set_nick( iu
, cmd
[2] ) )
845 irc_rootmsg( irc
, "Error while changing nick" );
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] );
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
;
884 if( !cmd
[2] && ( a
= account_get( irc
->b
, cmd
[1] ) ) && a
->ic
)
889 if( strchr( irc
->umode
, 'b' ) != NULL
)
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." );
907 irc_user_t
*iu
= irc_user_by_name( irc
, cmd
[1] );
910 irc_rootmsg( irc
, "Nick `%s' does not exist", cmd
[1] );
914 cmd
[2] = iu
->bu
->handle
;
916 else if( !( a
= account_get( irc
->b
, cmd
[1] ) ) )
918 irc_rootmsg( irc
, "Invalid account" );
921 else if( !( ( ic
= a
->ic
) && ( a
->ic
->flags
& OPT_LOGGED_IN
) ) )
923 irc_rootmsg( irc
, "That account is not on-line" );
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] );
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
;
944 if( !cmd
[2] && ( a
= account_get( irc
->b
, cmd
[1] ) ) && a
->ic
)
949 if( strchr( irc
->umode
, 'b' ) != NULL
)
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." );
967 irc_user_t
*iu
= irc_user_by_name( irc
, cmd
[1] );
970 irc_rootmsg( irc
, "Nick `%s' does not exist", cmd
[1] );
974 cmd
[2] = iu
->bu
->handle
;
976 else if( !( a
= account_get( irc
->b
, cmd
[1] ) ) )
978 irc_rootmsg( irc
, "Invalid account" );
981 else if( !( ( ic
= a
->ic
) && ( a
->ic
->flags
& OPT_LOGGED_IN
) ) )
983 irc_rootmsg( irc
, "That account is not on-line" );
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] );
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
)
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!",
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*))] );
1031 last_time
= time( NULL
);
1036 irc_rootmsg( irc
, "Did I ask you something?" );
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. */
1044 if( sscanf( cmd
[1], "%d", &numq
) != 1 )
1046 irc_rootmsg( irc
, "Invalid query number" );
1050 for( q
= irc
->queries
; q
; q
= q
->next
, numq
-- )
1056 irc_rootmsg( irc
, "Uhm, I never asked you something like that..." );
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;
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 )
1084 else if( cmd
[1] && g_strcasecmp( cmd
[1], "away" ) == 0 )
1086 else if( cmd
[1] && g_strcasecmp( cmd
[1], "online" ) == 0 )
1091 if( strchr( irc
->umode
, 'b' ) != NULL
)
1092 format
= "%s\t%s\t%s";
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
)
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
);
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
) )
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
) );
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
)
1153 g_snprintf( s
, sizeof( s
) - 1, "%s %s", bu
->handle
, bu
->ic
->acc
->tag
);
1154 irc_rootmsg( irc
, format
, iu
->nick
, s
, "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
;
1169 irc_rootmsg( irc
, "There are no pending questions." );
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
);
1179 irc_rootmsg( irc
, "%d, BitlBee: %s", num
, q
->question
);
1182 static void cmd_chat( irc_t
*irc
, char **cmd
)
1186 if( g_strcasecmp( cmd
[1], "add" ) == 0 )
1189 struct irc_channel
*ic
;
1193 if( !( acc
= account_get( irc
->b
, cmd
[2] ) ) )
1195 irc_rootmsg( irc
, "Invalid account" );
1198 else if( !acc
->prpl
->chat_join
)
1200 irc_rootmsg( irc
, "Named chatrooms not supported on that account." );
1204 if( cmd
[4] == NULL
)
1206 channel
= g_strdup( cmd
[3] );
1207 if( ( s
= strchr( channel
, '@' ) ) )
1212 channel
= g_strdup( cmd
[4] );
1215 if( strchr( CTYPES
, channel
[0] ) == NULL
)
1217 s
= g_strdup_printf( "#%s", channel
);
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." );
1235 irc_channel_free( ic
);
1237 irc_rootmsg( irc
, "Could not add chatroom." );
1241 else if( g_strcasecmp( cmd
[1], "with" ) == 0 )
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
);
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
);
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
)
1279 len
= strlen( cmd
[1] );
1280 if( g_strncasecmp( cmd
[1], "list", len
) == 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" );
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
};
1309 irc_rootmsg( irc
, "No pending transfers" );
1313 if( cmd
[1] && ( strcmp( cmd
[1], "reject" ) == 0 ) )
1317 else if( cmd
[1] && ( strcmp( cmd
[1], "cancel" ) == 0 ) &&
1318 cmd
[2] && ( sscanf( cmd
[2], "%d", &fid
) == 1 ) )
1323 for( ; files
; files
= g_slist_next( files
) )
1325 file_transfer_t
*file
= files
->data
;
1329 if ( file
->status
== FT_STATUS_LISTENING
)
1331 "Pending file(id %d): %s (Listening...)", file
->local_id
, file
->file_name
);
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
;
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
);
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" );
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" );
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" );
1373 if( last
>= BITLBEE_VERSION_CODE
)
1376 msg
= help_get_whatsnew( &(global
.help
), last
);
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
);
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. */
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
)
1433 if( root_commands
[num_root_commands
-2].command
)
1434 /* Planning fail! List is full. */
1437 for( i
= 0; root_commands
[i
].command
; i
++ )
1439 if( g_strcasecmp( root_commands
[i
].command
, command
) == 0 )
1441 else if( g_strcasecmp( root_commands
[i
].command
, command
) > 0 )
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
;