aboutsummaryrefslogtreecommitdiffstats
path: root/root_commands.c
blob: 8770e707f9e78cd0891c65455e7d6241a6203063 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
generated by cgit v1.2.3 (git 2.25.1) at 2025-05-07 03:24:33 +0000
 


6
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
  /********************************************************************\
  * BitlBee -- An IRC to other IM-networks gateway                     *
  *                                                                    *
  * Copyright 2002-2004 Wilmer van der Gaast and others                *
  \********************************************************************/

/* User manager (root) commands                                         */

/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License with
  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
  Suite 330, Boston, MA  02111-1307  USA
*/

#define BITLBEE_CORE
#include "commands.h"
#include "crypting.h"
#include "bitlbee.h"
#include "help.h"

#include <string.h>

void root_command_string( irc_t *irc, user_t *u, char *command, int flags )
{
	char *cmd[IRC_MAX_ARGS];
	char *s;
	int k;
	char q = 0;
	
	memset( cmd, 0, sizeof( cmd ) );
	cmd[0] = command;
	k = 1;
	for( s = command; *s && k < ( IRC_MAX_ARGS - 1 ); s ++ )
		if( *s == ' ' && !q )
		{
			*s = 0;
			while( *++s == ' ' );
			if( *s == '"' || *s == '\'' )
			{
				q = *s;
				s ++;
			}
			if( *s )
			{
				cmd[k++] = s;
				s --;
			}
			else
			{
				break;
			}
		}
		else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) )
		{
			char *cpy;
			
			for( cpy = s; *cpy; cpy ++ )
				cpy[0] = cpy[1];
		}
		else if( *s == q )
		{
			q = *s = 0;
		}
	cmd[k] = NULL;
	
	root_command( irc, cmd );
}

#define MIN_ARGS( x, y... )                                                    \
	do                                                                     \
	{                                                                      \
		int blaat;                                                     \
		for( blaat = 0; blaat <= x; blaat ++ )                         \
			if( cmd[blaat] == NULL )                               \
			{                                                      \
				irc_usermsg( irc, "Not enough parameters given (need %d).", x ); \
				return y;                                      \
			}                                                      \
	} while( 0 )

void root_command( irc_t *irc, char *cmd[] )
{	
	int i;
	
	if( !cmd[0] )
		return;
	
	for( i = 0; commands[i].command; i++ )
		if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
		{
			MIN_ARGS( commands[i].required_parameters );
			
			commands[i].execute( irc, cmd );
			return;
		}
	
	irc_usermsg( irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[0] );
}

static void cmd_help( irc_t *irc, char **cmd )
{
	char param[80];
	int i;
	char *s;
	
	memset( param, 0, sizeof(param) );
	for ( i = 1; (cmd[i] != NULL && ( strlen(param) < (sizeof(param)-1) ) ); i++ ) {
		if ( i != 1 )	// prepend space except for the first parameter
			strcat(param, " ");
		strncat( param, cmd[i], sizeof(param) - strlen(param) - 1 );
	}

	s = help_get( &(global.help), param );
	if( !s ) s = help_get( &(global.help), "" );
	
	if( s )
	{
		irc_usermsg( irc, "%s", s );
		g_free( s );
	}
	else
	{
		irc_usermsg( irc, "Error opening helpfile." );
	}
}

static void cmd_account( irc_t *irc, char **cmd );

static void cmd_identify( irc_t *irc, char **cmd )
{
	storage_status_t status = storage_load( irc, cmd[1] );
	char *account_on[] = { "account", "on", NULL };
	
	switch (status) {
	case STORAGE_INVALID_PASSWORD:
		irc_usermsg( irc, "Incorrect password" );
		break;
	case STORAGE_NO_SUCH_USER:
		irc_usermsg( irc, "The nick is (probably) not registered" );
		break;
	case STORAGE_OK:
		irc_usermsg( irc, "Password accepted, settings and accounts loaded" );
		irc_setpass( irc, cmd[1] );
		irc->status |= USTATUS_IDENTIFIED;
		irc_umode_set( irc, "+R", 1 );
		if( set_getbool( &irc->set, "auto_connect" ) )
			cmd_account( irc, account_on );
		break;
	case STORAGE_OTHER_ERROR:
	default:
		irc_usermsg( irc, "Unknown error while loading configuration" );
		break;
	}
}

static void cmd_register( irc_t *irc, char **cmd )
{
	if( global.conf->authmode == AUTHMODE_REGISTERED )
	{
		irc_usermsg( irc, "This server does not allow registering new accounts" );
		return;
	}

	switch( storage_save( irc, cmd[1], FALSE ) ) {
		case STORAGE_ALREADY_EXISTS:
			irc_usermsg( irc, "Nick is already registered" );
			break;
			
		case STORAGE_OK:
			irc_usermsg( irc, "Account successfully created" );
			irc_setpass( irc, cmd[1] );
			irc->status |= USTATUS_IDENTIFIED;
			irc_umode_set( irc, "+R", 1 );
			break;

		default:
			irc_usermsg( irc, "Error registering" );
			break;
	}
}

static void cmd_drop( irc_t *irc, char **cmd )
{
	storage_status_t status;
	
	status = storage_remove (irc->nick, cmd[1]);
	switch (status) {
	case STORAGE_NO_SUCH_USER:
		irc_usermsg( irc, "That account does not exist" );
		break;
	case STORAGE_INVALID_PASSWORD:
		irc_usermsg( irc, "Password invalid" );
		break;
	case STORAGE_OK:
		irc_setpass( irc, NULL );
		irc->status &= ~USTATUS_IDENTIFIED;
		irc_umode_set( irc, "-R", 1 );
		irc_usermsg( irc, "Account `%s' removed", irc->nick );
		break;
	default:
		irc_usermsg( irc, "Error: `%d'", status );
		break;
	}
}

struct cmd_account_del_data
{
	account_t *a;
	irc_t *irc;
};

void cmd_account_del_yes( void *data )
{
	struct cmd_account_del_data *cad = data;
	account_t *a;
	
	for( a = cad->irc->accounts; a && a != cad->a; a = a->next );
	
	if( a == NULL )
	{
		irc_usermsg( cad->irc, "Account already deleted" );
	}
	else if( a->ic )
	{
		irc_usermsg( cad->irc, "Account is still logged in, can't delete" );
	}
	else
	{
		account_del( cad->irc, a );
		irc_usermsg( cad->irc, "Account deleted" );
	}
	g_free( data );
}

void cmd_account_del_no( void *data )
{
	g_free( data );
}

static void cmd_showset( irc_t *irc, set_t **head, char *key )
{
	char *val;
	
	if( ( val = set_getstr( head, key ) ) )
		irc_usermsg( irc, "%s = `%s'", key, val );
	else
		irc_usermsg( irc, "%s is empty", key );
}

typedef set_t** (*cmd_set_findhead)( irc_t*, char* );

static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead )
{
	char *set_full = NULL, *set_name = NULL, *tmp;
	set_t **head;
	
	if( cmd[1] && g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
	{
		MIN_ARGS( 2, 0 );
		set_full = cmd[2];
	}
	else
		set_full = cmd[1];
	
	if( findhead == NULL )
	{
		set_name = set_full;
		
		head = &irc->set;
	}
	else 
	{
		char *id;
		
		if( ( tmp = strchr( set_full, '/' ) ) )
		{
			id = g_strndup( set_full, ( tmp - set_full ) );
			set_name = tmp + 1;
		}
		else
		{
			id = g_strdup( set_full );
		}
		
		if( ( head = findhead( irc, id ) ) == NULL )
		{
			g_free( id );
			irc_usermsg( irc, "Could not find setting." );
			return 0;
		}
		g_free( id );
	}
	
	if( cmd[1] && cmd[2] && set_name )
	{
		set_t *s = set_find( head, set_name );
		int st;
		
		/*
		FIXME: Make these work again. Probably a gross hack.
		
		if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
		{
			irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" );
			return 0;
		}
		else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
		{
			irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" );
			return 0;
		}
		*/
		
		if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
			st = set_reset( head, set_name );
		else
			st = set_setstr( head, set_name, cmd[2] );
		
		if( set_getstr( head, set_name ) == NULL )
		{
			if( st )
				irc_usermsg( irc, "Setting changed successfully" );
			else
				irc_usermsg( irc, "Failed to change setting" );
		}
		else
		{
			cmd_showset( irc, head, set_name );
		}
	}
	else if( set_name )
	{
		cmd_showset( irc, head, set_name );
	}
	else
	{
		set_t *s = *head;
		while( s )
		{
			cmd_showset( irc, &s, s->key );
			s = s->next;
		}
	}
	
	return 1;
}

static set_t **cmd_account_set_findhead( irc_t *irc, char *id )
{
	account_t *a;
	
	if( ( a = account_get( irc, id ) ) )
		return &a->set;
	else
		return NULL;
}

static void cmd_account( irc_t *irc, char **cmd )
{
	account_t *a;
	
	if( global.conf->authmode == AUTHMODE_REGISTERED && !( irc->status & USTATUS_IDENTIFIED ) )
	{
		irc_usermsg( irc, "This server only accepts registered users" );
		return;
	}
	
	if( g_strcasecmp( cmd[1], "add" ) == 0 )
	{
		struct prpl *prpl;
		
		MIN_ARGS( 4 );
		
		prpl = find_protocol( cmd[2] );
		
		if( prpl == NULL )
		{
			irc_usermsg( irc, "Unknown protocol" );
			return;
		}

		a = account_add( irc, prpl, cmd[3], cmd[4] );
		if( cmd[5] )
		{
			irc_usermsg( irc, "Warning: Passing a servername/other flags to `account add' "
			                  "is now deprecated. Use `account set' instead." );
			set_setstr( &a->set, "server", cmd[5] );
		}
		
		irc_usermsg( irc, "Account successfully added" );
	}
	else if( g_strcasecmp( cmd[1], "del" ) == 0 )
	{
		MIN_ARGS( 2 );

		if( !( a = account_get( irc, cmd[2] ) ) )
		{
			irc_usermsg( irc, "Invalid account" );
		}
		else if( a->ic )
		{
			irc_usermsg( irc, "Account is still logged in, can't delete" );
		}
		else
		{
			struct cmd_account_del_data *cad;
			char *msg;
			
			cad = g_malloc( sizeof( struct cmd_account_del_data ) );
			cad->a = a;
			cad->irc = irc;
			
			msg = g_strdup_printf( "If you remove this account (%s(%s)), BitlBee will "
			                       "also forget all your saved nicknames. If you want "
			                       "to change your username/password, use the `account "
			                       "set' command. Are you sure you want to delete this "
			                       "account?", a->prpl->name, a->user );
			query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad );
			g_free( msg );
		}
	}
	else if( g_strcasecmp( cmd[1], "list" ) == 0 )
	{
		int i = 0;
		
		if( strchr( irc->umode, 'b' ) )
			irc_usermsg( irc, "Account list:" );
		
		for( a = irc->accounts; a; a = a->next )
		{
			char *con;
			
			if( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) )
				con = " (connected)";
			else if( a->ic )
				con = " (connecting)";
			else if( a->reconnect )
				con = " (awaiting reconnect)";
			else
				con = "";
			
			irc_usermsg( irc, "%2d. %s, %s%s", i, a->prpl->name, a->user, con );
			
			i ++;
		}
		irc_usermsg( irc, "End of account list" );
	}
	else if( g_strcasecmp( cmd[1], "on" ) == 0 )
	{
		if( cmd[2] )
		{
			if( ( a = account_get( irc, cmd[2] ) ) )
			{
				if( a->ic )
				{
					irc_usermsg( irc, "Account already online" );
					return;
				}
				else
				{
					account_on( irc, a );
				}
			}
			else
			{
				irc_usermsg( irc, "Invalid account" );
				return;
			}
		}
		else
		{
			if ( irc->accounts ) {
				irc_usermsg( irc, "Trying to get all accounts connected..." );
			
				for( a = irc->accounts; a; a = a->next )
					if( !a->ic && a->auto_connect )
						account_on( irc, a );
			} 
			else
			{
				irc_usermsg( irc, "No accounts known. Use `account add' to add one." );
			}
		}
	}
	else if( g_strcasecmp( cmd[1], "off" ) == 0 )
	{
		if( !cmd[2] )
		{
			irc_usermsg( irc, "Deactivating all active (re)connections..." );
			
			for( a = irc->accounts; a; a = a->next )
			{
				if( a->ic )
					account_off( irc, a );
				else if( a->reconnect )
					cancel_auto_reconnect( a );
			}
		}
		else if( ( a = account_get( irc, cmd[2] ) ) )
		{
			if( a->ic )
			{
				account_off( irc, a );
			}
			else if( a->reconnect )
			{
				cancel_auto_reconnect( a );
				irc_usermsg( irc, "Reconnect cancelled" );
			}
			else
			{
				irc_usermsg( irc, "Account already offline" );
				return;
			}
		}
		else
		{
			irc_usermsg( irc, "Invalid account" );
			return;
		}
	}
	else if( g_strcasecmp( cmd[1], "set" ) == 0 )
	{
		MIN_ARGS( 2 );
		
		cmd_set_real( irc, cmd + 1, cmd_account_set_findhead );
	}
	else
	{
		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[1] );
	}
}

static void cmd_add( irc_t *irc, char **cmd )
{
	account_t *a;
	int add_on_server = 1;
	
	if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
	{
		add_on_server = 0;
		cmd ++;
	}
	
	if( !( a = account_get( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Invalid account" );
		return;
	}
	else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
	{
		irc_usermsg( irc, "That account is not on-line" );
		return;
	}
	
	if( cmd[3] )
	{
		if( !nick_ok( cmd[3] ) )
		{
			irc_usermsg( irc, "The requested nick `%s' is invalid", cmd[3] );
			return;
		}
		else if( user_find( irc, cmd[3] ) )
		{
			irc_usermsg( irc, "The requested nick `%s' already exists", cmd[3] );
			return;
		}
		else
		{
			nick_set( a, cmd[2], cmd[3] );
		}
	}
	
	if( add_on_server )
		a->ic->acc->prpl->add_buddy( a->ic, cmd[2], NULL );
	else
		/* Yeah, officially this is a call-*back*... So if we just
		   called add_buddy, we'll wait for the IM server to respond
		   before we do this. */
		imcb_add_buddy( a->ic, cmd[2], NULL );
	
	irc_usermsg( irc, "Adding `%s' to your contact list", cmd[2]  );
}

static void cmd_info( irc_t *irc, char **cmd )
{
	struct im_connection *ic;
	account_t *a;
	
	if( !cmd[2] )
	{
		user_t *u = user_find( irc, cmd[1] );
		if( !u || !u->ic )
		{
			irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
			return;
		}
		ic = u->ic;
		cmd[2] = u->handle;
	}
	else if( !( a = account_get( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Invalid account" );
		return;
	}
	else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
	{
		irc_usermsg( irc, "That account is not on-line" );
		return;
	}
	
	if( !ic->acc->prpl->get_info )
	{
		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
	}
	else
	{
		ic->acc->prpl->get_info( ic, cmd[2] );
	}
}

static void cmd_rename( irc_t *irc, char **cmd )
{
	user_t *u;
	
	if( g_strcasecmp( cmd[1], irc->nick ) == 0 )
	{
		irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] );
	}
	else if( user_find( irc, cmd[2] ) && ( nick_cmp( cmd[1], cmd[2] ) != 0 ) )
	{
		irc_usermsg( irc, "Nick `%s' already exists", cmd[2] );
	}
	else if( !nick_ok( cmd[2] ) )
	{
		irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] );
	}
	else if( !( u = user_find( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
	}
	else
	{
		user_rename( irc, cmd[1], cmd[2] );
		irc_write( irc, ":%s!%s@%s NICK %s", cmd[1], u->user, u->host, cmd[2] );
		if( g_strcasecmp( cmd[1], irc->mynick ) == 0 )
		{
			g_free( irc->mynick );
			irc->mynick = g_strdup( cmd[2] );
			
			/* If we're called internally (user did "set root_nick"),
			   let's not go O(INF). :-) */
			if( strcmp( cmd[0], "set_rename" ) != 0 )
				set_setstr( &irc->set, "root_nick", cmd[2] );
		}
		else if( u->send_handler == buddy_send_handler )
		{
			nick_set( u->ic->acc, u->handle, cmd[2] );
		}
		
		irc_usermsg( irc, "Nick successfully changed" );
	}
}

char *set_eval_root_nick( set_t *set, char *new_nick )
{
	irc_t *irc = set->data;
	
	if( strcmp( irc->mynick, new_nick ) != 0 )
	{
		char *cmd[] = { "set_rename", irc->mynick, new_nick, NULL };
		
		cmd_rename( irc, cmd );
	}
	
	return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : SET_INVALID;
}

static void cmd_remove( irc_t *irc, char **cmd )
{
	user_t *u;
	char *s;
	
	if( !( u = user_find( irc, cmd[1] ) ) || !u->ic )
	{
		irc_usermsg( irc, "Buddy `%s' not found", cmd[1] );
		return;
	}
	s = g_strdup( u->handle );
	
	u->ic->acc->prpl->remove_buddy( u->ic, u->handle, NULL );
	nick_del( u->ic->acc, u->handle );
	user_del( irc, cmd[1] );
	
	irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] );
	g_free( s );
	
	return;
}

static void cmd_block( irc_t *irc, char **cmd )
{
	struct im_connection *ic;
	account_t *a;
	
	if( !cmd[2] && ( a = account_get( irc, cmd[1] ) ) && a->ic )
	{
		char *format;
		GSList *l;
		
		if( strchr( irc->umode, 'b' ) != NULL )
			format = "%s\t%s";
		else
			format = "%-32.32s  %-16.16s";
		
		irc_usermsg( irc, format, "Handle", "Nickname" );
		for( l = a->ic->deny; l; l = l->next )
		{
			user_t *u = user_findhandle( a->ic, l->data );
			irc_usermsg( irc, format, l->data, u ? u->nick : "(none)" );
		}
		irc_usermsg( irc, "End of list." );
		
		return;
	}
	else if( !cmd[2] )
	{
		user_t *u = user_find( irc, cmd[1] );
		if( !u || !u->ic )
		{
			irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
			return;
		}
		ic = u->ic;
		cmd[2] = u->handle;
	}
	else if( !( a = account_get( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Invalid account" );
		return;
	}
	else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
	{
		irc_usermsg( irc, "That account is not on-line" );
		return;
	}
	
	if( !ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit )
	{
		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
	}
	else
	{
		imc_rem_allow( ic, cmd[2] );
		imc_add_block( ic, cmd[2] );
		irc_usermsg( irc, "Buddy `%s' moved from your allow- to your block-list", cmd[2] );
	}
}

static void cmd_allow( irc_t *irc, char **cmd )
{
	struct im_connection *ic;
	account_t *a;
	
	if( !cmd[2] && ( a = account_get( irc, cmd[1] ) ) && a->ic )
	{
		char *format;
		GSList *l;
		
		if( strchr( irc->umode, 'b' ) != NULL )
			format = "%s\t%s";
		else
			format = "%-32.32s  %-16.16s";
		
		irc_usermsg( irc, format, "Handle", "Nickname" );
		for( l = a->ic->permit; l; l = l->next )
		{
			user_t *u = user_findhandle( a->ic, l->data );
			irc_usermsg( irc, format, l->data, u ? u->nick : "(none)" );
		}
		irc_usermsg( irc, "End of list." );
		
		return;
	}
	else if( !cmd[2] )
	{
		user_t *u = user_find( irc, cmd[1] );
		if( !u || !u->ic )
		{
			irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] );
			return;
		}
		ic = u->ic;
		cmd[2] = u->handle;
	}
	else if( !( a = account_get( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Invalid account" );
		return;
	}
	else if( !( ( ic = a->ic ) && ( a->ic->flags & OPT_LOGGED_IN ) ) )
	{
		irc_usermsg( irc, "That account is not on-line" );
		return;
	}
	
	if( !ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit )
	{
		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
	}
	else
	{
		imc_rem_block( ic, cmd[2] );
		imc_add_allow( ic, cmd[2] );
		
		irc_usermsg( irc, "Buddy `%s' moved from your block- to your allow-list", cmd[2] );
	}
}

static void cmd_yesno( irc_t *irc, char **cmd )
{
	query_t *q = NULL;
	int numq = 0;
	
	if( irc->queries == NULL )
	{
		irc_usermsg( irc, "Did I ask you something?" );
		return;
	}
	
	/* If there's an argument, the user seems to want to answer another question than the
	   first/last (depending on the query_order setting) one. */
	if( cmd[1] )
	{
		if( sscanf( cmd[1], "%d", &numq ) != 1 )
		{
			irc_usermsg( irc, "Invalid query number" );
			return;
		}
		
		for( q = irc->queries; q; q = q->next, numq -- )
			if( numq == 0 )
				break;
		
		if( !q )
		{
			irc_usermsg( irc, "Uhm, I never asked you something like that..." );
			return;
		}
	}
	
	if( g_strcasecmp( cmd[0], "yes" ) == 0 )
		query_answer( irc, q, 1 );
	else if( g_strcasecmp( cmd[0], "no" ) == 0 )
		query_answer( irc, q, 0 );
}

static void cmd_set( irc_t *irc, char **cmd )
{
	cmd_set_real( irc, cmd, NULL );
}

static void cmd_save( irc_t *irc, char **cmd )
{
	if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
		irc_usermsg( irc, "Please create an account first" );
	else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
		irc_usermsg( irc, "Configuration saved" );
	else
		irc_usermsg( irc, "Configuration could not be saved!" );
}

static void cmd_blist( irc_t *irc, char **cmd )
{
	int online = 0, away = 0, offline = 0;
	user_t *u;
	char s[256];
	char *format;
	int n_online = 0, n_away = 0, n_offline = 0;
	
	if( cmd[1] && g_strcasecmp( cmd[1], "all" ) == 0 )
		online = offline = away = 1;
	else if( cmd[1] && g_strcasecmp( cmd[1], "offline" ) == 0 )
		offline = 1;
	else if( cmd[1] && g_strcasecmp( cmd[1], "away" ) == 0 )
		away = 1;
	else if( cmd[1] && g_strcasecmp( cmd[1], "online" ) == 0 )
		online = 1;
	else
		online =  away = 1;
	
	if( strchr( irc->umode, 'b' ) != NULL )
		format = "%s\t%s\t%s";
	else
		format = "%-16.16s  %-40.40s  %s";
	
	irc_usermsg( irc, format, "Nick", "User/Host/Network", "Status" );
	
	for( u = irc->users; u; u = u->next ) if( u->ic && u->online && !u->away )
	{
		if( online == 1 )
		{
			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
			irc_usermsg( irc, format, u->nick, s, "Online" );
		}
		
		n_online ++;
	}

	for( u = irc->users; u; u = u->next ) if( u->ic && u->online && u->away )
	{
		if( away == 1 )
		{
			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
			irc_usermsg( irc, format, u->nick, s, u->away );
		}
		n_away ++;
	}
	
	for( u = irc->users; u; u = u->next ) if( u->ic && !u->online )
	{
		if( offline == 1 )
		{
			g_snprintf( s, sizeof( s ) - 1, "%s@%s %s(%s)", u->user, u->host, u->ic->acc->prpl->name, u->ic->acc->user );
			irc_usermsg( irc, format, u->nick, s, "Offline" );
		}
		n_offline ++;
	}
	
	irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline );
}

static void cmd_nick( irc_t *irc, char **cmd ) 
{
	account_t *a;

	if( !cmd[1] || !( a = account_get( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Invalid account");
	}
	else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
	{
		irc_usermsg( irc, "That account is not on-line" );
	}
	else if ( !cmd[2] ) 
	{
		irc_usermsg( irc, "Your name is `%s'" , a->ic->displayname ? a->ic->displayname : "NULL" );
	}
	else if ( !a->prpl->set_my_name ) 
	{
		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
	}
	else
	{
		irc_usermsg( irc, "Setting your name to `%s'", cmd[2] );
		
		a->prpl->set_my_name( a->ic, cmd[2] );
	}
}

static void cmd_qlist( irc_t *irc, char **cmd )
{
	query_t *q = irc->queries;
	int num;
	
	if( !q )
	{
		irc_usermsg( irc, "There are no pending questions." );
		return;
	}
	
	irc_usermsg( irc, "Pending queries:" );
	
	for( num = 0; q; q = q->next, num ++ )
		if( q->ic ) /* Not necessary yet, but it might come later */
			irc_usermsg( irc, "%d, %s(%s): %s", num, q->ic->acc->prpl->name, q->ic->acc->user, q->question );
		else
			irc_usermsg( irc, "%d, BitlBee: %s", num, q->question );
}

static void cmd_join_chat( irc_t *irc, char **cmd )
{
	irc_usermsg( irc, "This command is now obsolete. "
	                  "Please try the `chat' command instead." );
}

static set_t **cmd_chat_set_findhead( irc_t *irc, char *id )
{
	struct chat *c;
	
	if( ( c = chat_get( irc, id ) ) )
		return &c->set;
	else
		return NULL;
}

static void cmd_chat( irc_t *irc, char **cmd )
{
	account_t *acc;
	struct chat *c;
	
	if( g_strcasecmp( cmd[1], "add" ) == 0 )
	{
		char *channel, *s;
		
		MIN_ARGS( 3 );
		
		if( !( acc = account_get( irc, cmd[2] ) ) )
		{
			irc_usermsg( irc, "Invalid account" );
			return;
		}
		
		if( cmd[4] == NULL )
		{
			channel = g_strdup( cmd[3] );
			if( ( s = strchr( channel, '@' ) ) )
				*s = 0;
		}
		else
		{
			channel = g_strdup( cmd[4] );
		}
		
		if( strchr( CTYPES, channel[0] ) == NULL )
		{
			s = g_strdup_printf( "%c%s", CTYPES[0], channel );
			g_free( channel );
			channel = s;
		}
		
		if( ( c = chat_add( irc, acc, cmd[3], channel ) ) )
			irc_usermsg( irc, "Chatroom added successfully." );
		else
			irc_usermsg( irc, "Could not add chatroom." );
		
		g_free( channel );
	}
	else if( g_strcasecmp( cmd[1], "list" ) == 0 )
	{
		int i = 0;
		
		if( strchr( irc->umode, 'b' ) )
			irc_usermsg( irc, "Chatroom list:" );
		
		for( c = irc->chatrooms; c; c = c->next )
		{
			irc_usermsg( irc, "%2d. %s(%s) %s, %s", i, c->acc->prpl->name,
			                  c->acc->user, c->handle, c->channel );
			
			i ++;
		}
		irc_usermsg( irc, "End of chatroom list" );
	}
	else if( g_strcasecmp( cmd[1], "set" ) == 0 )
	{
		cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead );
	}
	else if( g_strcasecmp( cmd[1], "del" ) == 0 )
	{
		MIN_ARGS( 2 );
		
		if( ( c = chat_get( irc, cmd[2] ) ) )
		{
			chat_del( irc, c );
		}
		else
		{
			irc_usermsg( irc, "Could not remove chat." );
		}
	}
	else if( g_strcasecmp( cmd[1], "with" ) == 0 )
	{
		user_t *u;
		
		MIN_ARGS( 2 );
		
		if( ( u = user_find( irc, cmd[2] ) ) && u->ic && u->ic->acc->prpl->chat_with )
		{
			if( !u->ic->acc->prpl->chat_with( u->ic, u->handle ) )
			{
				irc_usermsg( irc, "(Possible) failure while trying to open "
				                  "a groupchat with %s.", u->nick );
			}
		}
		else
		{
			irc_usermsg( irc, "Can't open a groupchat with %s.", cmd[2] );
		}
	}
	else
	{
		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat", cmd[1] );
	}



#if 0
	account_t *a;
	struct im_connection *ic;
	char *chat, *channel, *nick = NULL, *password = NULL;
	struct groupchat *c;
	
	if( !( a = account_get( irc, cmd[1] ) ) )
	{
		irc_usermsg( irc, "Invalid account" );
		return;
	}
	else if( !( a->ic && ( a->ic->flags & OPT_LOGGED_IN ) ) )
	{
		irc_usermsg( irc, "That account is not on-line" );
		return;
	}
	else if( a->prpl->chat_join == NULL )
	{
		irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] );
		return;
	}
	ic = a->ic;
	
	chat = cmd[2];
	if( cmd[3] )
	{
		if( strchr( CTYPES, cmd[3][0] ) == NULL )
			channel = g_strdup_printf( "&%s", cmd[3] );
		else
			channel = g_strdup( cmd[3] );
	}
	else
	{
		char *s;
		
		channel = g_strdup_printf( "&%s", chat );
		if( ( s = strchr( channel, '@' ) ) )
			*s = 0;
	}
	if( cmd[3] && cmd[4] )
		nick = cmd[4];
	else
		nick = irc->nick;
	if( cmd[3] && cmd[4] && cmd[5] )
		password = cmd[5];
	
	if( !nick_ok( channel + 1 ) )
	{
		irc_usermsg( irc, "Invalid channel name: %s", channel );
		g_free( channel );
		return;
	}
	else if( g_strcasecmp( channel, irc->channel ) == 0 || irc_chat_by_channel( irc, channel ) )
	{
		irc_usermsg( irc, "Channel already exists: %s", channel );
		g_free( channel );
		return;
	}
	
	if( ( c = a->prpl->chat_join( ic, chat, nick, password ) ) )
	{
		g_free( c->channel );
		c->channel = channel;
	}
	else
	{
		irc_usermsg( irc, "Tried to join chat, not sure if this was successful" );
		g_free( channel );
	}
#endif
}

const command_t commands[] = {
	{ "help",           0, cmd_help,           0 }, 
	{ "identify",       1, cmd_identify,       0 },
	{ "register",       1, cmd_register,       0 },
	{ "drop",           1, cmd_drop,           0 },
	{ "account",        1, cmd_account,        0 },
	{ "add",            2, cmd_add,            0 },
	{ "info",           1, cmd_info,           0 },
	{ "rename",         2, cmd_rename,         0 },
	{ "remove",         1, cmd_remove,         0 },
	{ "block",          1, cmd_block,          0 },
	{ "allow",          1, cmd_allow,          0 },
	{ "save",           0, cmd_save,           0 },
	{ "set",            0, cmd_set,            0 },
	{ "yes",            0, cmd_yesno,          0 },
	{ "no",             0, cmd_yesno,          0 },
	{ "blist",          0, cmd_blist,          0 },
	{ "nick",           1, cmd_nick,           0 },
	{ "qlist",          0, cmd_qlist,          0 },
	{ "join_chat",      2, cmd_join_chat,      0 },
	{ "chat",           1, cmd_chat,           0 },
	{ NULL }
};