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 * 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.
34 /*! \li \ref res_xmpp.c uses the configuration file \ref xmpp.conf and \ref jabber.conf
35 * \addtogroup configuration_file Configuration Files
39 * \page xmpp.conf xmpp.conf
40 * \verbinclude xmpp.conf.sample
44 <depend>iksemel</depend>
45 <use type="external">openssl</use>
46 <support_level>core</support_level>
51 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
56 #include "asterisk/xmpp.h"
57 #include "asterisk/module.h"
58 #include "asterisk/manager.h"
59 #include "asterisk/app.h"
60 #include "asterisk/message.h"
61 #include "asterisk/manager.h"
62 #include "asterisk/event.h"
63 #include "asterisk/cli.h"
64 #include "asterisk/config_options.h"
67 <application name="JabberSend" language="en_US" module="res_xmpp">
69 Sends an XMPP message to a buddy.
72 <parameter name="account" required="true">
73 <para>The local named account to listen on (specified in
76 <parameter name="jid" required="true">
77 <para>Jabber ID of the buddy to send the message to. It can be a
78 bare JID (username@domain) or a full JID (username@domain/resource).</para>
80 <parameter name="message" required="true">
81 <para>The message to send.</para>
85 <para>Sends the content of <replaceable>message</replaceable> as text message
86 from the given <replaceable>account</replaceable> to the buddy identified by
87 <replaceable>jid</replaceable></para>
88 <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
89 to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
90 <replaceable>asterisk</replaceable>, configured in xmpp.conf.</para>
93 <ref type="function">JABBER_STATUS</ref>
94 <ref type="function">JABBER_RECEIVE</ref>
97 <function name="JABBER_RECEIVE" language="en_US" module="res_xmpp">
102 <parameter name="account" required="true">
103 <para>The local named account to listen on (specified in
106 <parameter name="jid" required="true">
107 <para>Jabber ID of the buddy to receive message from. It can be a
108 bare JID (username@domain) or a full JID (username@domain/resource).</para>
110 <parameter name="timeout">
111 <para>In seconds, defaults to <literal>20</literal>.</para>
115 <para>Receives a text message on the given <replaceable>account</replaceable>
116 from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
117 <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
118 sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
119 the <replaceable>asterisk</replaceable> XMPP account configured in xmpp.conf.</para>
122 <ref type="function">JABBER_STATUS</ref>
123 <ref type="application">JabberSend</ref>
126 <function name="JABBER_STATUS" language="en_US" module="res_xmpp">
128 Retrieves a buddy's status.
131 <parameter name="account" required="true">
132 <para>The local named account to listen on (specified in
135 <parameter name="jid" required="true">
136 <para>Jabber ID of the buddy to receive message from. It can be a
137 bare JID (username@domain) or a full JID (username@domain/resource).</para>
141 <para>Retrieves the numeric status associated with the buddy identified
142 by <replaceable>jid</replaceable>.
143 If the buddy does not exist in the buddylist, returns 7.</para>
144 <para>Status will be 1-7.</para>
145 <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
146 <para>If not in roster variable will be set to 7.</para>
147 <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
148 <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
149 the associated XMPP account configured in xmpp.conf.</para>
152 <ref type="function">JABBER_RECEIVE</ref>
153 <ref type="application">JabberSend</ref>
156 <application name="JabberSendGroup" language="en_US" module="res_xmpp">
158 Send a Jabber Message to a specified chat room
161 <parameter name="Jabber" required="true">
162 <para>Client or transport Asterisk uses to connect to Jabber.</para>
164 <parameter name="RoomJID" required="true">
165 <para>XMPP/Jabber JID (Name) of chat room.</para>
167 <parameter name="Message" required="true">
168 <para>Message to be sent to the chat room.</para>
170 <parameter name="Nickname" required="false">
171 <para>The nickname Asterisk uses in the chat room.</para>
175 <para>Allows user to send a message to a chat room via XMPP.</para>
176 <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>
179 <application name="JabberJoin" language="en_US" module="res_xmpp">
184 <parameter name="Jabber" required="true">
185 <para>Client or transport Asterisk uses to connect to Jabber.</para>
187 <parameter name="RoomJID" required="true">
188 <para>XMPP/Jabber JID (Name) of chat room.</para>
190 <parameter name="Nickname" required="false">
191 <para>The nickname Asterisk will use in the chat room.</para>
192 <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>
196 <para>Allows Asterisk to join a chat room.</para>
199 <application name="JabberLeave" language="en_US" module="res_xmpp">
204 <parameter name="Jabber" required="true">
205 <para>Client or transport Asterisk uses to connect to Jabber.</para>
207 <parameter name="RoomJID" required="true">
208 <para>XMPP/Jabber JID (Name) of chat room.</para>
210 <parameter name="Nickname" required="false">
211 <para>The nickname Asterisk uses in the chat room.</para>
215 <para>Allows Asterisk to leave a chat room.</para>
218 <application name="JabberStatus" language="en_US" module="res_xmpp">
220 Retrieve the status of a jabber list member
223 <parameter name="Jabber" required="true">
224 <para>Client or transport Asterisk users to connect to Jabber.</para>
226 <parameter name="JID" required="true">
227 <para>XMPP/Jabber JID (Name) of recipient.</para>
229 <parameter name="Variable" required="true">
230 <para>Variable to store the status of requested user.</para>
234 <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
235 <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
236 The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
248 <para>Extended Away.</para>
251 <para>Do Not Disturb.</para>
254 <para>Offline.</para>
257 <para>Not In Roster.</para>
262 <manager name="JabberSend" language="en_US" module="res_xmpp">
264 Sends a message to a Jabber Client.
267 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
268 <parameter name="Jabber" required="true">
269 <para>Client or transport Asterisk uses to connect to JABBER.</para>
271 <parameter name="JID" required="true">
272 <para>XMPP/Jabber JID (Name) of recipient.</para>
274 <parameter name="Message" required="true">
275 <para>Message to be sent to the buddy.</para>
279 <para>Sends a message to a Jabber Client.</para>
282 <info name="XMPPMessageToInfo" language="en_US" tech="XMPP">
283 <para>Specifying a prefix of <literal>xmpp:</literal> will send the
284 message as an XMPP chat message.</para>
286 <info name="XMPPMessageFromInfo" language="en_US" tech="XMPP">
287 <para>Specifying a prefix of <literal>xmpp:</literal> will specify the
288 account defined in <literal>xmpp.conf</literal> to send the message from.
289 Note that this field is required for XMPP messages.</para>
293 /*! \brief Supported general configuration flags */
295 XMPP_AUTOPRUNE = (1 << 0),
296 XMPP_AUTOREGISTER = (1 << 1),
297 XMPP_AUTOACCEPT = (1 << 2),
298 XMPP_DEBUG = (1 << 3),
299 XMPP_USETLS = (1 << 4),
300 XMPP_USESASL = (1 << 5),
301 XMPP_FORCESSL = (1 << 6),
302 XMPP_KEEPALIVE = (1 << 7),
303 XMPP_COMPONENT = (1 << 8),
304 XMPP_SEND_TO_DIALPLAN = (1 << 9),
305 XMPP_DISTRIBUTE_EVENTS = (1 << 10),
308 /*! \brief Supported pubsub configuration flags */
310 XMPP_XEP0248 = (1 << 0),
311 XMPP_PUBSUB = (1 << 1),
312 XMPP_PUBSUB_AUTOCREATE = (1 << 2),
315 /*! \brief Number of buckets for client connections */
316 #define CLIENT_BUCKETS 53
318 /*! \brief Number of buckets for buddies (per client) */
319 #define BUDDY_BUCKETS 53
321 /*! \brief Number of buckets for resources (per buddy) */
322 #define RESOURCE_BUCKETS 53
324 /*! \brief Namespace for TLS support */
325 #define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
327 /*! \brief Status for a disappearing buddy */
328 #define STATUS_DISAPPEAR 6
330 /*! \brief Global debug status */
333 /*! \brief XMPP Global Configuration */
334 struct ast_xmpp_global_config {
335 struct ast_flags general; /*!< General configuration options */
336 struct ast_flags pubsub; /*!< Pubsub related configuration options */
339 /*! \brief XMPP Client Configuration */
340 struct ast_xmpp_client_config {
341 AST_DECLARE_STRING_FIELDS(
342 AST_STRING_FIELD(name); /*!< Name of the client connection */
343 AST_STRING_FIELD(user); /*!< Username to use for authentication */
344 AST_STRING_FIELD(password); /*!< Password to use for authentication */
345 AST_STRING_FIELD(server); /*!< Server hostname */
346 AST_STRING_FIELD(statusmsg); /*!< Status message for presence */
347 AST_STRING_FIELD(pubsubnode); /*!< Pubsub node */
348 AST_STRING_FIELD(context); /*!< Context for incoming messages */
350 int port; /*!< Port to use when connecting to server */
351 int message_timeout; /*!< Timeout for messages */
352 int priority; /*!< Resource priority */
353 struct ast_flags flags; /*!< Various options that have been set */
354 enum ikshowtype status; /*!< Presence status */
355 struct ast_xmpp_client *client; /*!< Pointer to the client */
356 struct ao2_container *buddies; /*!< Configured buddies */
360 struct ast_xmpp_global_config *global; /*!< Global configuration options */
361 struct ao2_container *clients; /*!< Configured clients */
364 static AO2_GLOBAL_OBJ_STATIC(globals);
366 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
367 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
368 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
369 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
371 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
372 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
374 /*! \brief Defined handlers for XMPP client states */
375 static const struct xmpp_state_handler {
378 int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
379 } xmpp_state_handlers[] = {
380 { XMPP_STATE_REQUEST_TLS, 0, xmpp_client_request_tls, },
381 { XMPP_STATE_REQUESTED_TLS, 0, xmpp_client_requested_tls, },
382 { XMPP_STATE_AUTHENTICATE, 0, xmpp_client_authenticate, },
383 { XMPP_STATE_AUTHENTICATING, 0, xmpp_client_authenticating, },
384 { XMPP_STATE_AUTHENTICATE, 1, xmpp_component_authenticate, },
385 { XMPP_STATE_AUTHENTICATING, 1, xmpp_component_authenticating, },
388 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
389 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
390 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
392 /*! \brief Defined handlers for different PAK types */
393 static const struct xmpp_pak_handler {
395 int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
396 } xmpp_pak_handlers[] = {
397 { IKS_PAK_MESSAGE, xmpp_pak_message, },
398 { IKS_PAK_PRESENCE, xmpp_pak_presence, },
399 { IKS_PAK_S10N, xmpp_pak_s10n, },
402 static const char *app_ajisend = "JabberSend";
403 static const char *app_ajisendgroup = "JabberSendGroup";
404 static const char *app_ajistatus = "JabberStatus";
405 static const char *app_ajijoin = "JabberJoin";
406 static const char *app_ajileave = "JabberLeave";
408 static ast_cond_t message_received_condition;
409 static ast_mutex_t messagelock;
411 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
413 /*! \brief Destructor function for configuration */
414 static void ast_xmpp_client_config_destructor(void *obj)
416 struct ast_xmpp_client_config *cfg = obj;
417 ast_string_field_free_memory(cfg);
418 ao2_cleanup(cfg->client);
419 ao2_cleanup(cfg->buddies);
422 /*! \brief Destroy function for XMPP messages */
423 static void xmpp_message_destroy(struct ast_xmpp_message *message)
426 ast_free(message->from);
428 if (message->message) {
429 ast_free(message->message);
435 /*! \brief Destructor callback function for XMPP client */
436 static void xmpp_client_destructor(void *obj)
438 struct ast_xmpp_client *client = obj;
439 struct ast_xmpp_message *message;
441 ast_xmpp_client_disconnect(client);
443 if (client->filter) {
444 iks_filter_delete(client->filter);
448 iks_stack_delete(client->stack);
451 ao2_cleanup(client->buddies);
453 while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
454 xmpp_message_destroy(message);
456 AST_LIST_HEAD_DESTROY(&client->messages);
459 /*! \brief Hashing function for XMPP buddy */
460 static int xmpp_buddy_hash(const void *obj, const int flags)
462 const struct ast_xmpp_buddy *buddy = obj;
463 const char *id = obj;
465 return ast_str_hash(flags & OBJ_KEY ? id : buddy->id);
468 /*! \brief Comparator function for XMPP buddy */
469 static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
471 struct ast_xmpp_buddy *buddy1 = obj, *buddy2 = arg;
472 const char *id = arg;
474 return !strcmp(buddy1->id, flags & OBJ_KEY ? id : buddy2->id) ? CMP_MATCH | CMP_STOP : 0;
477 /*! \brief Allocator function for ast_xmpp_client */
478 static struct ast_xmpp_client *xmpp_client_alloc(const char *name)
480 struct ast_xmpp_client *client;
482 if (!(client = ao2_alloc(sizeof(*client), xmpp_client_destructor))) {
486 AST_LIST_HEAD_INIT(&client->messages);
487 client->thread = AST_PTHREADT_NULL;
489 if (!(client->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
490 ast_log(LOG_ERROR, "Could not initialize buddy container for '%s'\n", name);
495 if (ast_string_field_init(client, 512)) {
496 ast_log(LOG_ERROR, "Could not initialize stringfields for '%s'\n", name);
501 if (!(client->stack = iks_stack_new(8192, 8192))) {
502 ast_log(LOG_ERROR, "Could not create an Iksemel stack for '%s'\n", name);
507 ast_string_field_set(client, name, name);
509 client->timeout = 50;
510 client->state = XMPP_STATE_DISCONNECTED;
511 ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
516 /*! \brief Find function for configuration */
517 static void *xmpp_config_find(struct ao2_container *tmp_container, const char *category)
519 return ao2_find(tmp_container, category, OBJ_KEY);
522 /*! \brief Look up existing client or create a new one */
523 static void *xmpp_client_find_or_create(const char *category)
525 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
526 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
528 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, category))) {
529 return xmpp_client_alloc(category);
532 ao2_ref(clientcfg->client, +1);
533 return clientcfg->client;
536 /*! \brief Allocator function for configuration */
537 static void *ast_xmpp_client_config_alloc(const char *cat)
539 struct ast_xmpp_client_config *cfg;
541 if (!(cfg = ao2_alloc(sizeof(*cfg), ast_xmpp_client_config_destructor))) {
545 if (ast_string_field_init(cfg, 512)) {
550 if (!(cfg->client = xmpp_client_find_or_create(cat))) {
555 if (!(cfg->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
560 ast_string_field_set(cfg, name, cat);
565 /*! \brief Destructor for XMPP configuration */
566 static void xmpp_config_destructor(void *obj)
568 struct xmpp_config *cfg = obj;
569 ao2_cleanup(cfg->global);
570 ao2_cleanup(cfg->clients);
573 /*! \brief Hashing function for configuration */
574 static int xmpp_config_hash(const void *obj, const int flags)
576 const struct ast_xmpp_client_config *cfg = obj;
577 const char *name = (flags & OBJ_KEY) ? obj : cfg->name;
578 return ast_str_case_hash(name);
581 /*! \brief Comparator function for configuration */
582 static int xmpp_config_cmp(void *obj, void *arg, int flags)
584 struct ast_xmpp_client_config *one = obj, *two = arg;
585 const char *match = (flags & OBJ_KEY) ? arg : two->name;
586 return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
589 /*! \brief Allocator for XMPP configuration */
590 static void *xmpp_config_alloc(void)
592 struct xmpp_config *cfg;
594 if (!(cfg = ao2_alloc(sizeof(*cfg), xmpp_config_destructor))) {
598 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
602 ast_set_flag(&cfg->global->general, XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE);
604 if (!(cfg->clients = ao2_container_alloc(1, xmpp_config_hash, xmpp_config_cmp))) {
614 static int xmpp_config_prelink(void *newitem)
616 struct ast_xmpp_client_config *clientcfg = newitem;
617 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
618 RAII_VAR(struct ast_xmpp_client_config *, oldclientcfg, NULL, ao2_cleanup);
620 if (ast_strlen_zero(clientcfg->user)) {
621 ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
623 } else if (ast_strlen_zero(clientcfg->password)) {
624 ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
626 } else if (ast_strlen_zero(clientcfg->server)) {
627 ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
631 /* If this is a new connection force a reconnect */
632 if (!cfg || !cfg->clients || !(oldclientcfg = xmpp_config_find(cfg->clients, clientcfg->name))) {
633 clientcfg->client->reconnect = 1;
637 /* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
638 if (strcmp(clientcfg->user, oldclientcfg->user) ||
639 strcmp(clientcfg->password, oldclientcfg->password) ||
640 strcmp(clientcfg->server, oldclientcfg->server) ||
641 (clientcfg->port != oldclientcfg->port) ||
642 (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
643 (clientcfg->priority != oldclientcfg->priority)) {
644 clientcfg->client->reconnect = 1;
646 clientcfg->client->reconnect = 0;
652 static void xmpp_config_post_apply(void)
654 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
656 ao2_callback(cfg->clients, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_config_post_apply, NULL);
659 static struct aco_type global_option = {
661 .item_offset = offsetof(struct xmpp_config, global),
662 .category_match = ACO_WHITELIST,
663 .category = "^general$",
666 struct aco_type *global_options[] = ACO_TYPES(&global_option);
668 static struct aco_type client_option = {
670 .category_match = ACO_BLACKLIST,
671 .category = "^(general)$",
672 .item_alloc = ast_xmpp_client_config_alloc,
673 .item_find = xmpp_config_find,
674 .item_prelink = xmpp_config_prelink,
675 .item_offset = offsetof(struct xmpp_config, clients),
678 struct aco_type *client_options[] = ACO_TYPES(&client_option);
680 struct aco_file res_xmpp_conf = {
681 .filename = "xmpp.conf",
682 .alias = "jabber.conf",
683 .types = ACO_TYPES(&global_option, &client_option),
686 CONFIG_INFO_STANDARD(cfg_info, globals, xmpp_config_alloc,
687 .files = ACO_FILES(&res_xmpp_conf),
688 .post_apply_config = xmpp_config_post_apply,
691 /*! \brief Destructor callback function for XMPP resource */
692 static void xmpp_resource_destructor(void *obj)
694 struct ast_xmpp_resource *resource = obj;
696 if (resource->description) {
697 ast_free(resource->description);
701 /*! \brief Hashing function for XMPP resource */
702 static int xmpp_resource_hash(const void *obj, const int flags)
704 const struct ast_xmpp_resource *resource = obj;
706 return flags & OBJ_KEY ? -1 : resource->priority;
709 /*! \brief Comparator function for XMPP resource */
710 static int xmpp_resource_cmp(void *obj, void *arg, int flags)
712 struct ast_xmpp_resource *resource1 = obj, *resource2 = arg;
713 const char *resource = arg;
715 return !strcmp(resource1->resource, flags & OBJ_KEY ? resource : resource2->resource) ? CMP_MATCH | CMP_STOP : 0;
718 /*! \brief Destructor callback function for XMPP buddy */
719 static void xmpp_buddy_destructor(void *obj)
721 struct ast_xmpp_buddy *buddy = obj;
723 if (buddy->resources) {
724 ao2_ref(buddy->resources, -1);
728 /*! \brief Helper function which returns whether an XMPP client connection is secure or not */
729 static int xmpp_is_secure(struct ast_xmpp_client *client)
732 return client->stream_flags & SECURE;
738 struct ast_xmpp_client *ast_xmpp_client_find(const char *name)
740 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
741 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
743 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
747 ao2_ref(clientcfg->client, +1);
748 return clientcfg->client;
751 void ast_xmpp_client_unref(struct ast_xmpp_client *client)
756 void ast_xmpp_client_lock(struct ast_xmpp_client *client)
761 void ast_xmpp_client_unlock(struct ast_xmpp_client *client)
766 /*! \brief Internal function used to send a message to a user or chatroom */
767 static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
769 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
770 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
772 char from[XMPP_MAX_JIDLEN];
775 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
776 !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
780 if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
781 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
783 snprintf(from, sizeof(from), "%s", client->jid->full);
786 iks_insert_attrib(message_packet, "from", from);
788 res = ast_xmpp_client_send(client, message_packet);
790 iks_delete(message_packet);
795 int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
797 return xmpp_client_send_message(client, 0, NULL, user, message);
800 int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
803 iks *invite, *body = NULL, *namespace = NULL;
805 if (!(invite = iks_new("message")) || !(body = iks_new("body")) || !(namespace = iks_new("x"))) {
810 iks_insert_attrib(invite, "to", user);
811 ast_xmpp_client_lock(client);
812 iks_insert_attrib(invite, "id", client->mid);
813 ast_xmpp_increment_mid(client->mid);
814 ast_xmpp_client_unlock(client);
815 iks_insert_cdata(body, message, 0);
816 iks_insert_node(invite, body);
817 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
818 iks_insert_attrib(namespace, "jid", room);
819 iks_insert_node(invite, namespace);
821 res = ast_xmpp_client_send(client, invite);
824 iks_delete(namespace);
831 static int xmpp_client_set_group_presence(struct ast_xmpp_client *client, const char *room, int level, const char *nick)
833 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
834 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
836 iks *presence = NULL, *x = NULL;
837 char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
839 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
840 !(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
845 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
846 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
847 snprintf(roomid, sizeof(roomid), "%s/%s", room, nick);
849 snprintf(from, sizeof(from), "%s", client->jid->full);
850 snprintf(roomid, sizeof(roomid), "%s/%s", room, S_OR(nick, client->jid->user));
853 iks_insert_attrib(presence, "to", roomid);
854 iks_insert_attrib(presence, "from", from);
855 iks_insert_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
856 iks_insert_node(presence, x);
858 res = ast_xmpp_client_send(client, presence);
862 iks_delete(presence);
867 int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
869 return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
872 int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
874 return xmpp_client_send_message(client, 1, nickname, address, message);
877 int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
879 return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
882 void ast_xmpp_increment_mid(char *mid)
886 for (i = strlen(mid) - 1; i >= 0; i--) {
897 * \brief Create an IQ packet
898 * \param client the configured XMPP client we use to connect to a XMPP server
899 * \param type the type of IQ packet to create
902 static iks* xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
904 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
905 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
908 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
909 !(request = iks_new("iq"))) {
913 if (!ast_strlen_zero(clientcfg->pubsubnode)) {
914 iks_insert_attrib(request, "to", clientcfg->pubsubnode);
917 iks_insert_attrib(request, "from", client->jid->full);
918 iks_insert_attrib(request, "type", type);
919 ast_xmpp_client_lock(client);
920 ast_xmpp_increment_mid(client->mid);
921 iks_insert_attrib(request, "id", client->mid);
922 ast_xmpp_client_unlock(client);
928 * \brief Build the skeleton of a publish
929 * \param client the configured XMPP client we use to connect to a XMPP server
930 * \param node Name of the node that will be published to
934 static iks* xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node,
935 const char *event_type, unsigned int cachable)
937 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
938 iks *request, *pubsub, *publish, *item;
940 if (!cfg || !cfg->global || !(request = xmpp_pubsub_iq_create(client, "set"))) {
944 pubsub = iks_insert(request, "pubsub");
945 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
946 publish = iks_insert(pubsub, "publish");
947 iks_insert_attrib(publish, "node", ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248) ? node : event_type);
948 item = iks_insert(publish, "item");
949 iks_insert_attrib(item, "id", node);
951 if (cachable == AST_DEVSTATE_NOT_CACHABLE) {
952 iks *options, *x, *field_form_type, *field_persist;
954 options = iks_insert(pubsub, "publish-options");
955 x = iks_insert(options, "x");
956 iks_insert_attrib(x, "xmlns", "jabber:x:data");
957 iks_insert_attrib(x, "type", "submit");
958 field_form_type = iks_insert(x, "field");
959 iks_insert_attrib(field_form_type, "var", "FORM_TYPE");
960 iks_insert_attrib(field_form_type, "type", "hidden");
961 iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options", 0);
962 field_persist = iks_insert(x, "field");
963 iks_insert_attrib(field_persist, "var", "pubsub#persist_items");
964 iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1);
971 static iks* xmpp_pubsub_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
973 iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
974 *field_deliver_payload, *field_persist_items, *field_access_model,
975 *field_pubsub_collection;
976 configure = iks_insert(pubsub, "configure");
977 x = iks_insert(configure, "x");
978 iks_insert_attrib(x, "xmlns", "jabber:x:data");
979 iks_insert_attrib(x, "type", "submit");
980 field_owner = iks_insert(x, "field");
981 iks_insert_attrib(field_owner, "var", "FORM_TYPE");
982 iks_insert_attrib(field_owner, "type", "hidden");
983 iks_insert_cdata(iks_insert(field_owner, "value"),
984 "http://jabber.org/protocol/pubsub#owner", 39);
986 field_node_type = iks_insert(x, "field");
987 iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
988 iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
990 field_node_config = iks_insert(x, "field");
991 iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
992 iks_insert_attrib(field_node_config, "type", "hidden");
993 iks_insert_cdata(iks_insert(field_node_config, "value"),
994 "http://jabber.org/protocol/pubsub#node_config", 45);
995 field_deliver_payload = iks_insert(x, "field");
996 iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
997 iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
998 field_persist_items = iks_insert(x, "field");
999 iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
1000 iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
1001 field_access_model = iks_insert(x, "field");
1002 iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
1003 iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
1004 if (node_type && !strcasecmp(node_type, "leaf")) {
1005 field_pubsub_collection = iks_insert(x, "field");
1006 iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
1007 iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
1008 strlen(collection_name));
1014 * \brief Add Owner affiliations for pubsub node
1015 * \param client the configured XMPP client we use to connect to a XMPP server
1016 * \param node the name of the node to which to add affiliations
1019 static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
1021 iks *modify_affiliates = xmpp_pubsub_iq_create(client, "set");
1022 iks *pubsub, *affiliations, *affiliate;
1023 struct ao2_iterator i;
1024 struct ast_xmpp_buddy *buddy;
1026 if (!modify_affiliates) {
1027 ast_log(LOG_ERROR, "Could not create IQ for creating affiliations on client '%s'\n", client->name);
1031 pubsub = iks_insert(modify_affiliates, "pubsub");
1032 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
1033 affiliations = iks_insert(pubsub, "affiliations");
1034 iks_insert_attrib(affiliations, "node", node);
1036 i = ao2_iterator_init(client->buddies, 0);
1037 while ((buddy = ao2_iterator_next(&i))) {
1038 affiliate = iks_insert(affiliations, "affiliation");
1039 iks_insert_attrib(affiliate, "jid", buddy->id);
1040 iks_insert_attrib(affiliate, "affiliation", "owner");
1043 ao2_iterator_destroy(&i);
1045 ast_xmpp_client_send(client, modify_affiliates);
1046 iks_delete(modify_affiliates);
1050 * \brief Create a pubsub node
1051 * \param client the configured XMPP client we use to connect to a XMPP server
1052 * \param node_type the type of node to create
1053 * \param name the name of the node to create
1054 * \param collection_name
1057 static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const
1058 char *name, const char *collection_name)
1060 iks *node, *pubsub, *create;
1062 if (!(node = xmpp_pubsub_iq_create(client, "set"))) {
1066 pubsub = iks_insert(node, "pubsub");
1067 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1068 create = iks_insert(pubsub, "create");
1069 iks_insert_attrib(create, "node", name);
1070 xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
1071 ast_xmpp_client_send(client, node);
1072 xmpp_pubsub_create_affiliations(client, name);
1077 * \brief Delete a PubSub node
1078 * \param client the configured XMPP client we use to connect to a XMPP server
1079 * \param node_name the name of the node to delete
1082 static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
1084 iks *request, *pubsub, *delete;
1086 if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
1090 pubsub = iks_insert(request, "pubsub");
1091 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
1092 delete = iks_insert(pubsub, "delete");
1093 iks_insert_attrib(delete, "node", node_name);
1094 ast_xmpp_client_send(client, request);
1098 iks_delete(request);
1102 * \brief Create a PubSub collection node.
1103 * \param client the configured XMPP client we use to connect to a XMPP server
1104 * \param collection_name The name to use for this collection
1107 static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
1109 xmpp_pubsub_create_node(client, "collection", collection_name, NULL);
1114 * \brief Create a PubSub leaf node.
1115 * \param client the configured XMPP client we use to connect to a XMPP server
1116 * \param collection_name
1117 * \param leaf_name The name to use for this collection
1120 static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name,
1121 const char *leaf_name)
1123 xmpp_pubsub_create_node(client, "leaf", leaf_name, collection_name);
1127 * \brief Publish MWI to a PubSub node
1128 * \param client the configured XMPP client we use to connect to a XMPP server
1129 * \param mailbox The Mailbox
1130 * \param context The Context
1131 * \param oldmsgs Old messages
1132 * \param newmsgs New Messages
1135 static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox,
1136 const char *context, const char *oldmsgs, const char *newmsgs)
1138 char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT], eid_str[20];
1139 iks *mailbox_node, *request;
1141 snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
1143 if (!(request = xmpp_pubsub_build_publish_skeleton(client, full_mailbox, "message_waiting", AST_DEVSTATE_CACHABLE))) {
1147 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1148 mailbox_node = iks_insert(request, "mailbox");
1149 iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
1150 iks_insert_attrib(mailbox_node, "eid", eid_str);
1151 iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
1152 iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
1154 ast_xmpp_client_send(client, iks_root(request));
1156 iks_delete(request);
1160 * \brief Publish device state to a PubSub node
1161 * \param client the configured XMPP client we use to connect to a XMPP server
1162 * \param device the name of the device whose state to publish
1163 * \param device_state the state to publish
1166 static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device,
1167 const char *device_state, unsigned int cachable)
1169 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1170 iks *request, *state;
1171 char eid_str[20], cachable_str[2];
1173 if (!cfg || !cfg->global || !(request = xmpp_pubsub_build_publish_skeleton(client, device, "device_state", cachable))) {
1177 if (ast_test_flag(&cfg->global->pubsub, XMPP_PUBSUB_AUTOCREATE)) {
1178 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1179 xmpp_pubsub_create_node(client, "leaf", device, "device_state");
1181 xmpp_pubsub_create_node(client, NULL, device, NULL);
1185 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1186 state = iks_insert(request, "state");
1187 iks_insert_attrib(state, "xmlns", "http://asterisk.org");
1188 iks_insert_attrib(state, "eid", eid_str);
1189 snprintf(cachable_str, sizeof(cachable_str), "%u", cachable);
1190 iks_insert_attrib(state, "cachable", cachable_str);
1191 iks_insert_cdata(state, device_state, strlen(device_state));
1192 ast_xmpp_client_send(client, iks_root(request));
1193 iks_delete(request);
1197 * \brief Callback function for MWI events
1199 * \param data void pointer to ast_client structure
1202 static void xmpp_pubsub_mwi_cb(const struct ast_event *ast_event, void *data)
1204 struct ast_xmpp_client *client = data;
1205 const char *mailbox, *context;
1206 char oldmsgs[10], newmsgs[10];
1208 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
1209 /* If the event didn't originate from this server, don't send it back out. */
1210 ast_debug(1, "Returning here\n");
1214 mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
1215 context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
1216 snprintf(oldmsgs, sizeof(oldmsgs), "%d",
1217 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
1218 snprintf(newmsgs, sizeof(newmsgs), "%d",
1219 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
1220 xmpp_pubsub_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
1224 * \brief Callback function for device state events
1226 * \param data void pointer to ast_client structure
1229 static void xmpp_pubsub_devstate_cb(const struct ast_event *ast_event, void *data)
1231 struct ast_xmpp_client *client = data;
1232 const char *device, *device_state;
1233 unsigned int cachable;
1235 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
1236 /* If the event didn't originate from this server, don't send it back out. */
1237 ast_debug(1, "Returning here\n");
1241 device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
1242 device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
1243 cachable = ast_event_get_ie_uint(ast_event, AST_EVENT_IE_CACHABLE);
1244 xmpp_pubsub_publish_device_state(client, device, device_state, cachable);
1248 * \brief Unsubscribe from a PubSub node
1249 * \param client the configured XMPP client we use to connect to a XMPP server
1250 * \param node the name of the node to which to unsubscribe from
1253 static void xmpp_pubsub_unsubscribe(struct ast_xmpp_client *client, const char *node)
1255 iks *request = xmpp_pubsub_iq_create(client, "set");
1256 iks *pubsub, *unsubscribe;
1259 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub unsubscription on client '%s'\n", client->name);
1263 pubsub = iks_insert(request, "pubsub");
1264 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1265 unsubscribe = iks_insert(pubsub, "unsubscribe");
1266 iks_insert_attrib(unsubscribe, "jid", client->jid->partial);
1267 iks_insert_attrib(unsubscribe, "node", node);
1269 ast_xmpp_client_send(client, request);
1270 iks_delete(request);
1274 * \brief Subscribe to a PubSub node
1275 * \param client the configured XMPP client we use to connect to a XMPP server
1276 * \param node the name of the node to which to subscribe
1279 static void xmpp_pubsub_subscribe(struct ast_xmpp_client *client, const char *node)
1281 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1282 iks *request = xmpp_pubsub_iq_create(client, "set");
1283 iks *pubsub, *subscribe;
1285 if (!cfg || !cfg->global || !request) {
1286 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub subscription on client '%s'\n", client->name);
1290 pubsub = iks_insert(request, "pubsub");
1291 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1292 subscribe = iks_insert(pubsub, "subscribe");
1293 iks_insert_attrib(subscribe, "jid", client->jid->partial);
1294 iks_insert_attrib(subscribe, "node", node);
1295 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1296 iks *options, *x, *sub_options, *sub_type, *sub_depth, *sub_expire;
1297 options = iks_insert(pubsub, "options");
1298 x = iks_insert(options, "x");
1299 iks_insert_attrib(x, "xmlns", "jabber:x:data");
1300 iks_insert_attrib(x, "type", "submit");
1301 sub_options = iks_insert(x, "field");
1302 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
1303 iks_insert_attrib(sub_options, "type", "hidden");
1304 iks_insert_cdata(iks_insert(sub_options, "value"),
1305 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
1306 sub_type = iks_insert(x, "field");
1307 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
1308 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
1309 sub_depth = iks_insert(x, "field");
1310 iks_insert_attrib(sub_depth, "var", "pubsub#subscription_depth");
1311 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
1312 sub_expire = iks_insert(x, "field");
1313 iks_insert_attrib(sub_expire, "var", "pubsub#expire");
1314 iks_insert_cdata(iks_insert(sub_expire, "value"), "presence", 8);
1316 ast_xmpp_client_send(client, request);
1317 iks_delete(request);
1321 * \brief Callback for handling PubSub events
1322 * \param data void pointer to ast_xmpp_client structure
1324 * \return IKS_FILTER_EAT
1326 static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
1328 char *item_id, *device_state, *context, *cachable_str;
1329 int oldmsgs, newmsgs;
1330 iks *item, *item_content;
1331 struct ast_eid pubsub_eid;
1332 struct ast_event *event;
1333 unsigned int cachable = AST_DEVSTATE_CACHABLE;
1334 item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
1336 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
1337 return IKS_FILTER_EAT;
1339 item_id = iks_find_attrib(item, "id");
1340 item_content = iks_child(item);
1341 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
1342 if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
1343 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
1344 return IKS_FILTER_EAT;
1346 if (!strcasecmp(iks_name(item_content), "state")) {
1347 device_state = iks_find_cdata(item, "state");
1348 if ((cachable_str = iks_find_cdata(item, "cachable"))) {
1349 sscanf(cachable_str, "%30d", &cachable);
1351 if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
1352 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
1353 AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
1354 AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
1355 AST_EVENT_IE_END))) {
1356 return IKS_FILTER_EAT;
1358 } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
1359 context = strsep(&item_id, "@");
1360 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
1361 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
1362 if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
1363 AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
1364 AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
1365 AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
1366 AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
1367 &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
1368 return IKS_FILTER_EAT;
1371 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
1372 iks_name(item_content));
1373 return IKS_FILTER_EAT;
1376 if (cachable == AST_DEVSTATE_CACHABLE) {
1377 ast_event_queue_and_cache(event);
1379 ast_event_queue(event);
1382 return IKS_FILTER_EAT;
1385 static int xmpp_pubsub_handle_error(void *data, ikspak *pak)
1387 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1388 char *node_name, *error;
1390 iks *orig_request, *orig_pubsub = iks_find(pak->x, "pubsub");
1391 struct ast_xmpp_client *client = data;
1393 if (!cfg || !cfg->global) {
1394 ast_log(LOG_ERROR, "No global configuration available\n");
1395 return IKS_FILTER_EAT;
1399 ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
1400 return IKS_FILTER_EAT;
1403 orig_request = iks_child(orig_pubsub);
1404 error = iks_find_attrib(iks_find(pak->x, "error"), "code");
1405 node_name = iks_find_attrib(orig_request, "node");
1407 if (!sscanf(error, "%30d", &error_num)) {
1408 return IKS_FILTER_EAT;
1411 if (error_num > 399 && error_num < 500 && error_num != 404) {
1413 "Error performing operation on PubSub node %s, %s.\n", node_name, error);
1414 return IKS_FILTER_EAT;
1415 } else if (error_num > 499 && error_num < 600) {
1416 ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
1417 return IKS_FILTER_EAT;
1420 if (!strcasecmp(iks_name(orig_request), "publish")) {
1423 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1424 if (iks_find(iks_find(orig_request, "item"), "state")) {
1425 xmpp_pubsub_create_leaf(client, "device_state", node_name);
1426 } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
1427 xmpp_pubsub_create_leaf(client, "message_waiting", node_name);
1430 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1433 if ((request = xmpp_pubsub_iq_create(client, "set"))) {
1434 iks_insert_node(request, orig_pubsub);
1435 ast_xmpp_client_send(client, request);
1436 iks_delete(request);
1438 ast_log(LOG_ERROR, "PubSub publish could not create IQ\n");
1441 return IKS_FILTER_EAT;
1442 } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
1443 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1444 xmpp_pubsub_create_collection(client, node_name);
1446 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1450 return IKS_FILTER_EAT;
1454 * \brief Initialize collections for event distribution
1455 * \param client the configured XMPP client we use to connect to a XMPP server
1458 static void xmpp_init_event_distribution(struct ast_xmpp_client *client)
1460 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1461 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1463 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
1467 xmpp_pubsub_unsubscribe(client, "device_state");
1468 xmpp_pubsub_unsubscribe(client, "message_waiting");
1470 if (!(client->mwi_sub = ast_event_subscribe(AST_EVENT_MWI, xmpp_pubsub_mwi_cb, "xmpp_pubsub_mwi_subscription",
1471 client, AST_EVENT_IE_END))) {
1475 if (ast_enable_distributed_devstate()) {
1480 if (!(client->device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
1481 xmpp_pubsub_devstate_cb, "xmpp_pubsub_devstate_subscription", client, AST_EVENT_IE_END))) {
1482 ast_event_unsubscribe(client->mwi_sub);
1483 client->mwi_sub = NULL;
1487 ast_event_dump_cache(client->device_state_sub);
1489 xmpp_pubsub_subscribe(client, "device_state");
1490 xmpp_pubsub_subscribe(client, "message_waiting");
1491 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_event, client, IKS_RULE_TYPE,
1492 IKS_PAK_MESSAGE, IKS_RULE_FROM, clientcfg->pubsubnode, IKS_RULE_DONE);
1493 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_error, client, IKS_RULE_TYPE,
1494 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
1498 /*! \brief Internal astobj2 callback function which returns the first resource, which is the highest priority one */
1499 static int xmpp_resource_immediate(void *obj, void *arg, int flags)
1501 return CMP_MATCH | CMP_STOP;
1506 * \brief Dial plan function status(). puts the status of watched user
1507 * into a channel variable.
1508 * \param chan ast_channel
1513 static int xmpp_status_exec(struct ast_channel *chan, const char *data)
1515 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1516 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1517 struct ast_xmpp_buddy *buddy;
1518 struct ast_xmpp_resource *resource;
1519 char *s = NULL, status[2];
1521 static int deprecation_warning = 0;
1522 AST_DECLARE_APP_ARGS(args,
1523 AST_APP_ARG(sender);
1525 AST_APP_ARG(variable);
1527 AST_DECLARE_APP_ARGS(jid,
1528 AST_APP_ARG(screenname);
1529 AST_APP_ARG(resource);
1532 if (deprecation_warning++ % 10 == 0) {
1533 ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
1536 if (ast_strlen_zero(data)) {
1537 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
1540 s = ast_strdupa(data);
1541 AST_STANDARD_APP_ARGS(args, s);
1543 if (args.argc != 3) {
1544 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
1548 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1549 if (jid.argc < 1 || jid.argc > 2) {
1550 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1554 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1555 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1559 if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
1560 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
1564 if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
1565 resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
1571 stat = resource->status;
1572 ao2_ref(resource, -1);
1574 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
1577 snprintf(status, sizeof(status), "%d", stat);
1578 pbx_builtin_setvar_helper(chan, args.variable, status);
1585 * \brief Dial plan funtcion to retrieve the status of a buddy.
1586 * \param channel The associated ast_channel, if there is one
1587 * \param data The account, buddy JID, and optional timeout
1590 * \retval -1 failure
1592 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1594 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1595 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1596 struct ast_xmpp_buddy *buddy;
1597 struct ast_xmpp_resource *resource;
1599 AST_DECLARE_APP_ARGS(args,
1600 AST_APP_ARG(sender);
1603 AST_DECLARE_APP_ARGS(jid,
1604 AST_APP_ARG(screenname);
1605 AST_APP_ARG(resource);
1608 if (ast_strlen_zero(data)) {
1609 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
1612 AST_STANDARD_APP_ARGS(args, data);
1614 if (args.argc != 2) {
1615 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
1619 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1620 if (jid.argc < 1 || jid.argc > 2) {
1621 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1625 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1626 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1630 if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
1631 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
1635 if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
1636 resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
1642 stat = resource->status;
1643 ao2_ref(resource, -1);
1645 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
1648 snprintf(buf, buflen, "%d", stat);
1653 static struct ast_custom_function jabberstatus_function = {
1654 .name = "JABBER_STATUS",
1655 .read = acf_jabberstatus_read,
1659 * \brief Application to join a chat room
1660 * \param chan ast_channel
1661 * \param data Data is sender|jid|nickname.
1665 static int xmpp_join_exec(struct ast_channel *chan, const char *data)
1667 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1668 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1669 char *s, nick[XMPP_MAX_RESJIDLEN];
1670 AST_DECLARE_APP_ARGS(args,
1671 AST_APP_ARG(sender);
1676 if (ast_strlen_zero(data)) {
1677 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1680 s = ast_strdupa(data);
1682 AST_STANDARD_APP_ARGS(args, s);
1683 if (args.argc < 2 || args.argc > 3) {
1684 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1688 if (strchr(args.jid, '/')) {
1689 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1693 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1694 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1698 if (ast_strlen_zero(args.nick)) {
1699 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1700 snprintf(nick, sizeof(nick), "asterisk");
1702 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1705 snprintf(nick, sizeof(nick), "%s", args.nick);
1708 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1709 ast_xmpp_chatroom_join(clientcfg->client, args.jid, nick);
1711 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1718 * \brief Application to leave a chat room
1719 * \param chan ast_channel
1720 * \param data Data is sender|jid|nickname.
1724 static int xmpp_leave_exec(struct ast_channel *chan, const char *data)
1726 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1727 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1728 char *s, nick[XMPP_MAX_RESJIDLEN];
1729 AST_DECLARE_APP_ARGS(args,
1730 AST_APP_ARG(sender);
1735 if (ast_strlen_zero(data)) {
1736 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1739 s = ast_strdupa(data);
1741 AST_STANDARD_APP_ARGS(args, s);
1742 if (args.argc < 2 || args.argc > 3) {
1743 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1747 if (strchr(args.jid, '/')) {
1748 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1752 if (ast_strlen_zero(args.jid) || !strchr(args.jid, '@')) {
1753 ast_log(LOG_ERROR, "No jabber ID specified\n");
1757 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1758 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1762 if (ast_strlen_zero(args.nick)) {
1763 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1764 snprintf(nick, sizeof(nick), "asterisk");
1766 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1769 snprintf(nick, sizeof(nick), "%s", args.nick);
1772 ast_xmpp_chatroom_leave(clientcfg->client, args.jid, nick);
1779 * \brief Dial plan function to send a message.
1780 * \param chan ast_channel
1781 * \param data Data is account,jid,message.
1783 * \retval -1 failure
1785 static int xmpp_send_exec(struct ast_channel *chan, const char *data)
1787 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1788 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1790 AST_DECLARE_APP_ARGS(args,
1791 AST_APP_ARG(sender);
1792 AST_APP_ARG(recipient);
1793 AST_APP_ARG(message);
1796 if (ast_strlen_zero(data)) {
1797 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1800 s = ast_strdupa(data);
1802 AST_STANDARD_APP_ARGS(args, s);
1804 if ((args.argc < 3) || ast_strlen_zero(args.message) || !strchr(args.recipient, '@')) {
1805 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1809 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1810 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1814 ast_xmpp_client_send_message(clientcfg->client, args.recipient, args.message);
1820 * \brief Application to send a message to a groupchat.
1821 * \param chan ast_channel
1822 * \param data Data is sender|groupchat|message.
1826 static int xmpp_sendgroup_exec(struct ast_channel *chan, const char *data)
1828 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1829 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1830 char *s, nick[XMPP_MAX_RESJIDLEN];
1831 AST_DECLARE_APP_ARGS(args,
1832 AST_APP_ARG(sender);
1833 AST_APP_ARG(groupchat);
1834 AST_APP_ARG(message);
1838 if (ast_strlen_zero(data)) {
1839 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1842 s = ast_strdupa(data);
1844 AST_STANDARD_APP_ARGS(args, s);
1845 if ((args.argc < 3) || (args.argc > 4) || ast_strlen_zero(args.message) || !strchr(args.groupchat, '@')) {
1846 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1850 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1851 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1855 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1856 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1857 snprintf(nick, sizeof(nick), "asterisk");
1859 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1862 snprintf(nick, sizeof(nick), "%s", args.nick);
1865 ast_xmpp_chatroom_send(clientcfg->client, nick, args.groupchat, args.message);
1872 * \brief Dial plan function to receive a message.
1873 * \param channel The associated ast_channel, if there is one
1874 * \param data The account, JID, and optional timeout
1877 * \retval -1 failure
1879 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1881 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
1882 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1884 int timeout, jidlen, resourcelen, found = 0;
1885 struct timeval start;
1887 struct ast_xmpp_message *message;
1888 AST_DECLARE_APP_ARGS(args,
1889 AST_APP_ARG(account);
1891 AST_APP_ARG(timeout);
1893 AST_DECLARE_APP_ARGS(jid,
1894 AST_APP_ARG(screenname);
1895 AST_APP_ARG(resource);
1898 if (ast_strlen_zero(data)) {
1899 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1903 parse = ast_strdupa(data);
1904 AST_STANDARD_APP_ARGS(args, parse);
1906 if (args.argc < 2 || args.argc > 3) {
1907 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1911 parse = ast_strdupa(args.jid);
1912 AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
1913 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > XMPP_MAX_JIDLEN) {
1914 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
1918 if (ast_strlen_zero(args.timeout)) {
1921 sscanf(args.timeout, "%d", &timeout);
1923 ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
1928 jidlen = strlen(jid.screenname);
1929 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
1931 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.account))) {
1932 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
1936 ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
1938 start = ast_tvnow();
1940 if (ast_autoservice_start(chan) < 0) {
1941 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
1945 /* search the messages list, grab the first message that matches with
1946 * the from JID we're expecting, and remove it from the messages list */
1947 while (diff < timeout) {
1948 struct timespec ts = { 0, };
1949 struct timeval wait;
1952 wait = ast_tvadd(start, ast_tv(timeout, 0));
1953 ts.tv_sec = wait.tv_sec;
1954 ts.tv_nsec = wait.tv_usec * 1000;
1956 /* wait up to timeout seconds for an incoming message */
1957 ast_mutex_lock(&messagelock);
1958 if (AST_LIST_EMPTY(&clientcfg->client->messages)) {
1959 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
1961 ast_mutex_unlock(&messagelock);
1962 if (res == ETIMEDOUT) {
1963 ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
1967 AST_LIST_LOCK(&clientcfg->client->messages);
1968 AST_LIST_TRAVERSE_SAFE_BEGIN(&clientcfg->client->messages, message, list) {
1969 if (jid.argc == 1) {
1970 /* no resource provided, compare bare JIDs */
1971 if (strncasecmp(jid.screenname, message->from, jidlen)) {
1975 /* resource appended, compare bare JIDs and resources */
1976 char *resource = strchr(message->from, '/');
1977 if (!resource || strlen(resource) == 0) {
1978 ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", message->from);
1979 if (strncasecmp(jid.screenname, message->from, jidlen)) {
1984 if (strncasecmp(jid.screenname, message->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
1989 /* check if the message is not too old */
1990 if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
1991 ast_debug(3, "Found old message from %s, deleting it\n", message->from);
1992 AST_LIST_REMOVE_CURRENT(list);
1993 xmpp_message_destroy(message);
1997 ast_copy_string(buf, message->message, buflen);
1998 AST_LIST_REMOVE_CURRENT(list);
1999 xmpp_message_destroy(message);
2002 AST_LIST_TRAVERSE_SAFE_END;
2003 AST_LIST_UNLOCK(&clientcfg->client->messages);
2009 diff = ast_tvdiff_ms(ast_tvnow(), start);
2012 if (ast_autoservice_stop(chan) < 0) {
2013 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
2016 /* return if we timed out */
2018 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
2025 static struct ast_custom_function jabberreceive_function = {
2026 .name = "JABBER_RECEIVE",
2027 .read = acf_jabberreceive_read,
2032 * \brief Delete old messages from a given JID
2033 * Messages stored during more than client->message_timeout are deleted
2034 * \param client Asterisk's XMPP client
2035 * \param from the JID we received messages from
2036 * \retval the number of deleted messages
2038 static int delete_old_messages(struct ast_xmpp_client *client, char *from)
2040 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2041 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2042 int deleted = 0, isold = 0;
2043 struct ast_xmpp_message *message = NULL;
2045 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2049 AST_LIST_LOCK(&client->messages);
2050 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, message, list) {
2052 if (!from || !strncasecmp(from, message->from, strlen(from))) {
2053 AST_LIST_REMOVE_CURRENT(list);
2054 xmpp_message_destroy(message);
2057 } else if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
2059 if (!from || !strncasecmp(from, message->from, strlen(from))) {
2060 AST_LIST_REMOVE_CURRENT(list);
2061 xmpp_message_destroy(message);
2066 AST_LIST_TRAVERSE_SAFE_END;
2067 AST_LIST_UNLOCK(&client->messages);
2072 static int xmpp_send_cb(const struct ast_msg *msg, const char *to, const char *from)
2074 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2075 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2076 char *sender, *dest;
2079 sender = ast_strdupa(from);
2080 strsep(&sender, ":");
2081 dest = ast_strdupa(to);
2084 if (ast_strlen_zero(sender)) {
2085 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for XMPP\n", from);
2089 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, sender))) {
2090 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
2094 ast_debug(1, "Sending message to '%s' from '%s'\n", dest, clientcfg->name);
2096 if ((res = ast_xmpp_client_send_message(clientcfg->client, dest, ast_msg_get_body(msg))) != IKS_OK) {
2097 ast_log(LOG_WARNING, "Failed to send XMPP message (%d).\n", res);
2100 return res == IKS_OK ? 0 : -1;
2103 static const struct ast_msg_tech msg_tech = {
2105 .msg_send = xmpp_send_cb,
2108 /*! \brief Internal function which changes the XMPP client state */
2109 static void xmpp_client_change_state(struct ast_xmpp_client *client, int state)
2111 client->state = state;
2114 /*! \brief Internal function which creates a buddy on a client */
2115 static struct ast_xmpp_buddy *xmpp_client_create_buddy(struct ao2_container *container, const char *id)
2117 struct ast_xmpp_buddy *buddy;
2119 if (!(buddy = ao2_alloc(sizeof(*buddy), xmpp_buddy_destructor))) {
2123 if (!(buddy->resources = ao2_container_alloc(RESOURCE_BUCKETS, xmpp_resource_hash, xmpp_resource_cmp))) {
2128 ast_copy_string(buddy->id, id, sizeof(buddy->id));
2130 /* Assume we need to subscribe to get their presence until proven otherwise */
2131 buddy->subscribe = 1;
2133 ao2_link(container, buddy);
2138 /*! \brief Helper function which unsubscribes a user and removes them from the roster */
2139 static int xmpp_client_unsubscribe_user(struct ast_xmpp_client *client, const char *user)
2141 iks *iq, *query = NULL, *item = NULL;
2143 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, user,
2144 "Goodbye. Your status is no longer required.\n"))) {
2148 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item"))) {
2149 ast_log(LOG_WARNING, "Could not allocate memory for roster removal of '%s' from client '%s'\n",
2150 user, client->name);
2154 iks_insert_attrib(iq, "from", client->jid->full);
2155 iks_insert_attrib(iq, "type", "set");
2156 iks_insert_attrib(query, "xmlns", "jabber:iq:roster");
2157 iks_insert_node(iq, query);
2158 iks_insert_attrib(item, "jid", user);
2159 iks_insert_attrib(item, "subscription", "remove");
2160 iks_insert_node(query, item);
2162 if (ast_xmpp_client_send(client, iq)) {
2163 ast_log(LOG_WARNING, "Could not send roster removal request of '%s' from client '%s'\n",
2164 user, client->name);
2175 /*! \brief Callback function which subscribes to a user if needed */
2176 static int xmpp_client_subscribe_user(void *obj, void *arg, int flags)
2178 struct ast_xmpp_buddy *buddy = obj;
2179 struct ast_xmpp_client *client = arg;
2181 if (!buddy->subscribe) {
2185 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, buddy->id,
2186 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"))) {
2187 ast_log(LOG_WARNING, "Could not send subscription for '%s' on client '%s'\n",
2188 buddy->id, client->name);
2191 buddy->subscribe = 0;
2196 /*! \brief Hook function called when roster is received from server */
2197 static int xmpp_roster_hook(void *data, ikspak *pak)
2199 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2200 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2201 struct ast_xmpp_client *client = data;
2204 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2205 return IKS_FILTER_EAT;
2208 for (item = iks_child(pak->query); item; item = iks_next(item)) {
2209 struct ast_xmpp_buddy *buddy;
2211 if (iks_strcmp(iks_name(item), "item")) {
2215 if (!(buddy = ao2_find(client->buddies, iks_find_attrib(item, "jid"), OBJ_KEY))) {
2216 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOPRUNE)) {
2217 /* The buddy has not been specified in the configuration file, we no longer
2218 * want them on our buddy list or to receive their presence. */
2219 if (xmpp_client_unsubscribe_user(client, iks_find_attrib(item, "jid"))) {
2220 ast_log(LOG_ERROR, "Could not unsubscribe user '%s' on client '%s'\n",
2221 iks_find_attrib(item, "jid"), client->name);
2226 if (!(buddy = xmpp_client_create_buddy(client->buddies, iks_find_attrib(item, "jid")))) {
2227 ast_log(LOG_ERROR, "Could not allocate buddy '%s' on client '%s'\n", iks_find_attrib(item, "jid"),
2233 /* Determine if we need to subscribe to their presence or not */
2234 if (!iks_strcmp(iks_find_attrib(item, "subscription"), "none") ||
2235 !iks_strcmp(iks_find_attrib(item, "subscription"), "from")) {
2236 buddy->subscribe = 1;
2238 buddy->subscribe = 0;
2244 /* If autoregister is enabled we need to go through every buddy that we need to subscribe to and do so */
2245 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOREGISTER)) {
2246 ao2_callback(client->buddies, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_subscribe_user, client);
2249 xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
2251 return IKS_FILTER_EAT;
2254 /*! \brief Internal function which changes the presence status of an XMPP client */
2255 static void xmpp_client_set_presence(struct ast_xmpp_client *client, const char *to, const char *from, int level, const char *desc)
2257 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2258 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2259 iks *presence = NULL, *cnode = NULL, *priority = NULL;
2262 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2263 !(presence = iks_make_pres(level, desc)) || !(cnode = iks_new("c")) || !(priority = iks_new("priority"))) {
2264 ast_log(LOG_ERROR, "Unable to allocate stanzas for setting presence status for client '%s'\n", client->name);
2268 if (!ast_strlen_zero(to)) {
2269 iks_insert_attrib(presence, "to", to);
2272 if (!ast_strlen_zero(from)) {
2273 iks_insert_attrib(presence, "from", from);
2276 snprintf(priorityS, sizeof(priorityS), "%d", clientcfg->priority);
2277 iks_insert_cdata(priority, priorityS, strlen(priorityS));
2278 iks_insert_node(presence, priority);
2279 iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
2280 iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
2281 iks_insert_attrib(cnode, "ext", "voice-v1 video-v1 camera-v1");
2282 iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
2283 iks_insert_node(presence, cnode);
2284 ast_xmpp_client_send(client, presence);
2288 iks_delete(presence);
2289 iks_delete(priority);
2292 /*! \brief Hook function called when client receives a service discovery get message */
2293 static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
2295 struct ast_xmpp_client *client = data;
2296 iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
2298 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(ident = iks_new("identity")) || !(disco = iks_new("feature")) ||
2299 !(google = iks_new("feature")) || !(jingle = iks_new("feature")) || !(ice = iks_new("feature")) || !(rtp = iks_new("feature")) ||
2300 !(audio = iks_new("feature")) || !(video = iks_new("feature"))) {
2301 ast_log(LOG_ERROR, "Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
2302 pak->from->full, client->name);
2306 iks_insert_attrib(iq, "from", client->jid->full);
2309 iks_insert_attrib(iq, "to", pak->from->full);
2312 iks_insert_attrib(iq, "type", "result");
2313 iks_insert_attrib(iq, "id", pak->id);
2314 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2315 iks_insert_attrib(ident, "category", "client");
2316 iks_insert_attrib(ident, "type", "pc");
2317 iks_insert_attrib(ident, "name", "asterisk");
2318 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2320 iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2321 iks_insert_attrib(jingle, "var", "urn:xmpp:jingle:1");
2322 iks_insert_attrib(ice, "var", "urn:xmpp:jingle:transports:ice-udp:1");
2323 iks_insert_attrib(rtp, "var", "urn:xmpp:jingle:apps:rtp:1");
2324 iks_insert_attrib(audio, "var", "urn:xmpp:jingle:apps:rtp:audio");
2325 iks_insert_attrib(video, "var", "urn:xmpp:jingle:apps:rtp:video");
2326 iks_insert_node(iq, query);
2327 iks_insert_node(query, ident);
2328 iks_insert_node(query, google);
2329 iks_insert_node(query, disco);
2330 iks_insert_node(query, jingle);
2331 iks_insert_node(query, ice);
2332 iks_insert_node(query, rtp);
2333 iks_insert_node(query, audio);
2334 iks_insert_node(query, video);
2335 ast_xmpp_client_send(client, iq);
2349 return IKS_FILTER_EAT;
2352 /*! \brief Hook function called when client receives a service discovery result message */
2353 static int xmpp_client_service_discovery_result_hook(void *data, ikspak *pak)
2355 struct ast_xmpp_client *client = data;
2356 struct ast_xmpp_buddy *buddy;
2357 struct ast_xmpp_resource *resource;
2359 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2360 return IKS_FILTER_EAT;
2363 if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY))) {
2365 return IKS_FILTER_EAT;
2370 if (iks_find_with_attrib(pak->query, "feature", "var", "urn:xmpp:jingle:1")) {
2371 resource->caps.jingle = 1;
2374 ao2_unlock(resource);
2376 ao2_ref(resource, -1);
2379 return IKS_FILTER_EAT;
2382 /*! \brief Hook function called when client finishes authenticating with the server */
2383 static int xmpp_connect_hook(void *data, ikspak *pak)
2385 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2386 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2387 struct ast_xmpp_client *client = data;
2390 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2394 client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
2396 if (ast_test_flag(&clientcfg->flags, XMPP_DISTRIBUTE_EVENTS)) {
2397 xmpp_init_event_distribution(client);
2400 if (!(roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER))) {
2401 ast_log(LOG_ERROR, "Unable to allocate memory for roster request for client '%s'\n", client->name);
2405 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);
2406 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);
2408 iks_insert_attrib(roster, "id", "roster");
2409 ast_xmpp_client_send(client, roster);
2411 iks_filter_remove_hook(client->filter, xmpp_connect_hook);
2412 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);
2414 xmpp_client_set_presence(client, NULL, client->jid->full, clientcfg->status, clientcfg->statusmsg);
2415 xmpp_client_change_state(client, XMPP_STATE_ROSTER);
2417 return IKS_FILTER_EAT;
2420 /*! \brief Logging hook function */
2421 static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incoming)
2423 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2424 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2425 struct ast_xmpp_client *client = data;
2427 if (!ast_strlen_zero(xmpp)) {
2428 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
2431 if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) {
2436 if (strlen(xmpp) == 1) {
2437 if (option_debug > 2 && xmpp[0] == ' ') {
2438 ast_verbose("\n<--- XMPP keep alive from '%s' --->\n", client->name);
2441 ast_verbose("\n<--- XMPP sent to '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2444 ast_verbose("\n<--- XMPP received from '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2448 /*! \brief Internal function which sends a raw message */
2449 static int xmpp_client_send_raw_message(struct ast_xmpp_client *client, const char *message)
2453 int len = strlen(message);
2455 if (xmpp_is_secure(client)) {
2456 ret = SSL_write(client->ssl_session, message, len);
2458 /* Log the message here, because iksemel's logHook is
2460 xmpp_log_hook(client, message, len, 0);
2465 /* If needed, data will be sent unencrypted, and logHook will
2466 be called inside iks_send_raw */
2467 ret = iks_send_raw(client->parser, message);
2468 if (ret != IKS_OK) {
2475 /*! \brief Helper function which sends an XMPP stream header to the server */
2476 static int xmpp_send_stream_header(struct ast_xmpp_client *client, const struct ast_xmpp_client_config *cfg, const char *to)
2478 char *namespace = ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client";
2479 char msg[91 + strlen(namespace) + 6 + strlen(to) + 16 + 1];
2481 snprintf(msg, sizeof(msg), "<?xml version='1.0'?>"
2482 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
2483 "%s' to='%s' version='1.0'>", namespace, to);
2485 return xmpp_client_send_raw_message(client, msg);
2488 int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza)
2490 return xmpp_client_send_raw_message(client, iks_string(iks_stack(stanza), stanza));
2493 /*! \brief Internal function called when we need to request TLS support */
2494 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2496 /* If the client connection is already secure we can jump straight to authenticating */
2497 if (xmpp_is_secure(client)) {
2498 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2502 #ifndef HAVE_OPENSSL
2503 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL is not available.\n", client->name);
2506 if (iks_send_raw(client->parser, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>") == IKS_NET_TLSFAIL) {
2507 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be started.\n", client->name);
2511 client->stream_flags |= TRY_SECURE;
2513 xmpp_client_change_state(client, XMPP_STATE_REQUESTED_TLS);
2519 /*! \brief Internal function called when we receive a response to our TLS initiation request */
2520 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2526 if (!strcmp(iks_name(node), "success")) {
2527 /* TLS is up and working, we can move on to authenticating now */
2528 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2530 } else if (!strcmp(iks_name(node), "failure")) {
2531 /* TLS negotiation was a failure, close it on down! */
2533 } else if (strcmp(iks_name(node), "proceed")) {
2534 /* Ignore any other responses */
2538 #ifndef HAVE_OPENSSL
2539 ast_log(LOG_ERROR, "Somehow we managed to try to start TLS negotiation on client '%s' without OpenSSL support, disconnecting\n", client->name);
2542 client->ssl_method = SSLv3_method();
2543 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
2547 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
2551 sock = iks_fd(client->parser);
2552 if (!SSL_set_fd(client->ssl_session, sock)) {
2556 if (!SSL_connect(client->ssl_session)) {
2560 client->stream_flags &= (~TRY_SECURE);
2561 client->stream_flags |= SECURE;
2563 if (xmpp_send_stream_header(client, cfg, client->jid->server) != IKS_OK) {
2564 ast_log(LOG_ERROR, "TLS connection for client '%s' could not be established, failed to send stream header after negotiation\n",
2569 ast_debug(1, "TLS connection for client '%s' started with server\n", client->name);
2571 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
2576 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL initialization failed.\n", client->name);
2581 /*! \brief Internal function called when we need to authenticate using non-SASL */
2582 static int xmpp_client_authenticate_digest(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2584 iks *iq = NULL, *query = NULL;
2585 char buf[41], sidpass[100];
2587 if (!(iq = iks_new("iq")) || !(query = iks_insert(iq, "query"))) {
2588 ast_log(LOG_ERROR, "Stanzas could not be allocated for authentication on client '%s'\n", client->name);
2593 iks_insert_attrib(iq, "type", "set");
2594 iks_insert_cdata(iks_insert(query, "username"), client->jid->user, 0);
2595 iks_insert_cdata(iks_insert(query, "resource"), client->jid->resource, 0);
2597 iks_insert_attrib(query, "xmlns", "jabber:iq:auth");
2598 snprintf(sidpass, sizeof(sidpass), "%s%s", iks_find_attrib(node, "id"), cfg->password);
2599 ast_sha1_hash(buf, sidpass);
2600 iks_insert_cdata(iks_insert(query, "digest"), buf, 0);
2602 ast_xmpp_client_lock(client);
2603 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);
2604 iks_insert_attrib(iq, "id", client->mid);
2605 ast_xmpp_increment_mid(client->mid);
2606 ast_xmpp_client_unlock(client);
2608 iks_insert_attrib(iq, "to", client->jid->server);
2610 ast_xmpp_client_send(client, iq);
2614 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2619 /*! \brief Internal function called when we need to authenticate using SASL */
2620 static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2622 int features, len = strlen(client->jid->user) + strlen(cfg->password) + 3;
2625 char base64[(len + 2) * 4 / 3];
2627 if (strcmp(iks_name(node), "stream:features")) {
2628 /* Ignore anything beside stream features */
2632 features = iks_stream_features(node);
2634 if ((features & IKS_STREAM_SASL_MD5) && !xmpp_is_secure(client)) {
2635 if (iks_start_sasl(client->parser, IKS_SASL_DIGEST_MD5, (char*)client->jid->user, (char*)cfg->password) != IKS_OK) {
2636 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL DIGEST-MD5 but could not\n", client->name);
2640 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2644 /* Our only other available option is plain so if they don't support it, bail out now */
2645 if (!(features & IKS_STREAM_SASL_PLAIN)) {
2646 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL PLAIN but server does not support it\n", client->name);
2650 if (!(auth = iks_new("auth"))) {
2651 ast_log(LOG_ERROR, "Could not allocate memory for SASL PLAIN authentication for client '%s'\n", client->name);
2655 iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
2656 iks_insert_attrib(auth, "mechanism", "PLAIN");
2658 if (strchr(client->jid->user, '/')) {
2659 char *user = ast_strdupa(client->jid->user);
2661 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, strsep(&user, "/"), 0, cfg->password);
2663 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, client->jid->user, 0, cfg->password);
2666 ast_base64encode(base64, (const unsigned char *) combined, len - 1, (len + 2) * 4 / 3);
2667 iks_insert_cdata(auth, base64, 0);
2669 ast_xmpp_client_send(client, auth);
2673 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2678 /*! \brief Internal function called when we need to authenticate */
2679 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2681 return ast_test_flag(&cfg->flags, XMPP_USESASL) ? xmpp_client_authenticate_sasl(client, cfg, type, node) : xmpp_client_authenticate_digest(client, cfg, type, node);
2684 /*! \brief Internal function called when we are authenticating */
2685 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2689 if (!strcmp(iks_name(node), "success")) {
2690 /* Authentication was a success, yay! */
2691 xmpp_send_stream_header(client, cfg, client->jid->server);
2694 } else if (!strcmp(iks_name(node), "failure")) {
2695 /* Authentication was a bust, disconnect and reconnect later */
2697 } else if (strcmp(iks_name(node), "stream:features")) {
2698 /* Ignore any other responses */
2702 features = iks_stream_features(node);
2704 if (features & IKS_STREAM_BIND) {
2707 if (!(auth = iks_make_resource_bind(client->jid))) {
2708 ast_log(LOG_ERROR, "Failed to allocate memory for stream bind on client '%s'\n", client->name);
2712 ast_xmpp_client_lock(client);
2713 iks_insert_attrib(auth, "id", client->mid);
2714 ast_xmpp_increment_mid(client->mid);
2715 ast_xmpp_client_unlock(client);
2716 ast_xmpp_client_send(client, auth);
2720 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);
2723 if (features & IKS_STREAM_SESSION) {
2726 if (!(auth = iks_make_session())) {
2727 ast_log(LOG_ERROR, "Failed to allocate memory for stream session on client '%s'\n", client->name);
2731 iks_insert_attrib(auth, "id", "auth");
2732 ast_xmpp_client_lock(client);
2733 ast_xmpp_increment_mid(client->mid);
2734 ast_xmpp_client_unlock(client);
2735 ast_xmpp_client_send(client, auth);
2739 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);
2745 /*! \brief Internal function called when we should authenticate as a component */
2746 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2748 char secret[160], shasum[320], message[344];
2749 ikspak *pak = iks_packet(node);
2751 snprintf(secret, sizeof(secret), "%s%s", pak->id, cfg->password);
2752 ast_sha1_hash(shasum, secret);
2753 snprintf(message, sizeof(message), "<handshake>%s</handshake>", shasum);
2755 if (xmpp_client_send_raw_message(client, message) != IKS_OK) {
2756 ast_log(LOG_ERROR, "Unable to send handshake for component '%s'\n", client->name);
2760 xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
2765 /*! \brief Hook function called when component receives a service discovery get message */
2766 static int xmpp_component_service_discovery_get_hook(void *data, ikspak *pak)
2768 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2769 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2770 struct ast_xmpp_client *client = data;
2771 iks *iq = NULL, *query = NULL, *identity = NULL, *disco = NULL, *reg = NULL, *commands = NULL, *gateway = NULL;
2772 iks *version = NULL, *vcard = NULL, *search = NULL, *item = NULL;
2775 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2776 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(identity = iks_new("identity")) || !(disco = iks_new("feature")) ||
2777 !(reg = iks_new("feature")) || !(commands = iks_new("feature")) || !(gateway = iks_new("feature")) || !(version = iks_new("feature")) ||
2778 !(vcard = iks_new("feature")) || !(search = iks_new("search")) || !(item = iks_new("item"))) {
2779 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery get response to '%s' on component '%s'\n",
2780 pak->from->partial, client->name);
2784 iks_insert_attrib(iq, "from", clientcfg->user);
2785 iks_insert_attrib(iq, "to", pak->from->full);
2786 iks_insert_attrib(iq, "id", pak->id);
2787 iks_insert_attrib(iq, "type", "result");
2789 if (!(node = iks_find_attrib(pak->query, "node"))) {
2790 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2791 iks_insert_attrib(identity, "category", "gateway");
2792 iks_insert_attrib(identity, "type", "pstn");
2793 iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2794 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2795 iks_insert_attrib(reg, "var", "jabber:iq:register");
2796 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2797 iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2798 iks_insert_attrib(version, "var", "jabber:iq:version");
2799 iks_insert_attrib(vcard, "var", "vcard-temp");
2800 iks_insert_attrib(search, "var", "jabber:iq:search");
2802 iks_insert_node(iq, query);
2803 iks_insert_node(query, identity);
2804 iks_insert_node(query, disco);
2805 iks_insert_node(query, reg);
2806 iks_insert_node(query, commands);
2807 iks_insert_node(query, gateway);
2808 iks_insert_node(query, version);
2809 iks_insert_node(query, vcard);
2810 iks_insert_node(query, search);
2811 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2812 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2813 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2814 iks_insert_attrib(item, "node", "confirmaccount");
2815 iks_insert_attrib(item, "name", "Confirm account");
2816 iks_insert_attrib(item, "jid", clientcfg->user);
2818 iks_insert_node(iq, query);
2819 iks_insert_node(query, item);
2820 } else if (!strcasecmp(node, "confirmaccount")) {
2821 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2822 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2824 iks_insert_node(iq, query);
2825 iks_insert_node(query, commands);
2827 ast_debug(3, "Unsupported service discovery info request received with node '%s' on component '%s'\n",
2828 node, client->name);
2832 if (ast_xmpp_client_send(client, iq)) {
2833 ast_log(LOG_WARNING, "Could not send response to service discovery request on component '%s'\n",
2840 iks_delete(version);
2841 iks_delete(gateway);
2842 iks_delete(commands);
2845 iks_delete(identity);
2849 return IKS_FILTER_EAT;
2852 /*! \brief Hook function called when the component is queried about registration */
2853 static int xmpp_component_register_get_hook(void *data, ikspak *pak)
2855 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2856 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2857 struct ast_xmpp_client *client = data;
2858 iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL, *instructions = NULL;
2859 struct ast_xmpp_buddy *buddy;
2862 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2863 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(error = iks_new("error")) || !(notacceptable = iks_new("not-acceptable")) ||
2864 !(instructions = iks_new("instructions"))) {
2865 ast_log(LOG_ERROR, "Failed to allocate stanzas for register get response to '%s' on component '%s'\n",
2866 pak->from->partial, client->name);
2870 iks_insert_attrib(iq, "from", clientcfg->user);
2871 iks_insert_attrib(iq, "to", pak->from->full);
2872 iks_insert_attrib(iq, "id", pak->id);
2873 iks_insert_attrib(iq, "type", "result");
2874 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
2875 iks_insert_node(iq, query);
2877 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2878 iks_insert_attrib(error, "code", "406");
2879 iks_insert_attrib(error, "type", "modify");
2880 iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
2882 iks_insert_node(iq, error);
2883 iks_insert_node(error, notacceptable);
2885 ast_log(LOG_ERROR, "Received register attempt from '%s' but buddy is not configured on component '%s'\n",
2886 pak->from->partial, client->name);
2887 } else if (!(node = iks_find_attrib(pak->query, "node"))) {
2888 iks_insert_cdata(instructions, "Welcome to Asterisk - the Open Source PBX.\n", 0);
2889 iks_insert_node(query, instructions);
2892 ast_log(LOG_WARNING, "Received register get to component '%s' using unsupported node '%s' from '%s'\n",
2893 client->name, node, pak->from->partial);
2898 if (ast_xmpp_client_send(client, iq)) {
2899 ast_log(LOG_WARNING, "Could not send response to '%s' for received register get on component '%s'\n",
2900 pak->from->partial, client->name);
2904 iks_delete(instructions);
2905 iks_delete(notacceptable);
2910 return IKS_FILTER_EAT;
2913 /*! \brief Hook function called when someone registers to the component */
2914 static int xmpp_component_register_set_hook(void *data, ikspak *pak)
2916 struct ast_xmpp_client *client = data;
2917 iks *iq, *presence = NULL, *x = NULL;
2919 if (!(iq = iks_new("iq")) || !(presence = iks_new("presence")) || !(x = iks_new("x"))) {
2920 ast_log(LOG_ERROR, "Failed to allocate stanzas for register set response to '%s' on component '%s'\n",
2921 pak->from->partial, client->name);
2925 iks_insert_attrib(iq, "from", client->jid->full);
2926 iks_insert_attrib(iq, "to", pak->from->full);
2927 iks_insert_attrib(iq, "id", pak->id);
2928 iks_insert_attrib(iq, "type", "result");
2930 if (ast_xmpp_client_send(client, iq)) {
2931 ast_log(LOG_WARNING, "Could not send response to '%s' for received register set on component '%s'\n",
2932 pak->from->partial, client->name);
2936 iks_insert_attrib(presence, "from", client->jid->full);
2937 iks_insert_attrib(presence, "to", pak->from->partial);
2938 ast_xmpp_client_lock(client);
2939 iks_insert_attrib(presence, "id", client->mid);
2940 ast_xmpp_increment_mid(client->mid);
2941 ast_xmpp_client_unlock(client);
2942 iks_insert_attrib(presence, "type", "subscribe");
2943 iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
2945 iks_insert_node(presence, x);
2947 if (ast_xmpp_client_send(client, presence)) {
2948 ast_log(LOG_WARNING, "Could not send subscription to '%s' on component '%s'\n",
2949 pak->from->partial, client->name);
2954 iks_delete(presence);
2957 return IKS_FILTER_EAT;
2960 /*! \brief Hook function called when we receive a service discovery items request */
2961 static int xmpp_component_service_discovery_items_hook(void *data, ikspak *pak)
2963 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
2964 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2965 struct ast_xmpp_client *client = data;
2966 iks *iq = NULL, *query = NULL, *item = NULL, *feature = NULL;
2969 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2970 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item")) || !(feature = iks_new("feature"))) {
2971 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery items response to '%s' on component '%s'\n",
2972 pak->from->partial, client->name);
2976 iks_insert_attrib(iq, "from", clientcfg->user);
2977 iks_insert_attrib(iq, "to", pak->from->full);
2978 iks_insert_attrib(iq, "id", pak->id);
2979 iks_insert_attrib(iq, "type", "result");
2980 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2981 iks_insert_node(iq, query);
2983 if (!(node = iks_find_attrib(pak->query, "node"))) {
2984 iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
2985 iks_insert_attrib(item, "name", "Asterisk Commands");
2986 iks_insert_attrib(item, "jid", clientcfg->user);
2988 iks_insert_node(query, item);
2989 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2990 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2992 ast_log(LOG_WARNING, "Received service discovery items request to component '%s' using unsupported node '%s' from '%s'\n",
2993 client->name, node, pak->from->partial);
2997 if (ast_xmpp_client_send(client, iq)) {
2998 ast_log(LOG_WARNING, "Could not send response to service discovery items request from '%s' on component '%s'\n",
2999 pak->from->partial, client->name);
3003 iks_delete(feature);
3008 return IKS_FILTER_EAT;
3011 /*! \brief Internal function called when we authenticated as a component */
3012 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
3014 if (strcmp(iks_name(node), "handshake")) {
3015 ast_log(LOG_ERROR, "Failed to authenticate component '%s'\n", client->name);
3019 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);
3021 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);
3023 /* This uses the client service discovery result hook on purpose, as the code is common between both */
3024 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);
3026 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);
3027 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);
3029 xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
3034 /*! \brief Internal function called when a message is received */
3035 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
3037 struct ast_xmpp_message *message;
3041 ast_debug(3, "XMPP client '%s' received a message\n", client->name);
3043 if (!(body = iks_find_cdata(pak->x, "body"))) {
3044 /* Message contains no body, ignore it. */
3048 if (!(message = ast_calloc(1, sizeof(*message)))) {
3052 message->arrived = ast_tvnow();
3054 message->message = ast_strdup(body);
3056 ast_copy_string(message->id, S_OR(pak->id, ""), sizeof(message->id));
3057 message->from = !ast_strlen_zero(pak->from->full) ? ast_strdup(pak->from->full) : NULL;
3059 if (ast_test_flag(&cfg->flags, XMPP_SEND_TO_DIALPLAN)) {
3060 struct ast_msg *msg;
3062 if ((msg = ast_msg_alloc())) {
3065 ast_xmpp_client_lock(client);
3067 res = ast_msg_set_to(msg, "xmpp:%s", cfg->user);
3068 res |= ast_msg_set_from(msg, "xmpp:%s", message->from);
3069 res |= ast_msg_set_body(msg, "%s", message->message);
3070 res |= ast_msg_set_context(msg, "%s", cfg->context);
3072 ast_xmpp_client_unlock(client);
3075 ast_msg_destroy(msg);
3082 /* remove old messages received from this JID
3083 * and insert received message */
3084 deleted = delete_old_messages(client, pak->from->partial);
3085 ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
3086 AST_LIST_LOCK(&client->messages);
3087 AST_LIST_INSERT_HEAD(&client->messages, message, list);
3088 AST_LIST_UNLOCK(&client->messages);
3090 /* wake up threads waiting for messages */
3091 ast_mutex_lock(&messagelock);
3092 ast_cond_broadcast(&message_received_condition);
3093 ast_mutex_unlock(&messagelock);
3098 /*! \brief Helper function which sends a discovery information request to a user */
3099 static int xmpp_client_send_disco_info_request(struct ast_xmpp_client *client, const char *to, const char *from)
3104 if (!(iq = iks_new("iq")) || !(query = iks_new("query"))) {
3109 iks_insert_attrib(iq, "type", "get");
3110 iks_insert_attrib(iq, "to", to);
3111 iks_insert_attrib(iq, "from", from);
3112 ast_xmpp_client_lock(client);
3113 iks_insert_attrib(iq, "id", client->mid);
3114 ast_xmpp_increment_mid(client->mid);
3115 ast_xmpp_client_unlock(client);
3116 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
3117 iks_insert_node(iq, query);
3119 res = ast_xmpp_client_send(client, iq);
3127 /*! \brief Internal function called when a presence message is received */
3128 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
3130 struct ast_xmpp_buddy *buddy;
3131 struct ast_xmpp_resource *resource;
3132 char *type = iks_find_attrib(pak->x, "type");
3133 int status = pak->show ? pak->show : STATUS_DISAPPEAR;
3135 /* If no resource is available this is a general buddy presence update, which we will ignore */
3136 if (!pak->from->resource) {
3140 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
3141 /* Only output the message if it is not about us */
3142 if (strcmp(client->jid->partial, pak->from->partial)) {
3143 ast_log(LOG_WARNING, "Received presence information about '%s' despite not having them in roster on client '%s'\n",
3144 pak->from->partial, client->name);
3149 /* If this is a component presence probe request answer immediately with our presence status */
3150 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT) && !ast_strlen_zero(type) && !strcasecmp(type, "probe")) {
3151 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3154 ao2_lock(buddy->resources);
3156 if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY | OBJ_NOLOCK))) {
3157 /* Only create the new resource if it is not going away - in reality this should not happen */
3158 if (status != STATUS_DISAPPEAR) {
3159 if (!(resource = ao2_alloc(sizeof(*resource), xmpp_resource_destructor))) {
3160 ast_log(LOG_ERROR, "Could not allocate resource object for resource '%s' of buddy '%s' on client '%s'\n",
3161 pak->from->resource, buddy->id, client->name);
3162 ao2_unlock(buddy->resources);
3167 ast_copy_string(resource->resource, pak->from->resource, sizeof(resource->resource));
3170 /* We unlink the resource in case the priority changes or in case they are going away */
3171 ao2_unlink_flags(buddy->resources, resource, OBJ_NOLOCK);
3174 /* Only update the resource and add it back in if it is not going away */
3175 if (resource && (status != STATUS_DISAPPEAR)) {
3178 /* Try to get the XMPP spec node, and fall back to Google if not found */
3179 if (!(node = iks_find_attrib(iks_find(pak->x, "c"), "node"))) {
3180 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
3183 if (!(ver = iks_find_attrib(iks_find(pak->x, "c"), "ver"))) {
3184 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
3187 if (resource->description) {
3188 ast_free(resource->description);
3191 if ((node && strcmp(resource->caps.node, node)) || (ver && strcmp(resource->caps.version, ver))) {
3192 /* For interoperability reasons, proceed even if the resource fails to provide node or version */
3194 ast_copy_string(resource->caps.node, node, sizeof(resource->caps.node));
3197 ast_copy_string(resource->caps.version, ver, sizeof(resource->caps.version));
3200 /* Google Talk places the capabilities information directly in presence, so see if it is there */
3201 if (iks_find_with_attrib(pak->x, "c", "node", "http://www.google.com/xmpp/client/caps") ||
3202 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.google.com/xmpp/client/caps") ||
3203 iks_find_with_attrib(pak->x, "c", "node", "http://www.android.com/gtalk/client/caps") ||
3204 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.android.com/gtalk/client/caps") ||
3205 iks_find_with_attrib(pak->x, "c", "node", "http://mail.google.com/xmpp/client/caps") ||
3206 iks_find_with_attrib(pak->x, "caps:c", "node", "http://mail.google.com/xmpp/client/caps")) {
3207 resource->caps.google = 1;
3210 /* To discover if the buddy supports Jingle we need to query, so do so */
3211 if (xmpp_client_send_disco_info_request(client, pak->from->full, client->jid->full)) {
3212 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);
3216 resource->status = status;
3217 resource->description = ast_strdup(iks_find_cdata(pak->x, "status"));
3218 resource->priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
3220 ao2_link_flags(buddy->resources, resource, OBJ_NOLOCK);
3222 manager_event(EVENT_FLAG_USER, "JabberStatus",
3223 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
3224 "\r\nDescription: %s\r\n",
3225 client->name, pak->from->partial, resource->resource, resource->status,
3226 resource->priority, S_OR(resource->description, ""));
3228 ao2_ref(resource, -1);
3230 /* This will get hit by presence coming in for an unknown resource, and also when a resource goes away */
3232 ao2_ref(resource, -1);
3235 manager_event(EVENT_FLAG_USER, "JabberStatus",
3236 "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
3237 client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
3240 ao2_unlock(buddy->resources);
3247 /*! \brief Internal function called when a subscription message is received */
3248 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg,iks *node, ikspak *pak)
3250 struct ast_xmpp_buddy *buddy;
3252 switch (pak->subtype) {
3253 case IKS_TYPE_SUBSCRIBE:
3254 if (ast_test_flag(&cfg->flags, XMPP_AUTOREGISTER)) {
3255 iks *presence, *status = NULL;
3257 if ((presence = iks_new("presence")) && (status = iks_new("status"))) {
3258 iks_insert_attrib(presence, "type", "subscribed");
3259 iks_insert_attrib(presence, "to", pak->from->full);
3260 iks_insert_attrib(presence, "from", client->jid->full);
3263 iks_insert_attrib(presence, "id", pak->id);
3266 iks_insert_cdata(status, "Asterisk has approved your subscription", 0);
3267 iks_insert_node(presence, status);
3269 if (ast_xmpp_client_send(client, presence)) {
3270 ast_log(LOG_ERROR, "Could not send subscription acceptance to '%s' from client '%s'\n",
3271 pak->from->partial, client->name);
3274 ast_log(LOG_ERROR, "Could not allocate presence stanzas for accepting subscription from '%s' to client '%s'\n",
3275 pak->from->partial, client->name);
3279 iks_delete(presence);
3282 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
3283 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3285 /* This purposely flows through so we have the subscriber amongst our buddies */
3286 case IKS_TYPE_SUBSCRIBED:
3287 ao2_lock(client->buddies);
3289 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY | OBJ_NOLOCK))) {
3290 buddy = xmpp_client_create_buddy(client->buddies, pak->from->partial);
3294 ast_log(LOG_WARNING, "Could not find or create buddy '%s' on client '%s'\n",
3295 pak->from->partial, client->name);
3300 ao2_unlock(client->buddies);
3310 /*! \brief Action hook for when things occur */
3311 static int xmpp_action_hook(void *data, int type, iks *node)
3313 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
3314 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3315 struct ast_xmpp_client *client = data;
3320 ast_log(LOG_ERROR, "xmpp_action_hook was called without a packet\n");
3324 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
3328 /* If the client is disconnecting ignore everything */
3329 if (client->state == XMPP_STATE_DISCONNECTING) {
3333 pak = iks_packet(node);
3335 /* work around iksemel's impossibility to recognize node names
3336 * containing a colon. Set the namespace of the corresponding
3337 * node accordingly. */
3338 if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
3339 char *node_ns = NULL;
3340 char attr[XMPP_MAX_ATTRLEN];
3341 char *node_name = iks_name(iks_child(node));
3342 char *aux = strchr(node_name, ':') + 1;
3343 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
3344 node_ns = iks_find_attrib(iks_child(node), attr);
3347 pak->query = iks_child(node);
3351 /* Process through any state handlers */
3352 for (i = 0; i < ARRAY_LEN(xmpp_state_handlers); i++) {
3353 if ((xmpp_state_handlers[i].state == client->state) && (xmpp_state_handlers[i].component == (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? 1 : 0))) {
3354 if (xmpp_state_handlers[i].handler(client, clientcfg, type, node)) {
3355 /* If the handler wants us to stop now, do so */
3362 /* Process through any PAK handlers */
3363 for (i = 0; i < ARRAY_LEN(xmpp_pak_handlers); i++) {
3364 if (xmpp_pak_handlers[i].type == pak->type) {
3365 if (xmpp_pak_handlers[i].handler(client, clientcfg, node, pak)) {
3366 /* If the handler wants us to stop now, do so */
3373 /* Send the packet through the filter in case any filters want to process it */
3374 iks_filter_packet(client->filter, pak);
3381 int ast_xmpp_client_disconnect(struct ast_xmpp_client *client)
3383 if ((client->thread != AST_PTHREADT_NULL) && !pthread_equal(pthread_self(), client->thread)) {
3384 client->state = XMPP_STATE_DISCONNECTING;
3385 pthread_join(client->thread, NULL);
3386 client->thread = AST_PTHREADT_NULL;
3389 if (client->mwi_sub) {
3390 ast_event_unsubscribe(client->mwi_sub);
3391 client->mwi_sub = NULL;
3392 xmpp_pubsub_unsubscribe(client, "message_waiting");
3395 if (client->device_state_sub) {
3396 ast_event_unsubscribe(client->device_state_sub);
3397 client->device_state_sub = NULL;
3398 xmpp_pubsub_unsubscribe(client, "device_state");
3402 if (client->stream_flags & SECURE) {
3403 SSL_shutdown(client->ssl_session);
3404 SSL_CTX_free(client->ssl_context);
3405 SSL_free(client->ssl_session);
3408 client->stream_flags = 0;
3411 if (client->parser) {
3412 iks_disconnect(client->parser);
3415 client->state = XMPP_STATE_DISCONNECTED;
3420 /*! \brief Internal function used to reconnect an XMPP client to its server */
3421 static int xmpp_client_reconnect(struct ast_xmpp_client *client)
3423 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
3424 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3425 int res = IKS_NET_NOCONN;
3427 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
3431 ast_xmpp_client_disconnect(client);
3433 client->timeout = 50;
3434 iks_parser_reset(client->parser);
3436 if (!client->filter && !(client->filter = iks_filter_new())) {
3437 ast_log(LOG_ERROR, "Could not create IKS filter for client connection '%s'\n", client->name);
3441 /* If it's a component connect to user otherwise connect to server */
3442 res = iks_connect_via(client->parser, S_OR(clientcfg->server, client->jid->server), clientcfg->port,
3443 ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? clientcfg->user : client->jid->server);
3445 if (res == IKS_NET_NOCONN) {
3446 ast_log(LOG_ERROR, "No XMPP connection available when trying to connect client '%s'\n", client->name);
3448 } else if (res == IKS_NET_NODNS) {
3449 ast_log(LOG_ERROR, "No DNS available for XMPP connection when trying to connect client '%s'\n", client->name);
3453 /* Depending on the configuration of the client we eiher jump to requesting TLS, or authenticating */
3454 xmpp_client_change_state(client, (ast_test_flag(&clientcfg->flags, XMPP_USETLS) ? XMPP_STATE_REQUEST_TLS : XMPP_STATE_AUTHENTICATE));
3459 /*! \brief Internal function which polls on an XMPP client and receives data */
3460 static int xmpp_io_recv(struct ast_xmpp_client *client, char *buffer, size_t buf_len, int timeout)
3462 struct pollfd pfd = { .events = POLLIN };
3466 if (xmpp_is_secure(client)) {
3467 pfd.fd = SSL_get_fd(client->ssl_session);
3472 #endif /* HAVE_OPENSSL */
3473 pfd.fd = iks_fd(client->parser);
3475 res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
3478 if (xmpp_is_secure(client)) {
3479 len = SSL_read(client->ssl_session, buffer, buf_len);
3481 #endif /* HAVE_OPENSSL */
3482 len = recv(pfd.fd, buffer, buf_len, 0);
3486 } else if (len <= 0) {
3493 /*! \brief Internal function which receives data from the XMPP client connection */
3494 static int xmpp_client_receive(struct ast_xmpp_client *client, unsigned int timeout)
3496 int len, ret, pos = 0, newbufpos = 0;
3497 char buf[NET_IO_BUF_SIZE - 1] = "";
3498 char newbuf[NET_IO_BUF_SIZE - 1] = "";
3502 len = xmpp_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
3503 if (len < 0) return IKS_NET_RWERR;
3504 if (len == 0) return IKS_NET_EXPIRED;
3507 /* our iksemel parser won't work as expected if we feed
3508 it with XML packets that contain multiple whitespace
3509 characters between tags */
3512 /* if we stumble on the ending tag character,
3513 we skip any whitespace that follows it*/
3515 while (isspace(buf[pos+1])) {
3519 newbuf[newbufpos] = c;
3526 /* Log the message here, because iksemel's logHook is
3528 xmpp_log_hook(client, buf, len, 1);
3530 /* let iksemel deal with the string length,
3531 and reset our buffer */
3532 ret = iks_parse(client->parser, newbuf, 0, 0);
3533 memset(newbuf, 0, sizeof(newbuf));
3537 ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
3540 ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
3543 ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
3546 if (ret != IKS_OK) {
3549 ast_debug(3, "XML parsing successful\n");
3554 /*! \brief XMPP client connection thread */
3555 static void *xmpp_client_thread(void *data)
3557 struct ast_xmpp_client *client = data;
3558 int res = IKS_NET_RWERR;
3561 if (client->state == XMPP_STATE_DISCONNECTING) {
3565 if (res == IKS_NET_RWERR || client->timeout == 0) {
3566 ast_debug(3, "Connecting client '%s'\n", client->name);
3567 if ((res = xmpp_client_reconnect(client)) != IKS_OK) {
3569 res = IKS_NET_RWERR;
3574 res = xmpp_client_receive(client, 1);
3576 /* Decrease timeout if no data received, and delete
3577 * old messages globally */
3578 if (res == IKS_NET_EXPIRED) {
3582 if (res == IKS_HOOK) {
3583 ast_debug(2, "JABBER: Got hook event.\n");
3584 } else if (res == IKS_NET_TLSFAIL) {
3585 ast_log(LOG_ERROR, "JABBER: Failure in TLS.\n");
3586 } else if (!client->timeout && client->state == XMPP_STATE_CONNECTED) {
3587 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
3588 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3590 if (cfg && cfg->clients && (clientcfg = xmpp_config_find(cfg->clients, client->name))) {
3591 res = ast_test_flag(&clientcfg->flags, XMPP_KEEPALIVE) ? xmpp_client_send_raw_message(client, " ") : IKS_OK;
3596 if (res == IKS_OK) {
3597 client->timeout = 50;
3599 ast_log(LOG_WARNING, "JABBER: Network Timeout\n");
3601 } else if (res == IKS_NET_RWERR) {
3602 ast_log(LOG_WARNING, "JABBER: socket read error\n");
3610 static int xmpp_client_config_merge_buddies(void *obj, void *arg, int flags)
3612 struct ast_xmpp_buddy *buddy1 = obj, *buddy2;
3613 struct ao2_container *buddies = arg;
3615 /* If the buddy does not already exist link it into the client buddies container */
3616 if (!(buddy2 = ao2_find(buddies, buddy1->id, OBJ_KEY))) {
3617 ao2_link(buddies, buddy1);
3619 ao2_ref(buddy2, -1);
3622 /* All buddies are unlinked from the configuration buddies container, always */
3626 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)
3628 struct ast_xmpp_client_config *cfg = obj;
3630 /* Merge buddies as need be */
3631 ao2_callback(cfg->buddies, OBJ_MULTIPLE | OBJ_UNLINK, xmpp_client_config_merge_buddies, cfg->client->buddies);
3633 if (cfg->client->reconnect) {
3634 /* Disconnect the existing session since our role is changing, or we are starting up */
3635 ast_xmpp_client_disconnect(cfg->client);
3637 if (!(cfg->client->parser = iks_stream_new(ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client", cfg->client,
3638 xmpp_action_hook))) {
3639 ast_log(LOG_ERROR, "Iksemel stream could not be created for client '%s' - client not active\n", cfg->name);
3643 iks_set_log_hook(cfg->client->parser, xmpp_log_hook);
3645 /* Create a JID based on the given user, if no resource is given use the default */
3646 if (!strchr(cfg->user, '/') && !ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
3647 char resource[strlen(cfg->user) + strlen("/asterisk-xmpp") + 1];
3649 snprintf(resource, sizeof(resource), "%s/asterisk-xmpp", cfg->user);
3650 cfg->client->jid = iks_id_new(cfg->client->stack, resource);
3652 cfg->client->jid = iks_id_new(cfg->client->stack, cfg->user);
3655 if (!cfg->client->jid) {
3656 ast_log(LOG_ERROR, "Jabber identity could not be created for client '%s' - client not active\n", cfg->name);
3660 ast_pthread_create_background(&cfg->client->thread, NULL, xmpp_client_thread, cfg->client);
3662 cfg->client->reconnect = 0;
3663 } else if (cfg->client->state == XMPP_STATE_CONNECTED) {
3664 /* If this client is connected update their presence status since it may have changed */
3665 xmpp_client_set_presence(cfg->client, NULL, cfg->client->jid->full, cfg->status, cfg->statusmsg);
3667 /* Subscribe to the status of any newly added buddies */
3668 if (ast_test_flag(&cfg->flags, XMPP_AUTOREGISTER)) {
3669 ao2_callback(cfg->client->buddies, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_subscribe_user, cfg->client);
3678 * \brief Send a Jabber Message via call from the Manager
3679 * \param s mansession Manager session
3680 * \param m message Message to send
3683 static int manager_jabber_send(struct mansession *s, const struct message *m)
3685 RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
3686 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3687 const char *id = astman_get_header(m, "ActionID");
3688 const char *jabber = astman_get_header(m, "Jabber");
3689 const char *screenname = astman_get_header(m, "ScreenName");
3690 const char *message = astman_get_header(m, "Message");
3692 if (ast_strlen_zero(jabber)) {
3693 astman_send_error(s, m, "No transport specified");
3696 if (ast_strlen_zero(screenname)) {
3697 astman_send_error(s, m, "No ScreenName specified");
3700 if (ast_strlen_zero(message)) {
3701 astman_send_error(s, m, "No Message specified");
3705 astman_send_ack(s, m, "Attempting to send Jabber Message");
3707 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, jabber))) {
3708 astman_send_error(s, m, "Could not find Sender");
3712 if (strchr(screenname, '@') && !ast_xmpp_client_send_message(clientcfg->client, screenname, message)) {
3713 astman_append(s, "Response: Success\r\n");
3715 astman_append(s, "Response: Error\r\n");
3718 if (!ast_strlen_zero(id)) {
3719 astman_append(s, "ActionID: %s\r\n", id);
3722 astman_append(s, "\r\n");
3728 * \brief Build the a node request
3729 * \param client the configured XMPP client we use to connect to a XMPP server
3730 * \param collection name of the collection for request
3733 static iks* xmpp_pubsub_build_node_request(struct ast_xmpp_client *client, const char *collection)
3735 iks *request = xmpp_pubsub_iq_create(client, "get"), *query;
3741 query = iks_insert(request, "query");
3742 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
3745 iks_insert_attrib(query, "node", collection);
3752 * \brief Receive pubsub item lists
3753 * \param data pointer to ast_xmpp_client structure
3754 * \param pak response from pubsub diso#items query
3755 * \return IKS_FILTER_EAT
3757 static int xmpp_pubsub_receive_node_list(void *data, ikspak* pak)