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
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
31 #include "google/gmail.h"
32 #include "google/google.h"
33 #include "google/jingleinfo.h"
34 #include "google/google_session.h"
36 #include "jingle/jingle.h"
41 #include "adhoccommands.h"
46 #include "win32/utsname.h"
49 static GHashTable
*iq_handlers
= NULL
;
50 static GHashTable
*signal_iq_handlers
= NULL
;
52 struct _JabberIqCallbackData
{
53 JabberIqCallback
*callback
;
58 void jabber_iq_callbackdata_free(JabberIqCallbackData
*jcd
)
60 jabber_id_free(jcd
->to
);
64 JabberIq
*jabber_iq_new(JabberStream
*js
, JabberIqType type
)
68 iq
= g_new0(JabberIq
, 1);
72 iq
->node
= purple_xmlnode_new("iq");
75 purple_xmlnode_set_attrib(iq
->node
, "type", "set");
78 purple_xmlnode_set_attrib(iq
->node
, "type", "get");
81 purple_xmlnode_set_attrib(iq
->node
, "type", "error");
83 case JABBER_IQ_RESULT
:
84 purple_xmlnode_set_attrib(iq
->node
, "type", "result");
87 /* this shouldn't ever happen */
93 if(type
== JABBER_IQ_GET
|| type
== JABBER_IQ_SET
) {
94 iq
->id
= jabber_get_next_id(js
);
95 purple_xmlnode_set_attrib(iq
->node
, "id", iq
->id
);
101 JabberIq
*jabber_iq_new_query(JabberStream
*js
, JabberIqType type
,
104 JabberIq
*iq
= jabber_iq_new(js
, type
);
105 PurpleXmlNode
*query
;
107 query
= purple_xmlnode_new_child(iq
->node
, "query");
108 purple_xmlnode_set_namespace(query
, xmlns
);
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
)
125 purple_xmlnode_set_attrib(iq
->node
, "id", id
);
126 iq
->id
= g_strdup(id
);
128 purple_xmlnode_remove_attrib(iq
->node
, "id");
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(purple_xmlnode_get_attrib(iq
->node
, "to"));
146 g_hash_table_insert(iq
->js
->iq_callbacks
, g_strdup(iq
->id
), jcd
);
152 void jabber_iq_free(JabberIq
*iq
)
154 g_return_if_fail(iq
!= NULL
);
157 purple_xmlnode_free(iq
->node
);
161 static void jabber_iq_last_parse(JabberStream
*js
, const char *from
,
162 JabberIqType type
, const char *id
,
163 PurpleXmlNode
*packet
)
166 PurpleXmlNode
*query
;
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
);
173 purple_xmlnode_set_attrib(iq
->node
, "to", from
);
175 query
= purple_xmlnode_get_child(iq
->node
, "query");
177 idle_time
= g_strdup_printf("%ld", js
->idle
? time(NULL
) - js
->idle
: 0);
178 purple_xmlnode_set_attrib(query
, "seconds", idle_time
);
185 static void jabber_time_parse(JabberStream
*js
, const char *from
,
186 JabberIqType type
, const char *id
,
187 PurpleXmlNode
*child
)
195 if(type
== JABBER_IQ_GET
) {
196 PurpleXmlNode
*tzo
, *utc
;
197 const char *date
, *tz
;
199 iq
= jabber_iq_new(js
, JABBER_IQ_RESULT
);
200 jabber_iq_set_id(iq
, id
);
202 purple_xmlnode_set_attrib(iq
->node
, "to", from
);
204 child
= purple_xmlnode_new_child(iq
->node
, child
->name
);
205 purple_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
= purple_xmlnode_new_child(child
, "tzo");
211 purple_xmlnode_insert_data(tzo
, tz
, -1);
213 /* <utc>2006-12-19T17:58:35Z</utc> */
215 date
= purple_utf8_strftime("%Y-%m-%dT%H:%M:%SZ", tm
);
216 utc
= purple_xmlnode_new_child(child
, "utc");
217 purple_xmlnode_insert_data(utc
, date
, -1);
225 static void jabber_iq_version_parse(JabberStream
*js
, const char *from
,
226 JabberIqType type
, const char *id
,
227 PurpleXmlNode
*packet
)
230 PurpleXmlNode
*query
;
232 if(type
== JABBER_IQ_GET
) {
234 const char *ui_name
= NULL
, *ui_version
= NULL
;
237 if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) {
238 struct utsname osinfo
;
241 os
= g_strdup_printf("%s %s %s", osinfo
.sysname
, osinfo
.release
,
246 iq
= jabber_iq_new_query(js
, JABBER_IQ_RESULT
, "jabber:iq:version");
248 purple_xmlnode_set_attrib(iq
->node
, "to", from
);
249 jabber_iq_set_id(iq
, id
);
251 query
= purple_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 purple_xmlnode_insert_data(purple_xmlnode_new_child(query
, "name"), ui_name
, -1);
263 purple_xmlnode_insert_data(purple_xmlnode_new_child(query
, "version"), version_complete
, -1);
264 g_free(version_complete
);
266 purple_xmlnode_insert_data(purple_xmlnode_new_child(query
, "name"), "libpurple", -1);
267 purple_xmlnode_insert_data(purple_xmlnode_new_child(query
, "version"), VERSION
, -1);
272 purple_xmlnode_insert_data(purple_xmlnode_new_child(query
, "os"), os
, -1);
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
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
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' */
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 */
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 */
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 */
340 void jabber_iq_parse(JabberStream
*js
, PurpleXmlNode
*packet
)
342 JabberIqCallbackData
*jcd
;
343 PurpleXmlNode
*child
, *error
, *x
;
345 const char *iq_type
, *id
, *from
;
346 JabberIqType type
= JABBER_IQ_NONE
;
347 gboolean signal_return
;
350 from
= purple_xmlnode_get_attrib(packet
, "from");
351 id
= purple_xmlnode_get_attrib(packet
, "id");
352 iq_type
= purple_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
);
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
371 for (child
= packet
->child
; child
; child
= child
->next
) {
372 if (child
->type
== PURPLE_XMLNODE_TYPE_TAG
)
377 if (!strcmp(iq_type
, "get"))
378 type
= JABBER_IQ_GET
;
379 else if (!strcmp(iq_type
, "set"))
380 type
= JABBER_IQ_SET
;
381 else if (!strcmp(iq_type
, "result"))
382 type
= JABBER_IQ_RESULT
;
383 else if (!strcmp(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
);
394 /* All IQs must have an ID, so send an error for a set/get that doesn't */
397 if(type
== JABBER_IQ_SET
|| type
== JABBER_IQ_GET
) {
398 JabberIq
*iq
= jabber_iq_new(js
, JABBER_IQ_ERROR
);
400 purple_xmlnode_free(iq
->node
);
401 iq
->node
= purple_xmlnode_copy(packet
);
403 purple_xmlnode_set_attrib(iq
->node
, "to", from
);
404 purple_xmlnode_remove_attrib(iq
->node
, "from");
407 purple_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 purple_xmlnode_set_attrib(iq
->node
, "id", iq
->id
);
411 error
= purple_xmlnode_new_child(iq
->node
, "error");
412 purple_xmlnode_set_attrib(error
, "type", "modify");
413 x
= purple_xmlnode_new_child(error
, "bad-request");
414 purple_xmlnode_set_namespace(x
, NS_XMPP_STANZAS
);
418 purple_debug_error("jabber", "IQ of type '%s' missing id - ignoring.\n",
421 jabber_id_free(from_id
);
425 signal_return
= GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js
->gc
),
426 "jabber-receiving-iq", js
->gc
, iq_type
, id
, from
, packet
));
428 jabber_id_free(from_id
);
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
);
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
);
445 expected_to
= jabber_id_get_full_jid(jcd
->to
);
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
);
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
= purple_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
));
467 if (signal_ref
> 0) {
468 signal_return
= GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_protocol(js
->gc
), "jabber-watched-iq",
469 js
->gc
, iq_type
, id
, from
, child
));
471 jabber_id_free(from_id
);
477 jih(js
, from
, type
, id
, child
);
478 jabber_id_free(from_id
);
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 purple_xmlnode_free(iq
->node
);
490 iq
->node
= purple_xmlnode_copy(packet
);
492 purple_xmlnode_set_attrib(iq
->node
, "to", from
);
493 purple_xmlnode_remove_attrib(iq
->node
, "from");
496 purple_xmlnode_set_attrib(iq
->node
, "type", "error");
497 error
= purple_xmlnode_new_child(iq
->node
, "error");
498 purple_xmlnode_set_attrib(error
, "type", "cancel");
499 purple_xmlnode_set_attrib(error
, "code", "501");
500 x
= purple_xmlnode_new_child(error
, "feature-not-implemented");
501 purple_xmlnode_set_namespace(x
, NS_XMPP_STANZAS
);
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
)
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
));
531 g_hash_table_insert(signal_iq_handlers
, key
, GINT_TO_POINTER(1));
533 g_hash_table_insert(signal_iq_handlers
, key
, GINT_TO_POINTER(ref
+ 1));
538 void jabber_iq_signal_unregister(const gchar
*node
, const gchar
*xmlns
)
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
));
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));
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
,
566 jabber_iq_register_handler("new-mail", NS_GOOGLE_MAIL_NOTIFY
,
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
);
584 jabber_iq_register_handler("session", NS_GOOGLE_SESSION
,
585 jabber_google_session_parse
);
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
;