Mercurial > pidgin
diff libpurple/protocols/jabber/iq.c @ 25979:439f07ce4c8a
propagate from branch 'im.pidgin.pidgin' (head 303af74a38e7b313d4fb0be4d4054a16cb13d819)
to branch 'im.pidgin.cpw.darkrain42.xmpp.iq-handlers' (head b04e8bdc99bebce19e9d8a5df5d7397cffcf2988)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Sat, 07 Mar 2009 02:14:34 +0000 |
parents | 5f9a24d1c25e ba2b63988b3c |
children | ae41d8e827e3 |
line wrap: on
line diff
--- a/libpurple/protocols/jabber/iq.c Thu Mar 05 23:54:50 2009 +0000 +++ b/libpurple/protocols/jabber/iq.c Sat Mar 07 02:14:34 2009 +0000 @@ -143,23 +143,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"); @@ -171,88 +167,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 @@ -265,11 +240,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"); @@ -310,33 +284,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); @@ -348,15 +345,15 @@ 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(id && *id && (jcd = g_hash_table_lookup(js->iq_callbacks, id))) { + if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) { + if((jcd = g_hash_table_lookup(js->iq_callbacks, id))) { jcd->callback(js, packet, jcd->data); jabber_iq_remove_callback_by_id(js, id); return; @@ -364,51 +361,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 (xmlnode_get_child_with_namespace(packet, "data", XEP_0047_NAMESPACE) - || xmlnode_get_child_with_namespace(packet, "close", XEP_0047_NAMESPACE) - || xmlnode_get_child_with_namespace(packet, "open", XEP_0047_NAMESPACE)) { - jabber_ibb_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"); @@ -420,26 +396,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) @@ -447,4 +433,3 @@ g_hash_table_destroy(iq_handlers); iq_handlers = NULL; } -