Standardize all protocol header guard macros.
[pidgin-git.git] / libpurple / protocols / jabber / iq.c
bloba8db6f5a22fe5300fa4e8ef751ca42e38154b086
1 /*
2 * purple - Jabber Protocol Plugin
4 * Purple is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "internal.h"
24 #include "core.h"
25 #include "debug.h"
26 #include "prefs.h"
27 #include "util.h"
29 #include "buddy.h"
30 #include "disco.h"
31 #include "google/gmail.h"
32 #include "google/google.h"
33 #include "google/jingleinfo.h"
34 #include "google/google_session.h"
35 #include "iq.h"
36 #include "jingle/jingle.h"
37 #include "oob.h"
38 #include "roster.h"
39 #include "si.h"
40 #include "ping.h"
41 #include "adhoccommands.h"
42 #include "data.h"
43 #include "ibb.h"
45 static GHashTable *iq_handlers = NULL;
46 static GHashTable *signal_iq_handlers = NULL;
48 struct _JabberIqCallbackData {
49 JabberIqCallback *callback;
50 gpointer data;
51 JabberID *to;
54 void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd)
56 jabber_id_free(jcd->to);
57 g_free(jcd);
60 JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
62 JabberIq *iq;
64 iq = g_new0(JabberIq, 1);
66 iq->type = type;
68 iq->node = purple_xmlnode_new("iq");
69 switch(iq->type) {
70 case JABBER_IQ_SET:
71 purple_xmlnode_set_attrib(iq->node, "type", "set");
72 break;
73 case JABBER_IQ_GET:
74 purple_xmlnode_set_attrib(iq->node, "type", "get");
75 break;
76 case JABBER_IQ_ERROR:
77 purple_xmlnode_set_attrib(iq->node, "type", "error");
78 break;
79 case JABBER_IQ_RESULT:
80 purple_xmlnode_set_attrib(iq->node, "type", "result");
81 break;
82 case JABBER_IQ_NONE:
83 /* this shouldn't ever happen */
84 break;
87 iq->js = js;
89 if(type == JABBER_IQ_GET || type == JABBER_IQ_SET) {
90 iq->id = jabber_get_next_id(js);
91 purple_xmlnode_set_attrib(iq->node, "id", iq->id);
94 return iq;
97 JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type,
98 const char *xmlns)
100 JabberIq *iq = jabber_iq_new(js, type);
101 PurpleXmlNode *query;
103 query = purple_xmlnode_new_child(iq->node, "query");
104 purple_xmlnode_set_namespace(query, xmlns);
106 return iq;
109 void
110 jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *callback, gpointer data)
112 iq->callback = callback;
113 iq->callback_data = data;
116 void jabber_iq_set_id(JabberIq *iq, const char *id)
118 g_free(iq->id);
120 if(id) {
121 purple_xmlnode_set_attrib(iq->node, "id", id);
122 iq->id = g_strdup(id);
123 } else {
124 purple_xmlnode_remove_attrib(iq->node, "id");
125 iq->id = NULL;
129 void jabber_iq_send(JabberIq *iq)
131 JabberIqCallbackData *jcd;
132 g_return_if_fail(iq != NULL);
134 jabber_send(iq->js, iq->node);
136 if(iq->id && iq->callback) {
137 jcd = g_new0(JabberIqCallbackData, 1);
138 jcd->callback = iq->callback;
139 jcd->data = iq->callback_data;
140 jcd->to = jabber_id_new(purple_xmlnode_get_attrib(iq->node, "to"));
142 g_hash_table_insert(iq->js->iq_callbacks, g_strdup(iq->id), jcd);
145 jabber_iq_free(iq);
148 void jabber_iq_free(JabberIq *iq)
150 g_return_if_fail(iq != NULL);
152 g_free(iq->id);
153 purple_xmlnode_free(iq->node);
154 g_free(iq);
157 static void jabber_iq_last_parse(JabberStream *js, const char *from,
158 JabberIqType type, const char *id,
159 PurpleXmlNode *packet)
161 JabberIq *iq;
162 PurpleXmlNode *query;
163 char *idle_time;
165 if(type == JABBER_IQ_GET) {
166 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, NS_LAST_ACTIVITY);
167 jabber_iq_set_id(iq, id);
168 if (from)
169 purple_xmlnode_set_attrib(iq->node, "to", from);
171 query = purple_xmlnode_get_child(iq->node, "query");
173 idle_time = g_strdup_printf("%ld", js->idle ? time(NULL) - js->idle : 0);
174 purple_xmlnode_set_attrib(query, "seconds", idle_time);
175 g_free(idle_time);
177 jabber_iq_send(iq);
181 static void jabber_time_parse(JabberStream *js, const char *from,
182 JabberIqType type, const char *id,
183 PurpleXmlNode *child)
185 JabberIq *iq;
187 if(type == JABBER_IQ_GET) {
188 PurpleXmlNode *tzo, *utc;
189 GDateTime *now, *now_utc;
190 gchar *date, *tz;
192 iq = jabber_iq_new(js, JABBER_IQ_RESULT);
193 jabber_iq_set_id(iq, id);
194 if (from)
195 purple_xmlnode_set_attrib(iq->node, "to", from);
197 child = purple_xmlnode_new_child(iq->node, child->name);
198 purple_xmlnode_set_namespace(child, NS_ENTITY_TIME);
200 /* <tzo>-06:00</tzo> */
201 now = g_date_time_new_now_local();
202 tz = g_date_time_format(now, "%:z");
203 tzo = purple_xmlnode_new_child(child, "tzo");
204 purple_xmlnode_insert_data(tzo, tz, -1);
205 g_free(tz);
207 /* <utc>2006-12-19T17:58:35Z</utc> */
208 now_utc = g_date_time_to_utc(now);
209 date = g_date_time_format(now_utc, "%FT%TZ");
210 utc = purple_xmlnode_new_child(child, "utc");
211 purple_xmlnode_insert_data(utc, date, -1);
212 g_free(date);
214 g_date_time_unref(now);
215 g_date_time_unref(now_utc);
217 jabber_iq_send(iq);
218 } else {
219 /* TODO: Errors */
223 static void jabber_iq_version_parse(JabberStream *js, const char *from,
224 JabberIqType type, const char *id,
225 PurpleXmlNode *packet)
227 JabberIq *iq;
228 PurpleXmlNode *query;
230 if(type == JABBER_IQ_GET) {
231 GHashTable *ui_info;
232 const char *ui_name = NULL, *ui_version = NULL;
234 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version");
235 if (from)
236 purple_xmlnode_set_attrib(iq->node, "to", from);
237 jabber_iq_set_id(iq, id);
239 query = purple_xmlnode_get_child(iq->node, "query");
241 ui_info = purple_core_get_ui_info();
243 if(NULL != ui_info) {
244 ui_name = g_hash_table_lookup(ui_info, "name");
245 ui_version = g_hash_table_lookup(ui_info, "version");
248 if(NULL != ui_name && NULL != ui_version) {
249 char *version_complete = g_strdup_printf("%s (libpurple " VERSION ")", ui_version);
250 purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), ui_name, -1);
251 purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), version_complete, -1);
252 g_free(version_complete);
253 } else {
254 purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "name"), "libpurple", -1);
255 purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), VERSION, -1);
258 jabber_iq_send(iq);
262 void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id)
264 g_hash_table_remove(js->iq_callbacks, id);
268 * Verify that the 'from' attribute of an IQ reply is a valid match for
269 * a given IQ request. The expected behavior is outlined in section
270 * 8.1.2.1 of the XMPP CORE spec (RFC 6120). We consider the reply to
271 * be a valid match if any of the following is true:
272 * - Request 'to' matches reply 'from' (including the case where
273 * neither are set).
274 * - Request 'to' was my JID (bare or full) and reply 'from' is empty.
275 * - Request 'to' was empty and reply 'from' is my JID. The spec says
276 * we should only allow bare JID, but we also allow full JID for
277 * compatibility with some servers.
278 * - Request 'to' was empty and reply 'from' is server JID. Not allowed by
279 * any spec, but for compatibility with some servers.
281 * These rules should allow valid IQ replies while preventing spoofed
282 * ones.
284 * For more discussion see the "Spoofing of iq ids and misbehaving
285 * servers" email thread from January 2014 on the jdev and security
286 * mailing lists. Also see https://developer.pidgin.im/ticket/15879
288 * @return TRUE if this reply is valid for the given request.
290 static gboolean does_reply_from_match_request_to(JabberStream *js, JabberID *to, JabberID *from)
292 if (jabber_id_equal(to, from)) {
293 /* Request 'to' matches reply 'from' */
294 return TRUE;
297 if (!from && purple_strequal(to->node, js->user->node)
298 && purple_strequal(to->domain, js->user->domain)) {
299 /* Request 'to' was my JID (bare or full) and reply 'from' is empty */
300 return TRUE;
303 if (!to && purple_strequal(from->domain, js->user->domain)) {
304 /* Request 'to' is empty and reply 'from' domain matches our domain */
306 if (!from->node && !from->resource) {
307 /* Reply 'from' is server bare JID */
308 return TRUE;
311 if (purple_strequal(from->node, js->user->node)
312 && (!from->resource || purple_strequal(from->resource, js->user->resource))) {
313 /* Reply 'from' is my full or bare JID */
314 return TRUE;
318 return FALSE;
321 void jabber_iq_parse(JabberStream *js, PurpleXmlNode *packet)
323 JabberIqCallbackData *jcd;
324 PurpleXmlNode *child, *error, *x;
325 const char *xmlns;
326 const char *iq_type, *id, *from;
327 JabberIqType type = JABBER_IQ_NONE;
328 gboolean signal_return;
329 JabberID *from_id;
331 from = purple_xmlnode_get_attrib(packet, "from");
332 id = purple_xmlnode_get_attrib(packet, "id");
333 iq_type = purple_xmlnode_get_attrib(packet, "type");
336 * Ensure the 'from' attribute is valid. No point in handling a stanza
337 * of which we don't understand where it came from.
339 from_id = jabber_id_new(from);
341 if (from && !from_id) {
342 purple_debug_error("jabber", "Received an iq with an invalid from: %s\n", from);
343 return;
347 * child will be either the first tag child or NULL if there is no child.
348 * Historically, we used just the 'query' subchild, but newer XEPs use
349 * differently named children. Grabbing the first child is (for the time
350 * being) sufficient.
352 for (child = packet->child; child; child = child->next) {
353 if (child->type == PURPLE_XMLNODE_TYPE_TAG)
354 break;
357 if (iq_type) {
358 if (purple_strequal(iq_type, "get"))
359 type = JABBER_IQ_GET;
360 else if (purple_strequal(iq_type, "set"))
361 type = JABBER_IQ_SET;
362 else if (purple_strequal(iq_type, "result"))
363 type = JABBER_IQ_RESULT;
364 else if (purple_strequal(iq_type, "error"))
365 type = JABBER_IQ_ERROR;
368 if (type == JABBER_IQ_NONE) {
369 purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n",
370 iq_type ? iq_type : "(null)");
371 jabber_id_free(from_id);
372 return;
375 /* All IQs must have an ID, so send an error for a set/get that doesn't */
376 if(!id || !*id) {
378 if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
379 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
381 purple_xmlnode_free(iq->node);
382 iq->node = purple_xmlnode_copy(packet);
383 if (from) {
384 purple_xmlnode_set_attrib(iq->node, "to", from);
385 purple_xmlnode_remove_attrib(iq->node, "from");
388 purple_xmlnode_set_attrib(iq->node, "type", "error");
389 /* This id is clearly not useful, but we must put something there for a valid stanza */
390 iq->id = jabber_get_next_id(js);
391 purple_xmlnode_set_attrib(iq->node, "id", iq->id);
392 error = purple_xmlnode_new_child(iq->node, "error");
393 purple_xmlnode_set_attrib(error, "type", "modify");
394 x = purple_xmlnode_new_child(error, "bad-request");
395 purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS);
397 jabber_iq_send(iq);
398 } else
399 purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n",
400 iq_type);
402 jabber_id_free(from_id);
403 return;
406 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc),
407 "jabber-receiving-iq", js->gc, iq_type, id, from, packet));
408 if (signal_return) {
409 jabber_id_free(from_id);
410 return;
413 /* First, lets see if a special callback got registered */
414 if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) {
415 jcd = g_hash_table_lookup(js->iq_callbacks, id);
416 if (jcd) {
417 if (does_reply_from_match_request_to(js, jcd->to, from_id)) {
418 jcd->callback(js, from, type, id, packet, jcd->data);
419 jabber_iq_remove_callback_by_id(js, id);
420 jabber_id_free(from_id);
421 return;
422 } else {
423 char *expected_to;
425 if (jcd->to) {
426 expected_to = jabber_id_get_full_jid(jcd->to);
427 } else {
428 expected_to = jabber_id_get_bare_jid(js->user);
431 purple_debug_error("jabber", "Got a result iq with id %s from %s instead of expected %s!\n", id, from ? from : "(null)", expected_to);
433 g_free(expected_to);
439 * Apparently not, so let's see if we have a pre-defined handler
440 * or if an outside plugin is interested.
442 if(child && (xmlns = purple_xmlnode_get_namespace(child))) {
443 char *key = g_strdup_printf("%s %s", child->name, xmlns);
444 JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key);
445 int signal_ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
446 g_free(key);
448 if (signal_ref > 0) {
449 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js->gc), "jabber-watched-iq",
450 js->gc, iq_type, id, from, child));
451 if (signal_return) {
452 jabber_id_free(from_id);
453 return;
457 if(jih) {
458 jih(js, from, type, id, child);
459 jabber_id_free(from_id);
460 return;
464 purple_debug_misc("jabber", "Unhandled IQ with id %s\n", id);
466 /* If we get here, send the default error reply mandated by XMPP-CORE */
467 if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
468 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
470 purple_xmlnode_free(iq->node);
471 iq->node = purple_xmlnode_copy(packet);
472 if (from) {
473 purple_xmlnode_set_attrib(iq->node, "to", from);
474 purple_xmlnode_remove_attrib(iq->node, "from");
477 purple_xmlnode_set_attrib(iq->node, "type", "error");
478 error = purple_xmlnode_new_child(iq->node, "error");
479 purple_xmlnode_set_attrib(error, "type", "cancel");
480 purple_xmlnode_set_attrib(error, "code", "501");
481 x = purple_xmlnode_new_child(error, "feature-not-implemented");
482 purple_xmlnode_set_namespace(x, NS_XMPP_STANZAS);
484 jabber_iq_send(iq);
487 jabber_id_free(from_id);
490 void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc)
493 * This is valid because nodes nor namespaces cannot have spaces in them
494 * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and
495 * http://www.w3.org/TR/REC-xml-names/)
497 char *key = g_strdup_printf("%s %s", node, xmlns);
498 g_hash_table_replace(iq_handlers, key, handlerfunc);
501 void jabber_iq_signal_register(const gchar *node, const gchar *xmlns)
503 gchar *key;
504 int ref;
506 g_return_if_fail(node != NULL && *node != '\0');
507 g_return_if_fail(xmlns != NULL && *xmlns != '\0');
509 key = g_strdup_printf("%s %s", node, xmlns);
510 ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
511 if (ref == 0) {
512 g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(1));
513 } else {
514 g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref + 1));
515 g_free(key);
519 void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns)
521 gchar *key;
522 int ref;
524 g_return_if_fail(node != NULL && *node != '\0');
525 g_return_if_fail(xmlns != NULL && *xmlns != '\0');
527 key = g_strdup_printf("%s %s", node, xmlns);
528 ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
530 if (ref == 1) {
531 g_hash_table_remove(signal_iq_handlers, key);
532 } else if (ref > 1) {
533 g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref - 1));
536 g_free(key);
539 void jabber_iq_init(void)
541 iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
542 signal_iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
544 jabber_iq_register_handler("jingle", JINGLE, jingle_parse);
545 jabber_iq_register_handler("mailbox", NS_GOOGLE_MAIL_NOTIFY,
546 jabber_gmail_poke);
547 jabber_iq_register_handler("new-mail", NS_GOOGLE_MAIL_NOTIFY,
548 jabber_gmail_poke);
549 jabber_iq_register_handler("ping", NS_PING, jabber_ping_parse);
550 jabber_iq_register_handler("query", NS_GOOGLE_JINGLE_INFO,
551 jabber_google_handle_jingle_info);
552 jabber_iq_register_handler("query", NS_BYTESTREAMS,
553 jabber_bytestreams_parse);
554 jabber_iq_register_handler("query", NS_DISCO_INFO, jabber_disco_info_parse);
555 jabber_iq_register_handler("query", NS_DISCO_ITEMS, jabber_disco_items_parse);
556 jabber_iq_register_handler("query", NS_LAST_ACTIVITY, jabber_iq_last_parse);
557 jabber_iq_register_handler("query", NS_OOB_IQ_DATA, jabber_oob_parse);
558 jabber_iq_register_handler("query", "jabber:iq:register",
559 jabber_register_parse);
560 jabber_iq_register_handler("query", "jabber:iq:roster",
561 jabber_roster_parse);
562 jabber_iq_register_handler("query", "jabber:iq:version",
563 jabber_iq_version_parse);
564 #ifdef USE_VV
565 jabber_iq_register_handler("session", NS_GOOGLE_SESSION,
566 jabber_google_session_parse);
567 #endif
568 jabber_iq_register_handler("block", NS_SIMPLE_BLOCKING, jabber_blocklist_parse_push);
569 jabber_iq_register_handler("unblock", NS_SIMPLE_BLOCKING, jabber_blocklist_parse_push);
570 jabber_iq_register_handler("time", NS_ENTITY_TIME, jabber_time_parse);
574 void jabber_iq_uninit(void)
576 g_hash_table_destroy(iq_handlers);
577 g_hash_table_destroy(signal_iq_handlers);
578 iq_handlers = signal_iq_handlers = NULL;