changeset 5093:89c0c811befa

[gaim-migrate @ 5455] jabber XHTML support. since people tend to not like to write valid XHTML all of the time, we now have html_to_xhtml() which does its best to figure out what you meant. i'm tired, hope this works for everyone committer: Tailor Script <tailor@pidgin.im>
author Nathan Walp <nwalp@pidgin.im>
date Thu, 10 Apr 2003 06:09:26 +0000
parents a4ad609ee6b3
children 6df2fde9740e
files ChangeLog src/gaim.h src/gtkimhtml.c src/html.c src/protocols/jabber/jabber.c src/util.c
diffstat 6 files changed, 194 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Apr 10 00:57:06 2003 +0000
+++ b/ChangeLog	Thu Apr 10 06:09:26 2003 +0000
@@ -3,6 +3,7 @@
 version 0.62
 	* Keyboard shortcuts in the buddy list work again (Thanks Joe 
 	 Clarke).
+	* Support for Jabber XHTML messages
 
 version 0.61 (04/07/2003):
 	* Split the buddy pounce core and UI, and rewrote the UI for it.
--- a/src/gaim.h	Thu Apr 10 00:57:06 2003 +0000
+++ b/src/gaim.h	Thu Apr 10 06:09:26 2003 +0000
@@ -371,6 +371,7 @@
 
 extern void grab_url(char *, gboolean, void (*callback)(gpointer, char *, unsigned long), gpointer);
 extern gchar *strip_html(const gchar *);
+extern char *html_to_xhtml(const char *);
 struct g_url *parse_url(char *url);
 
 /* Functions in idle.c */
--- a/src/gtkimhtml.c	Thu Apr 10 00:57:06 2003 +0000
+++ b/src/gtkimhtml.c	Thu Apr 10 06:09:26 2003 +0000
@@ -632,6 +632,9 @@
 	} else if (!g_ascii_strncasecmp (string, "&reg;", 5)) {
 		*replace = '®'; /* was: '®' */
 		*length = 5;
+	} else if (!g_ascii_strncasecmp (string, "&apos;", 6)) {
+		*replace = '\'';
+		*length = 6;
 	} else if (*(string + 1) == '#') {
 		guint pound = 0;
 		if ((sscanf (string, "&#%u;", &pound) == 1) && pound != 0) {
@@ -703,7 +706,7 @@
 	VALID_TAG ("/HEAD");
 	VALID_TAG ("BINARY");
 	VALID_TAG ("/BINARY");
-	
+
 	VALID_OPT_TAG ("HR");
 	VALID_OPT_TAG ("FONT");
 	VALID_OPT_TAG ("BODY");
@@ -711,6 +714,7 @@
 	VALID_OPT_TAG ("IMG");
 	VALID_OPT_TAG ("P");
 	VALID_OPT_TAG ("H3");
+	VALID_OPT_TAG ("HTML");
 
 	if (!g_ascii_strncasecmp(string, "!--", strlen ("!--"))) {
 		gchar *e = strstr (string + strlen("!--"), "-->");
@@ -1170,8 +1174,9 @@
 					}
 				case 47:	/* P (opt) */
 				case 48:	/* H3 (opt) */
+				case 49:	/* HTML (opt) */
 					break;
-				case 49:	/* comment */
+				case 50:	/* comment */
 					NEW_BIT (NEW_TEXT_BIT);
 					if (imhtml->show_comments)
 						wpos = g_snprintf (ws, len, "%s", tag);
--- a/src/html.c	Thu Apr 10 00:57:06 2003 +0000
+++ b/src/html.c	Thu Apr 10 06:09:26 2003 +0000
@@ -322,3 +322,138 @@
 		callback(data, g_strdup(_("g003: Error opening connection.\n")), 0);
 	}
 }
+
+#define ALLOW_TAG_ALT(x, y) if(!g_ascii_strncasecmp(c, "<" x " ", strlen("<" x " "))) { \
+						char *o = strchr(c+1, '<'); \
+						char *p = strchr(c+1, '>'); \
+						if(p && (!o || p < o)) { \
+							if(*(p-1) != '/') \
+								tags = g_list_prepend(tags, y); \
+							xhtml = g_string_append(xhtml, "<" y); \
+							c += strlen("<" x ); \
+							xhtml = g_string_append_len(xhtml, c, (p - c) + 1); \
+							c = p + 1; \
+						} else { \
+							xhtml = g_string_append(xhtml, "&lt;"); \
+						} \
+						continue; \
+					} \
+						if(!g_ascii_strncasecmp(c, "<" x, strlen("<" x)) && \
+								(*(c+strlen("<" x)) == '>' || \
+								 !g_ascii_strncasecmp(c+strlen("<" x), "/>", 2))) { \
+							xhtml = g_string_append(xhtml, "<" y); \
+							c += strlen("<" x); \
+							if(*c != '/') \
+								tags = g_list_prepend(tags, y); \
+							continue; \
+						}
+#define ALLOW_TAG(x) ALLOW_TAG_ALT(x, x)
+
+char *html_to_xhtml(const char *html) {
+	GString *xhtml = g_string_new("");
+	GList *tags = NULL, *tag;
+	const char *q = NULL, *c = html;
+	char *ret;
+	while(*c) {
+		if(!q && (*c == '\"' || *c == '\'')) {
+			q = c;
+			xhtml = g_string_append_c(xhtml, *c);
+			c++;
+		} else if(q) {
+			if(*c == *q) {
+				q = NULL;
+			} else if(*c == '\\') {
+				xhtml = g_string_append_c(xhtml, *c);
+				c++;
+			}
+			xhtml = g_string_append_c(xhtml, *c);
+			c++;
+		} else if(*c == '<') {
+			if(*(c+1) == '/') { /* closing tag */
+				tag = tags;
+				while(tag) {
+					if(!g_ascii_strncasecmp((c+2), tag->data, strlen(tag->data)) && *(c+strlen(tag->data)+2) == '>') {
+						c += strlen(tag->data) + 3;
+						break;
+					}
+					tag = tag->next;
+				}
+				if(tag) {
+					while(tags) {
+						g_string_append_printf(xhtml, "</%s>", (char *)tags->data);
+						if(tags == tag)
+							break;
+						tags = g_list_remove(tags, tags->data);
+					}
+					tags = g_list_remove(tags, tag->data);
+				} else {
+					/* we tried to close a tag we never opened! escape it
+					 * and move on */
+					xhtml = g_string_append(xhtml, "&lt;");
+					c++;
+				}
+			} else { /* opening tag */
+				ALLOW_TAG("a");
+				ALLOW_TAG("b");
+				ALLOW_TAG("blockquote");
+				ALLOW_TAG("body");
+				ALLOW_TAG_ALT("bold", "b");
+				ALLOW_TAG("br");
+				ALLOW_TAG("cite");
+				ALLOW_TAG("div");
+				ALLOW_TAG("em");
+				ALLOW_TAG("font");
+				ALLOW_TAG("h1");
+				ALLOW_TAG("h2");
+				ALLOW_TAG("h3");
+				ALLOW_TAG("h4");
+				ALLOW_TAG("h5");
+				ALLOW_TAG("h6");
+				ALLOW_TAG("head");
+				ALLOW_TAG("hr");
+				ALLOW_TAG("html");
+				ALLOW_TAG("i");
+				ALLOW_TAG_ALT("italic", "i");
+				ALLOW_TAG("li");
+				ALLOW_TAG("ol");
+				ALLOW_TAG("p");
+				ALLOW_TAG("pre");
+				ALLOW_TAG("q");
+				ALLOW_TAG_ALT("s", "strike");
+				ALLOW_TAG("span");
+				ALLOW_TAG("strike");
+				ALLOW_TAG("strong");
+				ALLOW_TAG("sub");
+				ALLOW_TAG("sup");
+				ALLOW_TAG("title");
+				ALLOW_TAG("u");
+				ALLOW_TAG_ALT("underline","u");
+				ALLOW_TAG("ul");
+
+				if(!g_ascii_strncasecmp(c, "<!--", strlen("<!--"))) {
+					char *p = strstr(c + strlen("<!--"), "-->");
+					if(p) {
+						xhtml = g_string_append(xhtml, "<!--");
+						c += strlen("<!--");
+						continue;
+					}
+				}
+
+				xhtml = g_string_append(xhtml, "&lt;");
+				c++;
+			}
+		} else {
+			xhtml = g_string_append_c(xhtml, *c);
+			c++;
+		}
+	}
+	tag = tags;
+	while(tag) {
+		g_string_append_printf(xhtml, "</%s>", (char *)tag->data);
+		tag = tag->next;
+	}
+	g_list_free(tags);
+	ret = g_strdup(xhtml->str);
+	g_string_free(xhtml, TRUE);
+	return ret;
+}
--- a/src/protocols/jabber/jabber.c	Thu Apr 10 00:57:06 2003 +0000
+++ b/src/protocols/jabber/jabber.c	Thu Apr 10 06:09:26 2003 +0000
@@ -192,6 +192,7 @@
 	char *away_msg;
 	char *thread_id;
 	gboolean has_composing;
+	gboolean has_xhtml;
 } *jab_res_info;
 
 /*
@@ -1106,6 +1107,7 @@
 			jri = g_new0(struct jabber_resource_info, 1);
 			jri->name = g_strdup(res);
 			jri->away_msg = NULL;
+			jri->has_xhtml = TRUE;
 			jbd->resources = g_slist_append(jbd->resources, jri);
 		}
 		jri->priority = priority;
@@ -1256,6 +1258,7 @@
 	xmlnode y, subj;
 	time_t time_sent = time(NULL);
 	gboolean typing = FALSE;
+	gboolean has_xhtml = TRUE;
 
 	char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL;
 	char *thread_id = NULL;
@@ -1287,13 +1290,11 @@
 	if (!type || !strcasecmp(type, "normal") || !strcasecmp(type, "chat")) {
 
 		from = jid_full(p->from);
-		/*
 		if ((y = xmlnode_get_tag(p->x, "html"))) {
+			msg = xmlnode2str(y);
+		} else if ((y = xmlnode_get_tag(p->x, "body"))) {
 			msg = xmlnode_get_data(y);
-		} else
-		*/
-		if ((y = xmlnode_get_tag(p->x, "body"))) {
-			msg = xmlnode_get_data(y);
+			has_xhtml = FALSE;
 		}
 
 		if (!from)
@@ -1320,8 +1321,11 @@
 			else {
 				int flags = 0;
 				jab_res_info jri = jabber_find_resource(GJ_GC(gjc), from);
-				if(jri && typing)
-					jri->has_composing = TRUE;
+				if(jri) {
+					if(typing)
+						jri->has_composing = TRUE;
+					jri->has_xhtml = has_xhtml;
+				}
 				if (xmlnode_get_tag(p->x, "gaim"))
 					flags = IM_FLAG_GAIMUSER;
 				jabber_track_convo_thread(gjc, from, thread_id);
@@ -1365,12 +1369,9 @@
 		struct jabber_chat *jc;
 		static int i = 0;
 
-		/*
 		if ((y = xmlnode_get_tag(p->x, "html"))) {
-			msg = xmlnode_get_data(y);
-		} else
-		*/
-		if ((y = xmlnode_get_tag(p->x, "body"))) {
+			msg = xmlnode2str(y);
+		} else if ((y = xmlnode_get_tag(p->x, "body"))) {
 			msg = xmlnode_get_data(y);
 		}
 
@@ -2421,11 +2422,36 @@
 	return JABBER_TYPING_NOTIFY_INT;
 }
 
+static void insert_message(xmlnode x, const char *message, gboolean use_xhtml) {
+	xmlnode y;
+	char *buf = strip_html(message);
+	y = xmlnode_insert_tag(x, "body");
+	xmlnode_insert_cdata(y, buf, -1);
+	g_free(buf);
+
+	if(use_xhtml) {
+		char *buf2 = g_strdup_printf("<html xmlns='http://jabber.org/protocol/xhtml-im'><body>%s</body></html>", message);
+		buf = html_to_xhtml(buf2);
+		g_free(buf2);
+
+		y = xmlnode_str(buf, strlen(buf));
+		if(y) {
+			xmlnode_insert_tag_node(x, y);
+			xmlnode_free(y);
+		} else {
+			debug_printf("holy cow, html_to_xhtml didn't work right!\n");
+			debug_printf("the invalid XML: %s\n", buf);
+		}
+		g_free(buf);
+	}
+}
+
 static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags)
 {
 	xmlnode x, y;
 	char *thread_id = NULL;
 	gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc;
+	jab_res_info jri = jabber_find_resource(gc, who);
 
 	if (!who || !message)
 		return 0;
@@ -2452,8 +2478,7 @@
 	xmlnode_insert_tag(y, "composing");
 
 	if (message && strlen(message)) {
-		y = xmlnode_insert_tag(x, "body");
-		xmlnode_insert_cdata(y, message, -1);
+		insert_message(x, message, jri ? jri->has_xhtml : TRUE);
 	}
 
 	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
@@ -2968,8 +2993,7 @@
 	g_free(subject);
 
 	if (message && strlen(message)) {
-		y = xmlnode_insert_tag(x, "body");
-		xmlnode_insert_cdata(y, message, -1);
+		insert_message(x, message, FALSE);
 	}
 
 	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
@@ -3024,8 +3048,7 @@
 		g_snprintf(buf, sizeof(buf), "/me has changed the subject to: %s", message + strlen("/topic"));
 		xmlnode_insert_cdata(y, buf, -1);
 	} else if (message && strlen(message)) {
-		y = xmlnode_insert_tag(x, "body");
-		xmlnode_insert_cdata(y, message, -1);
+		insert_message(x, message, FALSE);
 	}
 
 	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
@@ -3035,7 +3058,7 @@
 
 static void jabber_chat_whisper(struct gaim_connection *gc, int id, char *who, char *message)
 {
-	xmlnode x, y;
+	xmlnode x;
 	struct jabber_chat *jc = NULL;
 	char *chatname;
 
@@ -3051,8 +3074,7 @@
 	xmlnode_put_attrib(x, "type", "normal");
 
 	if (message && strlen(message)) {
-		y = xmlnode_insert_tag(x, "body");
-		xmlnode_insert_cdata(y, message, -1);
+		insert_message(x, message, FALSE);
 	}
 
 	gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x);
--- a/src/util.c	Thu Apr 10 00:57:06 2003 +0000
+++ b/src/util.c	Thu Apr 10 06:09:26 2003 +0000
@@ -139,7 +139,7 @@
 
 gint linkify_text(char *text)
 {
-	char *c, *t;
+	char *c, *t, *q = NULL;
 	char *cpy = g_malloc(strlen(text) * 3 + 1);
 	char url_buf[BUF_LEN * 4];
 	int cnt = 0;
@@ -149,7 +149,12 @@
 	cpy[strlen(text)] = 0;
 	c = cpy;
 	while (*c) {
-		if (!g_ascii_strncasecmp(c, "<A", 2)) {
+		if(!q && (*c == '\"' || *c == '\'')) {
+			q = c;
+		} else if(q) {
+			if(*c == *q)
+				q = NULL;
+		} else if (!g_ascii_strncasecmp(c, "<A", 2)) {
 			while (1) {
 				if (!g_ascii_strncasecmp(c, "/A>", 3)) {
 					break;