Index: root_commands.c
===================================================================
--- root_commands.c	(revision 23c4e648e3d38336f949498d0b93e5b399087e44)
+++ root_commands.c	(revision e9cf291a52097d5b9428b8d1650cc691d5cc5dc8)
@@ -29,4 +29,5 @@
 #include "bitlbee.h"
 #include "help.h"
+#include "chat.h"
 
 #include <string.h>
@@ -78,4 +79,16 @@
 }
 
+#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[] )
 {	
@@ -88,9 +101,6 @@
 		if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 )
 		{
-			if( !cmd[commands[i].required_parameters] )
-			{
-				irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters );
-				return;
-			}
+			MIN_ARGS( commands[i].required_parameters );
+			
 			commands[i].execute( irc, cmd );
 			return;
@@ -131,6 +141,12 @@
 static void cmd_identify( irc_t *irc, char **cmd )
 {
-	storage_status_t status = storage_load( irc->nick, cmd[1], irc );
+	storage_status_t status = storage_load( irc, cmd[1] );
 	char *account_on[] = { "account", "on", NULL };
+	
+	if( strchr( irc->umode, 'R' ) != NULL )
+	{
+		irc_usermsg( irc, "You're already logged in." );
+		return;
+	}
 	
 	switch (status) {
@@ -143,4 +159,6 @@
 	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" ) )
@@ -162,6 +180,5 @@
 	}
 
-	irc_setpass( irc, cmd[1] );
-	switch( storage_save( irc, FALSE )) {
+	switch( storage_save( irc, cmd[1], FALSE ) ) {
 		case STORAGE_ALREADY_EXISTS:
 			irc_usermsg( irc, "Nick is already registered" );
@@ -170,4 +187,5 @@
 		case STORAGE_OK:
 			irc_usermsg( irc, "Account successfully created" );
+			irc_setpass( irc, cmd[1] );
 			irc->status |= USTATUS_IDENTIFIED;
 			irc_umode_set( irc, "+R", 1 );
@@ -238,4 +256,129 @@
 }
 
+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* );
+typedef int (*cmd_set_checkflags)( irc_t*, set_t *set );
+
+static int cmd_set_real( irc_t *irc, char **cmd, cmd_set_findhead findhead, cmd_set_checkflags checkflags )
+{
+	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;
+		
+		if( s && checkflags && checkflags( irc, s ) == 0 )
+			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 int cmd_account_set_checkflags( irc_t *irc, set_t *s )
+{
+	account_t *a = s->data;
+	
+	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;
+	}
+	
+	return 1;
+}
+
 static void cmd_account( irc_t *irc, char **cmd )
 {
@@ -252,11 +395,7 @@
 		struct prpl *prpl;
 		
-		if( cmd[2] == NULL || cmd[3] == NULL || cmd[4] == NULL )
-		{
-			irc_usermsg( irc, "Not enough parameters" );
-			return;
-		}
-		
-		prpl = find_protocol(cmd[2]);
+		MIN_ARGS( 4 );
+		
+		prpl = find_protocol( cmd[2] );
 		
 		if( prpl == NULL )
@@ -278,9 +417,7 @@
 	else if( g_strcasecmp( cmd[1], "del" ) == 0 )
 	{
-		if( !cmd[2] )
-		{
-			irc_usermsg( irc, "Not enough parameters given (need %d)", 2 );
-		}
-		else if( !( a = account_get( irc, cmd[2] ) ) )
+		MIN_ARGS( 2 );
+
+		if( !( a = account_get( irc, cmd[2] ) ) )
 		{
 			irc_usermsg( irc, "Invalid account" );
@@ -410,84 +547,11 @@
 	else if( g_strcasecmp( cmd[1], "set" ) == 0 )
 	{
-		char *acc_handle, *set_name = NULL, *tmp;
-		
-		if( !cmd[2] )
-		{
-			irc_usermsg( irc, "Not enough parameters given (need %d)", 2 );
-			return;
-		}
-		
-		if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 )
-			acc_handle = g_strdup( cmd[3] );
-		else
-			acc_handle = g_strdup( cmd[2] );
-		
-		if( !acc_handle )
-		{
-			irc_usermsg( irc, "Not enough parameters given (need %d)", 3 );
-			return;
-		}
-		
-		if( ( tmp = strchr( acc_handle, '/' ) ) )
-		{
-			*tmp = 0;
-			set_name = tmp + 1;
-		}
-		
-		if( ( a = account_get( irc, acc_handle ) ) == NULL )
-		{
-			g_free( acc_handle );
-			irc_usermsg( irc, "Invalid account" );
-			return;
-		}
-		
-		if( cmd[3] && set_name )
-		{
-			set_t *s = set_find( &a->set, set_name );
-			
-			if( a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY )
-			{
-				g_free( acc_handle );
-				irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "off" );
-				return;
-			}
-			else if( !a->ic && s && s->flags & ACC_SET_ONLINE_ONLY )
-			{
-				g_free( acc_handle );
-				irc_usermsg( irc, "This setting can only be changed when the account is %s-line", "on" );
-				return;
-			}
-			
-			if( g_strncasecmp( cmd[2], "-del", 4 ) == 0 )
-				set_reset( &a->set, set_name );
-			else
-				set_setstr( &a->set, set_name, cmd[3] );
-		}
-		if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */
-		{
-			char *s = set_getstr( &a->set, set_name );
-			if( s )
-				irc_usermsg( irc, "%s = `%s'", set_name, s );
-			else
-				irc_usermsg( irc, "%s is empty", set_name );
-		}
-		else
-		{
-			set_t *s = a->set;
-			while( s )
-			{
-				if( s->value || s->def )
-					irc_usermsg( irc, "%s = `%s'", s->key, s->value ? s->value : s->def );
-				else
-					irc_usermsg( irc, "%s is empty", s->key );
-				s = s->next;
-			}
-		}
-		
-		g_free( acc_handle );
-	}
-	else
-	{
-		irc_usermsg( irc, "Unknown command: account %s. Please use \x02help commands\x02 to get a list of available commands.", cmd[1] );
+		MIN_ARGS( 2 );
+		
+		cmd_set_real( irc, cmd + 1, cmd_account_set_findhead, cmd_account_set_checkflags );
+	}
+	else
+	{
+		irc_usermsg( irc, "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "account", cmd[1] );
 	}
 }
@@ -500,4 +564,5 @@
 	if( g_strcasecmp( cmd[1], "-tmp" ) == 0 )
 	{
+		MIN_ARGS( 3 );
 		add_on_server = 0;
 		cmd ++;
@@ -610,4 +675,6 @@
 			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] );
@@ -633,5 +700,5 @@
 	}
 	
-	return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : NULL;
+	return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : SET_INVALID;
 }
 
@@ -817,46 +884,12 @@
 static void cmd_set( irc_t *irc, char **cmd )
 {
-	char *set_name = cmd[1];
-	
-	if( cmd[1] && cmd[2] )
-	{
-		if( g_strncasecmp( cmd[1], "-del", 4 ) == 0 )
-		{
-			set_reset( &irc->set, cmd[2] );
-			set_name = cmd[2];
-		}
-		else
-		{
-			set_setstr( &irc->set, cmd[1], cmd[2] );
-		}
-	}
-	if( set_name ) /* else 'forgotten' on purpose.. Must show new value after changing */
-	{
-		char *s = set_getstr( &irc->set, set_name );
- 		if( s )
-			irc_usermsg( irc, "%s = `%s'", set_name, s );
-		else
-			irc_usermsg( irc, "%s is empty", set_name );
-
-		if( strchr( set_name, '/' ) )
-			irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." );
-	}
-	else
-	{
-		set_t *s = irc->set;
-		while( s )
-		{
-			if( s->value || s->def )
-				irc_usermsg( irc, "%s = `%s'", s->key, s->value ? s->value : s->def );
-			else
-				irc_usermsg( irc, "%s is empty", s->key );
-			s = s->next;
-		}
-	}
+	cmd_set_real( irc, cmd, NULL, NULL );
 }
 
 static void cmd_save( irc_t *irc, char **cmd )
 {
-	if( storage_save( irc, TRUE ) == STORAGE_OK )
+	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
@@ -974,71 +1007,117 @@
 static void cmd_join_chat( irc_t *irc, char **cmd )
 {
-	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( cmd[3][0] != '#' && cmd[3][0] != '&' )
-			channel = g_strdup_printf( "&%s", cmd[3] );
-		else
+	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] );
-	}
-	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 );
+			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 );
-		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 );
+	}
+	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 )
+	{
+		MIN_ARGS( 2 );
+		
+		cmd_set_real( irc, cmd + 1, cmd_chat_set_findhead, NULL );
+	}
+	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] );
 	}
 }
@@ -1064,4 +1143,5 @@
 	{ "qlist",          0, cmd_qlist,          0 },
 	{ "join_chat",      2, cmd_join_chat,      0 },
+	{ "chat",           1, cmd_chat,           0 },
 	{ NULL }
 };
