diff src/protocols/irc/irc.c @ 6333:e06e04e44914

[gaim-migrate @ 6832] (20:48:32) Robot101: new IRC plugin y'all (20:48:51) Paco-Paco: The IRC Protocol Plugin that Sucks Less (TM) (20:49:18) Paco-Paco: I think that's what the prpl description field says (20:50:09) LSchiere2: :-) committer: Tailor Script <tailor@pidgin.im>
author Luke Schierer <lschiere@pidgin.im>
date Wed, 30 Jul 2003 00:50:29 +0000
parents 3613007cbb6e
children 34c07f5f34a0
line wrap: on
line diff
--- a/src/protocols/irc/irc.c	Tue Jul 29 13:45:49 2003 +0000
+++ b/src/protocols/irc/irc.c	Wed Jul 30 00:50:29 2003 +0000
@@ -1,11 +1,11 @@
-/*
- * gaim - IRC Protocol Plugin
+/**
+ * @file irc.c
+ * 
+ * gaim
  *
- * Copyright (C) 2000-2001, Rob Flynn <rob@tgflinux.com>
+ * Copyright (C) 2003, Ethan Blanton <eblanton@cs.purdue.edu>
+ * Copyright (C) 2000-2003, Rob Flynn <rob@tgflinux.com>
  * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
- *
- * A large portion of this was copied more or less directly from X-Chat,
- * the world's most rocking IRC client. http://www.xchat.org/
  * 
  * 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
@@ -20,2452 +20,101 @@
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
  */
+
 #include "internal.h"
 
-#include "account.h"
+#include "plugin.h"
 #include "accountopt.h"
-#include "conversation.h"
-#include "core.h"
-#include "debug.h"
-#include "ft.h"
 #include "multi.h"
-#include "notify.h"
-#include "proxy.h"
 #include "prpl.h"
-#include "request.h"
-#include "server.h"
-#include "util.h"
-
-/* XXX for g_show_info_text(), WEBSITE, etc. */
-#include "gaim.h"
-
-#define IRC_BUF_LEN 4096
-#define PDIWORDS 32
-
-#define DEFAULT_SERVER "irc.freenode.net"
-
-static GaimPlugin *my_protocol = NULL;
-
-#ifndef INET6_ADDRSTRLEN
-#define INET6_ADDRSTRLEN 46
-#endif
+#include "conversation.h"
+#include "debug.h"
+#include "blist.h"
+#include "irc.h"
 
-/* Datastructs */
-struct dcc_chat
-{
-	GaimConnection *gc;	
-	char ip_address[INET6_ADDRSTRLEN];
-	int port;		
-	int fd;			
-	int inpa;		
-	char nick[80];		
-};
-
-struct irc_xfer_data
-{
-	char *ip;
-	int port;
-
-	struct irc_data *idata;
-};
-
-struct irc_data {
-	int fd;
-	gboolean online;
-	guint32 timer;
-
-	char *server;
-
-	char *rxqueue;
-	int rxlen;
-
-	GString *str;
-	int bc;
+static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string);
 
-	char *chantypes;
-	char *chanmodes;
-	char *nickmodes;
-	gboolean six_modes;
-
-	gboolean in_whois;
-	gboolean in_list;
-	GString *liststr;
-	GSList *file_transfers;
-};
-
-/* Prototypes */
-static void irc_start_chat(GaimConnection *gc, const char *who);
-static void irc_ctcp_clientinfo(GaimConnection *gc, const char *who);
-static void irc_ctcp_userinfo(GaimConnection *gc, const char *who);
-static void irc_ctcp_version(GaimConnection *gc, const char *who);
-static void irc_ctcp_ping(GaimConnection *gc, const char *who);
-
-static void irc_send_privmsg(GaimConnection *gc, const char *who, const char *what, gboolean fragment);
-static void irc_send_notice(GaimConnection *gc, char *who, char *what);
-
-static char *irc_send_convert(GaimConnection *gc, const char *string, int maxlen, int *done);
-static char *irc_recv_convert(GaimConnection *gc, char *string);
-static void irc_parse_notice(GaimConnection *gc, char *nick, char *ex,
-                            char *word[], char *word_eol[]);
-static void irc_parse_join(GaimConnection *gc, char *nick,
-                          char *word[], char *word_eol[]);
-static gboolean irc_parse_part(GaimConnection *gc, char *nick, char *cmd,
-                          char *word[], char *word_eol[]);
-static void irc_parse_topic(GaimConnection *gc, char *nick,
-                           char *word[], char *word_eol[]);
-
-static void dcc_chat_cancel(struct dcc_chat *);
+static const char *irc_blist_icon(GaimAccount *a, struct buddy *b);
+static void irc_blist_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne);
+static GList *irc_away_states(GaimConnection *gc);
+/* static GList *irc_chat_info(GaimConnection *gc); */
+static void irc_login(GaimAccount *account);
+static void irc_login_cb(gpointer data, gint source, GaimInputCondition cond);
+static void irc_close(GaimConnection *gc);
+static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, int flags);
+static int irc_chat_send(GaimConnection *gc, int id, const char *what);
+static void irc_chat_join (GaimConnection *gc, GHashTable *data);
+static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond);
 
-/* Global variables */
-GSList *dcc_chat_list = NULL;
+static guint irc_nick_hash(const char *nick);
+static gboolean irc_nick_equal(const char *nick1, const char *nick2);
+static void irc_buddy_free(struct irc_buddy *ib);
 
-struct dcc_chat *
-find_dcc_chat (GaimConnection *gc, const char *nick)
-{
-	GSList *tmp;
-	struct dcc_chat *data;
-	tmp = dcc_chat_list;
-	while (tmp != NULL)
-		{
-			data = (struct dcc_chat *) (tmp)->data;
-			if (data 
-			    && data->nick 
-			    && strcmp (nick, data->nick) == 0
-			    && gc == data->gc)
-				{
-					return data;
-				}
-			tmp = tmp->next;
-		}
-	return NULL;
-}
+static GaimPlugin *_irc_plugin = NULL;
 
-static int 
-irc_write(int fd, char *data, int len)
-{
-	gaim_debug(GAIM_DEBUG_MISC, "irc", "C: %s", data);
-	return write(fd, data, len);
-}
-
-static char *
-irc_send_convert(GaimConnection *gc, const char *string, int maxlen, int *done)
+int irc_send(struct irc_conn *irc, const char *buf)
 {
-	char *converted = g_malloc(maxlen + 1);
-	gchar *inptr = (gchar*)string, *outptr = converted;
-	int inleft = strlen(string), outleft = maxlen;
-	GIConv conv;
-	
-	/* XXX - I think the below line is leaking */
-	conv = g_iconv_open(gaim_account_get_string(gaim_connection_get_account(gc), "charset", "UTF-8") , "UTF-8");
-	if (g_iconv(conv, &inptr, &inleft, &outptr, &outleft) == -1) {
-		gaim_debug(GAIM_DEBUG_ERROR, "irc", "Charset conversion error\n");
-		gaim_debug(GAIM_DEBUG_ERROR, "irc",
-				   "Sending as UTF-8 (this is a hack!)\n");
-		g_free(converted);
-		*done = maxlen;
-		return(g_strndup(string, maxlen));
-	}
-	
-	*done = strlen(string) - inleft;
-	*outptr = '\0';
-	return(converted);
-}
-
-static char *
-irc_recv_convert(GaimConnection *gc, char *string)
-{
-	char *utf8;
-	GError *err = NULL;
-	
-	utf8 = g_convert(string, strlen(string), "UTF-8",
-			 gaim_account_get_string(gaim_connection_get_account(gc), "charset", "UTF-8") , NULL, NULL, &err);
-	if (err) {
-		gaim_debug(GAIM_DEBUG_ERROR, "irc",
-				   "recv conversion error: %s\n", err->message);
-		utf8 = g_strdup(_("(There was an error converting this message.  Check the 'Encoding' option in the Account Editor)"));
-	}
-	
-	return (utf8);
-}
-
-static GaimConversation *
-irc_find_chat(GaimConnection *gc, const char *name)
-{
-	GSList *bcs = gc->buddy_chats;
-
-	while (bcs) {
-		GaimConversation *b = bcs->data;
-		if (!gaim_utf8_strcasecmp(b->name, name))
-			return b;
-		bcs = bcs->next;
-	}
-	return NULL;
-}
-
-static void 
-process_data_init(char *buf, char *cmd, char *word[], char *eol[], gboolean quotes)
-{
-	int wordcount = 2;
-	gboolean space = FALSE;
-	gboolean quote = FALSE;
-	int j = 0;
-
-	word[1] = cmd;
-	eol[1] = buf;
+	if (irc->fd < 0)
+		return -1;
 
-	while (TRUE) {
-		switch (*cmd) {
-		case 0:
-			buf[j] = 0;
-			for (j = wordcount; j < PDIWORDS; j++) {
-				word[j] = "\000\000";
-				eol[j] = "\000\000";
-			}
-			return;
-		case '"':
-			if (!quotes) {
-				space = FALSE;
-				buf[j++] = *cmd;
-				break;
-			}
-			quote = !quote;
-			break;
-		case ' ':
-			if (quote) {
-				space = FALSE;
-				buf[j++] = *cmd;
-				break;
-			}
-			if (space)
-				break;
-			buf[j++] = 0;
-			word[wordcount] = &buf[j];
-			eol[wordcount++] = cmd + 1;
-			if (wordcount == PDIWORDS - 1) {
-				buf[j] = 0;
-				return;
-			}
-			space = TRUE;
-			break;
-		default:
-			space = FALSE;
-			buf[j++] = *cmd;
-		}
-		cmd++;
-	}
-}
-
-static void 
-handle_005(GaimConnection *gc, char *word[], char *word_eol[])
-{
-	int w = 4;
-	struct irc_data *id = gc->proto_data;
-
-	while (w < PDIWORDS && *word[w]) {
-		if (!strncmp(word[w], "MODES=", 5)) {
-			if (atoi(word[w] + 6) >= 6)
-				id->six_modes = TRUE;
-		} else if (!strncmp(word[w], "CHANTYPES=", 10)) {
-			g_free(id->chantypes);
-			id->chantypes = g_strdup(word[w] + 10);
-		} else if (!strncmp(word[w], "CHANMODES=", 10)) {
-			g_free(id->chanmodes);
-			id->chanmodes = g_strdup(word[w] + 10);
-		} else if (!strncmp(word[w], "PREFIX=", 7)) {
-			char *pre = strchr(word[w] + 7, ')');
-			if (pre) {
-				*pre = 0;
-				g_free(id->nickmodes);
-				id->nickmodes = g_strdup(word[w] + 8);
-			}
-		}
-		w++;
-	}
+	/* gaim_debug(GAIM_DEBUG_MISC, "irc", "sent: %s", buf); */
+	return write(irc->fd, buf, strlen(buf));
 }
 
-static const char *irc_colors[] = {
-		"#000000", "#ffffff", "#000066", "#006600",
-		"#ff0000", "#660000", "#660066", "#666600",
-		"#cccc00", "#33cc33", "#00acac", "#00ccac",
-		"#0000ff", "#cc00cc", "#666666", "#00ccac"
-};
-
-#define int_to_col(c) (irc_colors[(((c)<0 || (c)> 15)?0:c)])
-
-static GString *
-encode_html(char *msg)
-{
-	GString *str = g_string_new("");
-	char *cur = msg, *end = msg;
-	gboolean bold = FALSE, underline = FALSE, italics = FALSE;
-
-	while ((end = strchr(cur, '<'))) {
-		*end = 0;
-		str = g_string_append(str, cur);
-		cur = ++end;
-		if (!g_ascii_strncasecmp(cur, "B>", 2)) {
-			if (!bold) {
-				bold = TRUE;
-				str = g_string_append_c(str, '\2');
-			}
-			cur = cur + 2;
-		} else if (!g_ascii_strncasecmp(cur, "I>", 2)) { /* use bold for italics too */
-			if (!italics) {
-				italics = TRUE;
-				str = g_string_append_c(str, '\2');
-			}
-			cur = cur + 2;
-		} else if (!g_ascii_strncasecmp(cur, "U>", 2)) {
-			if (!underline) {
-				underline = TRUE;
-				str = g_string_append_c(str, '\37');
-			}
-			cur = cur + 2;
-		}  else if (!g_ascii_strncasecmp(cur, "/B>", 3)) {
-			if (bold) {
-				bold = FALSE;
-				str = g_string_append_c(str, '\2');
-			}
-			cur = cur + 3;
-		}  else if (!g_ascii_strncasecmp(cur, "/I>", 3)) {
-			if (italics) {
-				italics = FALSE;
-				str = g_string_append_c(str, '\2');
-			}
-			cur = cur + 3;
-		}  else if (!g_ascii_strncasecmp(cur, "/U>", 3)) {
-			if (underline) {
-				underline = FALSE;
-				str = g_string_append_c(str, '\37');
-			}
-			cur = cur + 3;
-		}  else {
-			str = g_string_append_c(str, '<');
-		}
-
-	}
-	str = g_string_append(str, cur);
-	return str;
-}
-
-static GString *
-decode_html(char *msg)
+/* XXX I don't like messing directly with these buddies */
+gboolean irc_blist_timeout(struct irc_conn *irc)
 {
-	GString /* oo la la */ *str = g_string_new("");
-	char *cur = msg, *end = msg;
-	gboolean bold = FALSE, underline = FALSE, fg = FALSE, bg = FALSE;
-	int fore, back;
-	while (*end) {
-		switch (*end) {
-		case 02: /* ^B */
-			*end = 0;
-			str = g_string_append(str, cur);
-			if (bold)
-				str = g_string_append(str, "</B>");
-			else
-				str = g_string_append(str, "<B>");
-			bold = !bold;
-			cur = end + 1;
-			break;
-		case 03: /* ^C */
-			*end++ = 0;
-			str = g_string_append(str, cur);
-			fore = back = -1;
-			if (isdigit(*end)) {
-				fore = *end++ - '0';
-				if (isdigit(*end)) {
-					fore *= 10;
-					fore += *end++ - '0';
-				}
-				if (*end == ',' && isdigit(end[1])) {
-					end++;
-					back = *end++ - '0';
-					if (isdigit(*end)) {
-						back *= 10;
-						back += *end++ - '0';
-					}
-				}
-			}
-			if (fore == -1) {
-				if (fg)
-					str = g_string_append(str, "</FONT>");
-				if (bg)
-					str = g_string_append(str, "</FONT>");
-				fg = bg = FALSE;
-			} else {
-				fore %= 16;
-				if (fg)
-					str = g_string_append(str, "</FONT>");
-				if (back != -1) {
-					if (bg)
-						str = g_string_append(str, "</FONT>");
-					back %= 16;
-					str = g_string_append(str, "<FONT BACK=");
-					str = g_string_append(str, int_to_col(back));
-					str = g_string_append_c(str, '>');
-					bg = TRUE;
-				}
-				str = g_string_append(str, "<FONT COLOR=");
-				str = g_string_append(str, int_to_col(fore));
-				str = g_string_append_c(str, '>');
-				fg = TRUE;
-			}
-			cur = end--;
-			break;
-		case 017: /* ^O */
-			if (!bold && !underline && !fg && !bg)
-				break;
-			*end = 0;
-			str = g_string_append(str, cur);
-			if (bold)
-				str = g_string_append(str, "</B>");
-			if (underline)
-				str = g_string_append(str, "</U>");
-			if (fg)
-				str = g_string_append(str, "</FONT>");
-			if (bg)
-				str = g_string_append(str, "</FONT>");
-			bold = underline = fg = bg = FALSE;
-			cur = end + 1;
-			break;
-		case 037: /* ^_ */
-			*end = 0;
-			str = g_string_append(str, cur);
-			if (underline)
-				str = g_string_append(str, "</U>");
-			else
-				str = g_string_append(str, "<U>");
-			underline = !underline;
-			cur = end + 1;
-			break;
-		}
-		end++;
-	}
-	if (*cur)
-		str = g_string_append(str, cur);
-	return str;
-}
-
-static void 
-irc_got_im(GaimConnection *gc, char *who, char *what, int flags, time_t t)
-{
-	char *utf8 = irc_recv_convert(gc, what);
-	GString *str = decode_html(utf8);
-	serv_got_im(gc, who, str->str, flags, t, -1);
-	g_string_free(str, TRUE);
-	g_free(utf8);
-}
+	GString *string = g_string_sized_new(512);
+	char *list, *buf;
 
-static void 
-dcc_chat_cancel(struct dcc_chat *);
-
-void
-dcc_chat_in (gpointer data, gint source, GaimInputCondition condition)
-{
-	struct dcc_chat *chat = data;
-	gchar buffer[IRC_BUF_LEN];
-	gchar buf[128];
-	int n = 0;
-	GaimConversation *convo;
-	gaim_debug(GAIM_DEBUG_MISC, "irc", "THIS IS TOO MUCH EFFORT\n");
-	n = read (chat->fd, buffer, IRC_BUF_LEN);
-	if (n > 0) {
-
-		buffer[n] = 0;
-		g_strstrip(buffer);
-
-		/* Convert to HTML */
-		if (strlen(buffer)) {
-			gaim_debug(GAIM_DEBUG_INFO, "irc",
-					   "DCC Message from: %s\n", chat->nick);
-			irc_got_im(chat->gc, chat->nick, buffer, 0, 
-				   time(NULL));
-		}
-	}	
-	else	{
-		g_snprintf (buf, sizeof buf, _("DCC Chat with %s closed"),
-			    chat->nick);
-		convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account,
-									  chat->nick);
-		gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM,
-								time(NULL));
-		dcc_chat_cancel (chat);
-	}
-}
-
-void 
-irc_read_dcc_ack (gpointer data, gint source, GaimInputCondition condition) {
-	/* Read ACK Here */
-
-}
-
-void 
-dcc_send_callback (gpointer data, gint source, GaimInputCondition condition) {
-#if 0
-	struct irc_file_transfer *ift = data;
-	struct sockaddr_in addr;
-	int len = sizeof(addr);
-		
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons(ift->port);
-	addr.sin_addr.s_addr = INADDR_ANY;
-
-	ift->fd = accept(ift->fd, (struct sockaddr *)&addr, &len);
-	if (!ift->fd) {
-		/* FIXME: Handle this gracefully XXX */
-		printf("Something bad happened here, bubba!\n");
-		return;
-	}
-		
-	/*	ift->awatcher = gaim_input_add(ift->fd, GAIM_INPUT_READ, irc_read_dcc_ack, ift); */
-		
-	if (transfer_out_do(ift->xfer, ift->fd, 0)) {
-		gaim_input_remove(ift->watcher);
-		ift->watcher = 0;
-	}
-#endif
-}
-
-void 
-dcc_chat_callback (gpointer data, gint source, GaimInputCondition condition) {
-	struct dcc_chat *chat = data;
-	GaimConversation *convo;
-	char buf[IRC_BUF_LEN];
-
-	convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, chat->nick);
+	g_hash_table_foreach(irc->buddies, (GHFunc)irc_buddy_append, (gpointer)string);
 
-	chat->fd = source;
-	g_snprintf (buf, sizeof buf,
-		    _("DCC Chat with %s established"),
-		    chat->nick);
-	gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, time(NULL));
-	gaim_debug(GAIM_DEBUG_INFO, "irc",
-			   "Chat with %s established\n", chat->nick);
-	dcc_chat_list =  g_slist_append (dcc_chat_list, chat);
-	gaim_input_remove(chat->inpa);
-	chat->inpa = gaim_input_add(source, GAIM_INPUT_READ, dcc_chat_in, chat);
-}
-
-static void 
-irc_got_chat_in(GaimConnection *gc, int id, char *who, int whisper, char *msg, time_t t)
-{
-	char *utf8 = irc_recv_convert(gc, msg);
-	GString *str = decode_html(utf8);
-	serv_got_chat_in(gc, id, who, whisper, str->str, t);
-	g_string_free(str, TRUE);
-	g_free(utf8);
-}
-
-static void 
-handle_list(GaimConnection *gc, char *list)
-{
-	struct irc_data *id = gc->proto_data;
-	char *tmp;
-	GaimBlistNode *gnode, *bnode;
-
-	tmp = g_utf8_strdown(list, -1);
-
-	id->str = g_string_append_c(id->str, ' ');
-	id->str = g_string_append(id->str, tmp);
-	id->bc--;
-	g_free(tmp);
-	if (id->bc)
-		return;
-
-
-	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-		for(bnode = gnode->child; bnode; bnode = bnode->next) {
-			struct buddy *b = (struct buddy *)bnode;
-			if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-				continue;
-			if(b->account->gc == gc) {
-				char *tmp = g_utf8_strdown(b->name, -1);
-				char *x, *l;
-				x = strstr(id->str->str, tmp);
-				l = x + strlen(b->name);
-				if (x && (*l != ' ' && *l != 0))
-					x = 0;
-				if (!GAIM_BUDDY_IS_ONLINE(b) && x)
-					serv_got_update(gc, b->name, 1, 0, 0, 0, 0);
-				else if (GAIM_BUDDY_IS_ONLINE(b) && !x)
-					serv_got_update(gc, b->name, 0, 0, 0, 0, 0);
-				g_free(tmp);
-			}
-		}
-	}
-	g_string_free(id->str, TRUE);
-	id->str = g_string_new("");
-}
-
-static gboolean 
-irc_request_buddy_update(gpointer data)
-{
-	GaimConnection *gc = data;
-	struct irc_data *id = gc->proto_data;
-	char buf[500];
-	int n = g_snprintf(buf, sizeof(buf), "ISON");
-	gboolean found = FALSE;
-
-	GaimBlistNode *gnode, *bnode;
-
-	if (id->bc)
+	list = g_string_free(string, FALSE);
+	if (!list || !strlen(list)) {
+		g_free(list);
 		return TRUE;
-
-	for(gnode = gaim_get_blist()->root; gnode; gnode = gnode->next) {
-		if(!GAIM_BLIST_NODE_IS_GROUP(gnode))
-			continue;
-		for(bnode = gnode->child; bnode; bnode = bnode->next) {
-			struct buddy *b = (struct buddy *)bnode;
-			if(!GAIM_BLIST_NODE_IS_BUDDY(bnode))
-				continue;
-			if(b->account->gc == gc) {
-				if (n + strlen(b->name) + 2 > sizeof(buf)) {
-					g_snprintf(buf + n, sizeof(buf) - n, "\r\n");
-					irc_write(id->fd, buf, n);
-					id->bc++;
-					n = g_snprintf(buf, sizeof(buf), "ISON");
-				}
-				n += g_snprintf(buf + n, sizeof(buf) - n, " %s", b->name);
-
-				found = TRUE;
-			}
-		}
 	}
 
-	if (found) {
-		g_snprintf(buf + n, sizeof(buf) - n, "\r\n");
-		irc_write(id->fd, buf, strlen(buf));
-		id->bc++;
-	}
+	buf = irc_format(irc, "v:", "ISON", list);
+	g_free(list);
+	irc_send(irc, buf);
+	g_free(buf);
 
 	return TRUE;
 }
 
-static void 
-handle_names(GaimConnection *gc, char *chan, char *names)
-{
-	GaimConversation *c = irc_find_chat(gc, chan);
-	GaimChat *chat;
-	char **buf, **tmp;
-
-	if (!c) return;
-	if (*names == ':') names++;
-
-	chat = GAIM_CHAT(c);
-
-	buf = g_strsplit(names, " ", -1);
-
-	for (tmp = buf; *tmp; tmp++)
-		gaim_chat_add_user(chat, *tmp, NULL);
-
-	g_strfreev(buf);
-}
-
-static void 
-handle_notopic(GaimConnection *gc, char *text)
-{
-	GaimConversation *c;
-
-	if ((c = irc_find_chat(gc, text))) {
-		char buf[IRC_BUF_LEN];
-
-		g_snprintf(buf, sizeof(buf), _("No topic is set"));
-
-		gaim_chat_set_topic(GAIM_CHAT(c), NULL, buf);
-	}
-}
-
-static void 
-handle_topic(GaimConnection *gc, char *text)
-{
-	GaimConversation *c;
-	char *po = strchr(text, ' '), *buf;
-
-	if (!po)
-		return;
-
-	*po = 0;
-	po += 2;
-
-	if ((c = irc_find_chat(gc, text))) {
-                po = irc_recv_convert(gc, po);
-		gaim_chat_set_topic(GAIM_CHAT(c), NULL, po);
-		buf = g_strdup_printf(_("<B>%s has changed the topic to: %s</B>"), text, po);
-		gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL));
-                g_free(buf);
-                g_free(po);
-	}
-}
-
-static gboolean 
-mode_has_arg(GaimConnection *gc, char sign, char mode)
-{
-	struct irc_data *id = gc->proto_data;
-	char *cm = id->chanmodes;
-	int type = 0;
-
-	if (strchr(id->nickmodes, mode))
-		return TRUE;
-
-	while (*cm) {
-		if (*cm == ',')
-			type++;
-		else if (*cm == mode) {
-			switch (type) {
-			case 0:
-			case 1:
-				return TRUE;
-			case 2:
-				if (sign == '+')
-					return TRUE;
-			case 3:
-				return FALSE;
-			}
-		}
-		cm++;
-	}
-
-	return FALSE;
-}
-
-static void 
-irc_chan_mode(GaimConnection *gc, char *room, char sign, char mode, char *argstr, char *who)
-{
-	GaimConversation *c = irc_find_chat(gc, room);
-	char buf[IRC_BUF_LEN];
-	char *nick = g_strndup(who, strchr(who, '!') - who);
-
-	g_snprintf(buf, sizeof(buf), _("-:- mode/%s [%c%c %s] by %s"), 
-		   room, sign, mode, strlen(argstr) ? argstr : "",
-		   nick);
-	g_free(nick);
-
-	gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL));
-}
-
-static void 
-irc_user_mode(GaimConnection *gc, char *room, char sign, char mode, char *nick)
+static void irc_buddy_append(char *name, struct irc_buddy *ib, GString *string)
 {
-	GaimConversation *c = irc_find_chat(gc, room);
-	GList *r;
-
-	if (mode != 'o' && mode != 'v' && mode != 'h')
-		return;
-
-	if (!c)
-		return;
-
-	r = gaim_chat_get_users(GAIM_CHAT(c));
-	while (r) {
-		gboolean op = FALSE, halfop = FALSE, voice = FALSE;
-		char *who = r->data;
-
-		if (*who == '@') {
-			op = TRUE;
-			who++;
-		}
-
-		if (*who == '%') {
-			halfop = TRUE;
-			who++;
-		}
-
-		if (*who == '+') {
-			voice = TRUE;
-			who++;
-		}
-
-		if (!strcmp(who, nick)) {
-			char *tmp, buf[IRC_BUF_LEN];
-
-			if (mode == 'o') {
-				if (sign == '-')
-					op = FALSE;
-				else
-					op = TRUE;
-			}
-
-			if (mode == 'h') {
-				if (sign == '-')
-					halfop = FALSE;
-				else
-					halfop = TRUE;
-			}
-
-			if (mode == 'v') {
-				if (sign == '-')
-					voice = FALSE;
-				else
-					voice = TRUE;
-			}
-
-			tmp = g_strdup(r->data);
-			g_snprintf(buf, sizeof(buf), "%s%s%s",
-					   (op ? "@" : (halfop ? "%" : "")),
-					   voice ? "+" : "", nick);
-			gaim_chat_rename_user(GAIM_CHAT(c), tmp, buf);
-			g_free(tmp);
-			return;
-		}
-		r = r->next;
-	}
-}
-
-static void 
-handle_mode(GaimConnection *gc, char *word[], char *word_eol[], gboolean n324)
-{
-	struct irc_data *id = gc->proto_data;
-	int offset = n324 ? 4 : 3;
-	char *chan = word[offset];
-	GaimConversation *c = irc_find_chat(gc, chan);
-	char *modes = word[offset + 1];
-	int len = strlen(word_eol[offset]) - 1;
-	char sign = *modes++;
-	int arg = 1;
-	char *argstr;
-	char *who = word[1];
-
-	if (!c)
-		return;
-
-	if (word_eol[offset][len] == ' ')
-		word_eol[offset][len] = 0;
-
-	while (TRUE) {
-		switch (*modes) {
-		case 0:
-			return;
-		case '+':
-		case '-':
-			sign = *modes;
-			break;
-		default:
-			if (mode_has_arg(gc, sign, *modes))
-				argstr = word[++arg + offset];
-			else
-				argstr = "";
-			if (strchr(id->nickmodes, *modes))
-				irc_user_mode(gc, chan, sign, *modes, argstr);
-			else if (strchr(who, '!'))				
-				irc_chan_mode(gc, chan, sign, *modes, argstr, who);
-		}
-		modes++;
-	}
-}
-
-static void
-handle_version(GaimConnection *gc, char *word[], char *word_eol[], int num)
-{
-	struct irc_data *id = gc->proto_data;
-	GString *str;
-
-	id->liststr = g_string_new("");
-
-	id->liststr = g_string_append(id->liststr, "<b>Version: </b>");
-	id->liststr = g_string_append(id->liststr, word_eol[4]);
-
-	str = decode_html(id->liststr->str);
-	g_show_info_text(gc, NULL, 2, str->str, NULL);
-	g_string_free(str, TRUE);
-	g_string_free(id->liststr, TRUE);
-	id->liststr = NULL;
-}
-
-static void 
-handle_who(GaimConnection *gc, char *word[], char *word_eol[], int num)
-{
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-
-	if (!id->in_whois) {
-		id->in_whois = TRUE;
-		id->liststr = g_string_new("");
-	}
-
-	switch (num) {
-	case 352:
-		g_snprintf(buf, sizeof(buf), "<b>%s</b> (%s@%s): %s<br>", 
-			   word[8], word[5], word[6], word_eol[11]);
-		id->liststr = g_string_append(id->liststr, buf);
-		break;
-	}
-}
-
-/* Handle our whois stuff here.  You know what, I have a sore throat.  You know
- * what I think about that? I'm not too pleased with it.  Perhaps I should take
- * some medicine, or perhaps I should go to bed? Blah!! */
-
-static void 
-handle_whois(GaimConnection *gc, char *word[], char *word_eol[], int num)
-{
-	struct irc_data *id = gc->proto_data;
-	char tmp[1024];
-
-	if (!id->in_whois) {
-		id->in_whois = TRUE;
-		id->liststr = g_string_new("");
-	} else {
-		/* I can't decide if we should have one break or two */
-		id->liststr = g_string_append(id->liststr, "<BR>");
-		id->in_whois = TRUE;
-	}
-
-	switch (num) {
-	case 311:
-		g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("User"));
-		id->liststr = g_string_append(id->liststr, tmp);
-		break;
-	case 312:
-		g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Server"));
-		id->liststr = g_string_append(id->liststr, tmp);
-		break;
-	case 313:
-		g_snprintf(tmp, sizeof(tmp), "<b>%s:</b> %s ", _("IRC Operator"), word[4]);
-		id->liststr = g_string_append(id->liststr, tmp);
-		break;
-	case 314:
-		g_snprintf(tmp, sizeof(tmp), "<b>%s: </b><b>%s</b> (%s@%s) %s",
-			   _("User"), word[4], word[5], word[6], word_eol[8]);
-		id->liststr = g_string_append(id->liststr, tmp);
-		return;
-	case 317:
-		g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Idle Time"));
-		id->liststr = g_string_append(id->liststr, tmp);
-		break;
-	case 319:
-		g_snprintf(tmp, sizeof(tmp), "<b>%s: </b>", _("Channels"));
-		id->liststr = g_string_append(id->liststr, tmp);
-		break;
-	/* Numeric 320 is used by the freenode irc network for showing 
-	 * that a user is identified to services (Jason Straw <misato@wopn.org>)*/
-	case 320:
-		g_snprintf(tmp, sizeof(tmp), _("%s is an Identified User"), word[4]);
-		id->liststr = g_string_append(id->liststr, tmp);
-		return;
-	default:
-		break;
-	}
-
-	if (word_eol[5][0] == ':')
-		id->liststr = g_string_append(id->liststr, word_eol[5] + 1);
-	/* Nicer idle time output, by jonas@birme.se */
-	else if (isdigit(word_eol[5][0])) {
-		time_t idle = atol(word_eol[5]);
-		time_t signon = atol(strchr(word_eol[5], ' '));
-				
-		g_snprintf(tmp, sizeof(tmp), 
-			   _("%ld seconds [signon: %s]"), (idle / 1000), ctime(&signon));
-		id->liststr = g_string_append(id->liststr, tmp);
-	}
-	else
-		id->liststr = g_string_append(id->liststr, word_eol[5]);
-}
-
-static void 
-handle_roomlist(GaimConnection *gc, char *word[], char *word_eol[])
-{
-	struct irc_data *id = gc->proto_data;
-
-	if (!id->in_list) {
-		id->in_list = TRUE;
-		id->liststr = g_string_new("");
-	} else {
-		id->liststr = g_string_append(id->liststr, "<BR>");
-		id->in_list = TRUE;
-	}
-
-	id->liststr = g_string_append(id->liststr, word_eol[4]);
-}
-
-static void 
-irc_change_nick(void *a, const char *b) {
-	GaimConnection *gc = a;
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];	
-	g_snprintf(buf, sizeof(buf), "NICK %s\r\n", b);
-	irc_write(id->fd, buf, strlen(buf));
-	gaim_connection_set_display_name(gc, b);
+	ib->flag = FALSE;
+	g_string_append_printf(string, "%s ", name);
 }
 
-static void 
-process_numeric(GaimConnection *gc, char *word[], char *word_eol[])
-{
-	const char *displayname = gaim_connection_get_display_name(gc);
-	struct irc_data *id = gc->proto_data;
-	char *text = word_eol[3];
-	int n = atoi(word[2]);
-	char tmp[1024];
-
-	if (!g_ascii_strncasecmp(displayname, text, strlen(displayname)))
-		text += strlen(displayname) + 1;
-	if (*text == ':')
-		text++;
-
-	/* RPL_ and ERR_ */
-	switch (n) {
-	case 4:
-		if (!strncmp(word[5], "u2.10", 5))
-			id->six_modes = TRUE;
-		else
-			id->six_modes = FALSE;
-		break;
-	case 5:
-		handle_005(gc, word, word_eol);
-		break;
-	case 301: /* RPL_AWAY */
-		if (id->in_whois) {
-			g_snprintf(tmp, sizeof(tmp), "<BR><b>%s: </b>", _("Away"));
-			id->liststr = g_string_append(id->liststr, tmp);
-
-			if (word_eol[5][0] == ':')
-				id->liststr = g_string_append(id->liststr, word_eol[5] + 1);
-			else
-				id->liststr = g_string_append(id->liststr, word_eol[5]);
-		} else
-			irc_got_im(gc, word[4], word_eol[5], IM_FLAG_AWAY, time(NULL));
-		break;
-	case 303: /* RPL_ISON */
-		handle_list(gc, &word_eol[4][1]);
-		break;
-	case 311: /* RPL_WHOISUSER */
-	case 312: /* RPL_WHOISSERVER */
-	case 313: /* RPL_WHOISOPERATOR */
-	case 314: /* RPL_WHOWASUSER */
-	case 317: /* RPL_WHOISIDLE */
-	case 319: /* RPL_WHOISCHANNELS */
-	case 320: /* FreeNode Identified */
-		handle_whois(gc, word, word_eol, n);
-		break;
-	case 322: /* RPL_LIST */
-		handle_roomlist(gc, word, word_eol);
-		break;
-	case 315: /* RPL_ENDOFWHO */
-	case 318: /* RPL_ENDOFWHOIS */
-	case 323: /* RPL_LISTEND */
-	case 369: /* RPL_ENDOFWHOWAS */
-		if ((id->in_whois || id->in_list) && id->liststr) {
-			GString *str = decode_html(id->liststr->str);
-			g_show_info_text(gc, NULL, 2, str->str, NULL);
-			g_string_free(str, TRUE);
-			g_string_free(id->liststr, TRUE);
-			id->liststr = NULL;
-			id->in_whois = FALSE;
-			id->in_list = FALSE;
-		}
-		break;
-	case 324: /* RPL_CHANNELMODEIS */
-		handle_mode(gc, word, word_eol, TRUE);
-		break;
-	case 331: /* RPL_NOTOPIC */
-		handle_notopic(gc, text);
-		break;
-	case 332: /* RPL_TOPIC */
-		handle_topic(gc, text);
-		break;
-	case 351: /* RPL_VERSION */
-		handle_version(gc, word, word_eol, n);
-		break;
-	case 352: /* RPL_WHOREPLY */
-		handle_who(gc, word, word_eol, n);
-		break;
-	case 353: /* RPL_NAMREPLY */
-		handle_names(gc, word[5], word_eol[6]);
-		break;
-	case 376: /* RPL_ENDOFMOTD */
-		irc_request_buddy_update(gc);
-		break;
-	case 382: /* RPL_REHASHING */
-		gaim_notify_error(gc, NULL, _("Rehashing server"), _("IRC Operator"));
-		break;
-	case 401: /* ERR_NOSUCHNICK */
-		gaim_notify_error(gc, NULL, _("No such nick/channel"), _("IRC Error"));
-		break;
-	case 402: /* ERR_NOSUCHSERVER */
-		gaim_notify_error(gc, NULL, _("No such server"), _("IRC Error"));
-		break;
-	case 422: /* ERR_NOMOTD */
-		break;  /* drop it - bringing up dialog for NOMOTD is annoying */
-	case 431: /* ERR_NONICKNAMEGIVEN */
-		gaim_notify_error(gc, NULL, _("No nickname given"), _("IRC Error"));
-		break;
-	case 481: /* ERR_NOPRIVILEGES */
-		gaim_notify_error(gc, NULL, _("You're not an IRC operator!"),
-						  _("IRC Error"));
-		break;		
-	case 433:
-		gaim_request_input(gc, NULL, _("That nick is already in use.  "
-									   "Please enter a new nick"),
-						   NULL, gaim_connection_get_display_name(gc),
-						   FALSE, FALSE,
-						   _("OK"), G_CALLBACK(irc_change_nick),
-						   _("Cancel"), NULL, gc);
-		break;
-	default:
-		/* Other error messages */
-		if (n > 400 && n < 502) {
-			char errmsg[IRC_BUF_LEN];
-			char *errmsg1 = strrchr(text, ':');
-
-			g_snprintf(errmsg, sizeof(errmsg), "IRC Error %d", n);
-
-			if (errmsg) {
-				gaim_notify_error(gc, NULL, errmsg,
-								  (errmsg1 ? errmsg1 + 1 : NULL));
-			}
-		}
-
-		break;
-	}
-}
-
-static gboolean 
-is_channel(GaimConnection *gc, const char *name)
-{
-	struct irc_data *id = gc->proto_data;
-	if (strchr(id->chantypes, *name))
-		return TRUE;
-	return FALSE;
-}
-
-static void 
-irc_rem_chat_bud(GaimConnection *gc, char *nick, GaimConversation *b, char *reason)
-{
-
-	GaimChat *chat;
-
-	if (b) {
-		GList *r;
-
-		chat = GAIM_CHAT(b);
-
-		r = gaim_chat_get_users(chat);
-
-		while (r) {
-			char *who = r->data;
-			if (*who == '@')
-				who++;
-			if (*who == '%')
-				who++;
-			if (*who == '+')
-				who++;
-			if (!gaim_utf8_strcasecmp(who, nick)) {
-				gaim_chat_remove_user(chat, who, reason);
-				break;
-			}
-			r = r->next;
-		}
-	} else {
-		GSList *bcs = gc->buddy_chats;
-		while (bcs) {
-			GaimConversation *bc = bcs->data;
-			irc_rem_chat_bud(gc, nick, bc, reason);
-			bcs = bcs->next;
-		}
-	}
-}
-
-static void 
-irc_change_name(GaimConnection *gc, char *old, char *new)
-{
-	GSList *bcs = gc->buddy_chats;
-	char buf[IRC_BUF_LEN];
-
-	while (bcs) {
-		GaimConversation *b = bcs->data;
-		GaimChat *chat;
-		GList *r;
-
-		chat = GAIM_CHAT(b);
-
-		r = gaim_chat_get_users(chat);
-
-		while (r) {
-			char *who = r->data;
-			int n = 0;
-			if (*who == '@')
-				buf[n++] = *who++;
-			if (*who == '%')
-				buf[n++] = *who++;
-			if (*who == '+')
-				buf[n++] = *who++;
-			g_snprintf(buf + n, sizeof(buf) - n, "%s", new);
-			if (!strcmp(who, old)) {
-				char *tmp = g_strdup(r->data);
-				gaim_chat_rename_user(chat, tmp, buf);
-				r = gaim_chat_get_users(chat);
-				g_free(tmp);
-				break;
-			} else
-				r = r->next;
-		}
-		bcs = bcs->next;
-	}
-}
-
-static void 
-handle_privmsg(GaimConnection *gc, char *to, char *nick, char *msg)
-{
-	if (is_channel(gc, to)) {
-		GaimConversation *c = irc_find_chat(gc, to);
-		if (!c)
-			return;
-		irc_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)),
-						nick, 0, msg, time(NULL));
-	} else {
-		char *tmp = g_malloc(strlen(nick) + 2);
-		g_snprintf(tmp, strlen(nick) + 2, "@%s", nick);
-		if (gaim_find_conversation(tmp))
-			irc_got_im(gc, tmp, msg, 0, time(NULL));
-		else {
-			*tmp = '+';
-			if (gaim_find_conversation(tmp))
-				irc_got_im(gc, tmp, msg, 0, time(NULL));
-			else
-				irc_got_im(gc, nick, msg, 0, time(NULL));
-		}
-		g_free(tmp);
-	}
-}
-
-static void 
-dcc_chat_init(struct dcc_chat *data) {
-	if (g_list_find(gaim_connections_get_all(), data->gc)) {
-		gaim_proxy_connect(data->gc->account, data->ip_address, data->port, dcc_chat_callback, data);
-	} else {
-		g_free(data);
-	}
-}
-
-static void 
-dcc_chat_cancel(struct dcc_chat *data){
-	if (g_list_find(gaim_connections_get_all(), data->gc) && find_dcc_chat(data->gc, data->nick)) {
-		dcc_chat_list = g_slist_remove(dcc_chat_list, data); 
-		gaim_input_remove (data->inpa);
-		close (data->fd);
-	}
-	g_free(data);
-}
-
-static void 
-irc_convo_closed(GaimConnection *gc, const char *who)
+static const char *irc_blist_icon(GaimAccount *a, struct buddy *b)
 {
-	struct dcc_chat *dchat = find_dcc_chat(gc, who);
-	if (!dchat)
-		return;
-
-	dcc_chat_cancel(dchat);
-}
-
-static void
-irc_xfer_init(GaimXfer *xfer)
-{
-	struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data;
-
-	gaim_xfer_start(xfer, -1, data->ip, data->port);
-}
-
-static void
-irc_xfer_end(GaimXfer *xfer)
-{
-	struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data;
-
-	data->idata->file_transfers = g_slist_remove(data->idata->file_transfers,
-												 xfer);
-
-	g_free(data);
-	xfer->data = NULL;
-}
-
-static void
-irc_xfer_cancel_send(GaimXfer *xfer)
-{
-	struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data;
-
-	data->idata->file_transfers = g_slist_remove(data->idata->file_transfers,
-												 xfer);
-
-	g_free(data);
-	xfer->data = NULL;
-}
-
-static void
-irc_xfer_cancel_recv(GaimXfer *xfer)
-{
-	struct irc_xfer_data *data = (struct irc_xfer_data *)xfer->data;
-
-	data->idata->file_transfers = g_slist_remove(data->idata->file_transfers,
-												 xfer);
-
-	g_free(data);
-	xfer->data = NULL;
-}
-
-static void
-irc_xfer_ack(GaimXfer *xfer, const char *buffer, size_t size)
-{
-	guint32 pos;
-
-	pos = htonl(gaim_xfer_get_bytes_sent(xfer));
-
-	write(xfer->fd, (char *)&pos, 4);
-}
-
-/* NOTE: This was taken from irssi. Thanks irssi! */
-
-static gboolean
-is_numeric(const char *str, char end_char)
-{
-	g_return_val_if_fail(str != NULL, FALSE);
-
-	if (*str == '\0' || *str == end_char)
-		return FALSE;
-
-	while (*str != '\0' && *str != end_char) {
-		if (*str < '0' || *str > '9')
-			return FALSE;
-
-		str++;
-	}
-
-	return TRUE;
-}
-
-#define get_params_match(params, pos) \
-	(is_numeric(params[pos], '\0') && \
-	is_numeric(params[(pos)+1], '\0') && atol(params[(pos)+1]) < 65536 && \
-	is_numeric(params[(pos)+2], '\0'))
-
-/* Return number of parameters in `params' that belong to file name.
-   Normally it's paramcount-3, but I don't think anything forbids of
-   adding some extension where there could be more parameters after
-   file size.
-
-   MIRC sends filenames with spaces quoted ("file name"), but I'd rather
-   not trust that entirely either. At least some clients that don't really
-   understand the problem with spaces in file names sends the file name
-   without any quotes. */
-static int
-get_file_params_count(char **params, int paramcount)
-{
-	int pos, best;
-
-	if (*params[0] == '"') {
-		/* quoted file name? */
-		for (pos = 0; pos < paramcount - 3; pos++) {
-			if (params[pos][strlen(params[pos]) - 1] == '"' &&
-				get_params_match(params, pos + 1)) {
-
-				return pos + 1;
-			}
-		}
-	}
-
-	best = paramcount - 3;
-
-	for (pos = paramcount - 3; pos > 0; pos--) {
-		if (get_params_match(params, pos))
-			best = pos;
-	}
-
-	return best;
-}
-
-static void 
-handle_ctcp(GaimConnection *gc, char *to, char *nick,
-			char *msg, char *word[], char *word_eol[])
-{
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-	char out[IRC_BUF_LEN];
-
-	if (!g_ascii_strncasecmp(msg, "VERSION", 7)) {
-		g_snprintf(buf, sizeof(buf), "\001VERSION Gaim " VERSION ": The Penguin Pimpin' "
-			   "Multi-protocol Messaging Client: " WEBSITE "\001");
-		irc_send_notice (gc, nick, buf);
-		g_snprintf(out, sizeof(out), ">> CTCP VERSION requested from %s", nick);
-		gaim_notify_info(gc, NULL, out, _("IRC CTCP info"));
-	}
-	else if (!g_ascii_strncasecmp(msg, "CLIENTINFO", 10)) {
-		g_snprintf(buf, sizeof(buf), "\001CLIENTINFO USERINFO CLIENTINFO VERSION\001");
-		irc_send_notice (gc, nick, buf);
-		g_snprintf(out, sizeof(out), ">> CTCP CLIENTINFO requested from %s", nick);
-		gaim_notify_info(gc, NULL, out, _("IRC CTCP info"));
-	}
-	else if (!g_ascii_strncasecmp(msg, "USERINFO", 8)) {
-		g_snprintf(buf, sizeof(buf), "\001USERINFO Alias: %s\001", gc->account->alias);
-		irc_send_notice (gc, nick, buf);
-		g_snprintf(out, sizeof(out), ">> CTCP USERINFO requested from %s", nick);
-		gaim_notify_info(gc, NULL, out, _("IRC CTCP info"));
-	}
-	else if (!g_ascii_strncasecmp(msg, "ACTION", 6)) {
-		char *po = strchr(msg + 6, 1);
-		char *tmp;
-		if (po) *po = 0;
-		tmp = g_strconcat("/me", msg + 6, NULL);
-		handle_privmsg(gc, to, nick, tmp);
-		g_free(tmp);
-	}
-	else if (!g_ascii_strncasecmp(msg, "PING", 4)) {
-		g_snprintf(buf, sizeof(buf), "\001%s\001", msg);
-		irc_send_notice (gc, nick, buf);
-		g_snprintf(out, sizeof(out), ">> CTCP PING requested from %s", nick);
-		gaim_notify_info(gc, NULL, out, _("IRC CTCP info"));
-	}
-	else if (!g_ascii_strncasecmp(msg, "DCC CHAT", 8)) {
-		char **chat_args = g_strsplit(msg, " ", 5);
-		char ask[1024];
-		struct dcc_chat *dccchat = g_new0(struct dcc_chat, 1);
-		dccchat->gc = gc;	
-		g_snprintf(dccchat->ip_address, sizeof(dccchat->ip_address), chat_args[3]);	
-		dccchat->port=atoi(chat_args[4]);		
-		g_snprintf(dccchat->nick, sizeof(dccchat->nick), nick);	
-		g_snprintf(ask, sizeof(ask), _("%s would like to establish a DCC chat"), nick);
-
-		gaim_request_action(gc, NULL, ask,
-							_("This requires a direct connection to be "
-							  "established between the two computers.  "
-							  "Messages sent will not pass through the "
-							  "IRC server"), 0, dccchat, 2,
-							_("Connect"), G_CALLBACK(dcc_chat_init),
-							_("Cancel"), G_CALLBACK(dcc_chat_cancel));
-	}
-	else if (!g_ascii_strncasecmp(msg, "DCC SEND", 8)) {
-		GaimXfer *xfer;
-		char **send_args;
-		char *ip, *filename;
-		struct irc_xfer_data *xfer_data;
-		size_t size;
-		int param_count, file_params, len;
-		int port;
-
-		/* Okay, this is ugly, but should get us past "DCC SEND" */
-		msg = strstr(msg, "DCC SEND");
-		msg = strchr(msg, ' ') + 1;
-		msg = strchr(msg, ' ') + 1;
-
-		/* SEND <file name> <address> <port> <size> [...] */
-		send_args = g_strsplit(msg, " ", -1);
-
-		for (param_count = 0; send_args[param_count] != NULL; param_count++)
-			;
-
-		if (param_count < 4) {
-			char buf[IRC_BUF_LEN];
-
-			g_snprintf(buf, sizeof(buf),
-					   _("Received an invalid file send request from %s."),
-					   nick);
-
-			gaim_notify_error(gc, NULL, buf, _("IRC Error"));
-
-			return;
-		}
-
-		file_params = get_file_params_count(send_args, param_count);
-
-		/* send_args[paramcount - 1][strlen(send_args[5])-1] = 0; */
-
-		/* Give these better names. */
-		ip       = send_args[file_params];
-		port     = atoi(send_args[file_params + 1]);
-		size     = atoi(send_args[file_params + 2]);
-
-		send_args[file_params] = NULL;
-
-		filename = g_strjoinv(" ", send_args);
-
-		g_strfreev(send_args);
-
-		len = strlen(filename);
-
-		if (len > 1 && *filename == '"' && filename[len - 1] == '"') {
-			/* "file name" - MIRC sends filenames with spaces like this */
-			filename[len - 1] = '\0';
-			g_memmove(filename, filename + 1, len);
-		}
-
-		/* Setup the IRC-specific transfer data. */
-		xfer_data = g_malloc0(sizeof(struct irc_xfer_data));
-		xfer_data->ip    = ip;
-		xfer_data->port  = port;
-		xfer_data->idata = id;
-
-		/* Build the file transfer handle. */
-		xfer = gaim_xfer_new(gc->account, GAIM_XFER_RECEIVE, nick);
-		xfer->data = xfer_data;
-
-		/* Set the info about the incoming file. */
-		gaim_xfer_set_filename(xfer, filename);
-		gaim_xfer_set_size(xfer, size);
-
-		g_free(filename);
-
-		/* Setup our I/O op functions. */
-		gaim_xfer_set_init_fnc(xfer,        irc_xfer_init);
-		gaim_xfer_set_end_fnc(xfer,         irc_xfer_end);
-		gaim_xfer_set_cancel_send_fnc(xfer, irc_xfer_cancel_send);
-		gaim_xfer_set_cancel_recv_fnc(xfer, irc_xfer_cancel_recv);
-		gaim_xfer_set_ack_fnc(xfer,         irc_xfer_ack);
-
-		/* Keep track of this transfer for later. */
-		id->file_transfers = g_slist_append(id->file_transfers, xfer);
-
-		/* Now perform the request! */
-		gaim_xfer_request(xfer);
-	}
+	return "irc";
 }
 
-static gboolean 
-irc_parse(GaimConnection *gc, char *buf)
-{
-	struct irc_data *idata = gc->proto_data;
-	gchar outbuf[IRC_BUF_LEN];
-	char *word[PDIWORDS], *word_eol[PDIWORDS];
-	char pdibuf[522];
-	char *ex, ip[128], nick[128];
-	char *cmd;
-
-	/* Check for errors */
-	
-	if (*buf != ':') {
-		if (!strncmp(buf, "NOTICE ", 7))
-			buf += 7;
-		if (!strncmp(buf, "PING ", 5)) {
-			int r = FALSE;
-			g_snprintf(outbuf, sizeof(outbuf), "PONG %s\r\n", buf + 5);
-			if (irc_write(idata->fd, outbuf, strlen(outbuf)) < 0) {
-				gaim_connection_error(gc, _("Unable to write"));
-				r = TRUE;
-			}
-			return r;
-		}
-		/* XXX doesn't handle ERROR */
-		return FALSE;
-	}
-
-	if (!idata->online) {
-		/* Now lets sign ourselves on */
-		gaim_connection_set_state(gc, GAIM_CONNECTED);
-		serv_finish_login(gc);
-
-		/* we don't call this now because otherwise some IRC servers might not like us */
-		idata->timer = g_timeout_add(20000, irc_request_buddy_update, gc);
-		idata->online = TRUE;
-	}
-
-	buf++;
-
-	process_data_init(pdibuf, buf, word, word_eol, FALSE);
-
-	if (atoi(word[2])) {
-		if (*word_eol[3])
-			process_numeric(gc, word, word_eol);
-		return FALSE;
-	}
-
-	cmd = word[2];
-
-	ex = strchr(pdibuf, '!');
-	if (!ex) {
-		strncpy(ip, pdibuf, sizeof(ip));
-		ip[sizeof(ip)-1] = 0;
-		strncpy(nick, pdibuf, sizeof(nick));
-		nick[sizeof(nick)-1] = 0;
-	} else {
-		strncpy(ip, ex + 1, sizeof(ip));
-		ip[sizeof(ip)-1] = 0;
-		strncpy(nick, pdibuf, sizeof(nick));
-		nick[sizeof(nick)-1] = 0;
-		if ((ex - pdibuf) < sizeof (nick))
-			nick[ex - pdibuf] = 0; /* cut the buffer at the '!' */
-	}
-
-	if (!strcmp(cmd, "INVITE")) {
-		GHashTable *components = g_hash_table_new_full(g_str_hash, g_str_equal,
-				g_free, g_free);
-
-		g_hash_table_replace(components, g_strdup("channel"), g_strdup(word[4]));
-
-		serv_got_chat_invite(gc, word[4] + 1, nick, NULL, components);
-	} else if (!strcmp(cmd, "JOIN")) {
-		irc_parse_join(gc, nick, word, word_eol);
-	} else if (!strcmp(cmd, "KICK")) {
-		if (!strcmp(gaim_connection_get_display_name(gc), word[4])) {
-			GaimConversation *c = irc_find_chat(gc, word[3]);
-			if (!c)
-				return FALSE;
-			gc->buddy_chats = g_slist_remove(gc->buddy_chats, c);
-			gaim_conversation_set_account(c, NULL);
-			g_snprintf(outbuf, sizeof(outbuf), _("You have been kicked from %s: %s"),
-				   word[3], *word_eol[5] == ':' ? word_eol[5] + 1 : word_eol[5]);
-			gaim_notify_error(gc, NULL, outbuf, _("IRC Error"));
-		} else {
-			char *reason = *word_eol[5] == ':' ? word_eol[5] + 1 : word_eol[5];
-			char *msg = g_strdup_printf(_("Kicked by %s: %s"), nick, reason);
-			GaimConversation *c = irc_find_chat(gc, word[3]);
-			irc_rem_chat_bud(gc, word[4], c, msg);
-			g_free(msg);
-		}
-	} else if (!strcmp(cmd, "KILL")) { /* */
-	} else if (!strcmp(cmd, "MODE")) {
-		handle_mode(gc, word, word_eol, FALSE);
-	} else if (!strcmp(cmd, "NICK")) {
-		char *new = *word_eol[3] == ':' ? word_eol[3] + 1 : word_eol[3];
-		if (!strcmp(gaim_connection_get_display_name(gc), nick))
-			gaim_connection_set_display_name(gc, new);
-		irc_change_name(gc, nick, new);
-	} else if (!strcmp(cmd, "NOTICE")) {
-		irc_parse_notice(gc, nick, ex, word, word_eol);
-	} else if (!strcmp(cmd, "PART")) {
-		if (!irc_parse_part(gc, nick, cmd, word, word_eol))
-			return FALSE;
-	} else if (!strcmp(cmd, "PRIVMSG")) {
-		char *to, *msg;
-		if (!*word[3])
-			return FALSE;
-		to = word[3];
-		msg = *word_eol[4] == ':' ? word_eol[4] + 1 : word_eol[4];
-		if (msg[0] == 1 && msg[strlen (msg) - 1] == 1) { /* ctcp */
-			if (!g_ascii_strncasecmp(msg + 1, "DCC ", 4))
-				process_data_init(pdibuf, buf, word, word_eol, TRUE);
-			handle_ctcp(gc, to, nick, msg + 1, word, word_eol);
-		} else {
-			handle_privmsg(gc, to, nick, msg);
-		}
-	} else if (!strcmp(cmd, "PONG")) { /* */
-	} else if (!strcmp(cmd, "QUIT")) {
-		irc_rem_chat_bud(gc, nick, irc_find_chat(gc, word[3]), *word_eol[3] == ':' ? word_eol[3] + 1 : word_eol[3]);
-	} else if (!strcmp(cmd, "TOPIC")) {
-		irc_parse_topic(gc, nick, word, word_eol);
-	} else if (!strcmp(cmd, "WALLOPS")) { /* Don't know if a dialog box is the right way? */
-		char *msg = strrchr(word_eol[0], ':');
-		if (msg)
-			gaim_notify_error(gc, NULL, msg+1, _("IRC Operator"));
-	}
-
-	return FALSE;
-}
-
-/* CTCP by jonas@birme.se */
-static void
-irc_parse_notice(GaimConnection *gc, char *nick, char *ex,
-                 char *word[], char *word_eol[])
-{
-	char buf[IRC_BUF_LEN];
-
-	if (!g_ascii_strcasecmp(word[4], ":\001CLIENTINFO")) {
-		char *p = g_strrstr(word_eol[5], "\001");
-		*p = 0;
-		g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]);
-		gaim_notify_info(gc, NULL, buf, _("CTCP ClientInfo"));
-
-	} else if (!g_ascii_strcasecmp(word[4], ":\001USERINFO")) {
-		char *p = g_strrstr(word_eol[5], "\001");
-		*p = 0;
-		g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]);
-		gaim_notify_info(gc, NULL, buf, _("CTCP UserInfo"));
-
-	} else if (!g_ascii_strcasecmp(word[4], ":\001VERSION")) {
-		char *p = g_strrstr(word_eol[5], "\001");
-		*p = 0;
-		g_snprintf(buf, sizeof(buf), "CTCP Answer: %s", word_eol[5]);
-		gaim_notify_info(gc, NULL, buf, _("CTCP Version"));
-
-	} else if (!g_ascii_strcasecmp(word[4], ":\001PING")) {
-		char *p = g_strrstr(word_eol[5], "\001");
-		struct timeval ping_time;
-		struct timeval now;
-		gchar **vector;
-
-		if (p)
-			*p = 0;
-
-		vector = g_strsplit(word_eol[5], ".", 2);
-
-		if (gettimeofday(&now, NULL) == 0 && vector != NULL) {
-			if (vector[1] && now.tv_usec - atol(vector[1]) < 0) {
-				ping_time.tv_sec = now.tv_sec - atol(vector[0]) - 1;
-				ping_time.tv_usec = now.tv_usec - atol(vector[1]) + 1000000;
-
-			} else {
-				ping_time.tv_sec = now.tv_sec - atol(vector[0]);
-				if(vector[1])
-					ping_time.tv_usec = now.tv_usec - atol(vector[1]);
-			}
-
-			g_snprintf(buf, sizeof(buf),
-					   "CTCP Ping reply from %s: %lu.%.03lu seconds",
-					   nick, ping_time.tv_sec, (ping_time.tv_usec/1000));
-
-			gaim_notify_info(gc, NULL, buf, _("CTCP Ping"));
-			g_strfreev(vector);
-		}
-	} else {
-		if (*word_eol[4] == ':') word_eol[4]++;
-		if (ex)
-			irc_got_im(gc, nick, word_eol[4], 0, time(NULL));
-	}
-}
-
-static void
-irc_parse_join(GaimConnection *gc, char *nick,
-               char *word[], char *word_eol[])
+static void irc_blist_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
 {
-	char *chan = *word[3] == ':' ? word[3] + 1 : word[3];
-	static int id = 1;
-	GaimConversation *c;
-	char *hostmask, *p;
-
-	if (!gaim_utf8_strcasecmp(gaim_connection_get_display_name(gc), nick)) {
-		serv_got_joined_chat(gc, id++, chan);
-	} else {
-		c = irc_find_chat(gc, chan);
-		if (c) {
-			hostmask = g_strdup(word[1]);
-			p = strchr(hostmask, '!');
-			if (p) {
-				char *pend = strchr(p, ' ');
-				if (pend) {
-					*pend = 0;
-				}
-
-				gaim_chat_add_user(GAIM_CHAT(c), nick, p + 1);
-
-				g_free(hostmask);
-			}
-		}
-	}
-}
-	
-static void
-irc_parse_topic(GaimConnection *gc, char *nick,
-                char *word[], char *word_eol[])
-{
-	GaimConversation *c = irc_find_chat(gc, word[3]);
-	char *topic = irc_recv_convert(gc, *word_eol[4] == ':' ? word_eol[4] + 1 : word_eol[4]);
-	char buf[IRC_BUF_LEN];
-
-	if (c) {
-		gaim_chat_set_topic(GAIM_CHAT(c), nick, topic);
-		g_snprintf(buf, sizeof(buf),
-				   _("<B>%s has changed the topic to: %s</B>"), nick, topic);
-
-		gaim_conversation_write(c, NULL, buf, -1, WFLAG_SYSTEM, time(NULL));
-	}
-	g_free(topic);
-}
-
-static gboolean
-irc_parse_part(GaimConnection *gc, char *nick, char *cmd,
-               char *word[], char *word_eol[])
-{
-	char *chan = cmd + 5;
-	GaimConversation *c;
-	GaimChat *chat;
-	char *reason = word_eol[4];
-	GList *r;
-
-	if (*chan == ':')
-		chan++;
-	if (*reason == ':')
-		reason++;
-	if (!(c = irc_find_chat(gc, chan)))
-		return FALSE;
-	if (!strcmp(nick, gaim_connection_get_display_name(gc))) {
-		serv_got_chat_left(gc, gaim_chat_get_id(GAIM_CHAT(c)));
-		return FALSE;
-	}
-
-	chat = GAIM_CHAT(c);
-
-	r = gaim_chat_get_users(GAIM_CHAT(c));
-
-	while (r) {
-		char *who = r->data;
-		if (*who == '@')
-			who++;
-		if (*who == '%')
-			who++;
-		if (*who == '+')
-			who++;
-		if (!gaim_utf8_strcasecmp(who, nick)) {
-			gaim_chat_remove_user(chat, who, reason);
-			break;
-		}
-		r = r->next;
-	}
-	return TRUE;
-}
-
-static void 
-irc_callback(gpointer data, gint source, GaimInputCondition condition)
-{
-	GaimConnection *gc = data;
-	struct irc_data *idata = gc->proto_data;
-	int i = 0;
-	gchar buf[1024];
-	gboolean off;
-
-	i = read(idata->fd, buf, 1024);
-	if (i <= 0) {
-		gaim_connection_error(gc, _("Read error"));
-		return;
-	}
-
-	idata->rxqueue = g_realloc(idata->rxqueue, i + idata->rxlen + 1);
-	memcpy(idata->rxqueue + idata->rxlen, buf, i);
-	idata->rxlen += i;
-	idata->rxqueue[idata->rxlen] = 0;
-
-	while (1) {
-		char *d, *e;
-		int len;
-
-		if (!idata->rxqueue || ((e = strchr(idata->rxqueue, '\n')) == NULL))
-			return;
-
-		len = e - idata->rxqueue + 1;
-		d = g_strndup(idata->rxqueue, len);
-		g_strchomp(d);
-		gaim_debug(GAIM_DEBUG_MISC, "irc", "S: %s\n", d);
-
-		/* REMOVE ME BEFORE SUBMIT! */
-		/*fprintf(stderr, "IRC S: %s\n", d);*/
-
-		idata->rxlen -= len;
-		if (idata->rxlen) {
-			char *tmp = g_strdup(e + 1);
-			g_free(idata->rxqueue);
-			idata->rxqueue = tmp;
-		} else {
-			g_free(idata->rxqueue);
-			idata->rxqueue = NULL;
-		}
-
-		off = irc_parse(gc, d);
-
-		g_free(d);
-
-		if (off)
-			return;
-	}
-}
-
-static void 
-irc_login_callback(gpointer data, gint source, GaimInputCondition condition)
-{
-	GaimConnection *gc = data;
-	GaimAccount *account = gaim_connection_get_account(gc);
-	struct irc_data *idata;
-	char hostname[256];
-	char buf[IRC_BUF_LEN];
-	char *test;
-	const char *alias;
-	const char *charset = gaim_account_get_string(account, "charset", "UTF-8");
-	GError *err = NULL;
-
-	idata = gc->proto_data;
-
-	if (source < 0) {
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
-	idata->fd = source;
-	
-	/* Try a quick conversion to see if the specified encoding is OK */
-	test = g_convert("test", strlen("test"), charset,
-			 "UTF-8", NULL, NULL, &err);
-	if (err) {
-		gaim_debug(GAIM_DEBUG_ERROR, "irc",
-			   "Couldn't initialize %s for IRC charset conversion, using ISO-8859-1\n",
-			   charset);
-		gaim_account_set_string(account, "charset", "UTF-8");
-	}
-	
-	g_free(test);
-	
-	gethostname(hostname, sizeof(hostname) - 1);
-	hostname[sizeof(hostname) - 1] = 0;
-
-	if (!*hostname)
-		g_snprintf(hostname, sizeof(hostname), "localhost");
-
-	if (gaim_account_get_password(account) != NULL) {
-		g_snprintf(buf, sizeof(buf), "PASS %s\r\n",
-				   gaim_account_get_password(account));
-
-		if (irc_write(idata->fd, buf, strlen(buf)) < 0) {
-			gaim_connection_error(gc, _("Write error"));
-			return;
-		}
-	}
-
-	alias = gaim_account_get_alias(account);
-
-	g_snprintf(buf, sizeof(buf), "USER %s %s %s :%s\r\n",
-		   g_get_user_name(), hostname,
-		   idata->server,
-		   (alias == NULL ? "gaim" : alias));
-
-	if (irc_write(idata->fd, buf, strlen(buf)) < 0) {
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
-
-	g_snprintf(buf, sizeof(buf), "NICK %s\r\n",
-			   gaim_connection_get_display_name(gc));
-
-	if (irc_write(idata->fd, buf, strlen(buf)) < 0) {
-		gaim_connection_error(gc, _("Write error"));
-		return;
-	}
-
-	gc->inpa = gaim_input_add(idata->fd, GAIM_INPUT_READ, irc_callback, gc);
-}
-
-static void 
-irc_login(GaimAccount *account)
-{
-	const char *username = gaim_account_get_username(account);
-	char buf[IRC_BUF_LEN];
-	int rc;
-
-	GaimConnection *gc;
-	struct irc_data *idata;
-	char **parts;
-
-	gc = gaim_account_get_connection(account);
-	idata = gc->proto_data = g_new0(struct irc_data, 1);
-
-	parts = g_strsplit(username, "@", 2);
-	gaim_connection_set_display_name(gc, parts[0]);
-	idata->server = g_strdup(parts[1]);
-	g_strfreev(parts);
-
-	g_snprintf(buf, sizeof(buf), _("Signon: %s"), username);
-	gaim_connection_update_progress(gc, buf, 1, 2);
-
-	idata->chantypes = g_strdup("#&!+");
-	idata->chanmodes = g_strdup("beI,k,lnt");
-	idata->nickmodes = g_strdup("ohv");
-	idata->str = g_string_new("");
-	idata->fd = -1;
-
-	rc = gaim_proxy_connect(account, idata->server,
-			   gaim_account_get_int(account, "port", 6667),
-			   irc_login_callback, gc);
-	
-	if (rc != 0) {
-		gaim_connection_error(gc, _("Unable to create socket"));
-		return;
-	}
+	if (b->present == GAIM_BUDDY_OFFLINE)
+		*se = "offline";
 }
 
-static void 
-irc_close(GaimConnection *gc)
-{
-	struct irc_data *idata = (struct irc_data *)gc->proto_data;
-
-	gchar buf[IRC_BUF_LEN];
-
-	if (idata->str->len > 0) {
-		g_snprintf(buf, sizeof(buf), "QUIT :%s\r\n", idata->str->str);
-	} else {
-		g_snprintf(buf, sizeof(buf), 
-			   "QUIT :Download Gaim [%s]\r\n", WEBSITE);
-	}
-	irc_write(idata->fd, buf, strlen(buf));
-
-	if (idata->rxqueue)
-		g_free(idata->rxqueue);
-
-	idata->rxqueue = NULL;
-	idata->rxlen = 0;
-
-	/* Kill any existing transfers */
-	while (idata->file_transfers) {
-		GaimXfer *xfer;
-
-		xfer = (GaimXfer *)idata->file_transfers->data;
-
-		gaim_xfer_end(xfer);
-		gaim_xfer_destroy(xfer);
-
-		idata->file_transfers = idata->file_transfers->next;
-	}
-	idata->file_transfers = NULL;
-
-
-	g_free(idata->chantypes);
-	g_free(idata->chanmodes);
-	g_free(idata->nickmodes);
-
-	g_string_free(idata->str, TRUE);
-	if (idata->liststr)
-		g_string_free(idata->liststr, TRUE);
-
-	if (idata->timer)
-		g_source_remove(idata->timer);
-
-	if (gc->inpa)
-		gaim_input_remove(gc->inpa);
-
-	close(idata->fd);
-	g_free(gc->proto_data);
-}
-
-static void 
-set_mode_3(GaimConnection *gc, const char *who, int sign, int mode,
-		       int start, int end, char *word[])
+static GList *irc_away_states(GaimConnection *gc)
 {
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-	int left;
-	int i = start;
-
-	while (1) {
-		left = end - i;
-		switch (left) {
-		case 0:
-			return;
-		case 1:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c %s\r\n",
-				   who, sign, mode, word[i]);
-			i += 1;
-			break;
-		case 2:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c %s %s\r\n",
-				   who, sign, mode, mode, word[i], word[i + 1]);
-			i += 2;
-			break;
-		default:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c %s %s %s\r\n",
-				   who, sign, mode, mode, mode,
-				   word[i], word[i + 1], word[i + 2]);
-			i += 2;
-			break;
-		}
-		irc_write(id->fd, buf, strlen(buf));
-		if (left < 3)
-			return;
-	}
-}
-
-static void 
-set_mode_6(GaimConnection *gc, const char *who, int sign, int mode,
-		       int start, int end, char *word[])
-{
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-	int left;
-	int i = start;
-
-	while (1) {
-		left = end - i;
-		switch (left) {
-		case 0:
-			return;
-		case 1:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c %s\r\n",
-				   who, sign, mode, word[i]);
-			i += 1;
-			break;
-		case 2:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c %s %s\r\n",
-				   who, sign, mode, mode, word[i], word[i + 1]);
-			i += 2;
-			break;
-		case 3:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c %s %s %s\r\n",
-				   who, sign, mode, mode, mode,
-				   word[i], word[i + 1], word[i + 2]);
-			i += 3;
-			break;
-		case 4:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c %s %s %s %s\r\n",
-				   who, sign, mode, mode, mode, mode,
-				   word[i], word[i + 1], word[i + 2], word[i + 3]);
-			i += 4;
-			break;
-		case 5:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c%c %s %s %s %s %s\r\n",
-				   who, sign, mode, mode, mode, mode, mode,
-				   word[i], word[i + 1], word[i + 2],
-				   word[i + 3], word[i + 4]);
-			i += 5;
-			break;
-		default:
-			g_snprintf(buf, sizeof(buf), "MODE %s %c%c%c%c%c%c%c %s %s %s %s %s %s\r\n",
-				   who, sign, mode, mode, mode, mode, mode, mode,
-				   word[i], word[i + 1], word[i + 2],
-				   word[i + 3], word[i + 4], word[i + 5]);
-			i += 6;
-			break;
-		}
-		irc_write(id->fd, buf, strlen(buf));
-		if (left < 6)
-			return;
-	}
-}
-
-static void 
-set_mode(GaimConnection *gc, const char *who, int sign, int mode, char *word[])
-{
-	struct irc_data *id = gc->proto_data;
-	int i = 2;
-
-	while (1) {
-		if (!*word[i]) {
-			if (i == 2)
-				return;
-			if (id->six_modes)
-				set_mode_6(gc, who, sign, mode, 2, i, word);
-			else
-				set_mode_3(gc, who, sign, mode, 2, i, word);
-			return;
-		}
-		i++;
-	}
-}
-
-static void 
-set_chan_mode(GaimConnection *gc, const char *chan, const char *mode_str)
-{
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-
-	if ((mode_str[0] == '-') || (mode_str[0] == '+')) {
-		g_snprintf(buf, sizeof(buf), "MODE %s %s\r\n", chan, mode_str);
-		irc_write(id->fd, buf, strlen(buf));
-	}
+	return g_list_append(NULL, (gpointer)GAIM_AWAY_CUSTOM);
 }
 
-static int 
-handle_command(GaimConnection *gc, const char *who, const char *in_what)
+static GList *irc_buddy_menu(GaimConnection *gc, const char *who)
 {
-	char buf[IRC_BUF_LEN];
-	char pdibuf[IRC_BUF_LEN];
-	char *word[PDIWORDS], *word_eol[PDIWORDS];
-	char *tmp = g_strdup(in_what);
-	GString *str = encode_html(tmp);
-	char *intl;
-	int len;
-	struct dcc_chat *dccchat = find_dcc_chat(gc, who);
-	struct irc_data *id = gc->proto_data;
-	char *what = str->str;
-
-	g_free(tmp);
-
-	if (*what != '/') {
-		if (dccchat) {
-			intl = irc_send_convert(gc, what, sizeof(buf), &len);
-			g_snprintf(buf, sizeof(buf), "%s\r\n", intl);
-			g_free(intl);
-			irc_write(dccchat->fd, buf, strlen(buf));
-			g_string_free(str, TRUE);
-			return 1;
-		}
-		irc_send_privmsg (gc, who, what, TRUE);
-		g_string_free(str, TRUE);
-		return 1;
-	}
-	
-	process_data_init(pdibuf, what + 1, word, word_eol, TRUE);
-	g_string_free(str, FALSE);
-	if (!g_ascii_strcasecmp(pdibuf, "ME")) {
-		if (dccchat) {
-			intl = irc_send_convert(gc, word_eol[2], sizeof(buf), &len);
-			g_snprintf(buf, sizeof(buf), "\001ACTION %s\001\r\n", intl);
-			g_free(intl);
-			irc_write(dccchat->fd, buf, strlen(buf));
-			g_free(what);
-			return 1;
-		}
-		g_snprintf(buf, sizeof(buf), "\001ACTION %s\001", word_eol[2]);
-		irc_send_privmsg (gc, who, buf, FALSE);
-		g_free(what);
-		return 1;
-	} else if (!g_ascii_strcasecmp(pdibuf, "INVITE")) {
-		char buf[IRC_BUF_LEN];
-		g_snprintf(buf, sizeof(buf), "INVITE %s\r\n", word_eol[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "TOPIC")) {
-		if (!*word_eol[2]) {
-			GaimConversation *c;
-			GaimChat *chat;
-
-			c = irc_find_chat(gc, who);
-			chat = GAIM_CHAT(c);
-
-			g_snprintf(buf, sizeof(buf), _("Topic for %s is %s"),
-					   who, (gaim_chat_get_topic(chat)
-							 ? gaim_chat_get_topic(chat)
-							 : "(no topic set)"));
+	struct irc_conn *irc = gc->proto_data;
+	struct proto_buddy_menu *pbm;
 
-			gaim_conversation_write(c, NULL, buf, -1,
-									WFLAG_SYSTEM | WFLAG_NOLOG, time(NULL));
-		} else {
-			/* This could be too long */
-			intl = irc_send_convert(gc, word_eol[2], sizeof(buf), &len);
-			g_snprintf(buf, sizeof(buf), "TOPIC %s :%s\r\n", who, intl);
-			g_free(intl);
-			irc_write(id->fd, buf, strlen(buf));
-		}
-	} else if (!g_ascii_strcasecmp(pdibuf, "NICK")) {
-		if (!*word_eol[2]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		g_snprintf(buf, sizeof(buf), "NICK %s\r\n", word_eol[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "OP")) {
-		set_mode(gc, who, '+', 'o', word);
-	} else if (!g_ascii_strcasecmp(pdibuf, "DEOP")) {
-		set_mode(gc, who, '-', 'o', word);
-	} else if (!g_ascii_strcasecmp(pdibuf, "VOICE")) {
-		set_mode(gc, who, '+', 'v', word);
-	} else if (!g_ascii_strcasecmp(pdibuf, "DEVOICE")) {
-		set_mode(gc, who, '-', 'v', word);
-	} else if (!g_ascii_strcasecmp(pdibuf, "MODE")) {
-		set_chan_mode(gc, who, word_eol[2]);
-	} else if (!g_ascii_strcasecmp(pdibuf, "QUOTE")) {
-		if (!*word_eol[2]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		g_snprintf(buf, sizeof(buf), "%s\r\n", word_eol[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "SAY")) {
-		if (!*word_eol[2]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		irc_send_privmsg (gc, who, word_eol[2], TRUE);
-		return 1;
-	} else if (!g_ascii_strcasecmp(pdibuf, "MSG")) {
-		if (!*word[2]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		if (!*word_eol[3]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		irc_send_privmsg (gc, word[2], word_eol[3], TRUE);
-	} else if (!g_ascii_strcasecmp(pdibuf, "KICK")) {
-		if (!*word[2]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		if (*word_eol[3]) {
-			intl = irc_send_convert(gc, word_eol[3], sizeof(buf), &len);
-			g_snprintf(buf, sizeof(buf), "KICK %s %s :%s\r\n", who, word[2], intl);
-			g_free(intl);
-		} else
-			g_snprintf(buf, sizeof(buf), "KICK %s %s\r\n", who, word[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "JOIN") || !g_ascii_strcasecmp(pdibuf, "J")) {
-		if (!*word[2]) {
-			g_free(what);
-			return -EINVAL;
-		}
-		if (*word[3])
-			g_snprintf(buf, sizeof(buf), "JOIN %s %s\r\n", word[2], word[3]);
-		else
-			g_snprintf(buf, sizeof(buf), "JOIN %s\r\n", word[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "PART")) {
-		const char *chan = *word[2] ? word[2] : who;
-		char *reason = word_eol[3];
-		GaimConversation *c;
-		if (!is_channel(gc, chan)) {
-			g_free(what);
-			return -EINVAL;
-		}
-		c = irc_find_chat(gc, chan);
-		if (*reason) {
-			intl = irc_send_convert(gc, reason, sizeof(buf), &len);
-			g_snprintf(buf, sizeof(buf), "PART %s :%s\r\n", chan, intl);
-			g_free(intl);
-		} else
-			g_snprintf(buf, sizeof(buf), "PART %s\r\n", chan);
-		irc_write(id->fd, buf, strlen(buf));
-		if (c) {
-			gc->buddy_chats = g_slist_remove(gc->buddy_chats, c);
-			gaim_conversation_set_account(c, NULL);
-			g_snprintf(buf, sizeof(buf), _("You have left %s"), chan);
-			gaim_notify_info(gc, NULL, buf, _("IRC Part"));
-		}
-	} else if (!g_ascii_strcasecmp(pdibuf, "WHOIS")) {
-		g_snprintf(buf, sizeof(buf), "WHOIS %s\r\n", word_eol[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "WHOWAS")) {
-		g_snprintf(buf, sizeof(buf), "WHOWAS %s\r\n", word_eol[2]);
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "LIST")) {
-		g_snprintf(buf, sizeof(buf), "LIST\r\n");
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "QUIT")) {
-		char *reason = word_eol[2];
-		id->str = g_string_insert(id->str, 0, reason);
-		gaim_core_quit();
-	} else if (!g_ascii_strcasecmp(pdibuf, "VERSION")) {
-		g_snprintf(buf, sizeof(buf), "VERSION\r\n");
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "W")) {
-		g_snprintf(buf, sizeof(buf), "WHO *\r\n");
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "REHASH")) {
-		g_snprintf(buf, sizeof(buf), "REHASH\r\n");
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "RESTART")) {
-		g_snprintf(buf, sizeof(buf), "RESTART\r\n");
-		irc_write(id->fd, buf, strlen(buf));
-	} else if (!g_ascii_strcasecmp(pdibuf, "CTCP")) {
-		if (!g_ascii_strcasecmp(word[2], "CLIENTINFO")) {
-			if (word[3])
-				irc_ctcp_clientinfo(gc, word[3]);
-		} else if (!g_ascii_strcasecmp(word[2], "USERINFO")) {
-			if (word[3])
-				irc_ctcp_userinfo(gc, word[3]);
-		} else if (!g_ascii_strcasecmp(word[2], "VERSION")) {
-			if (word[3])
-				irc_ctcp_version(gc, word[3]);
-
-		} else if (!g_ascii_strcasecmp(word[2], "PING")) {
-			if (word[3])
-				irc_ctcp_ping(gc, word[3]);
-		}
-	} else if (!g_ascii_strcasecmp(pdibuf, "DCC")) {
-		GaimConversation *c = NULL;
-		if (!g_ascii_strcasecmp(word[2], "CHAT")) {
-			if (word[3])
-				irc_start_chat(gc, word[3]);
-			
-			if (is_channel(gc, who)) {
-				c = irc_find_chat(gc, who);
-			} else {
-				c = gaim_find_conversation(who);
-			}
-			if (c) {
-				gaim_conversation_write(c, NULL,
-										_("<I>Requesting DCC CHAT</I>"),
-										-1, WFLAG_SYSTEM, time(NULL));
-			}
-		}
-	} else if (!g_ascii_strcasecmp(pdibuf, "HELP")) {
-		GaimConversation *c = NULL;
-		if (is_channel(gc, who)) {
-			c = irc_find_chat(gc, who);
-		} else {
-			c = gaim_find_conversation(who);
-		}
-		if (!c) {
-			g_free(what);
-			return -EINVAL;
-		}
-		if (!g_ascii_strcasecmp(word[2], "OPER")) {
-			gaim_conversation_write(c, NULL,
-				_("<B>Operator commands:<BR>"
-				  "REHASH RESTART</B>"),
-				-1, WFLAG_NOLOG, time(NULL));
-		} else if (!g_ascii_strcasecmp(word[2], "CTCP")) {
-			gaim_conversation_write(c, NULL,
-			_("<B>CTCP commands:<BR>"
-			  "CLIENTINFO <nick><BR>"
-			  "USERINFO <nick><BR>"
-			  "VERSION <nick><BR>"
-			  "PING <nick></B><BR>"),
-			-1, WFLAG_NOLOG, time(NULL));
-		} else if (!g_ascii_strcasecmp(word[2], "DCC")) {
-			gaim_conversation_write(c, NULL,
-				_("<B>DCC commands:<BR>"
-				  "CHAT <nick></B>"),
-				-1, WFLAG_NOLOG, time(NULL));
-		} else {
-			gaim_conversation_write(c, NULL,
-				_("<B>Currently supported commands:<BR>"
-				  "WHOIS INVITE NICK LIST<BR>"
-				  "JOIN PART TOPIC KICK<BR>"
-				  "OP DEOP VOICE DEVOICE<BR>"
-				  "ME MSG QUOTE SAY QUIT<BR>"
-				  "MODE VERSION W WHOWAS<BR>"
-				  "Type /HELP OPER for operator commands<BR>"
-				  "Type /HELP CTCP for CTCP commands<BR>"
-				  "Type /HELP DCC for DCC commands"),
-				-1, WFLAG_NOLOG, time(NULL));
-		}
-	} else {
-		GaimConversation *c = NULL;
-		if (is_channel(gc, who)) {
-			c = irc_find_chat(gc, who);
-		} else {
-			c = gaim_find_conversation(who);
-		}
-		if (!c) {
-			g_free(what);
-			return -EINVAL;
-		}
-
-		gaim_conversation_write(c, NULL, _("<B>Unknown command</B>"),
-								-1, WFLAG_NOLOG, time(NULL));
-	}
-	g_free(what);
-	return 0;
+	return NULL;
 }
 
-static int 
-send_msg(GaimConnection *gc, const char *who, const char *what)
-{
-	char *cr = strchr(what, '\n');
-	if (cr) {
-		int ret = 0;
-		while (TRUE) {
-			if (cr)
-				*cr = 0;
-			ret = handle_command(gc, who, what);
-			if (!cr)
-				break;
-			what = cr + 1;
-			if (!*what)
-				break;
-			*cr = '\n';
-			cr = strchr(what, '\n');
-		}
-		return ret;
-	} else
-		return handle_command(gc, who, what);
-}
-
-static void 
-irc_chat_invite(GaimConnection *gc, int idn, const char *message, const char *name) {
-	char buf[IRC_BUF_LEN]; 
-	struct irc_data *id = gc->proto_data;
-	GaimConversation *c = gaim_find_chat(gc, idn);
-	g_snprintf(buf, sizeof(buf), "INVITE %s %s\r\n", name, c->name);
-	irc_write(id->fd, buf, strlen(buf));
-}
-
-static int 
-irc_send_im(GaimConnection *gc, const char *who, const char *what, int len, int flags)
-{
-	if (*who == '@' || *who == '%' || *who == '+')
-		return send_msg(gc, who + 1, what);
-	return send_msg(gc, who, what);
-}
-
-/* IRC doesn't have a buddy list, but we can still figure out who's online with ISON */
-static void 
-irc_add_buddy(GaimConnection *gc, const char *who) {}
-static void 
-irc_remove_buddy(GaimConnection *gc, const char *who, const char *group) {}
-
-static GList *
-irc_chat_info(GaimConnection *gc)
+static GList *irc_chat_join_info(GaimConnection *gc)
 {
 	GList *m = NULL;
 	struct proto_chat_entry *pce;
@@ -2478,448 +127,294 @@
 	pce = g_new0(struct proto_chat_entry, 1);
 	pce->label = _("Password:");
 	pce->identifier = "password";
-	pce->secret = TRUE;
 	m = g_list_append(m, pce);
 
 	return m;
 }
 
-static void
-irc_join_chat(GaimConnection *gc, GHashTable *data)
+static void irc_login(GaimAccount *account)
 {
-	struct irc_data *id = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-	char *name, *pass;
+	GaimConnection *gc;
+	struct irc_conn *irc;
+	char *buf, **userparts;
+	const char *username = gaim_account_get_username(account);
+	int err;
+
+	gc = gaim_account_get_connection(account);
+
+	gc->flags |= OPT_CONN_AUTO_RESP;
+
+	gc->proto_data = irc = g_new0(struct irc_conn, 1);
+	irc->account = account;
+
+	userparts = g_strsplit(username, "@", 2);
+	gaim_connection_set_display_name(gc, userparts[0]);
+	irc->server = g_strdup(userparts[1]);
+	g_strfreev(userparts);
 
-	if (!data)
+	irc->buddies = g_hash_table_new_full((GHashFunc)irc_nick_hash, (GEqualFunc)irc_nick_equal, 
+					     NULL, (GDestroyNotify)irc_buddy_free);
+	irc->cmds = g_hash_table_new(g_str_hash, g_str_equal);
+	irc_cmd_table_build(irc);
+	irc->msgs = g_hash_table_new(g_str_hash, g_str_equal);
+	irc_msg_table_build(irc);
+
+	buf = g_strdup_printf(_("Signon: %s"), username);
+	gaim_connection_update_progress(gc, buf, 1, 2);
+	g_free(buf);
+
+	err = gaim_proxy_connect(account, irc->server, 
+				 gaim_account_get_int(account, "port", IRC_DEFAULT_PORT),
+				 irc_login_cb, gc);
+
+	if (err || !account->gc) {
+		gaim_connection_error(gc, _("Couldn't create socket"));
 		return;
-
-	name = g_hash_table_lookup(data, "channel");
-	pass = g_hash_table_lookup(data, "password");
-	if (pass) {
-		g_snprintf(buf, sizeof(buf), "JOIN %s %s\r\n", name, pass);
-	} else
-		g_snprintf(buf, sizeof(buf), "JOIN %s\r\n", name);
-	irc_write(id->fd, buf, strlen(buf));
+	}
 }
 
-static void 
-irc_chat_leave(GaimConnection *gc, int id)
+static void irc_login_cb(gpointer data, gint source, GaimInputCondition cond)
 {
-	struct irc_data *idata = gc->proto_data;
-	GaimConversation *c = gaim_find_chat(gc, id);
-	char buf[IRC_BUF_LEN];
+	GaimConnection *gc = data;
+	struct irc_conn *irc = gc->proto_data;
+	char hostname[256];
+	char *buf;
+	GList *connections = gaim_connections_get_all();
+
+	if (source < 0)
+		return;
+
+	if (!g_list_find(connections, gc)) {
+		close(source);
+		return;
+	}
+
+	irc->fd = source;
 
-	if (!c) return;
+	if (gc->account->password && *gc->account->password) {
+		buf = irc_format(irc, "vv", "PASS", gc->account->password);
+		if (irc_send(irc, buf) < 0) {
+			gaim_connection_error(gc, "Error sending password");
+			return;
+		}
+		g_free(buf);
+	}
 
-	g_snprintf(buf, sizeof(buf), "PART %s\r\n", c->name);
-	irc_write(idata->fd, buf, strlen(buf));
+	gethostname(hostname, sizeof(hostname));
+	hostname[sizeof(hostname) - 1] = '\0';
+	buf = irc_format(irc, "vvvv:", "USER", g_get_user_name(), hostname, irc->server,
+			      gc->account->alias && *gc->account->alias ? gc->account->alias : IRC_DEFAULT_ALIAS);
+	if (irc_send(irc, buf) < 0) {
+		gaim_connection_error(gc, "Error registering with server");
+		return;
+	}
+	g_free(buf);
+	buf = irc_format(irc, "vn", "NICK", gaim_connection_get_display_name(gc));
+	if (irc_send(irc, buf) < 0) {
+		gaim_connection_error(gc, "Error sending nickname");
+		return;
+	}
+	g_free(buf);
+
+	gc->inpa = gaim_input_add(irc->fd, GAIM_INPUT_READ, irc_input_cb, gc);
 }
 
-static int 
-irc_chat_send(GaimConnection *gc, int id, const char *what)
+static void irc_close(GaimConnection *gc)
 {
-	GaimConversation *c = gaim_find_chat(gc, id);
-	if (!c)
-		return -EINVAL;
-	if (send_msg(gc, c->name, what) > 0)
-		serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(c)),
-			gaim_connection_get_display_name(gc), 0, what, time(NULL));
-	return 0;
+	struct irc_conn *irc = gc->proto_data;
+
+	irc_cmd_quit(irc, "quit", NULL, NULL);
+
+	if (gc->inpa)
+		g_source_remove(gc->inpa);
+
+	g_free(irc->inbuf);
+	close(irc->fd);
+	if (irc->timer)
+		g_source_remove(irc->timer);
+	g_hash_table_destroy(irc->cmds);
+	g_hash_table_destroy(irc->msgs);
+	if (irc->motd)
+		g_string_free(irc->motd, TRUE);
+	g_free(irc->server);
+	g_free(irc);
 }
 
-static GList *
-irc_away_states(GaimConnection *gc)
+static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, int flags)
 {
-	return g_list_append(NULL, GAIM_AWAY_CUSTOM);
+	struct irc_conn *irc = gc->proto_data;
+	const char *args[2];
+
+	if (*who == '@' || *who == '%' || *who == '+')
+		args[0] = who + 1;
+	else
+		args[0] = who;
+	args[1] = what;
+
+	if (*what == '/') {
+		return irc_parse_cmd(irc, who, what + 1);
+	}
+
+	irc_cmd_privmsg(irc, "msg", NULL, args);
+	return 1;
 }
 
-static void 
-irc_set_away(GaimConnection *gc, const char *state, const char *msg)
+static void irc_get_info(GaimConnection *gc, const char *who)
 {
-	struct irc_data *idata = gc->proto_data;
-	char buf[IRC_BUF_LEN];
+	struct irc_conn *irc = gc->proto_data;
+	const char *args[1];
+	args[0] = who;
+	irc_cmd_whois(irc, "whois", NULL, args);
+}
+
+static void irc_set_away(GaimConnection *gc, const char *state, const char *msg)
+{
+	struct irc_conn *irc = gc->proto_data;
+	const char *args[1];
 
 	if (gc->away) {
 		g_free(gc->away);
 		gc->away = NULL;
 	}
 
-	if (msg) {
-		g_snprintf(buf, sizeof(buf), "AWAY :%s\r\n", msg);
+	if (msg)
 		gc->away = g_strdup(msg);
-	} else
-		g_snprintf(buf, sizeof(buf), "AWAY\r\n");
-
-	irc_write(idata->fd, buf, strlen(buf));
-}
 
-static const char *
-irc_list_icon(GaimAccount *a, struct buddy *b)
-{
-	return "irc";
-}
-
-static void irc_list_emblems(struct buddy *b, char **se, char **sw, char **nw, char **ne)
-{
-	if (b->present == GAIM_BUDDY_OFFLINE)
-		*se = "offline";
+	args[0] = msg;
+	irc_cmd_away(irc, "away", NULL, args);
 }
 
-static void 
-dcc_chat_connected(gpointer data, gint source, GdkInputCondition condition)
+static void irc_add_buddy(GaimConnection *gc, const char *who)
 {
-	struct dcc_chat *chat = data;
-	GaimConversation *convo;
-	char buf[128];
-	struct sockaddr_in addr;
-	int addrlen = sizeof (addr);
-	addr.sin_family = AF_INET;
-	addr.sin_port = htons (chat->port);
-	addr.sin_addr.s_addr = INADDR_ANY;
-	chat->fd = accept (chat->fd, (struct sockaddr *) (&addr), &addrlen);
-	if (!chat->fd) {
-		dcc_chat_cancel (chat);
-		convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account,
-									  chat->nick);
-		g_snprintf (buf, sizeof buf, _("DCC Chat with %s closed"),
-			    chat->nick);
-		gaim_conversation_write(convo, NULL, buf, -1,
-								WFLAG_SYSTEM, time(NULL));
-		return;
-	}
-	chat->inpa =
-		gaim_input_add (chat->fd, GAIM_INPUT_READ, dcc_chat_in, chat);
-	convo = gaim_conversation_new(GAIM_CONV_IM, chat->gc->account, chat->nick);
-	g_snprintf (buf, sizeof buf, _("DCC Chat with %s established"),
-				chat->nick);
-	gaim_conversation_write(convo, NULL, buf, -1, WFLAG_SYSTEM, time(NULL));
-	gaim_debug(GAIM_DEBUG_INFO, "irc",
-			   "Chat with %s established\n", chat->nick);
-	dcc_chat_list = g_slist_append (dcc_chat_list, chat);
+	struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
+	struct irc_buddy *ib = g_new0(struct irc_buddy, 1);
+	ib->name = g_strdup(who);
+	g_hash_table_insert(irc->buddies, ib->name, ib);
 }
-#if 0
-static void 
-irc_ask_send_file(GaimConnection *gc, char *destsn) {
-	struct irc_data *id = (struct irc_data *)gc->proto_data;
-	struct irc_file_transfer *ift = g_new0(struct irc_file_transfer, 1);
-	char *localip = (char *)malloc(12);
 
-	if (getlocalip(localip) == -1) {
-		free(localip);
-		return;
-	} 
-
-	ift->type = IFT_SENDFILE_OUT;
-	ift->sn = g_strdup(destsn);
-	ift->gc = gc;
-	snprintf(ift->ip, sizeof(ift->ip), "%s", localip);
-	id->file_transfers = g_slist_append(id->file_transfers, ift);
-
-	ift->xfer = transfer_out_add(gc, ift->sn);
+static void irc_remove_buddy(GaimConnection *gc, const char *who, const char *group)
+{
+	struct irc_conn *irc = (struct irc_conn *)gc->proto_data;
+	g_hash_table_remove(irc->buddies, who);
 }
 
-static struct 
-irc_file_transfer *find_ift_by_xfer(GaimConnection *gc,
-									struct file_transfer *xfer) {
-		
-	GSList *g = ((struct irc_data *)gc->proto_data)->file_transfers;
-	struct irc_file_transfer *f = NULL;
 
-	while (g) {
-		f = (struct irc_file_transfer *)g->data;
-		if (f->xfer == xfer)
-			break;
-		g = g->next;
-		f = NULL;
+static void irc_input_cb(gpointer data, gint source, GaimInputCondition cond)
+{
+	GaimConnection *gc = data;
+	struct irc_conn *irc = gc->proto_data;
+	char *cur, *end;
+	int len;
+
+	if (irc->inbuflen < irc->inbufused + IRC_INITIAL_BUFSIZE) {
+		irc->inbuflen += IRC_INITIAL_BUFSIZE;
+		irc->inbuf = g_realloc(irc->inbuf, irc->inbuflen);
 	}
 
-	return f;
-}
+	if ((len = read(irc->fd, irc->inbuf + irc->inbufused, IRC_INITIAL_BUFSIZE - 1)) < 0) {
+		gaim_connection_error(gc, "Read error");
+		return;
+	}
+	irc->inbufused += len;
+	irc->inbuf[irc->inbufused] = '\0';
 
-static void 
-irc_file_transfer_data_chunk(GaimConnection *gc, struct file_transfer *xfer, const char *data, int len) {
-	struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer);
-	guint32 pos;
-	
-	ift->cur += len;
-	pos = htonl(ift->cur);
-	write(ift->fd, (char *)&pos, 4);
-
-	// FIXME: You should check to verify that they received the data when
-	// you are sending a file ...
+	for (cur = irc->inbuf; cur < irc->inbuf + irc->inbufused && (end = strstr(cur, "\r\n")); cur = end + 2) {
+		*end = '\0';
+		irc_parse_msg(irc, cur);
+	}
+	if (cur != irc->inbuf + irc->inbufused) { /* leftover */
+		irc->inbufused -= (cur - irc->inbuf);
+		memmove(irc->inbuf, cur, irc->inbufused);
+	} else {
+		irc->inbufused = 0;
+	}
 }
 
-static void 
-irc_file_transfer_cancel (GaimConnection *gc, struct file_transfer *xfer) {
-	struct irc_data *id = (struct irc_data *)gc->proto_data;
-	struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer);
-
-	printf("Our shit got canceled, yo!\n");
-
-	/* Remove the FT from our list of transfers */
-	id->file_transfers = g_slist_remove(id->file_transfers, ift);
-
-	gaim_input_remove(ift->watcher);
-
-	/* Close our FT because we're done */
-	close(ift->fd);
-
-	g_free(ift->sn);
-	g_free(ift->name);
-
-	g_free(ift);
-}
-
-static void 
-irc_file_transfer_done(GaimConnection *gc, struct file_transfer *xfer) {
-	struct irc_data *id = (struct irc_data *)gc->proto_data;
-	struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer);
-
-
-	printf("Our shit be done, yo.\n");
-	
-	/* Remove the FT from our list of transfers */
-	id->file_transfers = g_slist_remove(id->file_transfers, ift);
-
-	gaim_input_remove(ift->watcher);
+static void irc_chat_join (GaimConnection *gc, GHashTable *data)
+{
+	struct irc_conn *irc = gc->proto_data;
+	const char *args[2];
 
-	/* Close our FT because we're done */
-	close(ift->fd);
-
-	g_free(ift->sn);
-	g_free(ift->name);
-
-	g_free(ift);
-}
-
-static void 
-irc_file_transfer_out (GaimConnection *gc, struct file_transfer *xfer, const char *name, int totfiles, int totsize) {
-	struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer);
-	struct sockaddr_in addr;
-	char buf[IRC_BUF_LEN];
-	int len;
-	
-	
-	ift->fd = socket (AF_INET, SOCK_STREAM, 0);
-	addr.sin_family = AF_INET;
-	addr.sin_port = 0;
-	addr.sin_addr.s_addr = INADDR_ANY;
-	bind (ift->fd, (struct sockaddr *) &addr, sizeof(addr));
-	listen(ift->fd, 1);
-
-	len = sizeof(addr);
-	getsockname (ift->fd, (struct sockaddr *) &addr, &len);
-
-	ift->port = ntohs(addr.sin_port);
-
-	ift->watcher = gaim_input_add (ift->fd, GAIM_INPUT_READ, dcc_send_callback, ift);
-	printf("watcher is %d\n", ift->watcher);
-
-	snprintf(buf, sizeof(buf), "\001DCC SEND %s %s %d %d\001\n", name, ift->ip, ift->port, totsize);
-	printf("Trying: %s\n", buf);
-	irc_send_im (gc, ift->sn, buf, -1, 0);
+	args[0] = g_hash_table_lookup(data, "channel");
+	args[1] = g_hash_table_lookup(data, "password");
+	irc_cmd_join(irc, "join", NULL, args);
 }
 
-static void 
-irc_file_transfer_in(GaimConnection *gc,
-				 struct file_transfer *xfer, int offset) {
-
-	struct irc_file_transfer *ift = find_ift_by_xfer(gc, xfer);
-
-	ift->xfer = xfer;
-	gaim_proxy_connect(gc->account, ift->ip, ift->port, dcc_recv_callback, ift);
-}
-#endif
-
-static void 
-irc_ctcp_clientinfo(GaimConnection *gc, const char *who)
+static void irc_chat_invite(GaimConnection *gc, int id, const char *message, const char *name) 
 {
-	char buf[IRC_BUF_LEN];
-
-	snprintf (buf, sizeof buf, "\001CLIENTINFO\001");
-	irc_send_privmsg(gc, who, buf, FALSE);
-}
+	struct irc_conn *irc = gc->proto_data;
+	GaimConversation *convo = gaim_find_chat(gc, id);
+	const char *args[2];
 
-static void 
-irc_ctcp_userinfo(GaimConnection *gc, const char *who)
-{
-	char buf[IRC_BUF_LEN];
-
-	snprintf (buf, sizeof buf, "\001USERINFO\001");
-	irc_send_privmsg(gc, who, buf, FALSE);
+	if (!convo) {
+		gaim_debug(GAIM_DEBUG_ERROR, "irc", "Got chat invite request for bogus chat\n");
+		return;
+	}
+	args[0] = name;
+	args[1] = gaim_conversation_get_name(convo);
+	irc_cmd_invite(irc, "invite", gaim_conversation_get_name(convo), args);
 }
 
-static void 
-irc_ctcp_version(GaimConnection *gc, const char *who)
+
+static void irc_chat_leave (GaimConnection *gc, int id)
 {
-	char buf[IRC_BUF_LEN];
-
-	snprintf (buf, sizeof buf, "\001VERSION\001");
-	irc_send_privmsg(gc, who, buf, FALSE);
-}
-
-static void 
-irc_ctcp_ping(GaimConnection *gc, const char *who)
-{
-	char buf[IRC_BUF_LEN];
-	struct timeval now;
+	struct irc_conn *irc = gc->proto_data;
+	GaimConversation *convo = gaim_find_chat(gc, id);
+	const char *args[2];
 
-	gettimeofday(&now, NULL);
-	g_snprintf (buf, sizeof(buf), "\001PING %lu.%.03lu\001", now.tv_sec,
-			now.tv_usec/1000);
-	irc_send_privmsg(gc, who, buf, FALSE);
-}
+	if (!convo)
+		return;
 
-static void 
-irc_send_notice(GaimConnection *gc, char *who, char *what)
-{
-	char buf[IRC_BUF_LEN], *intl;
-	struct irc_data *id = gc->proto_data;
-	int len;
-	
-	intl = irc_send_convert(gc, what, 501, &len);
-	g_snprintf(buf, sizeof(buf), "NOTICE %s :%s\r\n", who, intl);
-	g_free(intl);
-	irc_write(id->fd, buf, strlen(buf));
+	args[0] = gaim_conversation_get_name(convo);
+	args[1] = NULL;
+	irc_cmd_part(irc, "part", gaim_conversation_get_name(convo), args);
+	serv_got_chat_left(gc, id);
 }
 
-/* Don't call this guy with fragment = 1 for anything but straight
- * up privmsgs.  (no CTCP/whatever)  It's still dangerous for CTCPs
- * (it might not include the trailing \001), but I think this behavior
- * is generally better than not fragmenting at all on lots of our
- * packets. */
-/* From RFC2812:
- * IRC messages are always lines of characters terminated with a CR-LF
- * (Carriage Return - Line Feed) pair, and these messages SHALL NOT
- * exceed 512 characters in length, counting all characters including
- * the trailing CR-LF. Thus, there are 510 characters maximum allowed
- * for the command and its parameters. */
-/* So apparently that includes all the inter-server crap, which is up
- * to NINETY-THREE chars on dancer, which seems to be a pretty liberal
- * ircd.  My rough calculation for now is ":<nick>!~<user>@<host> ",
- * where <host> is a max of an (uncalculated) 63 chars.  Thanks to
- * trelane and #freenode for giving a hand here. */
-static void 
-irc_send_privmsg(GaimConnection *gc, const char *who, const char *what, gboolean fragment)
+static int irc_chat_send(GaimConnection *gc, int id, const char *what)
 {
-	char buf[IRC_BUF_LEN], *intl;
-	struct irc_data *id = gc->proto_data;
-	/* 512 - 12 (for PRIVMSG" "" :""\r\n") - namelen - nicklen - 68 */
-	int nicklen = (gc->account->alias && strlen(gc->account->alias)) ? strlen(gc->account->alias) : 4;
-	int max = 444 - strlen(who) - strlen(g_get_user_name()) - nicklen;
-	
-	int len;
-	
-	do {
-		/* the \001 on CTCPs may cause a problem here for some 
-		 * charsets, but probably not ones people use for IRC. */
-		intl = irc_send_convert(gc, what, max, &len);
-		g_snprintf(buf, sizeof(buf), "PRIVMSG %s :%s\r\n", who, intl);
-		g_free(intl);
-		irc_write(id->fd, buf, strlen(buf));
-		what += len;
-	} while (fragment && strlen(what));
+	struct irc_conn *irc = gc->proto_data;
+	GaimConversation *convo = gaim_find_chat(gc, id);
+	const char *args[2];
+
+	if (!convo) {
+		gaim_debug(GAIM_DEBUG_ERROR, "irc", "chat send on nonexistent chat\n");
+		return -EINVAL;
+	}
+
+	if (*what == '/') {
+		return irc_parse_cmd(irc, convo->name, what + 1);
+	}
+
+	args[0] = convo->name;
+	args[1] = what;
+
+	irc_cmd_privmsg(irc, "msg", NULL, args);
+	serv_got_chat_in(gc, id, gaim_connection_get_display_name(gc), 0, what, time(NULL));
+	return 0;
 }
 
-static void 
-irc_start_chat(GaimConnection *gc, const char *who) {
-	struct dcc_chat *chat;
-	int len;
-	struct sockaddr_in addr;
-	char buf[IRC_BUF_LEN];
-	const char *ip;
-	
-	/* Create a socket */
-	chat = g_new0 (struct dcc_chat, 1);
-	chat->fd = socket (AF_INET, SOCK_STREAM, 0);
-	chat->gc = gc;
-	g_snprintf (chat->nick, sizeof (chat->nick), "%s", who);
-	if (chat->fd < 0)  {
-		dcc_chat_cancel (chat);
-		return;
-	}
-	addr.sin_family = AF_INET;
-	addr.sin_port = 0;
-	addr.sin_addr.s_addr = INADDR_ANY;
-	bind (chat->fd, (struct sockaddr *) &addr, sizeof (addr));
-	listen (chat->fd, 1);
-	len = sizeof (addr);
-	getsockname (chat->fd, (struct sockaddr *) &addr, &len);
-	chat->port = ntohs (addr.sin_port);
+static guint irc_nick_hash(const char *nick)
+{
+	char *lc;
+	guint bucket;
 
-	ip = gaim_xfers_get_ip_for_account(gaim_connection_get_account(gc));
-	strncpy(chat->ip_address, ip, INET6_ADDRSTRLEN);
+	lc = g_utf8_strdown(nick, -1);
+	bucket = g_str_hash(lc);
+	g_free(lc);
 
-	chat->inpa =
-		gaim_input_add (chat->fd, GAIM_INPUT_READ, dcc_chat_connected,
-				chat);
-	g_snprintf (buf, sizeof buf, "\001DCC CHAT chat %s %d\001\n",
-		    chat->ip_address, chat->port);
-	irc_send_im (gc, who, buf, -1, 0);
+	return bucket;
 }
 
-static void 
-irc_get_info(GaimConnection *gc, const char *who)
+static gboolean irc_nick_equal(const char *nick1, const char *nick2)
 {
-	struct irc_data *idata = gc->proto_data;
-	char buf[IRC_BUF_LEN];
-
-	if (*who == '@')
-		who++;
-	if (*who == '%')
-		who++;
-	if (*who == '+')
-		who++;
-
-	g_snprintf(buf, sizeof(buf), "WHOIS %s\r\n", who);
-	irc_write(idata->fd, buf, strlen(buf));
+	return (gaim_utf8_strcasecmp(nick1, nick2) == 0);
 }
 
-static GList *
-irc_buddy_menu(GaimConnection *gc, const char *who)
+static void irc_buddy_free(struct irc_buddy *ib)
 {
-	GList *m = NULL;
-	struct proto_buddy_menu *pbm;
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("DCC Chat");
-	pbm->callback = irc_start_chat;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-	/*
-	  pbm = g_new0(struct proto_buddy_menu, 1);
-	  pbm->label = _("DCC Send");
-	  pbm->callback = irc_ask_send_file;
-	  pbm->gc = gc;
-	  m = g_list_append(m, pbm);
-	*/
-	
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("CTCP ClientInfo");
-	pbm->callback = irc_ctcp_clientinfo;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-	
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("CTCP UserInfo");
-	pbm->callback = irc_ctcp_userinfo;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("CTCP Version");
-	pbm->callback = irc_ctcp_version;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	pbm = g_new0(struct proto_buddy_menu, 1);
-	pbm->label = _("CTCP Ping");
-	pbm->callback = irc_ctcp_ping;
-	pbm->gc = gc;
-	m = g_list_append(m, pbm);
-
-	return m;
+	g_free(ib->name);
+	g_free(ib);
 }
 
 static GaimPluginProtocolInfo prpl_info =
@@ -2928,17 +423,17 @@
 	OPT_PROTO_CHAT_TOPIC | OPT_PROTO_PASSWORD_OPTIONAL,
 	NULL,
 	NULL,
-	irc_list_icon,
-	irc_list_emblems,
+	irc_blist_icon,
+	irc_blist_emblems,
 	NULL,
 	NULL,
 	irc_away_states,
 	NULL,
-	irc_buddy_menu,
-	irc_chat_info,
+	NULL, /*irc_buddy_menu,*/
+	irc_chat_join_info,
 	irc_login,
 	irc_close,
-	irc_send_im,
+	irc_im_send,
 	NULL,
 	NULL,
 	irc_get_info,
@@ -2959,7 +454,7 @@
 	NULL,
 	NULL,
 	NULL,
-	irc_join_chat,
+	irc_chat_join,
 	irc_chat_invite,
 	irc_chat_leave,
 	NULL,
@@ -2972,7 +467,7 @@
 	NULL,
 	NULL,
 	NULL,
-	irc_convo_closed,
+	NULL, /*irc_convo_closed,*/
 	NULL
 };
 
@@ -2988,10 +483,8 @@
 	"prpl-irc",                                       /**< id             */
 	"IRC",                                            /**< name           */
 	VERSION,                                          /**< version        */
-	                                                  /**  summary        */
-	N_("IRC Protocol Plugin"),
-	                                                  /**  description    */
-	N_("IRC Protocol Plugin"),
+	N_("IRC Protocol Plugin"),                        /**  summary        */
+	N_("The IRC Protocol Plugin that Sucks Less"),    /**  description    */
 	NULL,                                             /**< author         */
 	WEBSITE,                                          /**< homepage       */
 
@@ -3003,26 +496,21 @@
 	&prpl_info                                        /**< extra_info     */
 };
 
-static void
-init_plugin(GaimPlugin *plugin)
+static void _init_plugin(GaimPlugin *plugin)
 {
 	GaimAccountUserSplit *split;
 	GaimAccountOption *option;
 
-	split = gaim_account_user_split_new(_("Server"), DEFAULT_SERVER, '@');
+	split = gaim_account_user_split_new(_("Server"), IRC_DEFAULT_SERVER, '@');
 	prpl_info.user_splits = g_list_append(prpl_info.user_splits, split);
 
-
-	option = gaim_account_option_int_new(_("Port"), "port", 6667);
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
+	option = gaim_account_option_int_new(_("Port:"), "port", IRC_DEFAULT_PORT);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-	option = gaim_account_option_string_new(_("Encoding"), "charset",
-											"ISO-8859-1");
-	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options,
-											   option);
+	option = gaim_account_option_string_new(_("Encoding"), "encoding", IRC_DEFAULT_CHARSET);
+	prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option);
 
-	my_protocol = plugin;
+	_irc_plugin = plugin;
 }
 
-GAIM_INIT_PLUGIN(irc, init_plugin, info);
+GAIM_INIT_PLUGIN(irc, _init_plugin, info);