view plugins/irc.c @ 1771:213607e89598

[gaim-migrate @ 1781] plug mem leak. don't show evil level if it decreased. mid's utf8 patch for jabber. my girlfriend got an accounting calculator today, you know, with the paper and the printing and things. it's kinda loud. she's really happy about having it. she had bought a different one yesterday but it didn't work so we returned it today. we also went to Albertson's and bought groceries. we bought 72 cans of soda for $15. That's 20 cents per soda. Not bad. we also bought a cow; i'm going to cook it tonight. ben&jerry's ice cream is good. committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Mon, 30 Apr 2001 01:25:30 +0000
parents 1e0613d9526b
children 1b61db419774
line wrap: on
line source

/*
 * gaim - IRC Protocol Plugin
 *
 * Copyright (C) 2000-2001, Rob Flynn <rob@tgflinux.com>
 * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "../config.h"


#include <netdb.h>
#include <gtk/gtk.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include "multi.h"
#include "prpl.h"
#include "gaim.h"

#include "pixmaps/free_icon.xpm"

#define IRC_BUF_LEN 4096


static int chat_id = 0;

struct irc_channel {
	int id;
	gchar *name;
};

struct irc_data {
	int fd;
	int inpa;		/* used for non-block logins */

	int timer;

	int totalblocks;
	int recblocks;

	GSList *templist;
	GList *channels;
};

static char *irc_name()
{
	return "IRC";
}

static void irc_get_info(struct gaim_connection *gc, char *who);
	
char *name()
{
	return "IRC";
}

char *description()
{
	return "Allows gaim to use the IRC protocol";
}

static void irc_join_chat(struct gaim_connection *gc, int id, char *name)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	gchar *buf = (gchar *) g_malloc(IRC_BUF_LEN + 1);

	g_snprintf(buf, IRC_BUF_LEN, "JOIN %s\n", name);
	write(idata->fd, buf, strlen(buf));
	write(idata->fd, buf, strlen(buf));

	g_free(buf);
}

static void irc_update_user(struct gaim_connection *gc, char *name, int status)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	struct irc_channel *u;
	GSList *temp = idata->templist;

	/* Loop through our list */

	while (temp) {
		u = (struct irc_channel *)temp->data;
		if (g_strcasecmp(u->name, name) == 0) {
			u->id = status;
			return;
		}

		temp = g_slist_next(temp);
	}
	return;
}

static void irc_request_buddy_update(struct gaim_connection *gc)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	GSList *grp = gc->groups;
	GSList *person;
	struct group *g;
	struct buddy *b;
	struct irc_channel *u;
	gchar buf[IRC_BUF_LEN + 1];

	if (idata->templist != NULL)
		return;

	idata->recblocks = 0;
	idata->totalblocks = 1;

	/* First, let's check to see if we have anyone on our buddylist */
	if (!grp) {
		return;
	}

	/* Send the first part of our request */
	write(idata->fd, "ISON", 4);

	/* Step through our list of groups */
	while (grp) {

		g = (struct group *)grp->data;
		person = g->members;

		while (person) {
			b = (struct buddy *)person->data;

			/* We will store our buddy info here.  I know, this is cheap
			 * but hey, its the exact same data structure.  Why should we
			 * bother with making another one */

			u = g_new0(struct irc_channel, 1);
			u->id = 0;	/* Assume by default that they're offline */
			u->name = strdup(b->name);

			write(idata->fd, " ", 1);
			write(idata->fd, u->name, strlen(u->name));
			idata->templist = g_slist_append(idata->templist, u);

			person = person->next;
		}

		grp = g_slist_next(grp);
	}
	write(idata->fd, "\n", 1);
}


static void irc_send_im(struct gaim_connection *gc, char *who, char *message, int away)
{

        struct irc_data *idata = (struct irc_data *)gc->proto_data;
	gchar *buf = (gchar *) g_malloc(IRC_BUF_LEN + 1);

	/* Before we actually send this, we should check to see if they're trying
	 * To issue a command and handle it properly. */

	if (message[0] == '/')
	{
		/* I'll change the implementation of this a little later :-) */
		if ((g_strncasecmp(message, "/me ", 4) == 0) && (strlen(message) > 4)) {
			/* We have /me!! We have /me!! :-) */

			gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
			strcpy(temp, message + 4);
			g_snprintf(buf, IRC_BUF_LEN, "PRIVMSG %s :%cACTION %s%c\n", who, '\001', temp, '\001');
			g_free(temp);
		} 
		else if (!g_strncasecmp(message, "/whois ", 7) && (strlen(message) > 7)) {
			gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
			strcpy(temp, message + 7);
			irc_get_info(gc, temp);
			g_free(temp);

			return;
		}	
				
	}
	else {
		g_snprintf(buf, IRC_BUF_LEN, "PRIVMSG %s :%s\n", who, message);
	}

	write(idata->fd, buf, strlen(buf));

	g_free(buf);
}

static int find_id_by_name(struct gaim_connection *gc, char *name)
{
	gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	GList *templist;
	struct irc_channel *channel;

	templist = ((struct irc_data *)gc->proto_data)->channels;

	while (templist) {
		channel = (struct irc_channel *)templist->data;

		g_snprintf(temp, IRC_BUF_LEN, "#%s", channel->name);

		if (g_strcasecmp(temp, name) == 0) {
			g_free(temp);
			return channel->id;
		}

		templist = templist->next;
	}

	g_free(temp);

	/* Return -1 if we have no ID */
	return -1;
}

static struct irc_channel *find_channel_by_name(struct gaim_connection *gc, char *name)
{
	gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	GList *templist;
	struct irc_channel *channel;

	templist = ((struct irc_data *)gc->proto_data)->channels;

	while (templist) {
		channel = (struct irc_channel *)templist->data;

		g_snprintf(temp, IRC_BUF_LEN, "%s", channel->name);

		if (g_strcasecmp(temp, name) == 0) {
			g_free(temp);
			return channel;
		}

		templist = templist->next;
	}

	g_free(temp);

	/* If we found nothing, return nothing :-) */
	return NULL;
}

static struct irc_channel *find_channel_by_id(struct gaim_connection *gc, int id)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	struct irc_channel *channel;

	GList *temp;

	temp = idata->channels;

	while (temp) {
		channel = (struct irc_channel *)temp->data;

		if (channel->id == id) {
			/* We've found our man */
			return channel;
		}

		temp = temp->next;
	}


	/* If we didnt find one, return NULL */
	return NULL;
}

static void irc_chat_send(struct gaim_connection *gc, int id, char *message)
{

        struct irc_data *idata = (struct irc_data *)gc->proto_data;
	struct irc_channel *channel = NULL;
	gchar *buf = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	char **kick; 
	gboolean is_command = FALSE;
	/* First lets get our current channel */
	channel = find_channel_by_id(gc, id);


	if (!channel) {
		/* If for some reason we've lost our channel, let's bolt */
		g_free(buf);
		return;
	}


	/* Before we actually send this, we should check to see if they're trying
	 * To issue a command and handle it properly. */

	if (message[0] == '/')
	  {
	   
	    if ((g_strncasecmp(message, "/me ", 4) == 0) && (strlen(message) > 4)) {
	      /* We have /me!! We have /me!! :-) */
	      
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 4);
	      
	      g_snprintf(buf, IRC_BUF_LEN, "PRIVMSG #%s :%cACTION %s%c\n", channel->name, '\001', temp,
			 '\001');
	      g_free(temp);
	    }
	    else if ((g_strncasecmp(message, "/op ", 4) == 0) && (strlen(message) > 4)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 4);
	      
	      g_snprintf(buf, IRC_BUF_LEN, "MODE #%s +o %s\n", channel->name, temp);
	      
	      g_free(temp);
	      is_command = TRUE;
	      
	    }
	    else if ((g_strncasecmp(message, "/deop ", 6) == 0) && (strlen(message) > 6)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 6);
	      g_snprintf(buf, IRC_BUF_LEN, "MODE #%s -o %s\n", channel->name, temp);
	      
	      g_free(temp);
	      is_command = TRUE;
		}
	    
	    else if ((g_strncasecmp(message, "/voice ", 7) == 0) && (strlen(message) > 7)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 7);
	      
	      g_snprintf(buf, IRC_BUF_LEN, "MODE #%s +v %s\n", channel->name, temp);
	      
	      g_free(temp);
	      is_command = TRUE;
	      
	    }
	    else if ((g_strncasecmp(message, "/devoice ", 9) == 0) && (strlen(message) > 9)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 6);
	      g_snprintf(buf, IRC_BUF_LEN, "MODE #%s -v %s\n", channel->name, temp);
	      
	      g_free(temp);
	      is_command = TRUE;
	    }
	    else if ((g_strncasecmp(message, "/mode ", 6) == 0) && (strlen(message) > 6)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 6);
	      g_snprintf(buf, IRC_BUF_LEN, "MODE #%s %s\n", channel->name, temp);
	      g_free(temp);
	      is_command = TRUE;
	    }
	    
	    else if (!g_strncasecmp(message, "/whois ", 7) && (strlen(message) > 7)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      
	      strcpy(temp, message + 7);
	      irc_get_info(gc, temp);
	      g_free(temp);
	      
	      
	    }	
	    
	    else if (!g_strncasecmp(message, "/raw ", 5) && (strlen(message) > 5)){
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 5);
	      g_snprintf(buf, IRC_BUF_LEN, "%s\r\n", temp);
	      g_free(temp);
	      is_command = TRUE;
	    }

	    else if (!g_strncasecmp(message, "/quote ", 7) && (strlen(message) >7)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 7);
	      g_snprintf(buf, IRC_BUF_LEN, "%s\r\n", temp);
	      g_free(temp);
	      is_command = TRUE;
	    }
	    
	    else if (!g_strncasecmp(message, "/kick ", 6) && (strlen(message) > 6)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 6);
	      kick =  g_strsplit(temp, " ", 2);
	      g_snprintf(buf, IRC_BUF_LEN, "KICK #%s %s :%s\r\n", channel->name, kick[0], kick[1]);
	      g_free(temp);
	      is_command = TRUE;
	    }

/* FIXME: I'll go back in and grab this later.   -- Rob */
/*
I THOUGHT THIS WOULD WORK, BUT I WAS WRONG.  WOULD SOMEONE KINDLY FIX IT?

	    
	    else if (!g_strncasecmp(message, "/help", 5)) {
	      gchar *temp = (gchar *) g_malloc(IRC_BUF_LEN + 1);
	      strcpy(temp, message + 5);
	      	  if (temp == "") {
	     
	      serv_got_chat_in(gc, id, "gAIM", 0, "Available Commands:");
	      		    serv_got_chat_in(gc, id, "gAIM", 0, " ");
				    serv_got_chat_in(gc, id, "gAIM", 0, "<b>op     voice     kick </b>");
				    serv_got_chat_in(gc, id, "gAIM", 0, "<b>deop   devoice   whois</b>");
				    serv_got_chat_in(gc, id, "gAIM", 0, "<b>me     raw       quote</b>");
				    serv_got_chat_in(gc, id, "gAIM", 0, "<b>mode</b>");
		      }
		      else {
		    serv_got_chat_in(gc, id, "gAIM", 0, "Usage: ");
		    if (temp == "op")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/op <nick></b> - Gives operator status to user.");
		    else if (temp == "deop")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/deop <nick></b> - Removes operator status from user.");
		    else if (temp == "me")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/me <action></b> - Sends an action to the channel.");
		    else if (temp == "mode")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/mode {[+|-}|o|p|s|i|t|n|b|v} [<limit][<nick>][<ban mask]</b> - Changes channel and user modes.");
		      else if (temp == "voice")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/voice <nick></b> - Gives voice status to user.");
		    else if (temp == "devoice")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/devoice <nick></b> - Removes voice status from user.");
		    else if (temp == "raw")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/raw <text></b> - Sends raw text to the server.");
		    else if (temp == "kick")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/kick [<comment>]</b> - Kicks a user out of the channel.");
		    else if (temp ==  "whois")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/whois <nick></b> - Gets information about user.");
		    else if (temp == "quote")
		      serv_got_chat_in(gc, id, "gAIM", 0, "<b>/raw <text></b> - Sends raw text to the server.");
		    else
		      serv_got_chat_in(gc, id, "gAIM", 0, "No such command.");
		  }      
		    
	      g_free(temp);
	      is_command = TRUE;
	    }
*/
	    
	  }
	
	else {
	  g_snprintf(buf, IRC_BUF_LEN, "PRIVMSG #%s :%s\n", channel->name, message);
	  
	    }
	
	
	write(idata->fd, buf, strlen(buf));
	
	/* Since AIM expects us to receive the message we send, we gotta fake it */
	if (is_command==FALSE)
	  serv_got_chat_in(gc, id, gc->username, 0, message, time((time_t)NULL));
	
	g_free(buf);
	
	
}
static struct conversation *find_conversation_by_id(struct gaim_connection *gc, int id)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	GSList *bc = gc->buddy_chats;
	struct conversation *b = NULL;

	while (bc) {
		b = (struct conversation *)bc->data;
		if (id == b->id) {
			break;
		}
		bc = bc->next;
		b = NULL;
	}

	if (!b) {
		return NULL;
	}

	return b;
}

static struct conversation *find_conversation_by_name(struct gaim_connection *gc, char *name)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	GSList *bc = gc->buddy_chats;
	struct conversation *b = NULL;

	while (bc) {
		int x;
		b = (struct conversation *)bc->data;

		if (g_strcasecmp(name, b->name) == 0) {
			break;
		}
		bc = bc->next;
		b = NULL;
	}

	if (!b) {
		return NULL;
	}

	return b;
}



static void irc_callback(gpointer data, gint source, GdkInputCondition condition)
{
	struct gaim_connection *gc = data;
	int i = 0;
	gchar buf[4096];
	gchar **buf2;
	struct irc_data *idata;
	
	idata = (struct irc_data *)gc->proto_data;


	do {
		if (read(idata->fd, buf + i, 1) < 0) {
			hide_login_progress(gc, "Read error");
			signoff(gc);
			return;
		}
	} while (buf[i++] != '\n');

	buf[--i] = '\0';
	g_strchomp(buf);
	g_print("%s\n", buf);

	/* Check for errors */

	if (((strstr(buf, "ERROR :") && (!strstr(buf, "PRIVMSG ")) &&
	      (!strstr(buf, "NOTICE ")) && (strlen(buf) > 7)))) {

		gchar *u_errormsg;

		/* Let's get our error message */
		u_errormsg = g_strdup(buf + 7);

		/* We got our error message.  Now, let's reaise an
		 * error dialog */

		do_error_dialog(u_errormsg, "Gaim: IRC Error");

		/* And our necessary garbage collection */
		g_free(u_errormsg);
	}

	/* This should be a whois response. I only care about the first (311) one.  I might do
	 * the other's later. They're boring.  */

	if (((strstr(buf, " 311 ")) && (!strstr(buf, "PRIVMSG")) && (!strstr(buf, "NOTICE")))) {
		char **res;

		res = g_strsplit(buf, " ", 7);

		if (!strcmp(res[1], "311"))
		{
			char buf[8192];

			g_snprintf(buf, 4096, "<b>Nick:</b> %s<br>"
					"<b>Host:</b> %s@%s<br>"
					"<b>Name:</b> %s<br>", res[3], res[4], res[5], res[7]+1);

			g_show_info_text(buf);
		}

		g_strfreev(res);
	}

	/* Autoresponse to an away message */
	if (((strstr(buf, " 301 ")) && (!strstr(buf, "PRIVMSG")) && (!strstr(buf, "NOTICE")))) {
		char **res;

		res = g_strsplit(buf, " ", 5);

		if (!strcmp(res[1], "301"))
			serv_got_im(gc, res[3], res[4] + 1, 1, time((time_t)NULL));

		g_strfreev(res);
	}

	/* Parse the list of names that we receive when we first sign on to
	 * a channel */

	if (((strstr(buf, " 353 ")) && (!strstr(buf, "PRIVMSG")) && (!strstr(buf, "NOTICE")))) {
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		gchar u_names[IRC_BUF_LEN + 1];
		struct conversation *convo = NULL;
		int j;

		for (j = 0, i = 0; buf[i] != ' '; j++, i++) {
			u_host[j] = buf[i];
		}

		u_host[j] = '\0';
		i++;

		for (j = 0; buf[i] != ' '; j++, i++) {
			u_command[j] = buf[i];
		}

		u_command[j] = '\0';
		i++;

		for (j = 0; buf[i] != '#'; j++, i++) {
		}
		i++;

		for (j = 0; buf[i] != ':'; j++, i++) {
			u_channel[j] = buf[i];
		}

		u_channel[j - 1] = '\0';
		i++;

		while ((buf[i] == ' ') || (buf[i] == ':')) {
			i++;
		}

		strcpy(u_names, buf + i);

		buf2 = g_strsplit(u_names, " ", 0);

		/* Let's get our conversation window */
		convo = find_conversation_by_name(gc, u_channel);

		if (!convo) {
			return;
		}

		/* Now that we've parsed the hell out of this big
		 * mess, let's try to split up the names properly */

		for (i = 0; buf2[i] != NULL; i++)
			add_chat_buddy(convo, buf2[i]);

		/* And free our pointers */
		g_strfreev(buf2);

		return;

	}

	/* Receive a list of users that are currently online */

	if (((strstr(buf, " 303 ")) && (!strstr(buf, "PRIVMSG")) && (!strstr(buf, "NOTICE")))) {
		gchar u_host[255];
		gchar u_command[32];
		gchar u_names[IRC_BUF_LEN + 1];
		int j;

		for (j = 0, i = 0; buf[i] != ' '; j++, i++) {
			u_host[j] = buf[i];
		}

		u_host[j] = '\0';
		i++;

		for (j = 0; buf[i] != ' '; j++, i++) {
			u_command[j] = buf[i];
		}

		u_command[j] = '\0';
		i++;

		for (j = 0; buf[i] != ':'; j++, i++) {
			/* My Nick */
		}
		i++;

		strcpy(u_names, buf + i);

		buf2 = g_strsplit(u_names, " ", 0);

		/* Now that we've parsed the hell out of this big
		 * mess, let's try to split up the names properly */

		for (i = 0; buf2[i] != NULL; i++) {
			/* If we have a name here then our buddy is online.  We should
			 * update our temporary gslist accordingly.  When we achieve our maximum
			 * list of names then we should force an update */

			irc_update_user(gc, buf2[i], 1);
		}

		/* Increase our received blocks counter */
		idata->recblocks++;

		/* If we have our total number of blocks */
		if (idata->recblocks == idata->totalblocks) {
			GSList *temp;
			struct irc_channel *u;

			/* Let's grab our list of people and bring them all on or off line */
			temp = idata->templist;

			/* Loop */
			while (temp) {

				u = temp->data;

				/* Tell Gaim to bring the person on or off line */
				serv_got_update(gc, u->name, u->id, 0, 0, 0, 0, 0);

				/* Grab the next entry */
				temp = g_slist_next(temp);
			}

			/* And now, let's delete all of our entries */
			temp = idata->templist;
			while (temp) {
				u = temp->data;
				g_free(u->name);
				temp = g_slist_remove(temp, u);
			}

			/* Reset our list */
			idata->totalblocks = 0;
			idata->recblocks = 0;

			idata->templist = NULL;

			return;
		}

		/* And free our pointers */
		g_strfreev(buf2);

		return;

	}


	if ((strstr(buf, " JOIN ")) && (buf[0] == ':') && (!strstr(buf, " NOTICE "))) {

		gchar u_channel[128];
		gchar u_nick[128];

		struct irc_channel *channel;
		int id;
		int j;

		for (j = 0, i = 1; buf[i] != '!'; j++, i++) {
			u_nick[j] = buf[i];
		}

		u_nick[j] = '\0';
		i++;

		for (j = 0; buf[i] != '#'; j++, i++) {
		}

		i++;

		strcpy(u_channel, buf + i);

		g_strchomp(u_channel);

		/* Looks like we're going to join the channel for real 
		 * now.  Let's create a valid channel structure and add 
		 * it to our list.  Let's make sure that
		 * we are not already in a channel first */

		channel = find_channel_by_name(gc, u_channel);

		if (!channel) {

			chat_id++;

			channel = g_new0(struct irc_channel, 1);

			channel->id = chat_id;
			channel->name = strdup(u_channel);

			idata->channels = g_list_append(idata->channels, channel);

			serv_got_joined_chat(gc, chat_id, u_channel);
		} else {
			struct conversation *convo = NULL;

			/* Someone else joined. Find their conversation
			 * window */
			convo = find_conversation_by_id(gc, channel->id);

			/* And add their name to it */
			add_chat_buddy(convo, u_nick);

		}

		return;
	}

	if ((strstr(buf, " NICK ")) && (buf[0] == ':') && (!strstr(buf, " NOTICE "))) {

		gchar old[128];
		gchar new[128];

		GList *templist;

		struct irc_channel *channel;
		int id;
		int j;

		for (j = 0, i = 1; buf[i] != '!'; j++, i++) {
			old[j] = buf[i];
		}

		old[j] = '\0';
		i++;

		for (j = 0; buf[i] != ':'; j++, i++) {
		}

		i++;
		strcpy(new, buf + i);

		g_strchomp(new);

		templist = ((struct irc_data *)gc->proto_data)->channels;

		while (templist) {
			struct conversation *convo = NULL;
			channel = templist->data;

			convo = find_conversation_by_id(gc, channel->id);

			rename_chat_buddy(convo, old, new);

			templist = templist->next;
		}
	}

	if ((strstr(buf, "QUIT ")) && (buf[0] == ':') && (!strstr(buf, " NOTICE "))) {

		gchar u_nick[128];

		GList *templist;

		struct irc_channel *channel;
		int j;

		for (j = 0, i = 1; buf[i] != '!'; j++, i++) {
			u_nick[j] = buf[i];
		}

		u_nick[j] = '\0';

		templist = ((struct irc_data *)gc->proto_data)->channels;

		while (templist) {
			struct conversation *convo = NULL;
			channel = templist->data;

			convo = find_conversation_by_id(gc, channel->id);

			remove_chat_buddy(convo, u_nick);

			templist = templist->next;
		}
	}

	if ((strstr(buf, " PART ")) && (buf[0] == ':') && (!strstr(buf, " NOTICE "))) {

		gchar u_channel[128];
		gchar u_nick[128];

		struct irc_channel *channel;
		int id;
		int j;
		GList *test = NULL;

		for (j = 0, i = 1; buf[i] != '!'; j++, i++) {
			u_nick[j] = buf[i];
		}
		u_nick[j] = '\0';

		i++;

		for (j = 0; buf[i] != '#'; j++, i++) {
		}

		i++;

		strcpy(u_channel, buf + i);


		/* Now, lets check to see if it was US that was leaving.  
		 * If so, do the correct thing by closing up all of our 
		 * old channel stuff. Otherwise,
		 * we should just print that someone left */

		channel = find_channel_by_name(gc, u_channel);

		if (!channel) {
			return;
		}

		if (g_strcasecmp(u_nick, gc->username) == 0) {

			/* Looks like we're going to leave the channel for 
			 * real now.  Let's create a valid channel structure 
			 * and add it to our list */

			serv_got_chat_left(gc, channel->id);

			idata->channels = g_list_remove(idata->channels, channel);
		} else {
			struct conversation *convo = NULL;

			/* Find their conversation window */
			convo = find_conversation_by_id(gc, channel->id);

			if (!convo) {
				/* Some how the window doesn't exist. 
				 * Let's get out of here */
				return;
			}

			/* And remove their name */
			remove_chat_buddy(convo, u_nick);

		}

		/* Go Home! */
		return;
	}

	if ((strstr(buf, " NOTICE ")) && (buf[0] == ':')) {
		gchar u_nick[128];
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		gchar u_message[IRC_BUF_LEN];
		int j;
		int msgcode = 0;

		for (j = 0, i = 1; buf[i] != '!'; j++, i++) {
			u_nick[j] = buf[i];
		}

		u_nick[j] = '\0';
		i++;

		for (j = 0; buf[i] != ' '; j++, i++) {
			u_host[j] = buf[i];
		}

		u_host[j] = '\0';
		i++;

		for (j = 0; buf[i] != ' '; j++, i++) {
			u_command[j] = buf[i];
		}

		u_command[j] = '\0';
		i++;

		for (j = 0; buf[i] != ':'; j++, i++) {
			u_channel[j] = buf[i];
		}

		u_channel[j - 1] = '\0';
		i++;


		/* Now that everything is parsed, the rest of this baby must be our message */
		strncpy(u_message, buf + i, IRC_BUF_LEN);

		/* Now, lets check the message to see if there's anything special in it */
		if (u_message[0] == '\001') {
			if ((g_strncasecmp(u_message, "\001PING ", 6) == 0) && (strlen(u_message) > 6)) {
				/* Someone's triyng to ping us.  Let's respond */
				gchar u_arg[24];
				gchar u_buf[200];
				unsigned long tend= time((time_t *)NULL);
				unsigned long tstart;
				
				printf("LA: %s\n", buf);

				strcpy(u_arg, u_message + 6);
				u_arg[strlen(u_arg) - 1] = '\0';

				tstart = atol(u_arg);

				g_snprintf(u_buf, sizeof(u_buf), "Ping Reply From %s: [%ld seconds]", u_nick, tend-tstart);

				do_error_dialog(u_buf, "Gaim IRC - Ping Reply");

				return;
			}
		}

	}


	if ((strstr(buf, " PRIVMSG ")) && (buf[0] == ':')) {
		gchar u_nick[128];
		gchar u_host[255];
		gchar u_command[32];
		gchar u_channel[128];
		gchar u_message[IRC_BUF_LEN];
		int j;
		int msgcode = 0;

		for (j = 0, i = 1; buf[i] != '!'; j++, i++) {
			u_nick[j] = buf[i];
		}

		u_nick[j] = '\0';
		i++;

		for (j = 0; buf[i] != ' '; j++, i++) {
			u_host[j] = buf[i];
		}

		u_host[j] = '\0';
		i++;

		for (j = 0; buf[i] != ' '; j++, i++) {
			u_command[j] = buf[i];
		}

		u_command[j] = '\0';
		i++;

		for (j = 0; buf[i] != ':'; j++, i++) {
			u_channel[j] = buf[i];
		}

		u_channel[j - 1] = '\0';
		i++;


		/* Now that everything is parsed, the rest of this baby must be our message */
		strncpy(u_message, buf + i, IRC_BUF_LEN);

		/* Now, lets check the message to see if there's anything special in it */
		if (u_message[0] == '\001') {
			if (g_strncasecmp(u_message, "\001VERSION", 8) == 0) {
				/* Looks like we have a version request.  Let
				 * us handle it thusly */

				g_snprintf(buf, IRC_BUF_LEN,
					   "NOTICE %s :%cVERSION GAIM %s:The Pimpin Penguin AIM Clone:www.marko.net/gaim%c\n",
					   u_nick, '\001', VERSION, '\001');

				write(idata->fd, buf, strlen(buf));

				/* And get the heck out of dodge */
				return;
			}

			if ((g_strncasecmp(u_message, "\001PING ", 6) == 0) && (strlen(u_message) > 6)) {
				/* Someone's triyng to ping us.  Let's respond */
				gchar u_arg[24];

				strcpy(u_arg, u_message + 6);
				u_arg[strlen(u_arg) - 1] = '\0';

				g_snprintf(buf, IRC_BUF_LEN, "NOTICE %s :%cPING %s%c\n", u_nick, '\001',
					   u_arg, '\001');

				write(idata->fd, buf, strlen(buf));

				/* And get the heck out of dodge */
				return;
			}

			if (g_strncasecmp(u_message, "\001ACTION ", 8) == 0) {
				/* Looks like we have an action. Let's parse it a little */
				strcpy(buf, u_message);

				strcpy(u_message, "/me ");
				for (j = 4, i = 8; buf[i] != '\001'; i++, j++) {
					u_message[j] = buf[i];
				}
				u_message[j] = '\0';
			}
		}


		/* Let's check to see if we have a channel on our hands */
		if (u_channel[0] == '#') {
			/* Yup.  We have a channel */
			int id;

			id = find_id_by_name(gc, u_channel);
			if (id != -1) {
				serv_got_chat_in(gc, id, u_nick, 0, u_message, time((time_t)NULL));
			}
		} else {
			/* Nope. Let's treat it as a private message */
			serv_got_im(gc, u_nick, u_message, 0, time((time_t)NULL));
		}

		return;
	}

	/* Let's parse PING requests so that we wont get booted for inactivity */

	if (strncmp(buf, "PING :", 6) == 0) {
		buf2 = g_strsplit(buf, ":", 1);

		/* Let's build a new response */
		g_snprintf(buf, IRC_BUF_LEN, "PONG :%s\n", buf2[1]);
		write(idata->fd, buf, strlen(buf));

		/* And clean up after ourselves */
		g_strfreev(buf2);

		return;
	}

}

static void irc_close(struct gaim_connection *gc)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	GList *chats = idata->channels;
	struct irc_channel *cc;

	gchar *buf = (gchar *) g_malloc(IRC_BUF_LEN);

	g_snprintf(buf, IRC_BUF_LEN, "QUIT :Download GAIM [www.marko.net/gaim]\n");
	write(idata->fd, buf, strlen(buf));

	g_free(buf);

	if (idata->timer)
		gtk_timeout_remove(idata->timer);

	while (chats) {
		cc = (struct irc_channel *)chats->data;
		g_free(cc->name);
		chats = g_list_remove(chats, cc);
		g_free(cc);
	}

	if (gc->inpa)
		gdk_input_remove(gc->inpa);

	if (idata->inpa)
		gdk_input_remove(idata->inpa);

	close(idata->fd);
	g_free(gc->proto_data);
}

static void irc_chat_leave(struct gaim_connection *gc, int id)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	struct irc_channel *channel;
	gchar *buf = (gchar *) g_malloc(IRC_BUF_LEN + 1);

	channel = find_channel_by_id(gc, id);

	if (!channel) {
		return;
	}

	g_snprintf(buf, IRC_BUF_LEN, "PART #%s\n", channel->name);
	write(idata->fd, buf, strlen(buf));

	g_free(buf);
}

static void irc_login_callback(gpointer data, gint source, GdkInputCondition condition)
{
	struct gaim_connection *gc = data;
	struct irc_data *idata = gc->proto_data;
	char buf[4096];
	int len, error = ETIMEDOUT;

	len = sizeof(error);
	if (getsockopt(idata->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
		error = errno;
	if (error) {
		hide_login_progress(gc, "Couldn't connect");
		signoff(gc);
		return;
	}

	fcntl(idata->fd, F_SETFL, 0);

	g_snprintf(buf, 4096, "NICK %s\n USER %s localhost %s :GAIM (www.marko.net/gaim)\n",
		   gc->username, g_get_user_name(), gc->user->proto_opt[0]);

	if (write(idata->fd, buf, strlen(buf)) < 0) {
		hide_login_progress(gc, "Write error");
		signoff(gc);
		return;
	}

	gdk_input_remove(idata->inpa);
	idata->inpa = 0;

	/* Now lets sign ourselves on */
	account_online(gc);
	serv_finish_login(gc);

	if (bud_list_cache_exists(gc))
		do_import(NULL, gc);

	/* we don't call this now because otherwise some IRC servers might not like us */
	idata->timer = gtk_timeout_add(20000, (GtkFunction)irc_request_buddy_update, gc);
}

static void irc_login(struct aim_user *user)
{
	int fd;
	struct hostent *host;
	struct sockaddr_in site;
	char buf[4096];

	struct gaim_connection *gc = new_gaim_conn(user);
	struct irc_data *idata = gc->proto_data = g_new0(struct irc_data, 1);
	char c;
	int i;
	int status;

	host = gethostbyname(user->proto_opt[0]);
	if (!host) {
		hide_login_progress(gc, "Unable to resolve hostname");
		signoff(gc);
		return;
	}

	site.sin_family = AF_INET;
	site.sin_addr.s_addr = *(long *)(host->h_addr);
	
	if (user->proto_opt[1][0])
		site.sin_port = htons(atoi(user->proto_opt[1]));
	else {
		site.sin_port = htons(6667);
		g_snprintf(user->proto_opt[1], 5, "6667");
	}

	fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) {
		hide_login_progress(gc, "Unable to create socket");
		signoff(gc);
		return;
	}

	fcntl(fd, F_SETFL, O_NONBLOCK);
	if (connect(fd, (struct sockaddr *)&site, sizeof(site)) < 0) {
		if ((errno == EINPROGRESS) || (errno == EINTR)) {
			idata->inpa = gdk_input_add(idata->fd, GDK_INPUT_WRITE, irc_login_callback, gc);
		} else {
			hide_login_progress(gc, "Unable to connect.");
			signoff(gc);
			return;
		}
	}

	idata->fd = fd;

	g_snprintf(buf, sizeof(buf), "Signon: %s", gc->username);
	set_login_progress(gc, 2, buf);

	/* This is where we will attempt to sign on */

	if (!idata->inpa)
		irc_login_callback(gc, idata->fd, GDK_INPUT_READ);

	gc->inpa = gdk_input_add(idata->fd, GDK_INPUT_READ, irc_callback, gc);
}

static void irc_print_option(GtkEntry * entry, struct aim_user *user)
{
	if (gtk_object_get_user_data(GTK_OBJECT(entry))) {
		g_snprintf(user->proto_opt[1], sizeof(user->proto_opt[1]), "%s",
			   gtk_entry_get_text(entry));
	} else {
		g_snprintf(user->proto_opt[0], sizeof(user->proto_opt[0]), "%s",
			   gtk_entry_get_text(entry));
	}
}

static void irc_user_opts(GtkWidget * book, struct aim_user *user)
{
	/* so here, we create the new notebook page */
	GtkWidget *vbox;
	GtkWidget *hbox;
	GtkWidget *label;
	GtkWidget *entry;

	vbox = gtk_vbox_new(FALSE, 0);
	gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, gtk_label_new("IRC Options"));
	gtk_widget_show(vbox);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
	gtk_widget_show(hbox);

	label = gtk_label_new("Server:");
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
	gtk_widget_show(label);

	entry = gtk_entry_new();
	gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
	gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(irc_print_option), user);
	if (user->proto_opt[0][0]) {
		debug_printf("setting text %s\n", user->proto_opt[0]);
		gtk_entry_set_text(GTK_ENTRY(entry), user->proto_opt[0]);
	}
	gtk_widget_show(entry);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
	gtk_widget_show(hbox);

	label = gtk_label_new("Port:");
	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 5);
	gtk_widget_show(label);

	entry = gtk_entry_new();
	gtk_box_pack_end(GTK_BOX(hbox), entry, FALSE, FALSE, 5);
	if (user->proto_opt[1][0]) {
		debug_printf("setting text %s\n", user->proto_opt[1]);
		gtk_entry_set_text(GTK_ENTRY(entry), user->proto_opt[1]);
	} else {
		gtk_entry_set_text(GTK_ENTRY(entry), "6667");
	}
	gtk_object_set_user_data(GTK_OBJECT(entry), user);
	gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(irc_print_option), user);
	gtk_widget_show(entry);
}

static char **irc_list_icon(int uc)
{
	return free_icon_xpm;
}

/* Send out a ping request to the specified user */
static void irc_send_ping(GtkObject * w, char *who)
{
	struct gaim_connection *gc = (struct gaim_connection *)gtk_object_get_user_data(w);
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	char buf[BUF_LEN];

	g_snprintf(buf, BUF_LEN, "PRIVMSG %s :%cPING %ld%c\n", who, '\001', time((time_t *)NULL), '\001');

	write(idata->fd, buf, strlen(buf));
}

/* Do a whois check on someone :-) */
static void irc_get_info(struct gaim_connection *gc, char *who)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	char buf[BUF_LEN];

	if ((who[0] == '@') || (who[0] == '+') && (strlen(who)>1))
		g_snprintf(buf, BUF_LEN, "WHOIS %s\n", who+1);
	else
		g_snprintf(buf, BUF_LEN, "WHOIS %s\n", who);
	write(idata->fd, buf, strlen(buf));
}

static void irc_send_whois(GtkObject * w, char *who)
{
	struct gaim_connection *gc = (struct gaim_connection *)gtk_object_get_user_data(w);
	irc_get_info(gc, who);
}

static void irc_buddy_menu(GtkWidget * menu, struct gaim_connection *gc, char *who)
{
	GtkWidget *button;

	button = gtk_menu_item_new_with_label("Ping");
	gtk_signal_connect(GTK_OBJECT(button), "activate", GTK_SIGNAL_FUNC(irc_send_ping), who);
	gtk_object_set_user_data(GTK_OBJECT(button), gc);
	gtk_menu_append(GTK_MENU(menu), button);
	gtk_widget_show(button);

	button = gtk_menu_item_new_with_label("Whois");
	gtk_signal_connect(GTK_OBJECT(button), "activate", GTK_SIGNAL_FUNC(irc_send_whois), who);
	gtk_object_set_user_data(GTK_OBJECT(button), gc);
	gtk_menu_append(GTK_MENU(menu), button);
	gtk_widget_show(button);
}


static void irc_set_away(struct gaim_connection *gc, char *state, char *msg)
{
	struct irc_data *idata = (struct irc_data *)gc->proto_data;
	char buf[BUF_LEN];

	if (msg)
		g_snprintf(buf, BUF_LEN, "AWAY :%s\n", msg);		
	else
		g_snprintf(buf, BUF_LEN, "AWAY\n");		

	write(idata->fd, buf, strlen(buf));
}

static void irc_fake_buddy(struct gaim_connection *gc, char *who)
{
	/* Heh, there is no buddy list. We fake it.
	 * I just need this here so the add and remove buttons will
	 * show up */
}



static struct prpl *my_protocol = NULL;

static void irc_init(struct prpl *ret)
{
	ret->protocol = PROTO_IRC;
	ret->name = irc_name;
	ret->list_icon = irc_list_icon;
	ret->buddy_menu = irc_buddy_menu;
	ret->user_opts = irc_user_opts;
	ret->login = irc_login;
	ret->close = irc_close;
	ret->send_im = irc_send_im;
	ret->join_chat = irc_join_chat;
	ret->chat_leave = irc_chat_leave;
	ret->chat_send = irc_chat_send;
	ret->get_info = irc_get_info;
	ret->set_away = irc_set_away;
	ret->add_buddy = irc_fake_buddy;
	ret->remove_buddy = irc_fake_buddy;

	my_protocol = ret;
}

char *gaim_plugin_init(GModule * handle)
{
	load_protocol(irc_init, sizeof(struct prpl));
	return NULL;
}

void gaim_plugin_remove()
{
	struct prpl *p = find_prpl(PROTO_IRC);
	if (p == my_protocol)
		unload_protocol(p);
}