changeset 6982:083d1e4a9c78

[gaim-migrate @ 7538] This is Mr. Holland's Opus. And by Mr. Holland I mean Robot101. He rewrote the coreish IM image support so that the binary data gets ripped out in the prpl and put in an imgstore instead of just being passed in the same huge as char string as the actual message. This is good because it's prpl agnostic, or something. It also means we don't have a silly length of "-1" with pretty much every send or receive IM function. It should be crash free, bug free, and memleak free, but additional testing is always a good thing. If you like good stuff then you'll love this patch. But don't take my word for it--ba dun dunt! committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sat, 27 Sep 2003 19:17:21 +0000
parents abd3c684da31
children 7db9b3020345
files ChangeLog configure.ac plugins/filectl.c plugins/gaim-remote/remote.c plugins/gaiminc.c plugins/history.c plugins/notify.c plugins/perl/common/Conversation.xs plugins/perl/common/Conversation_IM.xs plugins/statenotify.c plugins/tcl/tcl_cmds.c plugins/timestamp.c src/Makefile.am src/about.c src/away.c src/connection.h src/conversation.c src/conversation.h src/dialogs.c src/gtkconv.c src/gtkdebug.c src/gtkimhtml.c src/gtkimhtml.h src/gtknotify.c src/gtkpounce.c src/gtkprefs.c src/imgstore.c src/imgstore.h src/plugin.c src/protocols/gg/gg.c src/protocols/irc/cmds.c src/protocols/irc/irc.c src/protocols/irc/msgs.c src/protocols/jabber/jabber.c src/protocols/msn/msn.c src/protocols/msn/switchboard.c src/protocols/napster/napster.c src/protocols/oscar/oscar.c src/protocols/toc/toc.c src/protocols/trepia/trepia.c src/protocols/yahoo/yahoo.c src/protocols/zephyr/zephyr.c src/prpl.h src/server.c src/server.h src/ui.h src/util.c src/util.h
diffstat 48 files changed, 1014 insertions(+), 438 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Sep 27 15:45:49 2003 +0000
+++ b/ChangeLog	Sat Sep 27 19:17:21 2003 +0000
@@ -1,6 +1,8 @@
 Gaim: The Pimpin' Penguin IM Clone that's good for the soul!
 
 version 0.70:
+	* Robert "Robot101" McQueen cleaned and core/UI split IM 
+	  image support
 	* Chinese (Traditional) translation updated (Ambrose C. Li)
 	* Portuguese (Portugal) translation updated (Duarte Henriques)
 
--- a/configure.ac	Sat Sep 27 15:45:49 2003 +0000
+++ b/configure.ac	Sat Sep 27 19:17:21 2003 +0000
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 AC_INIT(src/main.c)
 AM_CONFIG_HEADER(config.h)
-AM_INIT_AUTOMAKE([gaim], [0.69])
+AM_INIT_AUTOMAKE([gaim], [0.70cvs])
 
 AC_PREREQ([2.50])
 
--- a/plugins/filectl.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/filectl.c	Sat Sep 27 19:17:21 2003 +0000
@@ -76,7 +76,7 @@
 			c = find_conversation(arg1);
 			if (!c) c = gaim_conversation_new(GAIM_CONV_IM, arg1);
 			write_to_conv(c, arg2, WFLAG_SEND, NULL, time(NULL), -1);
-			serv_send_im(c->gc, arg1, arg2, -1, 0);
+			serv_send_im(c->gc, arg1, arg2, 0);
 			free(arg1);
 			free(arg2);
 		} else if (!strncasecmp(command, "away", 4)) {
--- a/plugins/gaim-remote/remote.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/gaim-remote/remote.c	Sat Sep 27 19:17:21 2003 +0000
@@ -434,7 +434,7 @@
 			pos += len;
 
 			memcpy(&flags, data + pos, sizeof(flags));
-			serv_send_im(gc, who, msg, -1, flags);
+			serv_send_im(gc, who, msg, flags);
 
 			g_free(who);
 			g_free(msg);
--- a/plugins/gaiminc.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/gaiminc.c	Sat Sep 27 19:17:21 2003 +0000
@@ -43,7 +43,7 @@
 {
 	/* whenever someone comes online, it sends them a message. if i
 	 * cared more, i'd make it so it popped up on your screen too */
-	serv_send_im(gc, who, "Hello!", -1, 0);
+	serv_send_im(gc, who, "Hello!", 0);
 }
 
 /*
--- a/plugins/history.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/history.c	Sat Sep 27 19:17:21 2003 +0000
@@ -61,8 +61,8 @@
 
 	gtkconv = GAIM_GTK_CONVERSATION(c);
 
-	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), tmp2, strlen(tmp2), options);
-	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<br>", -1, options);
+	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), tmp2, options);
+	gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<br>", options);
 	gtk_text_buffer_get_end_iter(GTK_IMHTML(gtkconv->imhtml)->text_buffer, &
 end);
 	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(gtkconv->imhtml), &end, 0, 0,
--- a/plugins/notify.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/notify.c	Sat Sep 27 19:17:21 2003 +0000
@@ -514,7 +514,7 @@
 options_entry_cb(GtkWidget *widget, GdkEventFocus *evt, gpointer data)
 {
 	if (data == NULL)
-		return;
+		return FALSE;
 
 	if (!strcmp(data, "method_string")) {
 		gaim_prefs_set_string("/plugins/gtk/X11/notify/title_string", gtk_entry_get_text(GTK_ENTRY(widget)));
--- a/plugins/perl/common/Conversation.xs	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/perl/common/Conversation.xs	Sat Sep 27 19:17:21 2003 +0000
@@ -80,7 +80,7 @@
 	const char *message
 	int flags
 CODE:
-	gaim_conversation_write(conv, who, message, -1, flags, time(NULL));
+	gaim_conversation_write(conv, who, message, flags, time(NULL));
 
 Gaim::Conversation::IM
 gaim_conversation_get_im_data(conv)
--- a/plugins/perl/common/Conversation_IM.xs	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/perl/common/Conversation_IM.xs	Sat Sep 27 19:17:21 2003 +0000
@@ -30,7 +30,7 @@
 	const char *message
 	int flags
 CODE:
-	gaim_im_write(im, who, message, -1, flags, time(NULL));
+	gaim_im_write(im, who, message, flags, time(NULL));
 
 void
 gaim_im_send(im, message)
--- a/plugins/statenotify.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/statenotify.c	Sat Sep 27 19:17:21 2003 +0000
@@ -21,7 +21,7 @@
 
 	g_snprintf(buf, sizeof(buf), message, who);
 
-	gaim_conversation_write(conv, NULL, buf, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+	gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
 }
 
 static void
--- a/plugins/tcl/tcl_cmds.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/tcl/tcl_cmds.c	Sat Sep 27 19:17:21 2003 +0000
@@ -601,7 +601,7 @@
 		if (gaim_conversation_get_type(convo) == GAIM_CONV_CHAT)
 			gaim_chat_write(GAIM_CHAT(convo), from, what, flags, time(NULL));
 		else
-			gaim_im_write(GAIM_IM(convo), from, what, -1, flags, time(NULL));
+			gaim_im_write(GAIM_IM(convo), from, what, flags, time(NULL));
 		break;
 	}
 
@@ -849,7 +849,7 @@
 	who = Tcl_GetString(objv[2]);
 	text = Tcl_GetString(objv[3]);
 
-	serv_send_im(gc, who, text, -1, 0);
+	serv_send_im(gc, who, text, 0);
 
 	return TCL_OK;
 }
--- a/plugins/timestamp.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/plugins/timestamp.c	Sat Sep 27 19:17:21 2003 +0000
@@ -32,7 +32,7 @@
 
 	strftime(mdate, sizeof(mdate), "%H:%M", localtime(&tim));
 	buf = g_strdup_printf("            %s", mdate);
-	gaim_conversation_write(c, NULL, buf, -1, GAIM_MESSAGE_NO_LOG, tim);
+	gaim_conversation_write(c, NULL, buf, GAIM_MESSAGE_NO_LOG, tim);
 	g_free(buf);
 	return TRUE;
 }
--- a/src/Makefile.am	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/Makefile.am	Sat Sep 27 19:17:21 2003 +0000
@@ -68,6 +68,8 @@
 	ft.h \
 	html.c \
 	html.h \
+	imgstore.c \
+	imgstore.h \
 	md5.c \
 	md5.h \
 	multi.h \
--- a/src/about.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/about.c	Sat Sep 27 19:17:21 2003 +0000
@@ -89,15 +89,15 @@
 				  _("Gaim is a modular Instant Messaging client capable of "
 					  "using AIM, ICQ, Yahoo!, MSN, IRC, Jabber, Napster, "
 					  "Zephyr, and Gadu-Gadu all at once.  It is written using "
-					  "Gtk+ and is licensed under the GPL.<BR><BR>"), -1, GTK_IMHTML_NO_SCROLL);
+					  "Gtk+ and is licensed under the GPL.<BR><BR>"), GTK_IMHTML_NO_SCROLL);
 
 		gtk_imhtml_append_text(GTK_IMHTML(text),
 				"<FONT SIZE=\"3\">URL:</FONT> <A HREF=\"" GAIM_WEBSITE "\">"
-				GAIM_WEBSITE "</A><BR><BR>", -1, GTK_IMHTML_NO_SCROLL);
+				GAIM_WEBSITE "</A><BR><BR>", GTK_IMHTML_NO_SCROLL);
 
 		gtk_imhtml_append_text(GTK_IMHTML(text),
 				_("<FONT SIZE=\"3\">IRC:</FONT> #gaim on irc.freenode.net"
-				"<BR><BR>"), -1, GTK_IMHTML_NO_SCROLL);
+				"<BR><BR>"), GTK_IMHTML_NO_SCROLL);
 
 		/* Active Developers */
 		str = g_strconcat(
@@ -116,7 +116,7 @@
 			"  Ethan 'Paco-Paco' Blanton (", _("developer"), ")<br>"
 			"  Luke 'LSchiere' Schierer (", _("support"), ")<BR>"
 			"<BR>", NULL);
-		gtk_imhtml_append_text(GTK_IMHTML(text), str, -1, GTK_IMHTML_NO_SCROLL);
+		gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
 		g_free(str);
 
 		/* Crazy Patch Writers */
@@ -128,7 +128,7 @@
 			"  Robert 'Robot101' McQueen<BR>"
 			"  Tim 'marv' Ringenbach<br>"
 			"<BR>", NULL);
-		gtk_imhtml_append_text(GTK_IMHTML(text), str, -1, GTK_IMHTML_NO_SCROLL);
+		gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
 		g_free(str);
 
 		/* Just because */
@@ -136,7 +136,7 @@
 			"<FONT SIZE=\"3\">", "Just Because", ":</FONT><BR>"
 			"  Rachel Shelton (Vampire & Dark Lord)<br>"
 			"<BR>", NULL);
-		gtk_imhtml_append_text(GTK_IMHTML(text), str, -1, GTK_IMHTML_NO_SCROLL);
+		gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
 		g_free(str);
 
 		/* Retired Developers */
@@ -154,7 +154,7 @@
 				"  Syd Logan (", _("hacker and designated driver [lazy bum]"), 
 				")<BR>"
 				"<BR>", NULL);
-		gtk_imhtml_append_text(GTK_IMHTML(text), str, -1, GTK_IMHTML_NO_SCROLL);
+		gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
 		g_free(str);
 
 		/* Current Translators */
@@ -181,7 +181,7 @@
 				"  <b>", _("Simplified Chinese"), " (zh_CN)</b> - Funda Wang &lt;<a href=\"mailto: fundawang@linux.net.cn\">fundawang@linux.net.cn</a>&gt;<br>"
 				"  <b>", _("Traditional Chinese"), " (zh_TW)</b> - Ambrose C. Li &lt;<a href=\"mailto: acli@ada.dhs.org\">acli@ada.dhs.org</a>&gt;, Paladin R. Liu &lt;<a href=\"mailto: paladin@ms1.hinet.net\">paladin@ms1.hinet.net</a>&gt;<br>"
 				"<BR>", NULL);
-		gtk_imhtml_append_text(GTK_IMHTML(text), str, -1, GTK_IMHTML_NO_SCROLL);
+		gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
 		g_free(str);
 
 		/* Past Translators */
@@ -205,7 +205,7 @@
 				"  <b>", _("Swedish"), " (sv)</b> - Christian Rose<br>"
 				"  <b>", _("Chinese"), " (zh_CN, zh_TW)</b> - Hashao, Rocky S. Lee<br>"
 				"<BR>", NULL);
-		gtk_imhtml_append_text(GTK_IMHTML(text), str, -1, GTK_IMHTML_NO_SCROLL);
+		gtk_imhtml_append_text(GTK_IMHTML(text), str, GTK_IMHTML_NO_SCROLL);
 		g_free(str);
 
 		gtk_adjustment_set_value(gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw)), 0);
--- a/src/away.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/away.c	Sat Sep 27 19:17:21 2003 +0000
@@ -78,7 +78,7 @@
 				else
 					gaim_conversation_set_account(cnv, account);
 
-				gaim_im_write(GAIM_IM(cnv), NULL, qm->message, qm->len,
+				gaim_im_write(GAIM_IM(cnv), NULL, qm->message,
 						qm->flags, qm->tm);
 				g_free(qm->message);
 				g_free(qm);
@@ -118,7 +118,7 @@
 		else
 			gaim_conversation_set_account(cnv, account);
 
-		gaim_im_write(GAIM_IM(cnv), NULL, qm->message, -1, qm->flags, qm->tm);
+		gaim_im_write(GAIM_IM(cnv), NULL, qm->message, qm->flags, qm->tm);
 
 		g_free(qm->message);
 		g_free(qm);
@@ -228,10 +228,10 @@
 	gaim_setup_imhtml(awaytext);
 	gtk_widget_show(awaytext);
 	buf = stylize(a->message, BUF_LONG);
-	gtk_imhtml_append_text(GTK_IMHTML(awaytext), buf, -1, GTK_IMHTML_NO_TITLE |
+	gtk_imhtml_append_text(GTK_IMHTML(awaytext), buf, GTK_IMHTML_NO_TITLE |
 			GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
 	g_free(buf);
-	gtk_imhtml_append_text(GTK_IMHTML(awaytext), "<BR>", -1,
+	gtk_imhtml_append_text(GTK_IMHTML(awaytext), "<BR>",
 			GTK_IMHTML_NO_TITLE | GTK_IMHTML_NO_COMMENTS |
 			GTK_IMHTML_NO_SCROLL);
 
--- a/src/connection.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/connection.h	Sat Sep 27 19:17:21 2003 +0000
@@ -35,9 +35,10 @@
  */
 typedef enum
 {
-	GAIM_CONNECTION_HTML      = 0x0001, /**< Connection sends/receives in 'HTML'. */
-	GAIM_CONNECTION_NO_BGCOLOR = 0x0002, /**< Connection does not send/recieve background colors. */
-	GAIM_CONNECTION_AUTO_RESP = 0x0004  /**< Send auto responses when away.       */
+	GAIM_CONNECTION_HTML       = 0x0001, /**< Connection sends/receives in 'HTML'. */
+	GAIM_CONNECTION_NO_BGCOLOR = 0x0002, /**< Connection does not send/recieve
+					           background colors.                  */
+	GAIM_CONNECTION_AUTO_RESP  = 0x0004  /**< Send auto responses when away.       */
 } GaimConnectionFlags;
 
 typedef enum
--- a/src/conversation.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/conversation.c	Sat Sep 27 19:17:21 2003 +0000
@@ -21,6 +21,7 @@
 #include "blist.h"
 #include "conversation.h"
 #include "debug.h"
+#include "imgstore.h"
 #include "notify.h"
 #include "prefs.h"
 #include "prpl.h"
@@ -180,8 +181,6 @@
 	GaimConnection *gc;
 	GaimConversationUiOps *ops;
 	char *buf, *buf2, *buffy = NULL;
-	gulong length = 0;
-	gboolean binary = FALSE;
 	int plugin_return;
 	int limit;
 	int err = 0;
@@ -261,133 +260,34 @@
 						 gaim_conversation_get_name(conv), &buffy);
 
 		if (buffy != NULL) {
-			int imflags = 0;
-
-			if (conv->u.im->images != NULL) {
-				int id = 0, offset = 0;
-				char *bigbuf = NULL;
-				GSList *tmplist;
-
-				for (tmplist = conv->u.im->images;
-					 tmplist != NULL;
-					 tmplist = tmplist->next) {
-
-					char *img_filename = (char *)tmplist->data;
-					FILE *imgfile;
-					char *filename, *c;
-					struct stat st;
-					char imgtag[1024];
-
-					id++;
-
-					if (stat(img_filename, &st) != 0) {
-						gaim_debug(GAIM_DEBUG_ERROR, "conversation",
-								   "Could not stat image %s\n",
-								   img_filename);
-
-						continue;
-					}
-
-					/*
-					 * Here we check to make sure the user still wants to send
-					 * the image. He may have deleted the <img> tag in which
-					 * case we don't want to send the binary data.
-					 */
-					filename = img_filename;
-
-					while ((c = strchr(filename, '/')) != NULL)
-						filename = c + 1;
-
-					g_snprintf(imgtag, sizeof(imgtag),
-							   "<IMG SRC=\"file://%s\" ID=\"%d\" "
-							   "DATASIZE=\"%d\">",
-							   filename, id, (int)st.st_size);
-
-					if (strstr(buffy, imgtag) == 0) {
-						gaim_debug(GAIM_DEBUG_ERROR, "conversation",
-								   "Not sending image: %s\n", img_filename);
-						continue;
-					}
-
-					if (!binary) {
-						length = strlen(buffy) + strlen("<BINARY></BINARY>");
-						bigbuf = g_malloc(length + 1);
-						g_snprintf(bigbuf,
-								   strlen(buffy) + strlen("<BINARY> ") + 1,
-								   "%s<BINARY>", buffy);
-
-						offset = strlen(buffy) + strlen("<BINARY>");
-						binary = TRUE;
-					}
-
-					g_snprintf(imgtag, sizeof(imgtag),
-							   "<DATA ID=\"%d\" SIZE=\"%d\">",
-							   id, (int)st.st_size);
-
-					length += strlen(imgtag) + st.st_size + strlen("</DATA>");
-
-					bigbuf = g_realloc(bigbuf, length + 1);
-
-					if ((imgfile = fopen(img_filename, "r")) == NULL) {
-						gaim_debug(GAIM_DEBUG_ERROR, "conversation",
-								   "Could not open image %s\n", img_filename);
-						continue;
-					}
-
-					strncpy(bigbuf + offset, imgtag, strlen(imgtag) + 1);
-
-					offset += strlen(imgtag);
-					offset += fread(bigbuf + offset, 1, st.st_size, imgfile);
-
-					fclose(imgfile);
-
-					strncpy(bigbuf + offset, "</DATA>",
-							strlen("</DATA>") + 1);
-
-					offset += strlen("</DATA>");
+			GaimImFlags imflags = 0;
+			GaimMessageFlags msgflags = GAIM_MESSAGE_SEND;
+
+			if (im->images != NULL) {
+				imflags |= GAIM_IM_IMAGES;
+				msgflags |= GAIM_MESSAGE_IMAGES;
+			}
+
+			err = serv_send_im(gc, gaim_conversation_get_name(conv),
+							    buffy, imflags);
+
+			if (err > 0)
+				gaim_im_write(im, NULL, buf, msgflags, time(NULL));
+
+			if (im->images != NULL) {
+				GSList *tempy;
+				int image;
+
+				for (tempy = im->images;
+					 tempy != NULL;
+					 tempy = tempy->next) {
+
+					image = GPOINTER_TO_INT(tempy->data);
+					gaim_imgstore_unref(image);
 				}
 
-				if (binary) {
-					strncpy(bigbuf + offset, "</BINARY>",
-							strlen("<BINARY>") + 1);
-
-					err = serv_send_im(gc, gaim_conversation_get_name(conv),
-									   bigbuf, length, imflags);
-				}
-				else
-					err = serv_send_im(gc, gaim_conversation_get_name(conv),
-									   buffy, -1, imflags);
-
-				if (err > 0) {
-					GSList *tempy;
-
-					for (tempy = conv->u.im->images;
-						 tempy != NULL;
-						 tempy = tempy->next) {
-
-						g_free(tempy->data);
-					}
-
-					g_slist_free(conv->u.im->images);
-					conv->u.im->images = NULL;
-
-					if (binary)
-						gaim_im_write(im, NULL, bigbuf, length,
-									  GAIM_MESSAGE_SEND, time(NULL));
-					else
-						gaim_im_write(im, NULL, buffy, -1, GAIM_MESSAGE_SEND,
-									  time(NULL));
-				}
-
-				if (binary)
-					g_free(bigbuf);
-			}
-			else {
-				err = serv_send_im(gc, gaim_conversation_get_name(conv),
-								   buffy, -1, imflags);
-
-				if (err > 0)
-					gaim_im_write(im, NULL, buf, -1, GAIM_MESSAGE_SEND, time(NULL));
+				g_slist_free(im->images);
+				im->images = NULL;
 			}
 
 			gaim_signal_emit(gaim_conversations_get_handle(), "sent-im-msg",
@@ -1040,14 +940,18 @@
 	conversations = g_list_remove(conversations, conv);
 
 	if (conv->type == GAIM_CONV_IM) {
-		GSList *snode;
+		GSList *tempy;
+		int image;
 
 		gaim_im_stop_typing_timeout(conv->u.im);
 		gaim_im_stop_type_again_timeout(conv->u.im);
 
-		for (snode = conv->u.im->images; snode != NULL; snode = snode->next) {
-			if (snode->data != NULL)
-				g_free(snode->data);
+		for (tempy = conv->u.im->images;
+			 tempy != NULL;
+			 tempy = tempy->next) {
+
+			image = GPOINTER_TO_INT(tempy->data);
+			gaim_imgstore_unref(image);
 		}
 
 		g_slist_free(conv->u.im->images);
@@ -1432,7 +1336,7 @@
 
 void
 gaim_conversation_write(GaimConversation *conv, const char *who,
-						const char *message, size_t length, GaimMessageFlags flags,
+						const char *message, GaimMessageFlags flags,
 						time_t mtime)
 {
 	GaimPluginProtocolInfo *prpl_info = NULL;
@@ -1504,7 +1408,7 @@
 		}
 	}
 
-	ops->write_conv(conv, who, message, length, flags, mtime);
+	ops->write_conv(conv, who, message, flags, mtime);
 
 	win = gaim_conversation_get_window(conv);
 
@@ -1710,7 +1614,7 @@
 
 void
 gaim_im_write(GaimIm *im, const char *who, const char *message,
-			  size_t len, GaimMessageFlags flags, time_t mtime)
+			  GaimMessageFlags flags, time_t mtime)
 {
 	GaimConversation *c;
 
@@ -1721,9 +1625,9 @@
 
 	/* Raise the window, if specified in prefs. */
 	if (c->ui_ops != NULL && c->ui_ops->write_im != NULL)
-		c->ui_ops->write_im(c, who, message, len, flags, mtime);
+		c->ui_ops->write_im(c, who, message, flags, mtime);
 	else
-		gaim_conversation_write(c, who, message, -1, flags, mtime);
+		gaim_conversation_write(c, who, message, flags, mtime);
 }
 
 void
@@ -1943,7 +1847,7 @@
 	if (conv->ui_ops != NULL && conv->ui_ops->write_chat != NULL)
 		conv->ui_ops->write_chat(conv, who, message, flags, mtime);
 	else
-		gaim_conversation_write(conv, who, message, -1, flags, mtime);
+		gaim_conversation_write(conv, who, message, flags, mtime);
 }
 
 void
@@ -1986,7 +1890,7 @@
 					   _("%s [<I>%s</I>] entered the room."),
 					   user, extra_msg);
 
-		gaim_conversation_write(conv, NULL, tmp, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+		gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL));
 	}
 
 	gaim_signal_emit(gaim_conversations_get_handle(),
@@ -2070,7 +1974,7 @@
 		g_snprintf(tmp, sizeof(tmp),
 				   _("%s is now known as %s"), old_user, new_user);
 
-		gaim_conversation_write(conv, NULL, tmp, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+		gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL));
 	}
 }
 
@@ -2114,7 +2018,7 @@
 		else
 			g_snprintf(tmp, sizeof(tmp), _("%s left the room."), user);
 
-		gaim_conversation_write(conv, NULL, tmp, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+		gaim_conversation_write(conv, NULL, tmp, GAIM_MESSAGE_SYSTEM, time(NULL));
 	}
 
 	gaim_signal_emit(gaim_conversations_get_handle(), "chat-buddy-left",
@@ -2189,7 +2093,7 @@
 
 			g_snprintf(tmp, sizeof(tmp), _(" left the room (%s)."), reason);
 
-			gaim_conversation_write(conv, NULL, tmp, -1,
+			gaim_conversation_write(conv, NULL, tmp,
 									GAIM_MESSAGE_SYSTEM, time(NULL));
 		}
 	}
--- a/src/conversation.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/conversation.h	Sat Sep 27 19:17:21 2003 +0000
@@ -103,15 +103,15 @@
  */
 typedef enum
 {
-	GAIM_MESSAGE_SEND      = 0x0001, /**< Outgoing message.     */
-	GAIM_MESSAGE_RECV      = 0x0002, /**< Incoming message.     */
-	GAIM_MESSAGE_SYSTEM    = 0x0004, /**< System message.       */
-	GAIM_MESSAGE_AUTO_RESP = 0x0008, /**< Auto response.        */
-	GAIM_MESSAGE_COLORIZE  = 0x0010, /**< Colorize nicks.       */
-	GAIM_MESSAGE_NICK      = 0x0020, /**< Contains your nick.   */
-	GAIM_MESSAGE_NO_LOG    = 0x0040, /**< Do not log.           */
-	GAIM_MESSAGE_WHISPER   = 0x0080  /**< Whispered message.    */
-
+	GAIM_MESSAGE_SEND      = 0x0001, /**< Outgoing message.        */
+	GAIM_MESSAGE_RECV      = 0x0002, /**< Incoming message.        */
+	GAIM_MESSAGE_SYSTEM    = 0x0004, /**< System message.          */
+	GAIM_MESSAGE_AUTO_RESP = 0x0008, /**< Auto response.           */
+	GAIM_MESSAGE_COLORIZE  = 0x0010, /**< Colorize nicks.          */
+	GAIM_MESSAGE_NICK      = 0x0020, /**< Contains your nick.      */
+	GAIM_MESSAGE_NO_LOG    = 0x0040, /**< Do not log.              */
+	GAIM_MESSAGE_WHISPER   = 0x0080, /**< Whispered message.       */
+	GAIM_MESSAGE_IMAGES    = 0x0100  /**< Message contains images. */
 } GaimMessageFlags;
 
 #include "account.h"
@@ -156,9 +156,9 @@
 	void (*write_chat)(GaimConversation *conv, const char *who,
 					   const char *message, GaimMessageFlags flags, time_t mtime);
 	void (*write_im)(GaimConversation *conv, const char *who,
-					 const char *message, size_t len, GaimMessageFlags flags, time_t mtime);
+					 const char *message, GaimMessageFlags flags, time_t mtime);
 	void (*write_conv)(GaimConversation *conv, const char *who,
-					   const char *message, size_t length, GaimMessageFlags flags,
+					   const char *message, GaimMessageFlags flags,
 					   time_t mtime);
 
 	void (*chat_add_user)(GaimConversation *conv, const char *user);
@@ -754,7 +754,6 @@
  * @param conv    The conversation.
  * @param who     The user who sent the message.
  * @param message The message.
- * @param length  The length of the message.
  * @param flags   The message flags.
  * @param mtime   The time the message was sent.
  *
@@ -762,7 +761,7 @@
  * @see gaim_chat_write()
  */
 void gaim_conversation_write(GaimConversation *conv, const char *who,
-							 const char *message, size_t length, GaimMessageFlags flags,
+							 const char *message, GaimMessageFlags flags,
 							 time_t mtime);
 
 /**
@@ -921,19 +920,14 @@
 /**
  * Writes to an IM.
  *
- * The @a len parameter is used for writing binary data, such as an
- * image. If @c message is text, specify -1 for @a len.
- *
  * @param im      The IM.
  * @param who     The user who sent the message.
  * @param message The message to write.
- * @param len     The length of the message, or -1 to specify the length
- *                of @a message.
  * @param flags   The message flags.
  * @param mtime   The time the message was sent.
  */
 void gaim_im_write(GaimIm *im, const char *who,
-				   const char *message, size_t len, GaimMessageFlags flags, time_t mtime);
+				   const char *message, GaimMessageFlags flags, time_t mtime);
 
 /**
  * Sends a message to this IM conversation.
--- a/src/dialogs.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/dialogs.c	Sat Sep 27 19:17:21 2003 +0000
@@ -1874,19 +1874,19 @@
 
 	if (gaim_prefs_get_bool("/gaim/gtk/conversations/show_urls_as_links")) {
 		linkifyinated = linkify_text(info);
-		gtk_imhtml_append_text(GTK_IMHTML(b->text), linkifyinated, -1, options);
+		gtk_imhtml_append_text(GTK_IMHTML(b->text), linkifyinated, options);
 		g_free(linkifyinated);
 	} else
-		gtk_imhtml_append_text(GTK_IMHTML(b->text), info, -1, options);
+		gtk_imhtml_append_text(GTK_IMHTML(b->text), info, options);
 
 	va_start(ap, info);
 	while ((more_info = va_arg(ap, char *)) != NULL) {
 		if (gaim_prefs_get_bool("/gaim/gtk/conversations/show_urls_as_links")) {
 			linkifyinated = linkify_text(more_info);
-			gtk_imhtml_append_text(GTK_IMHTML(b->text), linkifyinated, -1, options);
+			gtk_imhtml_append_text(GTK_IMHTML(b->text), linkifyinated, options);
 			g_free(linkifyinated);
 		} else
-			gtk_imhtml_append_text(GTK_IMHTML(b->text), more_info, -1, options);
+			gtk_imhtml_append_text(GTK_IMHTML(b->text), more_info, options);
 	}
 	va_end(ap);
 
@@ -3270,7 +3270,7 @@
 		g_string_append(string, buf);
 
 		if (i == 30) {
-			gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, -1, view->options);
+			gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, view->options);
 			g_string_free(string, TRUE);
 			string = g_string_new("");
 			/* you can't have these anymore. if someone clicks on another item while one is
@@ -3282,8 +3282,8 @@
 		}
 
 	}
-	gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, -1, view->options);
-	gtk_imhtml_append_text(GTK_IMHTML(view->layout), "<BR>", -1, view->options);
+	gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, view->options);
+	gtk_imhtml_append_text(GTK_IMHTML(view->layout), "<BR>", view->options);
 
 	gtk_widget_set_sensitive(view->bbox, TRUE);
 	g_signal_handler_disconnect(G_OBJECT(view->window), block);
--- a/src/gtkconv.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtkconv.c	Sat Sep 27 19:17:21 2003 +0000
@@ -30,9 +30,11 @@
 #endif
 
 #include <gdk/gdkkeysyms.h>
+#include <locale.h>
 
 #include "debug.h"
 #include "html.h"
+#include "imgstore.h"
 #include "log.h"
 #include "multi.h"
 #include "notify.h"
@@ -195,29 +197,37 @@
 	GaimConversation *conv;
 	GaimGtkConversation *gtkconv;
 	GaimIm *im;
-	const char *name;
-	const char *filename;
-	char *buf;
-	struct stat st;
+	char *name, *filename;
+	char *buf, *data;
+	size_t size;
+	GError *error;
 	int id;
 
 	conv    = g_object_get_data(G_OBJECT(wid), "user_data");
 	gtkconv = GAIM_GTK_CONVERSATION(conv);
 	im      = GAIM_IM(conv);
-	name    = gtk_file_selection_get_filename(GTK_FILE_SELECTION(wid));
-	id      = g_slist_length(im->images) + 1;
-
-	if (gaim_gtk_check_if_dir(name, GTK_FILE_SELECTION(wid)))
+	name    = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION(wid)));
+
+	if (!name) {
+		gtk_widget_destroy(gtkconv->dialogs.image);
+		gtkconv->dialogs.image = NULL;
+		return;
+	}
+
+	if (gaim_gtk_check_if_dir(name, GTK_FILE_SELECTION(wid))) {
+		g_free(name);
 		return;
-
-	gtk_widget_destroy(wid);
-
-	if (!name)
-		return;
-
-	if (stat(name, &st) != 0) {
-		gaim_debug(GAIM_DEBUG_ERROR, "gtkconv",
-				   "Could not stat image %s\n", name);
+	}
+
+	gtk_widget_destroy(gtkconv->dialogs.image);
+	gtkconv->dialogs.image = NULL;
+
+	if (!g_file_get_contents(name, &data, &size, &error)) {
+		gaim_notify_error(NULL, NULL, error->message, NULL);
+
+		g_error_free(error);
+		g_free(name);
+
 		return;
 	}
 
@@ -225,13 +235,27 @@
 	while (strchr(filename, '/'))
 		filename = strchr(filename, '/') + 1;
 
-	buf = g_strdup_printf("<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
-						  filename, id, (int)st.st_size);
-	im->images = g_slist_append(im->images, g_strdup(name));
-	gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer),
-									 buf, -1);
+	id = gaim_imgstore_add(data, size, filename);
+	g_free(data);
+
+	if (!id) {
+		buf = g_strdup_printf(_("Failed to store image: %s\n"), name);
+		gaim_notify_error(NULL, NULL, buf, NULL);
+
+		g_free(buf);
+		g_free(name);
+
+		return;
+	}
+
+	im->images = g_slist_append(im->images, GINT_TO_POINTER(id));
+
+	buf = g_strdup_printf("<IMG ID=\"%d\" SRC=\"file://%s\">", id, filename);
+	gtk_text_buffer_insert_at_cursor(GTK_TEXT_BUFFER(gtkconv->entry_buffer), buf, -1);
 	g_free(buf);
 
+	g_free(name);
+
 	set_toggle(gtkconv->toolbar.image, FALSE);
 }
 
@@ -2700,7 +2724,7 @@
 			matches = g_list_remove(matches, matches->data);
 		}
 
-		gaim_conversation_write(conv, NULL, addthis, -1, GAIM_MESSAGE_NO_LOG,
+		gaim_conversation_write(conv, NULL, addthis, GAIM_MESSAGE_NO_LOG,
 								time(NULL));
 		gtk_text_buffer_insert_at_cursor(gtkconv->entry_buffer, partial, -1);
 		g_free(addthis);
@@ -4220,7 +4244,7 @@
 
 static void
 gaim_gtkconv_write_im(GaimConversation *conv, const char *who,
-					  const char *message, size_t len, GaimMessageFlags flags,
+					  const char *message, GaimMessageFlags flags,
 					  time_t mtime)
 {
 	GaimGtkConversation *gtkconv;
@@ -4251,7 +4275,7 @@
 
 	gtkconv->u.im->a_virgin = FALSE;
 
-	gaim_conversation_write(conv, who, message, len, flags, mtime);
+	gaim_conversation_write(conv, who, message, flags, mtime);
 }
 
 static void
@@ -4287,12 +4311,12 @@
 		gaim_window_raise(gaim_conversation_get_window(conv));
 	}
 
-	gaim_conversation_write(conv, who, message, -1, flags, mtime);
+	gaim_conversation_write(conv, who, message, flags, mtime);
 }
 
 static void
 gaim_gtkconv_write_conv(GaimConversation *conv, const char *who,
-						const char *message, size_t length, GaimMessageFlags flags,
+						const char *message, GaimMessageFlags flags,
 						time_t mtime)
 {
 	GaimGtkConversation *gtkconv;
@@ -4300,6 +4324,7 @@
 	GaimConnection *gc;
 	int gtk_font_options = 0;
 	GString *log_str;
+	GSList *images = NULL;
 	FILE *fd;
 	char buf[BUF_LONG];
 	char buf2[BUF_LONG];
@@ -4308,9 +4333,7 @@
 	char *str;
 	char *with_font_tag;
 	char *sml_attrib = NULL;
-
-	if(length == -1)
-		length = strlen(message) + 1;
+	size_t length = strlen(message) + 1;
 
 	gtkconv = GAIM_GTK_CONVERSATION(conv);
 	gc = gaim_conversation_get_gc(conv);
@@ -4327,6 +4350,67 @@
 		gaim_window_show(win);
 	}
 
+	if (flags & GAIM_MESSAGE_IMAGES) {
+		GData *attribs;
+		GdkPixbuf *broken;
+		const char *tmp, *start, *end;
+
+		broken = gtk_widget_render_icon(GTK_WIDGET(gtkconv->imhtml),
+				GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON,
+				"gaim-missing-image");
+
+		tmp = message;
+		while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
+			GaimStoredImage *image = NULL;
+			GdkPixbufLoader *loader;
+			GError *error = NULL;
+			char *id = NULL;
+
+			tmp = end + 1;
+
+			id = g_datalist_get_data(&attribs, "id");
+
+			if (id)
+				image = gaim_imgstore_get(atoi(id));
+
+			g_datalist_clear(&attribs);
+
+			if (!image) {
+				g_object_ref(G_OBJECT(broken));
+				images = g_slist_append(images, broken);
+				continue;
+			}
+
+			loader = gdk_pixbuf_loader_new();
+
+			if (!gdk_pixbuf_loader_write(loader, image->data, image->size, &error)) {
+				if (error) {
+					gaim_debug(GAIM_DEBUG_ERROR, "gtkconv",
+							"Failed to make pixbuf for IM Image: %s\n",
+							error->message);
+					g_error_free(error);
+				}
+				g_object_ref(G_OBJECT(broken));
+				images = g_slist_append(images, broken);
+			} else {
+				GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader);
+				if (pixbuf) {
+					if (image->filename)
+						g_object_set_data_full(G_OBJECT(pixbuf), "filename",
+								g_strdup(image->filename), g_free);
+					g_object_ref(G_OBJECT(pixbuf));
+					images = g_slist_append(images, pixbuf);
+				} else {
+					g_object_ref(G_OBJECT(broken));
+					images = g_slist_append(images, broken);
+				}
+			}
+
+			gdk_pixbuf_loader_close(loader, NULL);
+		}
+
+		g_object_unref(G_OBJECT(broken));
+	}
 
 	if(time(NULL) > mtime + 20*60) /* show date if older than 20 minutes */
 		strftime(mdate, sizeof(mdate), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
@@ -4367,7 +4451,7 @@
 				   "<!--(%s) --><B>%s</B><BR>",
 				   mdate, message);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, -1, 0);
+		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml), buf2, 0, images);
 
 		if (gaim_prefs_get_bool("/gaim/gtk/logging/strip_html")) {
 			char *t1 = strip_html(buf);
@@ -4419,7 +4503,7 @@
 				   "<B><FONT COLOR=\"#777777\">%s</FONT></B><BR>",
 				   message);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf, -1, 0);
+		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml), buf, 0, images);
 	}
 	else {
 		char *new_message = g_memdup(message, length);
@@ -4498,7 +4582,7 @@
 
 		g_free(str);
 
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), buf2, -1, 0);
+		gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml), buf2, 0, images);
 
 		if(gc){
 			char *pre = g_strdup_printf("<font %s>", sml_attrib ? sml_attrib : "");
@@ -4518,12 +4602,11 @@
 		else
 			with_font_tag = g_memdup(new_message, length);
 
-		log_str = gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml),
-										 with_font_tag, length, gtk_font_options);
-
-		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", -1, 0);
-
-		/* XXX This needs to be updated for the new length argument. */
+		log_str = gtk_imhtml_append_text_with_images(GTK_IMHTML(gtkconv->imhtml),
+										 with_font_tag, gtk_font_options, images);
+
+		gtk_imhtml_append_text(GTK_IMHTML(gtkconv->imhtml), "<BR>", 0);
+
 		if (gaim_prefs_get_bool("/gaim/gtk/logging/strip_html")) {
 			char *t1, *t2;
 
@@ -4553,7 +4636,6 @@
 			g_free(t2);
 		}
 
-		/* XXX This needs to be updated for the new length argument. */
 		if (gaim_conversation_is_logging(conv)) {
 			char *t1, *t2;
 			char nm[256];
@@ -4597,6 +4679,17 @@
 
 	if(sml_attrib)
 		g_free(sml_attrib);
+
+	if (images) {
+		GSList *tmp = images;
+		GdkPixbuf *pixbuf;
+		while (tmp) {
+			pixbuf = tmp->data;
+			g_object_unref(G_OBJECT(pixbuf));
+			tmp = tmp->next;
+		}
+		g_slist_free(images);
+	}
 }
 
 static void
--- a/src/gtkdebug.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtkdebug.c	Sat Sep 27 19:17:21 2003 +0000
@@ -319,7 +319,7 @@
 
 		g_free(cat_s);
 
-		gtk_imhtml_append_text(GTK_IMHTML(debug_win->text), s, -1, 0);
+		gtk_imhtml_append_text(GTK_IMHTML(debug_win->text), s, 0);
 
 		g_free(s);
 	}
--- a/src/gtkimhtml.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtkimhtml.c	Sat Sep 27 19:17:21 2003 +0000
@@ -853,6 +853,7 @@
 	VALID_OPT_TAG ("SPAN");
 	VALID_TAG ("/SPAN");
 	VALID_TAG ("BR/"); /* hack until gtkimhtml handles things better */
+	VALID_TAG ("IMG");
 
 	if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) {
 		gchar *e = strstr (string + strlen("!--"), "-->");
@@ -995,10 +996,10 @@
 
 
 
-GString* gtk_imhtml_append_text (GtkIMHtml        *imhtml,
-				 const gchar      *text,
-				 gint              len,
-				 GtkIMHtmlOptions  options)
+GString* gtk_imhtml_append_text_with_images (GtkIMHtml        *imhtml,
+					     const gchar      *text,
+					     GtkIMHtmlOptions  options,
+					     GSList           *images)
 {
 	gint pos = 0;
 	GString *str = NULL;
@@ -1009,6 +1010,7 @@
 	gchar *tag;
 	gchar *url = NULL;
 	gchar *bg = NULL;
+	gint len;
 	gint tlen, smilelen, wpos=0;
 	gint type;
 	const gchar *c;
@@ -1033,11 +1035,9 @@
 	g_return_val_if_fail (imhtml != NULL, NULL);
 	g_return_val_if_fail (GTK_IS_IMHTML (imhtml), NULL);
 	g_return_val_if_fail (text != NULL, NULL);
-	g_return_val_if_fail (len != 0, NULL);
-	
+
 	c = text;
-	if (len == -1)
-		len = strlen(text);
+	len = strlen(text);
 	ws = g_malloc(len + 1);
 	ws[0] = 0;
 
@@ -1154,7 +1154,7 @@
 					ws[wpos] = '\n';
 					wpos++;
 					NEW_BIT (NEW_TEXT_BIT);
-					break;	
+					break;
 				case 26:        /* HR */
 				case 42:        /* HR (opt) */
 					ws[wpos++] = '\n';
@@ -1196,16 +1196,8 @@
 				case 37:	/* FONT */
 				case 38:	/* HEAD */
 				case 39:	/* /HEAD */
-					break; 
-				case 40:        /* BINARY */
-					NEW_BIT (NEW_TEXT_BIT);
-					while (pos < len && g_ascii_strncasecmp("</BINARY>", c, strlen("</BINARY>"))) {
-						c++;
-						pos++;
-					}
-					c = c - tlen; /* Because it will add this later */
-					break;
-				case 41:        /* /BINARY */
+				case 40:	/* BINARY */
+				case 41:	/* /BINARY */
 					break;
 				case 43:	/* FONT (opt) */
 					{
@@ -1290,53 +1282,25 @@
 					}
 					break;
 				case 46:	/* IMG (opt) */
+				case 59:	/* IMG */
 					{
-						gchar *src = gtk_imhtml_get_html_opt (tag, "SRC=");
-						gchar *id = gtk_imhtml_get_html_opt (tag, "ID=");
-						gchar *datasize = gtk_imhtml_get_html_opt (tag, "DATASIZE=");
-						gint im_len = datasize?atoi(datasize):0;
-
-						if (src && id && im_len && im_len <= len - pos) {
-							/* This is an embedded IM image, or is it? */
-							char *tmp = NULL;
-							const char *alltext;
-							guchar *imagedata = NULL;
-
-							GdkPixbufLoader *load;
-							GdkPixbuf *imagepb = NULL;
-							GError *error = NULL;
-
-							tmp = g_strdup_printf("<DATA ID=\"%s\" SIZE=\"%s\">", id, datasize);
-							alltext = strstr(c, tmp);
-							imagedata = g_memdup(alltext + strlen(tmp), im_len);
-
-							g_free(tmp);
+						GdkPixbuf *img = NULL;
+						const gchar *filename = NULL;
 
-							load = gdk_pixbuf_loader_new();
-							if (!gdk_pixbuf_loader_write(load, imagedata, im_len, &error)){
-								fprintf(stderr, "IM Image corrupted or unreadable.: %s\n", error->message);
-							} else {
-								imagepb = gdk_pixbuf_loader_get_pixbuf(load);
-								if (imagepb) {
-									scalable = gtk_imhtml_image_new(imagepb, g_strdup(src));
-									NEW_BIT(NEW_SCALABLE_BIT);
-									g_object_unref(imagepb);
-								}
-							}
+						if (images && images->data) {
+							img = images->data;
+							images = images->next;
+							filename = g_object_get_data(G_OBJECT(img), "filename");
+							g_object_ref(G_OBJECT(img));
+						} else {
+							img = gtk_widget_render_icon(GTK_WIDGET(imhtml),
+							    GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON,
+							    "gtkimhtml-missing-image");
+						}
 
-							gdk_pixbuf_loader_close(load, NULL);
-
-
-							g_free(imagedata);
-							g_free(id);
-							g_free(datasize);
-							g_free(src);
-
-							break;
-						}
-						g_free(id);
-						g_free(datasize);
-						g_free(src);
+						scalable = gtk_imhtml_image_new(img, filename);
+						NEW_BIT(NEW_SCALABLE_BIT);
+						g_object_unref(G_OBJECT(img));
 					}
 				case 47:	/* P (opt) */
 				case 48:	/* H3 (opt) */
@@ -1346,7 +1310,7 @@
 				case 56:	/* SPAN */
 				case 57:	/* /SPAN */
 					break;
-				case 59:	/* comment */
+				case 60:	/* comment */
 					NEW_BIT (NEW_TEXT_BIT);
 					if (imhtml->show_comments)
 						wpos = g_snprintf (ws, len, "%s", tag);
@@ -1533,7 +1497,7 @@
 }
 
 /* GtkIMHtmlScalable, gtk_imhtml_image, gtk_imhtml_hr */
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, gchar *filename)
+GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename)
 {
 	GtkIMHtmlImage *im_image = g_malloc(sizeof(GtkIMHtmlImage));
 	GtkImage *image = GTK_IMAGE(gtk_image_new_from_pixbuf(img));
@@ -1547,7 +1511,7 @@
 	im_image->width = gdk_pixbuf_get_width(img);
 	im_image->height = gdk_pixbuf_get_height(img);
 	im_image->mark = NULL;
-	im_image->filename = filename;
+	im_image->filename = filename ? g_strdup(filename) : NULL;
 
 	g_object_ref(img);
 	return GTK_IMHTML_SCALABLE(im_image);
@@ -1668,7 +1632,8 @@
 {
 	GtkWidget *sel = gtk_file_selection_new(_("Save Image"));
 
-	gtk_file_selection_set_filename(GTK_FILE_SELECTION(sel), image->filename);
+	if (image->filename)
+		gtk_file_selection_set_filename(GTK_FILE_SELECTION(sel), image->filename);
 	g_object_set_data(G_OBJECT(sel), "GtkIMHtmlImage", image);
 	g_signal_connect(G_OBJECT(GTK_FILE_SELECTION(sel)->ok_button), "clicked",
 					 G_CALLBACK(write_img_to_file), sel);
@@ -1718,7 +1683,8 @@
 	GtkIMHtmlImage *image = (GtkIMHtmlImage *)scale;
 
 	g_object_unref(image->pixbuf);
-	g_free(image->filename);
+	if (image->filename)
+		g_free(image->filename);
 	g_free(scale);
 }
 
--- a/src/gtkimhtml.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtkimhtml.h	Sat Sep 27 19:17:21 2003 +0000
@@ -144,15 +144,20 @@
 
 void       gtk_imhtml_show_comments    (GtkIMHtml *imhtml, gboolean show);
 
-GString*   gtk_imhtml_append_text      (GtkIMHtml *imhtml,
-					const gchar *text, gint len, GtkIMHtmlOptions  options);
+#define    gtk_imhtml_append_text(x, y, z) \
+ gtk_imhtml_append_text_with_images(x, y, z, NULL)
+
+GString*   gtk_imhtml_append_text_with_images (GtkIMHtml *imhtml,
+					       const gchar *text,
+					       GtkIMHtmlOptions options,
+					       GSList *images);
 
 void       gtk_imhtml_clear            (GtkIMHtml *imhtml);
 void       gtk_imhtml_page_up          (GtkIMHtml *imhtml);
 void       gtk_imhtml_page_down        (GtkIMHtml *imhtml);
 
 GtkIMHtmlScalable *gtk_imhtml_scalable_new();
-GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, gchar *filename);
+GtkIMHtmlScalable *gtk_imhtml_image_new(GdkPixbuf *img, const gchar *filename);
 void gtk_imhtml_image_free(GtkIMHtmlScalable *);
 void gtk_imhtml_image_scale(GtkIMHtmlScalable *, int, int);
 void gtk_imhtml_image_add_to(GtkIMHtmlScalable *, GtkIMHtml *, GtkTextIter *);
--- a/src/gtknotify.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtknotify.c	Sat Sep 27 19:17:21 2003 +0000
@@ -329,7 +329,7 @@
 	options ^= GTK_IMHTML_NO_NEWLINE;
 	options ^= GTK_IMHTML_NO_SCROLL;
 
-	gtk_imhtml_append_text(GTK_IMHTML(imhtml), text, -1, options);
+	gtk_imhtml_append_text(GTK_IMHTML(imhtml), text, options);
 
 	/* Show the window */
 	gtk_widget_show(window);
--- a/src/gtkpounce.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtkpounce.c	Sat Sep 27 19:17:21 2003 +0000
@@ -859,10 +859,10 @@
 			if (conv == NULL)
 				conv = gaim_conversation_new(GAIM_CONV_IM, account, pouncee);
 
-			gaim_conversation_write(conv, NULL, message, -1,
+			gaim_conversation_write(conv, NULL, message,
 									GAIM_MESSAGE_SEND, time(NULL));
 
-			serv_send_im(account->gc, (char *)pouncee, (char *)message, -1, 0);
+			serv_send_im(account->gc, (char *)pouncee, (char *)message, 0);
 		}
 	}
 
--- a/src/gtkprefs.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/gtkprefs.c	Sat Sep 27 19:17:21 2003 +0000
@@ -2114,9 +2114,9 @@
 	gtk_imhtml_clear(GTK_IMHTML(away_text));
 	strncpy(buffer, am->message, BUF_LONG);
 	tmp = stylize(buffer, BUF_LONG);
-	gtk_imhtml_append_text(GTK_IMHTML(away_text), tmp, -1, GTK_IMHTML_NO_TITLE |
+	gtk_imhtml_append_text(GTK_IMHTML(away_text), tmp, GTK_IMHTML_NO_TITLE |
 			       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
-	gtk_imhtml_append_text(GTK_IMHTML(away_text), "<BR>", -1, GTK_IMHTML_NO_TITLE |
+	gtk_imhtml_append_text(GTK_IMHTML(away_text), "<BR>", GTK_IMHTML_NO_TITLE |
 			       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
 	g_free(tmp);
 	g_value_unset (&val);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imgstore.c	Sat Sep 27 19:17:21 2003 +0000
@@ -0,0 +1,140 @@
+/**
+ * @file imgstore.h IM Image Store API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Robert McQueen <robot101@debian.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
+ * 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 <glib.h>
+#include <debug.h>
+#include <imgstore.h>
+
+static GSList *imgstore = NULL;
+static int nextid = 0;
+
+typedef struct
+{
+	int id;
+	int refcount;
+	GaimStoredImage *img;
+} GaimStoredImagePriv;
+
+/* private functions */
+
+static GaimStoredImagePriv *gaim_imgstore_get_priv(int id) {
+	GSList *tmp = imgstore;
+	GaimStoredImagePriv *priv = NULL;
+
+	g_return_val_if_fail(id > 0, NULL);
+
+	while (tmp && !priv) {
+		GaimStoredImagePriv *tmp_priv = tmp->data;
+
+		if (tmp_priv->id == id)
+			priv = tmp_priv;
+
+		tmp = tmp->next;
+	}
+
+	if (!priv)
+		gaim_debug(GAIM_DEBUG_ERROR, "imgstore", "failed to find image id %d\n", id);
+
+	return priv;
+}
+
+static void gaim_imgstore_free_priv(GaimStoredImagePriv *priv) {
+	GaimStoredImage *img = NULL;
+
+	g_return_if_fail(priv != NULL);
+
+	img = priv->img;
+	if (img) {
+		if (img->data)
+			g_free(img->data);
+		if (img->filename)
+			g_free(img->filename);
+		g_free(img);
+	}
+
+	gaim_debug(GAIM_DEBUG_INFO, "imgstore", "freed image id %d\n", priv->id);
+
+	g_free(priv);
+
+	imgstore = g_slist_remove(imgstore, priv);
+}
+
+/* public functions */
+
+int gaim_imgstore_add(const void *data, size_t size, const char *filename) {
+	GaimStoredImagePriv *priv;
+	GaimStoredImage *img;
+
+	g_return_val_if_fail(data != NULL, 0);
+	g_return_val_if_fail(size > 0, 0);
+
+	img = g_new0(GaimStoredImage, 1);
+	img->data = g_memdup(data, size);
+	img->size = size;
+	img->filename = g_strdup(filename);
+
+	priv = g_new0(GaimStoredImagePriv, 1);
+	priv->id = ++nextid;
+	priv->refcount = 1;
+	priv->img = img;
+
+	imgstore = g_slist_append(imgstore, priv);
+	gaim_debug(GAIM_DEBUG_INFO, "imgstore", "added image id %d\n", priv->id);
+
+	return priv->id;
+}
+
+GaimStoredImage *gaim_imgstore_get(int id) {
+	GaimStoredImagePriv *priv = gaim_imgstore_get_priv(id);
+
+	g_return_val_if_fail(priv != NULL, NULL);
+
+	gaim_debug(GAIM_DEBUG_INFO, "imgstore", "retrieved image id %d\n", priv->id);
+
+	return priv->img;
+}
+
+void gaim_imgstore_ref(int id) {
+	GaimStoredImagePriv *priv = gaim_imgstore_get_priv(id);
+
+	g_return_if_fail(priv != NULL);
+
+	(priv->refcount)++;
+
+	gaim_debug(GAIM_DEBUG_INFO, "imgstore", "referenced image id %d (count now %d)\n", priv->id, priv->refcount);
+}
+
+void gaim_imgstore_unref(int id) {
+	GaimStoredImagePriv *priv = gaim_imgstore_get_priv(id);
+
+	g_return_if_fail(priv != NULL);
+	g_return_if_fail(priv->refcount > 0);
+
+	(priv->refcount)--;
+
+	gaim_debug(GAIM_DEBUG_INFO, "imgstore", "unreferenced image id %d (count now %d)\n", priv->id, priv->refcount);
+
+	if (priv->refcount == 0)
+		gaim_imgstore_free_priv(priv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imgstore.h	Sat Sep 27 19:17:21 2003 +0000
@@ -0,0 +1,88 @@
+/**
+ * @file imgstore.h IM Image Store API
+ * @ingroup core
+ *
+ * gaim
+ *
+ * Copyright (C) 2003 Robert McQueen <robot101@debian.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
+ * 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
+ *
+ */
+#ifndef _GAIM_IMGSTORE_H_
+#define _GAIM_IMGSTORE_H_
+
+/**
+ * Stored image
+ *
+ * Represents a single IM image awaiting display and/or transmission.
+ */
+typedef struct
+{
+	char *data;		/**< The image data.		*/
+	size_t size;		/**< The image data's size.	*/
+	char *filename;		/**< The filename (for the UI)	*/
+} GaimStoredImage;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Add an image to the store. The caller owns a reference
+ * to the image in the store, and must dereference the image
+ * with gaim_imgstore_unref for it to be freed.
+ *
+ * @param data		Pointer to the image data.
+ * @param size		Image data's size.
+ * @param filename	Filename associated with image.
+
+ * @return ID for the image.
+ */
+int gaim_imgstore_add(const void *data, size_t size, const char *filename);
+
+/**
+ * Retrieve an image from the store. The caller does not own a
+ * reference to the image.
+ *
+ * @param id		The ID for the image.
+ *
+ * @return A pointer to the requested image, or NULL if it was not found.
+ */
+GaimStoredImage *gaim_imgstore_get(int id);
+
+/**
+ * Increment the reference count for an image in the store. The
+ * image will be removed from the store when the reference count
+ * is zero.
+ *
+ * @param id		The ID for the image.
+ */
+void gaim_imgstore_ref(int id);
+
+/**
+ * Decrement the reference count for an image in the store. The
+ * image will be removed from the store when the reference count
+ * is zero.
+ *
+ * @param id		The ID for the image.
+ */
+void gaim_imgstore_unref(int id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GAIM_IMGSTORE_H_ */
--- a/src/plugin.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/plugin.c	Sat Sep 27 19:17:21 2003 +0000
@@ -779,7 +779,6 @@
 gaim_plugins_probe(const char *ext)
 {
 #ifdef GAIM_PLUGINS
-	GList *l, *l_next;
 	GDir *dir;
 	const gchar *file;
 	gchar *path;
--- a/src/protocols/gg/gg.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/gg/gg.c	Sat Sep 27 19:17:21 2003 +0000
@@ -1,6 +1,6 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 7326 2003-09-07 23:47:00Z chipx86 $
+ * $Id: gg.c 7538 2003-09-27 19:17:21Z thekingant $
  *
  * Copyright (C) 2001 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  *
@@ -315,7 +315,7 @@
 			imsg = charset_convert(e->event.msg.message, "CP1250", "UTF-8");
 			strip_linefeed(imsg);
 			/* e->event.msg.time - we don't know what this time is for */
-			serv_got_im(gc, user, imsg, 0, time(NULL), -1);
+			serv_got_im(gc, user, imsg, 0, time(NULL));
 			g_free(imsg);
 		}
 		break;
@@ -560,7 +560,7 @@
 	g_free(gc->proto_data);
 }
 
-static int agg_send_im(GaimConnection *gc, const char *who, const char *msg, int len, GaimImFlags flags)
+static int agg_send_im(GaimConnection *gc, const char *who, const char *msg, GaimImFlags flags)
 {
 	struct agg_data *gd = (struct agg_data *)gc->proto_data;
 	gchar *imsg;
--- a/src/protocols/irc/cmds.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/irc/cmds.c	Sat Sep 27 19:17:21 2003 +0000
@@ -40,7 +40,7 @@
 
 	buf = g_strdup_printf(_("Unknown command: %s"), cmd);
 	if (gaim_conversation_get_type(convo) == GAIM_CONV_IM)
-		gaim_im_write(GAIM_IM(convo), "", buf, -1, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+		gaim_im_write(GAIM_IM(convo), "", buf, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 	else
 		gaim_chat_write(GAIM_CHAT(convo), "", buf, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 	g_free(buf);
@@ -145,7 +145,7 @@
 						    "AWAY JOIN ME MODE<BR>"
 						    "MSG NICK OPERWALL PING<BR>"
 						    "QUERY QUIT QUOTE UMODE<BR>"
-						    "WALLOPS WHOIS"), -1, GAIM_MESSAGE_NO_LOG, time(NULL));
+						    "WALLOPS WHOIS"), GAIM_MESSAGE_NO_LOG, time(NULL));
 	}
 
 	return 0;
@@ -413,7 +413,7 @@
 		gc = gaim_account_get_connection(irc->account);
 		irc_cmd_privmsg(irc, cmd, target, args);
 		gaim_im_write(GAIM_IM(convo), gaim_connection_get_display_name(gc),
-			      args[1], -1, GAIM_MESSAGE_SEND, time(NULL));
+			      args[1], GAIM_MESSAGE_SEND, time(NULL));
 	}
 
 	return 0;
--- a/src/protocols/irc/irc.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/irc/irc.c	Sat Sep 27 19:17:21 2003 +0000
@@ -43,7 +43,7 @@
 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, GaimImFlags flags);
+static int irc_im_send(GaimConnection *gc, const char *who, const char *what, GaimImFlags 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);
@@ -245,7 +245,7 @@
 	g_free(irc);
 }
 
-static int irc_im_send(GaimConnection *gc, const char *who, const char *what, int len, GaimImFlags flags)
+static int irc_im_send(GaimConnection *gc, const char *who, const char *what, GaimImFlags flags)
 {
 	struct irc_conn *irc = gc->proto_data;
 	const char *args[2];
--- a/src/protocols/irc/msgs.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/irc/msgs.c	Sat Sep 27 19:17:21 2003 +0000
@@ -88,7 +88,7 @@
 
 	gc = gaim_account_get_connection(irc->account);
 	if (gc)
-		serv_got_im(gc, args[1], args[2], GAIM_IM_AUTO_RESP, time(NULL), -1);
+		serv_got_im(gc, args[1], args[2], GAIM_IM_AUTO_RESP, time(NULL));
 }
 
 void irc_msg_badmode(struct irc_conn *irc, const char *name, const char *from, char **args)
@@ -289,7 +289,7 @@
 			if (gaim_conversation_get_type(convo) == GAIM_CONV_CHAT)
 				gaim_chat_write(GAIM_CHAT(convo), "", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 			else
-				gaim_im_write(GAIM_IM(convo), "", msg, -1, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+				gaim_im_write(GAIM_IM(convo), "", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 			g_free(msg);
 			g_free(irc->nameconv);
 			irc->nameconv = NULL;
@@ -369,7 +369,7 @@
 			gaim_chat_write(GAIM_CHAT(convo), args[1], _("no such channel"),
 					GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 		else
-			gaim_im_write(GAIM_IM(convo), args[1], _("User is not logged in"), -1,
+			gaim_im_write(GAIM_IM(convo), args[1], _("User is not logged in"),
 				      GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 	} else {
 		if ((gc = gaim_account_get_connection(irc->account)) == NULL)
@@ -726,7 +726,7 @@
 		if (gaim_conversation_get_type (convo) == GAIM_CONV_CHAT)
 			gaim_chat_write(GAIM_CHAT(convo), "PONG", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 		else
-			gaim_im_write(GAIM_IM(convo), "PONG", msg, -1, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
+			gaim_im_write(GAIM_IM(convo), "PONG", msg, GAIM_MESSAGE_SYSTEM|GAIM_MESSAGE_NO_LOG, time(NULL));
 	} else {
 		gc = gaim_account_get_connection(irc->account);
 		if (!gc) {
@@ -765,9 +765,9 @@
 	}
 
 	if (!gaim_utf8_strcasecmp(args[0], gaim_connection_get_display_name(gc))) {
-		serv_got_im(gc, nick, msg, 0, time(NULL), -1);
+		serv_got_im(gc, nick, msg, 0, time(NULL));
 	} else if (notice) {
-		serv_got_im(gc, nick, msg, 0, time(NULL), -1);
+		serv_got_im(gc, nick, msg, 0, time(NULL));
 	} else {
 		convo = gaim_find_conversation_with_account(args[0], irc->account);
 		if (convo)
--- a/src/protocols/jabber/jabber.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/jabber/jabber.c	Sat Sep 27 19:17:21 2003 +0000
@@ -1481,7 +1481,7 @@
 				jabber_track_convo_thread(gjc, from, thread_id);
 				if (gaim_find_conversation_with_account(from, GJ_GC(gjc)->account))
 					serv_got_im(GJ_GC(gjc), from, m, flags,
-						time_sent, -1);
+						time_sent);
 				else {
 					if(p->from->user) {
 						from = g_strdup_printf("%s@%s", p->from->user,
@@ -1490,7 +1490,7 @@
 						/* server message? */
 						from = g_strdup(p->from->server);
 					}
-					serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1);
+					serv_got_im(GJ_GC(gjc), from, m, flags, time_sent);
 					g_free(from);
 				}
 			}
@@ -2603,7 +2603,7 @@
 	g_free(xhtml);
 }
 
-static int jabber_send_im(GaimConnection *gc, const char *who, const char *message, int len, GaimImFlags flags)
+static int jabber_send_im(GaimConnection *gc, const char *who, const char *message, GaimImFlags flags)
 {
 	xmlnode x, y;
 	char *thread_id = NULL;
--- a/src/protocols/msn/msn.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/msn/msn.c	Sat Sep 27 19:17:21 2003 +0000
@@ -478,7 +478,7 @@
 
 static int
 msn_send_im(GaimConnection *gc, const char *who, const char *message,
-			int len, GaimImFlags flags)
+			GaimImFlags flags)
 {
 	GaimAccount *account = gaim_connection_get_account(gc);
 	MsnSession *session = gc->proto_data;
@@ -525,7 +525,7 @@
 		 */
 		serv_got_typing_stopped(gc, (char *)who);
 		serv_got_im(gc, who, message, flags,
-					time(NULL), -1);
+					time(NULL));
 	}
 
 	return 1;
--- a/src/protocols/msn/switchboard.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/msn/switchboard.c	Sat Sep 27 19:17:21 2003 +0000
@@ -157,7 +157,7 @@
 		}
 
 		if (*buf != '\0' && (conv = gaim_find_conversation_with_account(user, account)) != NULL) {
-			gaim_conversation_write(conv, NULL, buf, -1, GAIM_MESSAGE_SYSTEM,
+			gaim_conversation_write(conv, NULL, buf, GAIM_MESSAGE_SYSTEM,
 									time(NULL));
 		}
 
@@ -352,7 +352,7 @@
 		serv_got_chat_in(gc, gaim_chat_get_id(GAIM_CHAT(swboard->chat)),
 						 servconn->msg_passport, 0, body, time(NULL));
 	else
-		serv_got_im(gc, servconn->msg_passport, body, 0, time(NULL), -1);
+		serv_got_im(gc, servconn->msg_passport, body, 0, time(NULL));
 
 	g_free(body);
 
@@ -633,7 +633,9 @@
 	char *buf;
 	size_t len;
 	int ret;
+#if 0
 	FILE *fp;
+#endif
 
 	g_return_val_if_fail(swboard != NULL, FALSE);
 	g_return_val_if_fail(msg     != NULL, FALSE);
--- a/src/protocols/napster/napster.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/napster/napster.c	Sat Sep 27 19:17:21 2003 +0000
@@ -129,7 +129,7 @@
 }
 
 /* 205 - MSG_CLIENT_PRIVMSG */
-static int nap_send_im(GaimConnection *gc, const char *who, const char *message, int len, GaimImFlags flags)
+static int nap_send_im(GaimConnection *gc, const char *who, const char *message, GaimImFlags flags)
 {
 
 	if ((strlen(message) < 2) || (message[0] != '/' ) || (message[1] == '/')) {
@@ -284,7 +284,7 @@
 
 	case 205: /* MSG_CLIENT_PRIVMSG */
 		res = g_strsplit(buf, " ", 2);
-		serv_got_im(gc, res[0], res[1], 0, time(NULL), -1);
+		serv_got_im(gc, res[0], res[1], 0, time(NULL));
 		g_strfreev(res);
 		break;
 
@@ -305,7 +305,7 @@
 	case 214: /* MSG_SERVER_STATS */
 		res = g_strsplit(buf, " ", 3);
 		buf2 = g_strdup_printf(_("users: %s, files: %s, size: %sGB"), res[0], res[1], res[2]);
-		serv_got_im(gc, "server", buf2, 0, time(NULL), -1);
+		serv_got_im(gc, "server", buf2, 0, time(NULL));
 		g_free(buf2);
 		g_strfreev(res);
 		break;
@@ -343,7 +343,7 @@
 
 	case 404: /* MSG_SERVER_NOSUCH */
 		/* abused by opennap servers to broadcast stuff */
-		serv_got_im(gc, "server", buf, 0, time(NULL), -1);
+		serv_got_im(gc, "server", buf, 0, time(NULL));
 		break;
 
 	case 405: /* MSG_SERVER_JOIN_ACK */
@@ -380,7 +380,7 @@
 
 	case 603: /* MSG_CLIENT_WHOIS */
 		buf2 = g_strdup_printf(_("%s requested your information"), buf);
-		serv_got_im(gc, "server", buf2, 0, time(NULL), -1);
+		serv_got_im(gc, "server", buf2, 0, time(NULL));
 		g_free(buf2);
 		break;
 
@@ -394,16 +394,16 @@
 	case 621:
 	case 622: /* MSG_CLIENT_MOTD */
 		/* also replaces MSG_SERVER_MOTD, so we should display it */
-		serv_got_im(gc, "motd", buf, 0, time(NULL), -1);
+		serv_got_im(gc, "motd", buf, 0, time(NULL));
 		break;
 
 	case 627: /* MSG_CLIENT_WALLOP */
 		/* abused by opennap server maintainers to broadcast stuff */
-		serv_got_im(gc, "wallop", buf, 0, time(NULL), -1);
+		serv_got_im(gc, "wallop", buf, 0, time(NULL));
 		break;
 
 	case 628: /* MSG_CLIENT_ANNOUNCE */
-		serv_got_im(gc, "announce", buf, 0, time(NULL), -1);
+		serv_got_im(gc, "announce", buf, 0, time(NULL));
 		break;
 
 	case 748: /* MSG_SERVER_GHOST */
@@ -416,7 +416,7 @@
 
 	case 751: /* MSG_CLIENT_PING */
 		buf2 = g_strdup_printf(_("%s requested a PING"), buf);
-		serv_got_im(gc, "server", buf2, 0, time(NULL), -1);
+		serv_got_im(gc, "server", buf2, 0, time(NULL));
 		g_free(buf2);
 		/* send back a pong */
 		/* MSG_CLIENT_PONG */
--- a/src/protocols/oscar/oscar.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/oscar/oscar.c	Sat Sep 27 19:17:21 2003 +0000
@@ -32,6 +32,7 @@
 #include "conversation.h"
 #include "debug.h"
 #include "ft.h"
+#include "imgstore.h"
 #include "multi.h"
 #include "notify.h"
 #include "privacy.h"
@@ -270,7 +271,7 @@
 static int gaim_odc_initiate     (aim_session_t *, aim_frame_t *, ...);
 static int gaim_odc_incoming     (aim_session_t *, aim_frame_t *, ...);
 static int gaim_odc_typing       (aim_session_t *, aim_frame_t *, ...);
-static int gaim_update_ui        (aim_session_t *, aim_frame_t *, ...);
+static int gaim_odc_update_ui    (aim_session_t *, aim_frame_t *, ...);
 
 /* for file transfer */
 static int oscar_sendfile_estblsh(aim_session_t *, aim_frame_t *, ...);
@@ -480,7 +481,7 @@
 
 	cnv = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
 	if (cnv)
-		gaim_conversation_write(cnv, NULL, buf, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+		gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
 
 	gaim_conversation_update_progress(cnv, 0);
 
@@ -1912,7 +1913,7 @@
 	if (getpeername(source, &name, &name_len) == 0) {
 		g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
 		dim->connected = TRUE;
-		gaim_conversation_write(cnv, NULL, buf, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+		gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
 	}
 	od->direct_ims = g_slist_append(od->direct_ims, dim);
 	
@@ -2136,7 +2137,7 @@
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING,
 				gaim_odc_typing, 0);
 	aim_conn_addhandler(od->sess, dim->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER,
-			        gaim_update_ui, 0);
+			        gaim_odc_update_ui, 0);
 	for (i = 0; i < (int)strlen(d->ip); i++) {
 		if (d->ip[i] == ':') {
 			port = atoi(&(d->ip[i+1]));
@@ -2267,7 +2268,7 @@
 	}
 
 	/* strip_linefeed(tmp); */
-	serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
+	serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL));
 	g_free(tmp);
 
 	return 1;
@@ -2583,9 +2584,9 @@
 				gchar *uin = g_strdup_printf("%u", args->uin);
 				if (t) { /* This is an offline message */
 					/* I think this timestamp is in UTC, or something */
-					serv_got_im(gc, uin, msg2[0], 0, t, -1);
+					serv_got_im(gc, uin, msg2[0], 0, t);
 				} else { /* This is a message from MacICQ/Miranda */
-					serv_got_im(gc, uin, msg2[0], 0, time(NULL), -1);
+					serv_got_im(gc, uin, msg2[0], 0, time(NULL));
 				}
 				g_free(uin);
 			}
@@ -2595,7 +2596,7 @@
 			if (i >= 2) {
 				gchar *uin = g_strdup_printf("%u", args->uin);
 				gchar *message = g_strdup_printf("<A HREF=\"%s\">%s</A>", msg2[1], msg2[0]);
-				serv_got_im(gc, uin, message, 0, time(NULL), -1);
+				serv_got_im(gc, uin, message, 0, time(NULL));
 				g_free(uin);
 				g_free(message);
 			}
@@ -3057,6 +3058,7 @@
 	return 1;
 }
 
+#if 0
 static char *images(int flags) {
 	static char buf[1024];
 	g_snprintf(buf, sizeof(buf), "%s%s%s%s%s%s%s",
@@ -3069,7 +3071,7 @@
 			(flags & AIM_FLAG_WIRELESS) ? "<IMG SRC=\"wireless_icon.gif\">" : "");
 	return buf;
 }
-
+#endif
 
 static char *caps_string(guint caps)
 {
@@ -3207,7 +3209,9 @@
 			"%s"
 			"%s\n"
 			"<hr>\n"),
-			info->sn, images(info->flags),
+			info->sn,
+			/* images(info->flags), */
+			"",
 			(int)((info->warnlevel/10.0) + 0.5),
 			onlinesince ? onlinesince : "",
 			membersince ? membersince : "",
@@ -4276,8 +4280,9 @@
 	return 0;
 }
 static void oscar_ask_direct_im(GaimConnection *gc, const char *name);
-
-static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, int len, GaimImFlags imflags) {
+static int gaim_odc_send_im(aim_session_t *, aim_conn_t *, const char *, GaimImFlags);
+
+static int oscar_send_im(GaimConnection *gc, const char *name, const char *message, GaimImFlags imflags) {
 	struct oscar_data *od = (struct oscar_data *)gc->proto_data;
 	struct direct_im *dim = find_direct_im(od, name);
 	int ret = 0;
@@ -4287,12 +4292,8 @@
 
 	if (dim && dim->connected) {
 		/* If we're directly connected, send a direct IM */
-		/* XXX - The last parameter below is the encoding.  Let Paco-Paco do something with it. */
-		if (imflags & GAIM_IM_AUTO_RESP)
-			ret =  aim_odc_send_im(od->sess, dim->conn, message, len == -1 ? strlen(message) : len, 0, 1);
-		else
-			ret =  aim_odc_send_im(od->sess, dim->conn, message, len == -1 ? strlen(message) : len, 0, 0);
-	} else if (len != -1) {
+		ret = gaim_odc_send_im(od->sess, dim->conn, message, imflags);
+	} else if (imflags & GAIM_IM_IMAGES) {
 		/* Trying to send an IM image outside of a direct connection. */
 		oscar_ask_direct_im(gc, name);
 		ret = -ENOTCONN;
@@ -5625,16 +5626,21 @@
 	dim->connected = TRUE;
 	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
 	g_free(sn);
-	gaim_conversation_write(cnv, NULL, buf, -1, GAIM_MESSAGE_SYSTEM, time(NULL));
+	gaim_conversation_write(cnv, NULL, buf, GAIM_MESSAGE_SYSTEM, time(NULL));
 
 	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING, gaim_odc_incoming, 0);
 	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING, gaim_odc_typing, 0);
-	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_update_ui, 0);
+	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER, gaim_odc_update_ui, 0);
 
 	return 1;
 }
 
-static int gaim_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) {
+/*
+ * This is called when each chunk of an image is received.  It can be used to 
+ * update a progress bar, or to eat lots of dry cat food.  Wet cat food is 
+ * nasty, you sicko.
+ */
+static int gaim_odc_update_ui(aim_session_t *sess, aim_frame_t *fr, ...) {
 	va_list ap;
 	char *sn;
 	double percent;
@@ -5654,10 +5660,11 @@
 		gaim_input_remove(dim->watcher);   /* Otherwise, the callback will callback */
 		dim->watcher = 0;
 	}
+	/* XXX is this really necessary? */
 	while (gtk_events_pending())
 		gtk_main_iteration();
 
-	c = gaim_find_conversation(sn);
+	c = gaim_find_conversation_with_account(sn, gaim_connection_get_account(gc));
 	if (c != NULL)
 		gaim_conversation_update_progress(c, percent);
 	dim->watcher = gaim_input_add(dim->conn->fd, GAIM_INPUT_READ,
@@ -5666,20 +5673,53 @@
 	return 1;
 }
 
+/*
+ * This is called after a direct IM has been received in its entirety.  This 
+ * function is passed a long chunk of data which contains the IM with any 
+ * data chunks (images) appended to it.
+ *
+ * This function rips out all the data chunks and creates an imgstore for 
+ * each one.  In order to do this, it first goes through the IM and takes 
+ * out all the IMG tags.  When doing so, it rewrites the original IMG tag 
+ * with one compatable with the imgstore Gaim core code. For each one, we 
+ * then read in chunks of data from the end of the message and actually 
+ * create the img store using the given data.
+ *
+ * For somewhat easy reference, here's a sample message
+ * (without the whitespace and asterisks):
+ *
+ * <HTML><BODY BGCOLOR="#ffffff">
+ *     <FONT LANG="0">
+ *     This is a really stupid picture:<BR>
+ *     <IMG SRC="Sample.jpg" ID="1" WIDTH="283" HEIGHT="212" DATASIZE="9894"><BR>
+ *     Yeah it is<BR>
+ *     Here is another one:<BR>
+ *     <IMG SRC="Soap Bubbles.bmp" ID="2" WIDTH="256" HEIGHT="256" DATASIZE="65978">   
+ *     </FONT>
+ * </BODY></HTML>
+ * <BINARY>
+ *     <DATA ID="1" SIZE="9894">datadatadatadata</DATA>
+ *     <DATA ID="2" SIZE="65978">datadatadatadata</DATA>
+ * </BINARY>
+ */
 static int gaim_odc_incoming(aim_session_t *sess, aim_frame_t *fr, ...) {
 	GaimConnection *gc = sess->aux_data;
 	GaimImFlags imflags = 0;
+	GString *newmsg = g_string_new("");
+	GSList *images = NULL;
 	va_list ap;
-	char *sn, *msg;
-	int len, encoding, isawaymsg;
+	const char *sn, *msg, *msgend, *binary;
+	size_t len;
+	int encoding, isawaymsg;
 
 	va_start(ap, fr);
-	sn = va_arg(ap, char *);
-	msg = va_arg(ap, char *);
-	len = va_arg(ap, int);
+	sn = va_arg(ap, const char *);
+	msg = va_arg(ap, const char *);
+	len = va_arg(ap, size_t);
 	encoding = va_arg(ap, int);
 	isawaymsg = va_arg(ap, int);
 	va_end(ap);
+	msgend = msg + len;
 
 	gaim_debug(GAIM_DEBUG_INFO, "oscar",
 			   "Got DirectIM message from %s\n", sn);
@@ -5687,8 +5727,91 @@
 	if (isawaymsg)
 		imflags |= GAIM_IM_AUTO_RESP;
 
+	/* message has a binary trailer */
+	if ((binary = gaim_strcasestr(msg, "<binary>"))) {
+		GData *attribs;
+		const char *tmp, *start, *end, *last = NULL;
+
+		tmp = msg;
+
+		/* for each valid image tag... */
+		while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
+			const char *id, *src, *datasize;
+			const char *tag = NULL, *data = NULL;
+			size_t size;
+			int imgid = 0;
+
+			/* update the location of the last img tag */
+			last = end;
+
+			/* grab attributes */
+			id       = g_datalist_get_data(&attribs, "id");
+			src      = g_datalist_get_data(&attribs, "src");
+			datasize = g_datalist_get_data(&attribs, "datasize");
+
+			/* if we have id & datasize, build the data tag */
+			if (id && datasize)
+				tag = g_strdup_printf("<data id=\"%s\" size=\"%s\">", id, datasize);
+
+			/* if we have a tag, find the start of the data */
+			if (tag && (data = gaim_strcasestr(binary, tag)))
+				data += strlen(tag);
+
+			/* check the data is here and store it */
+			if (data + (size = atoi(datasize)) <= msgend)
+				imgid = gaim_imgstore_add(data, size, src);
+
+			/* if we have a stored image... */
+			if (imgid) {
+				/* append the message up to the tag */
+				newmsg = g_string_append_len(newmsg, tmp, start - tmp);
+
+				/* write the new image tag */
+				g_string_append_printf(newmsg, "<IMG ID=\"%d\">", imgid);
+
+				/* and record the image number */
+				images = g_slist_append(images, GINT_TO_POINTER(imgid));
+			} else {
+				/* otherwise, copy up to the end of the tag */
+				newmsg = g_string_append_len(newmsg, tmp, (end + 1) - tmp);
+			}
+
+			/* clear the attribute list */
+			g_datalist_clear(&attribs);
+
+			/* continue from the end of the tag */
+			tmp = end + 1;
+		}
+
+		/* append any remaining message data (without the > :-) */
+		if (last++ && (last < binary))
+			newmsg = g_string_append_len(newmsg, last, binary - last);
+
+		/* set the flag if we caught any images */
+		if (images)
+			imflags |= GAIM_IM_IMAGES;
+	} else {
+		g_string_append_len(newmsg, msg, len);
+	}
+
 	/* XXX - I imagine Paco-Paco will want to do some voodoo with the encoding here */
-	serv_got_im(gc, sn, msg, imflags, time(NULL), len);
+	serv_got_im(gc, sn, newmsg->str, imflags, time(NULL));
+
+	/* free up the message */
+	g_string_free(newmsg, TRUE);
+
+	/* unref any images we allocated */
+	if (images) {
+		GSList *tmp;
+		int id;
+
+		for (tmp = images; tmp != NULL; tmp = tmp->next) {
+			id = GPOINTER_TO_INT(tmp->data);
+			gaim_imgstore_unref(id);
+		}
+
+		g_slist_free(images);
+	}
 
 	return 1;
 }
@@ -5716,6 +5839,91 @@
 	return 1;
 }
 
+static int gaim_odc_send_im(aim_session_t *sess, aim_conn_t *conn, const char *message, GaimImFlags imflags) {
+	char *buf;
+	size_t len;
+	int ret;
+
+	if (imflags & GAIM_IM_IMAGES) {
+		GString *msg = g_string_new("");
+		GString *data = g_string_new("<BINARY>");
+		GData *attribs;
+		const char *tmp, *start, *end, *last = NULL;
+		int oscar_id = 0;
+
+		tmp = message;
+
+		/* for each valid IMG tag... */
+		while (gaim_markup_find_tag("img", tmp, &start, &end, &attribs)) {
+			GaimStoredImage *image = NULL;
+			const char *id;
+
+			last = end;
+			id = g_datalist_get_data(&attribs, "id");
+
+			/* ... if it refers to a valid gaim image ... */
+			if (id && (image = gaim_imgstore_get(atoi(id)))) {
+				/* ... append the message from start to the tag ... */
+				msg = g_string_append_len(msg, tmp, start - tmp);
+				oscar_id++;
+
+				/* ... insert a new img tag with the oscar id ... */
+				if (image->filename)
+					g_string_append_printf(msg,
+						"<IMG SRC=\"file://%s\" ID=\"%d\" DATASIZE=\"%d\">",
+						image->filename, oscar_id, image->size);
+				else
+					g_string_append_printf(msg,
+						"<IMG ID=\"%d\" DATASIZE=\"%d\">",
+						oscar_id, image->size);
+
+				/* ... and append the data to the binary section ... */
+				g_string_append_printf(data, "<DATA ID=\"%d\" SIZE=\"%d\">",
+					oscar_id, image->size);
+				data = g_string_append_len(data, image->data, image->size);
+				data = g_string_append(data, "</DATA>");
+			} else {
+				/* ... otherwise, allow the possibly invalid img tag through. */
+				/* should we do something else? */
+				msg = g_string_append_len(msg, tmp, (end + 1) - tmp);
+			}
+
+			g_datalist_clear(&attribs);
+
+			/* continue from the end of the tag */
+			tmp = end + 1;
+		}
+
+		/* append any remaining message data (without the > :-) */
+		if (last++ && *last)
+			msg = g_string_append(msg, last);
+
+		/* if we inserted any images in the binary section, append it */
+		if (oscar_id) {
+			msg = g_string_append_len(msg, data->str, data->len);
+			msg = g_string_append(msg, "</BINARY>");
+		}
+
+		len = msg->len;
+		buf = msg->str;
+		g_string_free(msg, FALSE);
+		g_string_free(data, TRUE);
+	} else {
+		len = strlen(message);
+		buf = g_memdup(message, len+1);
+	}
+
+	/* XXX - The last parameter below is the encoding.  Let Paco-Paco do something with it. */
+	if (imflags & GAIM_IM_AUTO_RESP)
+		ret =  aim_odc_send_im(sess, conn, buf, len, 0, 1);
+	else
+		ret =  aim_odc_send_im(sess, conn, buf, len, 0, 0);
+
+	g_free(buf);
+
+	return ret;
+}
+
 struct ask_do_dir_im {
 	char *who;
 	GaimConnection *gc;
--- a/src/protocols/toc/toc.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/toc/toc.c	Sat Sep 27 19:17:21 2003 +0000
@@ -740,7 +740,7 @@
 
 		a = (away && (*away == 'T')) ? GAIM_IM_AUTO_RESP : 0;
 
-		serv_got_im(gc, c, message, a, time(NULL), -1);
+		serv_got_im(gc, c, message, a, time(NULL));
 	} else if (!g_ascii_strcasecmp(c, "UPDATE_BUDDY")) {
 		char *l, *uc, *tmp;
 		int logged, evil, idle, type = 0;
@@ -1059,7 +1059,7 @@
 	}
 }
 
-static int toc_send_im(GaimConnection *gc, const char *name, const char *message, int len, GaimImFlags flags)
+static int toc_send_im(GaimConnection *gc, const char *name, const char *message, GaimImFlags flags)
 {
 	char *buf1, *buf2;
 
@@ -1089,7 +1089,7 @@
 	 * that length is passed to sflap_send().
 	 */
 
-	if (len + 52 > MSG_LEN) {
+	if (strlen(buf1) + 52 > MSG_LEN) {
 		g_free(buf1);
 		return -E2BIG;
 	}
--- a/src/protocols/trepia/trepia.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/trepia/trepia.c	Sat Sep 27 19:17:21 2003 +0000
@@ -728,7 +728,7 @@
 				serv_got_im(session->gc,
 							trepia_profile_get_login(profile),
 							(char *)g_hash_table_lookup(info, "b"),
-							0, time(NULL), -1);
+							0, time(NULL));
 				break;
 
 			case TREPIA_MEMBER_UPDATE:
@@ -1174,7 +1174,7 @@
 
 static int
 trepia_send_im(GaimConnection *gc, const char *who, const char *message,
-			int len, GaimImFlags flags)
+			GaimImFlags flags)
 {
 	TrepiaSession *session = gc->proto_data;
 	TrepiaProfile *profile;
--- a/src/protocols/yahoo/yahoo.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/yahoo/yahoo.c	Sat Sep 27 19:17:21 2003 +0000
@@ -682,7 +682,7 @@
 
 				strip_linefeed(msg);
 				m = yahoo_codes_to_html(msg);
-				serv_got_im(gc, from, m, 0, tm, -1);
+				serv_got_im(gc, from, m, 0, tm);
 				g_free(m);
 
 				tm = time(NULL);
@@ -1645,7 +1645,7 @@
 	return m;
 }
 
-static int yahoo_send_im(GaimConnection *gc, const char *who, const char *what, int len, GaimImFlags flags)
+static int yahoo_send_im(GaimConnection *gc, const char *who, const char *what, GaimImFlags flags)
 {
 	struct yahoo_data *yd = gc->proto_data;
 	struct yahoo_packet *pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, 0);
--- a/src/protocols/zephyr/zephyr.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/protocols/zephyr/zephyr.c	Sat Sep 27 19:17:21 2003 +0000
@@ -369,7 +369,7 @@
                             !g_ascii_strcasecmp(notice.z_class_inst, "PERSONAL")) {
 				if (!g_ascii_strcasecmp(notice.z_message, "Automated reply:"))
 					flags |= GAIM_IM_AUTO_RESP;
-				serv_got_im(zgc, notice.z_sender, buf2, flags, time(NULL), -1);
+				serv_got_im(zgc, notice.z_sender, buf2, flags, time(NULL));
 			} else {
 				zephyr_triple *zt1, *zt2;
 				zt1 = new_triple(notice.z_class, notice.z_class_inst,
@@ -789,7 +789,7 @@
 	return 0;
 }
 
-static int zephyr_send_im(GaimConnection *gc, const char *who, const char *im, int len, GaimImFlags flags) {
+static int zephyr_send_im(GaimConnection *gc, const char *who, const char *im, GaimImFlags flags) {
 	ZNotice_t notice;
 	char *buf;
 	const char *sig;
--- a/src/prpl.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/prpl.h	Sat Sep 27 19:17:21 2003 +0000
@@ -92,7 +92,8 @@
  */
 typedef enum
 {
-	GAIM_IM_AUTO_RESP = 0x01    /**< Auto response.    */
+	GAIM_IM_AUTO_RESP = 0x0001,    /**< Auto response.    */
+	GAIM_IM_IMAGES    = 0x0002     /**< Contains images.  */
 } GaimImFlags;
 
 /**
@@ -241,7 +242,7 @@
 	void (*login)(GaimAccount *);
 	void (*close)(GaimConnection *);
 	int  (*send_im)(GaimConnection *, const char *who,
-					const char *message, int len,
+					const char *message,
 					GaimImFlags flags);
 	void (*set_info)(GaimConnection *, const char *info);
 	int  (*send_typing)(GaimConnection *, const char *name, int typing);
--- a/src/server.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/server.c	Sat Sep 27 19:17:21 2003 +0000
@@ -258,7 +258,7 @@
 }
 
 int serv_send_im(GaimConnection *gc, const char *name, const char *message,
-				 int len, GaimImFlags imflags)
+				 GaimImFlags imflags)
 {
 	GaimConversation *c;
 	int val = -EINVAL;
@@ -270,7 +270,7 @@
 	c = gaim_find_conversation_with_account(name, gc->account);
 
 	if (prpl_info && prpl_info->send_im)
-		val = prpl_info->send_im(gc, name, message, len, imflags);
+		val = prpl_info->send_im(gc, name, message, imflags);
 
 	if (!(imflags & GAIM_IM_AUTO_RESP))
 		serv_touch_idle(gc);
@@ -835,10 +835,10 @@
  * sure to follow along, kids
  */
 void serv_got_im(GaimConnection *gc, const char *who, const char *msg,
-				 GaimImFlags imflags, time_t mtime, gint len)
+				 GaimImFlags imflags, time_t mtime)
 {
 	GaimConversation *cnv;
-	GaimMessageFlags auto_resp;
+	GaimMessageFlags msgflags;
 	char *message, *name;
 	char *angel, *buffy;
 	int plugin_return;
@@ -852,41 +852,32 @@
 	/*
 	 * Plugin stuff. we pass a char ** but we don't want to pass what's
 	 * been given us by the prpls. So we create temp holders and pass
-	 * those instead. It's basically just to avoid segfaults. Of course,
-	 * if the data is binary, plugins don't see it. Bitch all you want;
-	 * I really don't want you to be dealing with it.
+	 * those instead. It's basically just to avoid segfaults.
 	 */
-	if (len < 0) {
-		buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG));
-		strcpy(buffy, msg);
-		angel = g_strdup(who);
-
-		plugin_return = GPOINTER_TO_INT(
-			gaim_signal_emit_return_1(gaim_conversations_get_handle(),
-									  "received-im-msg", gc->account,
-									  &angel, &buffy, &imflags));
+	buffy = g_malloc(MAX(strlen(msg) + 1, BUF_LONG));
+	strcpy(buffy, msg);
+	angel = g_strdup(who);
 
-		if (!buffy || !angel || plugin_return) {
-			if (buffy)
-				g_free(buffy);
-			if (angel)
-				g_free(angel);
-			return;
-		}
-		name = angel;
-		message = buffy;
-	} else {
-		name = g_strdup(who);
-		message = g_memdup(msg, len);
+	plugin_return = GPOINTER_TO_INT(
+		gaim_signal_emit_return_1(gaim_conversations_get_handle(),
+								  "received-im-msg", gc->account,
+								  &angel, &buffy, &imflags));
+
+	if (!buffy || !angel || plugin_return) {
+		if (buffy)
+			g_free(buffy);
+		if (angel)
+			g_free(angel);
+		return;
 	}
+	name = angel;
+	message = buffy;
 
 	/*
 	 * If you can't figure this out, stop reading right now.
 	 * "We're not worthy! We're not worthy!"
 	 */
-	if (len < 0 &&
-		gaim_prefs_get_bool("/gaim/gtk/conversations/show_urls_as_links")) {
-
+	if (gaim_prefs_get_bool("/gaim/gtk/conversations/show_urls_as_links")) {
 		buffy = linkify_text(message);
 		g_free(message);
 		message = buffy;
@@ -896,12 +887,15 @@
 	 * Um. When we call gaim_conversation_write with the message we received,
 	 * it's nice to pass whether or not it was an auto-response. So if it
 	 * was an auto-response, we set the appropriate flag. This is just so
-	 * prpls don't have to know about GAIM_MESSAGE_* (though some do anyway)
+	 * prpls don't have to know about GAIM_MESSAGE_* (though some do anyway).
+	 * We also need to preserve the flag that tells the UI to look for the
+	 * associated images.
 	 */
+	msgflags = GAIM_MESSAGE_RECV;
 	if (imflags & GAIM_IM_AUTO_RESP)
-		auto_resp = GAIM_MESSAGE_AUTO_RESP;
-	else
-		auto_resp = 0;
+		msgflags |= GAIM_MESSAGE_AUTO_RESP;
+	if (imflags & GAIM_IM_IMAGES)
+		msgflags |= GAIM_MESSAGE_IMAGES;
 
 	/*
 	 * Alright. Two cases for how to handle this. Either we're away or
@@ -940,11 +934,10 @@
 
 			qm = g_new0(struct queued_message, 1);
 			g_snprintf(qm->name, sizeof(qm->name), "%s", name);
-			qm->message = g_memdup(message, len == -1 ? strlen(message) + 1 : len);
+			qm->message = g_strdup(message);
 			qm->account = gc->account;
 			qm->tm = mtime;
-			qm->flags = GAIM_MESSAGE_RECV | auto_resp;
-			qm->len = len;
+			qm->flags = msgflags;
 			message_queue = g_slist_append(message_queue, qm);
 
 			row = find_queue_row_by_name(qm->name);
@@ -979,8 +972,7 @@
 			if (cnv == NULL)
 				cnv = gaim_conversation_new(GAIM_CONV_IM, gc->account, name);
 
-			gaim_im_write(GAIM_IM(cnv), NULL, message, len,
-						  GAIM_MESSAGE_RECV | auto_resp, mtime);
+			gaim_im_write(GAIM_IM(cnv), NULL, message, msgflags, mtime);
 		}
 
 		/*
@@ -1026,7 +1018,7 @@
 
 		/* apply default fonts and colors */
 		tmpmsg = stylize(gc->away, MSG_LEN);
-		serv_send_im(gc, name, away_subs(tmpmsg, alias), -1, GAIM_IM_AUTO_RESP);
+		serv_send_im(gc, name, away_subs(tmpmsg, alias), GAIM_IM_AUTO_RESP);
 		if (!cnv && awayqueue &&
 			gaim_prefs_get_bool("/gaim/gtk/away/queue_messages")) {
 
@@ -1038,11 +1030,10 @@
 			qm->account = gc->account;
 			qm->tm = mtime;
 			qm->flags = GAIM_MESSAGE_SEND | GAIM_MESSAGE_AUTO_RESP;
-			qm->len = -1;
 			message_queue = g_slist_append(message_queue, qm);
 		} else if (cnv != NULL)
 			gaim_im_write(GAIM_IM(cnv), NULL, away_subs(tmpmsg, alias),
-						  len, GAIM_MESSAGE_SEND | GAIM_MESSAGE_AUTO_RESP, mtime);
+						  GAIM_MESSAGE_SEND | GAIM_MESSAGE_AUTO_RESP, mtime);
 
 		g_free(tmpmsg);
 	} else {
@@ -1070,16 +1061,14 @@
 			qm->message = g_strdup(message);
 			qm->account = gc->account;
 			qm->tm = mtime;
-			qm->flags = GAIM_MESSAGE_RECV | auto_resp;
-			qm->len = len;
+			qm->flags = msgflags;
 			unread_message_queue = g_slist_append(unread_message_queue, qm);
 		}
 		else {
 			if (cnv == NULL)
 				cnv = gaim_conversation_new(GAIM_CONV_IM, gc->account, name);
 
-			gaim_im_write(GAIM_IM(cnv), NULL, message, len,
-						  GAIM_MESSAGE_RECV | auto_resp, mtime);
+			gaim_im_write(GAIM_IM(cnv), NULL, message, msgflags, mtime);
 			gaim_window_flash(gaim_conversation_get_window(cnv));
 		}
 	}
@@ -1156,7 +1145,7 @@
 					char *tmp = g_strdup_printf(_("%s logged in."),
 												gaim_get_buddy_alias(b));
 
-					gaim_conversation_write(c, NULL, tmp, -1, GAIM_MESSAGE_SYSTEM,
+					gaim_conversation_write(c, NULL, tmp, GAIM_MESSAGE_SYSTEM,
 											time(NULL));
 					g_free(tmp);
 				}
@@ -1168,7 +1157,6 @@
 					qm->account = gc->account;
 					qm->tm = time(NULL);
 					qm->flags = GAIM_MESSAGE_SYSTEM;
-					qm->len = -1;
 					message_queue = g_slist_append(message_queue, qm);
 				}
 			}
@@ -1183,7 +1171,7 @@
 
 					char *tmp = g_strdup_printf(_("%s logged out."),
 												gaim_get_buddy_alias(b));
-					gaim_conversation_write(c, NULL, tmp, -1,
+					gaim_conversation_write(c, NULL, tmp,
 											GAIM_MESSAGE_SYSTEM, time(NULL));
 					g_free(tmp);
 				} else if (awayqueue && find_queue_total_by_name(b->name)) {
@@ -1194,7 +1182,6 @@
 					qm->account = gc->account;
 					qm->tm = time(NULL);
 					qm->flags = GAIM_MESSAGE_SYSTEM;
-					qm->len = -1;
 					message_queue = g_slist_append(message_queue, qm);
 				}
 			}
@@ -1576,6 +1563,6 @@
 
 	gtk_widget_show_all(window);
 
-	gtk_imhtml_append_text(GTK_IMHTML(text), msg, -1, GTK_IMHTML_NO_NEWLINE);
+	gtk_imhtml_append_text(GTK_IMHTML(text), msg, GTK_IMHTML_NO_NEWLINE);
 }
 
--- a/src/server.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/server.h	Sat Sep 27 19:17:21 2003 +0000
@@ -39,7 +39,7 @@
 void serv_login(GaimAccount *);
 void serv_close(GaimConnection *);
 void serv_touch_idle(GaimConnection *);
-int  serv_send_im(GaimConnection *, const char *, const char *, int, GaimImFlags);
+int  serv_send_im(GaimConnection *, const char *, const char *, GaimImFlags);
 void serv_get_info(GaimConnection *, const char *);
 void serv_get_dir(GaimConnection *, const char *);
 void serv_set_idle(GaimConnection *, int);
@@ -82,7 +82,7 @@
 void serv_set_buddyicon(GaimConnection *gc, const char *filename);
 void serv_got_typing_stopped(GaimConnection *gc, const char *name);
 void serv_got_im(GaimConnection *gc, const char *who, const char *msg,
-				 GaimImFlags imflags, time_t mtime, gint len);
+				 GaimImFlags imflags, time_t mtime);
 void serv_got_update(GaimConnection *gc, const char *name, int loggedin,
 					 int evil, time_t signon, time_t idle, int type);
 void serv_finish_login(GaimConnection *gc);
--- a/src/ui.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/ui.h	Sat Sep 27 19:17:21 2003 +0000
@@ -84,7 +84,6 @@
 	time_t tm;
 	GaimAccount *account;
 	GaimMessageFlags flags;
-	int len;
 };
 
 struct smiley_theme {
--- a/src/util.c	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/util.c	Sat Sep 27 19:17:21 2003 +0000
@@ -940,6 +940,31 @@
 	return ret;
 }
 
+const char *gaim_strcasestr(const char *haystack, const char *needle) {
+	size_t hlen, nlen;
+	const char *tmp, *ret;
+
+	g_return_val_if_fail(haystack != NULL, NULL);
+	g_return_val_if_fail(needle != NULL, NULL);
+
+	hlen = strlen(haystack);
+	nlen = strlen(needle);
+	tmp = haystack,
+	ret = NULL;
+
+	g_return_val_if_fail(hlen > 0, NULL);
+	g_return_val_if_fail(nlen > 0, NULL);
+
+	while (*tmp && !ret) {
+		if (!g_ascii_strncasecmp(needle, tmp, nlen))
+			ret = tmp;
+		else
+			tmp++;
+	}
+
+	return ret;
+}
+
 char *gaim_get_size_string(size_t size)
 {
 	static const char *size_str[4] = { "bytes", "KB", "MB", "GB" };
@@ -963,3 +988,138 @@
 		return g_strdup_printf("%.2f %s", size_mag, size_str[size_index]);
 	}
 }
+
+gboolean gaim_markup_find_tag(const char *needle, const char *haystack, const char **start, const char **end, GData **attributes) {
+	GData *attribs;
+	const char *cur = haystack;
+	char *name = NULL;
+	gboolean found = FALSE;
+	gboolean in_tag = FALSE;
+	gboolean in_attr = FALSE;
+	gboolean in_quotes = FALSE;
+	size_t needlelen = strlen(needle);
+
+	g_datalist_init(&attribs);
+
+	while (*cur && !found) {
+		if (in_tag) {
+			if (in_quotes) {
+				const char *close = cur;
+
+				while (*close && *close != '"')
+					close++;
+
+				/* if we got the close quote, store the value and carry on from    *
+				 * after it. if we ran to the end of the string, point to the NULL *
+				 * and we're outta here */
+				if (*close) {
+					/* only store a value if we have an attribute name */
+					if (name) {
+						size_t len = close - cur;
+						char *val = g_strndup(cur, len);
+
+						g_datalist_set_data_full(&attribs, name, val, g_free);
+						g_free(name);
+						name = NULL;
+					}
+
+					in_quotes = FALSE;
+					cur = close + 1;
+				} else {
+					cur = close;
+				}
+			} else if (in_attr) {
+				const char *close = cur;
+
+				while (*close && *close != '>' && *close != '"' && *close != ' ' && *close != '=')
+					close++;
+
+				/* if we got the equals, store the name of the attribute. if we got
+				 * the quote, save the attribute and go straight to quote mode.
+				 * otherwise the tag closed or we reached the end of the string,
+				 * so we can get outta here */
+				switch (*close) {
+				case '"':
+					in_quotes = TRUE;
+				case '=':
+					{
+						size_t len = close - cur;
+
+						/* don't store a blank attribute name */
+						if (len) {
+							if (name)
+								g_free(name);
+							name = g_ascii_strdown(cur, len);
+						}
+
+						in_attr = FALSE;
+						cur = close + 1;
+						break;
+					}
+				case ' ':
+				case '>':
+					in_attr = FALSE;
+				default:
+					cur = close;
+					break;
+				}
+			} else {
+				switch (*cur) {
+				case ' ':
+					/* swallow extra spaces inside tag */
+					while (*cur && *cur == ' ') cur++;
+					in_attr = TRUE;
+					break;
+				case '>':
+					found = TRUE;
+					*end = cur;
+					break;
+				case '"':
+					in_quotes = TRUE;
+				default:
+					cur++;
+					break;
+				}
+			}
+		} else {
+			/* if we hit a < followed by the name of our tag... */
+			if (*cur == '<' && !g_ascii_strncasecmp(cur + 1, needle, needlelen)) {
+				*start = cur;
+				cur = cur + needlelen + 1;
+
+				/* if we're pointing at a space or a >, we found the right tag. if *
+				 * we're not, we've found a longer tag, so we need to skip to the  *
+				 * >, but not being distracted by >s inside quotes.                */
+				if (*cur == ' ' || *cur == '>') {
+					in_tag = TRUE;
+				} else {
+					while (*cur && *cur != '"' && *cur != '>') {
+						if (*cur == '"') {
+							cur++;
+							while (*cur && *cur != '"')
+								cur++;
+						} else {
+							cur++;
+						}
+					}
+				}
+			} else {
+				cur++;
+			}
+		}
+	}
+
+	/* clean up any attribute name from a premature termination */
+	if (name)
+		g_free(name);
+
+	if (found) {
+		*attributes = attribs;
+	} else {
+		*start = NULL;
+		*end = NULL;
+		*attributes = NULL;
+	}
+
+	return found;
+}
--- a/src/util.h	Sat Sep 27 15:45:49 2003 +0000
+++ b/src/util.h	Sat Sep 27 19:17:21 2003 +0000
@@ -314,6 +314,16 @@
 					   const gchar *replacement);
 
 /**
+ * This is like strstr, except that it ignores ASCII case in
+ * searching for the substring.
+ *
+ * @param haystack The string to search in.
+ * @param needle   The substring to find.
+ * @return the location of the substring if found, or NULL if not
+ */
+const char *gaim_strcasestr(const char *haystack, const char *needle);
+
+/**
  * Returns a string representing a filesize in the appropriate
  * units (MB, KB, GB, etc.)
  *
@@ -321,6 +331,21 @@
  */
 char *gaim_get_size_string(size_t size);
 
+/**
+ * Finds a HTML tag matching the given name, locating its start
+ * and end, and storing its attributes in a GData hash table.
+ * The names of the attributes are lower-cased in the hash table,
+ * and the name of the tag is case insensitive.
+ *
+ * @param needle	the name of the tag
+ * @param haystack	the null-delimited string to search in
+ * @param start		a pointer to the start of the tag if found
+ * @param end		a pointer to the end of the tag if found
+ * @param attributes	the attributes, if the tag was found
+ * @return TRUE if the tag was found
+ */
+gboolean gaim_markup_find_tag(const char *needle, const char *haystack, const char **start, const char **end, GData **attributes);
+
 #ifdef __cplusplus
 }
 #endif