diff --git a/src/irc/proxy/Makefile.am b/src/irc/proxy/Makefile.am
index e4fd73b6..a8259c7f 100644
--- a/src/irc/proxy/Makefile.am
+++ b/src/irc/proxy/Makefile.am
@@ -12,5 +12,4 @@ libproxy_la_SOURCES = \
 	listen.c
 
 noinst_HEADERS = \
-	proxy.h \
 	module.h
diff --git a/src/irc/proxy/dump.c b/src/irc/proxy/dump.c
index eb222956..1b848974 100644
--- a/src/irc/proxy/dump.c
+++ b/src/irc/proxy/dump.c
@@ -18,107 +18,198 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-#include "common.h"
+#include "module.h"
 #include "network.h"
-#include "servers.h"
-#include "irc-servers.h"
-#include "channels.h"
-#include "modes.h"
-#include "nicklist.h"
 #include "settings.h"
-#include "proxy.h"
 
-static void outdata(gint handle, gchar *data, ...)
+#include "irc-servers.h"
+#include "irc-channels.h"
+#include "irc-nicklist.h"
+#include "modes.h"
+
+void proxy_outdata(CLIENT_REC *client, const char *data, ...)
 {
-    va_list args;
-    gchar *str;
+	va_list args;
+	char *str;
 
-    va_start(args, data);
+	g_return_if_fail(client != NULL);
+	g_return_if_fail(data != NULL);
 
-    str = g_strdup_vprintf(data, args);
-    net_transmit(handle, str, strlen(str));
-    g_free(str);
+	va_start(args, data);
 
-    va_end(args);
+	str = g_strdup_vprintf(data, args);
+	net_transmit(client->handle, str, strlen(str));
+	g_free(str);
+
+	va_end(args);
 }
 
-static void outserver(gint handle, SERVER_REC *server, gchar *data, ...)
+void proxy_outdata_all(IRC_SERVER_REC *server, const char *data, ...)
 {
-    va_list args;
-    gchar *str;
+	va_list args;
+	GSList *tmp;
+	char *str;
+	int len;
 
-    va_start(args, data);
+	g_return_if_fail(server != NULL);
+	g_return_if_fail(data != NULL);
 
-    str = g_strdup_vprintf(data, args);
-    outdata(handle, ":%s!%s@proxy %s\n", server->nick, settings_get_str("user_name"), str);
-    g_free(str);
+	va_start(args, data);
 
-    va_end(args);
+	str = g_strdup_vprintf(data, args);
+	len = strlen(str);
+	for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+		CLIENT_REC *rec = tmp->data;
+
+		if (rec->connected && rec->server == server)
+			net_transmit(rec->handle, str, len);
+	}
+	g_free(str);
+
+	va_end(args);
+}
+
+void proxy_outserver(CLIENT_REC *client, const char *data, ...)
+{
+	va_list args;
+	char *str;
+
+	g_return_if_fail(client != NULL);
+	g_return_if_fail(data != NULL);
+
+	va_start(args, data);
+
+	str = g_strdup_vprintf(data, args);
+	proxy_outdata(client, ":%s!%s@proxy %s\n", client->nick,
+		      settings_get_str("user_name"), str);
+	g_free(str);
+
+	va_end(args);
+}
+
+/*void proxy_outserver_all(IRC_SERVER_REC *server, const char *data, ...)
+{
+	va_list args;
+	GSList *tmp;
+	char *str;
+
+	g_return_if_fail(server != NULL);
+	g_return_if_fail(data != NULL);
+
+	va_start(args, data);
+
+	str = g_strdup_vprintf(data, args);
+	for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+		CLIENT_REC *rec = tmp->data;
+
+		if (rec->connected && rec->server == server) {
+			proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+				      settings_get_str("user_name"), str);
+		}
+	}
+	g_free(str);
+
+	va_end(args);
+}*/
+
+void proxy_outserver_all_except(CLIENT_REC *client, const char *data, ...)
+{
+	va_list args;
+	GSList *tmp;
+	char *str;
+
+	g_return_if_fail(client != NULL);
+	g_return_if_fail(data != NULL);
+
+	va_start(args, data);
+
+	str = g_strdup_vprintf(data, args);
+	for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+		CLIENT_REC *rec = tmp->data;
+
+		if (rec->connected && rec != client &&
+		    rec->server == client->server) {
+			proxy_outdata(rec, ":%s!%s@proxy %s\n", rec->nick,
+				      settings_get_str("user_name"), str);
+		}
+	}
+	g_free(str);
+
+	va_end(args);
+}
+
+static void dump_join(IRC_CHANNEL_REC *channel, CLIENT_REC *client)
+{
+	GSList *tmp, *nicks;
+	GString *str;
+
+	proxy_outserver(client, "JOIN %s", channel->name);
+	proxy_outdata(client, ":proxy 353 %s %c %s :", client->nick,
+		      channel_mode_is_set(channel, 'p') ? '*' :
+		      channel_mode_is_set(channel, 's') ? '@' : '=',
+		      channel->name);
+
+	str = g_string_new(NULL);
+
+	nicks = nicklist_getnicks(CHANNEL(channel));
+	for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
+		NICK_REC *nick = tmp->data;
+
+		if (tmp != nicks)
+                        g_string_append_c(str, ' ');
+
+		if (nick->op)
+                        g_string_append_c(str, '@');
+		else if (nick->halfop)
+                        g_string_append_c(str, '%');
+		else if (nick->voice)
+                        g_string_append_c(str, '+');
+		g_string_append(str, nick->nick);
+	}
+	g_slist_free(nicks);
+
+	g_string_append_c(str, '\n');
+	proxy_outdata(client, str->str);
+	g_string_free(str, TRUE);
+
+	proxy_outdata(client, ":proxy 366 %s %s :End of /NAMES list.\n",
+		      client->nick, channel->name);
+	if (channel->topic != NULL) {
+		proxy_outdata(client, ":proxy 332 %s %s :%s\n",
+			      client->nick, channel->name, channel->topic);
+	}
 }
 
 void plugin_proxy_dump_data(CLIENT_REC *client)
 {
-    SERVER_REC *server;
-    GSList *tmp, *tmp2, *nicks;
-    gint handle;
-
-    handle = client->handle;
-    server = servers->data;
-    if (strcmp(server->nick, client->nick) != 0)
-    {
-	/* change nick first so that clients won't try to eg. set their own
-	   user mode with wrong nick.. hopefully works with all clients. */
-	outdata(handle, ":%s!proxy NICK :%s\n", client->nick, server->nick);
-	g_free(client->nick);
-	client->nick = g_strdup(server->nick);
-    }
-    outdata(handle, ":proxy 001 %s :Welcome to the Internet Relay Network\n", client->nick);
-    outdata(handle, ":proxy 002 %s :Your host is irssi-proxy, running version %s\n", client->nick, VERSION);
-    outdata(handle, ":proxy 003 %s :This server was created ...\n", client->nick);
-    if (!IRC_SERVER(server)->emode_known)
-	    outdata(handle, ":proxy 004 %s proxy %s oirw abiklmnopqstv\n", client->nick, VERSION);
-    else
-	    outdata(handle, ":proxy 004 %s proxy %s oirw abeIiklmnopqstv\n", client->nick, VERSION);
-    outdata(handle, ":proxy 251 %s :There are 0 users and 0 invisible on 1 servers\n", client->nick);
-    outdata(handle, ":proxy 255 %s :I have 0 clients, 0 services and 0 servers\n", client->nick);
-    outdata(handle, ":proxy 422 %s :MOTD File is missing\n", client->nick);
-
-    /* nick / mode */
-    outserver(handle, server, "MODE %s :+%s", server->nick, IRC_SERVER(server)->usermode);
-
-    if (server->usermode_away)
-	outdata(handle, ":proxy 306 %s :You have been marked as being away\n", server->nick);
-
-    /* Send channel joins */
-    for (tmp = server->channels; tmp != NULL; tmp = tmp->next)
-    {
-        CHANNEL_REC *rec = tmp->data;
-
-        outserver(handle, rec->server, "JOIN %s", rec->name);
-        outdata(handle, ":proxy 353 %s %c %s :", rec->server->nick,
-	        	channel_mode_is_set(IRC_CHANNEL(rec), 'p') ? '*' : 
-				channel_mode_is_set(IRC_CHANNEL(rec), 's') ? '@' : '=',
-                rec->name);
-
-        nicks = nicklist_getnicks(rec);
-        for (tmp2 = nicks; tmp2 != NULL; tmp2 = tmp2->next)
-        {
-            NICK_REC *nick = tmp2->data;
-
-            if (tmp2 != nicks)
-                net_transmit(handle, " ", 1);
-
-            if (nick->op)
-                net_transmit(handle, "@", 1);
-            else if (nick->voice)
-                net_transmit(handle, "+", 1);
-            net_transmit(handle, nick->nick, strlen(nick->nick));
+	if (strcmp(client->server->nick, client->nick) != 0) {
+		/* change nick first so that clients won't try to eg. set
+		   their own user mode with wrong nick.. hopefully works
+		   with all clients. */
+		proxy_outdata(client, ":%s!proxy NICK :%s\n",
+			client->nick, client->server->nick);
+		g_free(client->nick);
+		client->nick = g_strdup(client->server->nick);
 	}
-	g_slist_free(nicks);
-        net_transmit(handle, "\n", 1);
 
-        outdata(handle, ":proxy 366 %s %s :End of /NAMES list.\n", rec->server->nick, rec->name);
-        if (rec->topic != NULL)
-	    outdata(handle, ":proxy 332 %s %s :%s\n", rec->server->nick, rec->name, rec->topic);
-    }
+	/* welcome info */
+	proxy_outdata(client, ":proxy 001 %s :Welcome to the Internet Relay Network\n", client->nick);
+	proxy_outdata(client, ":proxy 002 %s :Your host is irssi-proxy, running version %s\n", client->nick, VERSION);
+	proxy_outdata(client, ":proxy 003 %s :This server was created ...\n", client->nick);
+	if (!client->server->emode_known)
+		proxy_outdata(client, ":proxy 004 %s proxy %s oirw abiklmnopqstv\n", client->nick, VERSION);
+	else
+		proxy_outdata(client, ":proxy 004 %s proxy %s oirw abeIiklmnopqstv\n", client->nick, VERSION);
+	proxy_outdata(client, ":proxy 251 %s :There are 0 users and 0 invisible on 1 servers\n", client->nick);
+	proxy_outdata(client, ":proxy 255 %s :I have 0 clients, 0 services and 0 servers\n", client->nick);
+	proxy_outdata(client, ":proxy 422 %s :MOTD File is missing\n", client->nick);
+
+        /* user mode / away status */
+	proxy_outserver(client, "MODE %s :+%s", client->server->nick,
+			client->server->usermode);
+	if (client->server->usermode_away)
+		proxy_outdata(client, ":proxy 306 %s :You have been marked as being away\n", client->nick);
+
+	/* Send channel joins */
+	g_slist_foreach(client->server->channels, (GFunc) dump_join, client);
 }
diff --git a/src/irc/proxy/listen.c b/src/irc/proxy/listen.c
index c5453442..ac8c0f67 100644
--- a/src/irc/proxy/listen.c
+++ b/src/irc/proxy/listen.c
@@ -19,154 +19,148 @@
 */
 
 #include "module.h"
-#include "proxy.h"
+#include "signals.h"
 #include "net-sendbuffer.h"
-#include "fe-common/core/printtext.h"
+#include "servers-redirect.h"
 #include "levels.h"
+#include "settings.h"
+
+#include "irc.h"
+#include "irc-channels.h"
+
+#include "fe-common/core/printtext.h"
+
+GSList *proxy_listens;
+GSList *proxy_clients;
 
-static PLUGIN_DATA *proxy_data;
 static GString *next_line;
 
-void remove_client(PLUGIN_DATA *data, CLIENT_REC *rec)
+static void remove_client(CLIENT_REC *rec)
 {
-    data->clients = g_slist_remove(data->clients, rec);
+	g_return_if_fail(rec != NULL);
 
-    net_disconnect(rec->handle);
-    g_source_remove(rec->tag);
-    line_split_free(rec->buffer);
-    g_free(rec);
+	proxy_clients = g_slist_remove(proxy_clients, rec);
+
+	net_disconnect(rec->handle);
+	g_source_remove(rec->tag);
+	line_split_free(rec->buffer);
+	g_free_not_null(rec->nick);
+	g_free(rec);
 }
 
-static void proxy_redirect_event(CLIENT_REC *client, gchar *args, gint last, ...)
+static void proxy_redirect_event(CLIENT_REC *client,
+				 const char *args, int last, ...)
 {
-    va_list vargs;
-    gchar *event;
-    gint argpos, group;
-    GString *str;
+	va_list vargs;
+	GString *str;
+	char *event;
+	int argpos, group;
 
-    g_return_if_fail(client != NULL);
+	g_return_if_fail(client != NULL);
 
-    va_start(vargs, last);
+	va_start(vargs, last);
 
-    str = g_string_new(NULL);
-    group = 0;
-    while ((event = va_arg(vargs, gchar *)) != NULL)
-    {
-	  argpos = va_arg(vargs, gint);
-	  g_string_sprintf(str, "proxy %d", client->handle);
-	  group = server_redirect_single_event(client->server, args, last > 0, group, event, str->str, argpos);
-	  last--;
-    }
-    g_string_free(str, TRUE);
-
-    va_end(vargs);
-}
-
-static void grab_who(CLIENT_REC *client, gchar *channel)
-{
-    gchar *chlist;
-    gchar **list, **tmp;
-
-    /* /WHO a,b,c may respond with either one "a,b,c End of WHO" message or
-       three different "a End of WHO", "b End of WHO", .. messages */
-    chlist = g_strdup(channel);
-    list = g_strsplit(channel, ",", -1);
-
-    for (tmp = list; *tmp != NULL; tmp++)
-    {
-	if (strcmp(*tmp, "0") == 0)
-	{
-	    /* /who 0 displays everyone */
-	    **tmp = '*';
+	str = g_string_new(NULL);
+	group = 0;
+	while ((event = va_arg(vargs, char *)) != NULL) {
+		argpos = va_arg(vargs, int);
+		g_string_sprintf(str, "proxy %d", client->handle);
+		group = server_redirect_single_event(SERVER(client->server), args, last > 0,
+						     group, event, str->str, argpos);
+		last--;
 	}
+	g_string_free(str, TRUE);
 
-	channel = g_strdup_printf("%s %s", chlist, *tmp);
-	proxy_redirect_event(client, channel, 2,
-			     "event 401", 1, "event 315", 1,
-			     "event 352", -1, NULL);
-	g_free(channel);
-    }
-    g_strfreev(list);
-    g_free(chlist);
+	va_end(vargs);
 }
 
-static void sig_listen_client(CLIENT_REC *client, gint handle)
+static void grab_who(CLIENT_REC *client, const char *channel)
 {
-    char tmpbuf[1024], *str, *cmd, *args, *p;
-    int ret, recvlen;
+	char *chlist, *chanevent;
+	char **list, **tmp;
 
-    g_return_if_fail(client != NULL);
+	/* /WHO a,b,c may respond with either one "a,b,c End of WHO" message
+	   or three different "a End of WHO", "b End of WHO", .. messages */
+	chlist = g_strdup(channel);
+	list = g_strsplit(channel, ",", -1);
 
-    for (;;)
-    {
-	recvlen = net_receive(handle, tmpbuf, sizeof(tmpbuf));
-	ret = line_split(tmpbuf, recvlen, &str, &client->buffer);
-        if (ret == -1)
-        {
-            /* connection lost */
-            remove_client(proxy_data, client);
-            break;
-        }
-	if (ret == 0) break;
-
-	if (client->server == NULL)
-	    continue;
-
-	cmd = g_strdup(str);
-	args = strchr(cmd, ' ');
-	if (args != NULL) *args++ = '\0'; else args = "";
-	if (*args == ':') args++;
-	g_strup(cmd);
-
-	if (!client->connected)
-	{
-	    if (proxy_data->password != NULL && strcmp(cmd, "PASS") == 0)
-	    {
-		if (strcmp(proxy_data->password, args) != 0)
-		{
-		    /* wrong password! */
-		    remove_client(proxy_data, client);
-                    break;
+	for (tmp = list; *tmp != NULL; tmp++) {
+		if (strcmp(*tmp, "0") == 0) {
+			/* /who 0 displays everyone */
+			**tmp = '*';
 		}
-		client->pass_sent = TRUE;
-	    }
-	    else if (strcmp(cmd, "NICK") == 0)
+
+		chanevent = g_strdup_printf("%s %s", chlist, *tmp);
+		proxy_redirect_event(client, chanevent, 2,
+				     "event 401", 1, "event 315", 1,
+				     "event 352", -1, NULL);
+		g_free(chanevent);
+	}
+	g_strfreev(list);
+	g_free(chlist);
+}
+
+static void handle_client_connect_cmd(CLIENT_REC *client,
+				      const char *cmd, const char *args)
+{
+	const char *password;
+
+	password = settings_get_str("irssiproxy_password");
+
+	if (password != NULL && strcmp(cmd, "PASS") == 0) {
+		if (strcmp(password, args) == 0)
+			client->pass_sent = TRUE;
+		else {
+			/* wrong password! */
+			remove_client(client);
+		}
+	} else if (strcmp(cmd, "NICK") == 0) {
+		g_free_not_null(client->nick);
 		client->nick = g_strdup(args);
-	    else if (strcmp(cmd, "USER") == 0)
-	    {
-		if (client->nick == NULL || (proxy_data->password != NULL && !client->pass_sent))
-		{
-		    /* stupid client didn't send us NICK/PASS or, kill it */
-		    remove_client(proxy_data, client);
-		    break;
+	} else if (strcmp(cmd, "USER") == 0) {
+		if (client->nick == NULL ||
+		    (*password != '\0' && !client->pass_sent)) {
+			/* stupid client didn't send us NICK/PASS, kill it */
+			remove_client(client);
+		} else {
+			client->connected = TRUE;
+			plugin_proxy_dump_data(client);
 		}
-		client->connected = TRUE;
-		plugin_proxy_dump_data(client);
-	    }
 	}
-        else if (strcmp(cmd, "QUIT") == 0)
-        {
-            remove_client(proxy_data, client);
-            break;
-        }
-	else if (strcmp(cmd, "PING") == 0)
-	{
-	    net_transmit(handle, "PONG proxy :nick\n", 17);
-	}
-	else
-	{
-	    net_transmit(net_sendbuffer_handle(client->server->handle), str, strlen(str));
-	    net_transmit(net_sendbuffer_handle(client->server->handle), "\n", 1);
+}
 
-	    if (strcmp(cmd, "WHO") == 0)
-	    {
+static void handle_client_cmd(CLIENT_REC *client, char *cmd, char *args)
+{
+	int server_handle;
+
+	if (!client->connected) {
+		handle_client_connect_cmd(client, cmd, args);
+		return;
+	}
+
+	if (strcmp(cmd, "QUIT") == 0) {
+		remove_client(client);
+		return;
+	}
+	if (strcmp(cmd, "PING") == 0) {
+		proxy_outdata(client, "PONG proxy :%s\n", args);
+		return;
+	}
+
+	server_handle = net_sendbuffer_handle(client->server->handle);
+	net_transmit(server_handle, cmd, strlen(cmd));
+	net_transmit(server_handle, " ", 1);
+	net_transmit(server_handle, args, strlen(args));
+	net_transmit(server_handle, "\n", 1);
+
+	if (strcmp(cmd, "WHO") == 0)
 		grab_who(client, args);
-	    }
-	    else if (strcmp(cmd, "WHOIS") == 0)
-	    {
+	else if (strcmp(cmd, "WHOIS") == 0) {
+		char *p;
+
 		/* convert dots to spaces */
 		for (p = args; *p != '\0'; p++)
-		    if (*p == ',') *p = ' ';
+			if (*p == ',') *p = ' ';
 
 		proxy_redirect_event(client, args, 2,
 				     "event 318", -1, "event 402", -1,
@@ -174,20 +168,14 @@ static void sig_listen_client(CLIENT_REC *client, gint handle)
 				     "event 301", 1, "event 312", 1,
 				     "event 313", 1, "event 317", 1,
 				     "event 319", 1, NULL);
-	    }
-	    else if (strcmp(cmd, "ISON") == 0)
-	    {
+	} else if (strcmp(cmd, "ISON") == 0)
 		proxy_redirect_event(client, NULL, 1, "event 303", -1, NULL);
-	    }
-	    else if (strcmp(cmd, "USERHOST") == 0)
-	    {
+	else if (strcmp(cmd, "USERHOST") == 0)
 		proxy_redirect_event(client, args, 1, "event 302", -1, "event 401", 1, NULL);
-	    }
-	    else if (strcmp(cmd, "MODE") == 0)
-	    {
+	else if (strcmp(cmd, "MODE") == 0) {
 		/* convert dots to spaces */
-		gchar *slist, *str, mode;
-		gint argc;
+		char *slist, *str, mode, *p;
+		int argc;
 
 		p = strchr(args, ' ');
 		if (p != NULL) *p++ = '\0';
@@ -195,205 +183,355 @@ static void sig_listen_client(CLIENT_REC *client, gint handle)
 
 		slist = g_strdup(args);
 		argc = 1;
-		for (p = slist; *p != '\0'; p++)
-		{
-		    if (*p == ',')
-		    {
-			*p = ' ';
-			argc++;
-		    }
+		for (p = slist; *p != '\0'; p++) {
+			if (*p == ',') {
+				*p = ' ';
+				argc++;
+			}
 		}
 
 		/* get channel mode / bans / exception / invite list */
 		str = g_strdup_printf("%s %s", args, slist);
-		switch (mode)
-		{
-		    case '\0':
-                        while (argc-- > 0)
-			    proxy_redirect_event(client, str, 3, "event 403", 1,
-						 "event 443", 1, "event 324", 1, NULL);
-			break;
-		    case 'b':
-                        while (argc-- > 0)
-			    proxy_redirect_event(client, str, 2, "event 403", 1,
-						 "event 368", 1, "event 367", 1, NULL);
-			break;
-		    case 'e':
+		switch (mode) {
+		case '\0':
 			while (argc-- > 0)
-			    proxy_redirect_event(client, str, 4, "event 403", 1,
-						 "event 482", 1, "event 472", -1,
-						 "event 349", 1, "event 348", 1, NULL);
+				proxy_redirect_event(client, str, 3, "event 403", 1,
+						     "event 443", 1, "event 324", 1, NULL);
 			break;
-		    case 'I':
-                        while (argc-- > 0)
-			    proxy_redirect_event(client, str, 4, "event 403", 1,
-						 "event 482", 1, "event 472", -1,
-						 "event 347", 1, "event 346", 1, NULL);
+		case 'b':
+			while (argc-- > 0)
+				proxy_redirect_event(client, str, 2, "event 403", 1,
+						     "event 368", 1, "event 367", 1, NULL);
+			break;
+		case 'e':
+			while (argc-- > 0)
+				proxy_redirect_event(client, str, 4, "event 403", 1,
+						     "event 482", 1, "event 472", -1,
+						     "event 349", 1, "event 348", 1, NULL);
+			break;
+		case 'I':
+			while (argc-- > 0)
+				proxy_redirect_event(client, str, 4, "event 403", 1,
+						     "event 482", 1, "event 472", -1,
+						     "event 347", 1, "event 346", 1, NULL);
 			break;
 		}
 		g_free(str);
 		g_free(slist);
-	    }
+	} else if (strcmp(cmd, "PRIVMSG") == 0) {
+		/* send the message to other clients as well */
+		char *params, *target, *msg;
+
+		params = event_get_params(args, 2 | PARAM_FLAG_GETREST,
+					  &target, &msg);
+		proxy_outserver_all_except(client, "PRIVMSG %s", args);
+		signal_emit("message public", 5, client->server, msg,
+			    client->nick, "proxy", target);
+		g_free(params);
 	}
-	g_free(cmd);
-    }
 }
 
-static void sig_listen(PLUGIN_DATA *data, gint handle)
+static void sig_listen_client(CLIENT_REC *client)
 {
-    CLIENT_REC *rec;
-    IPADDR ip;
-    gint port;
+	char tmpbuf[1024], *str, *cmd, *args;
+	int ret, recvlen;
 
-    g_return_if_fail(data != NULL);
-    if (servers == NULL) return;
+	g_return_if_fail(client != NULL);
 
-    /* accept connection */
-    handle = net_accept(handle, &ip, &port);
-    if (handle == -1)
-        return;
+	while (g_slist_find(proxy_clients, client) != NULL) {
+		recvlen = net_receive(client->handle, tmpbuf, sizeof(tmpbuf));
+		ret = line_split(tmpbuf, recvlen, &str, &client->buffer);
+		if (ret == -1) {
+			/* connection lost */
+			remove_client(client);
+			break;
+		}
 
-    rec = g_new0(CLIENT_REC, 1);
-    rec->handle = handle;
-    rec->server = servers == NULL ? NULL : servers->data;
-    rec->tag = g_input_add(handle, G_INPUT_READ, (GInputFunction) sig_listen_client, rec);
+		if (ret == 0) break;
 
-    data->clients = g_slist_append(data->clients, rec);
+		if (client->server == NULL)
+			continue;
+
+		cmd = g_strdup(str);
+		args = strchr(cmd, ' ');
+		if (args != NULL) *args++ = '\0'; else args = "";
+		if (*args == ':') args++;
+		g_strup(cmd);
+
+		handle_client_cmd(client, cmd, args);
+
+		g_free(cmd);
+	}
 }
 
-static gboolean sig_incoming(SERVER_REC *server, gchar *line)
+static void sig_listen(LISTEN_REC *listen)
 {
-    g_return_val_if_fail(line != NULL, FALSE);
+	CLIENT_REC *rec;
+	IPADDR ip;
+	char host[MAX_IP_LEN];
+	int port, handle;
 
-    /* send server event to all clients */
-    g_string_sprintf(next_line, "%s\n", line);
-    return TRUE;
+	g_return_if_fail(listen != NULL);
+
+	/* accept connection */
+	handle = net_accept(listen->handle, &ip, &port);
+	if (handle == -1)
+		return;
+	net_ip2host(&ip, host);
+
+	rec = g_new0(CLIENT_REC, 1);
+	rec->listen = listen;
+	rec->handle = handle;
+	rec->server = IRC_SERVER(server_find_chatnet(listen->ircnet));
+	rec->tag = g_input_add(handle, G_INPUT_READ,
+			       (GInputFunction) sig_listen_client, rec);
+
+	proxy_clients = g_slist_append(proxy_clients, rec);
+	printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+		  "Proxy: Client connected from %s", host);
 }
 
-static gboolean sig_server_event(gchar *line, SERVER_REC *server, gchar *nick, gchar *address)
+static void sig_incoming(IRC_SERVER_REC *server, const char *line)
 {
-    GSList *tmp, *list;
-    gchar *event, *args;
+	g_return_if_fail(line != NULL);
 
-    g_return_val_if_fail(line != NULL, FALSE);
+	/* send server event to all clients */
+	g_string_sprintf(next_line, "%s\n", line);
+}
 
-    /* get command.. */
-    event = g_strconcat("event ", line, NULL);
-    args = strchr(event+6, ' ');
-    if (args != NULL) *args++ = '\0'; else args = "";
-    while (*args == ' ') args++;
+static void sig_server_event(const char *line, IRC_SERVER_REC *server,
+			     const char *nick, const char *address)
+{
+	GSList *list;
+	char *event, *args;
 
-    list = server_redirect_getqueue(server, event, args);
+	g_return_if_fail(line != NULL);
+	if (!IS_IRC_SERVER(server))
+		return;
 
-    if (list != NULL)
-    {
-	/* we want to send this to one client (or proxy itself) only */
-	REDIRECT_REC *rec;
-	gint handle;
+	/* get command.. */
+	event = g_strconcat("event ", line, NULL);
+	args = strchr(event+6, ' ');
+	if (args != NULL) *args++ = '\0'; else args = "";
+	while (*args == ' ') args++;
 
-	rec = list->data;
-	if (g_strncasecmp(rec->name, "proxy ", 6) != 0)
-	{
-	    /* proxy only */
-	    g_free(event);
-	    return TRUE;
+	list = server_redirect_getqueue(SERVER(server), event, args);
+
+	if (list != NULL) {
+		/* we want to send this to one client (or proxy itself) only */
+		REDIRECT_REC *rec;
+		int handle;
+
+		rec = list->data;
+		if (g_strncasecmp(rec->name, "proxy ", 6) != 0) {
+			/* proxy only */
+			g_free(event);
+			return;
+		}
+
+		if (sscanf(rec->name+6, "%d", &handle) == 1) {
+			/* send it to specific client only */
+			server_redirect_remove_next(SERVER(server), event, list);
+			net_transmit(handle, next_line->str, next_line->len);
+			g_free(event);
+                        signal_stop();
+			return;
+		}
 	}
 
-	if (sscanf(rec->name+6, "%d", &handle) == 1)
-	{
-            /* send it to specific client only */
-	    server_redirect_remove_next(server, event, list);
-	    net_transmit(handle, next_line->str, next_line->len);
-	    g_free(event);
-	    return FALSE;
+	if (g_strcasecmp(event, "event ping") == 0 ||
+	    (g_strcasecmp(event, "event privmsg") == 0 &&
+	     strstr(args, " :\001") != NULL)) {
+		/* We want to answer ourself to PINGs and CTCPs */
+		g_free(event);
+		return;
 	}
-    }
 
-    if (g_strcasecmp(event, "event ping") == 0)
-    {
-	/* We want to answer ourself to PINGs.. */
+	/* send the data to clients.. */
+        proxy_outdata_all(server, next_line->str);
+
 	g_free(event);
-	return TRUE;
-    }
-
-    /* send the data to clients.. */
-    for (tmp = proxy_data->clients; tmp != NULL; tmp = tmp->next)
-    {
-	CLIENT_REC *rec = tmp->data;
-
-	if (rec->server == server)
-	    net_transmit(rec->handle, next_line->str, next_line->len);
-    }
-
-    g_free(event);
-    return TRUE;
 }
 
-static gboolean sig_server_connected(SERVER_REC *server)
+static void sig_server_connected(IRC_SERVER_REC *server)
 {
-    GSList *tmp;
+	GSList *tmp;
 
-    g_return_val_if_fail(server != NULL, FALSE);
+	if (!IS_IRC_SERVER(server) || server->connrec->chatnet == NULL)
+		return;
 
-    for (tmp = proxy_data->clients; tmp != NULL; tmp = tmp->next)
-    {
-	CLIENT_REC *rec = tmp->data;
+	for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+		CLIENT_REC *rec = tmp->data;
 
-	if (rec->server == NULL)
-	    rec->server = server;
-    }
-    return TRUE;
+		if (rec->connected && rec->server == NULL &&
+		    g_strcasecmp(server->connrec->chatnet, rec->listen->ircnet) == 0) {
+			proxy_outserver(rec, "NOTICE %s :Connected to server", rec->nick);
+			rec->server = server;
+		}
+	}
 }
 
-static gboolean sig_server_disconnected(SERVER_REC *server)
+static void proxy_server_disconnected(CLIENT_REC *client,
+				      IRC_SERVER_REC *server)
 {
-    GSList *tmp;
+	GSList *tmp;
 
-    g_return_val_if_fail(server != NULL, FALSE);
+	proxy_outdata(client, ":proxy NOTICE %s :Connection lost to server %s\n",
+		      client->nick, server->connrec->address);
 
-    for (tmp = proxy_data->clients; tmp != NULL; tmp = tmp->next)
-    {
-	CLIENT_REC *rec = tmp->data;
+	for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
+		IRC_CHANNEL_REC *rec = tmp->data;
 
-	if (rec->server == server)
-	    rec->server = NULL;
-    }
-    return TRUE;
+		proxy_outserver(client, "PART %s :Connection lost to server",
+				rec->name);
+	}
 }
 
-void plugin_proxy_listen_init(PLUGIN_DATA *data)
+static void sig_server_disconnected(IRC_SERVER_REC *server)
 {
-    proxy_data = data;
-    g_return_if_fail(proxy_data != NULL);
+	GSList *tmp;
 
-    next_line = g_string_new(NULL);
+	if (!IS_IRC_SERVER(server))
+		return;
 
-    /* start listening */
-    proxy_data->listen_handle = net_listen(&proxy_data->ip, &proxy_data->port);
-    if (proxy_data->listen_handle == -1)
-    {
-        printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Listen failed");
-        return;
-    }
+	for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+		CLIENT_REC *rec = tmp->data;
 
-    proxy_data->clients = NULL;
-    proxy_data->listen_tag = g_input_add(proxy_data->listen_handle, G_INPUT_READ,
-				   (GInputFunction) sig_listen, proxy_data);
-
-    signal_add("server incoming", (SIGNAL_FUNC) sig_incoming);
-    signal_add("server event", (SIGNAL_FUNC) sig_server_event);
-    signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
-    signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+		if (rec->connected && rec->server == server) {
+                        proxy_server_disconnected(rec, server);
+			rec->server = NULL;
+		}
+	}
 }
 
-void plugin_proxy_listen_deinit(PLUGIN_DATA *data)
+static void event_nick(const char *data, IRC_SERVER_REC *server)
 {
-    g_return_if_fail(data != NULL);
+	GSList *tmp;
 
-    g_string_free(next_line, TRUE);
-    while (data->clients != NULL)
-        remove_client(data, data->clients->data);
+	if (*data == ':') data++;
+	for (tmp = proxy_clients; tmp != NULL; tmp = tmp->next) {
+		CLIENT_REC *rec = tmp->data;
 
-    net_disconnect(data->listen_handle);
-    g_source_remove(data->listen_tag);
+		if (rec->connected && rec->server == server) {
+			g_free(rec->nick);
+			rec->nick = g_strdup(data);
+		}
+	}
+}
+
+static LISTEN_REC *find_listen(const char *ircnet, int port)
+{
+	GSList *tmp;
+
+	for (tmp = proxy_listens; tmp != NULL; tmp = tmp->next) {
+		LISTEN_REC *rec = tmp->data;
+
+		if (rec->port == port &&
+		    g_strcasecmp(rec->ircnet, ircnet) == 0)
+			return rec;
+	}
+
+	return NULL;
+}
+
+static void add_listen(const char *ircnet, int port)
+{
+	LISTEN_REC *rec;
+
+	if (port <= 0 || *ircnet == '\0') return;
+
+	rec = g_new0(LISTEN_REC, 1);
+	rec->ircnet = g_strdup(ircnet);
+	rec->port = port;
+
+	/* start listening */
+	rec->handle = net_listen(NULL, &rec->port);
+	if (rec->handle == -1) {
+		printtext(NULL, NULL, MSGLEVEL_CLIENTERROR,
+			  "Proxy: Listen in port %d failed: %s",
+			  rec->port, g_strerror(errno));
+		return;
+	}
+
+	rec->tag = g_input_add(rec->handle, G_INPUT_READ,
+			       (GInputFunction) sig_listen, rec);
+
+        proxy_listens = g_slist_append(proxy_listens, rec);
+}
+
+static void remove_listen(LISTEN_REC *rec)
+{
+	proxy_listens = g_slist_remove(proxy_listens, rec);
+
+	net_disconnect(rec->handle);
+	g_source_remove(rec->tag);
+	g_free(rec->ircnet);
+	g_free(rec);
+}
+
+static void read_settings(void)
+{
+	LISTEN_REC *rec;
+	GSList *remove_listens;
+	char **ports, **tmp, *ircnet, *port;
+	int portnum;
+
+	remove_listens = g_slist_copy(proxy_listens);
+
+	ports = g_strsplit(settings_get_str("irssiproxy_ports"), " ", -1);
+	for (tmp = ports; *tmp != NULL; tmp++) {
+		ircnet = *tmp;
+		port = strchr(ircnet, '=');
+		if (port == NULL)
+			continue;
+
+		*port++ = '\0';
+		portnum = atoi(port);
+		if (portnum <=  0)
+			continue;
+
+		rec = find_listen(ircnet, portnum);
+		if (rec == NULL)
+			add_listen(ircnet, portnum);
+		else
+			remove_listens = g_slist_remove(remove_listens, rec);
+	}
+	g_strfreev(ports);
+
+	while (remove_listens != NULL) {
+                remove_listen(remove_listens->data);
+		g_slist_remove(remove_listens, remove_listens->data);
+	}
+}
+
+void plugin_proxy_listen_init(void)
+{
+	next_line = g_string_new(NULL);
+
+	proxy_clients = NULL;
+	proxy_listens = NULL;
+	read_settings();
+
+	signal_add("server incoming", (SIGNAL_FUNC) sig_incoming);
+	signal_add("server event", (SIGNAL_FUNC) sig_server_event);
+	signal_add("server connected", (SIGNAL_FUNC) sig_server_connected);
+	signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+	signal_add("event nick", (SIGNAL_FUNC) event_nick);
+	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
+}
+
+void plugin_proxy_listen_deinit(void)
+{
+	while (proxy_clients != NULL)
+		remove_client(proxy_clients->data);
+	while (proxy_listens != NULL)
+		remove_listen(proxy_listens->data);
+	g_string_free(next_line, TRUE);
+
+	signal_remove("server incoming", (SIGNAL_FUNC) sig_incoming);
+	signal_remove("server event", (SIGNAL_FUNC) sig_server_event);
+	signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected);
+	signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
+	signal_remove("event nick", (SIGNAL_FUNC) event_nick);
+	signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 }
diff --git a/src/irc/proxy/module.h b/src/irc/proxy/module.h
index 809c2c51..e0ccf856 100644
--- a/src/irc/proxy/module.h
+++ b/src/irc/proxy/module.h
@@ -2,10 +2,46 @@
 
 #define MODULE_NAME "proxy"
 
+#include "network.h"
+#include "line-split.h"
+#include "irc-servers.h"
 
+typedef struct {
+	int port;
+	char *ircnet;
 
+	int tag;
+	int handle;
+} LISTEN_REC;
 
+typedef struct {
+	LINEBUF_REC *buffer;
 
+	char *nick;
+	int handle;
+	int tag;
 
+	LISTEN_REC *listen;
+	IRC_SERVER_REC *server;
+	int pass_sent:1;
+	int connected:1;
+} CLIENT_REC;
 
+extern GSList *proxy_listens;
+extern GSList *proxy_clients;
 
+void plugin_proxy_setup_init(void);
+void plugin_proxy_setup_deinit(void);
+
+void plugin_proxy_listen_init(void);
+void plugin_proxy_listen_deinit(void);
+
+void proxy_settings_init(void);
+
+void plugin_proxy_dump_data(CLIENT_REC *client);
+
+void proxy_outdata(CLIENT_REC *client, const char *data, ...);
+void proxy_outdata_all(IRC_SERVER_REC *server, const char *data, ...);
+void proxy_outserver(CLIENT_REC *client, const char *data, ...);
+/*void proxy_outserver_all(const char *data, ...);*/
+void proxy_outserver_all_except(CLIENT_REC *client, const char *data, ...);
diff --git a/src/irc/proxy/proxy.c b/src/irc/proxy/proxy.c
index 879d3205..ab7ef4cc 100644
--- a/src/irc/proxy/proxy.c
+++ b/src/irc/proxy/proxy.c
@@ -18,119 +18,34 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
-#include "proxy.h"
+#include "module.h"
+#include "settings.h"
 #include "levels.h"
 #include "fe-common/core/printtext.h"
-#include "servers.h"
-#include "net-sendbuffer.h"
 
-#include "lib-config/iconfig.h"
-#include "settings.h"
-
-PLUGIN_DATA *proxy_data;
-MODULE_REC *plug;
-
-
-
-gchar *plugin_description(void)
+void proxy_deinit(void)
 {
-    return "IRC proxy plugin";
+	plugin_proxy_listen_deinit();
 }
 
-/*gint plugin_version(void)
+void proxy_init(void)
 {
-    return PLUGIN_LAYER_VERSION;
-}
-*/
+	settings_add_str("irssiproxy", "irssiproxy_ports", "");
+	settings_add_str("irssiproxy", "irssiproxy_password", "");
 
-void proxy_settings_init(void)
-{
-	settings_add_str("proxy", "proxy_listen_addr", "localhost");
-	settings_add_int("proxy", "proxy_listen_port", 2777);
-	settings_add_str("proxy", "proxy_listen_password", "");
-}
-
-
-
-/* If we call plugin_deinit() in this code, it doesn't necessarily point to
-   _THIS_ module's plugin_deinit() but instead some other module's.. So,
-   we create static deinit() function which should be used.. */
-static void deinit(/*MODULE_REC *plugin*/)
-{
-	plugin_proxy_listen_deinit(proxy_data);
-}
-
-
-void proxy_deinit(/*MODULE_REC *plugin*/)
-{
-  deinit(/*plugin*/);
-}
-
-gboolean proxy_init(void)
-{
-
-    gchar ipaddr[MAX_IP_LEN];
-
-    const char *password;
-    const char *addr;
-    int port;
-
-    proxy_settings_init();
-
-    proxy_data = g_new0(PLUGIN_DATA, 1);
-    password = settings_get_str("proxy_listen_password");
-    addr = settings_get_str("proxy_listen_addr");
-    port = settings_get_int("proxy_listen_port");
-
-    plug = module_find("proxy");
-    proxy_data->plugin = plug;
-
-    if (*password != '\0')
-    {
-       	/* args = password */
-       	proxy_data->password = g_strdup(password);
-    }
-    if (*addr != '\0')
-    {
-       	/* specify ip address to listen */
-       	net_host2ip(addr, &proxy_data->ip);
-    }
-    if (port != 0)
-    {
-       	/* specify port to use */
-       	proxy_data->port = port;
-    }
-    
-    if (proxy_data->password == NULL)
-    {
-    	/* no password - bad idea! */
-    	printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, "Warning!! Password not specified, everyone can use this proxy! Use /set proxy_listen_password <password> to set it");
-    }
-
-    if (servers == NULL)
-    {
-    	/* FIXME: not good */
-    	printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "You need to specify IP address to listen with /set proxy_listen_addr <address>");
-    	deinit();
-    	return FALSE;
-    }
-    else
-    {
-    	SERVER_REC *server;
-
-    	server = servers->data;
-    	if (net_getsockname(net_sendbuffer_handle(server->handle), &proxy_data->ip, NULL))
-    	{
-	    deinit();
-	    return FALSE;
+	if (*settings_get_str("irssiproxy_password") == '\0') {
+		/* no password - bad idea! */
+		printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+			  "Warning!! Password not specified, everyone can "
+			  "use this proxy! Use /set irssiproxy_password "
+			  "<password> to set it");
+	}
+	if (*settings_get_str("irssiproxy_ports") == '\0') {
+		printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
+			  "No proxy ports specified. Use /SET "
+			  "irssiproxy_ports <ircnet>=<port> <ircnet2>=<port2> "
+			  "... to set them.");
 	}
-    }
 
-    net_ip2host(&proxy_data->ip, ipaddr);
-    printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, "Proxy plugin loaded - listening in interface %s port %d", ipaddr, proxy_data->port);
-
-    plugin_proxy_listen_init(proxy_data);
-
-    proxy_data->loaded = TRUE;
-    return TRUE;
+	plugin_proxy_listen_init();
 }
diff --git a/src/irc/proxy/proxy.h b/src/irc/proxy/proxy.h
deleted file mode 100644
index dadc044d..00000000
--- a/src/irc/proxy/proxy.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#ifndef __PROXY_H
-#define __PROXY_H
-
-
-#include "module.h"
-#include "../../core/modules.h"
-
-#include "network.h"
-#include <core/line-split.h>
-#include <core/servers-redirect.h>
-#include "commands.h"
-
-typedef struct
-{
-    MODULE_REC *plugin;
-    gboolean loaded;
-
-    IPADDR ip;
-    gint port;
-    gchar *password;
-
-    gint listen_tag;
-    gint listen_handle;
-
-    GSList *clients;
-}
-PLUGIN_DATA;
-
-typedef struct
-{
-    LINEBUF_REC *buffer;
-
-    gchar *nick;
-    gint handle;
-    gint tag;
-
-    SERVER_REC *server;
-    gboolean pass_sent;
-    gboolean connected;
-}
-CLIENT_REC;
-
-void plugin_proxy_setup_init(MODULE_REC *plugin);
-void plugin_proxy_setup_deinit(MODULE_REC *plugin);
-
-void plugin_proxy_listen_init();
-void plugin_proxy_listen_deinit();
-
-void proxy_settings_init(void);
-
-void plugin_proxy_dump_data(CLIENT_REC *client);
-
-/*  #define MODULE_NAME "proxy" */
-
-#endif