changeset 2856:b1e300a85678

[gaim-migrate @ 2869] rewrote the html parser in gtkimhtml. yes, that's really all i did. the reason for the massive change is because i added a length argument, which then needed to be propogated down to everything that would ever receive anything that would get drawn to the window. the new parser isn't any faster. that wasn't my goal. it's much more understandable now (hopefully, anyway). committer: Tailor Script <tailor@pidgin.im>
author Eric Warmenhoven <eric@warmenhoven.org>
date Sat, 08 Dec 2001 09:48:52 +0000
parents 19f786779b08
children 7a6f33cd6721
files configure.ac src/away.c src/buddy.c src/buddy_chat.c src/conversation.c src/convo.h src/core.h src/dialogs.c src/gaim.h src/gtkimhtml.c src/gtkimhtml.h src/perl.c src/prefs.c src/protocols/gg/gg.c src/protocols/icq/gaim_icq.c src/protocols/irc/irc.c src/protocols/jabber/jabber.c src/protocols/msn/msn.c src/protocols/napster/napster.c src/protocols/oscar/oscar.c src/protocols/toc/toc.c src/protocols/yahoo/yahoo.c src/protocols/zephyr/zephyr.c src/server.c src/ui.h
diffstat 25 files changed, 550 insertions(+), 855 deletions(-) [+]
line wrap: on
line diff
--- a/configure.ac	Sat Dec 08 08:46:00 2001 +0000
+++ b/configure.ac	Sat Dec 08 09:48:52 2001 +0000
@@ -85,7 +85,7 @@
 
 AC_ARG_ENABLE(gtk2,    [  --enable-gtk2           compile using GTK 2 (BROKEN)],,enable_gtk2=no)
 AC_ARG_ENABLE(gnome,   [  --disable-gnome         compile without Gnome bits],,enable_gnome=yes)
-AC_ARG_ENABLE(pixbuf,  [  --disable-pixbuf        compile without GdkPixbuf (needed for Buddy Icons)],,enable_pixbuf=yes)
+AC_ARG_ENABLE(pixbuf,  [  --disable-pixbuf        compile without GdkPixbuf],,enable_pixbuf=yes)
 AC_ARG_ENABLE(panel,   [  --enable-panel          compile as a GNOME applet],,enable_panel=$enable_distrib)
 
 AM_CONDITIONAL(GNOMEAPPLET, test "x$enable_panel" = "xyes")
@@ -428,9 +428,9 @@
 	echo UI Library.................... : GTK+ 1.2
 fi
 if test "x$enable_gtk2" = "xyes" ; then
-	echo Use GdkPixbuf for Buddy Icons. : yes
+	echo Use GdkPixbuf................. : yes
 else
-	echo Use GdkPixbuf for Buddy Icons. : $use_pixbuf
+	echo Use GdkPixbuf................. : $use_pixbuf
 fi
 echo
 echo Build with Plugin support..... : $enable_plugins
--- a/src/away.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/away.c	Sat Dec 08 09:48:52 2001 +0000
@@ -74,7 +74,7 @@
 		if (g_slist_index(connections, qm->gc) >= 0)
 			set_convo_gc(cnv, qm->gc);
 
-		write_to_conv(cnv, qm->message, qm->flags, NULL, qm->tm);
+		write_to_conv(cnv, qm->message, qm->flags, NULL, qm->tm, qm->len);
 
 		message_queue = g_slist_remove(message_queue, qm);
 
@@ -111,7 +111,7 @@
 				if (g_slist_index(connections, qm->gc) >= 0)
 					set_convo_gc(cnv, qm->gc);
 				
-				write_to_conv(cnv, qm->message, qm->flags, NULL, qm->tm);
+				write_to_conv(cnv, qm->message, qm->flags, NULL, qm->tm, qm->len);
 				g_free(qm->message);
 				g_free(qm);
 				templist = message_queue = g_slist_remove(message_queue, qm);
@@ -222,10 +222,10 @@
 		gaim_setup_imhtml(awaytext);
 		gtk_widget_show(awaytext);
 		buf = stylize(a->message, BUF_LONG);
-		gtk_imhtml_append_text(GTK_IMHTML(awaytext), buf, GTK_IMHTML_NO_TITLE |
+		gtk_imhtml_append_text(GTK_IMHTML(awaytext), buf, -1, GTK_IMHTML_NO_TITLE |
 				       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
 		g_free(buf);
-		gtk_imhtml_append_text(GTK_IMHTML(awaytext), "<BR>", GTK_IMHTML_NO_TITLE |
+		gtk_imhtml_append_text(GTK_IMHTML(awaytext), "<BR>", -1, GTK_IMHTML_NO_TITLE |
 				       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
 
 		clistqueuesw = gtk_scrolled_window_new(NULL, NULL);
--- a/src/buddy.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/buddy.c	Sat Dec 08 09:48:52 2001 +0000
@@ -1499,7 +1499,7 @@
 
 					set_convo_gc(c, u->gc);
 
-					write_to_conv(c, b->message, WFLAG_SEND, NULL, time((time_t) NULL));
+					write_to_conv(c, b->message, WFLAG_SEND, NULL, time(NULL), -1);
 					serv_send_im(u->gc, name, b->message, 0);
 				}
 			}
@@ -2110,7 +2110,7 @@
 				if (c) {
 					char tmp[1024];
 					g_snprintf(tmp, sizeof(tmp), _("%s logged in."), b->name);
-					write_to_conv(c, tmp, WFLAG_SYSTEM, NULL, time((time_t) NULL));
+					write_to_conv(c, tmp, WFLAG_SYSTEM, NULL, time(NULL), -1);
 				}
 			}
 			bs->sound = 2;
@@ -2165,7 +2165,7 @@
 			if (c) {
 				char tmp[1024];
 				g_snprintf(tmp, sizeof(tmp), _("%s logged out."), b->name);
-				write_to_conv(c, tmp, WFLAG_SYSTEM, NULL, time((time_t) NULL));
+				write_to_conv(c, tmp, WFLAG_SYSTEM, NULL, time(NULL), -1);
 			}
 		}
 
--- a/src/buddy_chat.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/buddy_chat.c	Sat Dec 08 09:48:52 2001 +0000
@@ -605,7 +605,7 @@
 			g_free(tmp);
 			matches = g_list_remove(matches, matches->data);
 		}
-		write_to_conv(c, addthis, WFLAG_NOLOG, NULL, time(NULL));
+		write_to_conv(c, addthis, WFLAG_NOLOG, NULL, time(NULL), -1);
 		gtk_editable_insert_text(GTK_EDITABLE(c->entry), partial, strlen(partial), &start);
 		if (t == start) {
 			t = start + strlen(partial);
@@ -618,7 +618,7 @@
 	g_free(partial);
 }
 
-gboolean meify(char *message)
+gboolean meify(char *message, int len)
 {
 	/* read /me-ify : if the message (post-HTML) starts with /me, remove
 	 * the "/me " part of it (including that space) and return TRUE */
@@ -626,6 +626,8 @@
 	int inside_HTML = 0;	/* i really don't like descriptive names */
 	if (!c)
 		return FALSE;	/* um... this would be very bad if this happens */
+	if (len == -1)
+		len = strlen(message);
 	while (*c) {
 		if (inside_HTML) {
 			if (*c == '>')
@@ -637,12 +639,13 @@
 				break;
 		}
 		c++;		/* i really don't like c++ either */
+		len--;
 	}
 	/* k, so now we've gotten past all the HTML crap. */
 	if (!*c)
 		return FALSE;
 	if (!g_strncasecmp(c, "/me ", 4)) {
-		sprintf(c, "%s", c + 4);
+		memmove(c, c + 4, len - 4);
 		return TRUE;
 	} else
 		return FALSE;
@@ -721,7 +724,7 @@
 	if ((flag & WFLAG_RECV) && find_nick(b->gc, message))
 		flag |= WFLAG_NICK;
 
-	write_to_conv(b, message, flag, who, mtime);
+	write_to_conv(b, message, flag, who, mtime, -1);
 }
 
 
@@ -884,7 +887,7 @@
 
 	if (chat_options & OPT_CHAT_LOGON) {
 		g_snprintf(tmp, sizeof(tmp), _("%s entered the room."), name);
-		write_to_conv(b, tmp, WFLAG_SYSTEM, NULL, time(NULL));
+		write_to_conv(b, tmp, WFLAG_SYSTEM, NULL, time(NULL), -1);
 	}
 }
 
@@ -958,7 +961,7 @@
 
 	if (chat_options & OPT_CHAT_LOGON) {
 		g_snprintf(tmp, sizeof(tmp), _("%s is now known as %s"), old, new);
-		write_to_conv(b, tmp, WFLAG_SYSTEM, NULL, time(NULL));
+		write_to_conv(b, tmp, WFLAG_SYSTEM, NULL, time(NULL), -1);
 	}
 }
 
@@ -1007,7 +1010,7 @@
 			g_snprintf(tmp, sizeof(tmp), _("%s left the room (%s)."), buddy, reason);
 		else
 			g_snprintf(tmp, sizeof(tmp), _("%s left the room."), buddy);
-		write_to_conv(b, tmp, WFLAG_SYSTEM, NULL, time(NULL));
+		write_to_conv(b, tmp, WFLAG_SYSTEM, NULL, time(NULL), -1);
 	}
 }
 
--- a/src/conversation.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/conversation.c	Sat Dec 08 09:48:52 2001 +0000
@@ -451,14 +451,12 @@
 		}
 	} else {
 		if (chat_options & OPT_CHAT_ONE_WINDOW) {
-			if ((convo_options & OPT_CONVO_COMBINE) &&
-					 (im_options & OPT_IM_ONE_WINDOW) && conversations) {
+			if ((g_list_length(chats) > 1) ||
+					((convo_options & OPT_CONVO_COMBINE) &&
+					 (im_options & OPT_IM_ONE_WINDOW) && conversations)) {
 				gtk_notebook_remove_page(GTK_NOTEBOOK(chat_notebook),
 							 g_list_index(chats, c) +
 								g_list_length(conversations));
-			} else if (g_list_length(chats) > 1) {
-				gtk_notebook_remove_page(GTK_NOTEBOOK(chat_notebook),
-							 g_list_index(chats, c));
 			} else {
 				if (c->window)
 					gtk_widget_destroy(c->window);
@@ -1005,7 +1003,7 @@
 
 
 		if (err > 0) {
-			write_to_conv(c, buf, WFLAG_SEND, NULL, time(NULL));
+			write_to_conv(c, buf, WFLAG_SEND, NULL, time(NULL), -1);
 
 			if (c->makesound && (sound_options & OPT_SOUND_SEND))
 				play_sound(SEND);
@@ -1443,7 +1441,7 @@
 
 /* this is going to be interesting since the conversation could either be a
  * normal IM conversation or a chat window. but hopefully it won't matter */
-void write_to_conv(struct conversation *c, char *what, int flags, char *who, time_t mtime)
+void write_to_conv(struct conversation *c, char *what, int flags, char *who, time_t mtime, gint length)
 {
 	char buf[BUF_LONG];
 	char *str;
@@ -1455,7 +1453,7 @@
 	char buf2[BUF_LONG];
 	char mdate[64];
 
-	if (c->is_chat && !g_list_find(chats, c))
+	if (c->is_chat && (!c->gc || !g_slist_find(c->gc->buddy_chats, c)))
 		return;
 
 	if (!c->is_chat && !g_list_find(conversations, c))
@@ -1492,7 +1490,7 @@
 		int index = g_slist_index(connections, c->gc);
 		int sconv = strlen(c->name);
 		int sname = strlen(who);
-		int swhat = strlen(what);
+		int swhat = length == -1 ? strlen(what) : length;
 		UI_build_broadcast(CUI_TYPE_MESSAGE, CUI_MESSAGE_RECV,
 				sizeof(index), &index,
 				sizeof(sconv), &sconv,
@@ -1529,7 +1527,7 @@
 		g_snprintf(buf2, sizeof(buf2), "<FONT SIZE=\"2\"><!--(%s) --></FONT><B>%s</B><BR>",
 			   mdate, what);
 
-		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf2, 0);
+		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf2, -1, 0);
 
 		if (logging_options & OPT_LOG_STRIP_HTML) {
 			char *t1 = strip_html(buf);
@@ -1569,11 +1567,11 @@
 		}
 	} else if (flags & WFLAG_NOLOG) {
 		g_snprintf(buf, BUF_LONG, "<B><FONT COLOR=\"#777777\">%s</FONT></B><BR>", what);
-		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf, 0);
+		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf, -1, 0);
 	} else {
 		if (flags & WFLAG_WHISPER) {
 			/* if we're whispering, it's not an autoresponse */
-			if (meify(what)) {
+			if (meify(what, length)) {
 				str = g_malloc(1024);
 				g_snprintf(str, 1024, "***%s", who);
 				strcpy(colour, "#6C2585");
@@ -1583,7 +1581,7 @@
 				strcpy(colour, "#00ff00");
 			}
 		} else {
-			if (meify(what)) {
+			if (meify(what, length)) {
 				str = g_malloc(1024);
 				if (flags & WFLAG_AUTO)
 					g_snprintf(str, 1024, "%s ***%s", AUTO_RESPONSE, who);
@@ -1618,11 +1616,11 @@
 
 		g_free(str);
 
-		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf2, 0);
-
-		logstr = gtk_imhtml_append_text(GTK_IMHTML(c->text), what, gtk_font_options);
-
-		gtk_imhtml_append_text(GTK_IMHTML(c->text), "<BR>", 0);
+		gtk_imhtml_append_text(GTK_IMHTML(c->text), buf2, -1, 0);
+
+		logstr = gtk_imhtml_append_text(GTK_IMHTML(c->text), what, length, gtk_font_options);
+
+		gtk_imhtml_append_text(GTK_IMHTML(c->text), "<BR>", -1, 0);
 
 		if (logging_options & OPT_LOG_STRIP_HTML) {
 			char *t1, *t2;
--- a/src/convo.h	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/convo.h	Sat Dec 08 09:48:52 2001 +0000
@@ -78,6 +78,6 @@
 extern void send_callback(GtkWidget *, struct conversation *);
 extern int close_callback(GtkWidget *, struct conversation *);
 
-extern gboolean meify(char *);
+extern gboolean meify(char *, int);
 
 #endif /* _CONVO_H_ */
--- a/src/core.h	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/core.h	Sat Dec 08 09:48:52 2001 +0000
@@ -191,7 +191,7 @@
 
 /* Functions in server.c */
 extern void serv_got_update(struct gaim_connection *, char *, int, int, time_t, time_t, int, gushort);
-extern void serv_got_im(struct gaim_connection *, char *, char *, guint32, time_t);
+extern void serv_got_im(struct gaim_connection *, char *, char *, guint32, time_t, gint);
 extern void serv_got_eviled(struct gaim_connection *, char *, int);
 extern void serv_got_chat_invite(struct gaim_connection *, char *, char *, char *, GList *);
 extern struct conversation *serv_got_joined_chat(struct gaim_connection *, int, char *);
--- a/src/dialogs.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/dialogs.c	Sat Dec 08 09:48:52 2001 +0000
@@ -1846,14 +1846,14 @@
 	options ^= GTK_IMHTML_NO_NEWLINE;
 	options ^= GTK_IMHTML_NO_SCROLL;
 
-	gtk_imhtml_append_text(GTK_IMHTML(b->text), info, options);
+	gtk_imhtml_append_text(GTK_IMHTML(b->text), info, -1, options);
 
 	va_start(ap, info);
 	while ((more_info = va_arg(ap, char *)) != NULL)
-		 gtk_imhtml_append_text(GTK_IMHTML(b->text), more_info, options);
+		 gtk_imhtml_append_text(GTK_IMHTML(b->text), more_info, -1, options);
 	va_end(ap);
 
-	gtk_imhtml_append_text(GTK_IMHTML(b->text), "<BR>", 0);
+	gtk_imhtml_append_text(GTK_IMHTML(b->text), "<BR>", -1, 0);
 
 	if (away)
 		info_dlgs = g_slist_remove(info_dlgs, b);
@@ -3584,7 +3584,7 @@
 		g_string_append(string, buf);
 
 		if (i == 30) {
-			gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, view->options);
+			gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, -1, 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
@@ -3596,8 +3596,8 @@
 		}
 
 	}
-	gtk_imhtml_append_text(GTK_IMHTML(view->layout), string->str, view->options);
-	gtk_imhtml_append_text(GTK_IMHTML(view->layout), "<BR>", view->options);
+	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_widget_set_sensitive(view->bbox, TRUE);
 	gtk_signal_disconnect(GTK_OBJECT(view->window), block);
--- a/src/gaim.h	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/gaim.h	Sat Dec 08 09:48:52 2001 +0000
@@ -331,7 +331,7 @@
 extern void remove_chat_buddy(struct conversation *, char *, char *);
 
 /* Functions in conversation.c */
-extern void write_to_conv(struct conversation *, char *, int, char *, time_t);
+extern void write_to_conv(struct conversation *, char *, int, char *, time_t, int);
 extern struct conversation *find_conversation(char *);
 
 /* Functions in dialogs.c */
--- a/src/gtkimhtml.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/gtkimhtml.c	Sat Dec 08 09:48:52 2001 +0000
@@ -36,10 +36,10 @@
 #endif
 
 #if GTK_CHECK_VERSION(1,3,0)
-#define GTK_IMHTML_GET_STYLE_FONT(style) gtk_style_get_font (style)
+#  define GTK_IMHTML_GET_STYLE_FONT(style) gtk_style_get_font (style)
 #else
-#define GTK_IMHTML_GET_STYLE_FONT(style) (style)->font
-#define GTK_CLASS_TYPE(class) (class)->type
+#  define GTK_IMHTML_GET_STYLE_FONT(style) (style)->font
+#  define GTK_CLASS_TYPE(class) (class)->type
 #endif
 
 #include "pixmaps/angel.xpm"
@@ -289,7 +289,7 @@
 	GtkIMHtmlBit *bit;
 };
 
-struct url_widget {
+struct clickable {
 	gint x;
 	gint y;
 	gint width;
@@ -327,56 +327,11 @@
 
 	imhtml = GTK_IMHTML (object);
 
-	while (imhtml->bits) {
-		GtkIMHtmlBit *bit = imhtml->bits->data;
-		imhtml->bits = g_list_remove (imhtml->bits, bit);
-		if (bit->text)
-			g_free (bit->text);
-		if (bit->font)
-			gdk_font_unref (bit->font);
-		if (bit->fore)
-			gdk_color_free (bit->fore);
-		if (bit->back)
-			gdk_color_free (bit->back);
-		if (bit->bg)
-			gdk_color_free (bit->bg);
-		if (bit->url)
-			g_free (bit->url);
-		if (bit->pm)
-			gdk_pixmap_unref (bit->pm);
-		if (bit->bm)
-			gdk_bitmap_unref (bit->bm);
-		while (bit->chunks) {
-			struct line_info *li = bit->chunks->data;
-			if (li->text)
-				g_free (li->text);
-			bit->chunks = g_list_remove (bit->chunks, li);
-			g_free (li);
-		}
-		g_free (bit);
-	}
-
-	if (imhtml->line)
-		g_list_free (imhtml->line);
-
-	while (imhtml->urls) {
-		g_free (imhtml->urls->data);
-		imhtml->urls = g_list_remove (imhtml->urls, imhtml->urls->data);
-	}
+	gtk_imhtml_clear (imhtml);
 
 	if (imhtml->selected_text)
 		g_string_free (imhtml->selected_text, TRUE);
 
-	if (imhtml->tip_timer) {
-		gtk_timeout_remove (imhtml->tip_timer);
-		imhtml->tip_timer = 0;
-	}
-	if (imhtml->tip_window) {
-		gtk_widget_destroy (imhtml->tip_window);
-		imhtml->tip_window = NULL;
-	}
-	imhtml->tip_bit = NULL;
-
 	if (imhtml->default_font)
 		gdk_font_unref (imhtml->default_font);
 	if (imhtml->default_fg_color)
@@ -831,9 +786,9 @@
 	g_list_free (imhtml->line);
 	imhtml->line = NULL;
 
-	while (imhtml->urls) {
-		g_free (imhtml->urls->data);
-		imhtml->urls = g_list_remove (imhtml->urls, imhtml->urls->data);
+	while (imhtml->click) {
+		g_free (imhtml->click->data);
+		imhtml->click = g_list_remove (imhtml->click, imhtml->click->data);
 	}
 
 	imhtml->x = 0;
@@ -1454,11 +1409,11 @@
 				gtk_imhtml_select_in_chunk (imhtml, chunk);
 		}
 	} else {
-		GList *urls = imhtml->urls;
-		struct url_widget *uw;
-
-		while (urls) {
-			uw = (struct url_widget *) urls->data;
+		GList *click = imhtml->click;
+		struct clickable *uw;
+
+		while (click) {
+			uw = (struct clickable *) click->data;
 			if ((x > uw->x) && (x < uw->x + uw->width) &&
 			    (y > uw->y) && (y < uw->y + uw->height)) {
 				if (imhtml->tip_bit != uw->bit) {
@@ -1477,7 +1432,7 @@
 						       imhtml->hand_cursor);
 				return TRUE;
 			}
-			urls = g_list_next (urls);
+			click = g_list_next (click);
 		}
 	}
 
@@ -1519,7 +1474,7 @@
 menu_open_url (GtkObject *object,
 	       gpointer   data)
 {
-	struct url_widget *uw = data;
+	struct clickable *uw = data;
 
 	gtk_signal_emit (GTK_OBJECT (uw->imhtml), signals [URL_CLICKED], uw->bit->url);
 }
@@ -1528,7 +1483,7 @@
 menu_copy_link (GtkObject *object,
 		gpointer   data)
 {
-	struct url_widget *uw = data;
+	struct clickable *uw = data;
 	GtkIMHtml *imhtml = uw->imhtml;
 
 	if (imhtml->selected_text)
@@ -1562,29 +1517,31 @@
 	}
 
 	if (event->button == 3) {
-		GList *urls = imhtml->urls;
-		struct url_widget *uw;
-
-		while (urls) {
-			uw = urls->data;
+		GList *click = imhtml->click;
+		struct clickable *uw;
+
+		while (click) {
+			uw = click->data;
 			if ((x > uw->x) && (x < uw->x + uw->width) &&
 			    (y > uw->y) && (y < uw->y + uw->height)) {
 				GtkWidget *menu = gtk_menu_new ();
-
-				GtkWidget *button = gtk_menu_item_new_with_label ("Open URL");
-				gtk_signal_connect (GTK_OBJECT (button), "activate",
-						    GTK_SIGNAL_FUNC (menu_open_url), uw);
-				gtk_menu_append (GTK_MENU (menu), button);
-				gtk_widget_show (button);
-
-				button = gtk_menu_item_new_with_label ("Copy Link Location");
-				gtk_signal_connect (GTK_OBJECT (button), "activate",
-						    GTK_SIGNAL_FUNC (menu_copy_link), uw);
-				gtk_menu_append (GTK_MENU (menu), button);
-				gtk_widget_show (button);
-
-				gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL,
-						3, event->time);
+				GtkWidget *button;
+
+				if (uw->bit->url) {
+					button = gtk_menu_item_new_with_label ("Open URL");
+					gtk_signal_connect (GTK_OBJECT (button), "activate",
+							    GTK_SIGNAL_FUNC (menu_open_url), uw);
+					gtk_menu_append (GTK_MENU (menu), button);
+					gtk_widget_show (button);
+
+					button = gtk_menu_item_new_with_label ("Copy Link Location");
+					gtk_signal_connect (GTK_OBJECT (button), "activate",
+							    GTK_SIGNAL_FUNC (menu_copy_link), uw);
+					gtk_menu_append (GTK_MENU (menu), button);
+					gtk_widget_show (button);
+				}
+
+				gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 3, event->time);
 
 				if (imhtml->tip_timer) {
 					gtk_timeout_remove (imhtml->tip_timer);
@@ -1598,7 +1555,7 @@
 
 				return TRUE;
 			}
-			urls = g_list_next (urls);
+			click = g_list_next (click);
 		}
 	}
 
@@ -1632,18 +1589,18 @@
 	}
 
 	if ((event->button == 1) && (imhtml->sel_startx == 0)) {
-		GList *urls = imhtml->urls;
-		struct url_widget *uw;
-
-		while (urls) {
-			uw = (struct url_widget *) urls->data;
+		GList *click = imhtml->click;
+		struct clickable *uw;
+
+		while (click) {
+			uw = (struct clickable *) click->data;
 			if ((x > uw->x) && (x < uw->x + uw->width) &&
 			    (y > uw->y) && (y < uw->y + uw->height)) {
 				gtk_signal_emit (GTK_OBJECT (imhtml), signals [URL_CLICKED],
 						 uw->bit->url);
 				return TRUE;
 			}
-			urls = g_list_next (urls);
+			click = g_list_next (click);
 		}
 	}
 
@@ -2092,7 +2049,7 @@
 	gtk_imhtml_set_adjustments (imhtml, hadj, vadj);
 
 	imhtml->bits = NULL;
-	imhtml->urls = NULL;
+	imhtml->click = NULL;
 
 	imhtml->x = 0;
 	imhtml->y = TOP_BORDER;
@@ -2222,7 +2179,7 @@
 	gint diff;
 	GList *ls = NULL;
 	struct line_info *li;
-	struct url_widget *uw;
+	struct clickable *uw;
 
 	if (height > imhtml->llheight) {
 		diff = height - imhtml->llheight;
@@ -2238,7 +2195,7 @@
 			ls = g_list_next (ls);
 		}
 
-		ls = imhtml->urls;
+		ls = imhtml->click;
 		while (ls) {
 			uw = ls->data;
 			if (uw->y + diff > imhtml->y)
@@ -2260,7 +2217,7 @@
 		   gchar        *text)
 {
 	struct line_info *li;
-	struct url_widget *uw;
+	struct clickable *uw;
 	gint width;
 
 	if (text)
@@ -2281,14 +2238,14 @@
 	li->bit = bit;
 
 	if (bit->url) {
-		uw = g_new0 (struct url_widget, 1);
+		uw = g_new0 (struct clickable, 1);
 		uw->x = imhtml->x;
 		uw->y = imhtml->y;
 		uw->width = width;
 		uw->height = imhtml->llheight;
 		uw->imhtml = imhtml;
 		uw->bit = bit;
-		imhtml->urls = g_list_append (imhtml->urls, uw);
+		imhtml->click = g_list_append (imhtml->click, uw);
 	}
 
 	bit->chunks = g_list_append (bit->chunks, li);
@@ -2300,7 +2257,7 @@
 		  GtkIMHtmlBit *bit)
 {
 	struct line_info *li;
-	struct url_widget *uw;
+	struct clickable *uw;
 	gint width;
 
 	gdk_window_get_size (bit->pm, &width, NULL);
@@ -2314,14 +2271,14 @@
 	li->bit = bit;
 
 	if (bit->url) {
-		uw = g_new0 (struct url_widget, 1);
+		uw = g_new0 (struct clickable, 1);
 		uw->x = imhtml->x;
 		uw->y = imhtml->y;
 		uw->width = width;
 		uw->height = imhtml->llheight;
 		uw->imhtml = imhtml;
 		uw->bit = bit;
-		imhtml->urls = g_list_append (imhtml->urls, uw);
+		imhtml->click = g_list_append (imhtml->click, uw);
 	}
 
 	bit->chunks = g_list_append (bit->chunks, li);
@@ -2482,11 +2439,13 @@
 	return gdk_color_copy (&c);
 }
 
-static gint
+static gboolean
 gtk_imhtml_is_smiley (GtkIMHtml   *imhtml,
-		      const gchar *text)
+		      const gchar *text,
+		      gint        *len)
 {
-	return gtk_smiley_tree_lookup (imhtml->smiley_data, text);
+	*len = gtk_smiley_tree_lookup (imhtml->smiley_data, text);
+	return (*len > 0);
 }
 
 static GtkIMHtmlBit *
@@ -2500,7 +2459,9 @@
 		    FontDetail *font,
 		    GdkColor   *bg,
 		    gchar      *url,
-		    gint	pre)
+		    gint        pre,
+		    gint        sub,
+		    gint        sup)
 {
 	GtkIMHtmlBit *bit = NULL;
 
@@ -2571,17 +2532,19 @@
 }
 
 #define NEW_TEXT_BIT    gtk_imhtml_new_bit (imhtml, TYPE_TEXT, ws, bold, italics, underline, strike, \
-				fonts ? fonts->data : NULL, bg, url, pre)
+				fonts ? fonts->data : NULL, bg, url, pre, sub, sup)
 #define NEW_SMILEY_BIT  gtk_imhtml_new_bit (imhtml, TYPE_SMILEY, ws, bold, italics, underline, strike, \
-				fonts ? fonts->data : NULL, bg, url, pre)
-#define NEW_SEP_BIT     gtk_imhtml_new_bit (imhtml, TYPE_SEP, NULL, 0, 0, 0, 0, NULL, bg, NULL, 0)
+				fonts ? fonts->data : NULL, bg, url, pre, sub, sup)
+#define NEW_SEP_BIT     gtk_imhtml_new_bit (imhtml, TYPE_SEP, NULL, 0, 0, 0, 0, NULL, bg, NULL, 0, 0, 0)
 #define NEW_BR_BIT      gtk_imhtml_new_bit (imhtml, TYPE_BR, NULL, 0, 0, 0, 0, \
-				fonts ? fonts->data : NULL, bg, NULL, 0)
+				fonts ? fonts->data : NULL, bg, NULL, 0, 0, 0)
 #define NEW_COMMENT_BIT gtk_imhtml_new_bit (imhtml, TYPE_COMMENT, ws, bold, italics, underline, strike, \
-				fonts ? fonts->data : NULL, bg, url, pre)
-
-#define NEW_BIT(bit) { GtkIMHtmlBit *tmp = bit; if (tmp != NULL) \
-				newbits = g_list_append (newbits, tmp); }
+				fonts ? fonts->data : NULL, bg, url, pre, sub, sup)
+
+#define NEW_BIT(bit)	ws [wpos] = '\0';				\
+			{ GtkIMHtmlBit *tmp = bit; if (tmp != NULL)	\
+			  newbits = g_list_append (newbits, tmp); }	\
+			wpos = 0; ws [wpos] = '\0'
 
 #define UPDATE_BG_COLORS \
 	{ \
@@ -2614,9 +2577,9 @@
 	}
 
 static gboolean
-is_amp_escape (const gchar *string,
-	       gchar       *replace,
-	       gint        *length)
+gtk_imhtml_is_amp_escape (const gchar *string,
+			  gchar       *replace,
+			  gint        *length)
 {
 	g_return_val_if_fail (string != NULL, FALSE);
 	g_return_val_if_fail (replace != NULL, FALSE);
@@ -2662,20 +2625,169 @@
 	return TRUE;
 }
 
+#define VALID_TAG(x)	if (!g_strncasecmp (string, x ">", strlen (x ">"))) {	\
+				*tag = g_strndup (string, strlen (x));		\
+				*len = strlen (x) + 1;				\
+				return TRUE;					\
+			}							\
+			(*type)++
+
+#define VALID_OPT_TAG(x)	if (!g_strncasecmp (string, x " ", strlen (x " "))) {	\
+					const gchar *c = string + strlen (x " ");	\
+					gchar e = '"';					\
+					gboolean quote = FALSE;				\
+					while (*c) {					\
+						if (*c == '"' || *c == '\'') {		\
+							if (quote && (*c == e))		\
+								quote = !quote;		\
+							else if (!quote) {		\
+								quote = !quote;		\
+								e = *c;			\
+							}				\
+						} else if (!quote && (*c == '>'))	\
+							break;				\
+						c++;					\
+					}						\
+					if (*c) {					\
+						*tag = g_strndup (string, c - string);	\
+						*len = c - string + 1;			\
+						return TRUE;				\
+					}						\
+				}							\
+				(*type)++
+
+static gboolean
+gtk_imhtml_is_tag (const gchar *string,
+		   gchar      **tag,
+		   gint        *len,
+		   gint        *type)
+{
+	*type = 1;
+
+	if (!strchr (string, '>'))
+		return FALSE;
+
+	VALID_TAG ("B");
+	VALID_TAG ("BOLD");
+	VALID_TAG ("/B");
+	VALID_TAG ("/BOLD");
+	VALID_TAG ("I");
+	VALID_TAG ("ITALIC");
+	VALID_TAG ("/I");
+	VALID_TAG ("/ITALIC");
+	VALID_TAG ("U");
+	VALID_TAG ("UNDERLINE");
+	VALID_TAG ("/U");
+	VALID_TAG ("/UNDERLINE");
+	VALID_TAG ("S");
+	VALID_TAG ("STRIKE");
+	VALID_TAG ("/S");
+	VALID_TAG ("/STRIKE");
+	VALID_TAG ("SUB");
+	VALID_TAG ("/SUB");
+	VALID_TAG ("SUP");
+	VALID_TAG ("/SUP");
+	VALID_TAG ("PRE");
+	VALID_TAG ("/PRE");
+	VALID_TAG ("TITLE");
+	VALID_TAG ("/TITLE");
+	VALID_TAG ("BR");
+	VALID_TAG ("HR");
+	VALID_TAG ("/FONT");
+	VALID_TAG ("/A");
+	VALID_TAG ("P");
+	VALID_TAG ("/P");
+	VALID_TAG ("H3");
+	VALID_TAG ("/H3");
+	VALID_TAG ("HTML");
+	VALID_TAG ("/HTML");
+	VALID_TAG ("BODY");
+	VALID_TAG ("/BODY");
+	VALID_TAG ("FONT");
+	VALID_TAG ("HEAD");
+	VALID_TAG ("HEAD");
+
+	VALID_OPT_TAG ("HR");
+	VALID_OPT_TAG ("FONT");
+	VALID_OPT_TAG ("BODY");
+	VALID_OPT_TAG ("A");
+	VALID_OPT_TAG ("IMG");
+	VALID_OPT_TAG ("P");
+	VALID_OPT_TAG ("H3");
+
+	if (!g_strncasecmp(string, "!--", strlen ("!--"))) {
+		gchar *e = strstr (string, "-->");
+		if (e) {
+			*len = e - string + strlen ("-->");
+			*tag = g_strndup (string + strlen ("!--"), *len - strlen ("!---->"));
+			return TRUE;
+		}
+	}
+
+	return FALSE;
+}
+
+static gchar*
+gtk_imhtml_get_html_opt (gchar       *tag,
+			 const gchar *opt)
+{
+	gchar *t = tag;
+	gchar *e, *a;
+
+	while (g_strncasecmp (t, opt, strlen (opt))) {
+		gboolean quote = FALSE;
+		if (*t == '\0') break;
+		while (*t && !((*t == ' ') && !quote)) {
+			if (*t == '\"')
+				quote = ! quote;
+			t++;
+		}
+		while (*t && (*t == ' ')) t++;
+	}
+
+	if (!g_strncasecmp (t, opt, strlen (opt))) {
+		t += strlen (opt);
+	} else {
+		return NULL;
+	}
+
+	if ((*t == '\"') || (*t == '\'')) {
+		e = a = ++t;
+		while (*e && (*e != *(t - 1))) e++;
+		if (*e != '\0') {
+			*e = '\0';
+			return g_strdup (a);
+		} else {
+			return NULL;
+		}
+	} else {
+		e = a = t;
+		while (*e && !isspace ((gint) *e)) e++;
+		*e = '\0';
+		return g_strdup (a);
+	}
+}
+
 GString*
 gtk_imhtml_append_text (GtkIMHtml        *imhtml,
 			const gchar      *text,
+			gint              len,
 			GtkIMHtmlOptions  options)
 {
 	const gchar *c;
-	gboolean intag = FALSE;
-	gint tagquote = 0;
-	gboolean incomment = FALSE;
+	gboolean binary = TRUE;
 	gchar *ws;
-	gchar *tag;
+	gint pos = 0;
 	gint wpos = 0;
-	gint tpos = 0;
+
+	gchar *tag;
+	gint tlen;
+	gint type;
+
+	gchar amp;
+
 	int smilelen;
+
 	GList *newbits = NULL;
 
 	guint	bold = 0,
@@ -2708,345 +2820,184 @@
 		scrolldown = FALSE;
 
 	c = text;
-	ws = g_malloc (strlen (text) + 1);
-	tag = g_malloc (strlen (text) + 1);
-
+	if (len == -1) {
+		binary = FALSE;
+		len = strlen (text);
+	}
+
+	ws = g_malloc (len + 1);
 	ws [0] = '\0';
 
-	while (*c) {
-		if (*c == '<') {
-			if (intag && (tagquote != 1)) {
-				char *d;
-				tag [tpos] = 0;
-				d = tag;
-				while (*d) {
-					if ((smilelen = gtk_imhtml_is_smiley (imhtml, d)) != 0) {
-						ws [wpos] = 0;
-						wpos = 0;
-						NEW_BIT (NEW_TEXT_BIT);
-						g_snprintf (ws, smilelen + 1, "%s", d);
-						NEW_BIT (NEW_SMILEY_BIT);
-						d += smilelen;
-					} else if (*d == '&') {
-						gchar replace;
-						gint length;
-						if (is_amp_escape (d, &replace, &length)) {
-							ws [wpos++] = replace;
-							d += length;
-						} else {
-							ws [wpos++] = *d++;
-						}
-					} else if (*d == '\n') {
-						if (!(options & GTK_IMHTML_NO_NEWLINE)) {
-							ws [wpos] = 0;
-							wpos = 0;
-							NEW_BIT (NEW_TEXT_BIT);
-							NEW_BIT (NEW_BR_BIT);
-						}
-						d++;
-					} else {
-						ws [wpos++] = *d++;
-					}
-				}
-				tpos = 0;
-			}
-
-			if (incomment) {
-				ws [wpos++] = *c++;
-				continue;
-			}
-
-			if (!g_strncasecmp (c, "<!--", strlen ("<!--"))) {
-				if (!(options & GTK_IMHTML_NO_COMMENTS)) {
-					ws [wpos] = 0;
-					wpos = 0;
-					tag [tpos] = 0;
-					strcat (tag, ws);
-					incomment = TRUE;
-					intag = FALSE;
-				}
-				ws [wpos++] = *c++;
-				ws [wpos++] = *c++;
-				ws [wpos++] = *c++;
-				ws [wpos++] = *c++;
-				continue;
-			}
-
-			tag [tpos++] = *c++;
-			intag = TRUE;
-			tagquote = 0;
-		} else if (incomment && (*c == '-') && !g_strncasecmp (c, "-->", strlen ("-->"))) {
-			gchar *tmp;
-			ws [wpos] = 0;
-			wpos = 0;
-			tmp = g_strdup (ws);
-			ws [wpos] = 0;
-			strcat (ws, tag);
-			NEW_BIT (NEW_TEXT_BIT);
-			ws [wpos] = 0;
-			strcat (ws, tmp + strlen ("<!--"));
-			g_free (tmp);
-			NEW_BIT (NEW_COMMENT_BIT);
-			incomment = FALSE;
-			c += strlen ("-->");
-		} else if (*c == '>' && intag && (tagquote != 1)) {
-			gboolean got_tag = FALSE;
-			tag [tpos++] = *c++;
-			tag [tpos] = 0;
-			ws [wpos] = 0;
-
-			if (!g_strcasecmp (tag, "<B>") || !g_strcasecmp (tag, "<BOLD>")) {
-				got_tag = TRUE;
+	while (pos < len) {
+		if (*c == '<' && gtk_imhtml_is_tag (c + 1, &tag, &tlen, &type)) {
+			c++;
+			pos++;
+			switch (type) {
+			case 1:		/* B */
+			case 2:		/* BOLD */
 				NEW_BIT (NEW_TEXT_BIT);
 				bold++;
-			} else if (!g_strcasecmp (tag, "</B>") || !g_strcasecmp (tag, "</BOLD>")) {
-				got_tag = TRUE;
+				break;
+			case 3:		/* /B */
+			case 4:		/* /BOLD */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (bold) {
+				if (bold)
 					bold--;
-				}
-			} else if (!g_strcasecmp (tag, "<I>") || !g_strcasecmp (tag, "<ITALIC>")) {
-				got_tag = TRUE;
+				break;
+			case 5:		/* I */
+			case 6:		/* ITALIC */
 				NEW_BIT (NEW_TEXT_BIT);
 				italics++;
-			} else if (!g_strcasecmp (tag, "</I>") || !g_strcasecmp (tag, "</ITALIC>")) {
-				got_tag = TRUE;
+				break;
+			case 7:		/* /I */
+			case 8:		/* /ITALIC */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (italics) {
+				if (italics)
 					italics--;
-				}
-			} else if (!g_strcasecmp (tag, "<U>") || !g_strcasecmp (tag, "<UNDERLINE>")) {
-				got_tag = TRUE;
+				break;
+			case 9:		/* U */
+			case 10:	/* UNDERLINE */
 				NEW_BIT (NEW_TEXT_BIT);
 				underline++;
-			} else if (!g_strcasecmp (tag, "</U>") || !g_strcasecmp (tag, "</UNDERLINE>")) {
-				got_tag = TRUE;
+				break;
+			case 11:	/* /U */
+			case 12:	/* /UNDERLINE */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (underline) {
+				if (underline)
 					underline--;
-				}
-			} else if (!g_strcasecmp (tag, "<S>") || !g_strcasecmp (tag, "<STRIKE>")) {
-				got_tag = TRUE;
+				break;
+			case 13:	/* S */
+			case 14:	/* STRIKE */
 				NEW_BIT (NEW_TEXT_BIT);
 				strike++;
-			} else if (!g_strcasecmp (tag, "</S>") || !g_strcasecmp (tag, "</STRIKE>")) {
-				got_tag = TRUE;
+				break;
+			case 15:	/* /S */
+			case 16:	/* /STRIKE */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (strike) {
+				if (strike)
 					strike--;
-				}
-			} else if (!g_strcasecmp (tag, "<SUB>")) {
-				got_tag = TRUE;
+				break;
+			case 17:	/* SUB */
 				NEW_BIT (NEW_TEXT_BIT);
 				sub++;
-			} else if (!g_strcasecmp (tag, "</SUB>")) {
-				got_tag = TRUE;
+				break;
+			case 18:	/* /SUB */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (sub) {
+				if (sub)
 					sub--;
-				}
-			} else if (!g_strcasecmp (tag, "<SUP>")) {
-				got_tag = TRUE;
+				break;
+			case 19:	/* SUP */
 				NEW_BIT (NEW_TEXT_BIT);
 				sup++;
-			} else if (!g_strcasecmp (tag, "</SUP>")) {
-				got_tag = TRUE;
+				break;
+			case 20:	/* /SUP */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (sup) {
+				if (sup)
 					sup--;
-				}
-			} else if (!g_strcasecmp (tag, "<PRE>")) {
-				got_tag = TRUE;
+				break;
+			case 21:	/* PRE */
 				NEW_BIT (NEW_TEXT_BIT);
 				pre++;
-			} else if (!g_strcasecmp (tag, "</PRE>")) {
-				got_tag = TRUE;
+				break;
+			case 22:	/* /PRE */
+				NEW_BIT (NEW_TEXT_BIT);
+				if (pre)
+					pre--;
+				break;
+			case 23:	/* TITLE */
 				NEW_BIT (NEW_TEXT_BIT);
-				if (pre) {
-					pre--;
+				title++;
+				break;
+			case 24:	/* /TITLE */
+				if (title) {
+					if (options & GTK_IMHTML_NO_TITLE) {
+						wpos = 0;
+						ws [wpos] = '\0';
+					}
+					title--;
 				}
-			} else if (!g_strcasecmp (tag, "<TITLE>")) {
-				if (options & GTK_IMHTML_NO_TITLE) {
-					got_tag = TRUE;
-					NEW_BIT (NEW_TEXT_BIT);
-					title++;
-				} else {
-					intag = FALSE;
-					tpos = 0;
-					continue;
-				}
-			} else if (!g_strcasecmp (tag, "</TITLE>")) {
-				if (title) {
-					got_tag = TRUE;
-					wpos = 0;
-					ws [wpos] = '\0';
-					title--;
-				} else {
-					intag = FALSE;
-					tpos = 0;
-					continue;
-				}
-			} else if (!g_strcasecmp (tag, "<BR>")) {
-				got_tag = TRUE;
+				break;
+			case 25:	/* BR */
 				NEW_BIT (NEW_TEXT_BIT);
 				NEW_BIT (NEW_BR_BIT);
-			} else if (!g_strcasecmp (tag, "<HR>") ||
-				   !g_strncasecmp (tag, "<HR ", strlen ("<HR "))) {
-				got_tag = TRUE;
+				break;
+			case 26:	/* HR */
 				NEW_BIT (NEW_TEXT_BIT);
 				NEW_BIT (NEW_SEP_BIT);
-			} else if (!g_strncasecmp (tag, "<FONT ", strlen ("<FONT "))) {
-				gchar *t, *e, *a, *value;
-				FontDetail *font = NULL;
-				GdkColor *clr;
-				gint saw;
-				gint i;
-
-				t = tag + strlen ("<FONT ");
-
-				while (*t != '\0') {
-					value = NULL;
-					saw = 0;
-
-					while (g_strncasecmp (t, "COLOR=", strlen ("COLOR="))
-					    && g_strncasecmp (t, "BACK=", strlen ("BACK="))
-					    && g_strncasecmp (t, "FACE=", strlen ("FACE="))
-					    && g_strncasecmp (t, "SIZE=", strlen ("SIZE="))) {
-						gboolean quote = FALSE;
-						if (*t == '\0') break;
-						while (*t && !((*t == ' ') && !quote)) {
-							if (*t == '\"')
-								quote = ! quote;
-							t++;
-						}
-						while (*t && (*t == ' ')) t++;
-					}
-
-					if (!g_strncasecmp (t, "COLOR=", strlen ("COLOR="))) {
-						t += strlen ("COLOR=");
-						saw = 1;
-					} else if (!g_strncasecmp (t, "BACK=", strlen ("BACK="))) {
-						t += strlen ("BACK=");
-						saw = 2;
-					} else if (!g_strncasecmp (t, "FACE=", strlen ("FACE="))) {
-						t += strlen ("FACE=");
-						saw = 3;
-					} else if (!g_strncasecmp (t, "SIZE=", strlen ("SIZE="))) {
-						t += strlen ("SIZE=");
-						saw = 4;
-					}
-
-					if (!saw)
-						continue;
-
-					if ((*t == '\"') || (*t == '\'')) {
-						e = a = ++t;
-						while (*e && (*e != *(t - 1))) e++;
-						if (*e != '\0') {
-							*e = '\0';
-							t = e + 1;
-							value = g_strdup (a);
-						} else {
-							*t = '\0';
-						}
-					} else {
-						e = a = t;
-						while (*e && !isspace ((gint) *e)) e++;
-						if (*e == '\0') e--;
-						*e = '\0';
-						t = e + 1;
-						value = g_strdup (a);
-					}
-
-					if (value == NULL)
-						continue;
-
-					if (font == NULL)
-						font = g_new0 (FontDetail, 1);
-
-					switch (saw) {
-					case 1:
-						clr = gtk_imhtml_get_color (value);
-						if (clr != NULL) {
-							if ( (font->fore == NULL) &&
-							    !(options & GTK_IMHTML_NO_COLOURS))
-								font->fore = clr;
-						}
-						break;
-					case 2:
-						clr = gtk_imhtml_get_color (value);
-						if (clr != NULL) {
-							if ( (font->back == NULL) &&
-							    !(options & GTK_IMHTML_NO_COLOURS))
-								font->back = clr;
-						}
-						break;
-					case 3:
-						if ( (font->face == NULL) &&
-						    !(options & GTK_IMHTML_NO_FONTS))
-							font->face = g_strdup (value);
-						break;
-					case 4:
-						if ((font->size != 0) ||
-						    (options & GTK_IMHTML_NO_SIZES))
-							break;
-
-						if (isdigit ((gint) value [0])) {
-							for (i = 0; i < strlen (value); i++)
-								if (!isdigit ((gint) value [i]))
-									break;
-							if (i != strlen (value))
-								break;
-
-							sscanf (value, "%hd", &font->size);
-							break;
-						}
-
-						if ((value [0] == '+') && (value [1] != '\0')) {
-							for (i = 1; i < strlen (value); i++)
-								if (!isdigit ((gint) value [i]))
-									break;
-							if (i != strlen (value))
-								break;
-
-							sscanf (value + 1, "%hd", &font->size);
-							font->size += DEFAULT_FONT_SIZE;
-							break;
-						}
-
-						if ((value [0] == '-') && (value [1] != '\0')) {
-							for (i = 1; i < strlen (value); i++)
-								if (!isdigit ((gint) value [i]))
-									break;
-							if (i != strlen (value))
-								break;
-
-							sscanf (value + 1, "%hd", &font->size);
-							font->size = MIN (font->size, 2);
-							font->size = DEFAULT_FONT_SIZE - font->size;
-							break;
-						}
-
-						break;
-					}
-
-					g_free (value);
+				break;
+			case 27:	/* /FONT */
+				if (fonts) {
+					FontDetail *font = fonts->data;
+					NEW_BIT (NEW_TEXT_BIT);
+					fonts = g_slist_remove (fonts, font);
+					if (font->face)
+						g_free (font->face);
+					if (font->fore)
+						gdk_color_free (font->fore);
+					if (font->back)
+						gdk_color_free (font->back);
+					g_free (font);
+				}
+				break;
+			case 28:	/* /A */
+				if (url) {
+					NEW_BIT (NEW_TEXT_BIT);
+					g_free (url);
+					url = NULL;
 				}
-
-				if (!font || !(font->size || font->face || font->fore || font->back)) {
-					g_free (font);
-					intag = FALSE;
-					tpos = 0;
-					continue;
-				}
+				break;
+			case 29:	/* P */
+			case 30:	/* /P */
+			case 31:	/* H3 */
+			case 32:	/* /H3 */
+			case 33:	/* HTML */
+			case 34:	/* /HTML */
+			case 35:	/* BODY */
+			case 36:	/* /BODY */
+			case 37:	/* FONT */
+			case 38:	/* HEAD */
+			case 39:	/* /HEAD */
+				break;
+
+			case 40:	/* HR (opt) */
+				NEW_BIT (NEW_TEXT_BIT);
+				NEW_BIT (NEW_SEP_BIT);
+				break;
+			case 41:	/* FONT (opt) */
+			{
+				gchar *color, *back, *face, *size;
+				FontDetail *font;
+
+				color = gtk_imhtml_get_html_opt (tag, "COLOR=");
+				back = gtk_imhtml_get_html_opt (tag, "BACK=");
+				face = gtk_imhtml_get_html_opt (tag, "FACE=");
+				size = gtk_imhtml_get_html_opt (tag, "SIZE=");
+
+				if (!(color || back || face || size))
+					break;
 
 				NEW_BIT (NEW_TEXT_BIT);
 
+				font = g_new0 (FontDetail, 1);
+				if (color && !(options & GTK_IMHTML_NO_COLOURS))
+					font->fore = gtk_imhtml_get_color (color);
+				if (back && !(options & GTK_IMHTML_NO_COLOURS))
+					font->back = gtk_imhtml_get_color (back);
+				if (face && !(options & GTK_IMHTML_NO_FONTS))
+					font->face = g_strdup (face);
+				if (size && !(options & GTK_IMHTML_NO_SIZES))
+					sscanf (size, "%hd", &font->size);
+
+				g_free (color);
+				g_free (back);
+				g_free (face);
+				g_free (size);
+
 				if (fonts) {
 					FontDetail *oldfont = fonts->data;
 					if (!font->size)
 						font->size = oldfont->size;
-					if (!font->face && oldfont->face)
+					if (!font->face && oldfont->face) 
 						font->face = g_strdup (oldfont->face);
 					if (!font->fore && oldfont->fore)
 						font->fore = gdk_color_copy (oldfont->fore);
@@ -3058,192 +3009,47 @@
 				}
 
 				fonts = g_slist_prepend (fonts, font);
-				got_tag = TRUE;
-			} else if (!g_strcasecmp (tag, "</FONT>")) {
-				FontDetail *font;
-
-				if (fonts) {
-					got_tag = TRUE;
-					NEW_BIT (NEW_TEXT_BIT);
-					font = fonts->data;
-					fonts = g_slist_remove (fonts, font);
-					if (font->face)
-						g_free (font->face);
-					if (font->fore)
-						gdk_color_free (font->fore);
-					if (font->back)
-						gdk_color_free (font->back);
-					g_free (font);
-				} else {
-					intag = FALSE;
-					tpos = 0;
-					continue;
-				}
-			} else if (!g_strncasecmp (tag, "<BODY ", strlen ("<BODY "))) {
-				gchar *t, *e, *color = NULL;
-				GdkColor *tmp;
-
-				got_tag = TRUE;
-
-				if (!(options & GTK_IMHTML_NO_COLOURS)) {
-					t = tag + strlen ("<BODY");
-					do {
-						gboolean quote = FALSE;
-						if (*t == '\0') break;
-						while (*t && !((*t == ' ') && !quote)) {
-							if (*t == '\"')
-								quote = ! quote;
-							t++;
-						}
-						while (*t && (*t == ' ')) t++;
-					} while (g_strncasecmp (t, "BGCOLOR=", strlen ("BGCOLOR=")));
-
-					if (!g_strncasecmp (t, "BGCOLOR=", strlen ("BGCOLOR="))) {
-						t += strlen ("BGCOLOR=");
-						if ((*t == '\"') || (*t == '\'')) {
-							e = ++t;
-							while (*e && (*e != *(t - 1))) e++;
-							if (*e != '\0') {
-								*e = '\0';
-								color = g_strdup (t);
-							}
-						} else {
-							e = t;
-							while (*e && !isspace ((gint) *e)) e++;
-							if (*e == '\0') e--;
-							*e = '\0';
-							color = g_strdup (t);
-						}
-
-						if (color != NULL) {
-							tmp = gtk_imhtml_get_color (color);
-							g_free (color);
-							if (tmp != NULL) {
-								NEW_BIT (NEW_TEXT_BIT);
-								bg = tmp;
-								UPDATE_BG_COLORS;
-							}
-						}
+			}
+				break;
+			case 42:	/* BODY (opt) */
+			{
+				gchar *bgcolor = gtk_imhtml_get_html_opt (tag, "BGCOLOR=");
+				if (bgcolor) {
+					GdkColor *tmp = gtk_imhtml_get_color (bgcolor);
+					g_free (bgcolor);
+					if (tmp) {
+						NEW_BIT (NEW_TEXT_BIT);
+						bg = tmp;
+						UPDATE_BG_COLORS;
 					}
 				}
-			} else if (!g_strncasecmp (tag, "<A ", strlen ("<A "))) {
-				gchar *t, *e;
-
-				got_tag = TRUE;
-				NEW_BIT (NEW_TEXT_BIT);
-
-				if (url != NULL)
-					g_free (url);
-				url = NULL;
-
-				t = tag + strlen ("<A");
-				do {
-					gboolean quote = FALSE;
-					if (*t == '\0') break;
-					while (*t && !((*t == ' ') && !quote)) {
-						if (*t == '\"')
-							quote = ! quote;
-						t++;
-					}
-					while (*t && (*t == ' ')) t++;
-				} while (g_strncasecmp (t, "HREF=", strlen ("HREF=")));
-
-				if (!g_strncasecmp (t, "HREF=", strlen ("HREF="))) {
-					t += strlen ("HREF=");
-					if ((*t == '\"') || (*t == '\'')) {
-						e = ++t;
-						while (*e && (*e != *(t - 1))) e++;
-						if (*e != '\0') {
-							*e = '\0';
-							url = g_strdup (t);
-						}
-					} else {
-						e = t;
-						while (*e && !isspace ((gint) *e)) e++;
-						if (*e == '\0') e--;
-						*e = '\0';
-						url = g_strdup (t);
-					}
-				}
-			} else if (!g_strcasecmp (tag, "</A>")) {
-				if (url != NULL) {
-					got_tag = TRUE;
+			}
+				break;
+			case 43:	/* A (opt) */
+			{
+				gchar *href = gtk_imhtml_get_html_opt (tag, "HREF=");
+				if (href) {
 					NEW_BIT (NEW_TEXT_BIT);
 					g_free (url);
-					url = NULL;
-				} else {
-					intag = FALSE;
-					tpos = 0;
-					continue;
-				}
-			} else if (!g_strncasecmp (tag, "<IMG ", strlen ("<IMG "))) {
-				gchar *t, *e, *src = NULL;
-				gchar *copy = g_strdup (tag);
-				gchar **xpm;
-				GdkColor *clr = NULL;
-				GtkIMHtmlBit *bit;
-
-				intag = FALSE;
-				tpos = 0;
-
-				if (imhtml->img == NULL) {
-					ws [wpos] = 0;
-					strcat (ws, copy);
-					wpos = strlen (ws);
-					g_free (copy);
-					continue;
+					url = href;
 				}
-
-				t = tag + strlen ("<IMG");
-				do {
-					gboolean quote = FALSE;
-					if (*t == '\0') break;
-					while (*t && !((*t == ' ') && !quote)) {
-						if (*t == '\"')
-							quote = ! quote;
-						t++;
-					}
-					while (*t && (*t == ' ')) t++;
-				} while (g_strncasecmp (t, "SRC=", strlen ("SRC=")));
-
-				if (!g_strncasecmp (t, "SRC=", strlen ("SRC="))) {
-					t += strlen ("SRC=");
-					if ((*t == '\"') || (*t == '\'')) {
-						e = ++t;
-						while (*e && (*e != *(t - 1))) e++;
-						if (*e != '\0') {
-							*e = '\0';
-							src = g_strdup (t);
-						}
-					} else {
-						e = t;
-						while (*e && !isspace ((gint) *e)) e++;
-						if (*e == '\0') e--;
-						*e = '\0';
-						src = g_strdup (t);
-					}
+			}
+				break;
+			case 44:	/* IMG (opt) */
+			{
+				gchar *src = gtk_imhtml_get_html_opt (tag, "SRC=");
+				gchar **xpm;
+				GdkColor *clr;
+				GtkIMHtmlBit *bit;
+
+				if (!src)
+					break;
+
+				if (!imhtml->img || ((xpm = imhtml->img (src)) == NULL)) {
+					g_free (src);
+					break;
 				}
 
-				if (src == NULL) {
-					ws [wpos] = 0;
-					strcat (ws, copy);
-					wpos = strlen (ws);
-					g_free (copy);
-					continue;
-				}
-
-				xpm = (* imhtml->img) (src);
-				if (xpm == NULL) {
-					g_free (src);
-					ws [wpos] = 0;
-					strcat (ws, copy);
-					wpos = strlen (ws);
-					g_free (copy);
-					continue;
-				}
-
-				g_free (copy);
-
 				if (!fonts || ((clr = ((FontDetail *) fonts->data)->back) == NULL))
 					clr = (bg != NULL) ? bg : imhtml->default_bg_color;
 
@@ -3253,96 +3059,33 @@
 				bit = g_new0 (GtkIMHtmlBit, 1);
 				bit->type = TYPE_IMG;
 				bit->pm = gdk_pixmap_create_from_xpm_d (GTK_WIDGET (imhtml)->window,
-									&bit->bm,
-									clr,
-									xpm);
+									&bit->bm, clr, xpm);
 				if (url)
 					bit->url = g_strdup (url);
 
 				NEW_BIT (bit);
 
 				g_free (src);
-
-				continue;
-			} else if (!g_strcasecmp (tag, "<P>") ||
-				   !g_strcasecmp (tag, "</P>") ||
-				   !g_strncasecmp (tag, "<P ", strlen ("<P ")) ||
-				   !g_strcasecmp (tag, "<H3>") ||
-				   !g_strncasecmp (tag, "<H3 ", strlen ("<H3 ")) ||
-				   !g_strcasecmp (tag, "</H3>") ||
-				   !g_strcasecmp (tag, "<HTML>") ||
-				   !g_strcasecmp (tag, "</HTML>") ||
-				   !g_strcasecmp (tag, "<BODY>") ||
-				   !g_strcasecmp (tag, "</BODY>") ||
-				   !g_strcasecmp (tag, "<FONT>") ||
-				   !g_strcasecmp (tag, "<HEAD>") ||
-				   !g_strcasecmp (tag, "</HEAD>")) {
-				intag = FALSE;
-				tpos = 0;
-				continue;
 			}
-
-			if (!got_tag) {
-				char *d;
-				tag [tpos] = 0;
-				d = tag;
-				while (*d) {
-					if ((smilelen = gtk_imhtml_is_smiley (imhtml, d)) != 0) {
-						ws [wpos] = 0;
-						wpos = 0;
-						NEW_BIT (NEW_TEXT_BIT);
-						g_snprintf (ws, smilelen + 1, "%s", d);
-						NEW_BIT (NEW_SMILEY_BIT);
-						d += smilelen;
-					} else if (*d == '&') {
-						gchar replace;
-						gint length;
-						if (is_amp_escape (d, &replace, &length)) {
-							ws [wpos++] = replace;
-							d += length;
-						} else {
-							ws [wpos++] = *d++;
-						}
-					} else if (*d == '\n') {
-						if (!(options & GTK_IMHTML_NO_NEWLINE)) {
-							ws [wpos] = 0;
-							wpos = 0;
-							NEW_BIT (NEW_TEXT_BIT);
-							NEW_BIT (NEW_BR_BIT);
-						}
-						d++;
-					} else {
-						ws [wpos++] = *d++;
-					}
-				}
-				tpos = 0;
-			} else {
-				wpos = 0;
+				break;
+			case 45:	/* P (opt) */
+			case 46:	/* H3 (opt) */
+				break;
+			case 47:	/* comment */
+				NEW_BIT (NEW_TEXT_BIT);
+				wpos = g_snprintf (ws, len, "%s", tag);
+				NEW_BIT (NEW_COMMENT_BIT);
+				break;
+			default:
+				break;
 			}
-			intag = FALSE;
-			tpos = 0;
-		} else if (*c == '&' && !intag) {
-			gchar replace;
-			gint length;
-			if (is_amp_escape (c, &replace, &length)) {
-				ws [wpos++] = replace;
-				c += length;
-			} else {
-				ws [wpos++] = *c++;
-			}
-		} else if (intag) {
-			if (*c == '\"')
-				tagquote++;
-			tag [tpos++] = *c++;
-		} else if (incomment) {
-			ws [wpos++] = *c++;
-		} else if (((smilelen = gtk_imhtml_is_smiley (imhtml, c)) != 0)) {
-			ws [wpos] = 0;
-			wpos = 0;
-			NEW_BIT (NEW_TEXT_BIT);
-			g_snprintf (ws, smilelen + 1, "%s", c);
-			NEW_BIT (NEW_SMILEY_BIT);
-			c += smilelen;
+			g_free (tag);
+			c += tlen;
+			pos += tlen;
+		} else if (*c == '&' && gtk_imhtml_is_amp_escape (c, &amp, &tlen)) {
+			ws [wpos++] = amp;
+			c += tlen;
+			pos += tlen;
 		} else if (*c == '\n') {
 			if (!(options & GTK_IMHTML_NO_NEWLINE)) {
 				ws [wpos] = 0;
@@ -3351,77 +3094,21 @@
 				NEW_BIT (NEW_BR_BIT);
 			}
 			c++;
-		} else {
+			pos++;
+		} else if (gtk_imhtml_is_smiley (imhtml, c, &smilelen)) {
+			ws [wpos] = 0;
+			wpos = 0;
+			NEW_BIT (NEW_TEXT_BIT);
+			g_snprintf (ws, smilelen + 1, "%s", c);
+			wpos = smilelen + 1;
+			NEW_BIT (NEW_SMILEY_BIT);
+			c += smilelen;
+			pos += smilelen;
+		} else if (*c) {
 			ws [wpos++] = *c++;
-		}
-	}
-
-	if (intag) {
-		tag [tpos] = 0;
-		c = tag;
-		while (*c) {
-			if ((smilelen = gtk_imhtml_is_smiley (imhtml, c)) != 0) {
-				ws [wpos] = 0;
-				wpos = 0;
-				NEW_BIT (NEW_TEXT_BIT);
-				g_snprintf (ws, smilelen + 1, "%s", c);
-				NEW_BIT (NEW_SMILEY_BIT);
-				c += smilelen;
-			} else if (*c == '&') {
-				gchar replace;
-				gint length;
-				if (is_amp_escape (c, &replace, &length)) {
-					ws [wpos++] = replace;
-					c += length;
-				} else {
-					ws [wpos++] = *c++;
-				}
-			} else if (*c == '\n') {
-				if (!(options & GTK_IMHTML_NO_NEWLINE)) {
-					ws [wpos] = 0;
-					wpos = 0;
-					NEW_BIT (NEW_TEXT_BIT);
-					NEW_BIT (NEW_BR_BIT);
-				}
-				c++;
-			} else {
-				ws [wpos++] = *c++;
-			}
-		}
-	} else if (incomment) {
-		ws [wpos] = 0;
-		wpos = 0;
-		strcat (tag, ws);
-		ws [wpos] = 0;
-		c = tag;
-		while (*c) {
-			if ((smilelen = gtk_imhtml_is_smiley (imhtml, c)) != 0) {
-				ws [wpos] = 0;
-				wpos = 0;
-				NEW_BIT (NEW_TEXT_BIT);
-				g_snprintf (ws, smilelen + 1, "%s", c);
-				NEW_BIT (NEW_SMILEY_BIT);
-				c += smilelen;
-			} else if (*c == '&') {
-				gchar replace;
-				gint length;
-				if (is_amp_escape (c, &replace, &length)) {
-					ws [wpos++] = replace;
-					c += length;
-				} else {
-					ws [wpos++] = *c++;
-				}
-			} else if (*c == '\n') {
-				if (!(options & GTK_IMHTML_NO_NEWLINE)) {
-					ws [wpos] = 0;
-					wpos = 0;
-					NEW_BIT (NEW_TEXT_BIT);
-					NEW_BIT (NEW_BR_BIT);
-				}
-				c++;
-			} else {
-				ws [wpos++] = *c++;
-			}
+			pos++;
+		} else {
+			break;
 		}
 	}
 
@@ -3518,7 +3205,6 @@
 		}
 	}
 	g_free (ws);
-	g_free (tag);
 
 	return retval;
 }
@@ -3562,9 +3248,9 @@
 		g_free (bit);
 	}
 
-	while (imhtml->urls) {
-		g_free (imhtml->urls->data);
-		imhtml->urls = g_list_remove (imhtml->urls, imhtml->urls->data);
+	while (imhtml->click) {
+		g_free (imhtml->click->data);
+		imhtml->click = g_list_remove (imhtml->click, imhtml->click->data);
 	}
 
 	if (imhtml->selected_text) {
@@ -3593,8 +3279,6 @@
 		imhtml->scroll_timer = 0;
 	}
 
-	gdk_window_set_cursor (GTK_LAYOUT (imhtml)->bin_window, imhtml->arrow_cursor);
-
 	imhtml->x = 0;
 	imhtml->y = TOP_BORDER;
 	imhtml->xsize = 0;
@@ -3616,11 +3300,12 @@
 	layout->vadjustment->upper = imhtml->y;
 	gtk_adjustment_set_value (layout->vadjustment, 0);
 
-	gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
-	gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
-
-	if (GTK_WIDGET_REALIZED (GTK_WIDGET (imhtml)))
+	if (GTK_WIDGET_REALIZED (GTK_WIDGET (imhtml))) {
+		gdk_window_set_cursor (GTK_LAYOUT (imhtml)->bin_window, imhtml->arrow_cursor);
 		gdk_window_clear (GTK_LAYOUT (imhtml)->bin_window);
+		gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
+		gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
+	}
 }
 
 void
--- a/src/gtkimhtml.h	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/gtkimhtml.h	Sat Dec 08 09:48:52 2001 +0000
@@ -53,7 +53,7 @@
 	GdkCursor *arrow_cursor;
 
 	GList *bits;
-	GList *urls;
+	GList *click;
 	struct _GtkIMHtmlBit *tip_bit;
 	GtkWidget *tip_window;
 	guint tip_timer;
@@ -127,6 +127,7 @@
 
 GString*   gtk_imhtml_append_text      (GtkIMHtml        *imhtml,
 					const gchar      *text,
+					gint              len,
 					GtkIMHtmlOptions  options);
 
 void       gtk_imhtml_clear            (GtkIMHtml        *imhtml);
--- a/src/perl.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/perl.c	Sat Dec 08 09:48:52 2001 +0000
@@ -564,7 +564,7 @@
 	if (!c)
 		c = new_conversation(nick);
 		
-	write_to_conv(c, what, wflags, who, time((time_t)NULL));
+	write_to_conv(c, what, wflags, who, time(NULL), -1);
 	XSRETURN(0);
 }
 
@@ -612,7 +612,7 @@
 	if (!c)
 		c = new_conversation(nick);
 	set_convo_gc(c, gc);
-	write_to_conv(c, what, WFLAG_SEND | (isauto ? WFLAG_AUTO : 0), NULL, time((time_t)NULL));
+	write_to_conv(c, what, WFLAG_SEND | (isauto ? WFLAG_AUTO : 0), NULL, time(NULL), -1);
 	serv_send_im(c->gc, nick, what, isauto ? IM_FLAG_AWAY : 0);
 	XSRETURN(0);
 }
--- a/src/prefs.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/prefs.c	Sat Dec 08 09:48:52 2001 +0000
@@ -1738,9 +1738,9 @@
 	/* Fill the text box with new message */
 	strcpy(buffer, a->message);
 	tmp = stylize(buffer, BUF_LONG);
-	gtk_imhtml_append_text(GTK_IMHTML(away_text), tmp, GTK_IMHTML_NO_TITLE |
+	gtk_imhtml_append_text(GTK_IMHTML(away_text), tmp, -1, GTK_IMHTML_NO_TITLE |
 			       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
-	gtk_imhtml_append_text(GTK_IMHTML(away_text), "<BR>", GTK_IMHTML_NO_TITLE |
+	gtk_imhtml_append_text(GTK_IMHTML(away_text), "<BR>", -1, GTK_IMHTML_NO_TITLE |
 			       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
 	g_free(tmp);
 }
@@ -1988,9 +1988,9 @@
 		a = (struct away_message *)awy->data;
 		g_snprintf(buffer, sizeof(buffer), "%s", a->message);
 		tmp = stylize(buffer, BUF_LONG);
-		gtk_imhtml_append_text(GTK_IMHTML(away_text), tmp, GTK_IMHTML_NO_TITLE |
+		gtk_imhtml_append_text(GTK_IMHTML(away_text), tmp, -1, GTK_IMHTML_NO_TITLE |
 				       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
-		gtk_imhtml_append_text(GTK_IMHTML(away_text), "<BR>", GTK_IMHTML_NO_TITLE |
+		gtk_imhtml_append_text(GTK_IMHTML(away_text), "<BR>", -1, GTK_IMHTML_NO_TITLE |
 				       GTK_IMHTML_NO_COMMENTS | GTK_IMHTML_NO_SCROLL);
 		g_free(tmp);
 	}
--- a/src/protocols/gg/gg.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/gg/gg.c	Sat Dec 08 09:48:52 2001 +0000
@@ -1,6 +1,6 @@
 /*
  * gaim - Gadu-Gadu Protocol Plugin
- * $Id: gg.c 2859 2001-12-05 09:48:56Z warmenhoven $
+ * $Id: gg.c 2869 2001-12-08 09:48:52Z warmenhoven $
  *
  * Copyright (C) 2001 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
  * 
@@ -369,7 +369,7 @@
 			imsg = charset_convert(e->event.msg.message, "CP1250", find_local_charset());
 			strip_linefeed(imsg);
 			/* e->event.msg.time - we don't know what this time is for */
-			serv_got_im(gc, user, imsg, 0, time((time_t) NULL));
+			serv_got_im(gc, user, imsg, 0, time(NULL), -1);
 			g_free(imsg);
 		}
 		break;
--- a/src/protocols/icq/gaim_icq.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/icq/gaim_icq.c	Sat Dec 08 09:48:52 2001 +0000
@@ -114,7 +114,7 @@
 	g_snprintf(tmp, BUF_LONG, "%s", data);
 	g_snprintf(buf, sizeof buf, "%lu", uin);
 	strip_linefeed(tmp);
-	serv_got_im(gc, buf, tmp, 0, time((time_t)NULL));
+	serv_got_im(gc, buf, tmp, 0, time(NULL), -1);
 	g_free(tmp);
 }
 
@@ -169,7 +169,7 @@
 	char *msg = g_malloc(BUF_LONG), buf[256];
 	g_snprintf(msg, BUF_LONG, "<A HREF=\"%s\">%s</A>", url, descr);
 	g_snprintf(buf, 256, "%lu", uin);
-	serv_got_im(gc, buf, msg, 0, time((time_t)NULL));
+	serv_got_im(gc, buf, msg, 0, time(NULL), -1);
 	g_free(msg);
 }
 
@@ -211,7 +211,7 @@
 	char *who = g_strdup_printf("ICQ Web Pager: %s (%s)", nick, email);
 	char *what = g_malloc(BUF_LONG);
 	g_snprintf(what, BUF_LONG, "%s", msg);
-	serv_got_im(gc, who, what, 0, time((time_t)NULL));
+	serv_got_im(gc, who, what, 0, time(NULL), -1);
 	g_free(who);
 	g_free(what);
 }
@@ -223,7 +223,7 @@
 	char *who = g_strdup_printf("ICQ Mail Express: %s (%s)", nick, email);
 	char *what = g_malloc(BUF_LONG);
 	g_snprintf(what, BUF_LONG, "%s", msg);
-	serv_got_im(gc, who, what, 0, time((time_t)NULL));
+	serv_got_im(gc, who, what, 0, time(NULL), -1);
 	g_free(who);
 	g_free(what);
 }
--- a/src/protocols/irc/irc.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/irc/irc.c	Sat Dec 08 09:48:52 2001 +0000
@@ -322,7 +322,7 @@
 static void irc_got_im(struct gaim_connection *gc, char *who, char *what, int flags, time_t t)
 {
 	GString *str = decode_html(what);
-	serv_got_im(gc, who, str->str, flags, t);
+	serv_got_im(gc, who, str->str, flags, t, -1);
 	g_string_free(str, TRUE);
 }
 
@@ -428,7 +428,7 @@
 		chat_set_topic(c, NULL, po);
 		g_snprintf(buf, sizeof(buf), _("<B>%s has changed the topic to: %s</B>"),
 				text, po);
-		write_to_conv(c, buf, WFLAG_SYSTEM, NULL, time(NULL));
+		write_to_conv(c, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 	}
 }
 
@@ -961,7 +961,7 @@
 			chat_set_topic(c, nick, topic);
 			g_snprintf(buf, sizeof(buf), _("<B>%s has changed the topic to: %s</B>"),
 					nick, topic);
-			write_to_conv(c, buf, WFLAG_SYSTEM, NULL, time(NULL));
+			write_to_conv(c, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 		}
 	} else if (!strcmp(cmd, "WALLOPS")) { /* */
 	}
@@ -1377,7 +1377,7 @@
 				 "JOIN PART TOPIC KICK<BR>"
 				 "OP DEOP VOICE DEVOICE<BR>"
 				 "ME MSG QUOTE SAY</B>",
-				 WFLAG_NOLOG, NULL, time(NULL));
+				 WFLAG_NOLOG, NULL, time(NULL), -1);
 	} else {
 		struct conversation *c = NULL;
 		if (is_channel(gc, who)) {
@@ -1387,7 +1387,7 @@
 		}
 		if (!c)
 			return -EINVAL;
-		write_to_conv(c, "<B>Unknown command</B>", WFLAG_NOLOG, NULL, time(NULL));
+		write_to_conv(c, "<B>Unknown command</B>", WFLAG_NOLOG, NULL, time(NULL), -1);
 	}
 
 	return 0;
--- a/src/protocols/jabber/jabber.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/jabber/jabber.c	Sat Dec 08 09:48:52 2001 +0000
@@ -622,10 +622,10 @@
 				if (xmlnode_get_tag(p->x, "gaim"))
 					flags = IM_FLAG_GAIMUSER;
 				if (find_conversation(jid_full(p->from)))
-					serv_got_im(GJ_GC(j), jid_full(p->from), m, flags, time(NULL));
+					serv_got_im(GJ_GC(j), jid_full(p->from), m, flags, time(NULL), -1);
 				else {
 					from = g_strdup_printf("%s@%s", p->from->user, p->from->server);
-					serv_got_im(GJ_GC(j), from, m, flags, time(NULL));
+					serv_got_im(GJ_GC(j), from, m, flags, time(NULL), -1);
 					g_free(from);
 				}
 			}
@@ -690,7 +690,7 @@
 					msg = xmlnode_get_data(y);
 					g_snprintf(buf, sizeof(buf), "%s now has status: %s",
 							p->from->resource, msg);
-					write_to_conv(jc->b, buf, WFLAG_SYSTEM, NULL, time(NULL));
+					write_to_conv(jc->b, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 				}
 			} else if (jc->b && msg) {
 				char buf[8192];
@@ -847,7 +847,7 @@
 					char *msg = xmlnode_get_data(y);
 					g_snprintf(buf, sizeof(buf), "%s now has status: %s",
 							p->from->resource, msg);
-					write_to_conv(jc->b, buf, WFLAG_SYSTEM, NULL, time(NULL));
+					write_to_conv(jc->b, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 				}
 			}
 		}
--- a/src/protocols/msn/msn.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/msn/msn.c	Sat Dec 08 09:48:52 2001 +0000
@@ -520,7 +520,7 @@
 		if (ms->chat)
 			serv_got_chat_in(ms->gc, ms->chat->id, ms->msguser, flags, utf, time(NULL));
 		else
-			serv_got_im(ms->gc, ms->msguser, utf, flags, time(NULL));
+			serv_got_im(ms->gc, ms->msguser, utf, flags, time(NULL), -1);
 
 		g_free(utf);
 	}
@@ -1084,7 +1084,7 @@
 	utf = utf8_to_str(skiphead);
 	strip_linefeed(utf);
 
-	serv_got_im(gc, md->msguser, utf, 0, time(NULL));
+	serv_got_im(gc, md->msguser, utf, 0, time(NULL), -1);
 
 	g_free(utf);
 }
@@ -1498,7 +1498,7 @@
 		ms->fd = -1;
 	} else
 		/* in msn you can't send messages to yourself, so we'll fake like we received it ;) */
-		serv_got_im(gc, who, message, flags | IM_FLAG_GAIMUSER, time(NULL));
+		serv_got_im(gc, who, message, flags | IM_FLAG_GAIMUSER, time(NULL), -1);
 	return 1;
 }
 
--- a/src/protocols/napster/napster.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/napster/napster.c	Sat Dec 08 09:48:52 2001 +0000
@@ -201,7 +201,7 @@
 
 	if (command == 0xCD) {
 		res = g_strsplit(buf, " ", 1);
-		serv_got_im(gc, res[0], res[1], 0, time((time_t)NULL));
+		serv_got_im(gc, res[0], res[1], 0, time(NULL), -1);
 		g_strfreev(res);
 		g_free(buf);
 		return;
--- a/src/protocols/oscar/oscar.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/oscar/oscar.c	Sat Dec 08 09:48:52 2001 +0000
@@ -304,7 +304,7 @@
 
 	g_snprintf(buf, sizeof buf, _("Direct IM with %s closed"), sn);
 	if ((cnv = find_conversation(sn)))
-		write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
+		write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 
 	g_free(dim); /* I guess? I don't see it anywhere else... -- mid */
 	g_free(sn);
@@ -1202,7 +1202,7 @@
 	if (!(cnv = find_conversation(dim->name))) 
 		cnv = new_conversation(dim->name);
 	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), dim->name);
-	write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
+	write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 
 	od->direct_ims = g_slist_append(od->direct_ims, dim);
 
@@ -1342,7 +1342,7 @@
 		g_snprintf(tmp, BUF_LONG, "%s", args->msg);
 
 	strip_linefeed(tmp);
-	serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL));
+	serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1);
 	g_free(tmp);
 
 	return 1;
@@ -2070,7 +2070,7 @@
 		time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0);
 		g_snprintf(sender, sizeof(sender), "%lu", msg->sender);
 		strip_linefeed(tmp);
-		serv_got_im(gc, sender, tmp, 0, t);
+		serv_got_im(gc, sender, tmp, 0, t, -1);
 		g_free(tmp);
 	} else {
 		debug_printf("unknown offline message type 0x%04x\n", msg->type);
@@ -2640,7 +2640,7 @@
 					oscar_callback, dim->conn);
 	g_snprintf(buf, sizeof buf, _("Direct IM with %s established"), sn);
 	g_free(sn);
-	write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time((time_t)NULL));
+	write_to_conv(cnv, buf, WFLAG_SYSTEM, NULL, time(NULL), -1);
 
 	aim_conn_addhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING,
 				gaim_directim_incoming, 0);
@@ -2662,7 +2662,7 @@
 
 	debug_printf("Got DirectIM message from %s\n", sn);
 
-	serv_got_im(gc, sn, msg, 0, time((time_t)NULL));
+	serv_got_im(gc, sn, msg, 0, time(NULL), -1);
 
 	return 1;
 }
--- a/src/protocols/toc/toc.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/toc/toc.c	Sat Dec 08 09:48:52 2001 +0000
@@ -619,7 +619,7 @@
 
 		a = (away && (*away == 'T')) ? IM_FLAG_AWAY : 0;
 
-		serv_got_im(gc, c, message, a, time((time_t)NULL));
+		serv_got_im(gc, c, message, a, time(NULL), -1);
 	} else if (!strcasecmp(c, "UPDATE_BUDDY")) {
 		char *l, *uc, *tmp;
 		int logged, evil, idle, type = 0;
--- a/src/protocols/yahoo/yahoo.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/yahoo/yahoo.c	Sat Dec 08 09:48:52 2001 +0000
@@ -488,7 +488,7 @@
 			msg[j++] = m[i];
 		}
 		msg[j] = 0;
-		serv_got_im(gc, from, msg, 0, tm);
+		serv_got_im(gc, from, msg, 0, tm, -1);
 	} else if (pkt->status == 2) {
 		do_error_dialog(_("Your message did not get sent."), _("Gaim - Error"));
 	}
--- a/src/protocols/zephyr/zephyr.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/protocols/zephyr/zephyr.c	Sat Dec 08 09:48:52 2001 +0000
@@ -374,7 +374,7 @@
 					away = TRUE;
 				else
 					away = FALSE;
-				serv_got_im(zgc, notice.z_sender, buf2, 0, time((time_t)NULL));
+				serv_got_im(zgc, notice.z_sender, buf2, 0, time(NULL), -1);
 			} else {
 				zephyr_triple *zt1, *zt2;
 				zt1 = new_triple(notice.z_class, notice.z_class_inst,
--- a/src/server.c	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/server.c	Sat Dec 08 09:48:52 2001 +0000
@@ -427,7 +427,7 @@
 }
 
 /* woo. i'm actually going to comment this function. isn't that fun. make sure to follow along, kids */
-void serv_got_im(struct gaim_connection *gc, char *name, char *message, guint32 flags, time_t mtime)
+void serv_got_im(struct gaim_connection *gc, char *name, char *message, guint32 flags, time_t mtime, gint len)
 {
 	char *buffy;
 	char *angel;
@@ -457,26 +457,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. */
-	buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG));
-	strcpy(buffy, message);
-	angel = g_strdup(name);
-	plugin_return = plugin_event(event_im_recv, gc, &angel, &buffy, (void *)flags);
+	 * 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. */
+	if (len < 0) {
+		buffy = g_malloc(MAX(strlen(message) + 1, BUF_LONG));
+		strcpy(buffy, message);
+		angel = g_strdup(name);
+		plugin_return = plugin_event(event_im_recv, gc, &angel, &buffy, (void *)flags);
 
-	if (!buffy || !angel || plugin_return) {
-		if (buffy)
-			g_free(buffy);
-		if (angel)
-			g_free(angel);
-		return;
+		if (!buffy || !angel || plugin_return) {
+			if (buffy)
+				g_free(buffy);
+			if (angel)
+				g_free(angel);
+			return;
+		}
+		name = angel;
+		message = buffy;
+	} else {
+		name = g_strdup(name);
+		message = g_memdup(message, len);
 	}
-	name = angel;
-	message = buffy;
 
 	/* TiK, using TOC, sends an automated message in order to get your away message. Now,
 	 * this is one of the biggest hacks I think I've seen. But, in order to be nice to
 	 * TiK, we're going to give users the option to ignore it. */
-	if ((away_options & OPT_AWAY_TIK_HACK) && gc->away && strlen(gc->away) &&
+	if ((away_options & OPT_AWAY_TIK_HACK) && gc->away && strlen(gc->away) && (len < 0) &&
 	    !strcmp(message, ">>>Automated Message: Getting Away Message<<<")) {
 		char *tmpmsg = stylize(awaymessage->message, MSG_LEN);
 		serv_send_im(gc, name, tmpmsg, IM_FLAG_AWAY);
@@ -488,7 +494,7 @@
 
 	/* if you can't figure this out, stop reading right now.
 	 * "we're not worthy! we're not worthy!" */
-	if (convo_options & OPT_CONVO_SEND_LINKS)
+	if ((len < 0) && (convo_options & OPT_CONVO_SEND_LINKS))
 		linkify_text(message);
 
 	/* um. when we call write_to_conv with the message we received, it's nice to pass whether
@@ -523,10 +529,11 @@
 
 			qm = g_new0(struct queued_message, 1);
 			g_snprintf(qm->name, sizeof(qm->name), "%s", name);
-			qm->message = g_strdup(message);
+			qm->message = g_memdup(message, len == -1 ? strlen(message) + 1 : len);
 			qm->gc = gc;
 			qm->tm = mtime;
 			qm->flags = WFLAG_RECV | away;
+			qm->len = len;
 			message_queue = g_slist_append(message_queue, qm);
 
 #ifdef USE_APPLET
@@ -572,7 +579,7 @@
 			else if (cnv->makesound && (sound_options & OPT_SOUND_RECV))
 				play_sound(RECEIVE);
 
-			write_to_conv(cnv, message, away | WFLAG_RECV, NULL, mtime);
+			write_to_conv(cnv, message, away | WFLAG_RECV, NULL, mtime, len);
 		}
 
 		/* regardless of whether we queue it or not, we should send an auto-response. That is,
@@ -616,7 +623,7 @@
 			message_queue = g_slist_append(message_queue, qm);
 		} else if (cnv != NULL)
 			write_to_conv(cnv, away_subs(tmpmsg, alias), WFLAG_SEND | WFLAG_AUTO, NULL,
-				      mtime);
+				      mtime, len);
 		g_free(tmpmsg);
 	} else {
 		/* we're not away. this is easy. if the convo window doesn't exist, create and update
@@ -631,7 +638,7 @@
 		else if (cnv->makesound && (sound_options & OPT_SOUND_RECV))
 			play_sound(RECEIVE);
 
-		write_to_conv(cnv, message, away | WFLAG_RECV, NULL, mtime);
+		write_to_conv(cnv, message, away | WFLAG_RECV, NULL, mtime, len);
 	}
 
 	plugin_event(event_im_displayed_rcvd, gc, name, message, (void *)flags);
@@ -986,5 +993,5 @@
 
 	gtk_widget_show_all(window);
 
-	gtk_imhtml_append_text(GTK_IMHTML(text), msg, GTK_IMHTML_NO_NEWLINE);
+	gtk_imhtml_append_text(GTK_IMHTML(text), msg, -1, GTK_IMHTML_NO_NEWLINE);
 }
--- a/src/ui.h	Sat Dec 08 08:46:00 2001 +0000
+++ b/src/ui.h	Sat Dec 08 09:48:52 2001 +0000
@@ -202,6 +202,7 @@
 	time_t tm;
 	struct gaim_connection *gc;
 	int flags;
+	int len;
 };
 
 struct mod_user {