changeset 25660:751df82b78e7

propagate from branch 'im.pidgin.pidgin' (head 23720684a7671144bc56d1b20b6a1a0a22862d73) to branch 'im.pidgin.cpw.darkrain42.xmpp.iq-handlers' (head 3bdb82c347184599c389f7ed65d02c61f2708b89)
author Paul Aurich <paul@darkrain42.org>
date Tue, 10 Feb 2009 06:12:24 +0000
parents d720bedc091e (current diff) 481e07d69a10 (diff)
children 43c41aee8fcb
files libpurple/protocols/jabber/buddy.c
diffstat 20 files changed, 357 insertions(+), 271 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/protocols/jabber/buddy.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.c	Tue Feb 10 06:12:24 2009 +0000
@@ -155,6 +155,7 @@
 		jbr->jb = jb;
 		jbr->name = g_strdup(resource);
 		jbr->capabilities = JABBER_CAP_XHTML;
+		jbr->tz_off = PURPLE_NO_TZ_OFF;
 		jb->resources = g_list_append(jb->resources, jbr);
 	}
 	jbr->priority = priority;
@@ -800,7 +801,22 @@
 			if(jbr->client.os) {
 				purple_notify_user_info_prepend_pair(user_info, _("Operating System"), jbr->client.os);
 			}
-		}		
+		}
+		if (jbr && jbr->tz_off != PURPLE_NO_TZ_OFF) {
+			time_t now_t;
+			struct tm *now;
+			char *timestamp;
+			time(&now_t);
+			now_t += jbr->tz_off;
+			now = gmtime(&now_t);
+
+			timestamp = g_strdup_printf("%s %c%02d%02d", purple_time_format(now),
+			                            jbr->tz_off < 0 ? '-' : '+',
+			                            abs(jbr->tz_off / (60*60)),
+			                            abs((jbr->tz_off % (60*60)) / 60));
+			purple_notify_user_info_prepend_pair(user_info, _("Local Time"), timestamp);
+			g_free(timestamp);
+		}
 		if(jbir) {
 			if(jbir->idle_seconds > 0) {
 				char *idle = purple_str_seconds_to_string(jbir->idle_seconds);
@@ -971,6 +987,22 @@
 				}
 			}
 
+			if (jbr->tz_off != PURPLE_NO_TZ_OFF) {
+				time_t now_t;
+				struct tm *now;
+				char *timestamp;
+				time(&now_t);
+				now_t += jbr->tz_off;
+				now = gmtime(&now_t);
+
+				timestamp = g_strdup_printf("%s %c%02d%02d", purple_time_format(now),
+				                            jbr->tz_off < 0 ? '-' : '+',
+				                            abs(jbr->tz_off / (60*60)),
+				                            abs((jbr->tz_off % (60*60)) / 60));
+				purple_notify_user_info_prepend_pair(user_info, _("Local Time"), timestamp);
+				g_free(timestamp);
+			}
+
 			if(jbr->name && (jbir = g_hash_table_lookup(jbi->resources, jbr->name))) {
 				if(jbir->idle_seconds > 0) {
 					char *idle = purple_str_seconds_to_string(jbir->idle_seconds);
@@ -1009,7 +1041,8 @@
 					
 					if(!strcmp(feature, "jabber:iq:last"))
 						feature = _("Last Activity");
-					else if(!strcmp(feature, "http://jabber.org/protocol/disco#info"))
+					else if(!strcm
+				p(feature, "http://jabber.org/protocol/disco#info"))
 						feature = _("Service Discovery Info");
 					else if(!strcmp(feature, "http://jabber.org/protocol/disco#items"))
 						feature = _("Service Discovery Items");
@@ -1640,6 +1673,59 @@
 	jabber_buddy_info_show_if_ready(jbi);
 }
 
+static void jabber_time_parse(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	JabberBuddyInfo *jbi = data;
+	JabberBuddyResource *jbr;
+	char *resource_name;
+	const char *type, *id, *from;
+
+	g_return_if_fail(jbi != NULL);
+
+	id = xmlnode_get_attrib(packet, "id");
+	type = xmlnode_get_attrib(packet, "type");
+	from = xmlnode_get_attrib(packet, "from");
+
+	jabber_buddy_info_remove_id(jbi, id);
+
+	if (!from)
+		return;
+
+	resource_name = jabber_get_resource(from);
+	jbr = resource_name ? jabber_buddy_find_resource(jbi->jb, resource_name) : NULL;
+	g_free(resource_name);
+	if (jbr) {
+		if (type && !strcmp(type, "result")) {
+			xmlnode *time = xmlnode_get_child(packet, "time");
+			xmlnode *tzo = time ? xmlnode_get_child(time, "tzo") : NULL;
+			char *tzo_data = tzo ? xmlnode_get_data(tzo) : NULL;
+			if (tzo_data) {
+				char *c = tzo_data;
+				int hours, minutes;
+				if (tzo_data[0] == 'Z' && tzo_data[1] == '\0') {
+					jbr->tz_off = 0;
+				} else {
+					gboolean offset_positive = (tzo_data[0] == '+');
+					/* [+-]HH:MM */
+					if (((*c == '+' || *c == '-') && (c = c + 1)) &&
+							sscanf(c, "%02d:%02d", &hours, &minutes) == 2) {
+						jbr->tz_off = 60*60*hours + 60*minutes;
+						if (!offset_positive)
+							jbr->tz_off *= -1;
+					} else {
+						purple_debug_info("jabber", "Ignoring malformed timezone %s",
+						                  tzo_data);
+					}
+				}
+
+				g_free(tzo_data);
+			}
+		}
+	}
+
+	jabber_buddy_info_show_if_ready(jbi);
+}
+
 void jabber_buddy_remove_all_pending_buddy_info_requests(JabberStream *js)
 {
 	if (js->pending_buddy_info_requests)
@@ -1771,6 +1857,19 @@
 			jabber_iq_send(iq);
 		}
 
+		if (jbr->tz_off == PURPLE_NO_TZ_OFF &&
+				(!jbr->caps ||
+				 	jabber_resource_has_capability(jbr, "urn:xmpp:time"))) {
+			xmlnode *child;
+			iq = jabber_iq_new(js, JABBER_IQ_GET);
+			xmlnode_set_attrib(iq->node, "to", full_jid);
+			child = xmlnode_new_child(iq->node, "time");
+			xmlnode_set_namespace(child, "urn:xmpp:time");
+			jabber_iq_set_callback(iq, jabber_time_parse, jbi);
+			jbi->ids = g_slist_prepend(jbi->ids, g_strdup(iq->id));
+			jabber_iq_send(iq);
+		}
+
 		g_free(full_jid);
 	}
 
--- a/libpurple/protocols/jabber/buddy.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/buddy.h	Tue Feb 10 06:12:24 2009 +0000
@@ -81,6 +81,8 @@
 		char *name;
 		char *os;
 	} client;
+	/* tz_off == PURPLE_NO_TZ_OFF when unset */
+	long tz_off;
 	JabberCapsClientInfo *caps;
 	GList *commands;
 } JabberBuddyResource;
--- a/libpurple/protocols/jabber/data.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/data.c	Tue Feb 10 06:12:24 2009 +0000
@@ -200,25 +200,26 @@
 }
 
 void
-jabber_data_parse(JabberStream *js, xmlnode *packet)
+jabber_data_parse(JabberStream *js, const char *who, JabberIqType type,
+                  const char *id, xmlnode *data_node)
 {
 	JabberIq *result = NULL;
-	const char *who = xmlnode_get_attrib(packet, "from");
-	xmlnode *data_node = xmlnode_get_child(packet, "data");
-	const JabberData *data =
-		jabber_data_find_local_by_cid(xmlnode_get_attrib(data_node, "cid"));
+	const char *cid = xmlnode_get_attrib(data_node, "cid");
+	const JabberData *data = cid ? jabber_data_find_local_by_cid(cid) : NULL;
 
 	if (!data) {
 		xmlnode *item_not_found = xmlnode_new("item-not-found");
 
 		result = jabber_iq_new(js, JABBER_IQ_ERROR);
-		xmlnode_set_attrib(result->node, "to", who);
-		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+		if (who)
+			xmlnode_set_attrib(result->node, "to", who);
+		xmlnode_set_attrib(result->node, "id", id);
 		xmlnode_insert_child(result->node, item_not_found);
 	} else {
 		result = jabber_iq_new(js, JABBER_IQ_RESULT);
-		xmlnode_set_attrib(result->node, "to", who);
-		xmlnode_set_attrib(result->node, "id", xmlnode_get_attrib(packet, "id"));
+		if (who)
+			xmlnode_set_attrib(result->node, "to", who);
+		xmlnode_set_attrib(result->node, "id", id);
 		xmlnode_insert_child(result->node,
 							 jabber_data_get_xml_definition(data));
 	}
--- a/libpurple/protocols/jabber/data.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/data.h	Tue Feb 10 06:12:24 2009 +0000
@@ -65,7 +65,8 @@
 void jabber_data_associate_remote(JabberData *data);
 
 /* handles iq requests */
-void jabber_data_parse(JabberStream *js, xmlnode *packet);
+void jabber_data_parse(JabberStream *js, const char *who, JabberIqType type,
+                       const char *id, xmlnode *data_node);
 
 void jabber_data_init(void);
 void jabber_data_uninit(void);
--- a/libpurple/protocols/jabber/disco.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/disco.c	Tue Feb 10 06:12:24 2009 +0000
@@ -85,29 +85,22 @@
 }
 
 
-void jabber_disco_info_parse(JabberStream *js, xmlnode *packet) {
-	const char *from = xmlnode_get_attrib(packet, "from");
-	const char *type = xmlnode_get_attrib(packet, "type");
+void jabber_disco_info_parse(JabberStream *js, const char *from,
+                             JabberIqType type, const char *id,
+                             xmlnode *in_query) {
 
-	if(!from || !type)
+	if(!from)
 		return;
 
-	if(!strcmp(type, "get")) {
+	if(type == JABBER_IQ_GET) {
 		xmlnode *query, *identity, *feature;
 		JabberIq *iq;
-
-		xmlnode *in_query;
-		const char *node = NULL;
-
-		if((in_query = xmlnode_get_child(packet, "query"))) {
-			node = xmlnode_get_attrib(in_query, "node");
-		}
-
+		const char *node = xmlnode_get_attrib(in_query, "node");
 
 		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
 				"http://jabber.org/protocol/disco#info");
 
-		jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id"));
+		jabber_iq_set_id(iq, id);
 
 		xmlnode_set_attrib(iq->node, "to", from);
 		query = xmlnode_get_child(iq->node, "query");
@@ -126,7 +119,7 @@
 			SUPPORT_FEATURE("jabber:iq:last")
 			SUPPORT_FEATURE("jabber:iq:oob")
 			SUPPORT_FEATURE("jabber:iq:time")
-			SUPPORT_FEATURE("xmpp:urn:time")
+			SUPPORT_FEATURE("urn:xmpp:time")
 			SUPPORT_FEATURE("jabber:iq:version")
 			SUPPORT_FEATURE("jabber:x:conference")
 			SUPPORT_FEATURE("http://jabber.org/protocol/bytestreams")
@@ -200,8 +193,7 @@
 		}
 
 		jabber_iq_send(iq);
-	} else if(!strcmp(type, "result")) {
-		xmlnode *query = xmlnode_get_child(packet, "query");
+	} else if(type == JABBER_IQ_RESULT) {
 		xmlnode *child;
 		JabberID *jid;
 		JabberBuddy *jb;
@@ -218,7 +210,7 @@
 		if(jbr)
 			capabilities = jbr->capabilities;
 
-		for(child = query->child; child; child = child->next) {
+		for(child = in_query->child; child; child = child->next) {
 			if(child->type != XMLNODE_TYPE_TAG)
 				continue;
 
@@ -285,7 +277,7 @@
 			jdicd->callback(js, from, capabilities, jdicd->data);
 			g_hash_table_remove(js->disco_callbacks, from);
 		}
-	} else if(!strcmp(type, "error")) {
+	} else if(type == JABBER_IQ_ERROR) {
 		JabberID *jid;
 		JabberBuddy *jb;
 		JabberBuddyResource *jbr = NULL;
@@ -309,28 +301,23 @@
 	}
 }
 
-void jabber_disco_items_parse(JabberStream *js, xmlnode *packet) {
-	const char *from = xmlnode_get_attrib(packet, "from");
-	const char *type = xmlnode_get_attrib(packet, "type");
-
-	if(type && !strcmp(type, "get")) {
+void jabber_disco_items_parse(JabberStream *js, const char *from,
+                              JabberIqType type, const char *id,
+                              xmlnode *query) {
+	if(type == JABBER_IQ_GET) {
 		JabberIq *iq = jabber_iq_new_query(js, JABBER_IQ_RESULT,
 				"http://jabber.org/protocol/disco#items");
 		
 		/* preserve node */
-		xmlnode *iq_query = xmlnode_get_child_with_namespace(iq->node,"query","http://jabber.org/protocol/disco#items");
-		if(iq_query) {
-			xmlnode *query = xmlnode_get_child_with_namespace(packet,"query","http://jabber.org/protocol/disco#items");
-			if(query) {
-				const char *node = xmlnode_get_attrib(query,"node");
-				if(node)
-					xmlnode_set_attrib(iq_query,"node",node);
-			}
-		}
+		xmlnode *iq_query = xmlnode_get_child(iq->node, "query");
+		const char *node = xmlnode_get_attrib(query, "node");
+		if(node)
+			xmlnode_set_attrib(iq_query,"node",node);
 
-		jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id"));
+		jabber_iq_set_id(iq, id);
 
-		xmlnode_set_attrib(iq->node, "to", from);
+		if (from)
+			xmlnode_set_attrib(iq->node, "to", from);
 		jabber_iq_send(iq);
 	}
 }
--- a/libpurple/protocols/jabber/disco.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/disco.h	Tue Feb 10 06:12:24 2009 +0000
@@ -27,8 +27,10 @@
 typedef void (JabberDiscoInfoCallback)(JabberStream *js, const char *who,
 		JabberCapabilities capabilities, gpointer data);
 
-void jabber_disco_info_parse(JabberStream *js, xmlnode *packet);
-void jabber_disco_items_parse(JabberStream *js, xmlnode *packet);
+void jabber_disco_info_parse(JabberStream *js, const char *from,
+                             JabberIqType type, const char *id, xmlnode *in_query);
+void jabber_disco_items_parse(JabberStream *js, const char *from,
+                              JabberIqType type, const char *id, xmlnode *query);
 
 void jabber_disco_items_server(JabberStream *js);
 
--- a/libpurple/protocols/jabber/google.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/google.c	Tue Feb 10 06:12:24 2009 +0000
@@ -144,9 +144,9 @@
 }
 
 void
-jabber_gmail_poke(JabberStream *js, xmlnode *packet)
+jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
+                  const char *id, xmlnode *new_mail)
 {
-	const char *type;
 	xmlnode *query;
 	JabberIq *iq;
 
@@ -154,11 +154,8 @@
 	if (!purple_account_get_check_mail(js->gc->account))
 		return;
 
-	type = xmlnode_get_attrib(packet, "type");
-
-
 	/* Is this an initial incoming mail notification? If so, send a request for more info */
-	if (strcmp(type, "set") || !xmlnode_get_child(packet, "new-mail"))
+	if (type != JABBER_IQ_SET)
 		return;
 
 	purple_debug(PURPLE_DEBUG_MISC, "jabber",
--- a/libpurple/protocols/jabber/google.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/google.h	Tue Feb 10 06:12:24 2009 +0000
@@ -27,7 +27,8 @@
 #include "jabber.h"
 
 void jabber_gmail_init(JabberStream *js);
-void jabber_gmail_poke(JabberStream *js, xmlnode *node);
+void jabber_gmail_poke(JabberStream *js, const char *from, JabberIqType type,
+                       const char *id, xmlnode *new_mail);
 
 void jabber_google_roster_init(JabberStream *js);
 void jabber_google_roster_outgoing(JabberStream *js, xmlnode *query, xmlnode *item);
--- a/libpurple/protocols/jabber/iq.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/iq.c	Tue Feb 10 06:12:24 2009 +0000
@@ -142,23 +142,19 @@
 	g_free(iq);
 }
 
-static void jabber_iq_last_parse(JabberStream *js, xmlnode *packet)
+static void jabber_iq_last_parse(JabberStream *js, const char *from,
+                                 JabberIqType type, const char *id,
+                                 xmlnode *packet)
 {
 	JabberIq *iq;
-	const char *type;
-	const char *from;
-	const char *id;
 	xmlnode *query;
 	char *idle_time;
 
-	type = xmlnode_get_attrib(packet, "type");
-	from = xmlnode_get_attrib(packet, "from");
-	id = xmlnode_get_attrib(packet, "id");
-
-	if(type && !strcmp(type, "get")) {
+	if(type == JABBER_IQ_GET) {
 		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:last");
 		jabber_iq_set_id(iq, id);
-		xmlnode_set_attrib(iq->node, "to", from);
+		if (from)
+			xmlnode_set_attrib(iq->node, "to", from);
 
 		query = xmlnode_get_child(iq->node, "query");
 
@@ -170,88 +166,67 @@
 	}
 }
 
-static void jabber_iq_time_parse(JabberStream *js, xmlnode *packet)
+static void jabber_iq_time_parse(JabberStream *js, const char *from,
+                                 JabberIqType type, const char *id,
+                                 xmlnode *child)
 {
-	const char *type, *from, *id, *xmlns;
+	const char *xmlns;
 	JabberIq *iq;
-	xmlnode *query;
 	time_t now_t;
+	struct tm now_local;
+	struct tm now_utc;
 	struct tm *now;
 
 	time(&now_t);
 	now = localtime(&now_t);
-
-	type = xmlnode_get_attrib(packet, "type");
-	from = xmlnode_get_attrib(packet, "from");
-	id = xmlnode_get_attrib(packet, "id");
+	memcpy(&now_local, now, sizeof(struct tm));
+	now = gmtime(&now_t);
+	memcpy(&now_utc, now, sizeof(struct tm));
 
-	/* we're gonna throw this away in a moment, but we need it
-	 * to get the xmlns, so we can figure out if this is
-	 * jabber:iq:time or urn:xmpp:time */
-	query = xmlnode_get_child(packet, "query");
-	xmlns = xmlnode_get_namespace(query);
+	xmlns = xmlnode_get_namespace(child);
 
-	if(type && !strcmp(type, "get")) {
+	if(type == JABBER_IQ_GET) {
 		xmlnode *utc;
-		const char *date;
+		const char *date, *tz, *display;
 
-		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, xmlns);
+		iq = jabber_iq_new(js, JABBER_IQ_RESULT);
 		jabber_iq_set_id(iq, id);
-		xmlnode_set_attrib(iq->node, "to", from);
-
-		query = xmlnode_get_child(iq->node, "query");
+		if (from)
+			xmlnode_set_attrib(iq->node, "to", from);
 
-		date = purple_utf8_strftime("%Y%m%dT%T", now);
-		utc = xmlnode_new_child(query, "utc");
-		xmlnode_insert_data(utc, date, -1);
+		child = xmlnode_new_child(iq->node, child->name);
+		xmlnode_set_namespace(child, xmlns);
+		utc = xmlnode_new_child(child, "utc");
 
 		if(!strcmp("urn:xmpp:time", xmlns)) {
-			xmlnode_insert_data(utc, "Z", 1); /* of COURSE the thing that is the same is different */
+			tz = purple_get_tzoff_str(&now_local, TRUE);
+			xmlnode_insert_data(xmlnode_new_child(child, "tzo"), tz, -1);
 
-			date = purple_get_tzoff_str(now, TRUE);
-			xmlnode_insert_data(xmlnode_new_child(query, "tzo"), date, -1);
+			date = purple_utf8_strftime("%FT%TZ", &now_utc);
+			xmlnode_insert_data(utc, date, -1);
 		} else { /* jabber:iq:time */
-			date = purple_utf8_strftime("%Z", now);
-			xmlnode_insert_data(xmlnode_new_child(query, "tz"), date, -1);
+			tz = purple_utf8_strftime("%Z", &now_local);
+			xmlnode_insert_data(xmlnode_new_child(child, "tz"), tz, -1);
 
-			date = purple_utf8_strftime("%d %b %Y %T", now);
-			xmlnode_insert_data(xmlnode_new_child(query, "display"), date, -1);
+			date = purple_utf8_strftime("%Y%m%dT%T", &now_utc);
+			xmlnode_insert_data(utc, date, -1);
+
+			display = purple_utf8_strftime("%d %b %Y %T", &now_local);
+			xmlnode_insert_data(xmlnode_new_child(child, "display"), display, -1);
 		}
 
 		jabber_iq_send(iq);
 	}
 }
 
-static void urn_xmpp_ping_parse(JabberStream *js, xmlnode *packet)
-{
-	const char *type, *id, *from;
-	JabberIq *iq;
-
-	type = xmlnode_get_attrib(packet, "type");
-	from = xmlnode_get_attrib(packet, "from");
-	id = xmlnode_get_attrib(packet, "id");
-
-	if(type && !strcmp(type, "get")) {
-		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "urn:xmpp:ping");
-
-		jabber_iq_set_id(iq, id);
-		xmlnode_set_attrib(iq->node, "to", from);
-
-		jabber_iq_send(iq);
-	} else {
-		/* XXX: error */
-	}
-}
-
-static void jabber_iq_version_parse(JabberStream *js, xmlnode *packet)
+static void jabber_iq_version_parse(JabberStream *js, const char *from,
+                                    JabberIqType type, const char *id,
+                                    xmlnode *packet)
 {
 	JabberIq *iq;
-	const char *type, *from, *id;
 	xmlnode *query;
 
-	type = xmlnode_get_attrib(packet, "type");
-
-	if(type && !strcmp(type, "get")) {
+	if(type == JABBER_IQ_GET) {
 		GHashTable *ui_info;
 		const char *ui_name = NULL, *ui_version = NULL;
 #if 0
@@ -264,11 +239,10 @@
 					osinfo.machine);
 		}
 #endif
-		from = xmlnode_get_attrib(packet, "from");
-		id = xmlnode_get_attrib(packet, "id");
 
 		iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version");
-		xmlnode_set_attrib(iq->node, "to", from);
+		if (from)
+			xmlnode_set_attrib(iq->node, "to", from);
 		jabber_iq_set_id(iq, id);
 
 		query = xmlnode_get_child(iq->node, "query");
@@ -309,33 +283,56 @@
 void jabber_iq_parse(JabberStream *js, xmlnode *packet)
 {
 	JabberCallbackData *jcd;
-	xmlnode *query, *error, *x;
+	xmlnode *child, *error, *x;
 	const char *xmlns;
-	const char *type, *id, *from;
-	JabberIqHandler *jih;
+	const char *iq_type, *id, *from;
+	JabberIqType type = JABBER_IQ_NONE;
 
-	query = xmlnode_get_child(packet, "query");
-	type = xmlnode_get_attrib(packet, "type");
+	/*
+	 * child will be either the first tag child or NULL if there is no child.
+	 * Historically, we used just the 'query' subchild, but newer XEPs use
+	 * differently named children. Grabbing the first child is (for the time
+	 * being) sufficient.
+	 */
+	for (child = packet->child; child; child = child->next) {
+		if (child->type == XMLNODE_TYPE_TAG)
+			break;
+	}
+
+	iq_type = xmlnode_get_attrib(packet, "type");
 	from = xmlnode_get_attrib(packet, "from");
 	id = xmlnode_get_attrib(packet, "id");
 
-	if(type == NULL || !(!strcmp(type, "get") || !strcmp(type, "set")
-			|| !strcmp(type, "result") || !strcmp(type, "error"))) {
+	if (iq_type) {
+		if (!strcmp(iq_type, "get"))
+			type = JABBER_IQ_GET;
+		else if (!strcmp(iq_type, "set"))
+			type = JABBER_IQ_SET;
+		else if (!strcmp(iq_type, "result"))
+			type = JABBER_IQ_RESULT;
+		else if (!strcmp(iq_type, "error"))
+			type = JABBER_IQ_ERROR;
+	}
+
+	if (type == JABBER_IQ_NONE) {
 		purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n",
-						   type ? type : "(null)");
+						   iq_type ? iq_type : "(null)");
 		return;
 	}
 
 	/* All IQs must have an ID, so send an error for a set/get that doesn't */
 	if(!id || !*id) {
 
-		if(!strcmp(type, "set") || !strcmp(type, "get")) {
+		if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
 			JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
 
 			xmlnode_free(iq->node);
 			iq->node = xmlnode_copy(packet);
-			xmlnode_set_attrib(iq->node, "to", from);
-			xmlnode_remove_attrib(iq->node, "from");
+			if (from) {
+				xmlnode_set_attrib(iq->node, "to", from);
+				xmlnode_remove_attrib(iq->node, "from");
+			}
+
 			xmlnode_set_attrib(iq->node, "type", "error");
 			/* This id is clearly not useful, but we must put something there for a valid stanza */
 			iq->id = jabber_get_next_id(js);
@@ -347,14 +344,14 @@
 
 			jabber_iq_send(iq);
 		} else
-			purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", type);
+			purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n",
+			                   iq_type);
 
 		return;
 	}
 
 	/* First, lets see if a special callback got registered */
-
-	if(!strcmp(type, "result") || !strcmp(type, "error")) {
+	if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) {
 		if(id && *id && (jcd = g_hash_table_lookup(js->iq_callbacks, id))) {
 			jcd->callback(js, packet, jcd->data);
 			jabber_iq_remove_callback_by_id(js, id);
@@ -363,44 +360,30 @@
 	}
 
 	/* Apparently not, so lets see if we have a pre-defined handler */
+	if(child && (xmlns = xmlnode_get_namespace(child))) {
+		char *key = g_strdup_printf("%s %s", child->name, xmlns);
+		JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key);
+		g_free(key);
 
-	if(query && (xmlns = xmlnode_get_namespace(query))) {
-		if((jih = g_hash_table_lookup(iq_handlers, xmlns))) {
-			jih(js, packet);
+		if(jih) {
+			jih(js, from, type, id, child);
 			return;
 		}
 	}
 
-	if(xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si")) {
-		jabber_si_parse(js, packet);
-		return;
-	}
-
-	if(xmlnode_get_child_with_namespace(packet, "new-mail", "google:mail:notify")) {
-		jabber_gmail_poke(js, packet);
-		return;
-	}
-
 	purple_debug_info("jabber", "jabber_iq_parse\n");
 
-	if(xmlnode_get_child_with_namespace(packet, "ping", "urn:xmpp:ping")) {
-		jabber_ping_parse(js, packet);
-		return;
-	}
-
-	if (xmlnode_get_child_with_namespace(packet, "data", XEP_0231_NAMESPACE)) {
-		jabber_data_parse(js, packet);
-		return;
-	}
-
 	/* If we get here, send the default error reply mandated by XMPP-CORE */
-	if(!strcmp(type, "set") || !strcmp(type, "get")) {
+	if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
 		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
 
 		xmlnode_free(iq->node);
 		iq->node = xmlnode_copy(packet);
-		xmlnode_set_attrib(iq->node, "to", from);
-		xmlnode_remove_attrib(iq->node, "from");
+		if (from) {
+			xmlnode_set_attrib(iq->node, "to", from);
+			xmlnode_remove_attrib(iq->node, "from");
+		}
+
 		xmlnode_set_attrib(iq->node, "type", "error");
 		error = xmlnode_new_child(iq->node, "error");
 		xmlnode_set_attrib(error, "type", "cancel");
@@ -412,26 +395,36 @@
 	}
 }
 
-void jabber_iq_register_handler(const char *xmlns, JabberIqHandler *handlerfunc)
+void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc)
 {
-	g_hash_table_replace(iq_handlers, g_strdup(xmlns), handlerfunc);
+	/*
+	 * This is valid because nodes nor namespaces cannot have spaces in them
+	 * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and
+	 * http://www.w3.org/TR/REC-xml-names/)
+	 */
+	char *key = g_strdup_printf("%s %s", node, xmlns);
+	g_hash_table_replace(iq_handlers, key, handlerfunc);
 }
 
 void jabber_iq_init(void)
 {
 	iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
 
-	jabber_iq_register_handler("jabber:iq:roster", jabber_roster_parse);
-	jabber_iq_register_handler("jabber:iq:oob", jabber_oob_parse);
-	jabber_iq_register_handler("http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse);
-	jabber_iq_register_handler("jabber:iq:last", jabber_iq_last_parse);
-	jabber_iq_register_handler("jabber:iq:time", jabber_iq_time_parse);
-	jabber_iq_register_handler("urn:xmpp:time", jabber_iq_time_parse);
-	jabber_iq_register_handler("jabber:iq:version", jabber_iq_version_parse);
-	jabber_iq_register_handler("http://jabber.org/protocol/disco#info", jabber_disco_info_parse);
-	jabber_iq_register_handler("http://jabber.org/protocol/disco#items", jabber_disco_items_parse);
-	jabber_iq_register_handler("jabber:iq:register", jabber_register_parse);
-	jabber_iq_register_handler("urn:xmpp:ping", urn_xmpp_ping_parse);
+	jabber_iq_register_handler("mailbox", "google:mail:notify", jabber_gmail_poke);
+	jabber_iq_register_handler("new-mail", "google:mail:notify", jabber_gmail_poke);
+	jabber_iq_register_handler("query", "http://jabber.org/protocol/bytestreams", jabber_bytestreams_parse);
+	jabber_iq_register_handler("query", "http://jabber.org/protocol/disco#info", jabber_disco_info_parse);
+	jabber_iq_register_handler("query", "http://jabber.org/protocol/disco#items", jabber_disco_items_parse);
+	jabber_iq_register_handler("si", "http://jabber.org/protocol/si", jabber_si_parse);
+	jabber_iq_register_handler("query", "jabber:iq:last", jabber_iq_last_parse);
+	jabber_iq_register_handler("query", "jabber:iq:oob", jabber_oob_parse);
+	jabber_iq_register_handler("query", "jabber:iq:register", jabber_register_parse);
+	jabber_iq_register_handler("query", "jabber:iq:roster", jabber_roster_parse);
+	jabber_iq_register_handler("query", "jabber:iq:time", jabber_iq_time_parse);
+	jabber_iq_register_handler("query", "jabber:iq:version", jabber_iq_version_parse);
+	jabber_iq_register_handler("data", XEP_0231_NAMESPACE, jabber_data_parse);
+	jabber_iq_register_handler("ping", "urn:xmpp:ping", jabber_ping_parse);
+	jabber_iq_register_handler("time", "urn:xmpp:time", jabber_iq_time_parse);
 }
 
 void jabber_iq_uninit(void)
--- a/libpurple/protocols/jabber/iq.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/iq.h	Tue Feb 10 06:12:24 2009 +0000
@@ -22,10 +22,6 @@
 #ifndef _PURPLE_JABBER_IQ_H_
 #define _PURPLE_JABBER_IQ_H_
 
-#include "jabber.h"
-
-typedef struct _JabberIq JabberIq;
-
 typedef enum {
 	JABBER_IQ_SET,
 	JABBER_IQ_GET,
@@ -34,7 +30,13 @@
 	JABBER_IQ_NONE
 } JabberIqType;
 
-typedef void (JabberIqHandler)(JabberStream *js, xmlnode *packet);
+#include "jabber.h"
+
+typedef struct _JabberIq JabberIq;
+
+typedef void (JabberIqHandler)(JabberStream *js, const char *from,
+                               JabberIqType type, const char *id,
+                               xmlnode *child);
 
 typedef void (JabberIqCallback)(JabberStream *js, xmlnode *packet, gpointer data);
 
@@ -65,6 +67,7 @@
 void jabber_iq_init(void);
 void jabber_iq_uninit(void);
 
-void jabber_iq_register_handler(const char *xmlns, JabberIqHandler *func);
+void jabber_iq_register_handler(const char *node, const char *xmlns,
+                                JabberIqHandler *func);
 
 #endif /* _PURPLE_JABBER_IQ_H_ */
--- a/libpurple/protocols/jabber/jabber.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.c	Tue Feb 10 06:12:24 2009 +0000
@@ -439,17 +439,11 @@
 	g_free(txt);
 }
 
-static void jabber_pong_cb(JabberStream *js, xmlnode *packet, gpointer unused)
-{
-	purple_timeout_remove(js->keepalive_timeout);
-	js->keepalive_timeout = -1;
-}
-
-static gboolean jabber_pong_timeout(PurpleConnection *gc)
+static gboolean jabber_keepalive_timeout(PurpleConnection *gc)
 {
 	JabberStream *js = gc->proto_data;
 	purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR,
-					_("Ping timeout"));
+	                               _("Ping timeout"));
 	js->keepalive_timeout = -1;
 	return FALSE;
 }
@@ -459,14 +453,9 @@
 	JabberStream *js = gc->proto_data;
 
 	if (js->keepalive_timeout == -1) {
-		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET);
-		
-		xmlnode *ping = xmlnode_new_child(iq->node, "ping");
-		xmlnode_set_namespace(ping, "urn:xmpp:ping");
-		
-		js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_pong_timeout), gc);
-		jabber_iq_set_callback(iq, jabber_pong_cb, NULL);
-		jabber_iq_send(iq);
+		jabber_ping_jid(js, NULL);
+		js->keepalive_timeout = purple_timeout_add_seconds(120,
+				(GSourceFunc)(jabber_keepalive_timeout), gc);
 	}
 }
 
@@ -980,23 +969,21 @@
 	jabber_iq_send(iq);
 }
 
-void jabber_register_parse(JabberStream *js, xmlnode *packet)
+void jabber_register_parse(JabberStream *js, const char *from, JabberIqType type,
+                           const char *id, xmlnode *query)
 {
 	PurpleAccount *account = purple_connection_get_account(js->gc);
-	const char *type;
-	const char *from;
 	PurpleRequestFields *fields;
 	PurpleRequestFieldGroup *group;
 	PurpleRequestField *field;
-	xmlnode *query, *x, *y;
+	xmlnode *x, *y;
 	char *instructions;
 	JabberRegisterCBData *cbdata;
 	gboolean registered = FALSE;
 
-	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "result"))
+	if (type != JABBER_IQ_RESULT)
 		return;
 
-	from = xmlnode_get_attrib(packet, "from");
 	if (!from)
 		from = js->serverFQDN;
 	g_return_if_fail(from != NULL);
@@ -1006,8 +993,6 @@
 		purple_connection_set_state(js->gc, PURPLE_CONNECTED);
 	}
 
-	query = xmlnode_get_child(packet, "query");
-
 	if(xmlnode_get_child(query, "registered")) {
 		registered = TRUE;
 
@@ -2421,10 +2406,16 @@
 static PurpleCmdRet jabber_cmd_ping(PurpleConversation *conv,
 		const char *cmd, char **args, char **error, void *data)
 {
+	PurpleAccount *account;
+	PurpleConnection *pc;
+
 	if(!args || !args[0])
 		return PURPLE_CMD_RET_FAILED;
 
-	if(!jabber_ping_jid(conv, args[0])) {
+	account = purple_conversation_get_account(conv);
+	pc = purple_account_get_connection(account);
+
+	if(!jabber_ping_jid(pc->proto_data, args[0])) {
 		*error = g_strdup_printf(_("Unable to ping user %s"), args[0]);
 		return PURPLE_CMD_RET_FAILED;
 	}
--- a/libpurple/protocols/jabber/jabber.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/jabber.h	Tue Feb 10 06:12:24 2009 +0000
@@ -57,6 +57,7 @@
 #include "roomlist.h"
 #include "sslconn.h"
 
+#include "iq.h"
 #include "jutil.h"
 #include "xmlnode.h"
 #include "buddy.h"
@@ -269,7 +270,8 @@
 
 void jabber_stream_set_state(JabberStream *js, JabberStreamState state);
 
-void jabber_register_parse(JabberStream *js, xmlnode *packet);
+void jabber_register_parse(JabberStream *js, const char *from,
+                           JabberIqType type, const char *id, xmlnode *query);
 void jabber_register_start(JabberStream *js);
 
 char *jabber_get_next_id(JabberStream *js);
--- a/libpurple/protocols/jabber/oob.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/oob.c	Tue Feb 10 06:12:24 2009 +0000
@@ -187,18 +187,18 @@
 	jabber_oob_xfer_recv_error(xfer, "404");
 }
 
-void jabber_oob_parse(JabberStream *js, xmlnode *packet) {
+void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
+                      const char *id, xmlnode *querynode) {
 	JabberOOBXfer *jox;
 	PurpleXfer *xfer;
 	char *filename;
 	char *url;
-	const char *type;
-	xmlnode *querynode, *urlnode;
+	xmlnode *urlnode;
 
-	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "set"))
+	if(type != JABBER_IQ_SET)
 		return;
 
-	if(!(querynode = xmlnode_get_child(packet, "query")))
+	if(!from)
 		return;
 
 	if(!(urlnode = xmlnode_get_child(querynode, "url")))
@@ -211,10 +211,9 @@
 	g_free(url);
 	jox->js = js;
 	jox->headers = g_string_new("");
-	jox->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	jox->iq_id = g_strdup(id);
 
-	xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE,
-			xmlnode_get_attrib(packet, "from"));
+	xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
 	if (xfer)
 	{
 		xfer->data = jox;
--- a/libpurple/protocols/jabber/oob.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/oob.h	Tue Feb 10 06:12:24 2009 +0000
@@ -22,6 +22,9 @@
 #ifndef _PURPLE_JABBER_OOB_H_
 #define _PURPLE_JABBER_OOB_H_
 
-void jabber_oob_parse(JabberStream *js, xmlnode *packet);
+#include "jabber.h"
+
+void jabber_oob_parse(JabberStream *js, const char *from, JabberIqType type,
+                      const char *id, xmlnode *querynode);
 
 #endif /* _PURPLE_JABBER_OOB_H_ */
--- a/libpurple/protocols/jabber/ping.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/ping.c	Tue Feb 10 06:12:24 2009 +0000
@@ -23,34 +23,49 @@
 #include "internal.h"
 
 #include "debug.h"
-#include "xmlnode.h"
 
 #include "jabber.h"
 #include "ping.h"
 #include "iq.h"
 
-void
-jabber_ping_parse(JabberStream *js, xmlnode *packet)
+static void jabber_keepalive_pong_cb(JabberStream *js)
 {
-	JabberIq *iq;
-
-	purple_debug_info("jabber", "jabber_ping_parse\n");
+	purple_timeout_remove(js->keepalive_timeout);
+	js->keepalive_timeout = -1;
+}
 
-	iq = jabber_iq_new(js, JABBER_IQ_RESULT);
-
-	xmlnode_set_attrib(iq->node, "to", xmlnode_get_attrib(packet, "from") );
+void
+jabber_ping_parse(JabberStream *js, const char *from,
+                  JabberIqType type, const char *id, xmlnode *ping)
+{
+	if (type == JABBER_IQ_GET) {
+		JabberIq *iq = jabber_iq_new(js, JABBER_IQ_RESULT);
 
-	jabber_iq_set_id(iq, xmlnode_get_attrib(packet, "id"));
+		if (from)
+			xmlnode_set_attrib(iq->node, "to", from);
+		xmlnode_set_attrib(iq->node, "id", id);
 
-	jabber_iq_send(iq);
+		jabber_iq_send(iq);
+	} else if (type == JABBER_IQ_SET) {
+		/* XXX: error */
+	}
 }
 
 static void jabber_ping_result_cb(JabberStream *js, xmlnode *packet,
-		gpointer data)
+                                  gpointer data)
 {
 	const char *type = xmlnode_get_attrib(packet, "type");
+	const char *from = xmlnode_get_attrib(packet, "from");
+	char *own_bare_jid = g_strdup_printf("%s@%s", js->user->node,
+	                                     js->user->domain);
 
-	purple_debug_info("jabber", "jabber_ping_result_cb\n");
+	if (!from || !strcmp(from, own_bare_jid)) {
+		/* If the pong is from our bare JID, treat it as a return from the
+		 * keepalive functions */
+		jabber_keepalive_pong_cb(js);
+	}
+	g_free(own_bare_jid);
+
 	if(type && !strcmp(type, "result")) {
 		purple_debug_info("jabber", "PONG!\n");
 	} else {
@@ -58,15 +73,14 @@
 	}
 }
 
-gboolean jabber_ping_jid(PurpleConversation *conv, const char *jid)
+gboolean jabber_ping_jid(JabberStream *js, const char *jid)
 {
 	JabberIq *iq;
 	xmlnode *ping;
 
-	purple_debug_info("jabber", "jabber_ping_jid\n");
-
-	iq = jabber_iq_new(conv->account->gc->proto_data, JABBER_IQ_GET);
-	xmlnode_set_attrib(iq->node, "to", jid);
+	iq = jabber_iq_new(js, JABBER_IQ_GET);
+	if (jid)
+		xmlnode_set_attrib(iq->node, "to", jid);
 
 	ping = xmlnode_new_child(iq->node, "ping");
 	xmlnode_set_namespace(ping, "urn:xmpp:ping");
@@ -74,7 +88,5 @@
 	jabber_iq_set_callback(iq, jabber_ping_result_cb, NULL);
 	jabber_iq_send(iq);
 
-
-
 	return TRUE;
 }
--- a/libpurple/protocols/jabber/ping.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/ping.h	Tue Feb 10 06:12:24 2009 +0000
@@ -23,13 +23,11 @@
 #define _PURPLE_JABBER_PING_H_
 
 #include "jabber.h"
-#include "conversation.h"
+#include "iq.h"
+#include "xmlnode.h"
 
-void jabber_ping_parse(JabberStream *js,
-						xmlnode *packet);
-
-
-gboolean jabber_ping_jid(PurpleConversation *conv, const char *jid);
-
+void jabber_ping_parse(JabberStream *js, const char *from,
+                       JabberIqType, const char *id, xmlnode *child);
+gboolean jabber_ping_jid(JabberStream *js, const char *jid);
 
 #endif /* _PURPLE_JABBER_PING_H_ */
--- a/libpurple/protocols/jabber/roster.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/roster.c	Tue Feb 10 06:12:24 2009 +0000
@@ -142,10 +142,10 @@
 	g_slist_free(buddies);
 }
 
-void jabber_roster_parse(JabberStream *js, xmlnode *packet)
+void jabber_roster_parse(JabberStream *js, const char *from,
+                         JabberIqType type, const char *id, xmlnode *query)
 {
-	xmlnode *query, *item, *group;
-	const char *from = xmlnode_get_attrib(packet, "from");
+	xmlnode *item, *group;
 
 	if(from) {
 		char *from_norm;
@@ -166,10 +166,6 @@
 			return;
 	}
 
-	query = xmlnode_get_child(packet, "query");
-	if(!query)
-		return;
-
 	js->currently_parsing_roster_push = TRUE;
 
 	for(item = xmlnode_get_child(query, "item"); item; item = xmlnode_get_next_twin(item))
--- a/libpurple/protocols/jabber/roster.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/roster.h	Tue Feb 10 06:12:24 2009 +0000
@@ -26,7 +26,8 @@
 
 void jabber_roster_request(JabberStream *js);
 
-void jabber_roster_parse(JabberStream *js, xmlnode *packet);
+void jabber_roster_parse(JabberStream *js, const char *from,
+                         JabberIqType type, const char *id, xmlnode *query);
 
 void jabber_roster_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy,
 		PurpleGroup *group);
--- a/libpurple/protocols/jabber/si.c	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/si.c	Tue Feb 10 06:12:24 2009 +0000
@@ -256,20 +256,18 @@
 	}
 }
 
-void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet)
+void jabber_bytestreams_parse(JabberStream *js, const char *from,
+                              JabberIqType type, const char *id, xmlnode *query)
 {
 	PurpleXfer *xfer;
 	JabberSIXfer *jsx;
-	xmlnode *query, *streamhost;
-	const char *sid, *from, *type;
+	xmlnode *streamhost;
+	const char *sid;
 
-	if(!(type = xmlnode_get_attrib(packet, "type")) || strcmp(type, "set"))
+	if(type != JABBER_IQ_SET)
 		return;
 
-	if(!(from = xmlnode_get_attrib(packet, "from")))
-		return;
-
-	if(!(query = xmlnode_get_child(packet, "query")))
+	if(!from)
 		return;
 
 	if(!(sid = xmlnode_get_attrib(query, "sid")))
@@ -285,7 +283,7 @@
 
 	if(jsx->iq_id)
 		g_free(jsx->iq_id);
-	jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	jsx->iq_id = g_strdup(id);
 
 	for(streamhost = xmlnode_get_child(query, "streamhost"); streamhost;
 			streamhost = xmlnode_get_next_twin(streamhost)) {
@@ -1182,17 +1180,15 @@
 		purple_xfer_request(xfer);
 }
 
-void jabber_si_parse(JabberStream *js, xmlnode *packet)
+void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
+                     const char *id, xmlnode *si)
 {
 	JabberSIXfer *jsx;
 	PurpleXfer *xfer;
-	xmlnode *si, *file, *feature, *x, *field, *option, *value;
-	const char *stream_id, *filename, *filesize_c, *profile, *from;
+	xmlnode *file, *feature, *x, *field, *option, *value;
+	const char *stream_id, *filename, *filesize_c, *profile;
 	size_t filesize = 0;
 
-	if(!(si = xmlnode_get_child(packet, "si")))
-		return;
-
 	if(!(profile = xmlnode_get_attrib(si, "profile")) ||
 			strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer"))
 		return;
@@ -1215,7 +1211,7 @@
 	if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
 		return;
 
-	if(!(from = xmlnode_get_attrib(packet, "from")))
+	if(!from)
 		return;
 
 	/* if they've already sent us this file transfer with the same damn id
@@ -1256,7 +1252,7 @@
 
 	jsx->js = js;
 	jsx->stream_id = g_strdup(stream_id);
-	jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));
+	jsx->iq_id = g_strdup(id);
 
 	xfer = purple_xfer_new(js->gc->account, PURPLE_XFER_RECEIVE, from);
 	if (xfer)
--- a/libpurple/protocols/jabber/si.h	Tue Feb 10 06:08:57 2009 +0000
+++ b/libpurple/protocols/jabber/si.h	Tue Feb 10 06:12:24 2009 +0000
@@ -26,8 +26,10 @@
 
 #include "jabber.h"
 
-void jabber_bytestreams_parse(JabberStream *js, xmlnode *packet);
-void jabber_si_parse(JabberStream *js, xmlnode *packet);
+void jabber_bytestreams_parse(JabberStream *js, const char *from,
+                              JabberIqType type, const char *id, xmlnode *query);
+void jabber_si_parse(JabberStream *js, const char *from, JabberIqType type,
+                     const char *id, xmlnode *si);
 PurpleXfer *jabber_si_new_xfer(PurpleConnection *gc, const char *who);
 void jabber_si_xfer_send(PurpleConnection *gc, const char *who, const char *file);