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