2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2012, Digium, Inc.
6 * Joshua Colp <jcolp@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief XMPP client and component module.
23 * \author Joshua Colp <jcolp@digium.com>
25 * \extref Iksemel http://code.google.com/p/iksemel/
27 * A reference module for interfacting Asterisk directly as a client or component with
28 * an XMPP/Jabber compliant server.
30 * This module is based upon the original res_jabber as done by Matt O'Gorman.
35 <depend>iksemel</depend>
36 <use type="external">openssl</use>
37 <support_level>core</support_level>
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47 #include "asterisk/xmpp.h"
48 #include "asterisk/module.h"
49 #include "asterisk/manager.h"
50 #include "asterisk/app.h"
51 #include "asterisk/message.h"
52 #include "asterisk/manager.h"
53 #include "asterisk/event.h"
54 #include "asterisk/cli.h"
55 #include "asterisk/config_options.h"
58 <application name="JabberSend" language="en_US">
60 Sends an XMPP message to a buddy.
63 <parameter name="account" required="true">
64 <para>The local named account to listen on (specified in
67 <parameter name="jid" required="true">
68 <para>Jabber ID of the buddy to send the message to. It can be a
69 bare JID (username@domain) or a full JID (username@domain/resource).</para>
71 <parameter name="message" required="true">
72 <para>The message to send.</para>
76 <para>Sends the content of <replaceable>message</replaceable> as text message
77 from the given <replaceable>account</replaceable> to the buddy identified by
78 <replaceable>jid</replaceable></para>
79 <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
80 to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
81 <replaceable>asterisk</replaceable>, configured in xmpp.conf.</para>
84 <ref type="function">JABBER_STATUS</ref>
85 <ref type="function">JABBER_RECEIVE</ref>
88 <function name="JABBER_RECEIVE" language="en_US">
93 <parameter name="account" required="true">
94 <para>The local named account to listen on (specified in
97 <parameter name="jid" required="true">
98 <para>Jabber ID of the buddy to receive message from. It can be a
99 bare JID (username@domain) or a full JID (username@domain/resource).</para>
101 <parameter name="timeout">
102 <para>In seconds, defaults to <literal>20</literal>.</para>
106 <para>Receives a text message on the given <replaceable>account</replaceable>
107 from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
108 <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
109 sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
110 the <replaceable>asterisk</replaceable> XMPP account configured in xmpp.conf.</para>
113 <ref type="function">JABBER_STATUS</ref>
114 <ref type="application">JabberSend</ref>
117 <function name="JABBER_STATUS" language="en_US">
119 Retrieves a buddy's status.
122 <parameter name="account" required="true">
123 <para>The local named account to listen on (specified in
126 <parameter name="jid" required="true">
127 <para>Jabber ID of the buddy to receive message from. It can be a
128 bare JID (username@domain) or a full JID (username@domain/resource).</para>
132 <para>Retrieves the numeric status associated with the buddy identified
133 by <replaceable>jid</replaceable>.
134 If the buddy does not exist in the buddylist, returns 7.</para>
135 <para>Status will be 1-7.</para>
136 <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
137 <para>If not in roster variable will be set to 7.</para>
138 <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
139 <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
140 the associated XMPP account configured in xmpp.conf.</para>
143 <ref type="function">JABBER_RECEIVE</ref>
144 <ref type="application">JabberSend</ref>
147 <application name="JabberSendGroup" language="en_US">
149 Send a Jabber Message to a specified chat room
152 <parameter name="Jabber" required="true">
153 <para>Client or transport Asterisk uses to connect to Jabber.</para>
155 <parameter name="RoomJID" required="true">
156 <para>XMPP/Jabber JID (Name) of chat room.</para>
158 <parameter name="Message" required="true">
159 <para>Message to be sent to the chat room.</para>
161 <parameter name="Nickname" required="false">
162 <para>The nickname Asterisk uses in the chat room.</para>
166 <para>Allows user to send a message to a chat room via XMPP.</para>
167 <note><para>To be able to send messages to a chat room, a user must have previously joined it. Use the <replaceable>JabberJoin</replaceable> function to do so.</para></note>
170 <application name="JabberJoin" language="en_US">
175 <parameter name="Jabber" required="true">
176 <para>Client or transport Asterisk uses to connect to Jabber.</para>
178 <parameter name="RoomJID" required="true">
179 <para>XMPP/Jabber JID (Name) of chat room.</para>
181 <parameter name="Nickname" required="false">
182 <para>The nickname Asterisk will use in the chat room.</para>
183 <note><para>If a different nickname is supplied to an already joined room, the old nick will be changed to the new one.</para></note>
187 <para>Allows Asterisk to join a chat room.</para>
190 <application name="JabberLeave" language="en_US">
195 <parameter name="Jabber" required="true">
196 <para>Client or transport Asterisk uses to connect to Jabber.</para>
198 <parameter name="RoomJID" required="true">
199 <para>XMPP/Jabber JID (Name) of chat room.</para>
201 <parameter name="Nickname" required="false">
202 <para>The nickname Asterisk uses in the chat room.</para>
206 <para>Allows Asterisk to leave a chat room.</para>
209 <application name="JabberStatus" language="en_US">
211 Retrieve the status of a jabber list member
214 <parameter name="Jabber" required="true">
215 <para>Client or transport Asterisk users to connect to Jabber.</para>
217 <parameter name="JID" required="true">
218 <para>XMPP/Jabber JID (Name) of recipient.</para>
220 <parameter name="Variable" required="true">
221 <para>Variable to store the status of requested user.</para>
225 <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
226 <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
227 The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
239 <para>Extended Away.</para>
242 <para>Do Not Disturb.</para>
245 <para>Offline.</para>
248 <para>Not In Roster.</para>
253 <manager name="JabberSend" language="en_US">
255 Sends a message to a Jabber Client.
258 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
259 <parameter name="Jabber" required="true">
260 <para>Client or transport Asterisk uses to connect to JABBER.</para>
262 <parameter name="JID" required="true">
263 <para>XMPP/Jabber JID (Name) of recipient.</para>
265 <parameter name="Message" required="true">
266 <para>Message to be sent to the buddy.</para>
270 <para>Sends a message to a Jabber Client.</para>
275 /*! \brief Supported general configuration flags */
277 XMPP_AUTOPRUNE = (1 << 0),
278 XMPP_AUTOREGISTER = (1 << 1),
279 XMPP_AUTOACCEPT = (1 << 2),
280 XMPP_DEBUG = (1 << 3),
281 XMPP_USETLS = (1 << 4),
282 XMPP_USESASL = (1 << 5),
283 XMPP_FORCESSL = (1 << 6),
284 XMPP_KEEPALIVE = (1 << 7),
285 XMPP_COMPONENT = (1 << 8),
286 XMPP_SEND_TO_DIALPLAN = (1 << 9),
287 XMPP_DISTRIBUTE_EVENTS = (1 << 10),
290 /*! \brief Supported pubsub configuration flags */
292 XMPP_XEP0248 = (1 << 0),
293 XMPP_PUBSUB = (1 << 1),
294 XMPP_PUBSUB_AUTOCREATE = (1 << 2),
297 /*! \brief Number of buckets for client connections */
298 #define CLIENT_BUCKETS 53
300 /*! \brief Number of buckets for buddies (per client) */
301 #define BUDDY_BUCKETS 53
303 /*! \brief Number of buckets for resources (per buddy) */
304 #define RESOURCE_BUCKETS 53
306 /*! \brief Namespace for TLS support */
307 #define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
309 /*! \brief Status for a disappearing buddy */
310 #define STATUS_DISAPPEAR 6
312 /*! \brief Global debug status */
315 /*! \brief XMPP Global Configuration */
316 struct ast_xmpp_global_config {
317 struct ast_flags general; /*!< General configuration options */
318 struct ast_flags pubsub; /*!< Pubsub related configuration options */
321 /*! \brief XMPP Client Configuration */
322 struct ast_xmpp_client_config {
323 AST_DECLARE_STRING_FIELDS(
324 AST_STRING_FIELD(name); /*!< Name of the client connection */
325 AST_STRING_FIELD(user); /*!< Username to use for authentication */
326 AST_STRING_FIELD(password); /*!< Password to use for authentication */
327 AST_STRING_FIELD(server); /*!< Server hostname */
328 AST_STRING_FIELD(statusmsg); /*!< Status message for presence */
329 AST_STRING_FIELD(pubsubnode); /*!< Pubsub node */
330 AST_STRING_FIELD(context); /*!< Context for incoming messages */
332 int port; /*!< Port to use when connecting to server */
333 int message_timeout; /*!< Timeout for messages */
334 int priority; /*!< Resource priority */
335 struct ast_flags flags; /*!< Various options that have been set */
336 enum ikshowtype status; /*!< Presence status */
337 struct ast_xmpp_client *client; /*!< Pointer to the client */
338 struct ao2_container *buddies; /*!< Configured buddies */
342 struct ast_xmpp_global_config *global; /*!< Global configuration options */
343 struct ao2_container *clients; /*!< Configured clients */
346 static AO2_GLOBAL_OBJ_STATIC(globals);
348 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
349 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
350 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
351 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
353 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
354 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
356 /*! \brief Defined handlers for XMPP client states */
357 static const struct xmpp_state_handler {
360 int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
361 } xmpp_state_handlers[] = {
362 { XMPP_STATE_REQUEST_TLS, 0, xmpp_client_request_tls, },
363 { XMPP_STATE_REQUESTED_TLS, 0, xmpp_client_requested_tls, },
364 { XMPP_STATE_AUTHENTICATE, 0, xmpp_client_authenticate, },
365 { XMPP_STATE_AUTHENTICATING, 0, xmpp_client_authenticating, },
366 { XMPP_STATE_AUTHENTICATE, 1, xmpp_component_authenticate, },
367 { XMPP_STATE_AUTHENTICATING, 1, xmpp_component_authenticating, },
370 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
371 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
372 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
374 /*! \brief Defined handlers for different PAK types */
375 static const struct xmpp_pak_handler {
377 int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
378 } xmpp_pak_handlers[] = {
379 { IKS_PAK_MESSAGE, xmpp_pak_message, },
380 { IKS_PAK_PRESENCE, xmpp_pak_presence, },
381 { IKS_PAK_S10N, xmpp_pak_s10n, },
384 static const char *app_ajisend = "JabberSend";
385 static const char *app_ajisendgroup = "JabberSendGroup";
386 static const char *app_ajistatus = "JabberStatus";
387 static const char *app_ajijoin = "JabberJoin";
388 static const char *app_ajileave = "JabberLeave";
390 static struct ast_event_sub *mwi_sub = NULL;
391 static struct ast_event_sub *device_state_sub = NULL;
393 static ast_cond_t message_received_condition;
394 static ast_mutex_t messagelock;
396 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
398 /*! \brief Destructor function for configuration */
399 static void ast_xmpp_client_config_destructor(void *obj)
401 struct ast_xmpp_client_config *cfg = obj;
402 ast_string_field_free_memory(cfg);
403 ao2_cleanup(cfg->client);
404 ao2_cleanup(cfg->buddies);
407 /*! \brief Destroy function for XMPP messages */
408 static void xmpp_message_destroy(struct ast_xmpp_message *message)
411 ast_free(message->from);
413 if (message->message) {
414 ast_free(message->message);
420 /*! \brief Destructor callback function for XMPP client */
421 static void xmpp_client_destructor(void *obj)
423 struct ast_xmpp_client *client = obj;
424 struct ast_xmpp_message *message;
426 ast_xmpp_client_disconnect(client);
429 iks_stack_delete(client->stack);
432 ao2_cleanup(client->buddies);
434 while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
435 xmpp_message_destroy(message);
437 AST_LIST_HEAD_DESTROY(&client->messages);
440 /*! \brief Hashing function for XMPP buddy */
441 static int xmpp_buddy_hash(const void *obj, const int flags)
443 const struct ast_xmpp_buddy *buddy = obj;
444 const char *id = obj;
446 return ast_str_hash(flags & OBJ_KEY ? id : buddy->id);
449 /*! \brief Comparator function for XMPP buddy */
450 static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
452 struct ast_xmpp_buddy *buddy1 = obj, *buddy2 = arg;
453 const char *id = arg;
455 return !strcmp(buddy1->id, flags & OBJ_KEY ? id : buddy2->id) ? CMP_MATCH | CMP_STOP : 0;
458 /*! \brief Allocator function for ast_xmpp_client */
459 static struct ast_xmpp_client *xmpp_client_alloc(const char *name)
461 struct ast_xmpp_client *client;
463 if (!(client = ao2_alloc(sizeof(*client), xmpp_client_destructor))) {
467 AST_LIST_HEAD_INIT(&client->messages);
468 client->thread = AST_PTHREADT_NULL;
470 if (!(client->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
471 ast_log(LOG_ERROR, "Could not initialize buddy container for '%s'\n", name);
476 if (ast_string_field_init(client, 512)) {
477 ast_log(LOG_ERROR, "Could not initialize stringfields for '%s'\n", name);
482 if (!(client->stack = iks_stack_new(8192, 8192))) {
483 ast_log(LOG_ERROR, "Could not create an Iksemel stack for '%s'\n", name);
488 ast_string_field_set(client, name, name);
490 client->timeout = 50;
491 client->state = XMPP_STATE_DISCONNECTED;
492 ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
497 /*! \brief Find function for configuration */
498 static void *xmpp_config_find(struct ao2_container *tmp_container, const char *category)
500 return ao2_find(tmp_container, category, OBJ_KEY);
503 /*! \brief Look up existing client or create a new one */
504 static void *xmpp_client_find_or_create(const char *category)
506 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
507 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
509 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, category))) {
510 return xmpp_client_alloc(category);
513 ao2_ref(clientcfg->client, +1);
514 return clientcfg->client;
517 /*! \brief Allocator function for configuration */
518 static void *ast_xmpp_client_config_alloc(const char *cat)
520 struct ast_xmpp_client_config *cfg;
522 if (!(cfg = ao2_alloc(sizeof(*cfg), ast_xmpp_client_config_destructor))) {
526 if (ast_string_field_init(cfg, 512)) {
531 if (!(cfg->client = xmpp_client_find_or_create(cat))) {
536 if (!(cfg->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
541 ast_string_field_set(cfg, name, cat);
546 /*! \brief Destructor for XMPP configuration */
547 static void xmpp_config_destructor(void *obj)
549 struct xmpp_config *cfg = obj;
550 ao2_cleanup(cfg->global);
551 ao2_cleanup(cfg->clients);
554 /*! \brief Hashing function for configuration */
555 static int xmpp_config_hash(const void *obj, const int flags)
557 const struct ast_xmpp_client_config *cfg = obj;
558 const char *name = (flags & OBJ_KEY) ? obj : cfg->name;
559 return ast_str_case_hash(name);
562 /*! \brief Comparator function for configuration */
563 static int xmpp_config_cmp(void *obj, void *arg, int flags)
565 struct ast_xmpp_client_config *one = obj, *two = arg;
566 const char *match = (flags & OBJ_KEY) ? arg : two->name;
567 return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
570 /*! \brief Allocator for XMPP configuration */
571 static void *xmpp_config_alloc(void)
573 struct xmpp_config *cfg;
575 if (!(cfg = ao2_alloc(sizeof(*cfg), xmpp_config_destructor))) {
579 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
583 ast_set_flag(&cfg->global->general, XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE);
585 if (!(cfg->clients = ao2_container_alloc(1, xmpp_config_hash, xmpp_config_cmp))) {
595 static int xmpp_config_prelink(void *newitem)
597 struct ast_xmpp_client_config *clientcfg = newitem;
598 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
599 RAII_VAR(struct ast_xmpp_client_config *, oldclientcfg, NULL, ao2_cleanup);
601 if (ast_strlen_zero(clientcfg->user)) {
602 ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
604 } else if (ast_strlen_zero(clientcfg->password)) {
605 ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
607 } else if (ast_strlen_zero(clientcfg->server)) {
608 ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
612 /* If this is a new connection force a reconnect */
613 if (!cfg || !cfg->clients || !(oldclientcfg = xmpp_config_find(cfg->clients, clientcfg->name))) {
614 clientcfg->client->reconnect = 1;
618 /* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
619 if (strcmp(clientcfg->user, oldclientcfg->user) ||
620 strcmp(clientcfg->password, oldclientcfg->password) ||
621 strcmp(clientcfg->server, oldclientcfg->server) ||
622 (clientcfg->port != oldclientcfg->port) ||
623 (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
624 (clientcfg->priority != oldclientcfg->priority)) {
625 clientcfg->client->reconnect = 1;
627 clientcfg->client->reconnect = 0;
633 static void xmpp_config_post_apply(void)
635 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
637 ao2_callback(cfg->clients, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_config_post_apply, NULL);
640 static struct aco_type global_option = {
642 .item_offset = offsetof(struct xmpp_config, global),
643 .category_match = ACO_WHITELIST,
644 .category = "^general$",
647 struct aco_type *global_options[] = ACO_TYPES(&global_option);
649 static struct aco_type client_option = {
651 .category_match = ACO_BLACKLIST,
652 .category = "^(general)$",
653 .item_alloc = ast_xmpp_client_config_alloc,
654 .item_find = xmpp_config_find,
655 .item_prelink = xmpp_config_prelink,
656 .item_offset = offsetof(struct xmpp_config, clients),
659 struct aco_type *client_options[] = ACO_TYPES(&client_option);
661 struct aco_file res_xmpp_conf = {
662 .filename = "xmpp.conf",
663 .alias = "jabber.conf",
664 .types = ACO_TYPES(&global_option, &client_option),
667 CONFIG_INFO_STANDARD(cfg_info, globals, xmpp_config_alloc,
668 .files = ACO_FILES(&res_xmpp_conf),
669 .post_apply_config = xmpp_config_post_apply,
672 /*! \brief Destructor callback function for XMPP resource */
673 static void xmpp_resource_destructor(void *obj)
675 struct ast_xmpp_resource *resource = obj;
677 if (resource->description) {
678 ast_free(resource->description);
682 /*! \brief Hashing function for XMPP resource */
683 static int xmpp_resource_hash(const void *obj, const int flags)
685 const struct ast_xmpp_resource *resource = obj;
687 return flags & OBJ_KEY ? -1 : resource->priority;
690 /*! \brief Comparator function for XMPP resource */
691 static int xmpp_resource_cmp(void *obj, void *arg, int flags)
693 struct ast_xmpp_resource *resource1 = obj, *resource2 = arg;
694 const char *resource = arg;
696 return !strcmp(resource1->resource, flags & OBJ_KEY ? resource : resource2->resource) ? CMP_MATCH | CMP_STOP : 0;
699 /*! \brief Destructor callback function for XMPP buddy */
700 static void xmpp_buddy_destructor(void *obj)
702 struct ast_xmpp_buddy *buddy = obj;
704 if (buddy->resources) {
705 ao2_ref(buddy->resources, -1);
709 /*! \brief Helper function which returns whether an XMPP client connection is secure or not */
710 static int xmpp_is_secure(struct ast_xmpp_client *client)
713 return client->stream_flags & SECURE;
719 struct ast_xmpp_client *ast_xmpp_client_find(const char *name)
721 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
722 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
724 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
728 ao2_ref(clientcfg->client, +1);
729 return clientcfg->client;
732 void ast_xmpp_client_unref(struct ast_xmpp_client *client)
737 void ast_xmpp_client_lock(struct ast_xmpp_client *client)
742 void ast_xmpp_client_unlock(struct ast_xmpp_client *client)
747 /*! \brief Internal function used to send a message to a user or chatroom */
748 static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
750 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
751 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
753 char from[XMPP_MAX_JIDLEN];
756 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
757 !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
761 if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
762 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
764 snprintf(from, sizeof(from), "%s", client->jid->full);
767 iks_insert_attrib(message_packet, "from", from);
769 res = ast_xmpp_client_send(client, message_packet);
771 iks_delete(message_packet);
776 int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
778 return xmpp_client_send_message(client, 0, NULL, user, message);
781 int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
784 iks *invite, *body = NULL, *namespace = NULL;
786 if (!(invite = iks_new("message")) || !(body = iks_new("body")) || !(namespace = iks_new("x"))) {
791 iks_insert_attrib(invite, "to", user);
792 ast_xmpp_client_lock(client);
793 iks_insert_attrib(invite, "id", client->mid);
794 ast_xmpp_increment_mid(client->mid);
795 ast_xmpp_client_unlock(client);
796 iks_insert_cdata(body, message, 0);
797 iks_insert_node(invite, body);
798 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
799 iks_insert_attrib(namespace, "jid", room);
800 iks_insert_node(invite, namespace);
802 res = ast_xmpp_client_send(client, invite);
805 iks_delete(namespace);
812 static int xmpp_client_set_group_presence(struct ast_xmpp_client *client, const char *room, int level, const char *nick)
814 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
815 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
817 iks *presence = NULL, *x = NULL;
818 char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
820 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
821 !(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
826 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
827 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
828 snprintf(roomid, sizeof(roomid), "%s/%s", room, nick);
830 snprintf(from, sizeof(from), "%s", client->jid->full);
831 snprintf(roomid, sizeof(roomid), "%s/%s", room, S_OR(nick, client->jid->user));
834 iks_insert_attrib(presence, "to", roomid);
835 iks_insert_attrib(presence, "from", from);
836 iks_insert_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
837 iks_insert_node(presence, x);
839 res = ast_xmpp_client_send(client, presence);
843 iks_delete(presence);
848 int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
850 return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
853 int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
855 return xmpp_client_send_message(client, 1, nickname, address, message);
858 int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
860 return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
863 void ast_xmpp_increment_mid(char *mid)
867 for (i = strlen(mid) - 1; i >= 0; i--) {
878 * \brief Create an IQ packet
879 * \param client the configured XMPP client we use to connect to a XMPP server
880 * \param type the type of IQ packet to create
883 static iks* xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
885 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
886 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
889 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
890 !(request = iks_new("iq"))) {
895 iks_insert_attrib(request, "to", clientcfg->pubsubnode);
896 iks_insert_attrib(request, "from", client->jid->full);
897 iks_insert_attrib(request, "type", type);
898 ast_xmpp_client_lock(client);
899 ast_xmpp_increment_mid(client->mid);
900 iks_insert_attrib(request, "id", client->mid);
901 ast_xmpp_client_unlock(client);
907 * \brief Build the skeleton of a publish
908 * \param client the configured XMPP client we use to connect to a XMPP server
909 * \param node Name of the node that will be published to
913 static iks* xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node,
914 const char *event_type)
916 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
917 iks *request, *pubsub, *publish, *item;
919 if (!cfg || !cfg->global || !(request = xmpp_pubsub_iq_create(client, "set"))) {
923 pubsub = iks_insert(request, "pubsub");
924 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
925 publish = iks_insert(pubsub, "publish");
926 iks_insert_attrib(publish, "node", ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248) ? node : event_type);
927 item = iks_insert(publish, "item");
928 iks_insert_attrib(item, "id", node);
934 static iks* xmpp_pubsub_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
936 iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
937 *field_deliver_payload, *field_persist_items, *field_access_model,
938 *field_pubsub_collection;
939 configure = iks_insert(pubsub, "configure");
940 x = iks_insert(configure, "x");
941 iks_insert_attrib(x, "xmlns", "jabber:x:data");
942 iks_insert_attrib(x, "type", "submit");
943 field_owner = iks_insert(x, "field");
944 iks_insert_attrib(field_owner, "var", "FORM_TYPE");
945 iks_insert_attrib(field_owner, "type", "hidden");
946 iks_insert_cdata(iks_insert(field_owner, "value"),
947 "http://jabber.org/protocol/pubsub#owner", 39);
949 field_node_type = iks_insert(x, "field");
950 iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
951 iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
953 field_node_config = iks_insert(x, "field");
954 iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
955 iks_insert_attrib(field_node_config, "type", "hidden");
956 iks_insert_cdata(iks_insert(field_node_config, "value"),
957 "http://jabber.org/protocol/pubsub#node_config", 45);
958 field_deliver_payload = iks_insert(x, "field");
959 iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
960 iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
961 field_persist_items = iks_insert(x, "field");
962 iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
963 iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
964 field_access_model = iks_insert(x, "field");
965 iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
966 iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
967 if (node_type && !strcasecmp(node_type, "leaf")) {
968 field_pubsub_collection = iks_insert(x, "field");
969 iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
970 iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
971 strlen(collection_name));
977 * \brief Add Owner affiliations for pubsub node
978 * \param client the configured XMPP client we use to connect to a XMPP server
979 * \param node the name of the node to which to add affiliations
982 static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
984 iks *modify_affiliates = xmpp_pubsub_iq_create(client, "set");
985 iks *pubsub, *affiliations, *affiliate;
986 struct ao2_iterator i;
987 struct ast_xmpp_buddy *buddy;
989 if (!modify_affiliates) {
990 ast_log(LOG_ERROR, "Could not create IQ for creating affiliations on client '%s'\n", client->name);
994 pubsub = iks_insert(modify_affiliates, "pubsub");
995 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
996 affiliations = iks_insert(pubsub, "affiliations");
997 iks_insert_attrib(affiliations, "node", node);
999 i = ao2_iterator_init(client->buddies, 0);
1000 while ((buddy = ao2_iterator_next(&i))) {
1001 affiliate = iks_insert(affiliations, "affiliation");
1002 iks_insert_attrib(affiliate, "jid", buddy->id);
1003 iks_insert_attrib(affiliate, "affiliation", "owner");
1006 ao2_iterator_destroy(&i);
1008 ast_xmpp_client_send(client, modify_affiliates);
1009 iks_delete(modify_affiliates);
1013 * \brief Create a pubsub node
1014 * \param client the configured XMPP client we use to connect to a XMPP server
1015 * \param node_type the type of node to create
1016 * \param name the name of the node to create
1019 static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const
1020 char *name, const char *collection_name)
1022 iks *node, *pubsub, *create;
1024 if (!(node = xmpp_pubsub_iq_create(client, "set"))) {
1028 pubsub = iks_insert(node, "pubsub");
1029 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1030 create = iks_insert(pubsub, "create");
1031 iks_insert_attrib(create, "node", name);
1032 xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
1033 ast_xmpp_client_send(client, node);
1034 xmpp_pubsub_create_affiliations(client, name);
1039 * \brief Delete a PubSub node
1040 * \param client the configured XMPP client we use to connect to a XMPP server
1041 * \param node_name the name of the node to delete
1044 static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
1046 iks *request, *pubsub, *delete;
1048 if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
1052 pubsub = iks_insert(request, "pubsub");
1053 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
1054 delete = iks_insert(pubsub, "delete");
1055 iks_insert_attrib(delete, "node", node_name);
1056 ast_xmpp_client_send(client, request);
1060 iks_delete(request);
1064 * \brief Create a PubSub collection node.
1065 * \param client the configured XMPP client we use to connect to a XMPP server
1066 * \param collection_name The name to use for this collection
1069 static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
1071 xmpp_pubsub_create_node(client, "collection", collection_name, NULL);
1076 * \brief Create a PubSub leaf node.
1077 * \param client the configured XMPP client we use to connect to a XMPP server
1078 * \param leaf_name The name to use for this collection
1081 static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name,
1082 const char *leaf_name)
1084 xmpp_pubsub_create_node(client, "leaf", leaf_name, collection_name);
1088 * \brief Publish MWI to a PubSub node
1089 * \param client the configured XMPP client we use to connect to a XMPP server
1090 * \param device the name of the device whose state to publish
1091 * \param device_state the state to publish
1094 static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox,
1095 const char *context, const char *oldmsgs, const char *newmsgs)
1097 char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT], eid_str[20];
1098 iks *mailbox_node, *request;
1100 snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
1102 if (!(request = xmpp_pubsub_build_publish_skeleton(client, full_mailbox, "message_waiting"))) {
1106 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1107 mailbox_node = iks_insert(request, "mailbox");
1108 iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
1109 iks_insert_attrib(mailbox_node, "eid", eid_str);
1110 iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
1111 iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
1113 ast_xmpp_client_send(client, iks_root(request));
1115 iks_delete(request);
1119 * \brief Publish device state to a PubSub node
1120 * \param client the configured XMPP client we use to connect to a XMPP server
1121 * \param device the name of the device whose state to publish
1122 * \param device_state the state to publish
1125 static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device,
1126 const char *device_state)
1128 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1129 iks *request, *state;
1132 if (!cfg || !cfg->global || !(request = xmpp_pubsub_build_publish_skeleton(client, device, "device_state"))) {
1136 if (ast_test_flag(&cfg->global->pubsub, XMPP_PUBSUB_AUTOCREATE)) {
1137 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1138 xmpp_pubsub_create_node(client, "leaf", device, "device_state");
1140 xmpp_pubsub_create_node(client, NULL, device, NULL);
1144 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1145 state = iks_insert(request, "state");
1146 iks_insert_attrib(state, "xmlns", "http://asterisk.org");
1147 iks_insert_attrib(state, "eid", eid_str);
1148 iks_insert_cdata(state, device_state, strlen(device_state));
1149 ast_xmpp_client_send(client, iks_root(request));
1150 iks_delete(request);
1154 * \brief Callback function for MWI events
1156 * \param data void pointer to ast_client structure
1159 static void xmpp_pubsub_mwi_cb(const struct ast_event *ast_event, void *data)
1161 struct ast_xmpp_client *client = data;
1162 const char *mailbox, *context;
1163 char oldmsgs[10], newmsgs[10];
1165 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
1166 /* If the event didn't originate from this server, don't send it back out. */
1167 ast_debug(1, "Returning here\n");
1171 mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
1172 context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
1173 snprintf(oldmsgs, sizeof(oldmsgs), "%d",
1174 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
1175 snprintf(newmsgs, sizeof(newmsgs), "%d",
1176 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
1177 xmpp_pubsub_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
1181 * \brief Callback function for device state events
1183 * \param data void pointer to ast_client structure
1186 static void xmpp_pubsub_devstate_cb(const struct ast_event *ast_event, void *data)
1188 struct ast_xmpp_client *client = data;
1189 const char *device, *device_state;
1191 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
1192 /* If the event didn't originate from this server, don't send it back out. */
1193 ast_debug(1, "Returning here\n");
1197 device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
1198 device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
1199 xmpp_pubsub_publish_device_state(client, device, device_state);
1203 * \brief Subscribe to a PubSub node
1204 * \param client the configured XMPP client we use to connect to a XMPP server
1205 * \param node the name of the node to which to subscribe
1208 static void xmpp_pubsub_subscribe(struct ast_xmpp_client *client, const char *node)
1210 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1211 iks *request = xmpp_pubsub_iq_create(client, "set");
1212 iks *pubsub, *subscribe;
1214 if (!cfg || !cfg->global || !request) {
1215 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub subscription on client '%s'\n", client->name);
1219 pubsub = iks_insert(request, "pubsub");
1220 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1221 subscribe = iks_insert(pubsub, "subscribe");
1222 iks_insert_attrib(subscribe, "jid", client->jid->partial);
1223 iks_insert_attrib(subscribe, "node", node);
1224 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1225 iks *options, *x, *sub_options, *sub_type, *sub_depth;
1226 options = iks_insert(pubsub, "options");
1227 x = iks_insert(options, "x");
1228 iks_insert_attrib(x, "xmlns", "jabber:x:data");
1229 iks_insert_attrib(x, "type", "submit");
1230 sub_options = iks_insert(x, "field");
1231 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
1232 iks_insert_attrib(sub_options, "type", "hidden");
1233 iks_insert_cdata(iks_insert(sub_options, "value"),
1234 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
1235 sub_type = iks_insert(x, "field");
1236 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
1237 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
1238 sub_depth = iks_insert(x, "field");
1239 iks_insert_attrib(sub_type, "var", "pubsub#subscription_depth");
1240 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
1242 ast_xmpp_client_send(client, request);
1243 iks_delete(request);
1247 * \brief Callback for handling PubSub events
1248 * \param data void pointer to ast_xmpp_client structure
1249 * \return IKS_FILTER_EAT
1251 static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
1253 char *item_id, *device_state, *context;
1254 int oldmsgs, newmsgs;
1255 iks *item, *item_content;
1256 struct ast_eid pubsub_eid;
1257 struct ast_event *event;
1258 item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
1260 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
1261 return IKS_FILTER_EAT;
1263 item_id = iks_find_attrib(item, "id");
1264 item_content = iks_child(item);
1265 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
1266 if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
1267 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
1268 return IKS_FILTER_EAT;
1270 if (!strcasecmp(iks_name(item_content), "state")) {
1271 device_state = iks_find_cdata(item, "state");
1272 if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
1273 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
1274 AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
1275 AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
1276 AST_EVENT_IE_END))) {
1277 return IKS_FILTER_EAT;
1279 } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
1280 context = strsep(&item_id, "@");
1281 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
1282 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
1283 if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
1284 AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
1285 AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
1286 AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
1287 AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
1288 &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
1289 return IKS_FILTER_EAT;
1292 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
1293 iks_name(item_content));
1294 return IKS_FILTER_EAT;
1296 ast_event_queue_and_cache(event);
1297 return IKS_FILTER_EAT;
1300 static int xmpp_pubsub_handle_error(void *data, ikspak *pak)
1302 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1303 char *node_name, *error;
1305 iks *orig_request, *orig_pubsub = iks_find(pak->x, "pubsub");
1306 struct ast_xmpp_client *client = data;
1308 if (!cfg || !cfg->global) {
1309 ast_log(LOG_ERROR, "No global configuration available\n");
1310 return IKS_FILTER_EAT;
1314 ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
1315 return IKS_FILTER_EAT;
1318 orig_request = iks_child(orig_pubsub);
1319 error = iks_find_attrib(iks_find(pak->x, "error"), "code");
1320 node_name = iks_find_attrib(orig_request, "node");
1322 if (!sscanf(error, "%30d", &error_num)) {
1323 return IKS_FILTER_EAT;
1326 if (error_num > 399 && error_num < 500 && error_num != 404) {
1328 "Error performing operation on PubSub node %s, %s.\n", node_name, error);
1329 return IKS_FILTER_EAT;
1330 } else if (error_num > 499 && error_num < 600) {
1331 ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
1332 return IKS_FILTER_EAT;
1335 if (!strcasecmp(iks_name(orig_request), "publish")) {
1338 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1339 if (iks_find(iks_find(orig_request, "item"), "state")) {
1340 xmpp_pubsub_create_leaf(client, "device_state", node_name);
1341 } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
1342 xmpp_pubsub_create_leaf(client, "message_waiting", node_name);
1345 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1348 if ((request = xmpp_pubsub_iq_create(client, "set"))) {
1349 iks_insert_node(request, orig_pubsub);
1350 ast_xmpp_client_send(client, request);
1351 iks_delete(request);
1353 ast_log(LOG_ERROR, "PubSub publish could not create IQ\n");
1356 return IKS_FILTER_EAT;
1357 } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
1358 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1359 xmpp_pubsub_create_collection(client, node_name);
1361 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1365 return IKS_FILTER_EAT;
1369 * \brief Initialize collections for event distribution
1370 * \param client the configured XMPP client we use to connect to a XMPP server
1373 static void xmpp_init_event_distribution(struct ast_xmpp_client *client)
1375 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1376 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1378 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
1383 mwi_sub = ast_event_subscribe(AST_EVENT_MWI, xmpp_pubsub_mwi_cb, "xmpp_pubsub_mwi_subscription",
1384 client, AST_EVENT_IE_END);
1386 if (!device_state_sub) {
1387 if (ast_enable_distributed_devstate()) {
1390 device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
1391 xmpp_pubsub_devstate_cb, "xmpp_pubsub_devstate_subscription", client, AST_EVENT_IE_END);
1392 ast_event_dump_cache(device_state_sub);
1395 xmpp_pubsub_subscribe(client, "device_state");
1396 xmpp_pubsub_subscribe(client, "message_waiting");
1397 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_event, client, IKS_RULE_TYPE,
1398 IKS_PAK_MESSAGE, IKS_RULE_FROM, clientcfg->pubsubnode, IKS_RULE_DONE);
1399 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_error, client, IKS_RULE_TYPE,
1400 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
1404 /*! \brief Internal astobj2 callback function which returns the first resource, which is the highest priority one */
1405 static int xmpp_resource_immediate(void *obj, void *arg, int flags)
1407 return CMP_MATCH | CMP_STOP;
1412 * \brief Dial plan function status(). puts the status of watched user
1413 * into a channel variable.
1414 * \param chan ast_channel
1419 static int xmpp_status_exec(struct ast_channel *chan, const char *data)
1421 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1422 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1423 struct ast_xmpp_buddy *buddy;
1424 struct ast_xmpp_resource *resource;
1425 char *s = NULL, status[2];
1427 static int deprecation_warning = 0;
1428 AST_DECLARE_APP_ARGS(args,
1429 AST_APP_ARG(sender);
1431 AST_APP_ARG(variable);
1433 AST_DECLARE_APP_ARGS(jid,
1434 AST_APP_ARG(screenname);
1435 AST_APP_ARG(resource);
1438 if (deprecation_warning++ % 10 == 0) {
1439 ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
1442 if (ast_strlen_zero(data)) {
1443 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
1446 s = ast_strdupa(data);
1447 AST_STANDARD_APP_ARGS(args, s);
1449 if (args.argc != 3) {
1450 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
1454 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1455 if (jid.argc < 1 || jid.argc > 2) {
1456 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1460 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1461 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1465 if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
1466 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
1470 if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
1471 resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
1477 stat = resource->status;
1478 ao2_ref(resource, -1);
1480 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
1483 snprintf(status, sizeof(status), "%d", stat);
1484 pbx_builtin_setvar_helper(chan, args.variable, status);
1491 * \brief Dial plan funtcion to retrieve the status of a buddy.
1492 * \param channel The associated ast_channel, if there is one
1493 * \param data The account, buddy JID, and optional timeout
1496 * \retval -1 failure
1498 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1500 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1501 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1502 struct ast_xmpp_buddy *buddy;
1503 struct ast_xmpp_resource *resource;
1505 AST_DECLARE_APP_ARGS(args,
1506 AST_APP_ARG(sender);
1509 AST_DECLARE_APP_ARGS(jid,
1510 AST_APP_ARG(screenname);
1511 AST_APP_ARG(resource);
1514 if (ast_strlen_zero(data)) {
1515 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
1518 AST_STANDARD_APP_ARGS(args, data);
1520 if (args.argc != 2) {
1521 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
1525 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1526 if (jid.argc < 1 || jid.argc > 2) {
1527 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1531 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1532 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1536 if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
1537 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
1541 if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
1542 resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
1548 stat = resource->status;
1549 ao2_ref(resource, -1);
1551 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
1554 snprintf(buf, buflen, "%d", stat);
1559 static struct ast_custom_function jabberstatus_function = {
1560 .name = "JABBER_STATUS",
1561 .read = acf_jabberstatus_read,
1565 * \brief Application to join a chat room
1566 * \param chan ast_channel
1567 * \param data Data is sender|jid|nickname.
1571 static int xmpp_join_exec(struct ast_channel *chan, const char *data)
1573 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1574 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1575 char *s, nick[XMPP_MAX_RESJIDLEN];
1576 AST_DECLARE_APP_ARGS(args,
1577 AST_APP_ARG(sender);
1582 if (ast_strlen_zero(data)) {
1583 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1586 s = ast_strdupa(data);
1588 AST_STANDARD_APP_ARGS(args, s);
1589 if (args.argc < 2 || args.argc > 3) {
1590 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1594 if (strchr(args.jid, '/')) {
1595 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1599 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1600 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1604 if (ast_strlen_zero(args.nick)) {
1605 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1606 snprintf(nick, sizeof(nick), "asterisk");
1608 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1611 snprintf(nick, sizeof(nick), "%s", args.nick);
1614 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1615 ast_xmpp_chatroom_join(clientcfg->client, args.jid, nick);
1617 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1624 * \brief Application to leave a chat room
1625 * \param chan ast_channel
1626 * \param data Data is sender|jid|nickname.
1630 static int xmpp_leave_exec(struct ast_channel *chan, const char *data)
1632 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1633 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1634 char *s, nick[XMPP_MAX_RESJIDLEN];
1635 AST_DECLARE_APP_ARGS(args,
1636 AST_APP_ARG(sender);
1641 if (ast_strlen_zero(data)) {
1642 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1645 s = ast_strdupa(data);
1647 AST_STANDARD_APP_ARGS(args, s);
1648 if (args.argc < 2 || args.argc > 3) {
1649 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1653 if (strchr(args.jid, '/')) {
1654 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1658 if (ast_strlen_zero(args.jid) || !strchr(args.jid, '@')) {
1659 ast_log(LOG_ERROR, "No jabber ID specified\n");
1663 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1664 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1668 if (ast_strlen_zero(args.nick)) {
1669 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1670 snprintf(nick, sizeof(nick), "asterisk");
1672 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1675 snprintf(nick, sizeof(nick), "%s", args.nick);
1678 ast_xmpp_chatroom_leave(clientcfg->client, args.jid, nick);
1685 * \brief Dial plan function to send a message.
1686 * \param chan ast_channel
1687 * \param data Data is account,jid,message.
1689 * \retval -1 failure
1691 static int xmpp_send_exec(struct ast_channel *chan, const char *data)
1693 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1694 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1696 AST_DECLARE_APP_ARGS(args,
1697 AST_APP_ARG(sender);
1698 AST_APP_ARG(recipient);
1699 AST_APP_ARG(message);
1702 if (ast_strlen_zero(data)) {
1703 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1706 s = ast_strdupa(data);
1708 AST_STANDARD_APP_ARGS(args, s);
1710 if ((args.argc < 3) || ast_strlen_zero(args.message) || !strchr(args.recipient, '2')) {
1711 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1715 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1716 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1720 ast_xmpp_client_send_message(clientcfg->client, args.recipient, args.message);
1726 * \brief Application to send a message to a groupchat.
1727 * \param chan ast_channel
1728 * \param data Data is sender|groupchat|message.
1732 static int xmpp_sendgroup_exec(struct ast_channel *chan, const char *data)
1734 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1735 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1736 char *s, nick[XMPP_MAX_RESJIDLEN];
1737 AST_DECLARE_APP_ARGS(args,
1738 AST_APP_ARG(sender);
1739 AST_APP_ARG(groupchat);
1740 AST_APP_ARG(message);
1744 if (ast_strlen_zero(data)) {
1745 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1748 s = ast_strdupa(data);
1750 AST_STANDARD_APP_ARGS(args, s);
1751 if ((args.argc < 3) || (args.argc > 4) || ast_strlen_zero(args.message) || !strchr(args.groupchat, '@')) {
1752 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1756 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1757 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1761 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1762 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1763 snprintf(nick, sizeof(nick), "asterisk");
1765 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1768 snprintf(nick, sizeof(nick), "%s", args.nick);
1771 ast_xmpp_chatroom_send(clientcfg->client, nick, args.groupchat, args.message);
1778 * \brief Dial plan function to receive a message.
1779 * \param channel The associated ast_channel, if there is one
1780 * \param data The account, JID, and optional timeout
1783 * \retval -1 failure
1785 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1787 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1788 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1789 char *aux = NULL, *parse = NULL;
1790 int timeout, jidlen, resourcelen, found = 0;
1791 struct timeval start;
1793 struct ast_xmpp_message *message;
1794 AST_DECLARE_APP_ARGS(args,
1795 AST_APP_ARG(account);
1797 AST_APP_ARG(timeout);
1799 AST_DECLARE_APP_ARGS(jid,
1800 AST_APP_ARG(screenname);
1801 AST_APP_ARG(resource);
1804 if (ast_strlen_zero(data)) {
1805 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1809 parse = ast_strdupa(data);
1810 AST_STANDARD_APP_ARGS(args, parse);
1812 if (args.argc < 2 || args.argc > 3) {
1813 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1817 parse = ast_strdupa(args.jid);
1818 AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
1819 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > XMPP_MAX_JIDLEN) {
1820 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
1824 if (ast_strlen_zero(args.timeout)) {
1827 sscanf(args.timeout, "%d", &timeout);
1829 ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
1834 jidlen = strlen(jid.screenname);
1835 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
1837 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.account))) {
1838 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
1842 ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
1844 start = ast_tvnow();
1846 if (ast_autoservice_start(chan) < 0) {
1847 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
1851 /* search the messages list, grab the first message that matches with
1852 * the from JID we're expecting, and remove it from the messages list */
1853 while (diff < timeout) {
1854 struct timespec ts = { 0, };
1855 struct timeval wait;
1858 wait = ast_tvadd(start, ast_tv(timeout, 0));
1859 ts.tv_sec = wait.tv_sec;
1860 ts.tv_nsec = wait.tv_usec * 1000;
1862 /* wait up to timeout seconds for an incoming message */
1863 ast_mutex_lock(&messagelock);
1864 if (AST_LIST_EMPTY(&clientcfg->client->messages)) {
1865 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
1867 ast_mutex_unlock(&messagelock);
1868 if (res == ETIMEDOUT) {
1869 ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
1873 AST_LIST_LOCK(&clientcfg->client->messages);
1874 AST_LIST_TRAVERSE_SAFE_BEGIN(&clientcfg->client->messages, message, list) {
1875 if (jid.argc == 1) {
1876 /* no resource provided, compare bare JIDs */
1877 if (strncasecmp(jid.screenname, message->from, jidlen)) {
1881 /* resource appended, compare bare JIDs and resources */
1882 char *resource = strchr(message->from, '/');
1883 if (!resource || strlen(resource) == 0) {
1884 ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", message->from);
1885 if (strncasecmp(jid.screenname, message->from, jidlen)) {
1890 if (strncasecmp(jid.screenname, message->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
1895 /* check if the message is not too old */
1896 if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
1897 ast_debug(3, "Found old message from %s, deleting it\n", message->from);
1898 AST_LIST_REMOVE_CURRENT(list);
1899 xmpp_message_destroy(message);
1903 aux = ast_strdupa(message->message);
1904 AST_LIST_REMOVE_CURRENT(list);
1905 xmpp_message_destroy(message);
1908 AST_LIST_TRAVERSE_SAFE_END;
1909 AST_LIST_UNLOCK(&clientcfg->client->messages);
1915 diff = ast_tvdiff_ms(ast_tvnow(), start);
1918 if (ast_autoservice_stop(chan) < 0) {
1919 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
1922 /* return if we timed out */
1924 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
1927 ast_copy_string(buf, aux, buflen);
1932 static struct ast_custom_function jabberreceive_function = {
1933 .name = "JABBER_RECEIVE",
1934 .read = acf_jabberreceive_read,
1939 * \brief Delete old messages from a given JID
1940 * Messages stored during more than client->message_timeout are deleted
1941 * \param client Asterisk's XMPP client
1942 * \param from the JID we received messages from
1943 * \retval the number of deleted messages
1945 static int delete_old_messages(struct ast_xmpp_client *client, char *from)
1947 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1948 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1949 int deleted = 0, isold = 0;
1950 struct ast_xmpp_message *message = NULL;
1952 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
1956 AST_LIST_LOCK(&client->messages);
1957 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, message, list) {
1959 if (!from || !strncasecmp(from, message->from, strlen(from))) {
1960 AST_LIST_REMOVE_CURRENT(list);
1961 xmpp_message_destroy(message);
1964 } else if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
1966 if (!from || !strncasecmp(from, message->from, strlen(from))) {
1967 AST_LIST_REMOVE_CURRENT(list);
1968 xmpp_message_destroy(message);
1973 AST_LIST_TRAVERSE_SAFE_END;
1974 AST_LIST_UNLOCK(&client->messages);
1979 static int xmpp_send_cb(const struct ast_msg *msg, const char *to, const char *from)
1981 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1982 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1983 char *sender, *dest;
1986 sender = ast_strdupa(from);
1987 strsep(&sender, ":");
1988 dest = ast_strdupa(to);
1991 if (ast_strlen_zero(sender)) {
1992 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for XMPP\n", from);
1996 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, sender))) {
1997 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
2001 ast_debug(1, "Sending message to '%s' from '%s'\n", dest, clientcfg->name);
2003 if ((res = ast_xmpp_client_send_message(clientcfg->client, dest, ast_msg_get_body(msg))) != IKS_OK) {
2004 ast_log(LOG_WARNING, "Failed to send XMPP message (%d).\n", res);
2007 return res == IKS_OK ? 0 : -1;
2010 static const struct ast_msg_tech msg_tech = {
2012 .msg_send = xmpp_send_cb,
2015 /*! \brief Internal function which changes the XMPP client state */
2016 static void xmpp_client_change_state(struct ast_xmpp_client *client, int state)
2018 client->state = state;
2021 /*! \brief Internal function which creates a buddy on a client */
2022 static struct ast_xmpp_buddy *xmpp_client_create_buddy(struct ao2_container *container, const char *id)
2024 struct ast_xmpp_buddy *buddy;
2026 if (!(buddy = ao2_alloc(sizeof(*buddy), xmpp_buddy_destructor))) {
2030 if (!(buddy->resources = ao2_container_alloc(RESOURCE_BUCKETS, xmpp_resource_hash, xmpp_resource_cmp))) {
2035 ast_copy_string(buddy->id, id, sizeof(buddy->id));
2037 /* Assume we need to subscribe to get their presence until proven otherwise */
2038 buddy->subscribe = 1;
2040 ao2_link(container, buddy);
2045 /*! \brief Helper function which unsubscribes a user and removes them from the roster */
2046 static int xmpp_client_unsubscribe_user(struct ast_xmpp_client *client, const char *user)
2048 iks *iq, *query = NULL, *item = NULL;
2050 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, user,
2051 "Goodbye. Your status is no longer required.\n"))) {
2055 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item"))) {
2056 ast_log(LOG_WARNING, "Could not allocate memory for roster removal of '%s' from client '%s'\n",
2057 user, client->name);
2061 iks_insert_attrib(iq, "from", client->jid->full);
2062 iks_insert_attrib(iq, "type", "set");
2063 iks_insert_attrib(query, "xmlns", "jabber:iq:roster");
2064 iks_insert_node(iq, query);
2065 iks_insert_attrib(item, "jid", user);
2066 iks_insert_attrib(item, "subscription", "remove");
2067 iks_insert_node(query, item);
2069 if (ast_xmpp_client_send(client, iq)) {
2070 ast_log(LOG_WARNING, "Could not send roster removal request of '%s' from client '%s'\n",
2071 user, client->name);
2082 /*! \brief Callback function which subscribes to a user if needed */
2083 static int xmpp_client_subscribe_user(void *obj, void *arg, int flags)
2085 struct ast_xmpp_buddy *buddy = obj;
2086 struct ast_xmpp_client *client = arg;
2088 if (!buddy->subscribe) {
2092 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, buddy->id,
2093 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"))) {
2094 ast_log(LOG_WARNING, "Could not send subscription for '%s' on client '%s'\n",
2095 buddy->id, client->name);
2098 buddy->subscribe = 0;
2103 /*! \brief Hook function called when roster is received from server */
2104 static int xmpp_roster_hook(void *data, ikspak *pak)
2106 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2107 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2108 struct ast_xmpp_client *client = data;
2111 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2112 return IKS_FILTER_EAT;
2115 for (item = iks_child(pak->query); item; item = iks_next(item)) {
2116 struct ast_xmpp_buddy *buddy;
2118 if (iks_strcmp(iks_name(item), "item")) {
2122 if (!(buddy = ao2_find(client->buddies, iks_find_attrib(item, "jid"), OBJ_KEY))) {
2123 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOPRUNE)) {
2124 /* The buddy has not been specified in the configuration file, we no longer
2125 * want them on our buddy list or to receive their presence. */
2126 if (xmpp_client_unsubscribe_user(client, iks_find_attrib(item, "jid"))) {
2127 ast_log(LOG_ERROR, "Could not unsubscribe user '%s' on client '%s'\n",
2128 iks_find_attrib(item, "jid"), client->name);
2133 if (!(buddy = xmpp_client_create_buddy(client->buddies, iks_find_attrib(item, "jid")))) {
2134 ast_log(LOG_ERROR, "Could not allocate buddy '%s' on client '%s'\n", iks_find_attrib(item, "jid"),
2140 /* Determine if we need to subscribe to their presence or not */
2141 if (!iks_strcmp(iks_find_attrib(item, "subscription"), "none") ||
2142 !iks_strcmp(iks_find_attrib(item, "subscription"), "from")) {
2143 buddy->subscribe = 1;
2145 buddy->subscribe = 0;
2151 /* If autoregister is enabled we need to go through every buddy that we need to subscribe to and do so */
2152 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOREGISTER)) {
2153 ao2_callback(client->buddies, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_subscribe_user, client);
2156 xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
2158 return IKS_FILTER_EAT;
2161 /*! \brief Internal function which changes the presence status of an XMPP client */
2162 static void xmpp_client_set_presence(struct ast_xmpp_client *client, const char *to, const char *from, int level, const char *desc)
2164 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2165 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2166 iks *presence = NULL, *cnode = NULL, *priority = NULL;
2169 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2170 !(presence = iks_make_pres(level, desc)) || !(cnode = iks_new("c")) || !(priority = iks_new("priority"))) {
2171 ast_log(LOG_ERROR, "Unable to allocate stanzas for setting presence status for client '%s'\n", client->name);
2175 if (!ast_strlen_zero(to)) {
2176 iks_insert_attrib(presence, "to", to);
2179 if (!ast_strlen_zero(from)) {
2180 iks_insert_attrib(presence, "from", from);
2183 snprintf(priorityS, sizeof(priorityS), "%d", clientcfg->priority);
2184 iks_insert_cdata(priority, priorityS, strlen(priorityS));
2185 iks_insert_node(presence, priority);
2186 iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
2187 iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
2188 iks_insert_attrib(cnode, "ext", "voice-v1 video-v1 camera-v1");
2189 iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
2190 iks_insert_node(presence, cnode);
2191 ast_xmpp_client_send(client, presence);
2195 iks_delete(presence);
2196 iks_delete(priority);
2199 /*! \brief Hook function called when client receives a service discovery get message */
2200 static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
2202 struct ast_xmpp_client *client = data;
2203 iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
2205 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(ident = iks_new("identity")) || !(disco = iks_new("feature")) ||
2206 !(google = iks_new("feature")) || !(jingle = iks_new("feature")) || !(ice = iks_new("feature")) || !(rtp = iks_new("feature")) ||
2207 !(audio = iks_new("feature")) || !(video = iks_new("feature"))) {
2208 ast_log(LOG_ERROR, "Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
2209 pak->from->full, client->name);
2213 iks_insert_attrib(iq, "from", client->jid->full);
2214 iks_insert_attrib(iq, "to", pak->from->full);
2215 iks_insert_attrib(iq, "type", "result");
2216 iks_insert_attrib(iq, "id", pak->id);
2217 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2218 iks_insert_attrib(ident, "category", "client");
2219 iks_insert_attrib(ident, "type", "pc");
2220 iks_insert_attrib(ident, "name", "asterisk");
2221 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2223 iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2224 iks_insert_attrib(jingle, "var", "urn:xmpp:jingle:1");
2225 iks_insert_attrib(ice, "var", "urn:xmpp:jingle:transports:ice-udp:1");
2226 iks_insert_attrib(rtp, "var", "urn:xmpp:jingle:apps:rtp:1");
2227 iks_insert_attrib(audio, "var", "urn:xmpp:jingle:apps:rtp:audio");
2228 iks_insert_attrib(video, "var", "urn:xmpp:jingle:apps:rtp:video");
2229 iks_insert_node(iq, query);
2230 iks_insert_node(query, ident);
2231 iks_insert_node(query, google);
2232 iks_insert_node(query, disco);
2233 iks_insert_node(query, jingle);
2234 iks_insert_node(query, ice);
2235 iks_insert_node(query, rtp);
2236 iks_insert_node(query, audio);
2237 iks_insert_node(query, video);
2238 ast_xmpp_client_send(client, iq);
2252 return IKS_FILTER_EAT;
2255 /*! \brief Hook function called when client receives a service discovery result message */
2256 static int xmpp_client_service_discovery_result_hook(void *data, ikspak *pak)
2258 struct ast_xmpp_client *client = data;
2259 struct ast_xmpp_buddy *buddy;
2260 struct ast_xmpp_resource *resource;
2262 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2263 return IKS_FILTER_EAT;
2266 if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY))) {
2268 return IKS_FILTER_EAT;
2273 if (iks_find_with_attrib(pak->query, "feature", "var", "urn:xmpp:jingle:1")) {
2274 resource->caps.jingle = 1;
2277 ao2_unlock(resource);
2279 ao2_ref(resource, -1);
2282 return IKS_FILTER_EAT;
2285 /*! \brief Hook function called when client finishes authenticating with the server */
2286 static int xmpp_connect_hook(void *data, ikspak *pak)
2288 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2289 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2290 struct ast_xmpp_client *client = data;
2293 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2297 client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
2299 if (ast_test_flag(&clientcfg->flags, XMPP_DISTRIBUTE_EVENTS)) {
2300 xmpp_init_event_distribution(client);
2303 if (!(roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER))) {
2304 ast_log(LOG_ERROR, "Unable to allocate memory for roster request for client '%s'\n", client->name);
2308 iks_filter_add_rule(client->filter, xmpp_client_service_discovery_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2309 iks_filter_add_rule(client->filter, xmpp_client_service_discovery_result_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2311 iks_insert_attrib(roster, "id", "roster");
2312 ast_xmpp_client_send(client, roster);
2314 iks_filter_remove_hook(client->filter, xmpp_connect_hook);
2315 iks_filter_add_rule(client->filter, xmpp_roster_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "roster", IKS_RULE_DONE);
2317 xmpp_client_set_presence(client, NULL, client->jid->full, clientcfg->status, clientcfg->statusmsg);
2318 xmpp_client_change_state(client, XMPP_STATE_ROSTER);
2320 return IKS_FILTER_EAT;
2323 /*! \brief Logging hook function */
2324 static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incoming)
2326 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2327 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2328 struct ast_xmpp_client *client = data;
2330 if (!ast_strlen_zero(xmpp)) {
2331 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
2334 if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) {
2339 if (strlen(xmpp) == 1) {
2340 if (option_debug > 2 && xmpp[0] == ' ') {
2341 ast_verbose("\n<--- XMPP keep alive from '%s' --->\n", client->name);
2344 ast_verbose("\n<--- XMPP sent to '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2347 ast_verbose("\n<--- XMPP received from '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2351 /*! \brief Internal function which sends a raw message */
2352 static int xmpp_client_send_raw_message(struct ast_xmpp_client *client, const char *message)
2356 int len = strlen(message);
2358 if (xmpp_is_secure(client)) {
2359 ret = SSL_write(client->ssl_session, message, len);
2361 /* Log the message here, because iksemel's logHook is
2363 xmpp_log_hook(client, message, len, 0);
2368 /* If needed, data will be sent unencrypted, and logHook will
2369 be called inside iks_send_raw */
2370 ret = iks_send_raw(client->parser, message);
2371 if (ret != IKS_OK) {
2378 /*! \brief Helper function which sends an XMPP stream header to the server */
2379 static int xmpp_send_stream_header(struct ast_xmpp_client *client, const struct ast_xmpp_client_config *cfg, const char *to)
2381 char *namespace = ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client";
2382 char msg[91 + strlen(namespace) + 6 + strlen(to) + 16 + 1];
2384 snprintf(msg, sizeof(msg), "<?xml version='1.0'?>"
2385 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
2386 "%s' to='%s' version='1.0'>", namespace, to);
2388 return xmpp_client_send_raw_message(client, msg);
2391 int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza)
2393 return xmpp_client_send_raw_message(client, iks_string(iks_stack(stanza), stanza));
2396 /*! \brief Internal function called when we need to request TLS support */
2397 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2399 /* If the client connection is already secure we can jump straight to authenticating */
2400 if (xmpp_is_secure(client)) {
2401 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2405 #ifndef HAVE_OPENSSL
2406 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL is not available.\n", client->name);
2409 if (iks_send_raw(client->parser, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>") == IKS_NET_TLSFAIL) {
2410 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be started.\n", client->name);
2414 client->stream_flags |= TRY_SECURE;
2416 xmpp_client_change_state(client, XMPP_STATE_REQUESTED_TLS);
2422 /*! \brief Internal function called when we receive a response to our TLS initiation request */
2423 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2429 if (!strcmp(iks_name(node), "success")) {
2430 /* TLS is up and working, we can move on to authenticating now */
2431 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2433 } else if (!strcmp(iks_name(node), "failure")) {
2434 /* TLS negotiation was a failure, close it on down! */
2436 } else if (strcmp(iks_name(node), "proceed")) {
2437 /* Ignore any other responses */
2441 #ifndef HAVE_OPENSSL
2442 ast_log(LOG_ERROR, "Somehow we managed to try to start TLS negotiation on client '%s' without OpenSSL support, disconnecting\n", client->name);
2445 client->ssl_method = SSLv3_method();
2446 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
2450 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
2454 sock = iks_fd(client->parser);
2455 if (!SSL_set_fd(client->ssl_session, sock)) {
2459 if (!SSL_connect(client->ssl_session)) {
2463 client->stream_flags &= (~TRY_SECURE);
2464 client->stream_flags |= SECURE;
2466 if (xmpp_send_stream_header(client, cfg, client->jid->server) != IKS_OK) {
2467 ast_log(LOG_ERROR, "TLS connection for client '%s' could not be established, failed to send stream header after negotiation\n",
2472 ast_debug(1, "TLS connection for client '%s' started with server\n", client->name);
2474 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2479 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL initialization failed.\n", client->name);
2484 /*! \brief Internal function called when we need to authenticate using non-SASL */
2485 static int xmpp_client_authenticate_digest(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2487 iks *iq = NULL, *query = NULL;
2488 char buf[41], sidpass[100];
2490 if (!(iq = iks_new("iq")) || !(query = iks_insert(iq, "query"))) {
2491 ast_log(LOG_ERROR, "Stanzas could not be allocated for authentication on client '%s'\n", client->name);
2496 iks_insert_attrib(iq, "type", "set");
2497 iks_insert_cdata(iks_insert(query, "username"), client->jid->user, 0);
2498 iks_insert_cdata(iks_insert(query, "resource"), client->jid->resource, 0);
2500 snprintf(sidpass, sizeof(sidpass), "%s%s", iks_find_attrib(node, "id"), cfg->password);
2501 ast_sha1_hash(buf, sidpass);
2502 iks_insert_cdata(iks_insert(query, "digest"), buf, 0);
2504 ast_xmpp_client_lock(client);
2505 iks_filter_add_rule(client->filter, xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE);
2506 iks_insert_attrib(iq, "id", client->mid);
2507 ast_xmpp_increment_mid(client->mid);
2508 ast_xmpp_client_unlock(client);
2510 iks_insert_attrib(iq, "to", client->jid->server);
2512 ast_xmpp_client_send(client, iq);
2516 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2521 /*! \brief Internal function called when we need to authenticate using SASL */
2522 static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2524 int features, len = strlen(cfg->user) + strlen(cfg->password) + 3;
2527 char base64[(len + 2) * 4 / 3];
2529 if (strcmp(iks_name(node), "stream:features")) {
2530 /* Ignore anything beside stream features */
2534 features = iks_stream_features(node);
2536 if ((features & IKS_STREAM_SASL_MD5) && !xmpp_is_secure(client)) {
2537 if (iks_start_sasl(client->parser, IKS_SASL_DIGEST_MD5, (char*)cfg->user, (char*)cfg->password) != IKS_OK) {
2538 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL DIGEST-MD5 but could not\n", client->name);
2542 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2546 /* Our only other available option is plain so if they don't support it, bail out now */
2547 if (!(features & IKS_STREAM_SASL_PLAIN)) {
2548 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL PLAIN but server does not support it\n", client->name);
2552 if (!(auth = iks_new("auth"))) {
2553 ast_log(LOG_ERROR, "Could not allocate memory for SASL PLAIN authentication for client '%s'\n", client->name);
2557 iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
2558 iks_insert_attrib(auth, "mechanism", "PLAIN");
2560 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, cfg->user, 0, cfg->password);
2561 ast_base64encode(base64, (const unsigned char *) combined, len - 1, (len + 2) * 4 / 3);
2562 iks_insert_cdata(auth, base64, 0);
2564 ast_xmpp_client_send(client, auth);
2568 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2573 /*! \brief Internal function called when we need to authenticate */
2574 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2576 return ast_test_flag(&cfg->flags, XMPP_USESASL) ? xmpp_client_authenticate_sasl(client, cfg, type, node) : xmpp_client_authenticate_digest(client, cfg, type, node);
2579 /*! \brief Internal function called when we are authenticating */
2580 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2584 if (!strcmp(iks_name(node), "success")) {
2585 /* Authentication was a success, yay! */
2586 xmpp_send_stream_header(client, cfg, client->jid->server);
2589 } else if (!strcmp(iks_name(node), "failure")) {
2590 /* Authentication was a bust, disconnect and reconnect later */
2592 } else if (strcmp(iks_name(node), "stream:features")) {
2593 /* Ignore any other responses */
2597 features = iks_stream_features(node);
2599 if (features & IKS_STREAM_BIND) {
2602 if (!(auth = iks_make_resource_bind(client->jid))) {
2603 ast_log(LOG_ERROR, "Failed to allocate memory for stream bind on client '%s'\n", client->name);
2607 ast_xmpp_client_lock(client);
2608 iks_insert_attrib(auth, "id", client->mid);
2609 ast_xmpp_increment_mid(client->mid);
2610 ast_xmpp_client_unlock(client);
2611 ast_xmpp_client_send(client, auth);
2615 iks_filter_add_rule(client->filter, xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
2618 if (features & IKS_STREAM_SESSION) {
2621 if (!(auth = iks_make_session())) {
2622 ast_log(LOG_ERROR, "Failed to allocate memory for stream session on client '%s'\n", client->name);
2626 iks_insert_attrib(auth, "id", "auth");
2627 ast_xmpp_client_lock(client);
2628 ast_xmpp_increment_mid(client->mid);
2629 ast_xmpp_client_unlock(client);
2630 ast_xmpp_client_send(client, auth);
2634 iks_filter_add_rule(client->filter, xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
2640 /*! \brief Internal function called when we should authenticate as a component */
2641 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2643 char secret[160], shasum[320], message[344];
2644 ikspak *pak = iks_packet(node);
2646 snprintf(secret, sizeof(secret), "%s%s", pak->id, cfg->password);
2647 ast_sha1_hash(shasum, secret);
2648 snprintf(message, sizeof(message), "<handshake>%s</handshake>", shasum);
2650 if (xmpp_client_send_raw_message(client, message) != IKS_OK) {
2651 ast_log(LOG_ERROR, "Unable to send handshake for component '%s'\n", client->name);
2655 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2660 /*! \brief Hook function called when component receives a service discovery get message */
2661 static int xmpp_component_service_discovery_get_hook(void *data, ikspak *pak)
2663 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2664 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2665 struct ast_xmpp_client *client = data;
2666 iks *iq = NULL, *query = NULL, *identity = NULL, *disco = NULL, *reg = NULL, *commands = NULL, *gateway = NULL;
2667 iks *version = NULL, *vcard = NULL, *search = NULL, *item = NULL;
2670 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2671 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(identity = iks_new("identity")) || !(disco = iks_new("feature")) ||
2672 !(reg = iks_new("feature")) || !(commands = iks_new("feature")) || !(gateway = iks_new("feature")) || !(version = iks_new("feature")) ||
2673 !(vcard = iks_new("feature")) || !(search = iks_new("search")) || !(item = iks_new("item"))) {
2674 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery get response to '%s' on component '%s'\n",
2675 pak->from->partial, client->name);
2679 iks_insert_attrib(iq, "from", clientcfg->user);
2680 iks_insert_attrib(iq, "to", pak->from->full);
2681 iks_insert_attrib(iq, "id", pak->id);
2682 iks_insert_attrib(iq, "type", "result");
2684 if (!(node = iks_find_attrib(pak->query, "node"))) {
2685 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2686 iks_insert_attrib(identity, "category", "gateway");
2687 iks_insert_attrib(identity, "type", "pstn");
2688 iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2689 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2690 iks_insert_attrib(reg, "var", "jabber:iq:register");
2691 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2692 iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2693 iks_insert_attrib(version, "var", "jabber:iq:version");
2694 iks_insert_attrib(vcard, "var", "vcard-temp");
2695 iks_insert_attrib(search, "var", "jabber:iq:search");
2697 iks_insert_node(iq, query);
2698 iks_insert_node(query, identity);
2699 iks_insert_node(query, disco);
2700 iks_insert_node(query, reg);
2701 iks_insert_node(query, commands);
2702 iks_insert_node(query, gateway);
2703 iks_insert_node(query, version);
2704 iks_insert_node(query, vcard);
2705 iks_insert_node(query, search);
2706 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2707 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2708 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2709 iks_insert_attrib(item, "node", "confirmaccount");
2710 iks_insert_attrib(item, "name", "Confirm account");
2711 iks_insert_attrib(item, "jid", clientcfg->user);
2713 iks_insert_node(iq, query);
2714 iks_insert_node(query, item);
2715 } else if (!strcasecmp(node, "confirmaccount")) {
2716 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2717 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2719 iks_insert_node(iq, query);
2720 iks_insert_node(query, commands);
2722 ast_debug(3, "Unsupported service discovery info request received with node '%s' on component '%s'\n",
2723 node, client->name);
2727 if (ast_xmpp_client_send(client, iq)) {
2728 ast_log(LOG_WARNING, "Could not send response to service discovery request on component '%s'\n",
2735 iks_delete(version);
2736 iks_delete(gateway);
2737 iks_delete(commands);
2740 iks_delete(identity);
2744 return IKS_FILTER_EAT;
2747 /*! \brief Hook function called when the component is queried about registration */
2748 static int xmpp_component_register_get_hook(void *data, ikspak *pak)
2750 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2751 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2752 struct ast_xmpp_client *client = data;
2753 iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL, *instructions = NULL;
2754 struct ast_xmpp_buddy *buddy;
2757 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2758 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(error = iks_new("error")) || !(notacceptable = iks_new("not-acceptable")) ||
2759 !(instructions = iks_new("instructions"))) {
2760 ast_log(LOG_ERROR, "Failed to allocate stanzas for register get response to '%s' on component '%s'\n",
2761 pak->from->partial, client->name);
2765 iks_insert_attrib(iq, "from", clientcfg->user);
2766 iks_insert_attrib(iq, "to", pak->from->full);
2767 iks_insert_attrib(iq, "id", pak->id);
2768 iks_insert_attrib(iq, "type", "result");
2769 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
2770 iks_insert_node(iq, query);
2772 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2773 iks_insert_attrib(error, "code", "406");
2774 iks_insert_attrib(error, "type", "modify");
2775 iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
2777 iks_insert_node(iq, error);
2778 iks_insert_node(error, notacceptable);
2780 ast_log(LOG_ERROR, "Received register attempt from '%s' but buddy is not configured on component '%s'\n",
2781 pak->from->partial, client->name);
2782 } else if (!(node = iks_find_attrib(pak->query, "node"))) {
2783 iks_insert_cdata(instructions, "Welcome to Asterisk - the Open Source PBX.\n", 0);
2784 iks_insert_node(query, instructions);
2787 ast_log(LOG_WARNING, "Received register get to component '%s' using unsupported node '%s' from '%s'\n",
2788 client->name, node, pak->from->partial);
2793 if (ast_xmpp_client_send(client, iq)) {
2794 ast_log(LOG_WARNING, "Could not send response to '%s' for received register get on component '%s'\n",
2795 pak->from->partial, client->name);
2799 iks_delete(instructions);
2800 iks_delete(notacceptable);
2805 return IKS_FILTER_EAT;
2808 /*! \brief Hook function called when someone registers to the component */
2809 static int xmpp_component_register_set_hook(void *data, ikspak *pak)
2811 struct ast_xmpp_client *client = data;
2812 iks *iq, *presence = NULL, *x = NULL;
2814 if (!(iq = iks_new("iq")) || !(presence = iks_new("presence")) || !(x = iks_new("x"))) {
2815 ast_log(LOG_ERROR, "Failed to allocate stanzas for register set response to '%s' on component '%s'\n",
2816 pak->from->partial, client->name);
2820 iks_insert_attrib(iq, "from", client->jid->full);
2821 iks_insert_attrib(iq, "to", pak->from->full);
2822 iks_insert_attrib(iq, "id", pak->id);
2823 iks_insert_attrib(iq, "type", "result");
2825 if (ast_xmpp_client_send(client, iq)) {
2826 ast_log(LOG_WARNING, "Could not send response to '%s' for received register set on component '%s'\n",
2827 pak->from->partial, client->name);
2831 iks_insert_attrib(presence, "from", client->jid->full);
2832 iks_insert_attrib(presence, "to", pak->from->partial);
2833 ast_xmpp_client_lock(client);
2834 iks_insert_attrib(presence, "id", client->mid);
2835 ast_xmpp_increment_mid(client->mid);
2836 ast_xmpp_client_unlock(client);
2837 iks_insert_attrib(presence, "type", "subscribe");
2838 iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
2840 iks_insert_node(presence, x);
2842 if (ast_xmpp_client_send(client, presence)) {
2843 ast_log(LOG_WARNING, "Could not send subscription to '%s' on component '%s'\n",
2844 pak->from->partial, client->name);
2849 iks_delete(presence);
2852 return IKS_FILTER_EAT;
2855 /*! \brief Hook function called when we receive a service discovery items request */
2856 static int xmpp_component_service_discovery_items_hook(void *data, ikspak *pak)
2858 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2859 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2860 struct ast_xmpp_client *client = data;
2861 iks *iq = NULL, *query = NULL, *item = NULL, *feature = NULL;
2864 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2865 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item")) || !(feature = iks_new("feature"))) {
2866 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery items response to '%s' on component '%s'\n",
2867 pak->from->partial, client->name);
2871 iks_insert_attrib(iq, "from", clientcfg->user);
2872 iks_insert_attrib(iq, "to", pak->from->full);
2873 iks_insert_attrib(iq, "id", pak->id);
2874 iks_insert_attrib(iq, "type", "result");
2875 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2876 iks_insert_node(iq, query);
2878 if (!(node = iks_find_attrib(pak->query, "node"))) {
2879 iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
2880 iks_insert_attrib(item, "name", "Asterisk Commands");
2881 iks_insert_attrib(item, "jid", clientcfg->user);
2883 iks_insert_node(query, item);
2884 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2885 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2887 ast_log(LOG_WARNING, "Received service discovery items request to component '%s' using unsupported node '%s' from '%s'\n",
2888 client->name, node, pak->from->partial);
2892 if (ast_xmpp_client_send(client, iq)) {
2893 ast_log(LOG_WARNING, "Could not send response to service discovery items request from '%s' on component '%s'\n",
2894 pak->from->partial, client->name);
2898 iks_delete(feature);
2903 return IKS_FILTER_EAT;
2906 /*! \brief Internal function called when we authenticated as a component */
2907 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2909 if (strcmp(iks_name(node), "handshake")) {
2910 ast_log(LOG_ERROR, "Failed to authenticate component '%s'\n", client->name);
2914 iks_filter_add_rule(client->filter, xmpp_component_service_discovery_items_hook, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
2916 iks_filter_add_rule(client->filter, xmpp_component_service_discovery_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2918 /* This uses the client service discovery result hook on purpose, as the code is common between both */
2919 iks_filter_add_rule(client->filter, xmpp_client_service_discovery_result_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2921 iks_filter_add_rule(client->filter, xmpp_component_register_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
2922 iks_filter_add_rule(client->filter, xmpp_component_register_set_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
2924 xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
2929 /*! \brief Internal function called when a message is received */
2930 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
2932 struct ast_xmpp_message *message;
2935 ast_debug(3, "XMPP client '%s' received a message\n", client->name);
2937 if (!(message = ast_calloc(1, sizeof(*message)))) {
2941 message->arrived = ast_tvnow();
2943 if (iks_find_cdata(pak->x, "body")) {
2944 message->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2947 ast_copy_string(message->id, S_OR(pak->id, ""), sizeof(message->id));
2948 message->from = !ast_strlen_zero(pak->from->full) ? ast_strdup(pak->from->full) : NULL;
2950 if (ast_test_flag(&cfg->flags, XMPP_SEND_TO_DIALPLAN)) {
2951 struct ast_msg *msg;
2953 if ((msg = ast_msg_alloc())) {
2956 ast_xmpp_client_lock(client);
2958 res = ast_msg_set_to(msg, "xmpp:%s", cfg->user);
2959 res |= ast_msg_set_from(msg, "xmpp:%s", message->from);
2960 res |= ast_msg_set_body(msg, "%s", message->message);
2961 res |= ast_msg_set_context(msg, "%s", cfg->context);
2963 ast_xmpp_client_unlock(client);
2966 ast_msg_destroy(msg);
2973 /* remove old messages received from this JID
2974 * and insert received message */
2975 deleted = delete_old_messages(client, pak->from->partial);
2976 ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2977 AST_LIST_LOCK(&client->messages);
2978 AST_LIST_INSERT_HEAD(&client->messages, message, list);
2979 AST_LIST_UNLOCK(&client->messages);
2981 /* wake up threads waiting for messages */
2982 ast_mutex_lock(&messagelock);
2983 ast_cond_broadcast(&message_received_condition);
2984 ast_mutex_unlock(&messagelock);
2989 /*! \brief Helper function which sends a discovery information request to a user */
2990 static int xmpp_client_send_disco_info_request(struct ast_xmpp_client *client, const char *to, const char *from)
2995 if (!(iq = iks_new("iq")) || !(query = iks_new("query"))) {
3000 iks_insert_attrib(iq, "type", "get");
3001 iks_insert_attrib(iq, "to", to);
3002 iks_insert_attrib(iq, "from", from);
3003 ast_xmpp_client_lock(client);
3004 iks_insert_attrib(iq, "id", client->mid);
3005 ast_xmpp_increment_mid(client->mid);
3006 ast_xmpp_client_unlock(client);
3007 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
3008 iks_insert_node(iq, query);
3010 res = ast_xmpp_client_send(client, iq);
3018 /*! \brief Internal function called when a presence message is received */
3019 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
3021 struct ast_xmpp_buddy *buddy;
3022 struct ast_xmpp_resource *resource;
3023 char *type = iks_find_attrib(pak->x, "type");
3024 int status = pak->show ? pak->show : STATUS_DISAPPEAR;
3026 /* If no resource is available this is a general buddy presence update, which we will ignore */
3027 if (!pak->from->resource) {
3031 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
3032 ast_log(LOG_WARNING, "Received presence information about '%s' despite not having them in roster on client '%s'\n",
3033 pak->from->partial, client->name);
3037 /* If this is a component presence probe request answer immediately with our presence status */
3038 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT) && !ast_strlen_zero(type) && !strcasecmp(type, "probe")) {
3039 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3042 ao2_lock(buddy->resources);
3044 if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY | OBJ_NOLOCK))) {
3045 /* Only create the new resource if it is not going away - in reality this should not happen */
3046 if (status != STATUS_DISAPPEAR) {
3047 if (!(resource = ao2_alloc(sizeof(*resource), xmpp_resource_destructor))) {
3048 ast_log(LOG_ERROR, "Could not allocate resource object for resource '%s' of buddy '%s' on client '%s'\n",
3049 pak->from->resource, buddy->id, client->name);
3050 ao2_unlock(buddy->resources);
3055 ast_copy_string(resource->resource, pak->from->resource, sizeof(resource->resource));
3058 /* We unlink the resource in case the priority changes or in case they are going away */
3059 ao2_unlink_flags(buddy->resources, resource, OBJ_NOLOCK);
3062 /* Only update the resource and add it back in if it is not going away */
3063 if (resource && (status != STATUS_DISAPPEAR)) {
3066 /* Try to get the XMPP spec node, and fall back to Google if not found */
3067 if (!(node = iks_find_attrib(iks_find(pak->x, "c"), "node"))) {
3068 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
3071 if (!(ver = iks_find_attrib(iks_find(pak->x, "c"), "ver"))) {
3072 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
3075 if (resource->description) {
3076 ast_free(resource->description);
3079 if ((node && strcmp(resource->caps.node, node)) || (ver && strcmp(resource->caps.version, ver))) {
3080 ast_copy_string(resource->caps.node, node, sizeof(resource->caps.node));
3081 ast_copy_string(resource->caps.version, ver, sizeof(resource->caps.version));
3083 /* Google Talk places the capabilities information directly in presence, so see if it is there */
3084 if (iks_find_with_attrib(pak->x, "c", "node", "http://www.google.com/xmpp/client/caps") ||
3085 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.google.com/xmpp/client/caps") ||
3086 iks_find_with_attrib(pak->x, "c", "node", "http://www.android.com/gtalk/client/caps") ||
3087 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.android.com/gtalk/client/caps") ||
3088 iks_find_with_attrib(pak->x, "c", "node", "http://mail.google.com/xmpp/client/caps") ||
3089 iks_find_with_attrib(pak->x, "caps:c", "node", "http://mail.google.com/xmpp/client/caps")) {
3090 resource->caps.google = 1;
3093 /* To discover if the buddy supports Jingle we need to query, so do so */
3094 if (xmpp_client_send_disco_info_request(client, pak->from->full, client->jid->full)) {
3095 ast_log(LOG_WARNING, "Could not send discovery information request to resource '%s' of buddy '%s' on client '%s', capabilities may be incomplete\n", resource->resource, buddy->id, client->name);
3099 resource->status = status;
3100 resource->description = ast_strdup(iks_find_cdata(pak->x, "status"));
3101 resource->priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
3103 ao2_link_flags(buddy->resources, resource, OBJ_NOLOCK);
3105 manager_event(EVENT_FLAG_USER, "JabberStatus",
3106 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
3107 "\r\nDescription: %s\r\n",
3108 client->name, pak->from->partial, resource->resource, resource->status,
3109 resource->priority, S_OR(resource->description, ""));
3111 ao2_ref(resource, -1);
3113 /* This will get hit by presence coming in for an unknown resource, and also when a resource goes away */
3115 ao2_ref(resource, -1);
3118 manager_event(EVENT_FLAG_USER, "JabberStatus",
3119 "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
3120 client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
3123 ao2_unlock(buddy->resources);
3130 /*! \brief Internal function called when a subscription message is received */
3131 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg,iks *node, ikspak *pak)
3133 struct ast_xmpp_buddy *buddy;
3135 switch (pak->subtype) {
3136 case IKS_TYPE_SUBSCRIBE:
3137 if (ast_test_flag(&cfg->flags, XMPP_AUTOREGISTER)) {
3138 iks *presence, *status = NULL;
3140 if ((presence = iks_new("presence")) && (status = iks_new("status"))) {
3141 iks_insert_attrib(presence, "type", "subscribed");
3142 iks_insert_attrib(presence, "to", pak->from->full);
3143 iks_insert_attrib(presence, "from", client->jid->full);
3146 iks_insert_attrib(presence, "id", pak->id);
3149 iks_insert_cdata(status, "Asterisk has approved your subscription", 0);
3150 iks_insert_node(presence, status);
3152 if (ast_xmpp_client_send(client, presence)) {
3153 ast_log(LOG_ERROR, "Could not send subscription acceptance to '%s' from client '%s'\n",
3154 pak->from->partial, client->name);
3157 ast_log(LOG_ERROR, "Could not allocate presence stanzas for accepting subscription from '%s' to client '%s'\n",
3158 pak->from->partial, client->name);
3162 iks_delete(presence);
3165 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
3166 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3168 /* This purposely flows through so we have the subscriber amongst our buddies */
3169 case IKS_TYPE_SUBSCRIBED:
3170 ao2_lock(client->buddies);
3172 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY | OBJ_NOLOCK))) {
3173 buddy = xmpp_client_create_buddy(client->buddies, pak->from->partial);
3177 ast_log(LOG_WARNING, "Could not find or create buddy '%s' on client '%s'\n",
3178 pak->from->partial, client->name);
3183 ao2_unlock(client->buddies);
3193 /*! \brief Action hook for when things occur */
3194 static int xmpp_action_hook(void *data, int type, iks *node)
3196 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
3197 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3198 struct ast_xmpp_client *client = data;
3203 ast_log(LOG_ERROR, "xmpp_action_hook was called without a packet\n");
3207 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
3211 /* If the client is disconnecting ignore everything */
3212 if (client->state == XMPP_STATE_DISCONNECTING) {
3216 pak = iks_packet(node);
3218 /* work around iksemel's impossibility to recognize node names
3219 * containing a colon. Set the namespace of the corresponding
3220 * node accordingly. */
3221 if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
3222 char *node_ns = NULL;
3223 char attr[XMPP_MAX_ATTRLEN];
3224 char *node_name = iks_name(iks_child(node));
3225 char *aux = strchr(node_name, ':') + 1;
3226 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
3227 node_ns = iks_find_attrib(iks_child(node), attr);
3230 pak->query = iks_child(node);
3234 /* Process through any state handlers */
3235 for (i = 0; i < ARRAY_LEN(xmpp_state_handlers); i++) {
3236 if ((xmpp_state_handlers[i].state == client->state) && (xmpp_state_handlers[i].component == (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? 1 : 0))) {
3237 if (xmpp_state_handlers[i].handler(client, clientcfg, type, node)) {
3238 /* If the handler wants us to stop now, do so */