Asterisk - The Open Source Telephony Project GIT-master-7921072
res_xmpp.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2012, Digium, Inc.
5 *
6 * Joshua Colp <jcolp@digium.com>
7 *
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.
13 *
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.
17 */
18
19/*! \file
20 *
21 * \brief XMPP client and component module.
22 *
23 * \author Joshua Colp <jcolp@digium.com>
24 *
25 * Iksemel http://code.google.com/p/iksemel/
26 *
27 * A reference module for interfacting Asterisk directly as a client or component with
28 * an XMPP/Jabber compliant server.
29 *
30 * This module is based upon the original res_jabber as done by Matt O'Gorman.
31 *
32 */
33
34/*! \li \ref res_xmpp.c uses the configuration file \ref xmpp.conf
35 * \addtogroup configuration_file Configuration Files
36 */
37
38/*!
39 * \page xmpp.conf xmpp.conf
40 * \verbinclude xmpp.conf.sample
41 */
42
43/*** MODULEINFO
44 <depend>iksemel</depend>
45 <use type="external">openssl</use>
46 <support_level>core</support_level>
47 ***/
48
49#include "asterisk.h"
50
51#include <ctype.h>
52#include <iksemel.h>
53
54#include "asterisk/xmpp.h"
55#include "asterisk/module.h"
56#include "asterisk/manager.h"
57#include "asterisk/app.h"
58#include "asterisk/mwi.h"
59#include "asterisk/message.h"
60#include "asterisk/manager.h"
61#include "asterisk/cli.h"
63#include "asterisk/json.h"
64
65/*** DOCUMENTATION
66 <application name="JabberSend" language="en_US" module="res_xmpp">
67 <synopsis>
68 Sends an XMPP message to a buddy.
69 </synopsis>
70 <syntax>
71 <parameter name="account" required="true">
72 <para>The local named account to listen on (specified in
73 xmpp.conf)</para>
74 </parameter>
75 <parameter name="jid" required="true">
76 <para>Jabber ID of the buddy to send the message to. It can be a
77 bare JID (username@domain) or a full JID (username@domain/resource).</para>
78 </parameter>
79 <parameter name="message" required="true">
80 <para>The message to send.</para>
81 </parameter>
82 </syntax>
83 <description>
84 <para>Sends the content of <replaceable>message</replaceable> as text message
85 from the given <replaceable>account</replaceable> to the buddy identified by
86 <replaceable>jid</replaceable></para>
87 <para>The example below sends "Hello world" to
88 <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
89 <replaceable>asterisk</replaceable>, configured in xmpp.conf.</para>
90 <example title="Send 'Hello world' to Bob">
91 same => n,JabberSend(asterisk,bob@domain.com,Hello world)
92 </example>
93 </description>
94 <see-also>
95 <ref type="function" module="res_xmpp">JABBER_STATUS</ref>
96 <ref type="function" module="res_xmpp">JABBER_RECEIVE</ref>
97 </see-also>
98 </application>
99 <function name="JABBER_RECEIVE" language="en_US" module="res_xmpp">
100 <synopsis>
101 Reads XMPP messages.
102 </synopsis>
103 <syntax>
104 <parameter name="account" required="true">
105 <para>The local named account to listen on (specified in
106 xmpp.conf)</para>
107 </parameter>
108 <parameter name="jid" required="true">
109 <para>Jabber ID of the buddy to receive message from. It can be a
110 bare JID (username@domain) or a full JID (username@domain/resource).</para>
111 </parameter>
112 <parameter name="timeout">
113 <para>In seconds, defaults to <literal>20</literal>.</para>
114 </parameter>
115 </syntax>
116 <description>
117 <para>Receives a text message on the given <replaceable>account</replaceable>
118 from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
119 <para>The example below returns an XMPP message sent from
120 <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
121 the <replaceable>asterisk</replaceable> XMPP account configured in xmpp.conf.</para>
122 <example title="Receive a message">
123 same => n,Set(msg=${JABBER_RECEIVE(asterisk,bob@domain.com)})
124 </example>
125 </description>
126 <see-also>
127 <ref type="function" module="res_xmpp">JABBER_STATUS</ref>
128 <ref type="application" module="res_xmpp">JabberSend</ref>
129 </see-also>
130 </function>
131 <function name="JABBER_STATUS" language="en_US" module="res_xmpp">
132 <synopsis>
133 Retrieves a buddy's status.
134 </synopsis>
135 <syntax>
136 <parameter name="account" required="true">
137 <para>The local named account to listen on (specified in
138 xmpp.conf)</para>
139 </parameter>
140 <parameter name="jid" required="true">
141 <para>Jabber ID of the buddy to receive message from. It can be a
142 bare JID (username@domain) or a full JID (username@domain/resource).</para>
143 </parameter>
144 </syntax>
145 <description>
146 <para>Retrieves the numeric status associated with the buddy identified
147 by <replaceable>jid</replaceable>. The return value will be one of the
148 following.</para>
149 <enumlist>
150 <enum name="1">
151 <para>Online</para>
152 </enum>
153 <enum name="2">
154 <para>Chatty</para>
155 </enum>
156 <enum name="3">
157 <para>Away</para>
158 </enum>
159 <enum name="4">
160 <para>Extended Away</para>
161 </enum>
162 <enum name="5">
163 <para>Do Not Disturb</para>
164 </enum>
165 <enum name="6">
166 <para>Offline</para>
167 </enum>
168 <enum name="7">
169 <para>Not In Roster</para>
170 </enum>
171 </enumlist>
172 </description>
173 <see-also>
174 <ref type="function" module="res_xmpp">JABBER_RECEIVE</ref>
175 <ref type="application" module="res_xmpp">JabberSend</ref>
176 </see-also>
177 </function>
178 <application name="JabberSendGroup" language="en_US" module="res_xmpp">
179 <synopsis>
180 Send a Jabber Message to a specified chat room
181 </synopsis>
182 <syntax>
183 <parameter name="Jabber" required="true">
184 <para>Client or transport Asterisk uses to connect to Jabber.</para>
185 </parameter>
186 <parameter name="RoomJID" required="true">
187 <para>XMPP/Jabber JID (Name) of chat room.</para>
188 </parameter>
189 <parameter name="Message" required="true">
190 <para>Message to be sent to the chat room.</para>
191 </parameter>
192 <parameter name="Nickname" required="false">
193 <para>The nickname Asterisk uses in the chat room.</para>
194 </parameter>
195 </syntax>
196 <description>
197 <para>Allows user to send a message to a chat room via XMPP.</para>
198 <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>
199 </description>
200 </application>
201 <application name="JabberJoin" language="en_US" module="res_xmpp">
202 <synopsis>
203 Join a chat room
204 </synopsis>
205 <syntax>
206 <parameter name="Jabber" required="true">
207 <para>Client or transport Asterisk uses to connect to Jabber.</para>
208 </parameter>
209 <parameter name="RoomJID" required="true">
210 <para>XMPP/Jabber JID (Name) of chat room.</para>
211 </parameter>
212 <parameter name="Nickname" required="false">
213 <para>The nickname Asterisk will use in the chat room.</para>
214 <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>
215 </parameter>
216 </syntax>
217 <description>
218 <para>Allows Asterisk to join a chat room.</para>
219 </description>
220 </application>
221 <application name="JabberLeave" language="en_US" module="res_xmpp">
222 <synopsis>
223 Leave a chat room
224 </synopsis>
225 <syntax>
226 <parameter name="Jabber" required="true">
227 <para>Client or transport Asterisk uses to connect to Jabber.</para>
228 </parameter>
229 <parameter name="RoomJID" required="true">
230 <para>XMPP/Jabber JID (Name) of chat room.</para>
231 </parameter>
232 <parameter name="Nickname" required="false">
233 <para>The nickname Asterisk uses in the chat room.</para>
234 </parameter>
235 </syntax>
236 <description>
237 <para>Allows Asterisk to leave a chat room.</para>
238 </description>
239 </application>
240 <manager name="JabberSend" language="en_US" module="res_xmpp">
241 <synopsis>
242 Sends a message to a Jabber Client.
243 </synopsis>
244 <syntax>
245 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
246 <parameter name="Jabber" required="true">
247 <para>Client or transport Asterisk uses to connect to JABBER.</para>
248 </parameter>
249 <parameter name="JID" required="true">
250 <para>XMPP/Jabber JID (Name) of recipient.</para>
251 </parameter>
252 <parameter name="Message" required="true">
253 <para>Message to be sent to the buddy.</para>
254 </parameter>
255 </syntax>
256 <description>
257 <para>Sends a message to a Jabber Client.</para>
258 </description>
259 </manager>
260 <info name="MessageDestinationInfo" language="en_US" tech="XMPP">
261 <para>Specifying a prefix of <literal>xmpp:</literal> will send the
262 message as an XMPP chat message.</para>
263 </info>
264 <info name="MessageFromInfo" language="en_US" tech="XMPP">
265 <para>Specifying a prefix of <literal>xmpp:</literal> will specify the
266 account defined in <literal>xmpp.conf</literal> to send the message from.
267 Note that this field is required for XMPP messages.</para>
268 </info>
269 <info name="MessageToInfo" language="en_US" tech="XMPP">
270 <para>Ignored</para>
271 </info>
272 <configInfo name="res_xmpp" language="en_US">
273 <synopsis>XMPP Messaging</synopsis>
274 <configFile name="xmpp.conf">
275 <configObject name="global">
276 <synopsis>Global configuration settings</synopsis>
277 <configOption name="debug">
278 <synopsis>Enable/disable XMPP message debugging</synopsis>
279 </configOption>
280 <configOption name="autoprune">
281 <synopsis>Auto-remove users from buddy list.</synopsis>
282 <description><para>Auto-remove users from buddy list. Depending on the setup
283 (e.g., using your personal Gtalk account for a test) this could cause loss of
284 the contact list.
285 </para></description>
286 </configOption>
287 <configOption name="autoregister">
288 <synopsis>Auto-register users from buddy list</synopsis>
289 </configOption>
290 <configOption name="collection_nodes">
291 <synopsis>Enable support for XEP-0248 for use with distributed device state</synopsis>
292 </configOption>
293 <configOption name="pubsub_autocreate">
294 <synopsis>Whether or not the PubSub server supports/is using auto-create for nodes</synopsis>
295 </configOption>
296 <configOption name="auth_policy">
297 <synopsis>Whether to automatically accept or deny users' subscription requests</synopsis>
298 </configOption>
299 </configObject>
300 <configObject name="client">
301 <synopsis>Configuration options for an XMPP client</synopsis>
302 <configOption name="username">
303 <synopsis>XMPP username with optional resource</synopsis>
304 </configOption>
305 <configOption name="secret">
306 <synopsis>XMPP password</synopsis>
307 </configOption>
308 <configOption name="refresh_token">
309 <synopsis>Google OAuth 2.0 refresh token</synopsis>
310 </configOption>
311 <configOption name="oauth_clientid">
312 <synopsis>Google OAuth 2.0 application's client id</synopsis>
313 </configOption>
314 <configOption name="oauth_secret">
315 <synopsis>Google OAuth 2.0 application's secret</synopsis>
316 </configOption>
317 <configOption name="serverhost">
318 <synopsis>Route to server, e.g. talk.google.com</synopsis>
319 </configOption>
320 <configOption name="statusmessage">
321 <synopsis>Custom status message</synopsis>
322 </configOption>
323 <configOption name="pubsub_node">
324 <synopsis>Node for publishing events via PubSub</synopsis>
325 </configOption>
326 <configOption name="context">
327 <synopsis>Dialplan context to send incoming messages to</synopsis>
328 </configOption>
329 <configOption name="priority">
330 <synopsis>XMPP resource priority</synopsis>
331 </configOption>
332 <configOption name="port">
333 <synopsis>XMPP server port</synopsis>
334 </configOption>
335 <configOption name="timeout">
336 <synopsis>Timeout in seconds to hold incoming messages</synopsis>
337 <description><para>Timeout (in seconds) on the message stack. Messages stored longer
338 than this value will be deleted by Asterisk. This option applies to incoming messages only
339 which are intended to be processed by the <literal>JABBER_RECEIVE</literal> dialplan function.
340 </para></description>
341 </configOption>
342 <configOption name="debug">
343 <synopsis>Enable debugging</synopsis>
344 </configOption>
345 <configOption name="type">
346 <synopsis>Connection is either a client or a component</synopsis>
347 </configOption>
348 <configOption name="distribute_events">
349 <synopsis>Whether or not to distribute events using this connection</synopsis>
350 </configOption>
351 <configOption name="usetls">
352 <synopsis>Whether to use TLS for the connection or not</synopsis>
353 </configOption>
354 <configOption name="usesasl">
355 <synopsis>Whether to use SASL for the connection or not</synopsis>
356 </configOption>
357 <configOption name="forceoldssl">
358 <synopsis>Force the use of old-style SSL for the connection</synopsis>
359 </configOption>
360 <configOption name="keepalive">
361 <synopsis>If enabled, periodically send an XMPP message from this client with an empty message</synopsis>
362 </configOption>
363 <configOption name="autoprune">
364 <synopsis>Auto-remove users from buddy list.</synopsis>
365 <description><para>Auto-remove users from buddy list. Depending on the setup
366 (e.g., using your personal Gtalk account for a test) this could cause loss of
367 the contact list.
368 </para></description>
369 </configOption>
370 <configOption name="autoregister">
371 <synopsis>Auto-register users bfrom buddy list</synopsis>
372 </configOption>
373 <configOption name="auth_policy">
374 <synopsis>Whether to automatically accept or deny users' subscription requests</synopsis>
375 </configOption>
376 <configOption name="sendtodialplan">
377 <synopsis>Send incoming messages into the dialplan</synopsis>
378 </configOption>
379 <configOption name="status">
380 <synopsis>Default XMPP status for the client</synopsis>
381 <description><para>Can be one of the following XMPP statuses:</para>
382 <enumlist>
383 <enum name="chat"/>
384 <enum name="available"/>
385 <enum name="away"/>
386 <enum name="xaway"/>
387 <enum name="dnd"/>
388 </enumlist>
389 </description>
390 </configOption>
391 <configOption name="buddy">
392 <synopsis>Manual addition of buddy to list</synopsis>
393 <description><para>
394 Manual addition of buddy to the buddy list. For distributed events, these buddies are
395 automatically added in the whitelist as 'owners' of the node(s).
396 </para></description>
397 </configOption>
398 </configObject>
399 </configFile>
400 </configInfo>
401***/
402
403/*! \brief Supported general configuration flags */
404enum {
405 XMPP_AUTOPRUNE = (1 << 0),
407 XMPP_AUTOACCEPT = (1 << 2),
408 XMPP_DEBUG = (1 << 3),
409 XMPP_USETLS = (1 << 4),
410 XMPP_USESASL = (1 << 5),
411 XMPP_FORCESSL = (1 << 6),
412 XMPP_KEEPALIVE = (1 << 7),
413 XMPP_COMPONENT = (1 << 8),
416};
417
418/*! \brief Supported pubsub configuration flags */
419enum {
420 XMPP_XEP0248 = (1 << 0),
421 XMPP_PUBSUB = (1 << 1),
423};
424
425/*! \brief Number of buckets for client connections */
426#define CLIENT_BUCKETS 53
427
428/*! \brief Number of buckets for buddies (per client) */
429#define BUDDY_BUCKETS 53
430
431/*! \brief Number of buckets for resources (per buddy) */
432#define RESOURCE_BUCKETS 53
433
434/*! \brief Namespace for TLS support */
435#define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
436
437/*! \brief Status for a disappearing buddy */
438#define STATUS_DISAPPEAR 6
439
440/*! \brief Global debug status */
441static int debug;
442
443/*! \brief XMPP Global Configuration */
445 struct ast_flags general; /*!< General configuration options */
446 struct ast_flags pubsub; /*!< Pubsub related configuration options */
447};
448
449/*! \brief XMPP Client Configuration */
452 AST_STRING_FIELD(name); /*!< Name of the client connection */
453 AST_STRING_FIELD(user); /*!< Username to use for authentication */
454 AST_STRING_FIELD(password); /*!< Password to use for authentication */
455 AST_STRING_FIELD(refresh_token); /*!< Refresh token to use for OAuth authentication */
456 AST_STRING_FIELD(oauth_clientid); /*!< Client ID to use for OAuth authentication */
457 AST_STRING_FIELD(oauth_secret); /*!< Secret to use for OAuth authentication */
458 AST_STRING_FIELD(server); /*!< Server hostname */
459 AST_STRING_FIELD(statusmsg); /*!< Status message for presence */
460 AST_STRING_FIELD(pubsubnode); /*!< Pubsub node */
461 AST_STRING_FIELD(context); /*!< Context for incoming messages */
462 );
463 int port; /*!< Port to use when connecting to server */
464 int message_timeout; /*!< Timeout for messages */
465 int priority; /*!< Resource priority */
466 struct ast_flags flags; /*!< Various options that have been set */
467 struct ast_flags mod_flags; /*!< Global options that have been modified */
468 enum ikshowtype status; /*!< Presence status */
469 struct ast_xmpp_client *client; /*!< Pointer to the client */
470 struct ao2_container *buddies; /*!< Configured buddies */
471};
472
474 struct ast_xmpp_global_config *global; /*!< Global configuration options */
475 struct ao2_container *clients; /*!< Configured clients */
476};
477
479
480static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
481static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
482static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
483static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
484
485static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
486static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
487
488/*! \brief Defined handlers for XMPP client states */
489static const struct xmpp_state_handler {
490 int state;
492 int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
501
502static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
503static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
504static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
505
506/*! \brief Defined handlers for different PAK types */
507static const struct xmpp_pak_handler {
508 int type;
509 int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
510} xmpp_pak_handlers[] = {
511 { IKS_PAK_MESSAGE, xmpp_pak_message, },
512 { IKS_PAK_PRESENCE, xmpp_pak_presence, },
513 { IKS_PAK_S10N, xmpp_pak_s10n, },
515
516static const char *app_ajisend = "JabberSend";
517static const char *app_ajisendgroup = "JabberSendGroup";
518static const char *app_ajistatus = "JabberStatus";
519static const char *app_ajijoin = "JabberJoin";
520static const char *app_ajileave = "JabberLeave";
521
524
525static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
526static int fetch_access_token(struct ast_xmpp_client_config *cfg);
527
528/*! \brief Destructor function for configuration */
530{
531 struct ast_xmpp_client_config *cfg = obj;
533 ao2_cleanup(cfg->client);
534 ao2_cleanup(cfg->buddies);
535}
536
537/*! \brief Destroy function for XMPP messages */
539{
540 if (message->from) {
541 ast_free(message->from);
542 }
543 if (message->message) {
544 ast_free(message->message);
545 }
546
548}
549
550/*! \brief Destructor callback function for XMPP client */
551static void xmpp_client_destructor(void *obj)
552{
553 struct ast_xmpp_client *client = obj;
555
557
559 client->endpoint = NULL;
560
561 if (client->filter) {
562 iks_filter_delete(client->filter);
563 }
564
565 if (client->stack) {
566 iks_stack_delete(client->stack);
567 }
568
569 ao2_cleanup(client->buddies);
570
571 while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
573 }
575}
576
577/*! \brief Hashing function for XMPP buddy */
578static int xmpp_buddy_hash(const void *obj, const int flags)
579{
580 const struct ast_xmpp_buddy *buddy = obj;
581 const char *id = obj;
582
583 return ast_str_hash(flags & OBJ_KEY ? id : buddy->id);
584}
585
586/*! \brief Comparator function for XMPP buddy */
587static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
588{
589 struct ast_xmpp_buddy *buddy1 = obj, *buddy2 = arg;
590 const char *id = arg;
591
592 return !strcmp(buddy1->id, flags & OBJ_KEY ? id : buddy2->id) ? CMP_MATCH | CMP_STOP : 0;
593}
594
595/*! \brief Internal function which changes the XMPP client state */
596static void xmpp_client_change_state(struct ast_xmpp_client *client, int state)
597{
598 if (state == client->state) {
599 return;
600 }
601 client->state = state;
602 if (client->state == XMPP_STATE_DISCONNECTED) {
604 } else if (client->state == XMPP_STATE_CONNECTED) {
606 }
607}
608
609/*! \brief Allocator function for ast_xmpp_client */
610static struct ast_xmpp_client *xmpp_client_alloc(const char *name)
611{
612 struct ast_xmpp_client *client;
613
614 if (!(client = ao2_alloc(sizeof(*client), xmpp_client_destructor))) {
615 return NULL;
616 }
617
619 client->thread = AST_PTHREADT_NULL;
620
621 client->endpoint = ast_endpoint_create("XMPP", name);
622 if (!client->endpoint) {
623 ao2_ref(client, -1);
624 return NULL;
625 }
626
629 if (!client->buddies) {
630 ast_log(LOG_ERROR, "Could not initialize buddy container for '%s'\n", name);
631 ao2_ref(client, -1);
632 return NULL;
633 }
634
635 if (ast_string_field_init(client, 512)) {
636 ast_log(LOG_ERROR, "Could not initialize stringfields for '%s'\n", name);
637 ao2_ref(client, -1);
638 return NULL;
639 }
640
641 if (!(client->stack = iks_stack_new(8192, 8192))) {
642 ast_log(LOG_ERROR, "Could not create an Iksemel stack for '%s'\n", name);
643 ao2_ref(client, -1);
644 return NULL;
645 }
646
648
649 client->timeout = 50;
651 ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
652
653 return client;
654}
655
656/*! \brief Find function for configuration */
657static void *xmpp_config_find(struct ao2_container *tmp_container, const char *category)
658{
659 return ao2_find(tmp_container, category, OBJ_KEY);
660}
661
662/*! \brief Look up existing client or create a new one */
663static void *xmpp_client_find_or_create(const char *category)
664{
666 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
667
668 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, category))) {
669 return xmpp_client_alloc(category);
670 }
671
672 ao2_ref(clientcfg->client, +1);
673 return clientcfg->client;
674}
675
676/*! \brief Allocator function for configuration */
677static void *ast_xmpp_client_config_alloc(const char *cat)
678{
679 struct ast_xmpp_client_config *cfg;
680
681 if (!(cfg = ao2_alloc(sizeof(*cfg), ast_xmpp_client_config_destructor))) {
682 return NULL;
683 }
684
685 if (ast_string_field_init(cfg, 512)) {
686 ao2_ref(cfg, -1);
687 return NULL;
688 }
689
690 if (!(cfg->client = xmpp_client_find_or_create(cat))) {
691 ao2_ref(cfg, -1);
692 return NULL;
693 }
694
697 if (!cfg->buddies) {
698 ao2_ref(cfg, -1);
699 return NULL;
700 }
701
702 ast_string_field_set(cfg, name, cat);
703
704 return cfg;
705}
706
707/*! \brief Destructor for XMPP configuration */
708static void xmpp_config_destructor(void *obj)
709{
710 struct xmpp_config *cfg = obj;
711 ao2_cleanup(cfg->global);
712 ao2_cleanup(cfg->clients);
713}
714
715/*! \brief Comparator function for configuration */
716static int xmpp_config_cmp(void *obj, void *arg, int flags)
717{
718 struct ast_xmpp_client_config *one = obj, *two = arg;
719 const char *match = (flags & OBJ_KEY) ? arg : two->name;
720 return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
721}
722
723/*! \brief Allocator for XMPP configuration */
724static void *xmpp_config_alloc(void)
725{
726 struct xmpp_config *cfg;
727
728 if (!(cfg = ao2_alloc(sizeof(*cfg), xmpp_config_destructor))) {
729 return NULL;
730 }
731
732 if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
733 goto error;
734 }
735
738 if (!cfg->clients) {
739 goto error;
740 }
741
742 return cfg;
743error:
744 ao2_ref(cfg, -1);
745 return NULL;
746}
747
748static int xmpp_config_prelink(void *newitem)
749{
750 struct ast_xmpp_client_config *clientcfg = newitem;
752 RAII_VAR(struct ast_xmpp_client_config *, oldclientcfg, NULL, ao2_cleanup);
753
754 if (ast_strlen_zero(clientcfg->user)) {
755 ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
756 return -1;
757 } else if (ast_strlen_zero(clientcfg->password) && ast_strlen_zero(clientcfg->refresh_token)) {
758 ast_log(LOG_ERROR, "No password or refresh_token specified on client '%s'\n", clientcfg->name);
759 return -1;
760 } else if (ast_strlen_zero(clientcfg->server)) {
761 ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
762 return -1;
763 } else if (!ast_strlen_zero(clientcfg->refresh_token) &&
764 (ast_strlen_zero(clientcfg->oauth_clientid) || ast_strlen_zero(clientcfg->oauth_secret))) {
765 ast_log(LOG_ERROR, "No oauth_clientid or oauth_secret specified, so client '%s' can't be used\n", clientcfg->name);
766 return -1;
767 }
768
769 /* If this is a new connection force a reconnect */
770 if (!cfg || !cfg->clients || !(oldclientcfg = xmpp_config_find(cfg->clients, clientcfg->name))) {
771 clientcfg->client->reconnect = 1;
772 return 0;
773 }
774
775 /* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
776 if (strcmp(clientcfg->user, oldclientcfg->user) ||
777 strcmp(clientcfg->password, oldclientcfg->password) ||
778 strcmp(clientcfg->refresh_token, oldclientcfg->refresh_token) ||
779 strcmp(clientcfg->oauth_clientid, oldclientcfg->oauth_clientid) ||
780 strcmp(clientcfg->oauth_secret, oldclientcfg->oauth_secret) ||
781 strcmp(clientcfg->server, oldclientcfg->server) ||
782 (clientcfg->port != oldclientcfg->port) ||
783 (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
784 (clientcfg->priority != oldclientcfg->priority)) {
785 clientcfg->client->reconnect = 1;
786 } else {
787 clientcfg->client->reconnect = 0;
788 }
789
790 return 0;
791}
792
793static void xmpp_config_post_apply(void)
794{
796
798}
799
800static struct aco_type global_option = {
801 .type = ACO_GLOBAL,
802 .name = "global",
803 .item_offset = offsetof(struct xmpp_config, global),
804 .category_match = ACO_WHITELIST_EXACT,
805 .category = "general",
806};
807
809
810static struct aco_type client_option = {
811 .type = ACO_ITEM,
812 .name = "client",
813 .category_match = ACO_BLACKLIST_EXACT,
814 .category = "general",
815 .item_alloc = ast_xmpp_client_config_alloc,
816 .item_find = xmpp_config_find,
817 .item_prelink = xmpp_config_prelink,
818 .item_offset = offsetof(struct xmpp_config, clients),
819};
820
822
824 .filename = "xmpp.conf",
825 .alias = "jabber.conf",
827};
828
830 .files = ACO_FILES(&res_xmpp_conf),
831 .post_apply_config = xmpp_config_post_apply,
832 );
833
834/*! \brief Destructor callback function for XMPP resource */
835static void xmpp_resource_destructor(void *obj)
836{
837 struct ast_xmpp_resource *resource = obj;
838
839 if (resource->description) {
840 ast_free(resource->description);
841 }
842}
843
844/*! \brief Hashing function for XMPP resource */
845static int xmpp_resource_hash(const void *obj, const int flags)
846{
847 const struct ast_xmpp_resource *resource = obj;
848
849 return flags & OBJ_KEY ? -1 : resource->priority;
850}
851
852/*! \brief Comparator function for XMPP resource */
853static int xmpp_resource_cmp(void *obj, void *arg, int flags)
854{
855 struct ast_xmpp_resource *resource1 = obj;
856 const char *resource = arg;
857
858 return !strcmp(resource1->resource, resource) ? CMP_MATCH | CMP_STOP : 0;
859}
860
861/*! \brief Destructor callback function for XMPP buddy */
862static void xmpp_buddy_destructor(void *obj)
863{
864 struct ast_xmpp_buddy *buddy = obj;
865
866 if (buddy->resources) {
867 ao2_ref(buddy->resources, -1);
868 }
869}
870
871/*! \brief Helper function which returns whether an XMPP client connection is secure or not */
872static int xmpp_is_secure(struct ast_xmpp_client *client)
873{
874#ifdef HAVE_OPENSSL
875 return client->stream_flags & SECURE;
876#else
877 return 0;
878#endif
879}
880
882{
884 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
885
886 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
887 return NULL;
888 }
889
890 ao2_ref(clientcfg->client, +1);
891 return clientcfg->client;
892}
893
895{
896 ao2_ref(client, -1);
897}
898
900{
901 ao2_lock(client);
902}
903
905{
906 ao2_unlock(client);
907}
908
909/*! \brief Internal function used to send a message to a user or chatroom */
910static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
911{
913 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
914 int res = 0;
915 char from[XMPP_MAX_JIDLEN];
916 iks *message_packet;
917
918 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
919 !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
920 return -1;
921 }
922
923 if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
924 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
925 } else {
926 snprintf(from, sizeof(from), "%s", client->jid->full);
927 }
928
929 iks_insert_attrib(message_packet, "from", from);
930
931 res = ast_xmpp_client_send(client, message_packet);
932
933 iks_delete(message_packet);
934
935 return res;
936}
937
938int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
939{
940 return xmpp_client_send_message(client, 0, NULL, user, message);
941}
942
943int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
944{
945 int res = 0;
946 iks *invite, *body = NULL, *namespace = NULL;
947
948 if (!(invite = iks_new("message")) || !(body = iks_new("body")) || !(namespace = iks_new("x"))) {
949 res = -1;
950 goto done;
951 }
952
953 iks_insert_attrib(invite, "to", user);
954 ast_xmpp_client_lock(client);
955 iks_insert_attrib(invite, "id", client->mid);
958 iks_insert_cdata(body, message, 0);
959 iks_insert_node(invite, body);
960 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
961 iks_insert_attrib(namespace, "jid", room);
962 iks_insert_node(invite, namespace);
963
964 res = ast_xmpp_client_send(client, invite);
965
966done:
967 iks_delete(namespace);
968 iks_delete(body);
969 iks_delete(invite);
970
971 return res;
972}
973
974static int xmpp_client_set_group_presence(struct ast_xmpp_client *client, const char *room, int level, const char *nick)
975{
977 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
978 int res = 0;
979 iks *presence = NULL, *x = NULL;
980 char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
981
982 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
983 !(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
984 res = -1;
985 goto done;
986 }
987
988 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
989 snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
990 snprintf(roomid, sizeof(roomid), "%s/%s", room, nick);
991 } else {
992 snprintf(from, sizeof(from), "%s", client->jid->full);
993 snprintf(roomid, sizeof(roomid), "%s/%s", room, S_OR(nick, client->jid->user));
994 }
995
996 iks_insert_attrib(presence, "to", roomid);
997 iks_insert_attrib(presence, "from", from);
998 iks_insert_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
999 iks_insert_node(presence, x);
1000
1001 res = ast_xmpp_client_send(client, presence);
1002
1003done:
1004 iks_delete(x);
1005 iks_delete(presence);
1006
1007 return res;
1008}
1009
1010int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
1011{
1012 return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
1013}
1014
1015int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
1016{
1017 return xmpp_client_send_message(client, 1, nickname, address, message);
1018}
1019
1020int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
1021{
1022 return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
1023}
1024
1026{
1027 int i = 0;
1028
1029 for (i = strlen(mid) - 1; i >= 0; i--) {
1030 if (mid[i] != 'z') {
1031 mid[i] = mid[i] + 1;
1032 i = 0;
1033 } else {
1034 mid[i] = 'a';
1035 }
1036 }
1037}
1038
1039/*!
1040 * \brief Create an IQ packet
1041 * \param client the configured XMPP client we use to connect to a XMPP server
1042 * \param type the type of IQ packet to create
1043 * \return iks *
1044 */
1045static iks* xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
1046{
1048 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1049 iks *request;
1050
1051 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
1052 !(request = iks_new("iq"))) {
1053 return NULL;
1054 }
1055
1056 if (!ast_strlen_zero(clientcfg->pubsubnode)) {
1057 iks_insert_attrib(request, "to", clientcfg->pubsubnode);
1058 }
1059
1060 iks_insert_attrib(request, "from", client->jid->full);
1061 iks_insert_attrib(request, "type", type);
1062 ast_xmpp_client_lock(client);
1063 ast_xmpp_increment_mid(client->mid);
1064 iks_insert_attrib(request, "id", client->mid);
1065 ast_xmpp_client_unlock(client);
1066
1067 return request;
1068}
1069
1070/*!
1071 * \brief Build the skeleton of a publish
1072 * \param client the configured XMPP client we use to connect to a XMPP server
1073 * \param node Name of the node that will be published to
1074 * \param event_type, cachable
1075 * \return iks *
1076 */
1077static iks* xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node,
1078 const char *event_type, unsigned int cachable)
1079{
1081 iks *request, *pubsub, *publish, *item;
1082
1083 if (!cfg || !cfg->global || !(request = xmpp_pubsub_iq_create(client, "set"))) {
1084 return NULL;
1085 }
1086
1087 pubsub = iks_insert(request, "pubsub");
1088 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1089 publish = iks_insert(pubsub, "publish");
1090 iks_insert_attrib(publish, "node", ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248) ? node : event_type);
1091 item = iks_insert(publish, "item");
1092 iks_insert_attrib(item, "id", node);
1093
1094 if (cachable == AST_DEVSTATE_NOT_CACHABLE) {
1095 iks *options, *x, *field_form_type, *field_persist;
1096
1097 options = iks_insert(pubsub, "publish-options");
1098 x = iks_insert(options, "x");
1099 iks_insert_attrib(x, "xmlns", "jabber:x:data");
1100 iks_insert_attrib(x, "type", "submit");
1101 field_form_type = iks_insert(x, "field");
1102 iks_insert_attrib(field_form_type, "var", "FORM_TYPE");
1103 iks_insert_attrib(field_form_type, "type", "hidden");
1104 iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options", 0);
1105 field_persist = iks_insert(x, "field");
1106 iks_insert_attrib(field_persist, "var", "pubsub#persist_items");
1107 iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1);
1108 }
1109
1110 return item;
1111
1112}
1113
1114static iks* xmpp_pubsub_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
1115{
1116 iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
1117 *field_deliver_payload, *field_persist_items, *field_access_model,
1118 *field_pubsub_collection;
1119 configure = iks_insert(pubsub, "configure");
1120 x = iks_insert(configure, "x");
1121 iks_insert_attrib(x, "xmlns", "jabber:x:data");
1122 iks_insert_attrib(x, "type", "submit");
1123 field_owner = iks_insert(x, "field");
1124 iks_insert_attrib(field_owner, "var", "FORM_TYPE");
1125 iks_insert_attrib(field_owner, "type", "hidden");
1126 iks_insert_cdata(iks_insert(field_owner, "value"),
1127 "http://jabber.org/protocol/pubsub#owner", 39);
1128 if (node_type) {
1129 field_node_type = iks_insert(x, "field");
1130 iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
1131 iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
1132 }
1133 field_node_config = iks_insert(x, "field");
1134 iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
1135 iks_insert_attrib(field_node_config, "type", "hidden");
1136 iks_insert_cdata(iks_insert(field_node_config, "value"),
1137 "http://jabber.org/protocol/pubsub#node_config", 45);
1138 field_deliver_payload = iks_insert(x, "field");
1139 iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
1140 iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
1141 field_persist_items = iks_insert(x, "field");
1142 iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
1143 iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
1144 field_access_model = iks_insert(x, "field");
1145 iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
1146 iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
1147 if (node_type && !strcasecmp(node_type, "leaf")) {
1148 field_pubsub_collection = iks_insert(x, "field");
1149 iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
1150 iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
1151 strlen(collection_name));
1152 }
1153 return configure;
1154}
1155
1156/*!
1157 * \brief Add Owner affiliations for pubsub node
1158 * \param client the configured XMPP client we use to connect to a XMPP server
1159 * \param node the name of the node to which to add affiliations
1160 */
1161static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
1162{
1163 iks *modify_affiliates = xmpp_pubsub_iq_create(client, "set");
1164 iks *pubsub, *affiliations, *affiliate;
1165 struct ao2_iterator i;
1166 struct ast_xmpp_buddy *buddy;
1167
1168 if (!modify_affiliates) {
1169 ast_log(LOG_ERROR, "Could not create IQ for creating affiliations on client '%s'\n", client->name);
1170 return;
1171 }
1172
1173 pubsub = iks_insert(modify_affiliates, "pubsub");
1174 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
1175 affiliations = iks_insert(pubsub, "affiliations");
1176 iks_insert_attrib(affiliations, "node", node);
1177
1178 i = ao2_iterator_init(client->buddies, 0);
1179 while ((buddy = ao2_iterator_next(&i))) {
1180 affiliate = iks_insert(affiliations, "affiliation");
1181 iks_insert_attrib(affiliate, "jid", buddy->id);
1182 iks_insert_attrib(affiliate, "affiliation", "owner");
1183 ao2_ref(buddy, -1);
1184 }
1186
1187 ast_xmpp_client_send(client, modify_affiliates);
1188 iks_delete(modify_affiliates);
1189}
1190
1191/*!
1192 * \brief Create a pubsub node
1193 * \param client the configured XMPP client we use to connect to a XMPP server
1194 * \param node_type the type of node to create
1195 * \param name the name of the node to create
1196 * \param collection_name
1197 */
1198static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const
1199 char *name, const char *collection_name)
1200{
1201 iks *node, *pubsub, *create;
1202
1203 if (!(node = xmpp_pubsub_iq_create(client, "set"))) {
1204 return;
1205 }
1206
1207 pubsub = iks_insert(node, "pubsub");
1208 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1209 create = iks_insert(pubsub, "create");
1210 iks_insert_attrib(create, "node", name);
1211 xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
1212 ast_xmpp_client_send(client, node);
1214 iks_delete(node);
1215}
1216
1217/*!
1218 * \brief Delete a PubSub node
1219 * \param client the configured XMPP client we use to connect to a XMPP server
1220 * \param node_name the name of the node to delete
1221 */
1222static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
1223{
1224 iks *request, *pubsub, *delete;
1225
1226 if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
1227 return;
1228 }
1229
1230 pubsub = iks_insert(request, "pubsub");
1231 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
1232 delete = iks_insert(pubsub, "delete");
1233 iks_insert_attrib(delete, "node", node_name);
1235
1236 iks_delete(request);
1237}
1238
1239/*!
1240 * \brief Create a PubSub collection node.
1241 * \param client the configured XMPP client we use to connect to a XMPP server
1242 * \param collection_name The name to use for this collection
1243 */
1244static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
1245{
1246 xmpp_pubsub_create_node(client, "collection", collection_name, NULL);
1247}
1248
1249
1250/*!
1251 * \brief Create a PubSub leaf node.
1252 * \param client the configured XMPP client we use to connect to a XMPP server
1253 * \param collection_name
1254 * \param leaf_name The name to use for this collection
1255 */
1256static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name,
1257 const char *leaf_name)
1258{
1259 xmpp_pubsub_create_node(client, "leaf", leaf_name, collection_name);
1260}
1261
1262/*!
1263 * \brief Publish MWI to a PubSub node
1264 * \param client the configured XMPP client we use to connect to a XMPP server
1265 * \param mailbox The mailbox identifier
1266 * \param oldmsgs Old messages
1267 * \param newmsgs New Messages
1268 */
1269static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox,
1270 const char *oldmsgs, const char *newmsgs)
1271{
1272 char eid_str[20];
1273 iks *mailbox_node, *request;
1274
1275 request = xmpp_pubsub_build_publish_skeleton(client, mailbox, "message_waiting",
1277 if (!request) {
1278 return;
1279 }
1280
1281 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1282 mailbox_node = iks_insert(request, "mailbox");
1283 iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
1284 iks_insert_attrib(mailbox_node, "eid", eid_str);
1285 iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
1286 iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
1287
1288 ast_xmpp_client_send(client, iks_root(request));
1289
1290 iks_delete(request);
1291}
1292
1293/*!
1294 * \brief Publish device state to a PubSub node
1295 * \param client the configured XMPP client we use to connect to a XMPP server
1296 * \param device the name of the device whose state to publish
1297 * \param device_state the state to publish
1298 * \param cachable
1299 */
1300static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device,
1301 const char *device_state, unsigned int cachable)
1302{
1304 iks *request, *state;
1305 char eid_str[20], cachable_str[2];
1306
1307 if (!cfg || !cfg->global || !(request = xmpp_pubsub_build_publish_skeleton(client, device, "device_state", cachable))) {
1308 return;
1309 }
1310
1311 if (ast_test_flag(&cfg->global->pubsub, XMPP_PUBSUB_AUTOCREATE)) {
1312 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1313 xmpp_pubsub_create_node(client, "leaf", device, "device_state");
1314 } else {
1315 xmpp_pubsub_create_node(client, NULL, device, NULL);
1316 }
1317 }
1318
1319 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
1320 state = iks_insert(request, "state");
1321 iks_insert_attrib(state, "xmlns", "http://asterisk.org");
1322 iks_insert_attrib(state, "eid", eid_str);
1323 snprintf(cachable_str, sizeof(cachable_str), "%u", cachable);
1324 iks_insert_attrib(state, "cachable", cachable_str);
1325 iks_insert_cdata(state, device_state, strlen(device_state));
1326 ast_xmpp_client_send(client, iks_root(request));
1327 iks_delete(request);
1328}
1329
1330/*!
1331 * \brief Callback function for MWI events
1332 * \param data void pointer to ast_client structure
1333 * \param sub, msg
1334 */
1335static void xmpp_pubsub_mwi_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
1336{
1337 struct ast_xmpp_client *client = data;
1338 char oldmsgs[10], newmsgs[10];
1339 struct ast_mwi_state *mwi_state;
1340
1342 return;
1343 }
1344
1345 mwi_state = stasis_message_data(msg);
1346
1347 if (ast_eid_cmp(&ast_eid_default, &mwi_state->eid)) {
1348 /* If the event didn't originate from this server, don't send it back out. */
1349 return;
1350 }
1351
1352 snprintf(oldmsgs, sizeof(oldmsgs), "%d", mwi_state->old_msgs);
1353 snprintf(newmsgs, sizeof(newmsgs), "%d", mwi_state->new_msgs);
1354 xmpp_pubsub_publish_mwi(client, mwi_state->uniqueid, oldmsgs, newmsgs);
1355}
1356
1357/*!
1358 * \brief Callback function for device state events
1359 * \param data void pointer to ast_client structure
1360 * \param sub, msg
1361 */
1362static void xmpp_pubsub_devstate_cb(void *data, struct stasis_subscription *sub, struct stasis_message *msg)
1363{
1364 struct ast_xmpp_client *client = data;
1365 struct ast_device_state_message *dev_state;
1366
1368 return;
1369 }
1370
1371 dev_state = stasis_message_data(msg);
1372 if (!dev_state->eid || ast_eid_cmp(&ast_eid_default, dev_state->eid)) {
1373 /* If the event is aggregate or didn't originate from this server, don't send it out. */
1374 return;
1375 }
1376
1377 xmpp_pubsub_publish_device_state(client, dev_state->device, ast_devstate_str(dev_state->state), dev_state->cachable);
1378}
1379
1380/*!
1381 * \brief Unsubscribe from a PubSub node
1382 * \param client the configured XMPP client we use to connect to a XMPP server
1383 * \param node the name of the node to which to unsubscribe from
1384 */
1385static void xmpp_pubsub_unsubscribe(struct ast_xmpp_client *client, const char *node)
1386{
1387 iks *request = xmpp_pubsub_iq_create(client, "set");
1388 iks *pubsub, *unsubscribe;
1389
1390 if (!request) {
1391 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub unsubscription on client '%s'\n", client->name);
1392 return;
1393 }
1394
1395 pubsub = iks_insert(request, "pubsub");
1396 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1397 unsubscribe = iks_insert(pubsub, "unsubscribe");
1398 iks_insert_attrib(unsubscribe, "jid", client->jid->partial);
1399 iks_insert_attrib(unsubscribe, "node", node);
1400
1402 iks_delete(request);
1403}
1404
1405/*!
1406 * \brief Subscribe to a PubSub node
1407 * \param client the configured XMPP client we use to connect to a XMPP server
1408 * \param node the name of the node to which to subscribe
1409 */
1410static void xmpp_pubsub_subscribe(struct ast_xmpp_client *client, const char *node)
1411{
1413 iks *request = xmpp_pubsub_iq_create(client, "set");
1414 iks *pubsub, *subscribe;
1415
1416 if (!cfg || !cfg->global || !request) {
1417 ast_log(LOG_ERROR, "Could not create IQ when creating pubsub subscription on client '%s'\n", client->name);
1418 return;
1419 }
1420
1421 pubsub = iks_insert(request, "pubsub");
1422 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
1423 subscribe = iks_insert(pubsub, "subscribe");
1424 iks_insert_attrib(subscribe, "jid", client->jid->partial);
1425 iks_insert_attrib(subscribe, "node", node);
1426 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1427 iks *options, *x, *sub_options, *sub_type, *sub_depth, *sub_expire;
1428 options = iks_insert(pubsub, "options");
1429 x = iks_insert(options, "x");
1430 iks_insert_attrib(x, "xmlns", "jabber:x:data");
1431 iks_insert_attrib(x, "type", "submit");
1432 sub_options = iks_insert(x, "field");
1433 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
1434 iks_insert_attrib(sub_options, "type", "hidden");
1435 iks_insert_cdata(iks_insert(sub_options, "value"),
1436 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
1437 sub_type = iks_insert(x, "field");
1438 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
1439 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
1440 sub_depth = iks_insert(x, "field");
1441 iks_insert_attrib(sub_depth, "var", "pubsub#subscription_depth");
1442 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
1443 sub_expire = iks_insert(x, "field");
1444 iks_insert_attrib(sub_expire, "var", "pubsub#expire");
1445 iks_insert_cdata(iks_insert(sub_expire, "value"), "presence", 8);
1446 }
1448 iks_delete(request);
1449}
1450
1451/*!
1452 * \brief Callback for handling PubSub events
1453 * \param data void pointer to ast_xmpp_client structure
1454 * \param pak A pak
1455 * \retval IKS_FILTER_EAT
1456 */
1457static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
1458{
1459 char *item_id, *device_state, *mailbox, *cachable_str;
1460 int oldmsgs, newmsgs;
1461 iks *item, *item_content;
1462 struct ast_eid pubsub_eid;
1463 unsigned int cachable = AST_DEVSTATE_CACHABLE;
1464 item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
1465 if (!item) {
1466 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
1467 return IKS_FILTER_EAT;
1468 }
1469 item_id = iks_find_attrib(item, "id");
1470 item_content = iks_child(item);
1471 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
1472 if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
1473 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
1474 return IKS_FILTER_EAT;
1475 }
1476 if (!strcasecmp(iks_name(item_content), "state")) {
1477 if ((cachable_str = iks_find_attrib(item_content, "cachable"))) {
1478 sscanf(cachable_str, "%30u", &cachable);
1479 }
1480 device_state = iks_find_cdata(item, "state");
1482 ast_devstate_val(device_state),
1484 &pubsub_eid);
1485 return IKS_FILTER_EAT;
1486 } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
1487 mailbox = strsep(&item_id, "@");
1488 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
1489 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
1490
1491 ast_publish_mwi_state_full(mailbox, item_id, newmsgs, oldmsgs, NULL, &pubsub_eid);
1492
1493 return IKS_FILTER_EAT;
1494 } else {
1495 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
1496 iks_name(item_content));
1497 return IKS_FILTER_EAT;
1498 }
1499
1500 return IKS_FILTER_EAT;
1501}
1502
1503static int xmpp_pubsub_handle_error(void *data, ikspak *pak)
1504{
1506 char *node_name, *error;
1507 int error_num;
1508 iks *orig_request, *orig_pubsub = iks_find(pak->x, "pubsub");
1509 struct ast_xmpp_client *client = data;
1510
1511 if (!cfg || !cfg->global) {
1512 ast_log(LOG_ERROR, "No global configuration available\n");
1513 return IKS_FILTER_EAT;
1514 }
1515
1516 if (!orig_pubsub) {
1517 ast_debug(1, "Error isn't a PubSub error, why are we here?\n");
1518 return IKS_FILTER_EAT;
1519 }
1520
1521 orig_request = iks_child(orig_pubsub);
1522 error = iks_find_attrib(iks_find(pak->x, "error"), "code");
1523 node_name = iks_find_attrib(orig_request, "node");
1524
1525 if (!sscanf(error, "%30d", &error_num)) {
1526 return IKS_FILTER_EAT;
1527 }
1528
1529 if (error_num > 399 && error_num < 500 && error_num != 404) {
1531 "Error performing operation on PubSub node %s, %s.\n", node_name, error);
1532 return IKS_FILTER_EAT;
1533 } else if (error_num > 499 && error_num < 600) {
1534 ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
1535 return IKS_FILTER_EAT;
1536 }
1537
1538 if (!strcasecmp(iks_name(orig_request), "publish")) {
1539 iks *request;
1540
1541 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1542 if (iks_find(iks_find(orig_request, "item"), "state")) {
1543 xmpp_pubsub_create_leaf(client, "device_state", node_name);
1544 } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
1545 xmpp_pubsub_create_leaf(client, "message_waiting", node_name);
1546 }
1547 } else {
1548 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1549 }
1550
1551 if ((request = xmpp_pubsub_iq_create(client, "set"))) {
1552 iks_insert_node(request, orig_pubsub);
1554 iks_delete(request);
1555 } else {
1556 ast_log(LOG_ERROR, "PubSub publish could not create IQ\n");
1557 }
1558
1559 return IKS_FILTER_EAT;
1560 } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
1561 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
1562 xmpp_pubsub_create_collection(client, node_name);
1563 } else {
1564 xmpp_pubsub_create_node(client, NULL, node_name, NULL);
1565 }
1566 }
1567
1568 return IKS_FILTER_EAT;
1569}
1570
1571static int cached_devstate_cb(void *obj, void *arg, int flags)
1572{
1573 struct stasis_message *msg = obj;
1574 struct ast_xmpp_client *client = arg;
1575 xmpp_pubsub_devstate_cb(client, client->device_state_sub, msg);
1576 return 0;
1577}
1578
1579/*!
1580 * \brief Initialize collections for event distribution
1581 * \param client the configured XMPP client we use to connect to a XMPP server
1582 */
1584{
1586 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1587 RAII_VAR(struct ao2_container *, cached, NULL, ao2_cleanup);
1588
1589 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
1590 return;
1591 }
1592
1593 xmpp_pubsub_unsubscribe(client, "device_state");
1594 xmpp_pubsub_unsubscribe(client, "message_waiting");
1595
1597 return;
1598 }
1601
1603 client->mwi_sub = stasis_unsubscribe(client->mwi_sub);
1604 return;
1605 }
1608
1610 ao2_callback(cached, OBJ_NODATA, cached_devstate_cb, client);
1611
1612 xmpp_pubsub_subscribe(client, "device_state");
1613 xmpp_pubsub_subscribe(client, "message_waiting");
1614 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_event, client, IKS_RULE_TYPE,
1615 IKS_PAK_MESSAGE, IKS_RULE_FROM, clientcfg->pubsubnode, IKS_RULE_DONE);
1616 iks_filter_add_rule(client->filter, xmpp_pubsub_handle_error, client, IKS_RULE_TYPE,
1617 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
1618
1619}
1620
1621/*! \brief Internal astobj2 callback function which returns the first resource, which is the highest priority one */
1622static int xmpp_resource_immediate(void *obj, void *arg, int flags)
1623{
1624 return CMP_MATCH | CMP_STOP;
1625}
1626
1627#define BUDDY_OFFLINE 6
1628#define BUDDY_NOT_IN_ROSTER 7
1629
1630static int get_buddy_status(struct ast_xmpp_client_config *clientcfg, char *screenname, char *resource)
1631{
1632 int status = BUDDY_OFFLINE;
1633 struct ast_xmpp_resource *res;
1634 struct ast_xmpp_buddy *buddy = ao2_find(clientcfg->client->buddies, screenname, OBJ_KEY);
1635
1636 if (!buddy) {
1637 return BUDDY_NOT_IN_ROSTER;
1638 }
1639
1640 res = ao2_callback(
1641 buddy->resources,
1642 0,
1644 resource);
1645
1646 if (res) {
1647 status = res->status;
1648 }
1649
1650 ao2_cleanup(res);
1651 ao2_cleanup(buddy);
1652
1653 return status;
1654}
1655
1656/*!
1657 * \internal
1658 * \brief Dial plan funtcion to retrieve the status of a buddy.
1659 * \param chan The associated ast_channel, if there is one
1660 * \param data The account, buddy JID, and optional timeout
1661 * \param name, buf, buflen
1662 *
1663 * \retval 0 success
1664 * \retval -1 failure
1665 */
1666static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1667{
1669 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1671 AST_APP_ARG(sender);
1672 AST_APP_ARG(jid);
1673 );
1675 AST_APP_ARG(screenname);
1676 AST_APP_ARG(resource);
1677 );
1678
1679 if (ast_strlen_zero(data)) {
1680 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
1681 return 0;
1682 }
1684
1685 if (args.argc != 2) {
1686 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
1687 return -1;
1688 }
1689
1690 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
1691 if (jid.argc < 1 || jid.argc > 2) {
1692 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
1693 return -1;
1694 }
1695
1696 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1697 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1698 return -1;
1699 }
1700
1701 snprintf(buf, buflen, "%d", get_buddy_status(clientcfg, jid.screenname, jid.resource));
1702
1703 return 0;
1704}
1705
1707 .name = "JABBER_STATUS",
1708 .read = acf_jabberstatus_read,
1709};
1710
1711/*!
1712 * \brief Application to join a chat room
1713 * \param chan ast_channel
1714 * \param data Data is sender|jid|nickname.
1715 * \retval 0 success
1716 * \retval -1 error
1717 */
1718static int xmpp_join_exec(struct ast_channel *chan, const char *data)
1719{
1721 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1722 char *s, nick[XMPP_MAX_RESJIDLEN];
1724 AST_APP_ARG(sender);
1725 AST_APP_ARG(jid);
1726 AST_APP_ARG(nick);
1727 );
1728
1729 if (ast_strlen_zero(data)) {
1730 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1731 return -1;
1732 }
1733 s = ast_strdupa(data);
1734
1736 if (args.argc < 2 || args.argc > 3) {
1737 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1738 return -1;
1739 }
1740
1741 if (strchr(args.jid, '/')) {
1742 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1743 return -1;
1744 }
1745
1746 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1747 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1748 return -1;
1749 }
1750
1751 if (ast_strlen_zero(args.nick)) {
1752 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1753 snprintf(nick, sizeof(nick), "asterisk");
1754 } else {
1755 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1756 }
1757 } else {
1758 snprintf(nick, sizeof(nick), "%s", args.nick);
1759 }
1760
1761 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1762 ast_xmpp_chatroom_join(clientcfg->client, args.jid, nick);
1763 } else {
1764 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1765 }
1766
1767 return 0;
1768}
1769
1770/*!
1771 * \brief Application to leave a chat room
1772 * \param chan ast_channel
1773 * \param data Data is sender|jid|nickname.
1774 * \retval 0 success
1775 * \retval -1 error
1776 */
1777static int xmpp_leave_exec(struct ast_channel *chan, const char *data)
1778{
1780 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1781 char *s, nick[XMPP_MAX_RESJIDLEN];
1783 AST_APP_ARG(sender);
1784 AST_APP_ARG(jid);
1785 AST_APP_ARG(nick);
1786 );
1787
1788 if (ast_strlen_zero(data)) {
1789 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1790 return -1;
1791 }
1792 s = ast_strdupa(data);
1793
1795 if (args.argc < 2 || args.argc > 3) {
1796 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1797 return -1;
1798 }
1799
1800 if (strchr(args.jid, '/')) {
1801 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1802 return -1;
1803 }
1804
1805 if (ast_strlen_zero(args.jid) || !strchr(args.jid, '@')) {
1806 ast_log(LOG_ERROR, "No jabber ID specified\n");
1807 return -1;
1808 }
1809
1810 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1811 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1812 return -1;
1813 }
1814
1815 if (ast_strlen_zero(args.nick)) {
1816 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1817 snprintf(nick, sizeof(nick), "asterisk");
1818 } else {
1819 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1820 }
1821 } else {
1822 snprintf(nick, sizeof(nick), "%s", args.nick);
1823 }
1824
1825 ast_xmpp_chatroom_leave(clientcfg->client, args.jid, nick);
1826
1827 return 0;
1828}
1829
1830/*!
1831 * \internal
1832 * \brief Dial plan function to send a message.
1833 * \param chan ast_channel
1834 * \param data Data is account,jid,message.
1835 * \retval 0 success
1836 * \retval -1 failure
1837 */
1838static int xmpp_send_exec(struct ast_channel *chan, const char *data)
1839{
1841 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1842 char *s;
1844 AST_APP_ARG(sender);
1845 AST_APP_ARG(recipient);
1847 );
1848
1849 if (ast_strlen_zero(data)) {
1850 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1851 return -1;
1852 }
1853 s = ast_strdupa(data);
1854
1856
1857 if ((args.argc < 3) || ast_strlen_zero(args.message) || !strchr(args.recipient, '@')) {
1858 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1859 return -1;
1860 }
1861
1862 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1863 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1864 return -1;
1865 }
1866
1867 ast_xmpp_client_send_message(clientcfg->client, args.recipient, args.message);
1868
1869 return 0;
1870}
1871
1872/*!
1873 * \brief Application to send a message to a groupchat.
1874 * \param chan ast_channel
1875 * \param data Data is sender|groupchat|message.
1876 * \retval 0 success
1877 * \retval -1 error
1878 */
1879static int xmpp_sendgroup_exec(struct ast_channel *chan, const char *data)
1880{
1882 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1883 char *s, nick[XMPP_MAX_RESJIDLEN];
1885 AST_APP_ARG(sender);
1886 AST_APP_ARG(groupchat);
1888 AST_APP_ARG(nick);
1889 );
1890
1891 if (ast_strlen_zero(data)) {
1892 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1893 return -1;
1894 }
1895 s = ast_strdupa(data);
1896
1898 if ((args.argc < 3) || (args.argc > 4) || ast_strlen_zero(args.message) || !strchr(args.groupchat, '@')) {
1899 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1900 return -1;
1901 }
1902
1903 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
1904 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1905 return -1;
1906 }
1907
1908 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1909 if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
1910 snprintf(nick, sizeof(nick), "asterisk");
1911 } else {
1912 snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
1913 }
1914 } else {
1915 snprintf(nick, sizeof(nick), "%s", args.nick);
1916 }
1917
1918 ast_xmpp_chatroom_send(clientcfg->client, nick, args.groupchat, args.message);
1919
1920 return 0;
1921}
1922
1923/*!
1924 * \internal
1925 * \brief Dial plan function to receive a message.
1926 * \param chan The associated ast_channel, if there is one
1927 * \param data The account, JID, and optional timeout
1928 * \param name, buf, buflen
1929 *
1930 * \retval 0 success
1931 * \retval -1 failure
1932 */
1933static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
1934{
1936 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
1937 char *parse = NULL;
1938 int timeout, jidlen, resourcelen, found = 0;
1939 struct timeval start;
1940 long diff = 0;
1941 struct ast_xmpp_message *message;
1943 AST_APP_ARG(account);
1944 AST_APP_ARG(jid);
1945 AST_APP_ARG(timeout);
1946 );
1948 AST_APP_ARG(screenname);
1949 AST_APP_ARG(resource);
1950 );
1951
1952 if (ast_strlen_zero(data)) {
1953 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1954 return -1;
1955 }
1956
1957 parse = ast_strdupa(data);
1959
1960 if (args.argc < 2 || args.argc > 3) {
1961 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
1962 return -1;
1963 }
1964
1965 parse = ast_strdupa(args.jid);
1966 AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
1967 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > XMPP_MAX_JIDLEN) {
1968 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
1969 return -1;
1970 }
1971
1972 if (ast_strlen_zero(args.timeout)) {
1973 timeout = 20;
1974 } else {
1975 sscanf(args.timeout, "%d", &timeout);
1976 if (timeout <= 0) {
1977 ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
1978 return -1;
1979 }
1980 }
1981
1982 jidlen = strlen(jid.screenname);
1983 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
1984
1985 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.account))) {
1986 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
1987 return -1;
1988 }
1989
1990 ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
1991
1992 start = ast_tvnow();
1993
1994 if (chan && ast_autoservice_start(chan) < 0) {
1995 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
1996 return -1;
1997 }
1998
1999 /* search the messages list, grab the first message that matches with
2000 * the from JID we're expecting, and remove it from the messages list */
2001 while (diff < timeout) {
2002 struct timespec ts = { 0, };
2003 struct timeval wait;
2004 int res = 0;
2005
2006 wait = ast_tvadd(start, ast_tv(timeout, 0));
2007 ts.tv_sec = wait.tv_sec;
2008 ts.tv_nsec = wait.tv_usec * 1000;
2009
2010 /* wait up to timeout seconds for an incoming message */
2012 if (AST_LIST_EMPTY(&clientcfg->client->messages)) {
2014 }
2016 if (res == ETIMEDOUT) {
2017 ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
2018 break;
2019 }
2020
2021 AST_LIST_LOCK(&clientcfg->client->messages);
2022 AST_LIST_TRAVERSE_SAFE_BEGIN(&clientcfg->client->messages, message, list) {
2023 if (jid.argc == 1) {
2024 /* no resource provided, compare bare JIDs */
2025 if (strncasecmp(jid.screenname, message->from, jidlen)) {
2026 continue;
2027 }
2028 } else {
2029 /* resource appended, compare bare JIDs and resources */
2030 char *resource = strchr(message->from, '/');
2031 if (!resource || strlen(resource) == 0) {
2032 ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", message->from);
2033 if (strncasecmp(jid.screenname, message->from, jidlen)) {
2034 continue;
2035 }
2036 } else {
2037 resource ++;
2038 if (strncasecmp(jid.screenname, message->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
2039 continue;
2040 }
2041 }
2042 }
2043 /* check if the message is not too old */
2044 if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
2045 ast_debug(3, "Found old message from %s, deleting it\n", message->from);
2048 continue;
2049 }
2050 found = 1;
2051 ast_copy_string(buf, message->message, buflen);
2054 break;
2055 }
2057 AST_LIST_UNLOCK(&clientcfg->client->messages);
2058 if (found) {
2059 break;
2060 }
2061
2062 /* check timeout */
2063 diff = ast_tvdiff_ms(ast_tvnow(), start);
2064 }
2065
2066 if (chan && ast_autoservice_stop(chan) < 0) {
2067 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
2068 }
2069
2070 /* return if we timed out */
2071 if (!found) {
2072 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
2073 return -1;
2074 }
2075
2076 return 0;
2077}
2078
2080 .name = "JABBER_RECEIVE",
2081 .read = acf_jabberreceive_read,
2082};
2083
2084/*!
2085 * \internal
2086 * \brief Delete old messages from a given JID
2087 * Messages stored during more than client->message_timeout are deleted
2088 * \param client Asterisk's XMPP client
2089 * \param from the JID we received messages from
2090 * \retval the number of deleted messages
2091 */
2092static int delete_old_messages(struct ast_xmpp_client *client, char *from)
2093{
2095 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2096 int deleted = 0, isold = 0;
2097 struct ast_xmpp_message *message = NULL;
2098
2099 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2100 return 0;
2101 }
2102
2103 AST_LIST_LOCK(&client->messages);
2105 if (isold) {
2106 if (!from || !strncasecmp(from, message->from, strlen(from))) {
2109 deleted++;
2110 }
2111 } else if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
2112 isold = 1;
2113 if (!from || !strncasecmp(from, message->from, strlen(from))) {
2116 deleted++;
2117 }
2118 }
2119 }
2121 AST_LIST_UNLOCK(&client->messages);
2122
2123 return deleted;
2124}
2125
2126static int xmpp_send_cb(const struct ast_msg *msg, const char *to, const char *from)
2127{
2129 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2130 char *sender, *dest;
2131 int res;
2132
2133 sender = ast_strdupa(from);
2134 strsep(&sender, ":");
2135 dest = ast_strdupa(to);
2136 strsep(&dest, ":");
2137
2138 if (ast_strlen_zero(sender)) {
2139 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for XMPP\n", from);
2140 return -1;
2141 }
2142
2143 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, sender))) {
2144 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
2145 return -1;
2146 }
2147
2148 ast_debug(1, "Sending message to '%s' from '%s'\n", dest, clientcfg->name);
2149
2150 if ((res = ast_xmpp_client_send_message(clientcfg->client, dest, ast_msg_get_body(msg))) != IKS_OK) {
2151 ast_log(LOG_WARNING, "Failed to send XMPP message (%d).\n", res);
2152 }
2153
2154 return res == IKS_OK ? 0 : -1;
2155}
2156
2157static const struct ast_msg_tech msg_tech = {
2158 .name = "xmpp",
2159 .msg_send = xmpp_send_cb,
2160};
2161
2162/*! \brief Internal function which creates a buddy on a client */
2164{
2165 struct ast_xmpp_buddy *buddy;
2166
2167 if (!(buddy = ao2_alloc(sizeof(*buddy), xmpp_buddy_destructor))) {
2168 return NULL;
2169 }
2170
2173 if (!buddy->resources) {
2174 ao2_ref(buddy, -1);
2175 return NULL;
2176 }
2177
2178 ast_copy_string(buddy->id, id, sizeof(buddy->id));
2179
2180 /* Assume we need to subscribe to get their presence until proven otherwise */
2181 buddy->subscribe = 1;
2182
2183 ao2_link(container, buddy);
2184
2185 return buddy;
2186}
2187
2188/*! \brief Helper function which unsubscribes a user and removes them from the roster */
2189static int xmpp_client_unsubscribe_user(struct ast_xmpp_client *client, const char *user)
2190{
2191 iks *iq, *query = NULL, *item = NULL;
2192
2193 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, user,
2194 "Goodbye. Your status is no longer required.\n"))) {
2195 return -1;
2196 }
2197
2198 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item"))) {
2199 ast_log(LOG_WARNING, "Could not allocate memory for roster removal of '%s' from client '%s'\n",
2200 user, client->name);
2201 goto done;
2202 }
2203
2204 iks_insert_attrib(iq, "from", client->jid->full);
2205 iks_insert_attrib(iq, "type", "set");
2206 iks_insert_attrib(query, "xmlns", "jabber:iq:roster");
2207 iks_insert_node(iq, query);
2208 iks_insert_attrib(item, "jid", user);
2209 iks_insert_attrib(item, "subscription", "remove");
2210 iks_insert_node(query, item);
2211
2212 if (ast_xmpp_client_send(client, iq)) {
2213 ast_log(LOG_WARNING, "Could not send roster removal request of '%s' from client '%s'\n",
2214 user, client->name);
2215 }
2216
2217done:
2218 iks_delete(item);
2219 iks_delete(query);
2220 iks_delete(iq);
2221
2222 return 0;
2223}
2224
2225/*! \brief Callback function which subscribes to a user if needed */
2226static int xmpp_client_subscribe_user(void *obj, void *arg, int flags)
2227{
2228 struct ast_xmpp_buddy *buddy = obj;
2229 struct ast_xmpp_client *client = arg;
2230
2231 if (!buddy->subscribe) {
2232 return 0;
2233 }
2234
2235 if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, buddy->id,
2236 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"))) {
2237 ast_log(LOG_WARNING, "Could not send subscription for '%s' on client '%s'\n",
2238 buddy->id, client->name);
2239 }
2240
2241 buddy->subscribe = 0;
2242
2243 return 0;
2244}
2245
2246/*! \brief Hook function called when roster is received from server */
2247static int xmpp_roster_hook(void *data, ikspak *pak)
2248{
2250 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2251 struct ast_xmpp_client *client = data;
2252 iks *item;
2253
2254 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2255 return IKS_FILTER_EAT;
2256 }
2257
2258 for (item = iks_child(pak->query); item; item = iks_next(item)) {
2259 struct ast_xmpp_buddy *buddy;
2260
2261 if (iks_strcmp(iks_name(item), "item")) {
2262 continue;
2263 }
2264
2265 if (!(buddy = ao2_find(client->buddies, iks_find_attrib(item, "jid"), OBJ_KEY))) {
2266 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOPRUNE)) {
2267 /* The buddy has not been specified in the configuration file, we no longer
2268 * want them on our buddy list or to receive their presence. */
2269 if (xmpp_client_unsubscribe_user(client, iks_find_attrib(item, "jid"))) {
2270 ast_log(LOG_ERROR, "Could not unsubscribe user '%s' on client '%s'\n",
2271 iks_find_attrib(item, "jid"), client->name);
2272 }
2273 continue;
2274 }
2275
2276 if (!(buddy = xmpp_client_create_buddy(client->buddies, iks_find_attrib(item, "jid")))) {
2277 ast_log(LOG_ERROR, "Could not allocate buddy '%s' on client '%s'\n", iks_find_attrib(item, "jid"),
2278 client->name);
2279 continue;
2280 }
2281 }
2282
2283 /* Determine if we need to subscribe to their presence or not */
2284 if (!iks_strcmp(iks_find_attrib(item, "subscription"), "none") ||
2285 !iks_strcmp(iks_find_attrib(item, "subscription"), "from")) {
2286 buddy->subscribe = 1;
2287 } else {
2288 buddy->subscribe = 0;
2289 }
2290
2291 ao2_ref(buddy, -1);
2292 }
2293
2294 /* If autoregister is enabled we need to go through every buddy that we need to subscribe to and do so */
2295 if (ast_test_flag(&clientcfg->flags, XMPP_AUTOREGISTER)) {
2297 }
2298
2300
2301 return IKS_FILTER_EAT;
2302}
2303
2304/*! \brief Internal function which changes the presence status of an XMPP client */
2305static void xmpp_client_set_presence(struct ast_xmpp_client *client, const char *to, const char *from, int level, const char *desc)
2306{
2308 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2309 iks *presence = NULL, *cnode = NULL, *priority = NULL;
2310 char priorityS[10];
2311
2312 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2313 !(presence = iks_make_pres(level, desc)) || !(cnode = iks_new("c")) || !(priority = iks_new("priority"))) {
2314 ast_log(LOG_ERROR, "Unable to allocate stanzas for setting presence status for client '%s'\n", client->name);
2315 goto done;
2316 }
2317
2318 if (!ast_strlen_zero(to)) {
2319 iks_insert_attrib(presence, "to", to);
2320 }
2321
2322 if (!ast_strlen_zero(from)) {
2323 iks_insert_attrib(presence, "from", from);
2324 }
2325
2326 snprintf(priorityS, sizeof(priorityS), "%d", clientcfg->priority);
2327 iks_insert_cdata(priority, priorityS, strlen(priorityS));
2328 iks_insert_node(presence, priority);
2329 iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
2330 iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
2331 iks_insert_attrib(cnode, "ext", "voice-v1 video-v1 camera-v1");
2332 iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
2333 iks_insert_node(presence, cnode);
2334 ast_xmpp_client_send(client, presence);
2335
2336done:
2337 iks_delete(cnode);
2338 iks_delete(presence);
2339 iks_delete(priority);
2340}
2341
2342/*! \brief Hook function called when client receives a service discovery get message */
2343static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
2344{
2345 struct ast_xmpp_client *client = data;
2346 iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
2347
2348 if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(ident = iks_new("identity")) || !(disco = iks_new("feature")) ||
2349 !(google = iks_new("feature")) || !(jingle = iks_new("feature")) || !(ice = iks_new("feature")) || !(rtp = iks_new("feature")) ||
2350 !(audio = iks_new("feature")) || !(video = iks_new("feature"))) {
2351 ast_log(LOG_ERROR, "Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
2352 pak->from->full, client->name);
2353 goto end;
2354 }
2355
2356 iks_insert_attrib(iq, "from", client->jid->full);
2357
2358 if (pak->from) {
2359 iks_insert_attrib(iq, "to", pak->from->full);
2360 }
2361
2362 iks_insert_attrib(iq, "type", "result");
2363 iks_insert_attrib(iq, "id", pak->id);
2364 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2365 iks_insert_attrib(ident, "category", "client");
2366 iks_insert_attrib(ident, "type", "pc");
2367 iks_insert_attrib(ident, "name", "asterisk");
2368 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2369
2370 iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2371 iks_insert_attrib(jingle, "var", "urn:xmpp:jingle:1");
2372 iks_insert_attrib(ice, "var", "urn:xmpp:jingle:transports:ice-udp:1");
2373 iks_insert_attrib(rtp, "var", "urn:xmpp:jingle:apps:rtp:1");
2374 iks_insert_attrib(audio, "var", "urn:xmpp:jingle:apps:rtp:audio");
2375 iks_insert_attrib(video, "var", "urn:xmpp:jingle:apps:rtp:video");
2376 iks_insert_node(iq, query);
2377 iks_insert_node(query, ident);
2378 iks_insert_node(query, google);
2379 iks_insert_node(query, disco);
2380 iks_insert_node(query, jingle);
2381 iks_insert_node(query, ice);
2382 iks_insert_node(query, rtp);
2383 iks_insert_node(query, audio);
2384 iks_insert_node(query, video);
2385 ast_xmpp_client_send(client, iq);
2386
2387end:
2388 iks_delete(query);
2389 iks_delete(video);
2390 iks_delete(audio);
2391 iks_delete(rtp);
2392 iks_delete(ice);
2393 iks_delete(jingle);
2394 iks_delete(google);
2395 iks_delete(ident);
2396 iks_delete(disco);
2397 iks_delete(iq);
2398
2399 return IKS_FILTER_EAT;
2400}
2401
2402/*! \brief Hook function called when client receives a service discovery result message */
2403static int xmpp_client_service_discovery_result_hook(void *data, ikspak *pak)
2404{
2405 struct ast_xmpp_client *client = data;
2406 struct ast_xmpp_buddy *buddy;
2408
2409 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2410 return IKS_FILTER_EAT;
2411 }
2412
2413 if (!(resource = ao2_callback(buddy->resources, 0, xmpp_resource_cmp, pak->from->resource))) {
2414 ao2_ref(buddy, -1);
2415 return IKS_FILTER_EAT;
2416 }
2417
2419
2420 if (iks_find_with_attrib(pak->query, "feature", "var", "urn:xmpp:jingle:1")) {
2421 resource->caps.jingle = 1;
2422 }
2423
2425
2426 ao2_ref(resource, -1);
2427 ao2_ref(buddy, -1);
2428
2429 return IKS_FILTER_EAT;
2430}
2431
2432/*! \brief Hook function called when client finishes authenticating with the server */
2433static int xmpp_connect_hook(void *data, ikspak *pak)
2434{
2436 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2437 struct ast_xmpp_client *client = data;
2438 iks *roster;
2439
2440 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
2441 return -1;
2442 }
2443
2444 client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
2445
2446 if (ast_test_flag(&clientcfg->flags, XMPP_DISTRIBUTE_EVENTS)) {
2448 }
2449
2450 if (!(roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER))) {
2451 ast_log(LOG_ERROR, "Unable to allocate memory for roster request for client '%s'\n", client->name);
2452 return -1;
2453 }
2454
2455 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);
2456 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);
2457
2458 iks_insert_attrib(roster, "id", "roster");
2459 ast_xmpp_client_send(client, roster);
2460
2461 iks_filter_remove_hook(client->filter, xmpp_connect_hook);
2462 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);
2463
2464 xmpp_client_set_presence(client, NULL, client->jid->full, clientcfg->status, clientcfg->statusmsg);
2466
2467 return IKS_FILTER_EAT;
2468}
2469
2470/*! \brief Logging hook function */
2471static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incoming)
2472{
2474 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2475 struct ast_xmpp_client *client = data;
2476
2477 if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) {
2478 return;
2479 }
2480
2481 if (!incoming) {
2482 ast_verbose("\n<--- XMPP sent to '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2483 } else {
2484 ast_verbose("\n<--- XMPP received from '%s' --->\n%s\n<------------->\n", client->name, xmpp);
2485 }
2486}
2487
2488/*! \brief Internal function which sends a raw message */
2489static int xmpp_client_send_raw_message(struct ast_xmpp_client *client, const char *message)
2490{
2491 int ret;
2492
2493 if (client->state == XMPP_STATE_DISCONNECTED) {
2494 /* iks_send_raw will crash without a connection */
2495 return IKS_NET_NOCONN;
2496 }
2497
2498#ifdef HAVE_OPENSSL
2499 if (xmpp_is_secure(client)) {
2500 int len = strlen(message);
2501
2502 ret = SSL_write(client->ssl_session, message, len);
2503 if (ret) {
2504 /* Log the message here, because iksemel's logHook is
2505 unaccessible */
2506 xmpp_log_hook(client, message, len, 0);
2507 return IKS_OK;
2508 }
2509 }
2510#endif
2511 /* If needed, data will be sent unencrypted, and logHook will
2512 be called inside iks_send_raw */
2513 ret = iks_send_raw(client->parser, message);
2514 if (ret != IKS_OK) {
2515 return ret;
2516 }
2517
2518 return IKS_OK;
2519}
2520
2521/*! \brief Helper function which sends an XMPP stream header to the server */
2522static int xmpp_send_stream_header(struct ast_xmpp_client *client, const struct ast_xmpp_client_config *cfg, const char *to)
2523{
2524 char *namespace = ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client";
2525 char msg[91 + strlen(namespace) + 6 + strlen(to) + 16 + 1];
2526
2527 snprintf(msg, sizeof(msg), "<?xml version='1.0'?>"
2528 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
2529 "%s' to='%s' version='1.0'>", namespace, to);
2530
2531 return xmpp_client_send_raw_message(client, msg);
2532}
2533
2534int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza)
2535{
2536 return xmpp_client_send_raw_message(client, iks_string(iks_stack(stanza), stanza));
2537}
2538
2539/*! \brief Internal function called when we need to request TLS support */
2540static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2541{
2542 /* If the client connection is already secure we can jump straight to authenticating */
2543 if (xmpp_is_secure(client)) {
2545 return 0;
2546 }
2547
2548#ifndef HAVE_OPENSSL
2549 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL is not available.\n", client->name);
2550 return -1;
2551#else
2552 if (iks_send_raw(client->parser, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>") == IKS_NET_TLSFAIL) {
2553 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be started.\n", client->name);
2554 return -1;
2555 }
2556
2557 client->stream_flags |= TRY_SECURE;
2558
2560
2561 return 0;
2562#endif
2563}
2564
2565#ifdef HAVE_OPENSSL
2566static char *openssl_error_string(void)
2567{
2568 char *buf = NULL, *ret;
2569 size_t len;
2570 BIO *bio = BIO_new(BIO_s_mem());
2571
2572 ERR_print_errors(bio);
2573 len = BIO_get_mem_data(bio, &buf);
2574 ret = ast_calloc(1, len + 1);
2575 if (ret) {
2576 memcpy(ret, buf, len);
2577 }
2578 BIO_free(bio);
2579 return ret;
2580}
2581#endif
2582
2583/*! \brief Internal function called when we receive a response to our TLS initiation request */
2584static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2585{
2586#ifdef HAVE_OPENSSL
2587 int sock;
2588 long ssl_opts;
2589 char *err;
2590#endif
2591
2592 if (!strcmp(iks_name(node), "success")) {
2593 /* TLS is up and working, we can move on to authenticating now */
2595 return 0;
2596 } else if (!strcmp(iks_name(node), "failure")) {
2597 /* TLS negotiation was a failure, close it on down! */
2598 return -1;
2599 } else if (strcmp(iks_name(node), "proceed")) {
2600 /* Ignore any other responses */
2601 return 0;
2602 }
2603
2604#ifndef HAVE_OPENSSL
2605 ast_log(LOG_ERROR, "Somehow we managed to try to start TLS negotiation on client '%s' without OpenSSL support, disconnecting\n", client->name);
2606 return -1;
2607#else
2608 client->ssl_method = SSLv23_method();
2609 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
2610 goto failure;
2611 }
2612
2613 ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
2614 SSL_CTX_set_options(client->ssl_context, ssl_opts);
2615
2616 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
2617 goto failure;
2618 }
2619
2620 sock = iks_fd(client->parser);
2621 if (!SSL_set_fd(client->ssl_session, sock)) {
2622 goto failure;
2623 }
2624
2625 if (SSL_connect(client->ssl_session) <= 0) {
2626 goto failure;
2627 }
2628
2629 client->stream_flags &= (~TRY_SECURE);
2630 client->stream_flags |= SECURE;
2631
2632 if (xmpp_send_stream_header(client, cfg, client->jid->server) != IKS_OK) {
2633 ast_log(LOG_ERROR, "TLS connection for client '%s' could not be established, failed to send stream header after negotiation\n",
2634 client->name);
2635 return -1;
2636 }
2637
2638 ast_debug(1, "TLS connection for client '%s' started with server\n", client->name);
2639
2641
2642 return 0;
2643
2644failure:
2645 err = openssl_error_string();
2646 ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. "
2647 "OpenSSL initialization failed: %s\n", client->name, err);
2648 ast_free(err);
2649 return -1;
2650#endif
2651}
2652
2653/*! \brief Internal function called when we need to authenticate using non-SASL */
2654static int xmpp_client_authenticate_digest(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2655{
2656 iks *iq = NULL, *query = NULL;
2657 char buf[41], sidpass[100];
2658
2659 if (!(iq = iks_new("iq")) || !(query = iks_insert(iq, "query"))) {
2660 ast_log(LOG_ERROR, "Stanzas could not be allocated for authentication on client '%s'\n", client->name);
2661 iks_delete(iq);
2662 return -1;
2663 }
2664
2665 iks_insert_attrib(iq, "type", "set");
2666 iks_insert_cdata(iks_insert(query, "username"), client->jid->user, 0);
2667 iks_insert_cdata(iks_insert(query, "resource"), client->jid->resource, 0);
2668
2669 iks_insert_attrib(query, "xmlns", "jabber:iq:auth");
2670 snprintf(sidpass, sizeof(sidpass), "%s%s", iks_find_attrib(node, "id"), cfg->password);
2671 ast_sha1_hash(buf, sidpass);
2672 iks_insert_cdata(iks_insert(query, "digest"), buf, 0);
2673
2674 ast_xmpp_client_lock(client);
2675 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);
2676 iks_insert_attrib(iq, "id", client->mid);
2677 ast_xmpp_increment_mid(client->mid);
2678 ast_xmpp_client_unlock(client);
2679
2680 iks_insert_attrib(iq, "to", client->jid->server);
2681
2682 ast_xmpp_client_send(client, iq);
2683
2684 iks_delete(iq);
2685
2687
2688 return 0;
2689}
2690
2691/*! \brief Internal function called when we need to authenticate using SASL */
2692static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2693{
2694 int features, len = strlen(client->jid->user) + strlen(cfg->password) + 3;
2695 iks *auth;
2696 char combined[len];
2697 char base64[(len + 2) * 4 / 3];
2698
2699 if (strcmp(iks_name(node), "stream:features")) {
2700 /* Ignore anything beside stream features */
2701 return 0;
2702 }
2703
2704 features = iks_stream_features(node);
2705
2706 if ((features & IKS_STREAM_SASL_MD5) && !xmpp_is_secure(client)) {
2707 if (iks_start_sasl(client->parser, IKS_SASL_DIGEST_MD5, (char*)client->jid->user, (char*)cfg->password) != IKS_OK) {
2708 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL DIGEST-MD5 but could not\n", client->name);
2709 return -1;
2710 }
2711
2713 return 0;
2714 }
2715
2716 /* Our only other available option is plain so if they don't support it, bail out now */
2717 if (!(features & IKS_STREAM_SASL_PLAIN)) {
2718 ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL PLAIN but server does not support it\n", client->name);
2719 return -1;
2720 }
2721
2722 if (!(auth = iks_new("auth"))) {
2723 ast_log(LOG_ERROR, "Could not allocate memory for SASL PLAIN authentication for client '%s'\n", client->name);
2724 return -1;
2725 }
2726
2727 iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
2728 if (!ast_strlen_zero(cfg->refresh_token)) {
2729 iks_insert_attrib(auth, "mechanism", "X-OAUTH2");
2730 iks_insert_attrib(auth, "auth:service", "oauth2");
2731 iks_insert_attrib(auth, "xmlns:auth", "http://www.google.com/talk/protocol/auth");
2732 } else {
2733 iks_insert_attrib(auth, "mechanism", "PLAIN");
2734 }
2735
2736 if (strchr(client->jid->user, '/')) {
2737 char *user = ast_strdupa(client->jid->user);
2738
2739 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, strsep(&user, "/"), 0, cfg->password);
2740 } else {
2741 snprintf(combined, sizeof(combined), "%c%s%c%s", 0, client->jid->user, 0, cfg->password);
2742 }
2743
2744 ast_base64encode(base64, (const unsigned char *) combined, len - 1, (len + 2) * 4 / 3);
2745 iks_insert_cdata(auth, base64, 0);
2746
2747 ast_xmpp_client_send(client, auth);
2748
2749 iks_delete(auth);
2750
2752
2753 return 0;
2754}
2755
2756/*! \brief Internal function called when we need to authenticate */
2757static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2758{
2760}
2761
2762/*! \brief Internal function called when we are authenticating */
2763static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2764{
2765 int features;
2766
2767 if (!strcmp(iks_name(node), "success")) {
2768 /* Authentication was a success, yay! */
2769 xmpp_send_stream_header(client, cfg, client->jid->server);
2770
2771 return 0;
2772 } else if (!strcmp(iks_name(node), "failure")) {
2773 /* Authentication was a bust, disconnect and reconnect later */
2774 return -1;
2775 } else if (strcmp(iks_name(node), "stream:features")) {
2776 /* Ignore any other responses */
2777 return 0;
2778 }
2779
2780 features = iks_stream_features(node);
2781
2782 if (features & IKS_STREAM_BIND) {
2783 iks *auth;
2784
2785 if (!(auth = iks_make_resource_bind(client->jid))) {
2786 ast_log(LOG_ERROR, "Failed to allocate memory for stream bind on client '%s'\n", client->name);
2787 return -1;
2788 }
2789
2790 ast_xmpp_client_lock(client);
2791 iks_insert_attrib(auth, "id", client->mid);
2792 ast_xmpp_increment_mid(client->mid);
2793 ast_xmpp_client_unlock(client);
2794 ast_xmpp_client_send(client, auth);
2795
2796 iks_delete(auth);
2797
2798 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);
2799 }
2800
2801 if (features & IKS_STREAM_SESSION) {
2802 iks *auth;
2803
2804 if (!(auth = iks_make_session())) {
2805 ast_log(LOG_ERROR, "Failed to allocate memory for stream session on client '%s'\n", client->name);
2806 return -1;
2807 }
2808
2809 iks_insert_attrib(auth, "id", "auth");
2810 ast_xmpp_client_lock(client);
2811 ast_xmpp_increment_mid(client->mid);
2812 ast_xmpp_client_unlock(client);
2813 ast_xmpp_client_send(client, auth);
2814
2815 iks_delete(auth);
2816
2817 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);
2818 }
2819
2820 return 0;
2821}
2822
2823/*! \brief Internal function called when we should authenticate as a component */
2824static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
2825{
2826 char secret[160], shasum[320], message[344];
2827 ikspak *pak = iks_packet(node);
2828
2829 snprintf(secret, sizeof(secret), "%s%s", pak->id, cfg->password);
2830 ast_sha1_hash(shasum, secret);
2831 snprintf(message, sizeof(message), "<handshake>%s</handshake>", shasum);
2832
2833 if (xmpp_client_send_raw_message(client, message) != IKS_OK) {
2834 ast_log(LOG_ERROR, "Unable to send handshake for component '%s'\n", client->name);
2835 return -1;
2836 }
2837
2839
2840 return 0;
2841}
2842
2843/*! \brief Hook function called when component receives a service discovery get message */
2844static int xmpp_component_service_discovery_get_hook(void *data, ikspak *pak)
2845{
2847 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2848 struct ast_xmpp_client *client = data;
2849 iks *iq = NULL, *query = NULL, *identity = NULL, *disco = NULL, *reg = NULL, *commands = NULL, *gateway = NULL;
2850 iks *version = NULL, *vcard = NULL, *search = NULL, *item = NULL;
2851 char *node;
2852
2853 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2854 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(identity = iks_new("identity")) || !(disco = iks_new("feature")) ||
2855 !(reg = iks_new("feature")) || !(commands = iks_new("feature")) || !(gateway = iks_new("feature")) || !(version = iks_new("feature")) ||
2856 !(vcard = iks_new("feature")) || !(search = iks_new("search")) || !(item = iks_new("item"))) {
2857 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery get response to '%s' on component '%s'\n",
2858 pak->from->partial, client->name);
2859 goto done;
2860 }
2861
2862 iks_insert_attrib(iq, "from", clientcfg->user);
2863 iks_insert_attrib(iq, "to", pak->from->full);
2864 iks_insert_attrib(iq, "id", pak->id);
2865 iks_insert_attrib(iq, "type", "result");
2866
2867 if (!(node = iks_find_attrib(pak->query, "node"))) {
2868 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2869 iks_insert_attrib(identity, "category", "gateway");
2870 iks_insert_attrib(identity, "type", "pstn");
2871 iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2872 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2873 iks_insert_attrib(reg, "var", "jabber:iq:register");
2874 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2875 iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2876 iks_insert_attrib(version, "var", "jabber:iq:version");
2877 iks_insert_attrib(vcard, "var", "vcard-temp");
2878 iks_insert_attrib(search, "var", "jabber:iq:search");
2879
2880 iks_insert_node(iq, query);
2881 iks_insert_node(query, identity);
2882 iks_insert_node(query, disco);
2883 iks_insert_node(query, reg);
2884 iks_insert_node(query, commands);
2885 iks_insert_node(query, gateway);
2886 iks_insert_node(query, version);
2887 iks_insert_node(query, vcard);
2888 iks_insert_node(query, search);
2889 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
2890 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2891 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2892 iks_insert_attrib(item, "node", "confirmaccount");
2893 iks_insert_attrib(item, "name", "Confirm account");
2894 iks_insert_attrib(item, "jid", clientcfg->user);
2895
2896 iks_insert_node(iq, query);
2897 iks_insert_node(query, item);
2898 } else if (!strcasecmp(node, "confirmaccount")) {
2899 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2900 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2901
2902 iks_insert_node(iq, query);
2903 iks_insert_node(query, commands);
2904 } else {
2905 ast_debug(3, "Unsupported service discovery info request received with node '%s' on component '%s'\n",
2906 node, client->name);
2907 goto done;
2908 }
2909
2910 if (ast_xmpp_client_send(client, iq)) {
2911 ast_log(LOG_WARNING, "Could not send response to service discovery request on component '%s'\n",
2912 client->name);
2913 }
2914
2915done:
2916 iks_delete(search);
2917 iks_delete(vcard);
2918 iks_delete(version);
2919 iks_delete(gateway);
2920 iks_delete(commands);
2921 iks_delete(reg);
2922 iks_delete(disco);
2923 iks_delete(identity);
2924 iks_delete(query);
2925 iks_delete(iq);
2926
2927 return IKS_FILTER_EAT;
2928}
2929
2930/*! \brief Hook function called when the component is queried about registration */
2931static int xmpp_component_register_get_hook(void *data, ikspak *pak)
2932{
2934 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
2935 struct ast_xmpp_client *client = data;
2936 iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL, *instructions = NULL;
2937 struct ast_xmpp_buddy *buddy;
2938 char *node;
2939
2940 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
2941 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(error = iks_new("error")) || !(notacceptable = iks_new("not-acceptable")) ||
2942 !(instructions = iks_new("instructions"))) {
2943 ast_log(LOG_ERROR, "Failed to allocate stanzas for register get response to '%s' on component '%s'\n",
2944 pak->from->partial, client->name);
2945 goto done;
2946 }
2947
2948 iks_insert_attrib(iq, "from", clientcfg->user);
2949 iks_insert_attrib(iq, "to", pak->from->full);
2950 iks_insert_attrib(iq, "id", pak->id);
2951 iks_insert_attrib(iq, "type", "result");
2952 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
2953 iks_insert_node(iq, query);
2954
2955 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
2956 iks_insert_attrib(error, "code", "406");
2957 iks_insert_attrib(error, "type", "modify");
2958 iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
2959
2960 iks_insert_node(iq, error);
2961 iks_insert_node(error, notacceptable);
2962
2963 ast_log(LOG_ERROR, "Received register attempt from '%s' but buddy is not configured on component '%s'\n",
2964 pak->from->partial, client->name);
2965 } else if (!(node = iks_find_attrib(pak->query, "node"))) {
2966 iks_insert_cdata(instructions, "Welcome to Asterisk - the Open Source PBX.\n", 0);
2967 iks_insert_node(query, instructions);
2968 ao2_ref(buddy, -1);
2969 } else {
2970 ast_log(LOG_WARNING, "Received register get to component '%s' using unsupported node '%s' from '%s'\n",
2971 client->name, node, pak->from->partial);
2972 ao2_ref(buddy, -1);
2973 goto done;
2974 }
2975
2976 if (ast_xmpp_client_send(client, iq)) {
2977 ast_log(LOG_WARNING, "Could not send response to '%s' for received register get on component '%s'\n",
2978 pak->from->partial, client->name);
2979 }
2980
2981done:
2982 iks_delete(instructions);
2983 iks_delete(notacceptable);
2984 iks_delete(error);
2985 iks_delete(query);
2986 iks_delete(iq);
2987
2988 return IKS_FILTER_EAT;
2989}
2990
2991/*! \brief Hook function called when someone registers to the component */
2992static int xmpp_component_register_set_hook(void *data, ikspak *pak)
2993{
2994 struct ast_xmpp_client *client = data;
2995 iks *iq, *presence = NULL, *x = NULL;
2996
2997 if (!(iq = iks_new("iq")) || !(presence = iks_new("presence")) || !(x = iks_new("x"))) {
2998 ast_log(LOG_ERROR, "Failed to allocate stanzas for register set response to '%s' on component '%s'\n",
2999 pak->from->partial, client->name);
3000 goto done;
3001 }
3002
3003 iks_insert_attrib(iq, "from", client->jid->full);
3004 iks_insert_attrib(iq, "to", pak->from->full);
3005 iks_insert_attrib(iq, "id", pak->id);
3006 iks_insert_attrib(iq, "type", "result");
3007
3008 if (ast_xmpp_client_send(client, iq)) {
3009 ast_log(LOG_WARNING, "Could not send response to '%s' for received register set on component '%s'\n",
3010 pak->from->partial, client->name);
3011 goto done;
3012 }
3013
3014 iks_insert_attrib(presence, "from", client->jid->full);
3015 iks_insert_attrib(presence, "to", pak->from->partial);
3016 ast_xmpp_client_lock(client);
3017 iks_insert_attrib(presence, "id", client->mid);
3018 ast_xmpp_increment_mid(client->mid);
3019 ast_xmpp_client_unlock(client);
3020 iks_insert_attrib(presence, "type", "subscribe");
3021 iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
3022
3023 iks_insert_node(presence, x);
3024
3025 if (ast_xmpp_client_send(client, presence)) {
3026 ast_log(LOG_WARNING, "Could not send subscription to '%s' on component '%s'\n",
3027 pak->from->partial, client->name);
3028 }
3029
3030done:
3031 iks_delete(x);
3032 iks_delete(presence);
3033 iks_delete(iq);
3034
3035 return IKS_FILTER_EAT;
3036}
3037
3038/*! \brief Hook function called when we receive a service discovery items request */
3039static int xmpp_component_service_discovery_items_hook(void *data, ikspak *pak)
3040{
3042 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3043 struct ast_xmpp_client *client = data;
3044 iks *iq = NULL, *query = NULL, *item = NULL, *feature = NULL;
3045 char *node;
3046
3047 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
3048 !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item")) || !(feature = iks_new("feature"))) {
3049 ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery items response to '%s' on component '%s'\n",
3050 pak->from->partial, client->name);
3051 goto done;
3052 }
3053
3054 iks_insert_attrib(iq, "from", clientcfg->user);
3055 iks_insert_attrib(iq, "to", pak->from->full);
3056 iks_insert_attrib(iq, "id", pak->id);
3057 iks_insert_attrib(iq, "type", "result");
3058 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
3059 iks_insert_node(iq, query);
3060
3061 if (!(node = iks_find_attrib(pak->query, "node"))) {
3062 iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
3063 iks_insert_attrib(item, "name", "Asterisk Commands");
3064 iks_insert_attrib(item, "jid", clientcfg->user);
3065
3066 iks_insert_node(query, item);
3067 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
3068 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
3069 } else {
3070 ast_log(LOG_WARNING, "Received service discovery items request to component '%s' using unsupported node '%s' from '%s'\n",
3071 client->name, node, pak->from->partial);
3072 goto done;
3073 }
3074
3075 if (ast_xmpp_client_send(client, iq)) {
3076 ast_log(LOG_WARNING, "Could not send response to service discovery items request from '%s' on component '%s'\n",
3077 pak->from->partial, client->name);
3078 }
3079
3080done:
3081 iks_delete(feature);
3082 iks_delete(item);
3083 iks_delete(query);
3084 iks_delete(iq);
3085
3086 return IKS_FILTER_EAT;
3087}
3088
3089/*! \brief Internal function called when we authenticated as a component */
3090static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
3091{
3092 if (!strcmp(iks_name(node), "stream:features")) {
3093 return 0;
3094 }
3095
3096 if (strcmp(iks_name(node), "handshake")) {
3097 ast_log(LOG_ERROR, "Failed to authenticate component '%s'\n", client->name);
3098 return -1;
3099 }
3100
3101 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);
3102
3103 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);
3104
3105 /* This uses the client service discovery result hook on purpose, as the code is common between both */
3106 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);
3107
3108 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);
3109 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);
3110
3112
3113 return 0;
3114}
3115
3116/*! \brief Internal function called when a message is received */
3117static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
3118{
3119 struct ast_xmpp_message *message;
3120 char *body;
3121 int deleted = 0;
3122
3123 ast_debug(3, "XMPP client '%s' received a message\n", client->name);
3124
3125 if (!(body = iks_find_cdata(pak->x, "body"))) {
3126 /* Message contains no body, ignore it. */
3127 return 0;
3128 }
3129
3130 if (!(message = ast_calloc(1, sizeof(*message)))) {
3131 return -1;
3132 }
3133
3134 message->arrived = ast_tvnow();
3135
3136 message->message = ast_strdup(body);
3137
3138 ast_copy_string(message->id, S_OR(pak->id, ""), sizeof(message->id));
3139 message->from = !ast_strlen_zero(pak->from->full) ? ast_strdup(pak->from->full) : NULL;
3140
3142 struct ast_msg *msg;
3143 struct ast_xmpp_buddy *buddy;
3144
3145 if ((msg = ast_msg_alloc())) {
3146 int res;
3147
3148 ast_xmpp_client_lock(client);
3149
3150 buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY | OBJ_NOLOCK);
3151
3152 res = ast_msg_set_to(msg, "xmpp:%s", cfg->user);
3153 res |= ast_msg_set_from(msg, "xmpp:%s", message->from);
3154 res |= ast_msg_set_body(msg, "%s", message->message);
3155 res |= ast_msg_set_context(msg, "%s", cfg->context);
3156 res |= ast_msg_set_tech(msg, "%s", "XMPP");
3157 res |= ast_msg_set_endpoint(msg, "%s", client->name);
3158
3159 if (buddy) {
3160 res |= ast_msg_set_var(msg, "XMPP_BUDDY", buddy->id);
3161 }
3162
3163 ao2_cleanup(buddy);
3164
3165 ast_xmpp_client_unlock(client);
3166
3167 if (res) {
3168 ast_msg_destroy(msg);
3169 } else {
3170 ast_msg_queue(msg);
3171 }
3172 }
3173 }
3174
3175 /* remove old messages received from this JID
3176 * and insert received message */
3177 deleted = delete_old_messages(client, pak->from->partial);
3178 ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
3179 AST_LIST_LOCK(&client->messages);
3180 AST_LIST_INSERT_HEAD(&client->messages, message, list);
3181 AST_LIST_UNLOCK(&client->messages);
3182
3183 /* wake up threads waiting for messages */
3187
3188 return 0;
3189}
3190
3191/*! \brief Helper function which sends a discovery information request to a user */
3192static int xmpp_client_send_disco_info_request(struct ast_xmpp_client *client, const char *to, const char *from)
3193{
3194 iks *iq, *query;
3195 int res;
3196
3197 if (!(iq = iks_new("iq")) || !(query = iks_new("query"))) {
3198 iks_delete(iq);
3199 return -1;
3200 }
3201
3202 iks_insert_attrib(iq, "type", "get");
3203 iks_insert_attrib(iq, "to", to);
3204 iks_insert_attrib(iq, "from", from);
3205 ast_xmpp_client_lock(client);
3206 iks_insert_attrib(iq, "id", client->mid);
3207 ast_xmpp_increment_mid(client->mid);
3208 ast_xmpp_client_unlock(client);
3209 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
3210 iks_insert_node(iq, query);
3211
3212 res = ast_xmpp_client_send(client, iq);
3213
3214 iks_delete(query);
3215 iks_delete(iq);
3216
3217 return res;
3218}
3219
3220/*! \brief Callback function which returns when the resource is available */
3221static int xmpp_resource_is_available(void *obj, void *arg, int flags)
3222{
3223 struct ast_xmpp_resource *resource = obj;
3224
3225 return (resource->status == IKS_SHOW_AVAILABLE) ? CMP_MATCH | CMP_STOP : 0;
3226}
3227
3228/*! \brief Helper function which sends a ping request to a server */
3229static int xmpp_ping_request(struct ast_xmpp_client *client, const char *to, const char *from)
3230{
3231 iks *iq, *ping;
3232 int res;
3233
3234 ast_debug(2, "JABBER: Sending Keep-Alive Ping for client '%s'\n", client->name);
3235
3236 if (!(iq = iks_new("iq")) || !(ping = iks_new("ping"))) {
3237 iks_delete(iq);
3238 return -1;
3239 }
3240
3241 iks_insert_attrib(iq, "type", "get");
3242 iks_insert_attrib(iq, "to", to);
3243 iks_insert_attrib(iq, "from", from);
3244
3245 ast_xmpp_client_lock(client);
3246 iks_insert_attrib(iq, "id", client->mid);
3247 ast_xmpp_increment_mid(client->mid);
3248 ast_xmpp_client_unlock(client);
3249
3250 iks_insert_attrib(ping, "xmlns", "urn:xmpp:ping");
3251 iks_insert_node(iq, ping);
3252
3253 res = ast_xmpp_client_send(client, iq);
3254
3255 iks_delete(ping);
3256 iks_delete(iq);
3257
3258
3259 return res;
3260}
3261
3262/*! \brief Internal function called when a presence message is received */
3263static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
3264{
3265 struct ast_xmpp_buddy *buddy;
3267 char *type = iks_find_attrib(pak->x, "type");
3268 int status = pak->show ? pak->show : STATUS_DISAPPEAR;
3270
3271 /* If this is a component presence probe request answer immediately with our presence status */
3272 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT) && !ast_strlen_zero(type) && !strcasecmp(type, "probe")) {
3273 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3274 }
3275
3276 /* If no resource is available this is a general buddy presence update, which we will ignore */
3277 if (!pak->from->resource) {
3278 return 0;
3279 }
3280
3281 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
3282 /* Only output the message if it is not about us */
3283 if (strcmp(client->jid->partial, pak->from->partial)) {
3284 ast_log(LOG_WARNING, "Received presence information about '%s' despite not having them in roster on client '%s'\n",
3285 pak->from->partial, client->name);
3286 }
3287 return 0;
3288 }
3289
3290 ao2_lock(buddy->resources);
3291
3292 if (!(resource = ao2_callback(buddy->resources, OBJ_NOLOCK, xmpp_resource_cmp, pak->from->resource))) {
3293 /* Only create the new resource if it is not going away - in reality this should not happen */
3294 if (status != STATUS_DISAPPEAR) {
3296 ast_log(LOG_ERROR, "Could not allocate resource object for resource '%s' of buddy '%s' on client '%s'\n",
3297 pak->from->resource, buddy->id, client->name);
3298 ao2_unlock(buddy->resources);
3299 ao2_ref(buddy, -1);
3300 return 0;
3301 }
3302
3303 ast_copy_string(resource->resource, pak->from->resource, sizeof(resource->resource));
3304 }
3305 } else {
3306 /* We unlink the resource in case the priority changes or in case they are going away */
3308 }
3309
3310 /* Only update the resource and add it back in if it is not going away */
3311 if (resource && (status != STATUS_DISAPPEAR)) {
3312 char *node, *ver;
3313
3314 /* Try to get the XMPP spec node, and fall back to Google if not found */
3315 if (!(node = iks_find_attrib(iks_find(pak->x, "c"), "node"))) {
3316 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
3317 }
3318
3319 if (!(ver = iks_find_attrib(iks_find(pak->x, "c"), "ver"))) {
3320 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
3321 }
3322
3323 if (resource->description) {
3324 ast_free(resource->description);
3325 }
3326
3327 if ((node && strcmp(resource->caps.node, node)) || (ver && strcmp(resource->caps.version, ver))) {
3328 /* For interoperability reasons, proceed even if the resource fails to provide node or version */
3329 if (node) {
3330 ast_copy_string(resource->caps.node, node, sizeof(resource->caps.node));
3331 }
3332 if (ver) {
3333 ast_copy_string(resource->caps.version, ver, sizeof(resource->caps.version));
3334 }
3335
3336 /* Google Talk places the capabilities information directly in presence, so see if it is there */
3337 if (iks_find_with_attrib(pak->x, "c", "node", "http://www.google.com/xmpp/client/caps") ||
3338 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.google.com/xmpp/client/caps") ||
3339 iks_find_with_attrib(pak->x, "c", "node", "http://www.android.com/gtalk/client/caps") ||
3340 iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.android.com/gtalk/client/caps") ||
3341 iks_find_with_attrib(pak->x, "c", "node", "http://mail.google.com/xmpp/client/caps") ||
3342 iks_find_with_attrib(pak->x, "caps:c", "node", "http://mail.google.com/xmpp/client/caps")) {
3343 resource->caps.google = 1;
3344 }
3345
3346 /* To discover if the buddy supports Jingle we need to query, so do so */
3347 if (xmpp_client_send_disco_info_request(client, pak->from->full, client->jid->full)) {
3348 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);
3349 }
3350 }
3351
3352 resource->status = status;
3353 resource->description = ast_strdup(iks_find_cdata(pak->x, "status"));
3354 resource->priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
3355
3357
3358 manager_event(EVENT_FLAG_USER, "JabberStatus",
3359 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
3360 "\r\nDescription: %s\r\n",
3361 client->name, pak->from->partial, resource->resource, resource->status,
3362 resource->priority, S_OR(resource->description, ""));
3363
3364 ao2_ref(resource, -1);
3365 } else {
3366 /* This will get hit by presence coming in for an unknown resource, and also when a resource goes away */
3367 if (resource) {
3368 ao2_ref(resource, -1);
3369 }
3370
3371 manager_event(EVENT_FLAG_USER, "JabberStatus",
3372 "Account: %s\r\nJID: %s\r\nStatus: %u\r\n",
3373 client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
3374 }
3375
3376 /* Determine if at least one resource is available for device state purposes */
3379 ao2_ref(resource, -1);
3380 }
3381
3382 ao2_unlock(buddy->resources);
3383
3384 ao2_ref(buddy, -1);
3385
3386 ast_devstate_changed(state, AST_DEVSTATE_CACHABLE, "XMPP/%s/%s", client->name, pak->from->partial);
3387
3388 return 0;
3389}
3390
3391/*! \brief Internal function called when a subscription message is received */
3392static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg,iks *node, ikspak *pak)
3393{
3394 struct ast_xmpp_buddy *buddy;
3395
3396 switch (pak->subtype) {
3397 case IKS_TYPE_SUBSCRIBE:
3399 iks *presence, *status = NULL;
3400
3401 if ((presence = iks_new("presence")) && (status = iks_new("status"))) {
3402 iks_insert_attrib(presence, "type", "subscribed");
3403 iks_insert_attrib(presence, "to", pak->from->full);
3404 iks_insert_attrib(presence, "from", client->jid->full);
3405
3406 if (pak->id) {
3407 iks_insert_attrib(presence, "id", pak->id);
3408 }
3409
3410 iks_insert_cdata(status, "Asterisk has approved your subscription", 0);
3411 iks_insert_node(presence, status);
3412
3413 if (ast_xmpp_client_send(client, presence)) {
3414 ast_log(LOG_ERROR, "Could not send subscription acceptance to '%s' from client '%s'\n",
3415 pak->from->partial, client->name);
3416 }
3417 } else {
3418 ast_log(LOG_ERROR, "Could not allocate presence stanzas for accepting subscription from '%s' to client '%s'\n",
3419 pak->from->partial, client->name);
3420 }
3421
3422 iks_delete(status);
3423 iks_delete(presence);
3424 }
3425
3426 if (ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
3427 xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
3428 }
3429 /* This purposely flows through so we have the subscriber amongst our buddies */
3430 case IKS_TYPE_SUBSCRIBED:
3431 ao2_lock(client->buddies);
3432
3433 if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY | OBJ_NOLOCK))) {
3434 buddy = xmpp_client_create_buddy(client->buddies, pak->from->partial);
3435 }
3436
3437 if (!buddy) {
3438 ast_log(LOG_WARNING, "Could not find or create buddy '%s' on client '%s'\n",
3439 pak->from->partial, client->name);
3440 } else {
3441 ao2_ref(buddy, -1);
3442 }
3443
3444 ao2_unlock(client->buddies);
3445
3446 break;
3447 default:
3448 break;
3449 }
3450
3451 return 0;
3452}
3453
3454/*! \brief Action hook for when things occur */
3455static int xmpp_action_hook(void *data, int type, iks *node)
3456{
3458 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3459 struct ast_xmpp_client *client = data;
3460 ikspak *pak;
3461 int i;
3462
3463 if (!node) {
3464 ast_log(LOG_ERROR, "xmpp_action_hook was called without a packet\n");
3465 return IKS_HOOK;
3466 }
3467
3468 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
3469 return IKS_HOOK;
3470 }
3471
3472 /* If the client is disconnecting ignore everything */
3473 if (client->state == XMPP_STATE_DISCONNECTING) {
3474 return IKS_HOOK;
3475 }
3476
3477 pak = iks_packet(node);
3478
3479 /* work around iksemel's impossibility to recognize node names
3480 * containing a colon. Set the namespace of the corresponding
3481 * node accordingly. */
3482 if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
3483 char *node_ns = NULL;
3484 char attr[XMPP_MAX_ATTRLEN];
3485 char *node_name = iks_name(iks_child(node));
3486 char *aux = strchr(node_name, ':') + 1;
3487 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
3488 node_ns = iks_find_attrib(iks_child(node), attr);
3489 if (node_ns) {
3490 pak->ns = node_ns;
3491 pak->query = iks_child(node);
3492 }
3493 }
3494
3495 /* Process through any state handlers */
3496 for (i = 0; i < ARRAY_LEN(xmpp_state_handlers); i++) {
3497 if ((xmpp_state_handlers[i].state == client->state) && (xmpp_state_handlers[i].component == (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? 1 : 0))) {
3498 if (xmpp_state_handlers[i].handler(client, clientcfg, type, node)) {
3499 /* If the handler wants us to stop now, do so */
3500 return IKS_HOOK;
3501 }
3502 break;
3503 }
3504 }
3505
3506 /* Process through any PAK handlers */
3507 for (i = 0; i < ARRAY_LEN(xmpp_pak_handlers); i++) {
3508 if (xmpp_pak_handlers[i].type == pak->type) {
3509 if (xmpp_pak_handlers[i].handler(client, clientcfg, node, pak)) {
3510 /* If the handler wants us to stop now, do so */
3511 return IKS_HOOK;
3512 }
3513 break;
3514 }
3515 }
3516
3517 /* Send the packet through the filter in case any filters want to process it */
3518 iks_filter_packet(client->filter, pak);
3519
3520 iks_delete(node);
3521
3522 return IKS_OK;
3523}
3524
3526{
3527 if ((client->thread != AST_PTHREADT_NULL) && !pthread_equal(pthread_self(), client->thread)) {
3529 pthread_cancel(client->thread);
3530 pthread_join(client->thread, NULL);
3531 client->thread = AST_PTHREADT_NULL;
3532 }
3533
3534 if (client->mwi_sub) {
3535 client->mwi_sub = stasis_unsubscribe_and_join(client->mwi_sub);
3536 xmpp_pubsub_unsubscribe(client, "message_waiting");
3537 }
3538
3539 if (client->device_state_sub) {
3541 xmpp_pubsub_unsubscribe(client, "device_state");
3542 }
3543
3544#ifdef HAVE_OPENSSL
3545 if (client->stream_flags & SECURE) {
3546 SSL_shutdown(client->ssl_session);
3547 SSL_CTX_free(client->ssl_context);
3548 SSL_free(client->ssl_session);
3549 }
3550
3551 client->stream_flags = 0;
3552#endif
3553
3554 if (client->parser) {
3555 iks_disconnect(client->parser);
3556 }
3557
3559
3560 return 0;
3561}
3562
3563/*! \brief Internal function used to reconnect an XMPP client to its server */
3564static int xmpp_client_reconnect(struct ast_xmpp_client *client)
3565{
3566 struct timeval tv = { .tv_sec = 5, .tv_usec = 0 };
3568 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3569 int res = IKS_NET_NOCONN;
3570
3571 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
3572 return -1;
3573 }
3574
3576
3577 client->timeout = 50;
3578 iks_parser_reset(client->parser);
3579
3580 if (!client->filter && !(client->filter = iks_filter_new())) {
3581 ast_log(LOG_ERROR, "Could not create IKS filter for client connection '%s'\n", client->name);
3582 return -1;
3583 }
3584
3585 if (!ast_strlen_zero(clientcfg->refresh_token)) {
3586 ast_debug(2, "Obtaining OAuth access token for client '%s'\n", client->name);
3587 if (fetch_access_token(clientcfg)) {
3588 return -1;
3589 }
3590 }
3591
3592 /* If it's a component connect to user otherwise connect to server */
3593 res = iks_connect_via(client->parser, S_OR(clientcfg->server, client->jid->server), clientcfg->port,
3594 ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? clientcfg->user : client->jid->server);
3595
3596 /* Set socket timeout options */
3597 setsockopt(iks_fd(client->parser), SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval));
3598
3599 if (res == IKS_NET_NOCONN) {
3600 ast_log(LOG_ERROR, "No XMPP connection available when trying to connect client '%s'\n", client->name);
3601 return -1;
3602 } else if (res == IKS_NET_NODNS) {
3603 ast_log(LOG_ERROR, "No DNS available for XMPP connection when trying to connect client '%s'\n", client->name);
3604 return -1;
3605 }
3606
3607 /* Depending on the configuration of the client we eiher jump to requesting TLS, or authenticating */
3609
3610 return 0;
3611}
3612
3613/*! \brief Internal function which polls on an XMPP client and receives data */
3614static int xmpp_io_recv(struct ast_xmpp_client *client, char *buffer, size_t buf_len, int timeout)
3615{
3616 struct pollfd pfd = { .events = POLLIN };
3617 int len, res;
3618
3619#ifdef HAVE_OPENSSL
3620 if (xmpp_is_secure(client)) {
3621 pfd.fd = SSL_get_fd(client->ssl_session);
3622 if (pfd.fd < 0) {
3623 return -1;
3624 }
3625 } else
3626#endif /* HAVE_OPENSSL */
3627 pfd.fd = iks_fd(client->parser);
3628
3629 res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
3630 if (res > 0) {
3631#ifdef HAVE_OPENSSL
3632 if (xmpp_is_secure(client)) {
3633 len = SSL_read(client->ssl_session, buffer, buf_len);
3634 } else
3635#endif /* HAVE_OPENSSL */
3636 len = recv(pfd.fd, buffer, buf_len, 0);
3637
3638 if (len > 0) {
3639 return len;
3640 } else if (len <= 0) {
3641 return -1;
3642 }
3643 }
3644 return res;
3645}
3646
3647/*! \brief Internal function which receives data from the XMPP client connection */
3648static int xmpp_client_receive(struct ast_xmpp_client *client, unsigned int timeout)
3649{
3650 int len, ret, pos = 0, newbufpos = 0;
3651 char buf[NET_IO_BUF_SIZE - 1] = "";
3652 char newbuf[NET_IO_BUF_SIZE - 1] = "";
3653 unsigned char c;
3654
3655 while (1) {
3656 len = xmpp_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
3657 if (len < 0) return IKS_NET_RWERR;
3658 if (len == 0) return IKS_NET_EXPIRED;
3659 buf[len] = '\0';
3660
3661 /* our iksemel parser won't work as expected if we feed
3662 it with XML packets that contain multiple whitespace
3663 characters between tags */
3664 while (pos < len) {
3665 c = buf[pos];
3666 /* if we stumble on the ending tag character,
3667 we skip any whitespace that follows it*/
3668 if (c == '>') {
3669 while (isspace(buf[pos+1])) {
3670 pos++;
3671 }
3672 }
3673 newbuf[newbufpos] = c;
3674 newbufpos++;
3675 pos++;
3676 }
3677 pos = 0;
3678 newbufpos = 0;
3679
3680 /* Log the message here, because iksemel's logHook is
3681 unaccessible */
3682 xmpp_log_hook(client, buf, len, 1);
3683
3684 if(buf[0] == ' ') {
3685 ast_debug(1, "JABBER: Detected Google Keep Alive. "
3686 "Sending out Ping request for client '%s'\n", client->name);
3687 /* If we just send out the ping here then we will have socket
3688 * read errors because the socket will timeout */
3689 xmpp_ping_request(client, client->jid->server, client->jid->full);
3690 }
3691
3692 /* let iksemel deal with the string length,
3693 and reset our buffer */
3694 ret = iks_parse(client->parser, newbuf, 0, 0);
3695 memset(newbuf, 0, sizeof(newbuf));
3696
3697 switch (ret) {
3698 case IKS_NOMEM:
3699 ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
3700 break;
3701 case IKS_BADXML:
3702 ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
3703 break;
3704 case IKS_HOOK:
3705 ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
3706 break;
3707 }
3708 if (ret != IKS_OK) {
3709 return ret;
3710 }
3711 ast_debug(3, "XML parsing successful\n");
3712 }
3713 return IKS_OK;
3714}
3715
3716static void sleep_with_backoff(unsigned int *sleep_time)
3717{
3718 /* We're OK with our thread dying here */
3719 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
3720
3721 sleep(*sleep_time);
3722 *sleep_time = MIN(60, *sleep_time * 2);
3723
3724 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
3725}
3726
3727/*! \brief XMPP client connection thread */
3728static void *xmpp_client_thread(void *data)
3729{
3730 struct ast_xmpp_client *client = data;
3731 int res = IKS_NET_RWERR;
3732 unsigned int sleep_time = 1;
3733
3734 /* We only allow cancellation while sleeping */
3735 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
3736
3737 do {
3738 if (client->state == XMPP_STATE_DISCONNECTING) {
3739 ast_debug(1, "[%s] Disconnecting\n", client->name);
3740 break;
3741 }
3742
3743 if (res == IKS_NET_RWERR || client->timeout == 0) {
3744 ast_debug(3, "[%s] Connecting\n", client->name);
3745 if ((res = xmpp_client_reconnect(client)) != IKS_OK) {
3746 sleep_with_backoff(&sleep_time);
3747 res = IKS_NET_RWERR;
3748 }
3749 continue;
3750 }
3751
3752 res = xmpp_client_receive(client, 1);
3753
3754 /* Decrease timeout if no data received, and delete
3755 * old messages globally */
3756 if (res == IKS_NET_EXPIRED) {
3757 client->timeout--;
3758 }
3759
3760 if (res == IKS_HOOK) {
3761 ast_debug(2, "[%s] Got hook event\n", client->name);
3762 } else if (res == IKS_NET_TLSFAIL) {
3763 ast_log(LOG_ERROR, "[%s] TLS failure\n", client->name);
3764 } else if (!client->timeout && client->state == XMPP_STATE_CONNECTED) {
3766 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3767
3768 if (cfg && cfg->clients) {
3769 clientcfg = xmpp_config_find(cfg->clients, client->name);
3770 }
3771
3772 if (clientcfg && ast_test_flag(&clientcfg->flags, XMPP_KEEPALIVE)) {
3773 res = xmpp_ping_request(client, client->jid->server, client->jid->full);
3774 } else {
3775 res = IKS_OK;
3776 }
3777
3778 if (res == IKS_OK) {
3779 client->timeout = 50;
3780 } else {
3781 ast_log(LOG_WARNING, "[%s] Network timeout\n", client->name);
3782 }
3783 } else if (res == IKS_NET_RWERR) {
3784 ast_log(LOG_WARNING, "[%s] Socket read error\n", client->name);
3786 sleep_with_backoff(&sleep_time);
3787 } else if (res == IKS_NET_NOSOCK) {
3788 ast_log(LOG_WARNING, "[%s] No socket\n", client->name);
3789 } else if (res == IKS_NET_NOCONN) {
3790 ast_log(LOG_WARNING, "[%s] No connection\n", client->name);
3791 } else if (res == IKS_NET_NODNS) {
3792 ast_log(LOG_WARNING, "[%s] No DNS\n", client->name);
3793 } else if (res == IKS_NET_NOTSUPP) {
3794 ast_log(LOG_WARNING, "[%s] Not supported\n", client->name);
3795 } else if (res == IKS_NET_DROPPED) {
3796 ast_log(LOG_WARNING, "[%s] Dropped?\n", client->name);
3797 } else if (res == IKS_NET_UNKNOWN) {
3798 ast_debug(5, "[%s] Unknown\n", client->name);
3799 } else if (res == IKS_OK) {
3800 sleep_time = 1;
3801 }
3802
3803 } while (1);
3804
3805 return NULL;
3806}
3807
3808static int xmpp_client_config_merge_buddies(void *obj, void *arg, int flags)
3809{
3810 struct ast_xmpp_buddy *buddy1 = obj, *buddy2;
3811 struct ao2_container *buddies = arg;
3812
3813 /* If the buddy does not already exist link it into the client buddies container */
3814 if (!(buddy2 = ao2_find(buddies, buddy1->id, OBJ_KEY))) {
3815 ao2_link(buddies, buddy1);
3816 } else {
3817 ao2_ref(buddy2, -1);
3818 }
3819
3820 /* All buddies are unlinked from the configuration buddies container, always */
3821 return 1;
3822}
3823
3825{
3826 RAII_VAR(char *, cmd, NULL, ast_free);
3827 char cBuf[1024] = "";
3828 const char *url = "https://www.googleapis.com/oauth2/v3/token";
3829 struct ast_json_error error;
3830 RAII_VAR(struct ast_json *, jobj, NULL, ast_json_unref);
3831
3832 if (ast_asprintf(&cmd,
3833 "CURL(%s,client_id=%s&client_secret=%s&refresh_token=%s&grant_type=refresh_token)",
3834 url, cfg->oauth_clientid, cfg->oauth_secret, cfg->refresh_token) < 0) {
3835 return -1;
3836 }
3837
3838 ast_debug(2, "Performing OAuth 2.0 authentication for client '%s' using command: %s\n",
3839 cfg->name, cmd);
3840
3841 if (ast_func_read(NULL, cmd, cBuf, sizeof(cBuf) - 1)) {
3842 ast_log(LOG_ERROR, "CURL is unavailable. This is required for OAuth 2.0 authentication of XMPP client '%s'. Please ensure it is loaded.\n",
3843 cfg->name);
3844 return -1;
3845 }
3846
3847 ast_debug(2, "OAuth 2.0 authentication for client '%s' returned: %s\n", cfg->name, cBuf);
3848
3849 jobj = ast_json_load_string(cBuf, &error);
3850 if (jobj) {
3851 const char *token = ast_json_string_get(ast_json_object_get(jobj, "access_token"));
3852 if (token) {
3853 ast_string_field_set(cfg, password, token);
3854 return 0;
3855 }
3856 }
3857
3858 ast_log(LOG_ERROR, "An error occurred while performing OAuth 2.0 authentication for client '%s': %s\n", cfg->name, cBuf);
3859
3860 return -1;
3861}
3862
3863static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)
3864{
3865 struct ast_xmpp_client_config *cfg = obj;
3867
3868 /* Merge global options that have not been modified */
3869 ast_copy_flags(&cfg->flags, &gcfg->global->general, ~(cfg->mod_flags.flags) & (XMPP_AUTOPRUNE | XMPP_AUTOREGISTER | XMPP_AUTOACCEPT));
3870
3871 /* Merge buddies as need be */
3873
3874 if (cfg->client->reconnect) {
3875 /* Disconnect the existing session since our role is changing, or we are starting up */
3877
3878 if (!(cfg->client->parser = iks_stream_new(ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client", cfg->client,
3879 xmpp_action_hook))) {
3880 ast_log(LOG_ERROR, "Iksemel stream could not be created for client '%s' - client not active\n", cfg->name);
3881 return -1;
3882 }
3883
3884 iks_set_log_hook(cfg->client->parser, xmpp_log_hook);
3885
3886 /* Create a JID based on the given user, if no resource is given use the default */
3887 if (!strchr(cfg->user, '/') && !ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
3888 char resource[strlen(cfg->user) + strlen("/asterisk-xmpp") + 1];
3889
3890 snprintf(resource, sizeof(resource), "%s/asterisk-xmpp", cfg->user);
3891 cfg->client->jid = iks_id_new(cfg->client->stack, resource);
3892 } else {
3893 cfg->client->jid = iks_id_new(cfg->client->stack, cfg->user);
3894 }
3895
3896 if (!cfg->client->jid || (ast_strlen_zero(cfg->client->jid->user) && !ast_test_flag(&cfg->flags, XMPP_COMPONENT))) {
3897 ast_log(LOG_ERROR, "Jabber identity '%s' could not be created for client '%s' - client not active\n", cfg->user, cfg->name);
3898 return -1;
3899 }
3900
3902
3903 cfg->client->reconnect = 0;
3904 } else if (cfg->client->state == XMPP_STATE_CONNECTED) {
3905 /* If this client is connected update their presence status since it may have changed */
3906 xmpp_client_set_presence(cfg->client, NULL, cfg->client->jid->full, cfg->status, cfg->statusmsg);
3907
3908 /* Subscribe to the status of any newly added buddies */
3911 }
3912 }
3913
3914 return 0;
3915}
3916
3917/*!
3918 * \internal
3919 * \brief Send a Jabber Message via call from the Manager
3920 * \param s mansession Manager session
3921 * \param m message Message to send
3922 * \retval 0
3923 */
3924static int manager_jabber_send(struct mansession *s, const struct message *m)
3925{
3927 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
3928 const char *id = astman_get_header(m, "ActionID");
3929 const char *jabber = astman_get_header(m, "Jabber");
3930 const char *screenname = astman_get_header(m, "ScreenName");
3931 const char *message = astman_get_header(m, "Message");
3932
3933 if (ast_strlen_zero(jabber)) {
3934 astman_send_error(s, m, "No transport specified");
3935 return 0;
3936 }
3937 if (ast_strlen_zero(screenname)) {
3938 astman_send_error(s, m, "No ScreenName specified");
3939 return 0;
3940 }
3941 if (ast_strlen_zero(message)) {
3942 astman_send_error(s, m, "No Message specified");
3943 return 0;
3944 }
3945
3946 astman_send_ack(s, m, "Attempting to send Jabber Message");
3947
3948 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, jabber))) {
3949 astman_send_error(s, m, "Could not find Sender");
3950 return 0;
3951 }
3952
3953 if (strchr(screenname, '@') && !ast_xmpp_client_send_message(clientcfg->client, screenname, message)) {
3954 astman_append(s, "Response: Success\r\n");
3955 } else {
3956 astman_append(s, "Response: Error\r\n");
3957 }
3958
3959 if (!ast_strlen_zero(id)) {
3960 astman_append(s, "ActionID: %s\r\n", id);
3961 }
3962
3963 astman_append(s, "\r\n");
3964
3965 return 0;
3966}
3967
3968/*!
3969 * \brief Build the a node request
3970 * \param client the configured XMPP client we use to connect to a XMPP server
3971 * \param collection name of the collection for request
3972 * \return iks *
3973 */
3974static iks* xmpp_pubsub_build_node_request(struct ast_xmpp_client *client, const char *collection)
3975{
3976 iks *request = xmpp_pubsub_iq_create(client, "get"), *query;
3977
3978 if (!request) {
3979 return NULL;
3980 }
3981
3982 query = iks_insert(request, "query");
3983 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
3984
3985 if (collection) {
3986 iks_insert_attrib(query, "node", collection);
3987 }
3988
3989 return request;
3990}
3991
3992/*!
3993 * \brief Receive pubsub item lists
3994 * \param data pointer to ast_xmpp_client structure
3995 * \param pak response from pubsub diso#items query
3996 * \retval IKS_FILTER_EAT
3997 */
3998static int xmpp_pubsub_receive_node_list(void *data, ikspak* pak)
3999{
4000 struct ast_xmpp_client *client = data;
4001 iks *item = NULL;
4002
4003 if (iks_has_children(pak->query)) {
4004 item = iks_first_tag(pak->query);
4005 ast_verbose("Connection %s: %s\nNode name: %s\n", client->name, client->jid->partial,
4006 iks_find_attrib(item, "node"));
4007 while ((item = iks_next_tag(item))) {
4008 ast_verbose("Node name: %s\n", iks_find_attrib(item, "node"));
4009 }
4010 }
4011
4012 if (item) {
4013 iks_delete(item);
4014 }
4015
4016
4017 return IKS_FILTER_EAT;
4018}
4019
4020/*!
4021* \brief Request item list from pubsub
4022* \param client the configured XMPP client we use to connect to a XMPP server
4023* \param collection name of the collection for request
4024*/
4025static void xmpp_pubsub_request_nodes(struct ast_xmpp_client *client, const char *collection)
4026{
4027 iks *request = xmpp_pubsub_build_node_request(client, collection);
4028
4029 if (!request) {
4030 ast_log(LOG_ERROR, "Could not request pubsub nodes on client '%s' - IQ could not be created\n", client->name);
4031 return;
4032 }
4033
4034 iks_filter_add_rule(client->filter, xmpp_pubsub_receive_node_list, client, IKS_RULE_TYPE,
4035 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
4036 IKS_RULE_DONE);
4038 iks_delete(request);
4039
4040}
4041
4042/*!
4043 * \brief Method to expose PubSub node list via CLI.
4044 * \param e pointer to ast_cli_entry structure
4045 * \param cmd
4046 * \param a pointer to ast_cli_args structure
4047 * \return char *
4048 */
4049static char *xmpp_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
4050 ast_cli_args *a)
4051{
4053 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
4054 const char *name = NULL, *collection = NULL;
4055
4056 switch (cmd) {
4057 case CLI_INIT:
4058 e->command = "xmpp list nodes";
4059 e->usage =
4060 "Usage: xmpp list nodes <connection> [collection]\n"
4061 " Lists the user's nodes on the respective connection\n"
4062 " ([connection] as configured in xmpp.conf.)\n";
4063 return NULL;
4064 case CLI_GENERATE:
4065 return NULL;
4066 }
4067
4068 if (a->argc > 5 || a->argc < 4) {
4069 return CLI_SHOWUSAGE;
4070 } else if (a->argc == 4 || a->argc == 5) {
4071 name = a->argv[3];
4072 }
4073
4074 if (a->argc == 5) {
4075 collection = a->argv[4];
4076 }
4077
4078 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
4079 ast_cli(a->fd, "Unable to find client '%s'!\n", name);
4080 return CLI_FAILURE;
4081 }
4082
4083 ast_cli(a->fd, "Listing pubsub nodes.\n");
4084
4085 xmpp_pubsub_request_nodes(clientcfg->client, collection);
4086
4087 return CLI_SUCCESS;
4088}
4089
4090/*!
4091 * \brief Delete pubsub item lists
4092 * \param data pointer to ast_xmpp_client structure
4093 * \param pak response from pubsub diso#items query
4094 * \retval IKS_FILTER_EAT
4095 */
4096static int xmpp_pubsub_delete_node_list(void *data, ikspak* pak)
4097{
4098 struct ast_xmpp_client *client = data;
4099 iks *item = NULL;
4100
4101 if (iks_has_children(pak->query)) {
4102 item = iks_first_tag(pak->query);
4103 ast_log(LOG_WARNING, "Connection: %s Node name: %s\n", client->jid->partial,
4104 iks_find_attrib(item, "node"));
4105 while ((item = iks_next_tag(item))) {
4106 xmpp_pubsub_delete_node(client, iks_find_attrib(item, "node"));
4107 }
4108 }
4109
4110 if (item) {
4111 iks_delete(item);
4112 }
4113
4114 return IKS_FILTER_EAT;
4115}
4116
4117static void xmpp_pubsub_purge_nodes(struct ast_xmpp_client *client, const char* collection_name)
4118{
4119 iks *request = xmpp_pubsub_build_node_request(client, collection_name);
4121 iks_filter_add_rule(client->filter, xmpp_pubsub_delete_node_list, client, IKS_RULE_TYPE,
4122 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
4123 IKS_RULE_DONE);
4125 iks_delete(request);
4126}
4127
4128/*!
4129 * \brief Method to purge PubSub nodes via CLI.
4130 * \param e pointer to ast_cli_entry structure
4131 * \param cmd
4132 * \param a pointer to ast_cli_args structure
4133 * \return char *
4134 */
4135static char *xmpp_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
4136 ast_cli_args *a)
4137{
4139 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
4140 const char *name;
4141
4142 switch (cmd) {
4143 case CLI_INIT:
4144 e->command = "xmpp purge nodes";
4145 e->usage =
4146 "Usage: xmpp purge nodes <connection> <node>\n"
4147 " Purges nodes on PubSub server\n"
4148 " as configured in xmpp.conf.\n";
4149 return NULL;
4150 case CLI_GENERATE:
4151 return NULL;
4152 }
4153
4154 if (a->argc != 5) {
4155 return CLI_SHOWUSAGE;
4156 }
4157 name = a->argv[3];
4158
4159 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
4160 ast_cli(a->fd, "Unable to find client '%s'!\n", name);
4161 return CLI_FAILURE;
4162 }
4163
4164 if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
4165 xmpp_pubsub_purge_nodes(clientcfg->client, a->argv[4]);
4166 } else {
4167 xmpp_pubsub_delete_node(clientcfg->client, a->argv[4]);
4168 }
4169
4170 return CLI_SUCCESS;
4171}
4172
4173/*!
4174 * \brief Method to expose PubSub node deletion via CLI.
4175 * \param e pointer to ast_cli_entry structure
4176 * \param cmd
4177 * \param a pointer to ast_cli_args structure
4178 * \return char *
4179 */
4180static char *xmpp_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
4181 ast_cli_args *a)
4182{
4184 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
4185 const char *name;
4186
4187 switch (cmd) {
4188 case CLI_INIT:
4189 e->command = "xmpp delete node";
4190 e->usage =
4191 "Usage: xmpp delete node <connection> <node>\n"
4192 " Deletes a node on PubSub server\n"
4193 " as configured in xmpp.conf.\n";
4194 return NULL;
4195 case CLI_GENERATE:
4196 return NULL;
4197 }
4198
4199 if (a->argc != 5) {
4200 return CLI_SHOWUSAGE;
4201 }
4202 name = a->argv[3];
4203
4204 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
4205 ast_cli(a->fd, "Unable to find client '%s'!\n", name);
4206 return CLI_FAILURE;
4207 }
4208
4209 xmpp_pubsub_delete_node(clientcfg->client, a->argv[4]);
4210
4211 return CLI_SUCCESS;
4212}
4213
4214/*!
4215 * \brief Method to expose PubSub collection node creation via CLI.
4216 * \return char *
4217 */
4218static char *xmpp_cli_create_collection(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4219{
4221 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
4222 const char *name, *collection_name;
4223
4224 switch (cmd) {
4225 case CLI_INIT:
4226 e->command = "xmpp create collection";
4227 e->usage =
4228 "Usage: xmpp create collection <connection> <collection>\n"
4229 " Creates a PubSub collection node using the account\n"
4230 " as configured in xmpp.conf.\n";
4231 return NULL;
4232 case CLI_GENERATE:
4233 return NULL;
4234 }
4235
4236 if (a->argc != 5) {
4237 return CLI_SHOWUSAGE;
4238 }
4239 name = a->argv[3];
4240 collection_name = a->argv[4];
4241
4242 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
4243 ast_cli(a->fd, "Unable to find client '%s'!\n", name);
4244 return CLI_FAILURE;
4245 }
4246
4247 ast_cli(a->fd, "Creating test PubSub node collection.\n");
4248
4249 xmpp_pubsub_create_collection(clientcfg->client, collection_name);
4250
4251 return CLI_SUCCESS;
4252}
4253
4254/*!
4255 * \brief Method to expose PubSub leaf node creation via CLI.
4256 * \return char *
4257 */
4258static char *xmpp_cli_create_leafnode(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4259{
4261 RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
4262 const char *name, *collection_name, *leaf_name;
4263
4264 switch (cmd) {
4265 case CLI_INIT:
4266 e->command = "xmpp create leaf";
4267 e->usage =
4268 "Usage: xmpp create leaf <connection> <collection> <leaf>\n"
4269 " Creates a PubSub leaf node using the account\n"
4270 " as configured in xmpp.conf.\n";
4271 return NULL;
4272 case CLI_GENERATE:
4273 return NULL;
4274 }
4275
4276 if (a->argc != 6) {
4277 return CLI_SHOWUSAGE;
4278 }
4279 name = a->argv[3];
4280 collection_name = a->argv[4];
4281 leaf_name = a->argv[5];
4282
4283 if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
4284 ast_cli(a->fd, "Unable to find client '%s'!\n", name);
4285 return CLI_FAILURE;
4286 }
4287
4288 ast_cli(a->fd, "Creating test PubSub node collection.\n");
4289
4290 xmpp_pubsub_create_leaf(clientcfg->client, collection_name, leaf_name);
4291
4292 return CLI_SUCCESS;
4293}
4294
4295/*!
4296 * \internal
4297 * \brief Turn on/off console debugging.
4298 * \retval CLI_SUCCESS
4299 */
4300static char *xmpp_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4301{
4302 switch (cmd) {
4303 case CLI_INIT:
4304 e->command = "xmpp set debug {on|off}";
4305 e->usage =
4306 "Usage: xmpp set debug {on|off}\n"
4307 " Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
4308 return NULL;
4309 case CLI_GENERATE:
4310 return NULL;
4311 }
4312
4313 if (a->argc != e->args) {
4314 return CLI_SHOWUSAGE;
4315 }
4316
4317 if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
4318 debug = 1;
4319 ast_cli(a->fd, "XMPP Debugging Enabled.\n");
4320 return CLI_SUCCESS;
4321 } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
4322 debug = 0;
4323 ast_cli(a->fd, "XMPP Debugging Disabled.\n");
4324 return CLI_SUCCESS;
4325 }
4326 return CLI_SHOWUSAGE; /* defaults to invalid */
4327}
4328
4329/*!
4330 * \internal
4331 * \brief Show client status.
4332 * \retval CLI_SUCCESS
4333 */
4334static char *xmpp_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4335{
4337 struct ao2_iterator i;
4338 struct ast_xmpp_client_config *clientcfg;
4339
4340 switch (cmd) {
4341 case CLI_INIT:
4342 e->command = "xmpp show connections";
4343 e->usage =
4344 "Usage: xmpp show connections\n"
4345 " Shows state of client and component connections\n";
4346 return NULL;
4347 case CLI_GENERATE:
4348 return NULL;
4349 }
4350
4351 if (!cfg || !cfg->clients) {
4352 return NULL;
4353 }
4354
4355 ast_cli(a->fd, "Jabber Users and their status:\n");
4356
4357 i = ao2_iterator_init(cfg->clients, 0);
4358 while ((clientcfg = ao2_iterator_next(&i))) {
4359 char *state;
4360
4361 switch (clientcfg->client->state) {
4363 state = "Disconnecting";
4364 break;
4366 state = "Disconnected";
4367 break;
4369 state = "Connecting";
4370 break;
4372 state = "Waiting to request TLS";
4373 break;
4375 state = "Requested TLS";
4376 break;
4378 state = "Waiting to authenticate";
4379 break;
4381 state = "Authenticating";
4382 break;
4383 case XMPP_STATE_ROSTER:
4384 state = "Retrieving roster";
4385 break;
4387 state = "Connected";
4388 break;
4389 default:
4390 state = "Unknown";
4391 }
4392
4393 ast_cli(a->fd, " [%s] %s - %s\n", clientcfg->name, clientcfg->user, state);
4394
4395 ao2_ref(clientcfg, -1);
4396 }
4398
4399 ast_cli(a->fd, "----\n");
4400 ast_cli(a->fd, " Number of clients: %d\n", ao2_container_count(cfg->clients));
4401
4402 return CLI_SUCCESS;
4403}
4404
4405/*!
4406 * \internal
4407 * \brief Show buddy lists
4408 * \retval CLI_SUCCESS
4409 */
4410static char *xmpp_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4411{
4413 struct ao2_iterator i;
4414 struct ast_xmpp_client_config *clientcfg;
4415
4416 switch (cmd) {
4417 case CLI_INIT:
4418 e->command = "xmpp show buddies";
4419 e->usage =
4420 "Usage: xmpp show buddies\n"
4421 " Shows buddy lists of our clients\n";
4422 return NULL;
4423 case CLI_GENERATE:
4424 return NULL;
4425 }
4426
4427 if (!cfg || !cfg->clients) {
4428 return NULL;
4429 }
4430
4431 ast_cli(a->fd, "XMPP buddy lists\n");
4432
4433 i = ao2_iterator_init(cfg->clients, 0);
4434 while ((clientcfg = ao2_iterator_next(&i))) {
4435 struct ao2_iterator bud;
4436 struct ast_xmpp_buddy *buddy;
4437
4438 ast_cli(a->fd, "Client: %s\n", clientcfg->name);
4439
4440 bud = ao2_iterator_init(clientcfg->client->buddies, 0);
4441 while ((buddy = ao2_iterator_next(&bud))) {
4442 struct ao2_iterator res;
4444
4445 ast_cli(a->fd, "\tBuddy:\t%s\n", buddy->id);
4446
4447 res = ao2_iterator_init(buddy->resources, 0);
4448 while ((resource = ao2_iterator_next(&res))) {
4449 ast_cli(a->fd, "\t\tResource: %s\n", resource->resource);
4450 ast_cli(a->fd, "\t\t\tnode: %s\n", resource->caps.node);
4451 ast_cli(a->fd, "\t\t\tversion: %s\n", resource->caps.version);
4452 ast_cli(a->fd, "\t\t\tGoogle Talk capable: %s\n", resource->caps.google ? "yes" : "no");
4453 ast_cli(a->fd, "\t\t\tJingle capable: %s\n", resource->caps.jingle ? "yes" : "no");
4454
4455 ao2_ref(resource, -1);
4456 }
4458
4459 ao2_ref(buddy, -1);
4460 }
4462
4463 ao2_ref(clientcfg, -1);
4464 }
4466
4467 return CLI_SUCCESS;
4468}
4469
4470static struct ast_cli_entry xmpp_cli[] = {
4471 AST_CLI_DEFINE(xmpp_do_set_debug, "Enable/Disable Jabber debug"),
4472 AST_CLI_DEFINE(xmpp_show_clients, "Show state of clients and components"),
4473 AST_CLI_DEFINE(xmpp_show_buddies, "Show buddy lists of our clients"),
4474 AST_CLI_DEFINE(xmpp_cli_create_collection, "Creates a PubSub node collection."),
4475 AST_CLI_DEFINE(xmpp_cli_list_pubsub_nodes, "Lists PubSub nodes"),
4476 AST_CLI_DEFINE(xmpp_cli_create_leafnode, "Creates a PubSub leaf node"),
4477 AST_CLI_DEFINE(xmpp_cli_delete_pubsub_node, "Deletes a PubSub node"),
4478 AST_CLI_DEFINE(xmpp_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
4479};
4480
4481static int unload_module(void)
4482{
4490 ast_manager_unregister("JabberSend");