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 ast_cond_t message_received_condition;
391 static ast_mutex_t messagelock;
393 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
395 /*! \brief Destructor function for configuration */
396 static void ast_xmpp_client_config_destructor(void *obj)
398 struct ast_xmpp_client_config *cfg = obj;
399 ast_string_field_free_memory(cfg);
400 ao2_cleanup(cfg->client);
401 ao2_cleanup(cfg->buddies);
404 /*! \brief Destroy function for XMPP messages */
405 static void xmpp_message_destroy(struct ast_xmpp_message *message)
408 ast_free(message->from);
410 if (message->message) {
411 ast_free(message->message);
417 /*! \brief Destructor callback function for XMPP client */
418 static void xmpp_client_destructor(void *obj)
420 struct ast_xmpp_client *client = obj;
421 struct ast_xmpp_message *message;
423 ast_xmpp_client_disconnect(client);
426 iks_stack_delete(client->stack);
429 ao2_cleanup(client->buddies);
431 while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
432 xmpp_message_destroy(message);
434 AST_LIST_HEAD_DESTROY(&client->messages);
437 /*! \brief Hashing function for XMPP buddy */
438 static int xmpp_buddy_hash(const void *obj, const int flags)
440 const struct ast_xmpp_buddy *buddy = obj;
441 const char *id = obj;
443 return ast_str_hash(flags & OBJ_KEY ? id : buddy->id);
446 /*! \brief Comparator function for XMPP buddy */
447 static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
449 struct ast_xmpp_buddy *buddy1 = obj, *buddy2 = arg;
450 const char *id = arg;
452 return !strcmp(buddy1->id, flags & OBJ_KEY ? id : buddy2->id) ? CMP_MATCH | CMP_STOP : 0;
455 /*! \brief Allocator function for ast_xmpp_client */
456 static struct ast_xmpp_client *xmpp_client_alloc(const char *name)
458 struct ast_xmpp_client *client;
460 if (!(client = ao2_alloc(sizeof(*client), xmpp_client_destructor))) {
464 AST_LIST_HEAD_INIT(&client->messages);
465 client->thread = AST_PTHREADT_NULL;
467 if (!(client->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
468 ast_log(LOG_ERROR, "Could not initialize buddy container for '%s'\n", name);
473 if (ast_string_field_init(client, 512)) {
474 ast_log(LOG_ERROR, "Could not initialize stringfields for '%s'\n", name);
479 if (!(client->stack = iks_stack_new(8192, 8192))) {
480 ast_log(LOG_ERROR, "Could not create an Iksemel stack for '%s'\n", name);
485 ast_string_field_set(client, name, name);
487 client->timeout = 50;
488 client->state = XMPP_STATE_DISCONNECTED;
489 ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
494 /*! \brief Find function for configuration */
495 static void *xmpp_config_find(struct ao2_container *tmp_container, const char *category)
497 return ao2_find(tmp_container, category, OBJ_KEY);
500 /*! \brief Look up existing client or create a new one */
501 static void *xmpp_client_find_or_create(const char *category)
503 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
504 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
506 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, category))) {
507 return xmpp_client_alloc(category);
510 ao2_ref(clientcfg->client, +1);
511 return clientcfg->client;
514 /*! \brief Allocator function for configuration */
515 static void *ast_xmpp_client_config_alloc(const char *cat)
517 struct ast_xmpp_client_config *cfg;
519 if (!(cfg = ao2_alloc(sizeof(*cfg), ast_xmpp_client_config_destructor))) {
523 if (ast_string_field_init(cfg, 512)) {
528 if (!(cfg->client = xmpp_client_find_or_create(cat))) {
533 if (!(cfg->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
538 ast_string_field_set(cfg, name, cat);
543 /*! \brief Destructor for XMPP configuration */
544 static void xmpp_config_destructor(void *obj)
546 struct xmpp_config *cfg = obj;
547 ao2_cleanup(cfg->global);
548 ao2_cleanup(cfg->clients);
551 /*! \brief Hashing function for configuration */
552 static int xmpp_config_hash(const void *obj, const int flags)
554 const struct ast_xmpp_client_config *cfg = obj;
555 const char *name = (flags & OBJ_KEY) ? obj : cfg->name;
556 return ast_str_case_hash(name);
559 /*! \brief Comparator function for configuration */
560 static int xmpp_config_cmp(void *obj, void *arg, int flags)
562 struct ast_xmpp_client_config *one = obj, *two = arg;
563 const char *match = (flags & OBJ_KEY) ? arg : two->name;
564 return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
567 /*! \brief Allocator for XMPP configuration */
568 static void *xmpp_config_alloc(void)
570 struct xmpp_config *cfg;
572 if (!(cfg = ao2_alloc(sizeof(*cfg), xmpp_config_destructor))) {
576 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
580 ast_set_flag(&cfg->global->general, XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE);
582 if (!(cfg->clients = ao2_container_alloc(1, xmpp_config_hash, xmpp_config_cmp))) {
592 static int xmpp_config_prelink(void *newitem)
594 struct ast_xmpp_client_config *clientcfg = newitem;
595 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
596 RAII_VAR(struct ast_xmpp_client_config *, oldclientcfg, NULL, ao2_cleanup);
598 if (ast_strlen_zero(clientcfg->user)) {
599 ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
601 } else if (ast_strlen_zero(clientcfg->password)) {
602 ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
604 } else if (ast_strlen_zero(clientcfg->server)) {
605 ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
609 /* If this is a new connection force a reconnect */
610 if (!cfg || !cfg->clients || !(oldclientcfg = xmpp_config_find(cfg->clients, clientcfg->name))) {
611 clientcfg->client->reconnect = 1;
615 /* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
616 if (strcmp(clientcfg->user, oldclientcfg->user) ||
617 strcmp(clientcfg->password, oldclientcfg->password) ||
618 strcmp(clientcfg->server, oldclientcfg->server) ||
619 (clientcfg->port != oldclientcfg->port) ||
620 (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
621 (clientcfg->priority != oldclientcfg->priority)) {
622 clientcfg->client->reconnect = 1;
624 clientcfg->client->reconnect = 0;
630 static void xmpp_config_post_apply(void)
632 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
634 ao2_callback(cfg->clients, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_config_post_apply, NULL);
637 static struct aco_type global_option = {
639 .item_offset = offsetof(struct xmpp_config, global),
640 .category_match = ACO_WHITELIST,
641 .category = "^general$",
644 struct aco_type *global_options[] = ACO_TYPES(&global_option);
646 static struct aco_type client_option = {
648 .category_match = ACO_BLACKLIST,
649 .category = "^(general)$",
650 .item_alloc = ast_xmpp_client_config_alloc,
651 .item_find = xmpp_config_find,
652 .item_prelink = xmpp_config_prelink,
653 .item_offset = offsetof(struct xmpp_config, clients),
656 struct aco_type *client_options[] = ACO_TYPES(&client_option);
658 struct aco_file res_xmpp_conf = {
659 .filename = "xmpp.conf",
660 .alias = "jabber.conf",
661 .types = ACO_TYPES(&global_option, &client_option),
664 CONFIG_INFO_STANDARD(cfg_info, globals, xmpp_config_alloc,
665 .files = ACO_FILES(&res_xmpp_conf),
666 .post_apply_config = xmpp_config_post_apply,
669 /*! \brief Destructor callback function for XMPP resource */
670 static void xmpp_resource_destructor(void *obj)
672 struct ast_xmpp_resource *resource = obj;
674 if (resource->description) {
675 ast_free(resource->description);
679 /*! \brief Hashing function for XMPP resource */
680 static int xmpp_resource_hash(const void *obj, const int flags)
682 const struct ast_xmpp_resource *resource = obj;
684 return flags & OBJ_KEY ? -1 : resource->priority;
687 /*! \brief Comparator function for XMPP resource */
688 static int xmpp_resource_cmp(void *obj, void *arg, int flags)
690 struct ast_xmpp_resource *resource1 = obj, *resource2 = arg;
691 const char *resource = arg;
693 return !strcmp(resource1->resource, flags & OBJ_KEY ? resource : resource2->resource) ? CMP_MATCH | CMP_STOP : 0;
696 /*! \brief Destructor callback function for XMPP buddy */
697 static void xmpp_buddy_destructor(void *obj)
699 struct ast_xmpp_buddy *buddy = obj;
701 if (buddy->resources) {
702 ao2_ref(buddy->resources, -1);
706 /*! \brief Helper function which returns whether an XMPP client connection is secure or not */
707 static int xmpp_is_secure(struct ast_xmpp_client *client)
710 return client->stream_flags & SECURE;
716 struct ast_xmpp_client *ast_xmpp_client_find(const char *name)
718 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
719 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
721 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
725 ao2_ref(clientcfg->client, +1);
726 return clientcfg->client;
729 void ast_xmpp_client_unref(struct ast_xmpp_client *client)
734 void ast_xmpp_client_lock(struct ast_xmpp_client *client)
739 void ast_xmpp_client_unlock(struct ast_xmpp_client *client)
744 /*! \brief Internal function used to send a message to a user or chatroom */
745 static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
747 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
748 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
750 char from[XMPP_MAX_JIDLEN];
753 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
754 !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
758 if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
759 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
761 snprintf(from, sizeof(from), "%s", client->jid->full);
764 iks_insert_attrib(message_packet, "from", from);
766 res = ast_xmpp_client_send(client, message_packet);
768 iks_delete(message_packet);
773 int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
775 return xmpp_client_send_message(client, 0, NULL, user, message);
778 int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
781 iks *invite, *body = NULL, *namespace = NULL;
783 if (!(invite = iks_new("message")) || !(body = iks_new("body")) || !(namespace = iks_new("x"))) {
788 iks_insert_attrib(invite, "to", user);
789 ast_xmpp_client_lock(client);
790 iks_insert_attrib(invite, "id", client->mid);
791 ast_xmpp_increment_mid(client->mid);
792 ast_xmpp_client_unlock(client);
793 iks_insert_cdata(body, message, 0);
794 iks_insert_node(invite, body);
795 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
796 iks_insert_attrib(namespace, "jid", room);
797 iks_insert_node(invite, namespace);
799 res = ast_xmpp_client_send(client, invite);
802 iks_delete(namespace);
809 static int xmpp_client_set_group_presence(struct ast_xmpp_client *client, const char *room, int level, const char *nick)
811 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
812 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
814 iks *presence = NULL, *x = NULL;
815 char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
817 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
818 !(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
823 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
824 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
825 snprintf(roomid, sizeof(roomid), "%s/%s", room, nick);
827 snprintf(from, sizeof(from), "%s", client->jid->full);
828 snprintf(roomid, sizeof(roomid), "%s/%s", room, S_OR(nick, client->jid->user));
831 iks_insert_attrib(presence, "to", roomid);
832 iks_insert_attrib(presence, "from", from);
833 iks_insert_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
834 iks_insert_node(presence, x);
836 res = ast_xmpp_client_send(client, presence);
840 iks_delete(presence);
845 int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
847 return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
850 int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
852 return xmpp_client_send_message(client, 1, nickname, address, message);
855 int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
857 return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
860 void ast_xmpp_increment_mid(char *mid)
864 for (i = strlen(mid) - 1; i >= 0; i--) {
875 * \brief Create an IQ packet
876 * \param client the configured XMPP client we use to connect to a XMPP server
877 * \param type the type of IQ packet to create
880 static iks* xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
882 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
883 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
886 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
887 !(request = iks_new("iq"))) {
891 if (!ast_strlen_zero(clientcfg->pubsubnode)) {
892 iks_insert_attrib(request, "to", clientcfg->pubsubnode);
895 iks_insert_attrib(request, "from", client->jid->full);
896 iks_insert_attrib(request, "type", type);
897 ast_xmpp_client_lock(client);
898 ast_xmpp_increment_mid(client->mid);
899 iks_insert_attrib(request, "id", client->mid);
900 ast_xmpp_client_unlock(client);
906 * \brief Build the skeleton of a publish
907 * \param client the configured XMPP client we use to connect to a XMPP server
908 * \param node Name of the node that will be published to
912 static iks* xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node,
913 const char *event_type)
915 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
916 iks *request, *pubsub, *publish, *item;
918 if (!cfg || !cfg->global || !(request = xmpp_pubsub_iq_create(client, "set"))) {
922 pubsub = iks_insert(request, "pubsub");
923 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
924 publish = iks_insert(pubsub, "publish");
925 iks_insert_attrib(publish, "node", ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248) ? node : event_type);
926 item = iks_insert(publish, "item");
927 iks_insert_attrib(item, "id", node);
933 static iks* xmpp_pubsub_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
935 iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
936 *field_deliver_payload, *field_persist_items, *field_access_model,
937 *field_pubsub_collection;
938 configure = iks_insert(pubsub, "configure");
939 x = iks_insert(configure, "x");
940 iks_insert_attrib(x, "xmlns", "jabber:x:data");
941 iks_insert_attrib(x, "type", "submit");
942 field_owner = iks_insert(x, "field");
943 iks_insert_attrib(field_owner, "var", "FORM_TYPE");
944 iks_insert_attrib(field_owner, "type", "hidden");
945 iks_insert_cdata(iks_insert(field_owner, "value"),
946 "http://jabber.org/protocol/pubsub#owner", 39);
948 field_node_type = iks_insert(x, "field");
949 iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
950 iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
952 field_node_config = iks_insert(x, "field");
953 iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
954 iks_insert_attrib(field_node_config, "type", "hidden");
955 iks_insert_cdata(iks_insert(field_node_config, "value"),
956 "http://jabber.org/protocol/pubsub#node_config", 45);
957 field_deliver_payload = iks_insert(x, "field");
958 iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
959 iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
960 field_persist_items = iks_insert(x, "field");
961 iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
962 iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
963 field_access_model = iks_insert(x, "field");
964 iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
965 iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
966 if (node_type && !strcasecmp(node_type, "leaf")) {
967 field_pubsub_collection = iks_insert(x, "field");
968 iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
969 iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
970 strlen(collection_name));
976 * \brief Add Owner affiliations for pubsub node
977 * \param client the configured XMPP client we use to connect to a XMPP server
978 * \param node the name of the node to which to add affiliations
981 static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
983 iks *modify_affiliates = xmpp_pubsub_iq_create(client, "set");
984 iks *pubsub, *affiliations, *affiliate;
985 struct ao2_iterator i;
986 struct ast_xmpp_buddy *buddy;
988 if (!modify_affiliates) {
989 ast_log(LOG_ERROR, "Could not create IQ for creating affiliations on client '%s'\n", client->name);
993 pubsub = iks_insert(modify_affiliates, "pubsub");
994 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
995 affiliations = iks_insert(pubsub, "affiliations");
996 iks_insert_attrib(affiliations, "node", node);
998 i = ao2_iterator_init(client->buddies, 0);
999 while ((buddy = ao2_iterator_next(&i))) {
1000 affiliate = iks_insert(affiliations, "affiliation");
1001 iks_insert_attrib(affiliate, "jid", buddy->id);
1002 iks_insert_attrib(affiliate, "affiliation", "owner");
1005 ao2_iterator_destroy(&i);
1007 ast_xmpp_client_send(client, modify_affiliates);
1008 iks_delete(modify_affiliates);
1012 * \brief Create a pubsub node
1013 * \param client the configured XMPP client we use to connect to a XMPP server
1014 * \param node_type the type of node to create
1015 * \param name the name of the node to create
1018 static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const
1019 char *name, const char *collection_name)
1021 iks *node, *pubsub, *create;
1023 if (!(node = xmpp_pubsub_iq_create(client, "set"))) {
1027 pubsub = iks_insert(node, "pubsub");
1028 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1029 create = iks_insert(pubsub, "create");
1030 iks_insert_attrib(create, "node", name);
1031 xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
1032 ast_xmpp_client_send(client, node);
1033 xmpp_pubsub_create_affiliations(client, name);
1038 * \brief Delete a PubSub node
1039 * \param client the configured XMPP client we use to connect to a XMPP server
1040 * \param node_name the name of the node to delete
1043 static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
1045 iks *request, *pubsub, *delete;
1047 if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
1051 pubsub = iks_insert(request, "pubsub");
1052 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
1053 delete = iks_insert(pubsub, "delete");
1054 iks_insert_attrib(delete, "node", node_name);
1055 ast_xmpp_client_send(client, request);
1059 iks_delete(request);
1063 * \brief Create a PubSub collection node.
1064 * \param client the configured XMPP client we use to connect to a XMPP server
1065 * \param collection_name The name to use for this collection
1068 static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
1070 xmpp_pubsub_create_node(client, "collection", collection_name, NULL);
1075 * \brief Create a PubSub leaf node.
1076 * \param client the configured XMPP client we use to connect to a XMPP server
1077 * \param leaf_name The name to use for this collection
1080 static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name,
1081 const char *leaf_name)
1083 xmpp_pubsub_create_node(client, "leaf", leaf_name, collection_name);
1087 * \brief Publish MWI to a PubSub node
1088 * \param client the configured XMPP client we use to connect to a XMPP server
1089 * \param device the name of the device whose state to publish
1090 * \param device_state the state to publish
1093 static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox,
1094 const char *context, const char *oldmsgs, const char *newmsgs)
1096 char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT], eid_str[20];
1097 iks *mailbox_node, *request;
1099 snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
1101 if (!(request = xmpp_pubsub_build_publish_skeleton(client, full_mailbox, "message_waiting"))) {
1105 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1106 mailbox_node = iks_insert(request, "mailbox");
1107 iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
1108 iks_insert_attrib(mailbox_node, "eid", eid_str);
1109 iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
1110 iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
1112 ast_xmpp_client_send(client, iks_root(request));
1114 iks_delete(request);
1118 * \brief Publish device state to a PubSub node
1119 * \param client the configured XMPP client we use to connect to a XMPP server
1120 * \param device the name of the device whose state to publish
1121 * \param device_state the state to publish
1124 static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device,
1125 const char *device_state)
1127 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1128 iks *request, *state;
1131 if (!cfg || !cfg->global || !(request = xmpp_pubsub_build_publish_skeleton(client, device, "device_state"))) {
1135 if (ast_test_flag(&cfg->global->pubsub, XMPP_PUBSUB_AUTOCREATE)) {
1136 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1137 xmpp_pubsub_create_node(client, "leaf", device, "device_state");
1139 xmpp_pubsub_create_node(client, NULL, device, NULL);
1143 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1144 state = iks_insert(request, "state");
1145 iks_insert_attrib(state, "xmlns", "http://asterisk.org");
1146 iks_insert_attrib(state, "eid", eid_str);
1147 iks_insert_cdata(state, device_state, strlen(device_state));
1148 ast_xmpp_client_send(client, iks_root(request));
1149 iks_delete(request);
1153 * \brief Callback function for MWI events
1155 * \param data void pointer to ast_client structure
1158 static void xmpp_pubsub_mwi_cb(const struct ast_event *ast_event, void *data)
1160 struct ast_xmpp_client *client = data;
1161 const char *mailbox, *context;
1162 char oldmsgs[10], newmsgs[10];
1164 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
1165 /* If the event didn't originate from this server, don't send it back out. */
1166 ast_debug(1, "Returning here\n");
1170 mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
1171 context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
1172 snprintf(oldmsgs, sizeof(oldmsgs), "%d",
1173 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
1174 snprintf(newmsgs, sizeof(newmsgs), "%d",
1175 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
1176 xmpp_pubsub_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
1180 * \brief Callback function for device state events
1182 * \param data void pointer to ast_client structure
1185 static void xmpp_pubsub_devstate_cb(const struct ast_event *ast_event, void *data)
1187 struct ast_xmpp_client *client = data;
1188 const char *device, *device_state;
1190 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
1191 /* If the event didn't originate from this server, don't send it back out. */
1192 ast_debug(1, "Returning here\n");
1196 device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
1197 device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
1198 xmpp_pubsub_publish_device_state(client, device, device_state);
1202 * \brief Unsubscribe from a PubSub node
1203 * \param client the configured XMPP client we use to connect to a XMPP server
1204 * \param node the name of the node to which to unsubscribe from
1207 static void xmpp_pubsub_unsubscribe(struct ast_xmpp_client *client, const char *node)
1209 iks *request = xmpp_pubsub_iq_create(client, "set");
1210 iks *pubsub, *unsubscribe;
1213 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub unsubscription on client '%s'\n", client->name);
1217 pubsub = iks_insert(request, "pubsub");
1218 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1219 unsubscribe = iks_insert(pubsub, "unsubscribe");
1220 iks_insert_attrib(unsubscribe, "jid", client->jid->partial);
1221 iks_insert_attrib(unsubscribe, "node", node);
1223 ast_xmpp_client_send(client, request);
1224 iks_delete(request);
1228 * \brief Subscribe to a PubSub node
1229 * \param client the configured XMPP client we use to connect to a XMPP server
1230 * \param node the name of the node to which to subscribe
1233 static void xmpp_pubsub_subscribe(struct ast_xmpp_client *client, const char *node)
1235 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1236 iks *request = xmpp_pubsub_iq_create(client, "set");
1237 iks *pubsub, *subscribe;
1239 if (!cfg || !cfg->global || !request) {
1240 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub subscription on client '%s'\n", client->name);
1244 pubsub = iks_insert(request, "pubsub");
1245 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1246 subscribe = iks_insert(pubsub, "subscribe");
1247 iks_insert_attrib(subscribe, "jid", client->jid->partial);
1248 iks_insert_attrib(subscribe, "node", node);
1249 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1250 iks *options, *x, *sub_options, *sub_type, *sub_depth, *sub_expire;
1251 options = iks_insert(pubsub, "options");
1252 x = iks_insert(options, "x");
1253 iks_insert_attrib(x, "xmlns", "jabber:x:data");
1254 iks_insert_attrib(x, "type", "submit");
1255 sub_options = iks_insert(x, "field");
1256 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
1257 iks_insert_attrib(sub_options, "type", "hidden");
1258 iks_insert_cdata(iks_insert(sub_options, "value"),
1259 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
1260 sub_type = iks_insert(x, "field");
1261 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
1262 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
1263 sub_depth = iks_insert(x, "field");
1264 iks_insert_attrib(sub_depth, "var", "pubsub#subscription_depth");
1265 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
1266 sub_expire = iks_insert(x, "field");
1267 iks_insert_attrib(sub_expire, "var", "pubsub#expire");
1268 iks_insert_cdata(iks_insert(sub_expire, "value"), "presence", 8);
1270 ast_xmpp_client_send(client, request);
1271 iks_delete(request);
1275 * \brief Callback for handling PubSub events
1276 * \param data void pointer to ast_xmpp_client structure
1277 * \return IKS_FILTER_EAT
1279 static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
1281 char *item_id, *device_state, *context;
1282 int oldmsgs, newmsgs;
1283 iks *item, *item_content;
1284 struct ast_eid pubsub_eid;
1285 struct ast_event *event;
1286 item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
1288 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
1289 return IKS_FILTER_EAT;
1291 item_id = iks_find_attrib(item, "id");
1292 item_content = iks_child(item);
1293 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
1294 if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
1295 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
1296 return IKS_FILTER_EAT;
1298 if (!strcasecmp(iks_name(item_content), "state")) {
1299 device_state = iks_find_cdata(item, "state");
1300 if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
1301 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
1302 AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
1303 AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
1304 AST_EVENT_IE_END))) {
1305 return IKS_FILTER_EAT;
1307 } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
1308 context = strsep(&item_id, "@");
1309 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
1310 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
1311 if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
1312 AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
1313 AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
1314 AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
1315 AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
1316 &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
1317 return IKS_FILTER_EAT;
1320 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
1321 iks_name(item_content));
1322 return IKS_FILTER_EAT;
1324 ast_event_queue_and_cache(event);
1325 return IKS_FILTER_EAT;
1328 static int xmpp_pubsub_handle_error(void *data, ikspak *pak)
1330 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1331 char *node_name, *error;
1333 iks *orig_request, *orig_pubsub = iks_find(pak->x, "pubsub");
1334 struct ast_xmpp_client *client = data;
1336 if (!cfg || !cfg->global) {
1337 ast_log(LOG_ERROR, "No global configuration available\n");
1338 return IKS_FILTER_EAT;
1342 ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
1343 return IKS_FILTER_EAT;
1346 orig_request = iks_child(orig_pubsub);
1347 error = iks_find_attrib(iks_find(pak->x, "error"), "code");
1348 node_name = iks_find_attrib(orig_request, "node");
1350 if (!sscanf(error, "%30d", &error_num)) {
1351 return IKS_FILTER_EAT;
1354 if (error_num > 399 && error_num < 500 && error_num != 404) {
1356 "Error performing operation on PubSub node %s, %s.\n", node_name, error);
1357 return IKS_FILTER_EAT;
1358 } else if (error_num > 499 && error_num < 600) {
1359 ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
1360 return IKS_FILTER_EAT;
1363 if (!strcasecmp(iks_name(orig_request), "publish")) {
1366 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1367 if (iks_find(iks_find(orig_request, "item"), "state")) {
1368 xmpp_pubsub_create_leaf(client, "device_state", node_name);
1369 } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
1370 xmpp_pubsub_create_leaf(client, "message_waiting", node_name);
1373 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1376 if ((request = xmpp_pubsub_iq_create(client, "set"))) {
1377 iks_insert_node(request, orig_pubsub);
1378 ast_xmpp_client_send(client, request);
1379 iks_delete(request);
1381 ast_log(LOG_ERROR, "PubSub publish could not create IQ\n");
1384 return IKS_FILTER_EAT;
1385 } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
1386 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1387 xmpp_pubsub_create_collection(client, node_name);
1389 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1393 return IKS_FILTER_EAT;
1397 * \brief Initialize collections for event distribution
1398 * \param client the configured XMPP client we use to connect to a XMPP server
1401 static void xmpp_init_event_distribution(struct ast_xmpp_client *client)
1403 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1404 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1406 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
1410 xmpp_pubsub_unsubscribe(client, "device_state");
1411 xmpp_pubsub_unsubscribe(client, "message_waiting");
1413 if (!(client->mwi_sub = ast_event_subscribe(AST_EVENT_MWI, xmpp_pubsub_mwi_cb, "xmpp_pubsub_mwi_subscription",
1414 client, AST_EVENT_IE_END))) {
1418 if (ast_enable_distributed_devstate()) {
1423 if (!(client->device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
1424 xmpp_pubsub_devstate_cb, "xmpp_pubsub_devstate_subscription", client, AST_EVENT_IE_END))) {
1425 ast_event_unsubscribe(client->mwi_sub);
1426 client->mwi_sub = NULL;
1430 ast_event_dump_cache(client->device_state_sub);
1432 xmpp_pubsub_subscribe(client, "device_state");
1433 xmpp_pubsub_subscribe(client, "message_waiting");
1434 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_event, client, IKS_RULE_TYPE,
1435 IKS_PAK_MESSAGE, IKS_RULE_FROM, clientcfg->pubsubnode, IKS_RULE_DONE);
1436 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_error, client, IKS_RULE_TYPE,
1437 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
1441 /*! \brief Internal astobj2 callback function which returns the first resource, which is the highest priority one */
1442 static int xmpp_resource_immediate(void *obj, void *arg, int flags)
1444 return CMP_MATCH | CMP_STOP;
1449 * \brief Dial plan function status(). puts the status of watched user
1450 * into a channel variable.
1451 * \param chan ast_channel
1456 static int xmpp_status_exec(struct ast_channel *chan, const char *data)
1458 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1459 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1460 struct ast_xmpp_buddy *buddy;
1461 struct ast_xmpp_resource *resource;
1462 char *s = NULL, status[2];
1464 static int deprecation_warning = 0;
1465 AST_DECLARE_APP_ARGS(args,
1466 AST_APP_ARG(sender);
1468 AST_APP_ARG(variable);
1470 AST_DECLARE_APP_ARGS(jid,
1471 AST_APP_ARG(screenname);
1472 AST_APP_ARG(resource);
1475 if (deprecation_warning++ % 10 == 0) {
1476 ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
1479 if (ast_strlen_zero(data)) {
1480 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
1483 s = ast_strdupa(data);
1484 AST_STANDARD_APP_ARGS(args, s);
1486 if (args.argc != 3) {
1487 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
1491 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1492 if (jid.argc < 1 || jid.argc > 2) {
1493 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1497 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1498 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1502 if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
1503 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
1507 if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
1508 resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
1514 stat = resource->status;
1515 ao2_ref(resource, -1);
1517 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
1520 snprintf(status, sizeof(status), "%d", stat);
1521 pbx_builtin_setvar_helper(chan, args.variable, status);
1528 * \brief Dial plan funtcion to retrieve the status of a buddy.
1529 * \param channel The associated ast_channel, if there is one
1530 * \param data The account, buddy JID, and optional timeout
1533 * \retval -1 failure
1535 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1537 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1538 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1539 struct ast_xmpp_buddy *buddy;
1540 struct ast_xmpp_resource *resource;
1542 AST_DECLARE_APP_ARGS(args,
1543 AST_APP_ARG(sender);
1546 AST_DECLARE_APP_ARGS(jid,
1547 AST_APP_ARG(screenname);
1548 AST_APP_ARG(resource);
1551 if (ast_strlen_zero(data)) {
1552 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
1555 AST_STANDARD_APP_ARGS(args, data);
1557 if (args.argc != 2) {
1558 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
1562 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1563 if (jid.argc < 1 || jid.argc > 2) {
1564 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1568 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1569 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1573 if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
1574 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
1578 if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
1579 resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
1585 stat = resource->status;
1586 ao2_ref(resource, -1);
1588 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
1591 snprintf(buf, buflen, "%d", stat);
1596 static struct ast_custom_function jabberstatus_function = {
1597 .name = "JABBER_STATUS",
1598 .read = acf_jabberstatus_read,
1602 * \brief Application to join a chat room
1603 * \param chan ast_channel
1604 * \param data Data is sender|jid|nickname.
1608 static int xmpp_join_exec(struct ast_channel *chan, const char *data)
1610 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1611 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1612 char *s, nick[XMPP_MAX_RESJIDLEN];
1613 AST_DECLARE_APP_ARGS(args,
1614 AST_APP_ARG(sender);
1619 if (ast_strlen_zero(data)) {
1620 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1623 s = ast_strdupa(data);
1625 AST_STANDARD_APP_ARGS(args, s);
1626 if (args.argc < 2 || args.argc > 3) {
1627 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1631 if (strchr(args.jid, '/')) {
1632 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1636 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1637 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1641 if (ast_strlen_zero(args.nick)) {
1642 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1643 snprintf(nick, sizeof(nick), "asterisk");
1645 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1648 snprintf(nick, sizeof(nick), "%s", args.nick);
1651 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1652 ast_xmpp_chatroom_join(clientcfg->client, args.jid, nick);
1654 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1661 * \brief Application to leave a chat room
1662 * \param chan ast_channel
1663 * \param data Data is sender|jid|nickname.
1667 static int xmpp_leave_exec(struct ast_channel *chan, const char *data)
1669 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1670 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1671 char *s, nick[XMPP_MAX_RESJIDLEN];
1672 AST_DECLARE_APP_ARGS(args,
1673 AST_APP_ARG(sender);
1678 if (ast_strlen_zero(data)) {
1679 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1682 s = ast_strdupa(data);
1684 AST_STANDARD_APP_ARGS(args, s);
1685 if (args.argc < 2 || args.argc > 3) {
1686 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1690 if (strchr(args.jid, '/')) {
1691 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1695 if (ast_strlen_zero(args.jid) || !strchr(args.jid, '@')) {
1696 ast_log(LOG_ERROR, "No jabber ID specified\n");
1700 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1701 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1705 if (ast_strlen_zero(args.nick)) {
1706 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1707 snprintf(nick, sizeof(nick), "asterisk");
1709 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1712 snprintf(nick, sizeof(nick), "%s", args.nick);
1715 ast_xmpp_chatroom_leave(clientcfg->client, args.jid, nick);
1722 * \brief Dial plan function to send a message.
1723 * \param chan ast_channel
1724 * \param data Data is account,jid,message.
1726 * \retval -1 failure
1728 static int xmpp_send_exec(struct ast_channel *chan, const char *data)
1730 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1731 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1733 AST_DECLARE_APP_ARGS(args,
1734 AST_APP_ARG(sender);
1735 AST_APP_ARG(recipient);
1736 AST_APP_ARG(message);
1739 if (ast_strlen_zero(data)) {
1740 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1743 s = ast_strdupa(data);
1745 AST_STANDARD_APP_ARGS(args, s);
1747 if ((args.argc < 3) || ast_strlen_zero(args.message) || !strchr(args.recipient, '2')) {
1748 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1752 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1753 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1757 ast_xmpp_client_send_message(clientcfg->client, args.recipient, args.message);
1763 * \brief Application to send a message to a groupchat.
1764 * \param chan ast_channel
1765 * \param data Data is sender|groupchat|message.
1769 static int xmpp_sendgroup_exec(struct ast_channel *chan, const char *data)
1771 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1772 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1773 char *s, nick[XMPP_MAX_RESJIDLEN];
1774 AST_DECLARE_APP_ARGS(args,
1775 AST_APP_ARG(sender);
1776 AST_APP_ARG(groupchat);
1777 AST_APP_ARG(message);
1781 if (ast_strlen_zero(data)) {
1782 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1785 s = ast_strdupa(data);
1787 AST_STANDARD_APP_ARGS(args, s);
1788 if ((args.argc < 3) || (args.argc > 4) || ast_strlen_zero(args.message) || !strchr(args.groupchat, '@')) {
1789 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1793 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1794 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1798 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1799 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1800 snprintf(nick, sizeof(nick), "asterisk");
1802 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1805 snprintf(nick, sizeof(nick), "%s", args.nick);
1808 ast_xmpp_chatroom_send(clientcfg->client, nick, args.groupchat, args.message);
1815 * \brief Dial plan function to receive a message.
1816 * \param channel The associated ast_channel, if there is one
1817 * \param data The account, JID, and optional timeout
1820 * \retval -1 failure
1822 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1824 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1825 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1826 char *aux = NULL, *parse = NULL;
1827 int timeout, jidlen, resourcelen, found = 0;
1828 struct timeval start;
1830 struct ast_xmpp_message *message;
1831 AST_DECLARE_APP_ARGS(args,
1832 AST_APP_ARG(account);
1834 AST_APP_ARG(timeout);
1836 AST_DECLARE_APP_ARGS(jid,
1837 AST_APP_ARG(screenname);
1838 AST_APP_ARG(resource);
1841 if (ast_strlen_zero(data)) {
1842 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1846 parse = ast_strdupa(data);
1847 AST_STANDARD_APP_ARGS(args, parse);
1849 if (args.argc < 2 || args.argc > 3) {
1850 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1854 parse = ast_strdupa(args.jid);
1855 AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
1856 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > XMPP_MAX_JIDLEN) {
1857 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
1861 if (ast_strlen_zero(args.timeout)) {
1864 sscanf(args.timeout, "%d", &timeout);
1866 ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
1871 jidlen = strlen(jid.screenname);
1872 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
1874 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.account))) {
1875 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
1879 ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
1881 start = ast_tvnow();
1883 if (ast_autoservice_start(chan) < 0) {
1884 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
1888 /* search the messages list, grab the first message that matches with
1889 * the from JID we're expecting, and remove it from the messages list */
1890 while (diff < timeout) {
1891 struct timespec ts = { 0, };
1892 struct timeval wait;
1895 wait = ast_tvadd(start, ast_tv(timeout, 0));
1896 ts.tv_sec = wait.tv_sec;
1897 ts.tv_nsec = wait.tv_usec * 1000;
1899 /* wait up to timeout seconds for an incoming message */
1900 ast_mutex_lock(&messagelock);
1901 if (AST_LIST_EMPTY(&clientcfg->client->messages)) {
1902 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
1904 ast_mutex_unlock(&messagelock);
1905 if (res == ETIMEDOUT) {
1906 ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
1910 AST_LIST_LOCK(&clientcfg->client->messages);
1911 AST_LIST_TRAVERSE_SAFE_BEGIN(&clientcfg->client->messages, message, list) {
1912 if (jid.argc == 1) {
1913 /* no resource provided, compare bare JIDs */
1914 if (strncasecmp(jid.screenname, message->from, jidlen)) {
1918 /* resource appended, compare bare JIDs and resources */
1919 char *resource = strchr(message->from, '/');
1920 if (!resource || strlen(resource) == 0) {
1921 ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", message->from);
1922 if (strncasecmp(jid.screenname, message->from, jidlen)) {
1927 if (strncasecmp(jid.screenname, message->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
1932 /* check if the message is not too old */
1933 if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
1934 ast_debug(3, "Found old message from %s, deleting it\n", message->from);
1935 AST_LIST_REMOVE_CURRENT(list);
1936 xmpp_message_destroy(message);
1940 aux = ast_strdupa(message->message);
1941 AST_LIST_REMOVE_CURRENT(list);
1942 xmpp_message_destroy(message);
1945 AST_LIST_TRAVERSE_SAFE_END;
1946 AST_LIST_UNLOCK(&clientcfg->client->messages);
1952 diff = ast_tvdiff_ms(ast_tvnow(), start);
1955 if (ast_autoservice_stop(chan) < 0) {
1956 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
1959 /* return if we timed out */
1961 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
1964 ast_copy_string(buf, aux, buflen);
1969 static struct ast_custom_function jabberreceive_function = {
1970 .name = "JABBER_RECEIVE",
1971 .read = acf_jabberreceive_read,
1976 * \brief Delete old messages from a given JID
1977 * Messages stored during more than client->message_timeout are deleted
1978 * \param client Asterisk's XMPP client
1979 * \param from the JID we received messages from
1980 * \retval the number of deleted messages
1982 static int delete_old_messages(struct ast_xmpp_client *client, char *from)
1984 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1985 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1986 int deleted = 0, isold = 0;
1987 struct ast_xmpp_message *message = NULL;
1989 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
1993 AST_LIST_LOCK(&client->messages);
1994 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, message, list) {
1996 if (!from || !strncasecmp(from, message->from, strlen(from))) {
1997 AST_LIST_REMOVE_CURRENT(list);
1998 xmpp_message_destroy(message);
2001 } else if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
2003 if (!from || !strncasecmp(from, message->from, strlen(from))) {
2004 AST_LIST_REMOVE_CURRENT(list);
2005 xmpp_message_destroy(message);
2010 AST_LIST_TRAVERSE_SAFE_END;
2011 AST_LIST_UNLOCK(&client->messages);
2016 static int xmpp_send_cb(const struct ast_msg *msg, const char *to, const char *from)
2018 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2019 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2020 char *sender, *dest;
2023 sender = ast_strdupa(from);
2024 strsep(&sender, ":");
2025 dest = ast_strdupa(to);
2028 if (ast_strlen_zero(sender)) {
2029 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for XMPP\n", from);
2033 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, sender))) {
2034 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
2038 ast_debug(1, "Sending message to '%s' from '%s'\n", dest, clientcfg->name);
2040 if ((res = ast_xmpp_client_send_message(clientcfg->client, dest, ast_msg_get_body(msg))) != IKS_OK) {
2041 ast_log(LOG_WARNING, "Failed to send XMPP message (%d).\n", res);
2044 return res == IKS_OK ? 0 : -1;
2047 static const struct ast_msg_tech msg_tech = {
2049 .msg_send = xmpp_send_cb,
2052 /*! \brief Internal function which changes the XMPP client state */
2053 static void xmpp_client_change_state(struct ast_xmpp_client *client, int state)
2055 client->state = state;
2058 /*! \brief Internal function which creates a buddy on a client */
2059 static struct ast_xmpp_buddy *xmpp_client_create_buddy(struct ao2_container *container, const char *id)
2061 struct ast_xmpp_buddy *buddy;
2063 if (!(buddy = ao2_alloc(sizeof(*buddy), xmpp_buddy_destructor))) {
2067 if (!(buddy->resources = ao2_container_alloc(RESOURCE_BUCKETS, xmpp_resource_hash, xmpp_resource_cmp))) {
2072 ast_copy_string(buddy->id, id, sizeof(buddy->id));
2074 /* Assume we need to subscribe to get their presence until proven otherwise */
2075 buddy->subscribe = 1;
2077 ao2_link(container, buddy);
2082 /*! \brief Helper function which unsubscribes a user and removes them from the roster */
2083 static int xmpp_client_unsubscribe_user(struct ast_xmpp_client *client, const char *user)
2085 iks *iq, *query = NULL, *item = NULL;
2087 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, user,
2088 "Goodbye. Your status is no longer required.\n"))) {
2092 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item"))) {
2093 ast_log(LOG_WARNING, "Could not allocate memory for roster removal of '%s' from client '%s'\n",
2094 user, client->name);
2098 iks_insert_attrib(iq, "from", client->jid->full);
2099 iks_insert_attrib(iq, "type", "set");
2100 iks_insert_attrib(query, "xmlns", "jabber:iq:roster");
2101 iks_insert_node(iq, query);
2102 iks_insert_attrib(item, "jid", user);
2103 iks_insert_attrib(item, "subscription", "remove");
2104 iks_insert_node(query, item);
2106 if (ast_xmpp_client_send(client, iq)) {
2107 ast_log(LOG_WARNING, "Could not send roster removal request of '%s' from client '%s'\n",
2108 user, client->name);
2119 /*! \brief Callback function which subscribes to a user if needed */
2120 static int xmpp_client_subscribe_user(void *obj, void *arg, int flags)
2122 struct ast_xmpp_buddy *buddy = obj;
2123 struct ast_xmpp_client *client = arg;
2125 if (!buddy->subscribe) {
2129 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, buddy->id,
2130 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"))) {
2131 ast_log(LOG_WARNING, "Could not send subscription for '%s' on client '%s'\n",
2132 buddy->id, client->name);
2135 buddy->subscribe = 0;
2140 /*! \brief Hook function called when roster is received from server */
2141 static int xmpp_roster_hook(void *data, ikspak *pak)
2143 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2144 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2145 struct ast_xmpp_client *client = data;
2148 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2149 return IKS_FILTER_EAT;
2152 for (item = iks_child(pak->query); item; item = iks_next(item)) {
2153 struct ast_xmpp_buddy *buddy;
2155 if (iks_strcmp(iks_name(item), "item")) {
2159 if (!(buddy = ao2_find(client->buddies, iks_find_attrib(item, "jid"), OBJ_KEY))) {
2160 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOPRUNE)) {
2161 /* The buddy has not been specified in the configuration file, we no longer
2162 * want them on our buddy list or to receive their presence. */
2163 if (xmpp_client_unsubscribe_user(client, iks_find_attrib(item, "jid"))) {
2164 ast_log(LOG_ERROR, "Could not unsubscribe user '%s' on client '%s'\n",
2165 iks_find_attrib(item, "jid"), client->name);
2170 if (!(buddy = xmpp_client_create_buddy(client->buddies, iks_find_attrib(item, "jid")))) {
2171 ast_log(LOG_ERROR, "Could not allocate buddy '%s' on client '%s'\n", iks_find_attrib(item, "jid"),
2177 /* Determine if we need to subscribe to their presence or not */
2178 if (!iks_strcmp(iks_find_attrib(item, "subscription"), "none") ||
2179 !iks_strcmp(iks_find_attrib(item, "subscription"), "from")) {
2180 buddy->subscribe = 1;
2182 buddy->subscribe = 0;
2188 /* If autoregister is enabled we need to go through every buddy that we need to subscribe to and do so */
2189 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOREGISTER)) {
2190 ao2_callback(client->buddies, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_subscribe_user, client);
2193 xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
2195 return IKS_FILTER_EAT;
2198 /*! \brief Internal function which changes the presence status of an XMPP client */
2199 static void xmpp_client_set_presence(struct ast_xmpp_client *client, const char *to, const char *from, int level, const char *desc)
2201 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2202 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2203 iks *presence = NULL, *cnode = NULL, *priority = NULL;
2206 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2207 !(presence = iks_make_pres(level, desc)) || !(cnode = iks_new("c")) || !(priority = iks_new("priority"))) {
2208 ast_log(LOG_ERROR, "Unable to allocate stanzas for setting presence status for client '%s'\n", client->name);
2212 if (!ast_strlen_zero(to)) {
2213 iks_insert_attrib(presence, "to", to);
2216 if (!ast_strlen_zero(from)) {
2217 iks_insert_attrib(presence, "from", from);
2220 snprintf(priorityS, sizeof(priorityS), "%d", clientcfg->priority);
2221 iks_insert_cdata(priority, priorityS, strlen(priorityS));
2222 iks_insert_node(presence, priority);
2223 iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
2224 iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
2225 iks_insert_attrib(cnode, "ext", "voice-v1 video-v1 camera-v1");
2226 iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
2227 iks_insert_node(presence, cnode);
2228 ast_xmpp_client_send(client, presence);
2232 iks_delete(presence);
2233 iks_delete(priority);
2236 /*! \brief Hook function called when client receives a service discovery get message */
2237 static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
2239 struct ast_xmpp_client *client = data;
2240 iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
2242 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(ident = iks_new("identity")) || !(disco = iks_new("feature")) ||
2243 !(google = iks_new("feature")) || !(jingle = iks_new("feature")) || !(ice = iks_new("feature")) || !(rtp = iks_new("feature")) ||
2244 !(audio = iks_new("feature")) || !(video = iks_new("feature"))) {
2245 ast_log(LOG_ERROR, "Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
2246 pak->from->full, client->name);
2250 iks_insert_attrib(iq, "from", client->jid->full);
2253 iks_insert_attrib(iq, "to", pak->from->full);
2256 iks_insert_attrib(iq, "type", "result");
2257 iks_insert_attrib(iq, "id", pak->id);
2258 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2259 iks_insert_attrib(ident, "category", "client");
2260 iks_insert_attrib(ident, "type", "pc");
2261 iks_insert_attrib(ident, "name", "asterisk");
2262 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2264 iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2265 iks_insert_attrib(jingle, "var", "urn:xmpp:jingle:1");
2266 iks_insert_attrib(ice, "var", "urn:xmpp:jingle:transports:ice-udp:1");
2267 iks_insert_attrib(rtp, "var", "urn:xmpp:jingle:apps:rtp:1");
2268 iks_insert_attrib(audio, "var", "urn:xmpp:jingle:apps:rtp:audio");
2269 iks_insert_attrib(video, "var", "urn:xmpp:jingle:apps:rtp:video");
2270 iks_insert_node(iq, query);
2271 iks_insert_node(query, ident);
2272 iks_insert_node(query, google);
2273 iks_insert_node(query, disco);
2274 iks_insert_node(query, jingle);
2275 iks_insert_node(query, ice);
2276 iks_insert_node(query, rtp);
2277 iks_insert_node(query, audio);
2278 iks_insert_node(query, video);
2279 ast_xmpp_client_send(client, iq);
2293 return IKS_FILTER_EAT;
2296 /*! \brief Hook function called when client receives a service discovery result message */
2297 static int xmpp_client_service_discovery_result_hook(void *data, ikspak *pak)
2299 struct ast_xmpp_client *client = data;
2300 struct ast_xmpp_buddy *buddy;
2301 struct ast_xmpp_resource *resource;
2303 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2304 return IKS_FILTER_EAT;
2307 if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY))) {
2309 return IKS_FILTER_EAT;
2314 if (iks_find_with_attrib(pak->query, "feature", "var", "urn:xmpp:jingle:1")) {
2315 resource->caps.jingle = 1;
2318 ao2_unlock(resource);
2320 ao2_ref(resource, -1);
2323 return IKS_FILTER_EAT;
2326 /*! \brief Hook function called when client finishes authenticating with the server */
2327 static int xmpp_connect_hook(void *data, ikspak *pak)
2329 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2330 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2331 struct ast_xmpp_client *client = data;
2334 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2338 client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
2340 if (ast_test_flag(&clientcfg->flags, XMPP_DISTRIBUTE_EVENTS)) {
2341 xmpp_init_event_distribution(client);
2344 if (!(roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER))) {
2345 ast_log(LOG_ERROR, "Unable to allocate memory for roster request for client '%s'\n", client->name);
2349 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);
2350 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);
2352 iks_insert_attrib(roster, "id", "roster");
2353 ast_xmpp_client_send(client, roster);
2355 iks_filter_remove_hook(client->filter, xmpp_connect_hook);
2356 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);
2358 xmpp_client_set_presence(client, NULL, client->jid->full, clientcfg->status, clientcfg->statusmsg);
2359 xmpp_client_change_state(client, XMPP_STATE_ROSTER);
2361 return IKS_FILTER_EAT;
2364 /*! \brief Logging hook function */
2365 static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incoming)
2367 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2368 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2369 struct ast_xmpp_client *client = data;
2371 if (!ast_strlen_zero(xmpp)) {
2372 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
2375 if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) {
2380 if (strlen(xmpp) == 1) {
2381 if (option_debug > 2 && xmpp[0] == ' ') {
2382 ast_verbose("\n<--- XMPP keep alive from '%s' --->\n", client->name);
2385 ast_verbose("\n<--- XMPP sent to '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2388 ast_verbose("\n<--- XMPP received from '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2392 /*! \brief Internal function which sends a raw message */
2393 static int xmpp_client_send_raw_message(struct ast_xmpp_client *client, const char *message)
2397 int len = strlen(message);
2399 if (xmpp_is_secure(client)) {
2400 ret = SSL_write(client->ssl_session, message, len);
2402 /* Log the message here, because iksemel's logHook is
2404 xmpp_log_hook(client, message, len, 0);
2409 /* If needed, data will be sent unencrypted, and logHook will
2410 be called inside iks_send_raw */
2411 ret = iks_send_raw(client->parser, message);
2412 if (ret != IKS_OK) {
2419 /*! \brief Helper function which sends an XMPP stream header to the server */
2420 static int xmpp_send_stream_header(struct ast_xmpp_client *client, const struct ast_xmpp_client_config *cfg, const char *to)
2422 char *namespace = ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client";
2423 char msg[91 + strlen(namespace) + 6 + strlen(to) + 16 + 1];
2425 snprintf(msg, sizeof(msg), "<?xml version='1.0'?>"
2426 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
2427 "%s' to='%s' version='1.0'>", namespace, to);
2429 return xmpp_client_send_raw_message(client, msg);
2432 int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza)
2434 return xmpp_client_send_raw_message(client, iks_string(iks_stack(stanza), stanza));
2437 /*! \brief Internal function called when we need to request TLS support */
2438 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2440 /* If the client connection is already secure we can jump straight to authenticating */
2441 if (xmpp_is_secure(client)) {
2442 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2446 #ifndef HAVE_OPENSSL
2447 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL is not available.\n", client->name);
2450 if (iks_send_raw(client->parser, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>") == IKS_NET_TLSFAIL) {
2451 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be started.\n", client->name);
2455 client->stream_flags |= TRY_SECURE;
2457 xmpp_client_change_state(client, XMPP_STATE_REQUESTED_TLS);
2463 /*! \brief Internal function called when we receive a response to our TLS initiation request */
2464 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2470 if (!strcmp(iks_name(node), "success")) {
2471 /* TLS is up and working, we can move on to authenticating now */
2472 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2474 } else if (!strcmp(iks_name(node), "failure")) {
2475 /* TLS negotiation was a failure, close it on down! */
2477 } else if (strcmp(iks_name(node), "proceed")) {
2478 /* Ignore any other responses */
2482 #ifndef HAVE_OPENSSL
2483 ast_log(LOG_ERROR, "Somehow we managed to try to start TLS negotiation on client '%s' without OpenSSL support, disconnecting\n", client->name);
2486 client->ssl_method = SSLv3_method();
2487 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
2491 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
2495 sock = iks_fd(client->parser);
2496 if (!SSL_set_fd(client->ssl_session, sock)) {
2500 if (!SSL_connect(client->ssl_session)) {
2504 client->stream_flags &= (~TRY_SECURE);
2505 client->stream_flags |= SECURE;
2507 if (xmpp_send_stream_header(client, cfg, client->jid->server) != IKS_OK) {
2508 ast_log(LOG_ERROR, "TLS connection for client '%s' could not be established, failed to send stream header after negotiation\n",
2513 ast_debug(1, "TLS connection for client '%s' started with server\n", client->name);
2515 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2520 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL initialization failed.\n", client->name);
2525 /*! \brief Internal function called when we need to authenticate using non-SASL */
2526 static int xmpp_client_authenticate_digest(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2528 iks *iq = NULL, *query = NULL;
2529 char buf[41], sidpass[100];
2531 if (!(iq = iks_new("iq")) || !(query = iks_insert(iq, "query"))) {
2532 ast_log(LOG_ERROR, "Stanzas could not be allocated for authentication on client '%s'\n", client->name);
2537 iks_insert_attrib(iq, "type", "set");
2538 iks_insert_cdata(iks_insert(query, "username"), client->jid->user, 0);
2539 iks_insert_cdata(iks_insert(query, "resource"), client->jid->resource, 0);
2541 iks_insert_attrib(query, "xmlns", "jabber:iq:auth");
2542 snprintf(sidpass, sizeof(sidpass), "%s%s", iks_find_attrib(node, "id"), cfg->password);
2543 ast_sha1_hash(buf, sidpass);
2544 iks_insert_cdata(iks_insert(query, "digest"), buf, 0);
2546 ast_xmpp_client_lock(client);
2547 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);
2548 iks_insert_attrib(iq, "id", client->mid);
2549 ast_xmpp_increment_mid(client->mid);
2550 ast_xmpp_client_unlock(client);
2552 iks_insert_attrib(iq, "to", client->jid->server);
2554 ast_xmpp_client_send(client, iq);
2558 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2563 /*! \brief Internal function called when we need to authenticate using SASL */
2564 static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2566 int features, len = strlen(client->jid->user) + strlen(cfg->password) + 3;
2569 char base64[(len + 2) * 4 / 3];
2571 if (strcmp(iks_name(node), "stream:features")) {
2572 /* Ignore anything beside stream features */
2576 features = iks_stream_features(node);
2578 if ((features & IKS_STREAM_SASL_MD5) && !xmpp_is_secure(client)) {
2579 if (iks_start_sasl(client->parser, IKS_SASL_DIGEST_MD5, (char*)client->jid->user, (char*)cfg->password) != IKS_OK) {
2580 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL DIGEST-MD5 but could not\n", client->name);
2584 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2588 /* Our only other available option is plain so if they don't support it, bail out now */
2589 if (!(features & IKS_STREAM_SASL_PLAIN)) {
2590 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL PLAIN but server does not support it\n", client->name);
2594 if (!(auth = iks_new("auth"))) {
2595 ast_log(LOG_ERROR, "Could not allocate memory for SASL PLAIN authentication for client '%s'\n", client->name);
2599 iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
2600 iks_insert_attrib(auth, "mechanism", "PLAIN");
2602 if (strchr(client->jid->user, '/')) {
2603 char *user = ast_strdupa(client->jid->user);
2605 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, strsep(&user, "/"), 0, cfg->password);
2607 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, client->jid->user, 0, cfg->password);
2610 ast_base64encode(base64, (const unsigned char *) combined, len - 1, (len + 2) * 4 / 3);
2611 iks_insert_cdata(auth, base64, 0);
2613 ast_xmpp_client_send(client, auth);
2617 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2622 /*! \brief Internal function called when we need to authenticate */
2623 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2625 return ast_test_flag(&cfg->flags, XMPP_USESASL) ? xmpp_client_authenticate_sasl(client, cfg, type, node) : xmpp_client_authenticate_digest(client, cfg, type, node);
2628 /*! \brief Internal function called when we are authenticating */
2629 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2633 if (!strcmp(iks_name(node), "success")) {
2634 /* Authentication was a success, yay! */
2635 xmpp_send_stream_header(client, cfg, client->jid->server);
2638 } else if (!strcmp(iks_name(node), "failure")) {
2639 /* Authentication was a bust, disconnect and reconnect later */
2641 } else if (strcmp(iks_name(node), "stream:features")) {
2642 /* Ignore any other responses */
2646 features = iks_stream_features(node);
2648 if (features & IKS_STREAM_BIND) {
2651 if (!(auth = iks_make_resource_bind(client->jid))) {
2652 ast_log(LOG_ERROR, "Failed to allocate memory for stream bind on client '%s'\n", client->name);
2656 ast_xmpp_client_lock(client);
2657 iks_insert_attrib(auth, "id", client->mid);
2658 ast_xmpp_increment_mid(client->mid);
2659 ast_xmpp_client_unlock(client);
2660 ast_xmpp_client_send(client, auth);
2664 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);
2667 if (features & IKS_STREAM_SESSION) {
2670 if (!(auth = iks_make_session())) {
2671 ast_log(LOG_ERROR, "Failed to allocate memory for stream session on client '%s'\n", client->name);
2675 iks_insert_attrib(auth, "id", "auth");
2676 ast_xmpp_client_lock(client);
2677 ast_xmpp_increment_mid(client->mid);
2678 ast_xmpp_client_unlock(client);
2679 ast_xmpp_client_send(client, auth);
2683 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);
2689 /*! \brief Internal function called when we should authenticate as a component */
2690 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2692 char secret[160], shasum[320], message[344];
2693 ikspak *pak = iks_packet(node);
2695 snprintf(secret, sizeof(secret), "%s%s", pak->id, cfg->password);
2696 ast_sha1_hash(shasum, secret);
2697 snprintf(message, sizeof(message), "<handshake>%s</handshake>", shasum);
2699 if (xmpp_client_send_raw_message(client, message) != IKS_OK) {
2700 ast_log(LOG_ERROR, "Unable to send handshake for component '%s'\n", client->name);
2704 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2709 /*! \brief Hook function called when component receives a service discovery get message */
2710 static int xmpp_component_service_discovery_get_hook(void *data, ikspak *pak)
2712 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2713 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2714 struct ast_xmpp_client *client = data;
2715 iks *iq = NULL, *query = NULL, *identity = NULL, *disco = NULL, *reg = NULL, *commands = NULL, *gateway = NULL;
2716 iks *version = NULL, *vcard = NULL, *search = NULL, *item = NULL;
2719 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2720 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(identity = iks_new("identity")) || !(disco = iks_new("feature")) ||
2721 !(reg = iks_new("feature")) || !(commands = iks_new("feature")) || !(gateway = iks_new("feature")) || !(version = iks_new("feature")) ||
2722 !(vcard = iks_new("feature")) || !(search = iks_new("search")) || !(item = iks_new("item"))) {
2723 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery get response to '%s' on component '%s'\n",
2724 pak->from->partial, client->name);
2728 iks_insert_attrib(iq, "from", clientcfg->user);
2729 iks_insert_attrib(iq, "to", pak->from->full);
2730 iks_insert_attrib(iq, "id", pak->id);
2731 iks_insert_attrib(iq, "type", "result");
2733 if (!(node = iks_find_attrib(pak->query, "node"))) {
2734 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2735 iks_insert_attrib(identity, "category", "gateway");
2736 iks_insert_attrib(identity, "type", "pstn");
2737 iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2738 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2739 iks_insert_attrib(reg, "var", "jabber:iq:register");
2740 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2741 iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2742 iks_insert_attrib(version, "var", "jabber:iq:version");
2743 iks_insert_attrib(vcard, "var", "vcard-temp");
2744 iks_insert_attrib(search, "var", "jabber:iq:search");
2746 iks_insert_node(iq, query);
2747 iks_insert_node(query, identity);
2748 iks_insert_node(query, disco);
2749 iks_insert_node(query, reg);
2750 iks_insert_node(query, commands);
2751 iks_insert_node(query, gateway);
2752 iks_insert_node(query, version);
2753 iks_insert_node(query, vcard);
2754 iks_insert_node(query, search);
2755 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2756 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2757 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2758 iks_insert_attrib(item, "node", "confirmaccount");
2759 iks_insert_attrib(item, "name", "Confirm account");
2760 iks_insert_attrib(item, "jid", clientcfg->user);
2762 iks_insert_node(iq, query);
2763 iks_insert_node(query, item);
2764 } else if (!strcasecmp(node, "confirmaccount")) {
2765 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2766 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2768 iks_insert_node(iq, query);
2769 iks_insert_node(query, commands);
2771 ast_debug(3, "Unsupported service discovery info request received with node '%s' on component '%s'\n",
2772 node, client->name);
2776 if (ast_xmpp_client_send(client, iq)) {
2777 ast_log(LOG_WARNING, "Could not send response to service discovery request on component '%s'\n",
2784 iks_delete(version);
2785 iks_delete(gateway);
2786 iks_delete(commands);
2789 iks_delete(identity);
2793 return IKS_FILTER_EAT;
2796 /*! \brief Hook function called when the component is queried about registration */
2797 static int xmpp_component_register_get_hook(void *data, ikspak *pak)
2799 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2800 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2801 struct ast_xmpp_client *client = data;
2802 iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL, *instructions = NULL;
2803 struct ast_xmpp_buddy *buddy;
2806 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2807 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(error = iks_new("error")) || !(notacceptable = iks_new("not-acceptable")) ||
2808 !(instructions = iks_new("instructions"))) {
2809 ast_log(LOG_ERROR, "Failed to allocate stanzas for register get response to '%s' on component '%s'\n",
2810 pak->from->partial, client->name);
2814 iks_insert_attrib(iq, "from", clientcfg->user);
2815 iks_insert_attrib(iq, "to", pak->from->full);
2816 iks_insert_attrib(iq, "id", pak->id);
2817 iks_insert_attrib(iq, "type", "result");
2818 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
2819 iks_insert_node(iq, query);
2821 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2822 iks_insert_attrib(error, "code", "406");
2823 iks_insert_attrib(error, "type", "modify");
2824 iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
2826 iks_insert_node(iq, error);
2827 iks_insert_node(error, notacceptable);
2829 ast_log(LOG_ERROR, "Received register attempt from '%s' but buddy is not configured on component '%s'\n",
2830 pak->from->partial, client->name);
2831 } else if (!(node = iks_find_attrib(pak->query, "node"))) {
2832 iks_insert_cdata(instructions, "Welcome to Asterisk - the Open Source PBX.\n", 0);
2833 iks_insert_node(query, instructions);
2836 ast_log(LOG_WARNING, "Received register get to component '%s' using unsupported node '%s' from '%s'\n",
2837 client->name, node, pak->from->partial);
2842 if (ast_xmpp_client_send(client, iq)) {
2843 ast_log(LOG_WARNING, "Could not send response to '%s' for received register get on component '%s'\n",
2844 pak->from->partial, client->name);
2848 iks_delete(instructions);
2849 iks_delete(notacceptable);
2854 return IKS_FILTER_EAT;
2857 /*! \brief Hook function called when someone registers to the component */
2858 static int xmpp_component_register_set_hook(void *data, ikspak *pak)
2860 struct ast_xmpp_client *client = data;
2861 iks *iq, *presence = NULL, *x = NULL;
2863 if (!(iq = iks_new("iq")) || !(presence = iks_new("presence")) || !(x = iks_new("x"))) {
2864 ast_log(LOG_ERROR, "Failed to allocate stanzas for register set response to '%s' on component '%s'\n",
2865 pak->from->partial, client->name);
2869 iks_insert_attrib(iq, "from", client->jid->full);
2870 iks_insert_attrib(iq, "to", pak->from->full);
2871 iks_insert_attrib(iq, "id", pak->id);
2872 iks_insert_attrib(iq, "type", "result");
2874 if (ast_xmpp_client_send(client, iq)) {
2875 ast_log(LOG_WARNING, "Could not send response to '%s' for received register set on component '%s'\n",
2876 pak->from->partial, client->name);
2880 iks_insert_attrib(presence, "from", client->jid->full);
2881 iks_insert_attrib(presence, "to", pak->from->partial);
2882 ast_xmpp_client_lock(client);
2883 iks_insert_attrib(presence, "id", client->mid);
2884 ast_xmpp_increment_mid(client->mid);
2885 ast_xmpp_client_unlock(client);
2886 iks_insert_attrib(presence, "type", "subscribe");
2887 iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
2889 iks_insert_node(presence, x);
2891 if (ast_xmpp_client_send(client, presence)) {
2892 ast_log(LOG_WARNING, "Could not send subscription to '%s' on component '%s'\n",
2893 pak->from->partial, client->name);
2898 iks_delete(presence);
2901 return IKS_FILTER_EAT;
2904 /*! \brief Hook function called when we receive a service discovery items request */
2905 static int xmpp_component_service_discovery_items_hook(void *data, ikspak *pak)
2907 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2908 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2909 struct ast_xmpp_client *client = data;
2910 iks *iq = NULL, *query = NULL, *item = NULL, *feature = NULL;
2913 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2914 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item")) || !(feature = iks_new("feature"))) {
2915 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery items response to '%s' on component '%s'\n",
2916 pak->from->partial, client->name);
2920 iks_insert_attrib(iq, "from", clientcfg->user);
2921 iks_insert_attrib(iq, "to", pak->from->full);
2922 iks_insert_attrib(iq, "id", pak->id);
2923 iks_insert_attrib(iq, "type", "result");
2924 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2925 iks_insert_node(iq, query);
2927 if (!(node = iks_find_attrib(pak->query, "node"))) {
2928 iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
2929 iks_insert_attrib(item, "name", "Asterisk Commands");
2930 iks_insert_attrib(item, "jid", clientcfg->user);
2932 iks_insert_node(query, item);
2933 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2934 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2936 ast_log(LOG_WARNING, "Received service discovery items request to component '%s' using unsupported node '%s' from '%s'\n",
2937 client->name, node, pak->from->partial);
2941 if (ast_xmpp_client_send(client, iq)) {
2942 ast_log(LOG_WARNING, "Could not send response to service discovery items request from '%s' on component '%s'\n",
2943 pak->from->partial, client->name);
2947 iks_delete(feature);
2952 return IKS_FILTER_EAT;
2955 /*! \brief Internal function called when we authenticated as a component */
2956 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2958 if (strcmp(iks_name(node), "handshake")) {
2959 ast_log(LOG_ERROR, "Failed to authenticate component '%s'\n", client->name);
2963 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);
2965 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);
2967 /* This uses the client service discovery result hook on purpose, as the code is common between both */
2968 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);
2970 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);
2971 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);
2973 xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
2978 /*! \brief Internal function called when a message is received */
2979 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
2981 struct ast_xmpp_message *message;
2984 ast_debug(3, "XMPP client '%s' received a message\n", client->name);
2986 if (!(message = ast_calloc(1, sizeof(*message)))) {
2990 message->arrived = ast_tvnow();
2992 if (iks_find_cdata(pak->x, "body")) {
2993 message->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2996 ast_copy_string(message->id, S_OR(pak->id, ""), sizeof(message->id));
2997 message->from = !ast_strlen_zero(pak->from->full) ? ast_strdup(pak->from->full) : NULL;
2999 if (ast_test_flag(&cfg->flags, XMPP_SEND_TO_DIALPLAN)) {
3000 struct ast_msg *msg;
3002 if ((msg = ast_msg_alloc())) {
3005 ast_xmpp_client_lock(client);
3007 res = ast_msg_set_to(msg, "xmpp:%s", cfg->user);
3008 res |= ast_msg_set_from(msg, "xmpp:%s", message->from);
3009 res |= ast_msg_set_body(msg, "%s", message->message);
3010 res |= ast_msg_set_context(msg, "%s", cfg->context);
3012 ast_xmpp_client_unlock(client);
3015 ast_msg_destroy(msg);
3022 /* remove old messages received from this JID
3023 * and insert received message */
3024 deleted = delete_old_messages(client, pak->from->partial);
3025 ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
3026 AST_LIST_LOCK(&client->messages);
3027 AST_LIST_INSERT_HEAD(&client->messages, message, list);
3028 AST_LIST_UNLOCK(&client->messages);
3030 /* wake up threads waiting for messages */
3031 ast_mutex_lock(&messagelock);
3032 ast_cond_broadcast(&message_received_condition);
3033 ast_mutex_unlock(&messagelock);
3038 /*! \brief Helper function which sends a discovery information request to a user */
3039 static int xmpp_client_send_disco_info_request(struct ast_xmpp_client *client, const char *to, const char *from)
3044 if (!(iq = iks_new("iq")) || !(query = iks_new("query"))) {
3049 iks_insert_attrib(iq, "type", "get");
3050 iks_insert_attrib(iq, "to", to);
3051 iks_insert_attrib(iq, "from", from);
3052 ast_xmpp_client_lock(client);
3053 iks_insert_attrib(iq, "id", client->mid);
3054 ast_xmpp_increment_mid(client->mid);
3055 ast_xmpp_client_unlock(client);
3056 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
3057 iks_insert_node(iq, query);
3059 res = ast_xmpp_client_send(client, iq);
3067 /*! \brief Internal function called when a presence message is received */
3068 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
3070 struct ast_xmpp_buddy *buddy;
3071 struct ast_xmpp_resource *resource;
3072 char *type = iks_find_attrib(pak->x, "type");
3073 int status = pak->show ? pak->show : STATUS_DISAPPEAR;
3075 /* If no resource is available this is a general buddy presence update, which we will ignore */
3076 if (!pak->from->resource) {
3080 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
3081 /* Only output the message if it is not about us */
3082 if (strcmp(client->jid->partial, pak->from->partial)) {
3083 ast_log(LOG_WARNING, "Received presence information about '%s' despite not having them in roster on client '%s'\n",
3084 pak->from->partial, client->name);
3089 /* If this is a component presence probe request answer immediately with our presence status */
3090 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT) && !ast_strlen_zero(type) && !strcasecmp(type, "probe")) {
3091 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3094 ao2_lock(buddy->resources);
3096 if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY | OBJ_NOLOCK))) {
3097 /* Only create the new resource if it is not going away - in reality this should not happen */
3098 if (status != STATUS_DISAPPEAR) {
3099 if (!(resource = ao2_alloc(sizeof(*resource), xmpp_resource_destructor))) {
3100 ast_log(LOG_ERROR, "Could not allocate resource object for resource '%s' of buddy '%s' on client '%s'\n",
3101 pak->from->resource, buddy->id, client->name);
3102 ao2_unlock(buddy->resources);
3107 ast_copy_string(resource->resource, pak->from->resource, sizeof(resource->resource));
3110 /* We unlink the resource in case the priority changes or in case they are going away */
3111 ao2_unlink_flags(buddy->resources, resource, OBJ_NOLOCK);
3114 /* Only update the resource and add it back in if it is not going away */
3115 if (resource && (status != STATUS_DISAPPEAR)) {
3118 /* Try to get the XMPP spec node, and fall back to Google if not found */
3119 if (!(node = iks_find_attrib(iks_find(pak->x, "c"), "node"))) {
3120 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
3123 if (!(ver = iks_find_attrib(iks_find(pak->x, "c"), "ver"))) {
3124 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
3127 if (resource->description) {
3128 ast_free(resource->description);
3131 if ((node && strcmp(resource->caps.node, node)) || (ver && strcmp(resource->caps.version, ver))) {
3132 ast_copy_string(resource->caps.node, node, sizeof(resource->caps.node));
3133 ast_copy_string(resource->caps.version, ver, sizeof(resource->caps.version));
3135 /* Google Talk places the capabilities information directly in presence, so see if it is there */
3136 if (iks_find_with_attrib(pak->x, "c", "node", "http://www.google.com/xmpp/client/caps") ||
3137 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.google.com/xmpp/client/caps") ||
3138 iks_find_with_attrib(pak->x, "c", "node", "http://www.android.com/gtalk/client/caps") ||
3139 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.android.com/gtalk/client/caps") ||
3140 iks_find_with_attrib(pak->x, "c", "node", "http://mail.google.com/xmpp/client/caps") ||
3141 iks_find_with_attrib(pak->x, "caps:c", "node", "http://mail.google.com/xmpp/client/caps")) {
3142 resource->caps.google = 1;
3145 /* To discover if the buddy supports Jingle we need to query, so do so */
3146 if (xmpp_client_send_disco_info_request(client, pak->from->full, client->jid->full)) {
3147 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);
3151 resource->status = status;
3152 resource->description = ast_strdup(iks_find_cdata(pak->x, "status"));
3153 resource->priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
3155 ao2_link_flags(buddy->resources, resource, OBJ_NOLOCK);
3157 manager_event(EVENT_FLAG_USER, "JabberStatus",
3158 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
3159 "\r\nDescription: %s\r\n",
3160 client->name, pak->from->partial, resource->resource, resource->status,
3161 resource->priority, S_OR(resource->description, ""));
3163 ao2_ref(resource, -1);
3165 /* This will get hit by presence coming in for an unknown resource, and also when a resource goes away */
3167 ao2_ref(resource, -1);
3170 manager_event(EVENT_FLAG_USER, "JabberStatus",
3171 "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
3172 client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
3175 ao2_unlock(buddy->resources);
3182 /*! \brief Internal function called when a subscription message is received */
3183 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg,iks *node, ikspak *pak)
3185 struct ast_xmpp_buddy *buddy;
3187 switch (pak->subtype) {
3188 case IKS_TYPE_SUBSCRIBE:
3189 if (ast_test_flag(&cfg->flags, XMPP_AUTOREGISTER)) {
3190 iks *presence, *status = NULL;
3192 if ((presence = iks_new("presence")) && (status = iks_new("status"))) {
3193 iks_insert_attrib(presence, "type", "subscribed");
3194 iks_insert_attrib(presence, "to", pak->from->full);
3195 iks_insert_attrib(presence, "from", client->jid->full);
3198 iks_insert_attrib(presence, "id", pak->id);
3201 iks_insert_cdata(status, "Asterisk has approved your subscription", 0);
3202 iks_insert_node(presence, status);
3204 if (ast_xmpp_client_send(client, presence)) {
3205 ast_log(LOG_ERROR, "Could not send subscription acceptance to '%s' from client '%s'\n",
3206 pak->from->partial, client->name);
3209 ast_log(LOG_ERROR, "Could not allocate presence stanzas for accepting subscription from '%s' to client '%s'\n",
3210 pak->from->partial, client->name);
3214 iks_delete(presence);
3217 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
3218 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3220 /* This purposely flows through so we have the subscriber amongst our buddies */
3221 case IKS_TYPE_SUBSCRIBED:
3222 ao2_lock(client->buddies);
3224 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY | OBJ_NOLOCK))) {
3225 buddy = xmpp_client_create_buddy(client->buddies, pak->from->partial);
3229 ast_log(LOG_WARNING, "Could not find or create buddy '%s' on client '%s'\n",
3230 pak->from->partial, client->name);
3235 ao2_unlock(client->buddies);
3245 /*! \brief Action hook for when things occur */
3246 static int xmpp_action_hook(void *data, int type, iks *node)
3248 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
3249 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3250 struct ast_xmpp_client *client = data;