예제 #1
0
static void
jabber_vcard_parse_avatar(JabberStream *js, const char *from,
                          JabberIqType type, const char *id,
                          xmlnode *packet, gpointer blah)
{
	JabberBuddy *jb = NULL;
	xmlnode *vcard, *photo, *binval, *fn, *nick;
	char *text;

	if(!from)
		return;

	jb = jabber_buddy_find(js, from, TRUE);

	js->pending_avatar_requests = g_slist_remove(js->pending_avatar_requests, jb);

	if((vcard = xmlnode_get_child(packet, "vCard")) ||
			(vcard = xmlnode_get_child_with_namespace(packet, "query", "vcard-temp"))) {
		/* The logic here regarding the nickname and full name is copied from
		 * buddy.c:jabber_vcard_parse. */
		gchar *nickname = NULL;
		if ((fn = xmlnode_get_child(vcard, "FN")))
			nickname = xmlnode_get_data(fn);

		if ((nick = xmlnode_get_child(vcard, "NICKNAME"))) {
			char *tmp = xmlnode_get_data(nick);
			char *bare_jid = jabber_get_bare_jid(from);
			if (tmp && strstr(bare_jid, tmp) == NULL) {
				g_free(nickname);
				nickname = tmp;
			} else if (tmp)
				g_free(tmp);

			g_free(bare_jid);
		}

		if (nickname) {
			serv_got_alias(js->gc, from, nickname);
			g_free(nickname);
		}

		if ((photo = xmlnode_get_child(vcard, "PHOTO")) &&
				(binval = xmlnode_get_child(photo, "BINVAL")) &&
				(text = xmlnode_get_data(binval))) {
			guchar *data;
			gsize size;

			data = purple_base64_decode(text, &size);
			if (data) {
				gchar *hash = jabber_calculate_data_hash(data, size, "sha1");
				purple_buddy_icons_set_for_user(js->gc->account, from, data,
				                                size, hash);
				g_free(hash);
			}

			g_free(text);
		}
	}
}
예제 #2
0
파일: pep.c 프로젝트: bf4/pidgin-mac
static void do_pep_iq_request_item_callback(JabberStream *js, xmlnode *packet, gpointer data) {
	const char *from = xmlnode_get_attrib(packet,"from");
	xmlnode *pubsub = xmlnode_get_child_with_namespace(packet,"pubsub","http://jabber.org/protocol/pubsub");
	xmlnode *items = NULL;
	JabberPEPHandler *cb = data;
	
	if(pubsub)
		items = xmlnode_get_child(pubsub, "items");
	
	cb(js, from, items);
}
예제 #3
0
static void
jabber_google_jingle_info_cb(JabberStream *js, const char *from,
                             JabberIqType type, const char *id,
                             xmlnode *packet, gpointer data)
{
	xmlnode *query = xmlnode_get_child_with_namespace(packet, "query",
			NS_GOOGLE_JINGLE_INFO);

	if (query)
		jabber_google_jingle_info_common(js, from, type, query);
	else
		purple_debug_warning("jabber", "Got invalid google:jingleinfo\n");
}
예제 #4
0
파일: si.c 프로젝트: VoxOx/VoxOx
static void jabber_si_xfer_send_method_cb(JabberStream *js, xmlnode *packet,
		gpointer data)
{
	GaimXfer *xfer = data;
	xmlnode *si, *feature, *x, *field, *value;

	if(!(si = xmlnode_get_child_with_namespace(packet, "si", "http://jabber.org/protocol/si"))) {
		gaim_xfer_cancel_remote(xfer);
		return;
	}

	if(!(feature = xmlnode_get_child_with_namespace(si, "feature", "http://jabber.org/protocol/feature-neg"))) {
		gaim_xfer_cancel_remote(xfer);
		return;
	}

	if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data"))) {
		gaim_xfer_cancel_remote(xfer);
		return;
	}

	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
		const char *var = xmlnode_get_attrib(field, "var");

		if(var && !strcmp(var, "stream-method")) {
			if((value = xmlnode_get_child(field, "value"))) {
				char *val = xmlnode_get_data(value);
				if(val && !strcmp(val, "http://jabber.org/protocol/bytestreams")) {
					jabber_si_xfer_bytestreams_send_init(xfer);
					g_free(val);
					return;
				}
				g_free(val);
			}
		}
	}
	gaim_xfer_cancel_remote(xfer);
}
예제 #5
0
static void jabber_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
	/* it doesn't make sense to have more than one item here, so let's just pick the first one */
	xmlnode *item = xmlnode_get_child(items, "item");
	JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE);
	xmlnode *nick;
	char *nickname = NULL;

	/* ignore the nick of people not on our buddy list */
	if (!buddy || !item)
		return;

	nick = xmlnode_get_child_with_namespace(item, "nick", "http://jabber.org/protocol/nick");
	if (!nick)
		return;
	nickname = xmlnode_get_data(nick);
	serv_got_alias(js->gc, from, nickname);
	g_free(nickname);
}
예제 #6
0
static void do_nick_got_own_nick_cb(JabberStream *js, const char *from, xmlnode *items) {
	char *oldnickname = NULL;
	xmlnode *item = NULL;
	
	if (items)
		item = xmlnode_get_child(items,"item");

	if(item) {
		xmlnode *nick = xmlnode_get_child_with_namespace(item,"nick","http://jabber.org/protocol/nick");
		if(nick)
			oldnickname = xmlnode_get_data(nick);
	}

	purple_request_input(js->gc, _("Set User Nickname"), _("Please specify a new nickname for you."),
		_("This information is visible to all contacts on your contact list, so choose something appropriate."),
		oldnickname, FALSE, FALSE, NULL, _("Set"), PURPLE_CALLBACK(do_nick_set), _("Cancel"), NULL,
		purple_connection_get_account(js->gc), NULL, NULL, js);
	g_free(oldnickname);
}
예제 #7
0
/* ------------------
 * called on received presence
 * ------------------ */
static gboolean
jabber_presence_received(PurpleConnection *pc, const char *type,
                         const char *from, xmlnode *presence)
{
	const xmlnode* parent_node = presence;
	xmlnode* x_node = NULL;

	// check if presence has special "x" childnode
	x_node = xmlnode_get_child_with_namespace(parent_node,"x",NS_SIGNED);
	if (x_node != NULL)
	{
		// user supports openpgp encryption
		purple_debug_info(PLUGIN_ID, "user %s supports openpgp encryption!\n",from);

		char* x_node_data = xmlnode_get_data(x_node);
		if (x_node_data != NULL)
		{
			// try to verify
			char* fpr = verify(x_node_data);
			if (fpr != NULL)
			{
				char* bare_jid = get_bare_jid(from);
				purple_debug_info(PLUGIN_ID, "user %s has fingerprint %s\n",bare_jid,fpr);

				// add key to list
				struct list_item *item = malloc(sizeof(struct list_item));
				item->fpr = fpr;
				g_hash_table_replace(list_fingerprints,bare_jid,item);
			}else
			{
				purple_debug_error(PLUGIN_ID, "could not verify presence of user %s\n",from);
			}
		}else
		{
			purple_debug_info(PLUGIN_ID, "user %s sent empty signed presence\n",from);
		}
	}

	/* We don't want the plugin to stop processing */
	return FALSE;
}
예제 #8
0
파일: xmlnode.c 프로젝트: bf4/pidgin-mac
xmlnode*
xmlnode_get_child(const xmlnode *parent, const char *name)
{
	return xmlnode_get_child_with_namespace(parent, name, NULL);
}
예제 #9
0
파일: jabber.c 프로젝트: Draghtnod/pidgin
static void
_jabber_parse_and_write_message_to_ui(xmlnode *message_node, PurpleBuddy *pb)
{
	xmlnode *body_node, *html_node, *events_node;
	PurpleConnection *gc = purple_account_get_connection(purple_buddy_get_account(pb));
	gchar *body = NULL;

	body_node = xmlnode_get_child(message_node, "body");
	html_node = xmlnode_get_child(message_node, "html");

	if (body_node == NULL && html_node == NULL) {
		purple_debug_error("bonjour", "No body or html node found, discarding message.\n");
		return;
	}

	events_node = xmlnode_get_child_with_namespace(message_node, "x", "jabber:x:event");
	if (events_node != NULL) {
#if 0
		if (xmlnode_get_child(events_node, "composing") != NULL)
			composing_event = TRUE;
#endif
		if (xmlnode_get_child(events_node, "id") != NULL) {
			/* The user is just typing */
			/* TODO: Deal with typing notification */
			return;
		}
	}

	if (html_node != NULL) {
		xmlnode *html_body_node;

		html_body_node = xmlnode_get_child(html_node, "body");
		if (html_body_node != NULL) {
			xmlnode *html_body_font_node;

			html_body_font_node = xmlnode_get_child(html_body_node, "font");
			/* Types of messages sent by iChat */
			if (html_body_font_node != NULL) {
				gchar *html_body;
				const char *font_face, *font_size,
					*ichat_balloon_color, *ichat_text_color;

				font_face = xmlnode_get_attrib(html_body_font_node, "face");
				/* The absolute iChat font sizes should be converted to 1..7 range */
				font_size = xmlnode_get_attrib(html_body_font_node, "ABSZ");
				if (font_size != NULL)
					font_size = _font_size_ichat_to_purple(atoi(font_size));
				/*font_color = xmlnode_get_attrib(html_body_font_node, "color");*/
				ichat_balloon_color = xmlnode_get_attrib(html_body_node, "ichatballooncolor");
				ichat_text_color = xmlnode_get_attrib(html_body_node, "ichattextcolor");

				html_body = get_xmlnode_contents(html_body_font_node);

				if (html_body == NULL)
					/* This is the kind of formatted messages that Purple creates */
					html_body = xmlnode_to_str(html_body_font_node, NULL);

				if (html_body != NULL) {
					GString *str = g_string_new("<font");

					if (font_face)
						g_string_append_printf(str, " face='%s'", font_face);
					if (font_size)
						g_string_append_printf(str, " size='%s'", font_size);
					if (ichat_text_color)
						g_string_append_printf(str, " color='%s'", ichat_text_color);
					if (ichat_balloon_color)
						g_string_append_printf(str, " back='%s'", ichat_balloon_color);
					g_string_append_printf(str, ">%s</font>", html_body);

					body = g_string_free(str, FALSE);

					g_free(html_body);
				}
			}
		}
	}

	/* Compose the message */
	if (body == NULL && body_node != NULL)
		body = xmlnode_get_data(body_node);

	if (body == NULL) {
		purple_debug_error("bonjour", "No html body or regular body found.\n");
		return;
	}

	/* Send the message to the UI */
	serv_got_im(gc, purple_buddy_get_name(pb), body, 0, time(NULL));

	g_free(body);
}
예제 #10
0
파일: presence.c 프로젝트: Lilitana/Pidgin
void jabber_presence_parse(JabberStream *js, xmlnode *packet)
{
	const char *type;
	JabberBuddyResource *jbr = NULL;
	gboolean signal_return, ret;
	JabberPresence presence;
	xmlnode *child;

	memset(&presence, 0, sizeof(presence));
	/* defaults */
	presence.state = JABBER_BUDDY_STATE_UNKNOWN;
	presence.sent = time(NULL);
	/* interesting values */
	presence.from = xmlnode_get_attrib(packet, "from");
	presence.to   = xmlnode_get_attrib(packet, "to");
	type = xmlnode_get_attrib(packet, "type");
	presence.type = str_to_presence_type(type);

	presence.jb = jabber_buddy_find(js, presence.from, TRUE);
	g_return_if_fail(presence.jb != NULL);

	presence.jid_from = jabber_id_new(presence.from);
	if (presence.jid_from == NULL) {
		purple_debug_error("jabber", "Ignoring presence with malformed 'from' "
		                   "JID: %s\n", presence.from);
		return;
	}

	signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc),
			"jabber-receiving-presence", js->gc, type, presence.from, packet));
	if (signal_return) {
		goto out;
	}

	if (presence.jid_from->node)
		presence.chat = jabber_chat_find(js, presence.jid_from->node,
		                                 presence.jid_from->domain);
	if(presence.jb->error_msg) {
		g_free(presence.jb->error_msg);
		presence.jb->error_msg = NULL;
	}

	if (presence.type == JABBER_PRESENCE_AVAILABLE) {
		presence.state = JABBER_BUDDY_STATE_ONLINE;
	} else if (presence.type == JABBER_PRESENCE_ERROR) {
		/* TODO: Is this handled properly?  Should it be treated as per-jbr? */
		char *msg = jabber_parse_error(js, packet, NULL);
		presence.state = JABBER_BUDDY_STATE_ERROR;
		presence.jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence"));
	} else if (presence.type == JABBER_PRESENCE_SUBSCRIBE) {
		/* TODO: Move to handle_subscribe() (so nick is extracted by the
		 * PresenceHandler */
		struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1);
		gboolean onlist = FALSE;
		PurpleAccount *account;
		PurpleBuddy *buddy;
		xmlnode *nick;

		account = purple_connection_get_account(js->gc);
		buddy = purple_find_buddy(account, presence.from);
		nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick");
		if (nick)
			presence.nickname = xmlnode_get_data(nick);

		if (buddy) {
			if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING)))
				onlist = TRUE;
		}

		jap->gc = js->gc;
		jap->who = g_strdup(presence.from);
		jap->js = js;

		purple_account_request_authorization(account, presence.from, NULL, presence.nickname,
				NULL, onlist, authorize_add_cb, deny_add_cb, jap);

		goto out;
	} else if (presence.type == JABBER_PRESENCE_SUBSCRIBED) {
예제 #11
0
파일: presence.c 프로젝트: Lilitana/Pidgin
static void
jabber_presence_set_capabilities(JabberCapsClientInfo *info, GList *exts,
                                 JabberPresenceCapabilities *userdata)
{
	JabberBuddyResource *jbr;
	char *resource = strchr(userdata->from, '/');

	if (resource)
		resource += 1;

	jbr = jabber_buddy_find_resource(userdata->jb, resource);
	if (!jbr) {
		g_free(userdata->from);
		g_free(userdata);
		if (exts) {
			g_list_foreach(exts, (GFunc)g_free, NULL);
			g_list_free(exts);
		}
		return;
	}

	/* Any old jbr->caps.info is owned by the caps code */
	if (jbr->caps.exts) {
		g_list_foreach(jbr->caps.exts, (GFunc)g_free, NULL);
		g_list_free(jbr->caps.exts);
	}

	jbr->caps.info = info;
	jbr->caps.exts = exts;

	purple_prpl_got_media_caps(
			purple_connection_get_account(userdata->js->gc),
			userdata->from);
	if (info == NULL)
		goto out;

	if (!jbr->commands_fetched && jabber_resource_has_capability(jbr, "http://jabber.org/protocol/commands")) {
		JabberIq *iq = jabber_iq_new_query(userdata->js, JABBER_IQ_GET, NS_DISCO_ITEMS);
		xmlnode *query = xmlnode_get_child_with_namespace(iq->node, "query", NS_DISCO_ITEMS);
		xmlnode_set_attrib(iq->node, "to", userdata->from);
		xmlnode_set_attrib(query, "node", "http://jabber.org/protocol/commands");
		jabber_iq_set_callback(iq, jabber_adhoc_disco_result_cb, NULL);
		jabber_iq_send(iq);

		jbr->commands_fetched = TRUE;
	}

#if 0
	/*
	 * Versions of libpurple before 2.6.0 didn't advertise this capability, so
	 * we can't yet use Entity Capabilities to determine whether or not the
	 * other client supports Chat States.
	 */
	if (jabber_resource_has_capability(jbr, "http://jabber.org/protocol/chatstates"))
		jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
	else
		jbr->chat_states = JABBER_CHAT_STATES_UNSUPPORTED;
#endif

out:
	g_free(userdata->from);
	g_free(userdata);
}
예제 #12
0
/* ------------------
 * called on received message
 * ------------------ */
static gboolean
jabber_message_received(PurpleConnection *pc, const char *type, const char *id,
                        const char *from, const char *to, xmlnode *message)
{
	const xmlnode* parent_node = message;
	xmlnode* x_node = NULL;
	xmlnode* body_node = NULL;

	if (parent_node == NULL)
		return FALSE;

	// check if message is a key
	body_node = xmlnode_get_child(parent_node,"body");
	if (body_node != NULL)
	{
		char* data = xmlnode_get_data(body_node);
		if (data != NULL)
		{
			char* header = "-----BEGIN PGP PUBLIC KEY BLOCK-----";
			if (strncmp(data,header,strlen(header)) == 0)
			{
				// if we received a ascii armored key
				// try to import it
				//purple_conversation_write(conv,"","received key",PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LOG,time(NULL));
				if (import_key(data) == TRUE)
				{
					xmlnode_clear_data(body_node);
					xmlnode_insert_data(body_node,"key import ok",-1);
				}
				else
				{
					xmlnode_clear_data(body_node);
					xmlnode_insert_data(body_node,"key import failed",-1);
				}
			}
		}
	}

	// check if the user with the jid=from has signed his presence
	char* bare_jid = get_bare_jid(from);

	// get stored info about user
	struct list_item* item = g_hash_table_lookup(list_fingerprints,bare_jid);
	if (item == NULL)
	{
		//TODO: maybe create item in list?
	}else
	{
		// set default value to "not encrypted mode"
		item->mode_sec = FALSE;
	}
	free(bare_jid);

	// check if message has special "x" child node => encrypted message
	x_node = xmlnode_get_child_with_namespace(parent_node,"x",NS_ENC);
	if (x_node != NULL)
	{
		purple_debug_info(PLUGIN_ID, "user %s sent us an encrypted message\n",from);

		// get data of "x" node
		char* cipher_str = xmlnode_get_data(x_node);
		if (cipher_str != NULL)
		{
			// try to decrypt
			char* plain_str = decrypt(cipher_str);
			if (plain_str != NULL)
			{
				purple_debug_info(PLUGIN_ID, "decrypted message: %s\n",plain_str);
				// find body node
				xmlnode *body_node = xmlnode_get_child(parent_node,"body");
				if (body_node != NULL)
				{
					// clear body node data if it is found
					xmlnode_clear_data(body_node);
				}else
				{
					// add body node if it is not found
					body_node = xmlnode_new_child(message,"body");
				}
				// set "body" content node to decrypted string
				//xmlnode_insert_data(body_node,"Encrypted message: ",-1);
				xmlnode_insert_data(body_node,plain_str,-1);

				// only set to encrypted mode, if we know other users key fingerprint
				if (item != NULL)
				{
					// all went well, we received an encrypted message
					item->mode_sec = TRUE;
				}
			}else
			{
				purple_debug_error(PLUGIN_ID, "could not decrypt message!\n");
			}
		}else
		{
			purple_debug_error(PLUGIN_ID, "xml token had no data!\n");
		}
	}

	/* We don't want the plugin to stop processing */
	return FALSE;
}
예제 #13
0
파일: si.c 프로젝트: VoxOx/VoxOx
void jabber_si_parse(JabberStream *js, xmlnode *packet)
{
	JabberSIXfer *jsx;
	GaimXfer *xfer;
	xmlnode *si, *file, *feature, *x, *field, *option, *value;
	const char *stream_id, *filename, *filesize_c, *profile, *from;
	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;

	if(!(stream_id = xmlnode_get_attrib(si, "id")))
		return;

	if(!(file = xmlnode_get_child(si, "file")))
		return;

	if(!(filename = xmlnode_get_attrib(file, "name")))
		return;

	if((filesize_c = xmlnode_get_attrib(file, "size")))
		filesize = atoi(filesize_c);

	if(!(feature = xmlnode_get_child(si, "feature")))
		return;

	if(!(x = xmlnode_get_child_with_namespace(feature, "x", "jabber:x:data")))
		return;

	if(!(from = xmlnode_get_attrib(packet, "from")))
		return;

	/* if they've already sent us this file transfer with the same damn id
	 * then we're gonna ignore it, until I think of something better to do
	 * with it */
	if((xfer = jabber_si_xfer_find(js, stream_id, from)))
		return;

	jsx = g_new0(JabberSIXfer, 1);

	for(field = xmlnode_get_child(x, "field"); field; field = xmlnode_get_next_twin(field)) {
		const char *var = xmlnode_get_attrib(field, "var");
		if(var && !strcmp(var, "stream-method")) {
			for(option = xmlnode_get_child(field, "option"); option;
					option = xmlnode_get_next_twin(option)) {
				if((value = xmlnode_get_child(option, "value"))) {
					char *val;
					if((val = xmlnode_get_data(value))) {
						if(!strcmp(val, "http://jabber.org/protocol/bytestreams")) {
							jsx->stream_method |= STREAM_METHOD_BYTESTREAMS;
							/*
						} else if(!strcmp(val, "http://jabber.org/protocol/ibb")) {
							jsx->stream_method |= STREAM_METHOD_IBB;
							*/
						}
						g_free(val);
					}
				}
			}
		}
	}

	if(jsx->stream_method == STREAM_METHOD_UNKNOWN) {
		g_free(jsx);
		return;
	}

	jsx->js = js;
	jsx->stream_id = g_strdup(stream_id);
	jsx->iq_id = g_strdup(xmlnode_get_attrib(packet, "id"));

	xfer = gaim_xfer_new(js->gc->account, GAIM_XFER_RECEIVE, from);
	if (xfer)
	{
		xfer->data = jsx;

		gaim_xfer_set_filename(xfer, filename);
		if(filesize > 0)
			gaim_xfer_set_size(xfer, filesize);

		gaim_xfer_set_init_fnc(xfer, jabber_si_xfer_init);
		gaim_xfer_set_request_denied_fnc(xfer, jabber_si_xfer_request_denied);
		gaim_xfer_set_cancel_recv_fnc(xfer, jabber_si_xfer_cancel_recv);
		gaim_xfer_set_end_fnc(xfer, jabber_si_xfer_end);

		js->file_transfers = g_list_append(js->file_transfers, xfer);

		gaim_xfer_request(xfer);
	}
}
예제 #14
0
파일: iq.c 프로젝트: bf4/pidgin-mac
void jabber_iq_parse(JabberStream *js, xmlnode *packet)
{
	JabberCallbackData *jcd;
	xmlnode *query, *error, *x;
	const char *xmlns;
	const char *type, *id, *from;
	JabberIqHandler *jih;

	query = xmlnode_get_child(packet, "query");
	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"))) {
		purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n",
						   type ? 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")) {
			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");
			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);
			xmlnode_set_attrib(iq->node, "id", iq->id);
			error = xmlnode_new_child(iq->node, "error");
			xmlnode_set_attrib(error, "type", "modify");
			x = xmlnode_new_child(error, "bad-request");
			xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas");

			jabber_iq_send(iq);
		} else
			purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n", type);

		return;
	}

	/* First, lets see if a special callback got registered */

	if(!strcmp(type, "result") || !strcmp(type, "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);
			return;
		}
	}

	/* Apparently not, so lets see if we have a pre-defined handler */

	if(query && (xmlns = xmlnode_get_namespace(query))) {
		if((jih = g_hash_table_lookup(iq_handlers, xmlns))) {
			jih(js, packet);
			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")) {
		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");
		xmlnode_set_attrib(iq->node, "type", "error");
		error = xmlnode_new_child(iq->node, "error");
		xmlnode_set_attrib(error, "type", "cancel");
		xmlnode_set_attrib(error, "code", "501");
		x = xmlnode_new_child(error, "feature-not-implemented");
		xmlnode_set_namespace(x, "urn:ietf:params:xml:ns:xmpp-stanzas");

		jabber_iq_send(iq);
	}
}