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