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