Remove redundant NULL checks
[pidgin-git.git] / libpurple / protocols / jabber / iq.c
blob0c312d37a1675d8341e2d873e9962e026aeafdec
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 #ifdef _WIN32
46 #include "utsname.h"
47 #endif
49 static GHashTable *iq_handlers = NULL;
50 static GHashTable *signal_iq_handlers = NULL;
52 struct _JabberIqCallbackData {
53 JabberIqCallback *callback;
54 gpointer data;
55 JabberID *to;
58 void jabber_iq_callbackdata_free(JabberIqCallbackData *jcd)
60 jabber_id_free(jcd->to);
61 g_free(jcd);
64 JabberIq *jabber_iq_new(JabberStream *js, JabberIqType type)
66 JabberIq *iq;
68 iq = g_new0(JabberIq, 1);
70 iq->type = type;
72 iq->node = xmlnode_new("iq");
73 switch(iq->type) {
74 case JABBER_IQ_SET:
75 xmlnode_set_attrib(iq->node, "type", "set");
76 break;
77 case JABBER_IQ_GET:
78 xmlnode_set_attrib(iq->node, "type", "get");
79 break;
80 case JABBER_IQ_ERROR:
81 xmlnode_set_attrib(iq->node, "type", "error");
82 break;
83 case JABBER_IQ_RESULT:
84 xmlnode_set_attrib(iq->node, "type", "result");
85 break;
86 case JABBER_IQ_NONE:
87 /* this shouldn't ever happen */
88 break;
91 iq->js = js;
93 if(type == JABBER_IQ_GET || type == JABBER_IQ_SET) {
94 iq->id = jabber_get_next_id(js);
95 xmlnode_set_attrib(iq->node, "id", iq->id);
98 return iq;
101 JabberIq *jabber_iq_new_query(JabberStream *js, JabberIqType type,
102 const char *xmlns)
104 JabberIq *iq = jabber_iq_new(js, type);
105 xmlnode *query;
107 query = xmlnode_new_child(iq->node, "query");
108 xmlnode_set_namespace(query, xmlns);
110 return iq;
113 void
114 jabber_iq_set_callback(JabberIq *iq, JabberIqCallback *callback, gpointer data)
116 iq->callback = callback;
117 iq->callback_data = data;
120 void jabber_iq_set_id(JabberIq *iq, const char *id)
122 g_free(iq->id);
124 if(id) {
125 xmlnode_set_attrib(iq->node, "id", id);
126 iq->id = g_strdup(id);
127 } else {
128 xmlnode_remove_attrib(iq->node, "id");
129 iq->id = NULL;
133 void jabber_iq_send(JabberIq *iq)
135 JabberIqCallbackData *jcd;
136 g_return_if_fail(iq != NULL);
138 jabber_send(iq->js, iq->node);
140 if(iq->id && iq->callback) {
141 jcd = g_new0(JabberIqCallbackData, 1);
142 jcd->callback = iq->callback;
143 jcd->data = iq->callback_data;
144 jcd->to = jabber_id_new(xmlnode_get_attrib(iq->node, "to"));
146 g_hash_table_insert(iq->js->iq_callbacks, g_strdup(iq->id), jcd);
149 jabber_iq_free(iq);
152 void jabber_iq_free(JabberIq *iq)
154 g_return_if_fail(iq != NULL);
156 g_free(iq->id);
157 xmlnode_free(iq->node);
158 g_free(iq);
161 static void jabber_iq_last_parse(JabberStream *js, const char *from,
162 JabberIqType type, const char *id,
163 xmlnode *packet)
165 JabberIq *iq;
166 xmlnode *query;
167 char *idle_time;
169 if(type == JABBER_IQ_GET) {
170 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, NS_LAST_ACTIVITY);
171 jabber_iq_set_id(iq, id);
172 if (from)
173 xmlnode_set_attrib(iq->node, "to", from);
175 query = xmlnode_get_child(iq->node, "query");
177 idle_time = g_strdup_printf("%ld", js->idle ? time(NULL) - js->idle : 0);
178 xmlnode_set_attrib(query, "seconds", idle_time);
179 g_free(idle_time);
181 jabber_iq_send(iq);
185 static void jabber_time_parse(JabberStream *js, const char *from,
186 JabberIqType type, const char *id,
187 xmlnode *child)
189 JabberIq *iq;
190 time_t now_t;
191 struct tm *tm;
193 time(&now_t);
195 if(type == JABBER_IQ_GET) {
196 xmlnode *tzo, *utc;
197 const char *date, *tz;
199 iq = jabber_iq_new(js, JABBER_IQ_RESULT);
200 jabber_iq_set_id(iq, id);
201 if (from)
202 xmlnode_set_attrib(iq->node, "to", from);
204 child = xmlnode_new_child(iq->node, child->name);
205 xmlnode_set_namespace(child, NS_ENTITY_TIME);
207 /* <tzo>-06:00</tzo> */
208 tm = localtime(&now_t);
209 tz = purple_get_tzoff_str(tm, TRUE);
210 tzo = xmlnode_new_child(child, "tzo");
211 xmlnode_insert_data(tzo, tz, -1);
213 /* <utc>2006-12-19T17:58:35Z</utc> */
214 tm = gmtime(&now_t);
215 date = purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm);
216 utc = xmlnode_new_child(child, "utc");
217 xmlnode_insert_data(utc, date, -1);
219 jabber_iq_send(iq);
220 } else {
221 /* TODO: Errors */
225 static void jabber_iq_version_parse(JabberStream *js, const char *from,
226 JabberIqType type, const char *id,
227 xmlnode *packet)
229 JabberIq *iq;
230 xmlnode *query;
232 if(type == JABBER_IQ_GET) {
233 GHashTable *ui_info;
234 const char *ui_name = NULL, *ui_version = NULL;
235 #if 0
236 char *os = NULL;
237 if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) {
238 struct utsname osinfo;
240 uname(&osinfo);
241 os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
242 osinfo.machine);
244 #endif
246 iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version");
247 if (from)
248 xmlnode_set_attrib(iq->node, "to", from);
249 jabber_iq_set_id(iq, id);
251 query = xmlnode_get_child(iq->node, "query");
253 ui_info = purple_core_get_ui_info();
255 if(NULL != ui_info) {
256 ui_name = g_hash_table_lookup(ui_info, "name");
257 ui_version = g_hash_table_lookup(ui_info, "version");
260 if(NULL != ui_name && NULL != ui_version) {
261 char *version_complete = g_strdup_printf("%s (libpurple " VERSION ")", ui_version);
262 xmlnode_insert_data(xmlnode_new_child(query, "name"), ui_name, -1);
263 xmlnode_insert_data(xmlnode_new_child(query, "version"), version_complete, -1);
264 g_free(version_complete);
265 } else {
266 xmlnode_insert_data(xmlnode_new_child(query, "name"), "libpurple", -1);
267 xmlnode_insert_data(xmlnode_new_child(query, "version"), VERSION, -1);
270 #if 0
271 if(os) {
272 xmlnode_insert_data(xmlnode_new_child(query, "os"), os, -1);
273 g_free(os);
275 #endif
277 jabber_iq_send(iq);
281 void jabber_iq_remove_callback_by_id(JabberStream *js, const char *id)
283 g_hash_table_remove(js->iq_callbacks, id);
287 * Verify that the 'from' attribute of an IQ reply is a valid match for
288 * a given IQ request. The expected behavior is outlined in section
289 * 8.1.2.1 of the XMPP CORE spec (RFC 6120). We consider the reply to
290 * be a valid match if any of the following is true:
291 * - Request 'to' matches reply 'from' (including the case where
292 * neither are set).
293 * - Request 'to' was my JID (bare or full) and reply 'from' is empty.
294 * - Request 'to' was empty and reply 'from' is my JID. The spec says
295 * we should only allow bare JID, but we also allow full JID for
296 * compatibility with some servers.
297 * - Request 'to' was empty and reply 'from' is server JID. Not allowed by
298 * any spec, but for compatibility with some servers.
300 * These rules should allow valid IQ replies while preventing spoofed
301 * ones.
303 * For more discussion see the "Spoofing of iq ids and misbehaving
304 * servers" email thread from January 2014 on the jdev and security
305 * mailing lists. Also see https://developer.pidgin.im/ticket/15879
307 * @return TRUE if this reply is valid for the given request.
309 static gboolean does_reply_from_match_request_to(JabberStream *js, JabberID *to, JabberID *from)
311 if (jabber_id_equal(to, from)) {
312 /* Request 'to' matches reply 'from' */
313 return TRUE;
316 if (!from && purple_strequal(to->node, js->user->node)
317 && purple_strequal(to->domain, js->user->domain)) {
318 /* Request 'to' was my JID (bare or full) and reply 'from' is empty */
319 return TRUE;
322 if (!to && purple_strequal(from->domain, js->user->domain)) {
323 /* Request 'to' is empty and reply 'from' domain matches our domain */
325 if (!from->node && !from->resource) {
326 /* Reply 'from' is server bare JID */
327 return TRUE;
330 if (purple_strequal(from->node, js->user->node)
331 && (!from->resource || purple_strequal(from->resource, js->user->resource))) {
332 /* Reply 'from' is my full or bare JID */
333 return TRUE;
337 return FALSE;
340 void jabber_iq_parse(JabberStream *js, xmlnode *packet)
342 JabberIqCallbackData *jcd;
343 xmlnode *child, *error, *x;
344 const char *xmlns;
345 const char *iq_type, *id, *from;
346 JabberIqType type = JABBER_IQ_NONE;
347 gboolean signal_return;
348 JabberID *from_id;
350 from = xmlnode_get_attrib(packet, "from");
351 id = xmlnode_get_attrib(packet, "id");
352 iq_type = xmlnode_get_attrib(packet, "type");
355 * Ensure the 'from' attribute is valid. No point in handling a stanza
356 * of which we don't understand where it came from.
358 from_id = jabber_id_new(from);
360 if (from && !from_id) {
361 purple_debug_error("jabber", "Received an iq with an invalid from: %s\n", from);
362 return;
366 * child will be either the first tag child or NULL if there is no child.
367 * Historically, we used just the 'query' subchild, but newer XEPs use
368 * differently named children. Grabbing the first child is (for the time
369 * being) sufficient.
371 for (child = packet->child; child; child = child->next) {
372 if (child->type == XMLNODE_TYPE_TAG)
373 break;
376 if (iq_type) {
377 if (purple_strequal(iq_type, "get"))
378 type = JABBER_IQ_GET;
379 else if (purple_strequal(iq_type, "set"))
380 type = JABBER_IQ_SET;
381 else if (purple_strequal(iq_type, "result"))
382 type = JABBER_IQ_RESULT;
383 else if (purple_strequal(iq_type, "error"))
384 type = JABBER_IQ_ERROR;
387 if (type == JABBER_IQ_NONE) {
388 purple_debug_error("jabber", "IQ with invalid type ('%s') - ignoring.\n",
389 iq_type ? iq_type : "(null)");
390 jabber_id_free(from_id);
391 return;
394 /* All IQs must have an ID, so send an error for a set/get that doesn't */
395 if(!id || !*id) {
397 if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
398 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
400 xmlnode_free(iq->node);
401 iq->node = xmlnode_copy(packet);
402 if (from) {
403 xmlnode_set_attrib(iq->node, "to", from);
404 xmlnode_remove_attrib(iq->node, "from");
407 xmlnode_set_attrib(iq->node, "type", "error");
408 /* This id is clearly not useful, but we must put something there for a valid stanza */
409 iq->id = jabber_get_next_id(js);
410 xmlnode_set_attrib(iq->node, "id", iq->id);
411 error = xmlnode_new_child(iq->node, "error");
412 xmlnode_set_attrib(error, "type", "modify");
413 x = xmlnode_new_child(error, "bad-request");
414 xmlnode_set_namespace(x, NS_XMPP_STANZAS);
416 jabber_iq_send(iq);
417 } else
418 purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n",
419 iq_type);
421 jabber_id_free(from_id);
422 return;
425 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc),
426 "jabber-receiving-iq", js->gc, iq_type, id, from, packet));
427 if (signal_return) {
428 jabber_id_free(from_id);
429 return;
432 /* First, lets see if a special callback got registered */
433 if(type == JABBER_IQ_RESULT || type == JABBER_IQ_ERROR) {
434 jcd = g_hash_table_lookup(js->iq_callbacks, id);
435 if (jcd) {
436 if (does_reply_from_match_request_to(js, jcd->to, from_id)) {
437 jcd->callback(js, from, type, id, packet, jcd->data);
438 jabber_iq_remove_callback_by_id(js, id);
439 jabber_id_free(from_id);
440 return;
441 } else {
442 char *expected_to;
444 if (jcd->to) {
445 expected_to = jabber_id_get_full_jid(jcd->to);
446 } else {
447 expected_to = jabber_id_get_bare_jid(js->user);
450 purple_debug_error("jabber", "Got a result iq with id %s from %s instead of expected %s!\n", id, from ? from : "(null)", expected_to);
452 g_free(expected_to);
458 * Apparently not, so let's see if we have a pre-defined handler
459 * or if an outside plugin is interested.
461 if(child && (xmlns = xmlnode_get_namespace(child))) {
462 char *key = g_strdup_printf("%s %s", child->name, xmlns);
463 JabberIqHandler *jih = g_hash_table_lookup(iq_handlers, key);
464 int signal_ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
465 g_free(key);
467 if (signal_ref > 0) {
468 signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), "jabber-watched-iq",
469 js->gc, iq_type, id, from, child));
470 if (signal_return) {
471 jabber_id_free(from_id);
472 return;
476 if(jih) {
477 jih(js, from, type, id, child);
478 jabber_id_free(from_id);
479 return;
483 purple_debug_misc("jabber", "Unhandled IQ with id %s\n", id);
485 /* If we get here, send the default error reply mandated by XMPP-CORE */
486 if(type == JABBER_IQ_SET || type == JABBER_IQ_GET) {
487 JabberIq *iq = jabber_iq_new(js, JABBER_IQ_ERROR);
489 xmlnode_free(iq->node);
490 iq->node = xmlnode_copy(packet);
491 if (from) {
492 xmlnode_set_attrib(iq->node, "to", from);
493 xmlnode_remove_attrib(iq->node, "from");
496 xmlnode_set_attrib(iq->node, "type", "error");
497 error = xmlnode_new_child(iq->node, "error");
498 xmlnode_set_attrib(error, "type", "cancel");
499 xmlnode_set_attrib(error, "code", "501");
500 x = xmlnode_new_child(error, "feature-not-implemented");
501 xmlnode_set_namespace(x, NS_XMPP_STANZAS);
503 jabber_iq_send(iq);
506 jabber_id_free(from_id);
509 void jabber_iq_register_handler(const char *node, const char *xmlns, JabberIqHandler *handlerfunc)
512 * This is valid because nodes nor namespaces cannot have spaces in them
513 * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and
514 * http://www.w3.org/TR/REC-xml-names/)
516 char *key = g_strdup_printf("%s %s", node, xmlns);
517 g_hash_table_replace(iq_handlers, key, handlerfunc);
520 void jabber_iq_signal_register(const gchar *node, const gchar *xmlns)
522 gchar *key;
523 int ref;
525 g_return_if_fail(node != NULL && *node != '\0');
526 g_return_if_fail(xmlns != NULL && *xmlns != '\0');
528 key = g_strdup_printf("%s %s", node, xmlns);
529 ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
530 if (ref == 0) {
531 g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(1));
532 } else {
533 g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref + 1));
534 g_free(key);
538 void jabber_iq_signal_unregister(const gchar *node, const gchar *xmlns)
540 gchar *key;
541 int ref;
543 g_return_if_fail(node != NULL && *node != '\0');
544 g_return_if_fail(xmlns != NULL && *xmlns != '\0');
546 key = g_strdup_printf("%s %s", node, xmlns);
547 ref = GPOINTER_TO_INT(g_hash_table_lookup(signal_iq_handlers, key));
549 if (ref == 1) {
550 g_hash_table_remove(signal_iq_handlers, key);
551 } else if (ref > 1) {
552 g_hash_table_insert(signal_iq_handlers, key, GINT_TO_POINTER(ref - 1));
555 g_free(key);
558 void jabber_iq_init(void)
560 iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
561 signal_iq_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
563 jabber_iq_register_handler("jingle", JINGLE, jingle_parse);
564 jabber_iq_register_handler("mailbox", NS_GOOGLE_MAIL_NOTIFY,
565 jabber_gmail_poke);
566 jabber_iq_register_handler("new-mail", NS_GOOGLE_MAIL_NOTIFY,
567 jabber_gmail_poke);
568 jabber_iq_register_handler("ping", NS_PING, jabber_ping_parse);
569 jabber_iq_register_handler("query", NS_GOOGLE_JINGLE_INFO,
570 jabber_google_handle_jingle_info);
571 jabber_iq_register_handler("query", NS_BYTESTREAMS,
572 jabber_bytestreams_parse);
573 jabber_iq_register_handler("query", NS_DISCO_INFO, jabber_disco_info_parse);
574 jabber_iq_register_handler("query", NS_DISCO_ITEMS, jabber_disco_items_parse);
575 jabber_iq_register_handler("query", NS_LAST_ACTIVITY, jabber_iq_last_parse);
576 jabber_iq_register_handler("query", NS_OOB_IQ_DATA, jabber_oob_parse);
577 jabber_iq_register_handler("query", "jabber:iq:register",
578 jabber_register_parse);
579 jabber_iq_register_handler("query", "jabber:iq:roster",
580 jabber_roster_parse);
581 jabber_iq_register_handler("query", "jabber:iq:version",
582 jabber_iq_version_parse);
583 #ifdef USE_VV
584 jabber_iq_register_handler("session", NS_GOOGLE_SESSION,
585 jabber_google_session_parse);
586 #endif
587 jabber_iq_register_handler("block", NS_SIMPLE_BLOCKING, jabber_blocklist_parse_push);
588 jabber_iq_register_handler("unblock", NS_SIMPLE_BLOCKING, jabber_blocklist_parse_push);
589 jabber_iq_register_handler("time", NS_ENTITY_TIME, jabber_time_parse);
593 void jabber_iq_uninit(void)
595 g_hash_table_destroy(iq_handlers);
596 g_hash_table_destroy(signal_iq_handlers);
597 iq_handlers = signal_iq_handlers = NULL;