Wed Jan 8 2020 09:49:50

Asterisk developer's documentation


res_jabber.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Matt O'Gorman <mogorman@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  * \brief A resource for interfacing Asterisk directly as a client
21  * or a component to a XMPP/Jabber compliant server.
22  *
23  * References:
24  * - http://www.xmpp.org - The XMPP standards foundation
25  *
26  * \extref Iksemel http://code.google.com/p/iksemel/
27  *
28  * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
29  * \todo Dialplan applications need RETURN variable, like JABBERSENDSTATUS
30  *
31  */
32 
33 /*** MODULEINFO
34  <depend>iksemel</depend>
35  <use>openssl</use>
36  <support_level>extended</support_level>
37  ***/
38 
39 #include "asterisk.h"
40 
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 425985 $")
42 
43 #include <ctype.h>
44 #include <iksemel.h>
45 
46 #include "asterisk/channel.h"
47 #include "asterisk/jabber.h"
48 #include "asterisk/file.h"
49 #include "asterisk/config.h"
50 #include "asterisk/callerid.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/app.h"
54 #include "asterisk/pbx.h"
55 #include "asterisk/md5.h"
56 #include "asterisk/acl.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/module.h"
59 #include "asterisk/astobj.h"
60 #include "asterisk/astdb.h"
61 #include "asterisk/manager.h"
62 #include "asterisk/event.h"
63 #include "asterisk/devicestate.h"
64 
65 /*** DOCUMENTATION
66  <application name="JabberSend" language="en_US">
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  jabber.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 jabber.conf.</para>
90  </description>
91  <see-also>
92  <ref type="function">JABBER_STATUS</ref>
93  <ref type="function">JABBER_RECEIVE</ref>
94  </see-also>
95  </application>
96  <function name="JABBER_RECEIVE" language="en_US">
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  jabber.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 jabber.conf.</para>
119  </description>
120  <see-also>
121  <ref type="function">JABBER_STATUS</ref>
122  <ref type="application">JabberSend</ref>
123  </see-also>
124  </function>
125  <function name="JABBER_STATUS" language="en_US">
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  jabber.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>.
142  If the buddy does not exist in the buddylist, returns 7.</para>
143  <para>Status will be 1-7.</para>
144  <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
145  <para>If not in roster variable will be set to 7.</para>
146  <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
147  <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
148  the associated XMPP account configured in jabber.conf.</para>
149  </description>
150  <see-also>
151  <ref type="function">JABBER_RECEIVE</ref>
152  <ref type="application">JabberSend</ref>
153  </see-also>
154  </function>
155  <application name="JabberSendGroup" language="en_US">
156  <synopsis>
157  Send a Jabber Message to a specified chat room
158  </synopsis>
159  <syntax>
160  <parameter name="Jabber" required="true">
161  <para>Client or transport Asterisk uses to connect to Jabber.</para>
162  </parameter>
163  <parameter name="RoomJID" required="true">
164  <para>XMPP/Jabber JID (Name) of chat room.</para>
165  </parameter>
166  <parameter name="Message" required="true">
167  <para>Message to be sent to the chat room.</para>
168  </parameter>
169  <parameter name="Nickname" required="false">
170  <para>The nickname Asterisk uses in the chat room.</para>
171  </parameter>
172  </syntax>
173  <description>
174  <para>Allows user to send a message to a chat room via XMPP.</para>
175  <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>
176  </description>
177  </application>
178  <application name="JabberJoin" language="en_US">
179  <synopsis>
180  Join a chat room
181  </synopsis>
182  <syntax>
183  <parameter name="Jabber" required="true">
184  <para>Client or transport Asterisk uses to connect to Jabber.</para>
185  </parameter>
186  <parameter name="RoomJID" required="true">
187  <para>XMPP/Jabber JID (Name) of chat room.</para>
188  </parameter>
189  <parameter name="Nickname" required="false">
190  <para>The nickname Asterisk will use in the chat room.</para>
191  <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>
192  </parameter>
193  </syntax>
194  <description>
195  <para>Allows Asterisk to join a chat room.</para>
196  </description>
197  </application>
198  <application name="JabberLeave" language="en_US">
199  <synopsis>
200  Leave a chat room
201  </synopsis>
202  <syntax>
203  <parameter name="Jabber" required="true">
204  <para>Client or transport Asterisk uses to connect to Jabber.</para>
205  </parameter>
206  <parameter name="RoomJID" required="true">
207  <para>XMPP/Jabber JID (Name) of chat room.</para>
208  </parameter>
209  <parameter name="Nickname" required="false">
210  <para>The nickname Asterisk uses in the chat room.</para>
211  </parameter>
212  </syntax>
213  <description>
214  <para>Allows Asterisk to leave a chat room.</para>
215  </description>
216  </application>
217  <application name="JabberStatus" language="en_US">
218  <synopsis>
219  Retrieve the status of a jabber list member
220  </synopsis>
221  <syntax>
222  <parameter name="Jabber" required="true">
223  <para>Client or transport Asterisk users to connect to Jabber.</para>
224  </parameter>
225  <parameter name="JID" required="true">
226  <para>XMPP/Jabber JID (Name) of recipient.</para>
227  </parameter>
228  <parameter name="Variable" required="true">
229  <para>Variable to store the status of requested user.</para>
230  </parameter>
231  </syntax>
232  <description>
233  <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
234  <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
235  The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
236  <enumlist>
237  <enum name="1">
238  <para>Online.</para>
239  </enum>
240  <enum name="2">
241  <para>Chatty.</para>
242  </enum>
243  <enum name="3">
244  <para>Away.</para>
245  </enum>
246  <enum name="4">
247  <para>Extended Away.</para>
248  </enum>
249  <enum name="5">
250  <para>Do Not Disturb.</para>
251  </enum>
252  <enum name="6">
253  <para>Offline.</para>
254  </enum>
255  <enum name="7">
256  <para>Not In Roster.</para>
257  </enum>
258  </enumlist>
259  </description>
260  </application>
261  <manager name="JabberSend" language="en_US">
262  <synopsis>
263  Sends a message to a Jabber Client.
264  </synopsis>
265  <syntax>
266  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
267  <parameter name="Jabber" required="true">
268  <para>Client or transport Asterisk uses to connect to JABBER.</para>
269  </parameter>
270  <parameter name="JID" required="true">
271  <para>XMPP/Jabber JID (Name) of recipient.</para>
272  </parameter>
273  <parameter name="Message" required="true">
274  <para>Message to be sent to the buddy.</para>
275  </parameter>
276  </syntax>
277  <description>
278  <para>Sends a message to a Jabber Client.</para>
279  </description>
280  </manager>
281  ***/
282 
283 /*!\todo This should really be renamed to xmpp.conf. For backwards compatibility, we
284  * need to read both files */
285 #define JABBER_CONFIG "jabber.conf"
286 
287 /*-- Forward declarations */
288 static void aji_message_destroy(struct aji_message *obj);
289 static int aji_is_secure(struct aji_client *client);
290 #ifdef HAVE_OPENSSL
291 static int aji_start_tls(struct aji_client *client);
292 static int aji_tls_handshake(struct aji_client *client);
293 #endif
294 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
295 static int aji_recv(struct aji_client *client, int timeout);
296 static int aji_send_header(struct aji_client *client, const char *to);
297 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
298 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
299 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
300 static int aji_act_hook(void *data, int type, iks *node);
301 static void aji_handle_iq(struct aji_client *client, iks *node);
302 static void aji_handle_message(struct aji_client *client, ikspak *pak);
303 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
304 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
305 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
306 static void *aji_recv_loop(void *data);
307 static int aji_initialize(struct aji_client *client);
308 static int aji_client_connect(void *data, ikspak *pak);
309 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
310 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
311 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
312 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
313 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
314 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
315 static int aji_create_client(char *label, struct ast_variable *var, int debug);
316 static int aji_create_buddy(char *label, struct aji_client *client);
317 static int aji_reload(int reload);
318 static int aji_load_config(int reload);
319 static void aji_pruneregister(struct aji_client *client);
320 static int aji_filter_roster(void *data, ikspak *pak);
321 static int aji_get_roster(struct aji_client *client);
322 static int aji_client_info_handler(void *data, ikspak *pak);
323 static int aji_dinfo_handler(void *data, ikspak *pak);
324 static int aji_ditems_handler(void *data, ikspak *pak);
325 static int aji_register_query_handler(void *data, ikspak *pak);
326 static int aji_register_approve_handler(void *data, ikspak *pak);
327 static int aji_reconnect(struct aji_client *client);
328 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd,
329  struct ast_cli_args *a);
330 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd,
331  struct ast_cli_args *a);
332 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
333  ast_cli_args *a);
334 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
335  ast_cli_args *a);
336 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
337 static int aji_receive_node_list(void *data, ikspak* pak);
338 static void aji_init_event_distribution(struct aji_client *client);
339 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type,
340  const char *name, const char *collection_name);
341 static iks* aji_build_node_config(iks *pubsub, const char *node_type,
342  const char *collection_name);
343 static void aji_create_pubsub_collection(struct aji_client *client,
344  const char *collection_name);
345 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
346  const char *leaf_name);
347 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
348  struct ast_cli_args *a);
349 static void aji_create_affiliations(struct aji_client *client, const char *node);
350 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
351 static void aji_publish_device_state(struct aji_client *client, const char * device,
352  const char *device_state, unsigned int cachable);
353 static int aji_handle_pubsub_error(void *data, ikspak *pak);
354 static int aji_handle_pubsub_event(void *data, ikspak *pak);
355 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
356 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name);
357 static iks* aji_build_node_request(struct aji_client *client, const char *collection);
358 static int aji_delete_node_list(void *data, ikspak* pak);
359 static void aji_pubsub_purge_nodes(struct aji_client *client,
360  const char* collection_name);
361 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
362  const char *context, const char *oldmsgs, const char *newmsgs);
363 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
364 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
365 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
366  const char *event_type, unsigned int cachable);
367 /* No transports in this version */
368 /*
369 static int aji_create_transport(char *label, struct aji_client *client);
370 static int aji_register_transport(void *data, ikspak *pak);
371 static int aji_register_transport2(void *data, ikspak *pak);
372 */
373 
374 static struct ast_cli_entry aji_cli[] = {
375  AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
376  AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
377  AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
378  AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
379  AST_CLI_DEFINE(aji_cli_create_collection, "Creates a PubSub node collection."),
380  AST_CLI_DEFINE(aji_cli_list_pubsub_nodes, "Lists PubSub nodes"),
381  AST_CLI_DEFINE(aji_cli_create_leafnode, "Creates a PubSub leaf node"),
382  AST_CLI_DEFINE(aji_cli_delete_pubsub_node, "Deletes a PubSub node"),
383  AST_CLI_DEFINE(aji_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
384 };
385 
386 static char *app_ajisend = "JabberSend";
387 static char *app_ajisendgroup = "JabberSendGroup";
388 static char *app_ajistatus = "JabberStatus";
389 static char *app_ajijoin = "JabberJoin";
390 static char *app_ajileave = "JabberLeave";
391 
393 static struct aji_capabilities *capabilities = NULL;
394 static struct ast_event_sub *mwi_sub = NULL;
395 static struct ast_event_sub *device_state_sub = NULL;
398 
399 /*! \brief Global flags, initialized to default values */
401 
402 /*! \brief PubSub flags, initialized to default values */
403 static struct ast_flags pubsubflags = { 0 };
404 /*!
405  * \internal
406  * \brief Deletes the aji_client data structure.
407  * \param obj aji_client The structure we will delete.
408  * \return void.
409  */
411 {
412  struct aji_message *tmp;
415  iks_filter_delete(obj->f);
416  iks_parser_delete(obj->p);
417  iks_stack_delete(obj->stack);
418  AST_LIST_LOCK(&obj->messages);
419  while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
420  aji_message_destroy(tmp);
421  }
423  ast_free(obj);
424 }
425 
426 /*!
427  * \internal
428  * \brief Deletes the aji_buddy data structure.
429  * \param obj aji_buddy The structure we will delete.
430  * \return void.
431  */
433 {
434  struct aji_resource *tmp;
435 
436  while ((tmp = obj->resources)) {
437  obj->resources = obj->resources->next;
438  ast_free(tmp->description);
439  ast_free(tmp);
440  }
441 
442  ast_free(obj);
443 }
444 
445 /*!
446  * \internal
447  * \brief Deletes the aji_message data structure.
448  * \param obj aji_message The structure we will delete.
449  * \return void.
450  */
451 static void aji_message_destroy(struct aji_message *obj)
452 {
453  if (obj->from) {
454  ast_free(obj->from);
455  }
456  if (obj->message) {
457  ast_free(obj->message);
458  }
459  ast_free(obj);
460 }
461 
462 /*!
463  * \internal
464  * \brief Find version in XML stream and populate our capabilities list
465  * \param node the node attribute in the caps element we'll look for or add to
466  * our list
467  * \param version the version attribute in the caps element we'll look for or
468  * add to our list
469  * \param pak struct The XML stanza we're processing
470  * \return a pointer to the added or found aji_version structure
471  */
472 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
473 {
474  struct aji_capabilities *list = NULL;
475  struct aji_version *res = NULL;
476 
477  list = capabilities;
478 
479  if (!node) {
480  node = pak->from->full;
481  }
482  if (!version) {
483  version = "none supplied.";
484  }
485  while (list) {
486  if (!strcasecmp(list->node, node)) {
487  res = list->versions;
488  while(res) {
489  if (!strcasecmp(res->version, version)) {
490  return res;
491  }
492  res = res->next;
493  }
494  /* Specified version not found. Let's add it to
495  this node in our capabilities list */
496  if (!res) {
497  res = ast_malloc(sizeof(*res));
498  if (!res) {
499  ast_log(LOG_ERROR, "Out of memory!\n");
500  return NULL;
501  }
502  res->jingle = 0;
503  res->parent = list;
504  ast_copy_string(res->version, version, sizeof(res->version));
505  res->next = list->versions;
506  list->versions = res;
507  return res;
508  }
509  }
510  list = list->next;
511  }
512  /* Specified node not found. Let's add it our capabilities list */
513  if (!list) {
514  list = ast_malloc(sizeof(*list));
515  if (!list) {
516  ast_log(LOG_ERROR, "Out of memory!\n");
517  return NULL;
518  }
519  res = ast_malloc(sizeof(*res));
520  if (!res) {
521  ast_log(LOG_ERROR, "Out of memory!\n");
522  ast_free(list);
523  return NULL;
524  }
525  ast_copy_string(list->node, node, sizeof(list->node));
526  ast_copy_string(res->version, version, sizeof(res->version));
527  res->jingle = 0;
528  res->parent = list;
529  res->next = NULL;
530  list->versions = res;
531  list->next = capabilities;
532  capabilities = list;
533  }
534  return res;
535 }
536 
537 /*!
538  * \internal
539  * \brief Find the aji_resource we want
540  * \param buddy aji_buddy A buddy
541  * \param name
542  * \return aji_resource object
543 */
544 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
545 {
546  struct aji_resource *res = NULL;
547  if (!buddy || !name) {
548  return res;
549  }
550  res = buddy->resources;
551  while (res) {
552  if (!strcasecmp(res->resource, name)) {
553  break;
554  }
555  res = res->next;
556  }
557  return res;
558 }
559 
560 /*!
561  * \internal
562  * \brief Jabber GTalk function
563  * \param node iks
564  * \return 1 on success, 0 on failure.
565 */
566 static int gtalk_yuck(iks *node)
567 {
568  if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) {
569  ast_debug(1, "Found resource with Googletalk voice capabilities\n");
570  return 1;
571  } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1")) {
572  ast_debug(1, "Found resource with Gmail voice/video chat capabilities\n");
573  return 1;
574  } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 video-v1 voice-v1")) {
575  ast_debug(1, "Found resource with Gmail voice/video chat capabilities (no camera)\n");
576  return 1;
577  }
578 
579  return 0;
580 }
581 
582 /*!
583  * \internal
584  * \brief Setup the authentication struct
585  * \param id iksid
586  * \param pass password
587  * \param sid
588  * \return x iks
589 */
590 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
591 {
592  iks *x, *y;
593  x = iks_new("iq");
594  iks_insert_attrib(x, "type", "set");
595  y = iks_insert(x, "query");
596  iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
597  iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
598  iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
599  if (sid) {
600  char buf[41];
601  char sidpass[100];
602  snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
603  ast_sha1_hash(buf, sidpass);
604  iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
605  } else {
606  iks_insert_cdata(iks_insert(y, "password"), pass, 0);
607  }
608  return x;
609 }
610 
611 /*!
612  * \internal
613  * \brief Dial plan function status(). puts the status of watched user
614  * into a channel variable.
615  * \param chan ast_channel
616  * \param data
617  * \retval 0 success
618  * \retval -1 error
619  */
620 static int aji_status_exec(struct ast_channel *chan, const char *data)
621 {
622  struct aji_client *client = NULL;
623  struct aji_buddy *buddy = NULL;
624  struct aji_resource *r = NULL;
625  char *s = NULL;
626  int stat = 7;
627  char status[2];
628  static int deprecation_warning = 0;
630  AST_APP_ARG(sender);
631  AST_APP_ARG(jid);
632  AST_APP_ARG(variable);
633  );
635  AST_APP_ARG(screenname);
637  );
638 
639  if (deprecation_warning++ % 10 == 0) {
640  ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
641  }
642 
643  if (!data) {
644  ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
645  return 0;
646  }
647  s = ast_strdupa(data);
649 
650  if (args.argc != 3) {
651  ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
652  return -1;
653  }
654 
655  AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
656  if (jid.argc < 1 || jid.argc > 2) {
657  ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
658  return -1;
659  }
660 
661  if (!(client = ast_aji_get_client(args.sender))) {
662  ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
663  return -1;
664  }
665  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
666  if (!buddy) {
667  ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
669  return -1;
670  }
671  r = aji_find_resource(buddy, jid.resource);
672  if (!r && buddy->resources) {
673  r = buddy->resources;
674  }
677  if (!r) {
678  ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
679  } else {
680  stat = r->status;
681  }
682  snprintf(status, sizeof(status), "%d", stat);
683  pbx_builtin_setvar_helper(chan, args.variable, status);
684  return 0;
685 }
686 
687 /*!
688  * \internal
689  * \brief Dial plan funtcion to retrieve the status of a buddy.
690  * \param channel The associated ast_channel, if there is one
691  * \param data The account, buddy JID, and optional timeout
692  * timeout.
693  * \retval 0 success
694  * \retval -1 failure
695  */
696 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
697 {
698  struct aji_client *client = NULL;
699  struct aji_buddy *buddy = NULL;
700  struct aji_resource *r = NULL;
701  int stat = 7;
703  AST_APP_ARG(sender);
704  AST_APP_ARG(jid);
705  );
707  AST_APP_ARG(screenname);
709  );
710 
711  if (!data) {
712  ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
713  return 0;
714  }
716 
717  if (args.argc != 2) {
718  ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
719  return -1;
720  }
721 
722  AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
723  if (jid.argc < 1 || jid.argc > 2) {
724  ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
725  return -1;
726  }
727 
728  if (!(client = ast_aji_get_client(args.sender))) {
729  ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
730  return -1;
731  }
732  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
733  if (!buddy) {
734  ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
736  return -1;
737  }
738  r = aji_find_resource(buddy, jid.resource);
739  if (!r && buddy->resources) {
740  r = buddy->resources;
741  }
744  if (!r) {
745  ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
746  } else {
747  stat = r->status;
748  }
749  snprintf(buf, buflen, "%d", stat);
750  return 0;
751 }
752 
754  .name = "JABBER_STATUS",
755  .read = acf_jabberstatus_read,
756 };
757 
758 /*!
759  * \internal
760  * \brief Dial plan function to receive a message.
761  * \param channel The associated ast_channel, if there is one
762  * \param data The account, JID, and optional timeout
763  * timeout.
764  * \retval 0 success
765  * \retval -1 failure
766  */
767 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
768 {
769  char *parse = NULL;
770  int timeout;
771  int jidlen, resourcelen;
772  struct timeval start;
773  long diff = 0;
774  struct aji_client *client = NULL;
775  int found = 0;
776  struct aji_message *tmp = NULL;
778  AST_APP_ARG(account);
779  AST_APP_ARG(jid);
780  AST_APP_ARG(timeout);
781  );
783  AST_APP_ARG(screenname);
784  AST_APP_ARG(resource);
785  );
786 
787  if (ast_strlen_zero(data)) {
788  ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
789  return -1;
790  }
791 
792  parse = ast_strdupa(data);
793  AST_STANDARD_APP_ARGS(args, parse);
794 
795  if (args.argc < 2 || args.argc > 3) {
796  ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
797  return -1;
798  }
799 
800  parse = ast_strdupa(args.jid);
801  AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
802  if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
803  ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
804  return -1;
805  }
806 
807  if (ast_strlen_zero(args.timeout)) {
808  timeout = 20;
809  } else {
810  sscanf(args.timeout, "%d", &timeout);
811  if (timeout <= 0) {
812  ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
813  return -1;
814  }
815  }
816 
817  jidlen = strlen(jid.screenname);
818  resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
819 
820  client = ast_aji_get_client(args.account);
821  if (!client) {
822  ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
823  return -1;
824  }
825 
826  ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
827 
828  start = ast_tvnow();
829 
830  if (chan && ast_autoservice_start(chan) < 0) {
831  ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", chan->name);
833  return -1;
834  }
835 
836  /* search the messages list, grab the first message that matches with
837  * the from JID we're expecting, and remove it from the messages list */
838  while (diff < timeout) {
839  struct timespec ts = { 0, };
840  struct timeval wait;
841  int res;
842 
843  wait = ast_tvadd(start, ast_tv(timeout, 0));
844  ts.tv_sec = wait.tv_sec;
845  ts.tv_nsec = wait.tv_usec * 1000;
846 
847  /* wait up to timeout seconds for an incoming message */
848  ast_mutex_lock(&messagelock);
849  res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
850  ast_mutex_unlock(&messagelock);
851  if (res == ETIMEDOUT) {
852  ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
853  break;
854  }
855 
856  AST_LIST_LOCK(&client->messages);
857  AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
858  if (jid.argc == 1) {
859  /* no resource provided, compare bare JIDs */
860  if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
861  continue;
862  }
863  } else {
864  /* resource appended, compare bare JIDs and resources */
865  char *resource = strchr(tmp->from, '/');
866  if (!resource || strlen(resource) == 0) {
867  ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
868  if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
869  continue;
870  }
871  } else {
872  resource ++;
873  if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
874  continue;
875  }
876  }
877  }
878  /* check if the message is not too old */
879  if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
880  ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
882  aji_message_destroy(tmp);
883  continue;
884  }
885  found = 1;
886  ast_copy_string(buf, tmp->message, buflen);
888  aji_message_destroy(tmp);
889  break;
890  }
892  AST_LIST_UNLOCK(&client->messages);
893  if (found) {
894  break;
895  }
896 
897  /* check timeout */
898  diff = ast_tvdiff_ms(ast_tvnow(), start);
899  }
900 
902  if (chan && ast_autoservice_stop(chan) < 0) {
903  ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", chan->name);
904  }
905 
906  /* return if we timed out */
907  if (!found) {
908  ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
909  return -1;
910  }
911 
912  return 0;
913 }
914 
916  .name = "JABBER_RECEIVE",
917  .read = acf_jabberreceive_read,
918 };
919 
920 /*!
921  * \internal
922  * \brief Delete old messages from a given JID
923  * Messages stored during more than client->message_timeout are deleted
924  * \param client Asterisk's XMPP client
925  * \param from the JID we received messages from
926  * \retval the number of deleted messages
927  * \retval -1 failure
928  */
929 static int delete_old_messages(struct aji_client *client, char *from)
930 {
931  int deleted = 0;
932  int isold = 0;
933  struct aji_message *tmp = NULL;
934  if (!client) {
935  ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
936  return -1;
937  }
938 
939  /* remove old messages */
940  AST_LIST_LOCK(&client->messages);
941  if (AST_LIST_EMPTY(&client->messages)) {
942  AST_LIST_UNLOCK(&client->messages);
943  return 0;
944  }
945 
946  AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
947  if (isold) {
948  if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
950  aji_message_destroy(tmp);
951  deleted ++;
952  }
953  } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
954  isold = 1;
955  if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
957  aji_message_destroy(tmp);
958  deleted ++;
959  }
960  }
961  }
963  AST_LIST_UNLOCK(&client->messages);
964 
965  return deleted;
966 }
967 
968 /*!
969  * \internal
970  * \brief Delete old messages
971  * Messages stored during more than client->message_timeout are deleted
972  * \param client Asterisk's XMPP client
973  * \retval the number of deleted messages
974  * \retval -1 failure
975  */
976 static int delete_old_messages_all(struct aji_client *client)
977 {
978  return delete_old_messages(client, NULL);
979 }
980 
981 /*!
982 * \brief Application to join a chat room
983 * \param chan ast_channel
984 * \param data Data is sender|jid|nickname.
985 * \retval 0 success
986 * \retval -1 error
987 */
988 static int aji_join_exec(struct ast_channel *chan, const char *data)
989 {
990  struct aji_client *client = NULL;
991  char *s;
992  char nick[AJI_MAX_RESJIDLEN];
993 
995  AST_APP_ARG(sender);
996  AST_APP_ARG(jid);
997  AST_APP_ARG(nick);
998  );
999 
1000  if (!data) {
1001  ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1002  return -1;
1003  }
1004  s = ast_strdupa(data);
1005 
1007  if (args.argc < 2 || args.argc > 3) {
1008  ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1009  return -1;
1010  }
1011 
1012  if (strchr(args.jid, '/')) {
1013  ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1014  return -1;
1015  }
1016 
1017  if (!(client = ast_aji_get_client(args.sender))) {
1018  ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1019  return -1;
1020  }
1021 
1022  if (!ast_strlen_zero(args.nick)) {
1023  snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1024  } else {
1025  if (client->component) {
1026  sprintf(nick, "asterisk");
1027  } else {
1028  snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1029  }
1030  }
1031 
1032  if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1033  ast_aji_join_chat(client, args.jid, nick);
1034  } else {
1035  ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1036  }
1037 
1039  return 0;
1040 }
1041 
1042 /*!
1043 * \brief Application to leave a chat room
1044 * \param chan ast_channel
1045 * \param data Data is sender|jid|nickname.
1046 * \retval 0 success
1047 * \retval -1 error
1048 */
1049 static int aji_leave_exec(struct ast_channel *chan, const char *data)
1050 {
1051  struct aji_client *client = NULL;
1052  char *s;
1053  char nick[AJI_MAX_RESJIDLEN];
1055  AST_APP_ARG(sender);
1056  AST_APP_ARG(jid);
1057  AST_APP_ARG(nick);
1058  );
1059 
1060  if (!data) {
1061  ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1062  return -1;
1063  }
1064  s = ast_strdupa(data);
1065 
1067  if (args.argc < 2 || args.argc > 3) {
1068  ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1069  return -1;
1070  }
1071 
1072  if (strchr(args.jid, '/')) {
1073  ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1074  return -1;
1075  }
1076 
1077  if (!(client = ast_aji_get_client(args.sender))) {
1078  ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1079  return -1;
1080  }
1081 
1082  if (!ast_strlen_zero(args.nick)) {
1083  snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1084  } else {
1085  if (client->component) {
1086  sprintf(nick, "asterisk");
1087  } else {
1088  snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1089  }
1090  }
1091 
1092  if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1093  ast_aji_leave_chat(client, args.jid, nick);
1094  }
1096  return 0;
1097 }
1098 
1099 /*!
1100  * \internal
1101  * \brief Dial plan function to send a message.
1102  * \param chan ast_channel
1103  * \param data Data is account,jid,message.
1104  * \retval 0 success
1105  * \retval -1 failure
1106  */
1107 static int aji_send_exec(struct ast_channel *chan, const char *data)
1108 {
1109  struct aji_client *client = NULL;
1110  char *s;
1112  AST_APP_ARG(sender);
1113  AST_APP_ARG(recipient);
1115  );
1116 
1117  if (!data) {
1118  ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1119  return -1;
1120  }
1121  s = ast_strdupa(data);
1122 
1124  if (args.argc < 3) {
1125  ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1126  return -1;
1127  }
1128 
1129  if (!(client = ast_aji_get_client(args.sender))) {
1130  ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1131  return -1;
1132  }
1133  if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
1134  ast_aji_send_chat(client, args.recipient, args.message);
1135  }
1137  return 0;
1138 }
1139 
1140 /*!
1141 * \brief Application to send a message to a groupchat.
1142 * \param chan ast_channel
1143 * \param data Data is sender|groupchat|message.
1144 * \retval 0 success
1145 * \retval -1 error
1146 */
1147 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
1148 {
1149  struct aji_client *client = NULL;
1150  char *s;
1151  char nick[AJI_MAX_RESJIDLEN];
1152  int res = 0;
1154  AST_APP_ARG(sender);
1155  AST_APP_ARG(groupchat);
1157  AST_APP_ARG(nick);
1158  );
1159 
1160  if (!data) {
1161  ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1162  return -1;
1163  }
1164  s = ast_strdupa(data);
1165 
1167  if (args.argc < 3 || args.argc > 4) {
1168  ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1169  return -1;
1170  }
1171 
1172  if (!(client = ast_aji_get_client(args.sender))) {
1173  ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1174  return -1;
1175  }
1176 
1177  if (ast_strlen_zero(args.nick) || args.argc == 3) {
1178  if (client->component) {
1179  sprintf(nick, "asterisk");
1180  } else {
1181  snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1182  }
1183  } else {
1184  snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1185  }
1186 
1187  if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
1188  res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
1189  }
1190 
1192  if (res != IKS_OK) {
1193  return -1;
1194  }
1195  return 0;
1196 }
1197 
1198 /*!
1199  * \internal
1200  * \brief Tests whether the connection is secured or not
1201  * \return 0 if the connection is not secured
1202  */
1203 static int aji_is_secure(struct aji_client *client)
1204 {
1205 #ifdef HAVE_OPENSSL
1206  return client->stream_flags & SECURE;
1207 #else
1208  return 0;
1209 #endif
1210 }
1211 
1212 #ifdef HAVE_OPENSSL
1213 /*!
1214  * \internal
1215  * \brief Starts the TLS procedure
1216  * \param client the configured XMPP client we use to connect to a XMPP server
1217  * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
1218  * if OpenSSL is not installed
1219  */
1220 static int aji_start_tls(struct aji_client *client)
1221 {
1222  int ret;
1223 
1224  /* This is sent not encrypted */
1225  if ((ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"))) {
1226  return ret;
1227  }
1228 
1229  client->stream_flags |= TRY_SECURE;
1230  return IKS_OK;
1231 }
1232 
1233 /*!
1234  * \internal
1235  * \brief TLS handshake, OpenSSL initialization
1236  * \param client the configured XMPP client we use to connect to a XMPP server
1237  * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
1238  */
1239 static int aji_tls_handshake(struct aji_client *client)
1240 {
1241  int ret;
1242  int sock;
1243  long ssl_opts;
1244 
1245  ast_debug(1, "Starting TLS handshake\n");
1246 
1247  /* Choose an SSL/TLS protocol version, create SSL_CTX */
1248  client->ssl_method = SSLv23_method();
1249  if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
1250  return IKS_NET_TLSFAIL;
1251  }
1252  ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
1253  SSL_CTX_set_options(client->ssl_context, ssl_opts);
1254 
1255  /* Create new SSL session */
1256  if (!(client->ssl_session = SSL_new(client->ssl_context))) {
1257  return IKS_NET_TLSFAIL;
1258  }
1259 
1260  /* Enforce TLS on our XMPP connection */
1261  sock = iks_fd(client->p);
1262  if (!(ret = SSL_set_fd(client->ssl_session, sock))) {
1263  return IKS_NET_TLSFAIL;
1264  }
1265 
1266  /* Perform SSL handshake */
1267  if (!(ret = SSL_connect(client->ssl_session))) {
1268  return IKS_NET_TLSFAIL;
1269  }
1270 
1271  client->stream_flags &= (~TRY_SECURE);
1272  client->stream_flags |= SECURE;
1273 
1274  /* Sent over the established TLS connection */
1275  if ((ret = aji_send_header(client, client->jid->server)) != IKS_OK) {
1276  return IKS_NET_TLSFAIL;
1277  }
1278 
1279  ast_debug(1, "TLS started with server\n");
1280 
1281  return IKS_OK;
1282 }
1283 #endif /* HAVE_OPENSSL */
1284 
1285 /*!
1286  * \internal
1287  * \brief Secured or unsecured IO socket receiving function
1288  * \param client the configured XMPP client we use to connect to a XMPP server
1289  * \param buffer the reception buffer
1290  * \param buf_len the size of the buffer
1291  * \param timeout the select timer
1292  * \retval the number of read bytes
1293  * \retval 0 timeout expiration
1294  * \retval -1 error
1295  */
1296 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
1297 {
1298  struct pollfd pfd = { .events = POLLIN };
1299  int len, res;
1300 
1301 #ifdef HAVE_OPENSSL
1302  if (aji_is_secure(client)) {
1303  pfd.fd = SSL_get_fd(client->ssl_session);
1304  if (pfd.fd < 0) {
1305  return -1;
1306  }
1307  } else
1308 #endif /* HAVE_OPENSSL */
1309  pfd.fd = iks_fd(client->p);
1310 
1311  res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
1312  if (res > 0) {
1313 #ifdef HAVE_OPENSSL
1314  if (aji_is_secure(client)) {
1315  len = SSL_read(client->ssl_session, buffer, buf_len);
1316  } else
1317 #endif /* HAVE_OPENSSL */
1318  len = recv(pfd.fd, buffer, buf_len, 0);
1319 
1320  if (len > 0) {
1321  return len;
1322  } else if (len <= 0) {
1323  return -1;
1324  }
1325  }
1326  return res;
1327 }
1328 
1329 /*!
1330  * \internal
1331  * \brief Tries to receive data from the Jabber server
1332  * \param client the configured XMPP client we use to connect to a XMPP server
1333  * \param timeout the timeout value
1334  * This function receives (encrypted or unencrypted) data from the XMPP server,
1335  * and passes it to the parser.
1336  * \retval IKS_OK success
1337  * \retval IKS_NET_RWERR IO error
1338  * \retval IKS_NET_NOCONN no connection available
1339  * \retval IKS_NET_EXPIRED timeout expiration
1340  */
1341 static int aji_recv (struct aji_client *client, int timeout)
1342 {
1343  int len, ret;
1344  char buf[NET_IO_BUF_SIZE - 1];
1345  char newbuf[NET_IO_BUF_SIZE - 1];
1346  int pos = 0;
1347  int newbufpos = 0;
1348  unsigned char c;
1349 
1350  memset(buf, 0, sizeof(buf));
1351  memset(newbuf, 0, sizeof(newbuf));
1352 
1353  while (1) {
1354  len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
1355  if (len < 0) return IKS_NET_RWERR;
1356  if (len == 0) return IKS_NET_EXPIRED;
1357  buf[len] = '\0';
1358 
1359  /* our iksemel parser won't work as expected if we feed
1360  it with XML packets that contain multiple whitespace
1361  characters between tags */
1362  while (pos < len) {
1363  c = buf[pos];
1364  /* if we stumble on the ending tag character,
1365  we skip any whitespace that follows it*/
1366  if (c == '>') {
1367  while (isspace(buf[pos+1])) {
1368  pos++;
1369  }
1370  }
1371  newbuf[newbufpos] = c;
1372  newbufpos ++;
1373  pos++;
1374  }
1375  pos = 0;
1376  newbufpos = 0;
1377 
1378  /* Log the message here, because iksemel's logHook is
1379  unaccessible */
1380  aji_log_hook(client, buf, len, 1);
1381 
1382  /* let iksemel deal with the string length,
1383  and reset our buffer */
1384  ret = iks_parse(client->p, newbuf, 0, 0);
1385  memset(newbuf, 0, sizeof(newbuf));
1386 
1387  switch (ret) {
1388  case IKS_NOMEM:
1389  ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
1390  break;
1391  case IKS_BADXML:
1392  ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
1393  break;
1394  case IKS_HOOK:
1395  ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
1396  break;
1397  }
1398  if (ret != IKS_OK) {
1399  return ret;
1400  }
1401  ast_debug(3, "XML parsing successful\n");
1402  }
1403  return IKS_OK;
1404 }
1405 
1406 /*!
1407  * \internal
1408  * \brief Sends XMPP header to the server
1409  * \param client the configured XMPP client we use to connect to a XMPP server
1410  * \param to the target XMPP server
1411  * \return IKS_OK on success, any other value on failure
1412  */
1413 static int aji_send_header(struct aji_client *client, const char *to)
1414 {
1415  char *msg;
1416  int len, err;
1417 
1418  len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
1419  msg = iks_malloc(len);
1420  if (!msg)
1421  return IKS_NOMEM;
1422  sprintf(msg, "<?xml version='1.0'?>"
1423  "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
1424  "%s' to='%s' version='1.0'>", client->name_space, to);
1425  err = aji_send_raw(client, msg);
1426  iks_free(msg);
1427  if (err != IKS_OK)
1428  return err;
1429 
1430  return IKS_OK;
1431 }
1432 
1433 /*!
1434  * \brief Wraps raw sending
1435  * \param client the configured XMPP client we use to connect to a XMPP server
1436  * \param x the XMPP packet to send
1437  * \return IKS_OK on success, any other value on failure
1438  */
1439 int ast_aji_send(struct aji_client *client, iks *x)
1440 {
1441  return aji_send_raw(client, iks_string(iks_stack(x), x));
1442 }
1443 
1444 /*!
1445  * \internal
1446  * \brief Sends an XML string over an XMPP connection
1447  * \param client the configured XMPP client we use to connect to a XMPP server
1448  * \param xmlstr the XML string to send
1449  * The XML data is sent whether the connection is secured or not. In the
1450  * latter case, we just call iks_send_raw().
1451  * \return IKS_OK on success, any other value on failure
1452  */
1453 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
1454 {
1455  int ret;
1456 #ifdef HAVE_OPENSSL
1457  int len = strlen(xmlstr);
1458 
1459  if (aji_is_secure(client)) {
1460  ret = SSL_write(client->ssl_session, xmlstr, len);
1461  if (ret) {
1462  /* Log the message here, because iksemel's logHook is
1463  unaccessible */
1464  aji_log_hook(client, xmlstr, len, 0);
1465  return IKS_OK;
1466  }
1467  }
1468 #endif
1469  /* If needed, data will be sent unencrypted, and logHook will
1470  be called inside iks_send_raw */
1471  ret = iks_send_raw(client->p, xmlstr);
1472  if (ret != IKS_OK) {
1473  return ret;
1474  }
1475 
1476  return IKS_OK;
1477 }
1478 
1479 /*!
1480  * \internal
1481  * \brief the debug loop.
1482  * \param data void
1483  * \param xmpp xml data as string
1484  * \param size size of string
1485  * \param is_incoming direction of packet 1 for inbound 0 for outbound.
1486  */
1487 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
1488 {
1489  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1490 
1491  if (!ast_strlen_zero(xmpp)) {
1492  manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
1493  }
1494 
1495  if (client->debug) {
1496  if (is_incoming) {
1497  ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
1498  } else {
1499  if (strlen(xmpp) == 1) {
1500  if (option_debug > 2 && xmpp[0] == ' ') {
1501  ast_verbose("\nJABBER: Keep alive packet\n");
1502  }
1503  } else {
1504  ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
1505  }
1506  }
1507 
1508  }
1510 }
1511 
1512 /*!
1513  * \internal
1514  * \brief A wrapper function for iks_start_sasl
1515  * \param client the configured XMPP client we use to connect to a XMPP server
1516  * \param type the SASL authentication type. Supported types are PLAIN and MD5
1517  * \param username
1518  * \param pass password.
1519  *
1520  * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
1521  */
1522 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
1523 {
1524  iks *x = NULL;
1525  int len;
1526  char *s;
1527  char *base64;
1528 
1529  /* trigger SASL DIGEST-MD5 only over an unsecured connection.
1530  iks_start_sasl is an iksemel API function and relies on GnuTLS,
1531  whereas we use OpenSSL */
1532  if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
1533  return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass);
1534  if (!(type & IKS_STREAM_SASL_PLAIN)) {
1535  ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
1536  return IKS_NET_NOTSUPP;
1537  }
1538 
1539  x = iks_new("auth");
1540  if (!x) {
1541  ast_log(LOG_ERROR, "Out of memory.\n");
1542  return IKS_NET_NOTSUPP;
1543  }
1544 
1545  iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
1546  len = strlen(username) + strlen(pass) + 3;
1547  s = ast_alloca(len);
1548  base64 = ast_alloca((len + 2) * 4 / 3);
1549  iks_insert_attrib(x, "mechanism", "PLAIN");
1550  snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
1551 
1552  /* exclude the NULL training byte from the base64 encoding operation
1553  as some XMPP servers will refuse it.
1554  The format for authentication is [authzid]\0authcid\0password
1555  not [authzid]\0authcid\0password\0 */
1556  ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
1557  iks_insert_cdata(x, base64, 0);
1558  ast_aji_send(client, x);
1559  iks_delete(x);
1560 
1561  return IKS_OK;
1562 }
1563 
1564 /*!
1565  * \internal
1566  * \brief The action hook parses the inbound packets, constantly running.
1567  * \param data aji client structure
1568  * \param type type of packet
1569  * \param node the actual packet.
1570  * \return IKS_OK or IKS_HOOK .
1571  */
1572 static int aji_act_hook(void *data, int type, iks *node)
1573 {
1574  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1575  ikspak *pak = NULL;
1576  iks *auth = NULL;
1577  int features = 0;
1578 
1579  if (!node) {
1580  ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
1582  return IKS_HOOK;
1583  }
1584 
1585  if (client->state == AJI_DISCONNECTING) {
1587  return IKS_HOOK;
1588  }
1589 
1590  pak = iks_packet(node);
1591 
1592  /* work around iksemel's impossibility to recognize node names
1593  * containing a semicolon. Set the namespace of the corresponding
1594  * node accordingly. */
1595  if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
1596  char *node_ns = NULL;
1597  char attr[AJI_MAX_ATTRLEN];
1598  char *node_name = iks_name(iks_child(node));
1599  char *aux = strchr(node_name, ':') + 1;
1600  snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
1601  node_ns = iks_find_attrib(iks_child(node), attr);
1602  if (node_ns) {
1603  pak->ns = node_ns;
1604  pak->query = iks_child(node);
1605  }
1606  }
1607 
1608 
1609  if (!client->component) { /*client */
1610  switch (type) {
1611  case IKS_NODE_START:
1612  if (client->usetls && !aji_is_secure(client)) {
1613 #ifndef HAVE_OPENSSL
1614  ast_log(LOG_ERROR, "TLS connection cannot be established. Please install OpenSSL and its development libraries on this system, or disable the TLS option in your configuration file\n");
1616  return IKS_HOOK;
1617 #else
1618  if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
1619  ast_log(LOG_ERROR, "Could not start TLS\n");
1621  return IKS_HOOK;
1622  }
1623 #endif
1624  break;
1625  }
1626  if (!client->usesasl) {
1627  iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE);
1628  auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
1629  if (auth) {
1630  iks_insert_attrib(auth, "id", client->mid);
1631  iks_insert_attrib(auth, "to", client->jid->server);
1632  ast_aji_increment_mid(client->mid);
1633  ast_aji_send(client, auth);
1634  iks_delete(auth);
1635  } else {
1636  ast_log(LOG_ERROR, "Out of memory.\n");
1637  }
1638  }
1639  break;
1640 
1641  case IKS_NODE_NORMAL:
1642 #ifdef HAVE_OPENSSL
1643  if (client->stream_flags & TRY_SECURE) {
1644  if (!strcmp("proceed", iks_name(node))) {
1645  return aji_tls_handshake(client);
1646  }
1647  }
1648 #endif
1649  if (!strcmp("stream:features", iks_name(node))) {
1650  features = iks_stream_features(node);
1651  if (client->usesasl) {
1652  if (client->usetls && !aji_is_secure(client)) {
1653  break;
1654  }
1655  if (client->authorized) {
1656  if (features & IKS_STREAM_BIND) {
1657  iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
1658  auth = iks_make_resource_bind(client->jid);
1659  if (auth) {
1660  iks_insert_attrib(auth, "id", client->mid);
1661  ast_aji_increment_mid(client->mid);
1662  ast_aji_send(client, auth);
1663  iks_delete(auth);
1664  } else {
1665  ast_log(LOG_ERROR, "Out of memory.\n");
1666  break;
1667  }
1668  }
1669  if (features & IKS_STREAM_SESSION) {
1670  iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
1671  auth = iks_make_session();
1672  if (auth) {
1673  iks_insert_attrib(auth, "id", "auth");
1674  ast_aji_increment_mid(client->mid);
1675  ast_aji_send(client, auth);
1676  iks_delete(auth);
1677  } else {
1678  ast_log(LOG_ERROR, "Out of memory.\n");
1679  }
1680  }
1681  } else {
1682  int ret;
1683  if (!client->jid->user) {
1684  ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
1685  break;
1686  }
1687 
1688  ret = aji_start_sasl(client, features, client->jid->user, client->password);
1689  if (ret != IKS_OK) {
1691  return IKS_HOOK;
1692  }
1693  break;
1694  }
1695  }
1696  } else if (!strcmp("failure", iks_name(node))) {
1697  ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
1698  } else if (!strcmp("success", iks_name(node))) {
1699  client->authorized = 1;
1700  aji_send_header(client, client->jid->server);
1701  }
1702  break;
1703  case IKS_NODE_ERROR:
1704  ast_log(LOG_ERROR, "JABBER: Node Error\n");
1706  return IKS_HOOK;
1707  break;
1708  case IKS_NODE_STOP:
1709  ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1711  return IKS_HOOK;
1712  break;
1713  }
1714  } else if (client->state != AJI_CONNECTED && client->component) {
1715  switch (type) {
1716  case IKS_NODE_START:
1717  if (client->state == AJI_DISCONNECTED) {
1718  char secret[160], shasum[320], *handshake;
1719 
1720  sprintf(secret, "%s%s", pak->id, client->password);
1721  ast_sha1_hash(shasum, secret);
1722  if (ast_asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
1723  aji_send_raw(client, handshake);
1724  ast_free(handshake);
1725  }
1726  client->state = AJI_CONNECTING;
1727  if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
1728  client->state = AJI_CONNECTED;
1729  else
1730  ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1731  break;
1732  }
1733  break;
1734 
1735  case IKS_NODE_NORMAL:
1736  break;
1737 
1738  case IKS_NODE_ERROR:
1739  ast_log(LOG_ERROR, "JABBER: Node Error\n");
1741  return IKS_HOOK;
1742 
1743  case IKS_NODE_STOP:
1744  ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1746  return IKS_HOOK;
1747  }
1748  }
1749 
1750  switch (pak->type) {
1751  case IKS_PAK_NONE:
1752  ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1753  break;
1754  case IKS_PAK_MESSAGE:
1755  aji_handle_message(client, pak);
1756  ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1757  break;
1758  case IKS_PAK_PRESENCE:
1759  aji_handle_presence(client, pak);
1760  ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1761  break;
1762  case IKS_PAK_S10N:
1763  aji_handle_subscribe(client, pak);
1764  ast_debug(1, "JABBER: Handling paktype S10N\n");
1765  break;
1766  case IKS_PAK_IQ:
1767  ast_debug(1, "JABBER: Handling paktype IQ\n");
1768  aji_handle_iq(client, node);
1769  break;
1770  default:
1771  ast_debug(1, "JABBER: I don't know anything about paktype '%u'\n", pak->type);
1772  break;
1773  }
1774 
1775  iks_filter_packet(client->f, pak);
1776 
1777  if (node)
1778  iks_delete(node);
1779 
1781  return IKS_OK;
1782 }
1783 /*!
1784  * \internal
1785  * \brief Unknown
1786  * \param data void
1787  * \param pak ikspak
1788  * \return IKS_FILTER_EAT.
1789 */
1790 static int aji_register_approve_handler(void *data, ikspak *pak)
1791 {
1792  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1793  iks *iq = NULL, *presence = NULL, *x = NULL;
1794 
1795  iq = iks_new("iq");
1796  presence = iks_new("presence");
1797  x = iks_new("x");
1798  if (client && iq && presence && x) {
1799  if (!iks_find(pak->query, "remove")) {
1800  iks_insert_attrib(iq, "from", client->jid->full);
1801  iks_insert_attrib(iq, "to", pak->from->full);
1802  iks_insert_attrib(iq, "id", pak->id);
1803  iks_insert_attrib(iq, "type", "result");
1804  ast_aji_send(client, iq);
1805 
1806  iks_insert_attrib(presence, "from", client->jid->full);
1807  iks_insert_attrib(presence, "to", pak->from->partial);
1808  iks_insert_attrib(presence, "id", client->mid);
1809  ast_aji_increment_mid(client->mid);
1810  iks_insert_attrib(presence, "type", "subscribe");
1811  iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1812  iks_insert_node(presence, x);
1813  ast_aji_send(client, presence);
1814  }
1815  } else {
1816  ast_log(LOG_ERROR, "Out of memory.\n");
1817  }
1818 
1819  iks_delete(iq);
1820  iks_delete(presence);
1821  iks_delete(x);
1822 
1824  return IKS_FILTER_EAT;
1825 }
1826 /*!
1827  * \internal
1828  * \brief register handler for incoming querys (IQ's)
1829  * \param data incoming aji_client request
1830  * \param pak ikspak
1831  * \return IKS_FILTER_EAT.
1832 */
1833 static int aji_register_query_handler(void *data, ikspak *pak)
1834 {
1835  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1836  struct aji_buddy *buddy = NULL;
1837  char *node = NULL;
1838  iks *iq = NULL, *query = NULL;
1839 
1840  client = (struct aji_client *) data;
1841 
1842  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1843  if (!buddy) {
1844  iks *error = NULL, *notacceptable = NULL;
1845 
1846  ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1847  iq = iks_new("iq");
1848  query = iks_new("query");
1849  error = iks_new("error");
1850  notacceptable = iks_new("not-acceptable");
1851  if (iq && query && error && notacceptable) {
1852  iks_insert_attrib(iq, "type", "error");
1853  iks_insert_attrib(iq, "from", client->user);
1854  iks_insert_attrib(iq, "to", pak->from->full);
1855  iks_insert_attrib(iq, "id", pak->id);
1856  iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1857  iks_insert_attrib(error, "code" , "406");
1858  iks_insert_attrib(error, "type", "modify");
1859  iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1860  iks_insert_node(iq, query);
1861  iks_insert_node(iq, error);
1862  iks_insert_node(error, notacceptable);
1863  ast_aji_send(client, iq);
1864  } else {
1865  ast_log(LOG_ERROR, "Out of memory.\n");
1866  }
1867 
1868  iks_delete(error);
1869  iks_delete(notacceptable);
1870  } else if (!(node = iks_find_attrib(pak->query, "node"))) {
1871  iks *instructions = NULL;
1872  char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1873  iq = iks_new("iq");
1874  query = iks_new("query");
1875  instructions = iks_new("instructions");
1876  if (iq && query && instructions && client) {
1877  iks_insert_attrib(iq, "from", client->user);
1878  iks_insert_attrib(iq, "to", pak->from->full);
1879  iks_insert_attrib(iq, "id", pak->id);
1880  iks_insert_attrib(iq, "type", "result");
1881  iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1882  iks_insert_cdata(instructions, explain, 0);
1883  iks_insert_node(iq, query);
1884  iks_insert_node(query, instructions);
1885  ast_aji_send(client, iq);
1886  } else {
1887  ast_log(LOG_ERROR, "Out of memory.\n");
1888  }
1889 
1890  iks_delete(instructions);
1891  }
1892  iks_delete(iq);
1893  iks_delete(query);
1896  return IKS_FILTER_EAT;
1897 }
1898 
1899 /*!
1900  * \internal
1901  * \brief Handles stuff
1902  * \param data void
1903  * \param pak ikspak
1904  * \return IKS_FILTER_EAT.
1905 */
1906 static int aji_ditems_handler(void *data, ikspak *pak)
1907 {
1908  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1909  char *node = NULL;
1910 
1911  if (!(node = iks_find_attrib(pak->query, "node"))) {
1912  iks *iq = NULL, *query = NULL, *item = NULL;
1913  iq = iks_new("iq");
1914  query = iks_new("query");
1915  item = iks_new("item");
1916 
1917  if (iq && query && item) {
1918  iks_insert_attrib(iq, "from", client->user);
1919  iks_insert_attrib(iq, "to", pak->from->full);
1920  iks_insert_attrib(iq, "id", pak->id);
1921  iks_insert_attrib(iq, "type", "result");
1922  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1923  iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1924  iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1925  iks_insert_attrib(item, "jid", client->user);
1926 
1927  iks_insert_node(iq, query);
1928  iks_insert_node(query, item);
1929  ast_aji_send(client, iq);
1930  } else {
1931  ast_log(LOG_ERROR, "Out of memory.\n");
1932  }
1933 
1934  iks_delete(iq);
1935  iks_delete(query);
1936  iks_delete(item);
1937 
1938  } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1939  iks *iq, *query, *confirm;
1940  iq = iks_new("iq");
1941  query = iks_new("query");
1942  confirm = iks_new("item");
1943  if (iq && query && confirm && client) {
1944  iks_insert_attrib(iq, "from", client->user);
1945  iks_insert_attrib(iq, "to", pak->from->full);
1946  iks_insert_attrib(iq, "id", pak->id);
1947  iks_insert_attrib(iq, "type", "result");
1948  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1949  iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1950  iks_insert_attrib(confirm, "node", "confirmaccount");
1951  iks_insert_attrib(confirm, "name", "Confirm AIM account");
1952  iks_insert_attrib(confirm, "jid", "blog.astjab.org");
1953 
1954  iks_insert_node(iq, query);
1955  iks_insert_node(query, confirm);
1956  ast_aji_send(client, iq);
1957  } else {
1958  ast_log(LOG_ERROR, "Out of memory.\n");
1959  }
1960 
1961  iks_delete(iq);
1962  iks_delete(query);
1963  iks_delete(confirm);
1964 
1965  } else if (!strcasecmp(node, "confirmaccount")) {
1966  iks *iq = NULL, *query = NULL, *feature = NULL;
1967 
1968  iq = iks_new("iq");
1969  query = iks_new("query");
1970  feature = iks_new("feature");
1971 
1972  if (iq && query && feature && client) {
1973  iks_insert_attrib(iq, "from", client->user);
1974  iks_insert_attrib(iq, "to", pak->from->full);
1975  iks_insert_attrib(iq, "id", pak->id);
1976  iks_insert_attrib(iq, "type", "result");
1977  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1978  iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
1979  iks_insert_node(iq, query);
1980  iks_insert_node(query, feature);
1981  ast_aji_send(client, iq);
1982  } else {
1983  ast_log(LOG_ERROR, "Out of memory.\n");
1984  }
1985 
1986  iks_delete(iq);
1987  iks_delete(query);
1988  iks_delete(feature);
1989  }
1990 
1992  return IKS_FILTER_EAT;
1993 
1994 }
1995 
1996 /*!
1997  * \internal
1998  * \brief Handle add extra info
1999  * \param data void
2000  * \param pak ikspak
2001  * \return IKS_FILTER_EAT
2002 */
2003 static int aji_client_info_handler(void *data, ikspak *pak)
2004 {
2005  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2006  struct aji_resource *resource = NULL;
2007  struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2008 
2009  if (!buddy) {
2010  ast_log(LOG_NOTICE, "JABBER: Received client info from unknown buddy: %s.\n", pak->from->full);
2012  return IKS_FILTER_EAT;
2013  }
2014 
2015  resource = aji_find_resource(buddy, pak->from->resource);
2016  if (pak->subtype == IKS_TYPE_RESULT) {
2017  if (!resource) {
2018  ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2021  return IKS_FILTER_EAT;
2022  }
2023  if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2024  resource->cap->jingle = 1;
2025  } else {
2026  resource->cap->jingle = 0;
2027  }
2028  } else if (pak->subtype == IKS_TYPE_GET) {
2029  iks *iq, *disco, *ident, *google, *query;
2030  iq = iks_new("iq");
2031  query = iks_new("query");
2032  ident = iks_new("identity");
2033  disco = iks_new("feature");
2034  google = iks_new("feature");
2035  if (iq && ident && disco && google) {
2036  iks_insert_attrib(iq, "from", client->jid->full);
2037  iks_insert_attrib(iq, "to", pak->from->full);
2038  iks_insert_attrib(iq, "type", "result");
2039  iks_insert_attrib(iq, "id", pak->id);
2040  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2041  iks_insert_attrib(ident, "category", "client");
2042  iks_insert_attrib(ident, "type", "pc");
2043  iks_insert_attrib(ident, "name", "asterisk");
2044  iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2045  iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2046  iks_insert_node(iq, query);
2047  iks_insert_node(query, ident);
2048  iks_insert_node(query, google);
2049  iks_insert_node(query, disco);
2050  ast_aji_send(client, iq);
2051  } else {
2052  ast_log(LOG_ERROR, "Out of Memory.\n");
2053  }
2054 
2055  iks_delete(iq);
2056  iks_delete(query);
2057  iks_delete(ident);
2058  iks_delete(google);
2059  iks_delete(disco);
2060  } else if (pak->subtype == IKS_TYPE_ERROR) {
2061  ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
2062  }
2065  return IKS_FILTER_EAT;
2066 }
2067 
2068 /*!
2069  * \internal
2070  * \brief Handler of the return info packet
2071  * \param data aji_client
2072  * \param pak ikspak
2073  * \return IKS_FILTER_EAT
2074 */
2075 static int aji_dinfo_handler(void *data, ikspak *pak)
2076 {
2077  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2078  char *node = NULL;
2079  struct aji_resource *resource = NULL;
2080  struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2081 
2082  if (!buddy) {
2083  ast_log(LOG_NOTICE, "JABBER: Received client info from unknown buddy: %s.\n", pak->from->full);
2085  return IKS_FILTER_EAT;
2086  }
2087 
2088  if (pak->subtype == IKS_TYPE_ERROR) {
2089  ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
2091  return IKS_FILTER_EAT;
2092  }
2093  resource = aji_find_resource(buddy, pak->from->resource);
2094  if (pak->subtype == IKS_TYPE_RESULT) {
2095  if (!resource) {
2096  ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2099  return IKS_FILTER_EAT;
2100  }
2101  if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2102  resource->cap->jingle = 1;
2103  } else {
2104  resource->cap->jingle = 0;
2105  }
2106  } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
2107  iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
2108 
2109  iq = iks_new("iq");
2110  query = iks_new("query");
2111  identity = iks_new("identity");
2112  disco = iks_new("feature");
2113  reg = iks_new("feature");
2114  commands = iks_new("feature");
2115  gateway = iks_new("feature");
2116  version = iks_new("feature");
2117  vcard = iks_new("feature");
2118  search = iks_new("feature");
2119  if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
2120  iks_insert_attrib(iq, "from", client->user);
2121  iks_insert_attrib(iq, "to", pak->from->full);
2122  iks_insert_attrib(iq, "id", pak->id);
2123  iks_insert_attrib(iq, "type", "result");
2124  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2125  iks_insert_attrib(identity, "category", "gateway");
2126  iks_insert_attrib(identity, "type", "pstn");
2127  iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2128  iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2129  iks_insert_attrib(reg, "var", "jabber:iq:register");
2130  iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2131  iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2132  iks_insert_attrib(version, "var", "jabber:iq:version");
2133  iks_insert_attrib(vcard, "var", "vcard-temp");
2134  iks_insert_attrib(search, "var", "jabber:iq:search");
2135 
2136  iks_insert_node(iq, query);
2137  iks_insert_node(query, identity);
2138  iks_insert_node(query, disco);
2139  iks_insert_node(query, reg);
2140  iks_insert_node(query, commands);
2141  iks_insert_node(query, gateway);
2142  iks_insert_node(query, version);
2143  iks_insert_node(query, vcard);
2144  iks_insert_node(query, search);
2145  ast_aji_send(client, iq);
2146  } else {
2147  ast_log(LOG_ERROR, "Out of memory.\n");
2148  }
2149 
2150  iks_delete(iq);
2151  iks_delete(query);
2152  iks_delete(identity);
2153  iks_delete(disco);
2154  iks_delete(reg);
2155  iks_delete(commands);
2156  iks_delete(gateway);
2157  iks_delete(version);
2158  iks_delete(vcard);
2159  iks_delete(search);
2160  } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
2161  iks *iq, *query, *confirm;
2162  iq = iks_new("iq");
2163  query = iks_new("query");
2164  confirm = iks_new("item");
2165 
2166  if (iq && query && confirm && client) {
2167  iks_insert_attrib(iq, "from", client->user);
2168  iks_insert_attrib(iq, "to", pak->from->full);
2169  iks_insert_attrib(iq, "id", pak->id);
2170  iks_insert_attrib(iq, "type", "result");
2171  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2172  iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2173  iks_insert_attrib(confirm, "node", "confirmaccount");
2174  iks_insert_attrib(confirm, "name", "Confirm AIM account");
2175  iks_insert_attrib(confirm, "jid", client->user);
2176  iks_insert_node(iq, query);
2177  iks_insert_node(query, confirm);
2178  ast_aji_send(client, iq);
2179  } else {
2180  ast_log(LOG_ERROR, "Out of memory.\n");
2181  }
2182 
2183  iks_delete(iq);
2184  iks_delete(query);
2185  iks_delete(confirm);
2186 
2187  } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
2188  iks *iq, *query, *feature;
2189 
2190  iq = iks_new("iq");
2191  query = iks_new("query");
2192  feature = iks_new("feature");
2193 
2194  if (iq && query && feature && client) {
2195  iks_insert_attrib(iq, "from", client->user);
2196  iks_insert_attrib(iq, "to", pak->from->full);
2197  iks_insert_attrib(iq, "id", pak->id);
2198  iks_insert_attrib(iq, "type", "result");
2199  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2200  iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2201  iks_insert_node(iq, query);
2202  iks_insert_node(query, feature);
2203  ast_aji_send(client, iq);
2204  } else {
2205  ast_log(LOG_ERROR, "Out of memory.\n");
2206  }
2207 
2208  iks_delete(iq);
2209  iks_delete(query);
2210  iks_delete(feature);
2211  }
2212 
2215  return IKS_FILTER_EAT;
2216 }
2217 
2218 /*!
2219  * \internal
2220  * \brief Handles \verbatim <iq> \endverbatim stanzas.
2221  * \param client the configured XMPP client we use to connect to a XMPP server
2222  * \param node iks
2223  * \return void.
2224  */
2225 static void aji_handle_iq(struct aji_client *client, iks *node)
2226 {
2227  /*Nothing to see here */
2228 }
2229 
2230 /*!
2231  * \internal
2232  * \brief Handles \verbatim <message>\endverbatim stanzas.
2233  * Adds the incoming message to the client's message list.
2234  * \param client the configured XMPP client we use to connect to a XMPP server
2235  * \param pak ikspak the node
2236  */
2237 static void aji_handle_message(struct aji_client *client, ikspak *pak)
2238 {
2239  struct aji_message *insert;
2240  int deleted = 0;
2241 
2242  ast_debug(3, "client %s received a message\n", client->name);
2243 
2244  if (!(insert = ast_calloc(1, sizeof(*insert)))) {
2245  return;
2246  }
2247 
2248  insert->arrived = ast_tvnow();
2249 
2250  /* wake up threads waiting for messages */
2251  ast_mutex_lock(&messagelock);
2252  ast_cond_broadcast(&message_received_condition);
2253  ast_mutex_unlock(&messagelock);
2254 
2255  if (iks_find_cdata(pak->x, "body")) {
2256  insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2257  }
2258  if (pak->id) {
2259  ast_copy_string(insert->id, pak->id, sizeof(insert->id));
2260  }
2261  if (pak->from){
2262  /* insert will furtherly be added to message list */
2263  insert->from = ast_strdup(pak->from->full);
2264  if (!insert->from) {
2265  ast_free(insert);
2266  ast_log(LOG_ERROR, "Memory allocation failure\n");
2267  return;
2268  }
2269  ast_debug(3, "message comes from %s\n", insert->from);
2270  }
2271 
2272  /* remove old messages received from this JID
2273  * and insert received message */
2274  deleted = delete_old_messages(client, pak->from->partial);
2275  ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2276  AST_LIST_LOCK(&client->messages);
2277  AST_LIST_INSERT_HEAD(&client->messages, insert, list);
2278  AST_LIST_UNLOCK(&client->messages);
2279 }
2280 
2281 /*!
2282  * \internal
2283  * \brief handles \verbatim <presence>\endverbatim stanzas.
2284  * \param client the configured XMPP client we use to connect to a XMPP server
2285  * \param pak ikspak
2286  */
2287 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
2288 {
2289  int status, priority;
2290  struct aji_buddy *buddy;
2291  struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
2292  char *ver, *node, *descrip, *type;
2293 
2294  if (client->state != AJI_CONNECTED)
2295  aji_create_buddy(pak->from->partial, client);
2296 
2297  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2298  if (!buddy && pak->from->partial) {
2299  /* allow our jid to be used to log in with another resource */
2300  if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
2301  aji_create_buddy(pak->from->partial, client);
2302  else
2303  ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
2304  return;
2305  }
2306  type = iks_find_attrib(pak->x, "type");
2307  if (client->component && type &&!strcasecmp("probe", type)) {
2308  aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2309  ast_verbose("what i was looking for \n");
2310  }
2311  ASTOBJ_WRLOCK(buddy);
2312  status = (pak->show) ? pak->show : 6;
2313  priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
2314  tmp = buddy->resources;
2315  descrip = ast_strdup(iks_find_cdata(pak->x, "status"));
2316 
2317  while (tmp && pak->from->resource) {
2318  if (!strcasecmp(tmp->resource, pak->from->resource)) {
2319  tmp->status = status;
2320  if (tmp->description) {
2321  ast_free(tmp->description);
2322  }
2323  tmp->description = descrip;
2324  found = tmp;
2325  if (status == 6) { /* Sign off Destroy resource */
2326  if (last && found->next) {
2327  last->next = found->next;
2328  } else if (!last) {
2329  if (found->next) {
2330  buddy->resources = found->next;
2331  } else {
2332  buddy->resources = NULL;
2333  }
2334  } else if (!found->next) {
2335  if (last) {
2336  last->next = NULL;
2337  } else {
2338  buddy->resources = NULL;
2339  }
2340  }
2341  ast_free(found);
2342  found = NULL;
2343  break;
2344  }
2345  /* resource list is sorted by descending priority */
2346  if (tmp->priority != priority) {
2347  found->priority = priority;
2348  if (!last && !found->next) {
2349  /* resource was found to be unique,
2350  leave loop */
2351  break;
2352  }
2353  /* search for resource in our list
2354  and take it out for the moment */
2355  if (last) {
2356  last->next = found->next;
2357  } else {
2358  buddy->resources = found->next;
2359  }
2360 
2361  last = NULL;
2362  tmp = buddy->resources;
2363  if (!buddy->resources) {
2364  buddy->resources = found;
2365  }
2366  /* priority processing */
2367  while (tmp) {
2368  /* insert resource back according to
2369  its priority value */
2370  if (found->priority > tmp->priority) {
2371  if (last) {
2372  /* insert within list */
2373  last->next = found;
2374  }
2375  found->next = tmp;
2376  if (!last) {
2377  /* insert on top */
2378  buddy->resources = found;
2379  }
2380  break;
2381  }
2382  if (!tmp->next) {
2383  /* insert at the end of the list */
2384  tmp->next = found;
2385  found->next = NULL;
2386  break;
2387  }
2388  last = tmp;
2389  tmp = tmp->next;
2390  }
2391  }
2392  break;
2393  }
2394  last = tmp;
2395  tmp = tmp->next;
2396  }
2397 
2398  /* resource not found in our list, create it */
2399  if (!found && status != 6 && pak->from->resource) {
2400  found = ast_calloc(1, sizeof(*found));
2401 
2402  if (!found) {
2403  ast_log(LOG_ERROR, "Out of memory!\n");
2404  ASTOBJ_UNLOCK(buddy);
2406  return;
2407  }
2408  ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
2409  found->status = status;
2410  found->description = descrip;
2411  found->priority = priority;
2412  found->next = NULL;
2413  last = NULL;
2414  tmp = buddy->resources;
2415  while (tmp) {
2416  if (found->priority > tmp->priority) {
2417  if (last) {
2418  last->next = found;
2419  }
2420  found->next = tmp;
2421  if (!last) {
2422  buddy->resources = found;
2423  }
2424  break;
2425  }
2426  if (!tmp->next) {
2427  tmp->next = found;
2428  break;
2429  }
2430  last = tmp;
2431  tmp = tmp->next;
2432  }
2433  if (!tmp) {
2434  buddy->resources = found;
2435  }
2436  }
2437 
2438  ASTOBJ_UNLOCK(buddy);
2440 
2441  node = iks_find_attrib(iks_find(pak->x, "c"), "node");
2442  ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
2443 
2444  /* handle gmail client's special caps:c tag */
2445  if (!node && !ver) {
2446  node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
2447  ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
2448  }
2449 
2450  /* retrieve capabilites of the new resource */
2451  if (status != 6 && found && !found->cap) {
2452  found->cap = aji_find_version(node, ver, pak);
2453  if (gtalk_yuck(pak->x)) { /* gtalk should do discover */
2454  found->cap->jingle = 1;
2455  }
2456  if (found->cap->jingle) {
2457  ast_debug(1, "Special case for google till they support discover.\n");
2458  } else {
2459  iks *iq, *query;
2460  iq = iks_new("iq");
2461  query = iks_new("query");
2462  if (query && iq) {
2463  iks_insert_attrib(iq, "type", "get");
2464  iks_insert_attrib(iq, "to", pak->from->full);
2465  iks_insert_attrib(iq, "from", client->jid->full);
2466  iks_insert_attrib(iq, "id", client->mid);
2467  ast_aji_increment_mid(client->mid);
2468  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2469  iks_insert_node(iq, query);
2470  ast_aji_send(client, iq);
2471  } else {
2472  ast_log(LOG_ERROR, "Out of memory.\n");
2473  }
2474  iks_delete(query);
2475  iks_delete(iq);
2476  }
2477  }
2478  switch (pak->subtype) {
2479  case IKS_TYPE_AVAILABLE:
2480  ast_debug(3, "JABBER: I am available ^_* %u\n", pak->subtype);
2481  break;
2482  case IKS_TYPE_UNAVAILABLE:
2483  ast_debug(3, "JABBER: I am unavailable ^_* %u\n", pak->subtype);
2484  break;
2485  default:
2486  ast_debug(3, "JABBER: Ohh sexy and the wrong type: %u\n", pak->subtype);
2487  }
2488  switch (pak->show) {
2489  case IKS_SHOW_UNAVAILABLE:
2490  ast_debug(3, "JABBER: type: %u subtype %u\n", pak->subtype, pak->show);
2491  break;
2492  case IKS_SHOW_AVAILABLE:
2493  ast_debug(3, "JABBER: type is available\n");
2494  break;
2495  case IKS_SHOW_CHAT:
2496  ast_debug(3, "JABBER: type: %u subtype %u\n", pak->subtype, pak->show);
2497  break;
2498  case IKS_SHOW_AWAY:
2499  ast_debug(3, "JABBER: type is away\n");
2500  break;
2501  case IKS_SHOW_XA:
2502  ast_debug(3, "JABBER: type: %u subtype %u\n", pak->subtype, pak->show);
2503  break;
2504  case IKS_SHOW_DND:
2505  ast_debug(3, "JABBER: type: %u subtype %u\n", pak->subtype, pak->show);
2506  break;
2507  default:
2508  ast_debug(3, "JABBER: Kinky! how did that happen %u\n", pak->show);
2509  }
2510 
2511  if (found) {
2512  manager_event(EVENT_FLAG_USER, "JabberStatus",
2513  "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
2514  "\r\nDescription: %s\r\n",
2515  client->name, pak->from->partial, found->resource, found->status,
2516  found->priority, S_OR(found->description, ""));
2517  } else {
2518  manager_event(EVENT_FLAG_USER, "JabberStatus",
2519  "Account: %s\r\nJID: %s\r\nStatus: %u\r\n",
2520  client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
2521  }
2522 }
2523 
2524 /*!
2525  * \internal
2526  * \brief handles subscription requests.
2527  * \param client the configured XMPP client we use to connect to a XMPP server
2528  * \param pak ikspak iksemel packet.
2529  * \return void.
2530  */
2531 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
2532 {
2533  iks *presence = NULL, *status = NULL;
2534  struct aji_buddy* buddy = NULL;
2535 
2536  switch (pak->subtype) {
2537  case IKS_TYPE_SUBSCRIBE:
2538  if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
2539  presence = iks_new("presence");
2540  status = iks_new("status");
2541  if (presence && status) {
2542  iks_insert_attrib(presence, "type", "subscribed");
2543  iks_insert_attrib(presence, "to", pak->from->full);
2544  iks_insert_attrib(presence, "from", client->jid->full);
2545  if (pak->id)
2546  iks_insert_attrib(presence, "id", pak->id);
2547  iks_insert_cdata(status, "Asterisk has approved subscription", 0);
2548  iks_insert_node(presence, status);
2549  ast_aji_send(client, presence);
2550  } else {
2551  ast_log(LOG_ERROR, "Unable to allocate nodes\n");
2552  }
2553 
2554  iks_delete(presence);
2555  iks_delete(status);
2556  }
2557 
2558  if (client->component)
2559  aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2560  case IKS_TYPE_SUBSCRIBED:
2561  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2562  if (!buddy && pak->from->partial) {
2563  aji_create_buddy(pak->from->partial, client);
2564  } else if (buddy) {
2566  }
2567  default:
2568  if (option_verbose > 4) {
2569  ast_verbose(VERBOSE_PREFIX_3 "JABBER: This is a subcription of type %u\n", pak->subtype);
2570  }
2571  }
2572 }
2573 
2574 /*!
2575  * \brief sends messages.
2576  * \param client the configured XMPP client we use to connect to a XMPP server
2577  * \param address
2578  * \param message
2579  * \retval IKS_OK success
2580  * \retval -1 failure
2581  */
2582 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
2583 {
2584  return aji_send_raw_chat(client, 0, NULL, address, message);
2585 }
2586 
2587 /*!
2588 * \brief sends message to a groupchat
2589 * Prior to sending messages to a groupchat, one must be connected to it.
2590 * \param client the configured XMPP client we use to connect to a XMPP server
2591 * \param nick the nickname we use in the chatroom
2592 * \param address the user the messages must be sent to
2593 * \param message the message to send
2594 * \return IKS_OK on success, any other value on failure
2595 */
2596 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
2597  return aji_send_raw_chat(client, 1, nick, address, message);
2598 }
2599 
2600 /*!
2601 * \brief sends messages.
2602 * \param client the configured XMPP client we use to connect to a XMPP server
2603 * \param groupchat
2604 * \param nick the nickname we use in chatrooms
2605 * \param address
2606 * \param message
2607 * \return IKS_OK on success, any other value on failure
2608 */
2609 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
2610 {
2611  int res = 0;
2612  iks *message_packet = NULL;
2613  char from[AJI_MAX_JIDLEN];
2614  /* the nickname is used only in component mode */
2615  if (nick && client->component) {
2616  snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
2617  } else {
2618  snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
2619  }
2620 
2621  if (client->state != AJI_CONNECTED) {
2622  ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
2623  return -1;
2624  }
2625 
2626  message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
2627  if (!message_packet) {
2628  ast_log(LOG_ERROR, "Out of memory.\n");
2629  return -1;
2630  }
2631  iks_insert_attrib(message_packet, "from", from);
2632  res = ast_aji_send(client, message_packet);
2633  iks_delete(message_packet);
2634 
2635  return res;
2636 }
2637 
2638 /*!
2639  * \brief create a chatroom.
2640  * \param client the configured XMPP client we use to connect to a XMPP server
2641  * \param room name of room
2642  * \param server name of server
2643  * \param topic topic for the room.
2644  * \return 0.
2645  */
2646 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
2647 {
2648  int res = 0;
2649  iks *iq = NULL;
2650  iq = iks_new("iq");
2651 
2652  if (iq && client) {
2653  iks_insert_attrib(iq, "type", "get");
2654  iks_insert_attrib(iq, "to", server);
2655  iks_insert_attrib(iq, "id", client->mid);
2656  ast_aji_increment_mid(client->mid);
2657  ast_aji_send(client, iq);
2658  } else {
2659  ast_log(LOG_ERROR, "Out of memory.\n");
2660  }
2661 
2662  iks_delete(iq);
2663 
2664  return res;
2665 }
2666 
2667 /*!
2668  * \brief join a chatroom.
2669  * \param client the configured XMPP client we use to connect to a XMPP server
2670  * \param room room to join
2671  * \param nick the nickname to use in this room
2672  * \return IKS_OK on success, any other value on failure.
2673  */
2674 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
2675 {
2676  return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
2677 }
2678 
2679 /*!
2680  * \brief leave a chatroom.
2681  * \param client the configured XMPP client we use to connect to a XMPP server
2682  * \param room room to leave
2683  * \param nick the nickname used in this room
2684  * \return IKS_OK on success, any other value on failure.
2685  */
2686 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
2687 {
2688  return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
2689 }
2690 /*!
2691  * \brief invite to a chatroom.
2692  * \param client the configured XMPP client we use to connect to a XMPP server
2693  * \param user
2694  * \param room
2695  * \param message
2696  * \return res.
2697  */
2698 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
2699 {
2700  int res = 0;
2701  iks *invite, *body, *namespace;
2702 
2703  invite = iks_new("message");
2704  body = iks_new("body");
2705  namespace = iks_new("x");
2706  if (client && invite && body && namespace) {
2707  iks_insert_attrib(invite, "to", user);
2708  iks_insert_attrib(invite, "id", client->mid);
2709  ast_aji_increment_mid(client->mid);
2710  iks_insert_cdata(body, message, 0);
2711  iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
2712  iks_insert_attrib(namespace, "jid", room);
2713  iks_insert_node(invite, body);
2714  iks_insert_node(invite, namespace);
2715  res = ast_aji_send(client, invite);
2716  } else {
2717  ast_log(LOG_ERROR, "Out of memory.\n");
2718  }
2719 
2720  iks_delete(body);
2721  iks_delete(namespace);
2722  iks_delete(invite);
2723 
2724  return res;
2725 }
2726 
2727 /*!
2728  * \internal
2729  * \brief receive message loop.
2730  * \param data void
2731  * \return void.
2732  */
2733 static void *aji_recv_loop(void *data)
2734 {
2735  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2736  int res = IKS_HOOK;
2737 
2738  while (res != IKS_OK) {
2739  ast_debug(3, "JABBER: Connecting.\n");
2740  res = aji_reconnect(client);
2741  sleep(4);
2742  }
2743 
2744  do {
2745  if (res == IKS_NET_RWERR || client->timeout == 0) {
2746  while (res != IKS_OK) {
2747  ast_debug(3, "JABBER: reconnecting.\n");
2748  res = aji_reconnect(client);
2749  sleep(4);
2750  }
2751  }
2752 
2753  res = aji_recv(client, 1);
2754 
2755  if (client->state == AJI_DISCONNECTING) {
2756  ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
2757  pthread_exit(NULL);
2758  }
2759 
2760  /* Decrease timeout if no data received, and delete
2761  * old messages globally */
2762  if (res == IKS_NET_EXPIRED) {
2763  client->timeout--;
2764  delete_old_messages_all(client);
2765  }
2766  if (res == IKS_HOOK) {
2767  ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
2768  } else if (res == IKS_NET_TLSFAIL) {
2769  ast_log(LOG_ERROR, "JABBER: Failure in TLS.\n");
2770  } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
2771  res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
2772  if (res == IKS_OK) {
2773  client->timeout = 50;
2774  } else {
2775  ast_log(LOG_WARNING, "JABBER: Network Timeout\n");
2776  }
2777  } else if (res == IKS_NET_RWERR) {
2778  ast_log(LOG_WARNING, "JABBER: socket read error\n");
2779  }
2780  } while (client);
2782  return 0;
2783 }
2784 
2785 /*!
2786  * \brief increments the mid field for messages and other events.
2787  * \param mid char.
2788  * \return void.
2789  */
2791 {
2792  int i = 0;
2793 
2794  for (i = strlen(mid) - 1; i >= 0; i--) {
2795  if (mid[i] != 'z') {
2796  mid[i] = mid[i] + 1;
2797  i = 0;
2798  } else
2799  mid[i] = 'a';
2800  }
2801 }
2802 
2803 #if 0
2804 /*!
2805  * \brief attempts to register to a transport.
2806  * \param aji_client struct, and xml packet.
2807  * \return IKS_FILTER_EAT.
2808  */
2809 /*allows for registering to transport , was too sketch and is out for now. */
2810 static int aji_register_transport(void *data, ikspak *pak)
2811 {
2812  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2813  int res = 0;
2814  struct aji_buddy *buddy = NULL;
2815  iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
2816 
2817  if (client && send) {
2818  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2819  ASTOBJ_RDLOCK(iterator);
2820  if (iterator->btype == AJI_TRANS) {
2821  buddy = iterator;
2822  }
2823  ASTOBJ_UNLOCK(iterator);
2824  });
2825  iks_filter_remove_hook(client->f, aji_register_transport);
2826  iks_filter_add_rule(client->f, aji_register_transport2, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, IKS_NS_REGISTER, IKS_RULE_DONE);
2827  iks_insert_attrib(send, "to", buddy->host);
2828  iks_insert_attrib(send, "id", client->mid);
2829  ast_aji_increment_mid(client->mid);
2830  iks_insert_attrib(send, "from", client->user);
2831  res = ast_aji_send(client, send);
2832  } else
2833  ast_log(LOG_ERROR, "Out of memory.\n");
2834 
2835  if (send)
2836  iks_delete(send);
2838  return IKS_FILTER_EAT;
2839 
2840 }
2841 /*!
2842  * \brief attempts to register to a transport step 2.
2843  * \param aji_client struct, and xml packet.
2844  * \return IKS_FILTER_EAT.
2845  */
2846 /* more of the same blob of code, too wonky for now*/
2847 static int aji_register_transport2(void *data, ikspak *pak)
2848 {
2849  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2850  int res = 0;
2851  struct aji_buddy *buddy = NULL;
2852 
2853  iks *regiq = iks_new("iq");
2854  iks *regquery = iks_new("query");
2855  iks *reguser = iks_new("username");
2856  iks *regpass = iks_new("password");
2857 
2858  if (client && regquery && reguser && regpass && regiq) {
2859  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2860  ASTOBJ_RDLOCK(iterator);
2861  if (iterator->btype == AJI_TRANS)
2862  buddy = iterator; ASTOBJ_UNLOCK(iterator);
2863  });
2864  iks_filter_remove_hook(client->f, aji_register_transport2);
2865  iks_insert_attrib(regiq, "to", buddy->host);
2866  iks_insert_attrib(regiq, "type", "set");
2867  iks_insert_attrib(regiq, "id", client->mid);
2868  ast_aji_increment_mid(client->mid);
2869  iks_insert_attrib(regiq, "from", client->user);
2870  iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2871  iks_insert_cdata(reguser, buddy->user, 0);
2872  iks_insert_cdata(regpass, buddy->pass, 0);
2873  iks_insert_node(regiq, regquery);
2874  iks_insert_node(regquery, reguser);
2875  iks_insert_node(regquery, regpass);
2876  res = ast_aji_send(client, regiq);
2877  } else
2878  ast_log(LOG_ERROR, "Out of memory.\n");
2879  if (regiq)
2880  iks_delete(regiq);
2881  if (regquery)
2882  iks_delete(regquery);
2883  if (reguser)
2884  iks_delete(reguser);
2885  if (regpass)
2886  iks_delete(regpass);
2888  return IKS_FILTER_EAT;
2889 }
2890 #endif
2891 
2892 /*!
2893  * \internal
2894  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2895  * \param client the configured XMPP client we use to connect to a XMPP server
2896  * \return void.
2897  * \note The messages here should be configurable.
2898  */
2899 static void aji_pruneregister(struct aji_client *client)
2900 {
2901  iks *removeiq = iks_new("iq");
2902  iks *removequery = iks_new("query");
2903  iks *removeitem = iks_new("item");
2904  iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2905  if (!client || !removeiq || !removequery || !removeitem || !send) {
2906  ast_log(LOG_ERROR, "Out of memory.\n");
2907  goto safeout;
2908  }
2909 
2910  iks_insert_node(removeiq, removequery);
2911  iks_insert_node(removequery, removeitem);
2912  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2913  ASTOBJ_RDLOCK(iterator);
2914  /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2915  * be called at the same time */
2916  if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
2917  ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2918  "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2919  " so I am no longer subscribing to your presence.\n"));
2920  ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2921  "GoodBye. You are no longer in the Asterisk config file so I am removing"
2922  " your access to my presence.\n"));
2923  iks_insert_attrib(removeiq, "from", client->jid->full);
2924  iks_insert_attrib(removeiq, "type", "set");
2925  iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2926  iks_insert_attrib(removeitem, "jid", iterator->name);
2927  iks_insert_attrib(removeitem, "subscription", "remove");
2928  ast_aji_send(client, removeiq);
2929  } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2930  ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
2931  "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2932  ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2933  }
2934  ASTOBJ_UNLOCK(iterator);
2935  });
2936 
2937  safeout:
2938  iks_delete(removeiq);
2939  iks_delete(removequery);
2940  iks_delete(removeitem);
2941  iks_delete(send);
2942 
2944 }
2945 
2946 /*!
2947  * \internal
2948  * \brief filters the roster packet we get back from server.
2949  * \param data void
2950  * \param pak ikspak iksemel packet.
2951  * \return IKS_FILTER_EAT.
2952  */
2953 static int aji_filter_roster(void *data, ikspak *pak)
2954 {
2955  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2956  int flag = 0;
2957  iks *x = NULL;
2958  struct aji_buddy *buddy;
2959 
2960  client->state = AJI_CONNECTED;
2961  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2962  ASTOBJ_RDLOCK(iterator);
2963  x = iks_child(pak->query);
2964  flag = 0;
2965  while (x) {
2966  if (!iks_strcmp(iks_name(x), "item")) {
2967  if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
2968  flag = 1;
2969  ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
2970  }
2971  }
2972  x = iks_next(x);
2973  }
2974  if (!flag) {
2975  ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
2976  }
2977  iks_delete(x);
2978 
2979  ASTOBJ_UNLOCK(iterator);
2980  });
2981 
2982  x = iks_child(pak->query);
2983  while (x) {
2984  flag = 0;
2985  if (iks_strcmp(iks_name(x), "item") == 0) {
2986  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2987  ASTOBJ_RDLOCK(iterator);
2988  if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
2989  flag = 1;
2990  ASTOBJ_UNLOCK(iterator);
2991  });
2992 
2993  if (flag) {
2994  /* found buddy, don't create a new one */
2995  x = iks_next(x);
2996  continue;
2997  }
2998 
2999  buddy = ast_calloc(1, sizeof(*buddy));
3000  if (!buddy) {
3001  ast_log(LOG_WARNING, "Out of memory\n");
3003  return 0;
3004  }
3005  ASTOBJ_INIT(buddy);
3006  ASTOBJ_WRLOCK(buddy);
3007  ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
3009  if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
3010  ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
3011  ASTOBJ_MARK(buddy);
3012  } else if (ast_test_flag(&client->flags, AJI_AUTOREGISTER)) {
3013  if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
3014  /* subscribe to buddy's presence only
3015  if we really need to */
3017  }
3018  }
3019  ASTOBJ_UNLOCK(buddy);
3020  if (buddy) {
3021  ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
3023  }
3024  }
3025  x = iks_next(x);
3026  }
3027 
3028  iks_delete(x);
3029  aji_pruneregister(client);
3030 
3032  return IKS_FILTER_EAT;
3033 }
3034 
3035 /*!
3036  * \internal
3037  * \brief reconnect to jabber server
3038  * \param client the configured XMPP client we use to connect to a XMPP server
3039  * \return res.
3040 */
3041 static int aji_reconnect(struct aji_client *client)
3042 {
3043  int res = 0;
3044 
3045  if (client->state) {
3046  client->state = AJI_DISCONNECTED;
3047  }
3048  client->timeout = 50;
3049  if (client->p) {
3050  iks_parser_reset(client->p);
3051  }
3052  if (client->authorized) {
3053  client->authorized = 0;
3054  }
3055 
3056  res = aji_initialize(client);
3057 
3058  return res;
3059 }
3060 
3061 /*!
3062  * \internal
3063  * \brief Get the roster of jabber users
3064  * \param client the configured XMPP client we use to connect to a XMPP server
3065  * \return 1.
3066 */
3067 static int aji_get_roster(struct aji_client *client)
3068 {
3069  iks *roster = NULL;
3070  roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
3071 
3072  if (roster) {
3073  iks_insert_attrib(roster, "id", "roster");
3074  aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
3075  ast_aji_send(client, roster);
3076  }
3077 
3078  iks_delete(roster);
3079 
3080  return 1;
3081 }
3082 
3083 /*!
3084  * \internal
3085  * \brief connects as a client to jabber server.
3086  * \param data void
3087  * \param pak ikspak iksemel packet
3088  * \return res.
3089  */
3090 static int aji_client_connect(void *data, ikspak *pak)
3091 {
3092  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3093  int res = IKS_FILTER_PASS;
3094 
3095  if (client) {
3096  if (client->state == AJI_DISCONNECTED) {
3097  iks_filter_add_rule(client->f, aji_filter_roster, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "roster", IKS_RULE_DONE);
3098  client->state = AJI_CONNECTING;
3099  client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
3100  if (!client->component) { /*client*/
3101  aji_get_roster(client);
3102  }
3103  if (client->distribute_events) {
3105  }
3106 
3107  iks_filter_remove_hook(client->f, aji_client_connect);
3108  /* Once we remove the hook for this routine, we must return EAT or we will crash or corrupt memory */
3109  res = IKS_FILTER_EAT;
3110  }
3111  } else {
3112  ast_log(LOG_ERROR, "Out of memory.\n");
3113  }
3114 
3116  return res;
3117 }
3118 
3119 /*!
3120  * \internal
3121  * \brief prepares client for connect.
3122  * \param client the configured XMPP client we use to connect to a XMPP server
3123  * \return 1.
3124  */
3125 static int aji_initialize(struct aji_client *client)
3126 {
3127  int connected = IKS_NET_NOCONN;
3128 
3129 #ifdef HAVE_OPENSSL
3130  /* reset stream flags */
3131  client->stream_flags = 0;
3132 #endif
3133  /* If it's a component, connect to user, otherwise, connect to server */
3134  connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
3135 
3136  if (connected == IKS_NET_NOCONN) {
3137  ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
3138  return IKS_HOOK;
3139  } else if (connected == IKS_NET_NODNS) {
3140  ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name,
3141  S_OR(client->serverhost, client->jid->server));
3142  return IKS_HOOK;
3143  }
3144 
3145  return IKS_OK;
3146 }
3147 
3148 /*!
3149  * \brief disconnect from jabber server.
3150  * \param client the configured XMPP client we use to connect to a XMPP server
3151  * \return 1.
3152  */
3153 int ast_aji_disconnect(struct aji_client *client)
3154 {
3155  if (client) {
3156  ast_verb(4, "JABBER: Disconnecting\n");
3157 #ifdef HAVE_OPENSSL
3158  if (client->stream_flags & SECURE) {
3159  SSL_shutdown(client->ssl_session);
3160  SSL_CTX_free(client->ssl_context);
3161  SSL_free(client->ssl_session);
3162  }
3163 #endif
3164  iks_disconnect(client->p);
3165  iks_parser_delete(client->p);
3167  }
3168 
3169  return 1;
3170 }
3171 
3172 /*!
3173  * \brief Callback function for MWI events
3174  * \param ast_event
3175  * \param data void pointer to ast_client structure
3176  * \return void
3177  */
3178 static void aji_mwi_cb(const struct ast_event *ast_event, void *data)
3179 {
3180  const char *mailbox;
3181  const char *context;
3182  char oldmsgs[10];
3183  char newmsgs[10];
3184  struct aji_client *client;
3186  {
3187  /* If the event didn't originate from this server, don't send it back out. */
3188  ast_log(LOG_DEBUG, "Returning here\n");
3189  return;
3190  }
3191 
3192  client = ASTOBJ_REF((struct aji_client *) data);
3193  mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
3194  context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
3195  snprintf(oldmsgs, sizeof(oldmsgs), "%u",
3197  snprintf(newmsgs, sizeof(newmsgs), "%u",
3199  aji_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
3201 
3202 }
3203 /*!
3204  * \brief Callback function for device state events
3205  * \param ast_event
3206  * \param data void pointer to ast_client structure
3207  * \return void
3208  */
3209 static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
3210 {
3211  const char *device;
3212  const char *device_state;
3213  unsigned int cachable;
3214  struct aji_client *client;
3216  {
3217  /* If the event didn't originate from this server, don't send it back out. */
3218  ast_log(LOG_DEBUG, "Returning here\n");
3219  return;
3220  }
3221 
3222  client = ASTOBJ_REF((struct aji_client *) data);
3223  device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
3224  device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
3225  cachable = ast_event_get_ie_uint(ast_event, AST_EVENT_IE_CACHABLE);
3226  aji_publish_device_state(client, device, device_state, cachable);
3228 }
3229 
3230 /*!
3231  * \brief Initialize collections for event distribution
3232  * \param client the configured XMPP client we use to connect to a XMPP server
3233  * \return void
3234  */
3235 static void aji_init_event_distribution(struct aji_client *client)
3236 {
3237  if (!mwi_sub) {
3238  mwi_sub = ast_event_subscribe(AST_EVENT_MWI, aji_mwi_cb, "aji_mwi_subscription",
3239  client, AST_EVENT_IE_END);
3240  }
3241  if (!device_state_sub) {
3243  return;
3244  }
3246  aji_devstate_cb, "aji_devstate_subscription", client, AST_EVENT_IE_END);
3247  ast_event_dump_cache(device_state_sub);
3248  }
3249 
3250  aji_pubsub_subscribe(client, "device_state");
3251  aji_pubsub_subscribe(client, "message_waiting");
3252  iks_filter_add_rule(client->f, aji_handle_pubsub_event, client, IKS_RULE_TYPE,
3253  IKS_PAK_MESSAGE, IKS_RULE_FROM, client->pubsub_node, IKS_RULE_DONE);
3254  iks_filter_add_rule(client->f, aji_handle_pubsub_error, client, IKS_RULE_TYPE,
3255  IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
3256 
3257 }
3258 
3259 /*!
3260  * \brief Callback for handling PubSub events
3261  * \param data void pointer to aji_client structure
3262  * \return IKS_FILTER_EAT
3263  */
3264 static int aji_handle_pubsub_event(void *data, ikspak *pak)
3265 {
3266  char *item_id, *device_state, *mailbox, *cachable_str;
3267  int oldmsgs, newmsgs;
3268  iks *item, *item_content;
3269  struct ast_eid pubsub_eid;
3270  struct ast_event *event;
3271  unsigned int cachable = AST_DEVSTATE_CACHABLE;
3272 
3273  item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
3274  if (!item) {
3275  ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
3276  return IKS_FILTER_EAT;
3277  }
3278  item_id = iks_find_attrib(item, "id");
3279  item_content = iks_child(item);
3280  ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
3281  if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
3282  ast_log(LOG_DEBUG, "Returning here, eid of incoming event matches ours!\n");
3283  return IKS_FILTER_EAT;
3284  }
3285  if (!strcasecmp(iks_name(item_content), "state")) {
3286  if ((cachable_str = iks_find_attrib(item_content, "cachable"))) {
3287  sscanf(cachable_str, "%30u", &cachable);
3288  }
3289  device_state = iks_find_cdata(item, "state");
3293  AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
3295  AST_EVENT_IE_END))) {
3296  return IKS_FILTER_EAT;
3297  }
3298  } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
3299  mailbox = strsep(&item_id, "@");
3300  sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
3301  sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
3302  if (!(event = ast_event_new(AST_EVENT_MWI,
3307  AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
3308  AST_EVENT_IE_END))) {
3309  return IKS_FILTER_EAT;
3310  }
3311  } else {
3312  ast_log(LOG_DEBUG, "Don't know how to handle PubSub event of type %s\n",
3313  iks_name(item_content));
3314  return IKS_FILTER_EAT;
3315  }
3316 
3317  if (cachable == AST_DEVSTATE_CACHABLE) {
3319  } else {
3320  ast_event_queue(event);
3321  }
3322 
3323  return IKS_FILTER_EAT;
3324 }
3325 
3326 /*!
3327  * \brief Add Owner affiliations for pubsub node
3328  * \param client the configured XMPP client we use to connect to a XMPP server
3329  * \param node the name of the node to which to add affiliations
3330  * \return void
3331  */
3332 static void aji_create_affiliations(struct aji_client *client, const char *node)
3333 {
3334  iks *modify_affiliates = aji_pubsub_iq_create(client, "set");
3335  iks *pubsub, *affiliations, *affiliate;
3336  pubsub = iks_insert(modify_affiliates, "pubsub");
3337  iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
3338  affiliations = iks_insert(pubsub, "affiliations");
3339  iks_insert_attrib(affiliations, "node", node);
3340  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3341  ASTOBJ_RDLOCK(iterator);
3342  affiliate = iks_insert(affiliations, "affiliation");
3343  iks_insert_attrib(affiliate, "jid", iterator->name);
3344  iks_insert_attrib(affiliate, "affiliation", "owner");
3345  ASTOBJ_UNLOCK(iterator);
3346  });
3347  ast_aji_send(client, modify_affiliates);
3348  iks_delete(modify_affiliates);
3349 }
3350 
3351 /*!
3352  * \brief Subscribe to a PubSub node
3353  * \param client the configured XMPP client we use to connect to a XMPP server
3354  * \param node the name of the node to which to subscribe
3355  * \return void
3356  */
3357 static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
3358 {
3359  iks *request = aji_pubsub_iq_create(client, "set");
3360  iks *pubsub, *subscribe;
3361 
3362  pubsub = iks_insert(request, "pubsub");
3363  iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3364  subscribe = iks_insert(pubsub, "subscribe");
3365  iks_insert_attrib(subscribe, "jid", client->jid->partial);
3366  iks_insert_attrib(subscribe, "node", node);
3367  if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3368  iks *options, *x, *sub_options, *sub_type, *sub_depth;
3369  options = iks_insert(pubsub, "options");
3370  x = iks_insert(options, "x");
3371  iks_insert_attrib(x, "xmlns", "jabber:x:data");
3372  iks_insert_attrib(x, "type", "submit");
3373  sub_options = iks_insert(x, "field");
3374  iks_insert_attrib(sub_options, "var", "FORM_TYPE");
3375  iks_insert_attrib(sub_options, "type", "hidden");
3376  iks_insert_cdata(iks_insert(sub_options, "value"),
3377  "http://jabber.org/protocol/pubsub#subscribe_options", 51);
3378  sub_type = iks_insert(x, "field");
3379  iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
3380  iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
3381  sub_depth = iks_insert(x, "field");
3382  iks_insert_attrib(sub_type, "var", "pubsub#subscription_depth");
3383  iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
3384  }
3385  ast_aji_send(client, request);
3386  iks_delete(request);
3387 }
3388 
3389 /*!
3390  * \brief Build the skeleton of a publish
3391  * \param client the configured XMPP client we use to connect to a XMPP server
3392  * \param node Name of the node that will be published to
3393  * \param event_type
3394  * \return iks *
3395  */
3396 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
3397  const char *event_type, unsigned int cachable)
3398 {
3399  iks *request = aji_pubsub_iq_create(client, "set");
3400  iks *pubsub, *publish, *item;
3401  pubsub = iks_insert(request, "pubsub");
3402  iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3403  publish = iks_insert(pubsub, "publish");
3404  if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3405  iks_insert_attrib(publish, "node", node);
3406  } else {
3407  iks_insert_attrib(publish, "node", event_type);
3408  }
3409  item = iks_insert(publish, "item");
3410  iks_insert_attrib(item, "id", node);
3411 
3412  if (cachable == AST_DEVSTATE_NOT_CACHABLE) {
3413  iks *options, *x, *field_form_type, *field_persist;
3414 
3415  options = iks_insert(pubsub, "publish-options");
3416  x = iks_insert(options, "x");
3417  iks_insert_attrib(x, "xmlns", "jabber:x:data");
3418  iks_insert_attrib(x, "type", "submit");
3419  field_form_type = iks_insert(x, "field");
3420  iks_insert_attrib(field_form_type, "var", "FORM_TYPE");
3421  iks_insert_attrib(field_form_type, "type", "hidden");
3422  iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options", 0);
3423  field_persist = iks_insert(x, "field");
3424  iks_insert_attrib(field_persist, "var", "pubsub#persist_items");
3425  iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1);
3426  }
3427 
3428  return item;
3429 }
3430 
3431 /*!
3432  * \brief Publish device state to a PubSub node
3433  * \param client the configured XMPP client we use to connect to a XMPP server
3434  * \param device the name of the device whose state to publish
3435  * \param device_state the state to publish
3436  * \return void
3437  */
3438 static void aji_publish_device_state(struct aji_client *client, const char *device,
3439  const char *device_state, unsigned int cachable)
3440 {
3441  iks *request = aji_build_publish_skeleton(client, device, "device_state", cachable);
3442  iks *state;
3443  char eid_str[20], cachable_str[2];
3444  if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
3445  if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3446  aji_create_pubsub_node(client, "leaf", device, "device_state");
3447  } else {
3448  aji_create_pubsub_node(client, NULL, device, NULL);
3449  }
3450  }
3451  ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3452  state = iks_insert(request, "state");
3453  iks_insert_attrib(state, "xmlns", "http://asterisk.org");
3454  iks_insert_attrib(state, "eid", eid_str);
3455  snprintf(cachable_str, sizeof(cachable_str), "%u", cachable);
3456  iks_insert_attrib(state, "cachable", cachable_str);
3457  iks_insert_cdata(state, device_state, strlen(device_state));
3458  ast_aji_send(client, iks_root(request));
3459  iks_delete(request);
3460 }
3461 
3462 /*!
3463  * \brief Publish MWI to a PubSub node
3464  * \param client the configured XMPP client we use to connect to a XMPP server
3465  * \param device the name of the device whose state to publish
3466  * \param device_state the state to publish
3467  * \return void
3468  */
3469 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
3470  const char *context, const char *oldmsgs, const char *newmsgs)
3471 {
3472  char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT];
3473  char eid_str[20];
3474  iks *mailbox_node, *request;
3475  snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
3476  request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting", 1);
3477  ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3478  mailbox_node = iks_insert(request, "mailbox");
3479  iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
3480  iks_insert_attrib(mailbox_node, "eid", eid_str);
3481  iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
3482  iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
3483  ast_aji_send(client, iks_root(request));
3484  iks_delete(request);
3485 }
3486 
3487 /*!
3488  * \brief Create an IQ packet
3489  * \param client the configured XMPP client we use to connect to a XMPP server
3490  * \param type the type of IQ packet to create
3491  * \return iks*
3492  */
3493 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type)
3494 {
3495  iks *request = iks_new("iq");
3496 
3497  iks_insert_attrib(request, "to", client->pubsub_node);
3498  iks_insert_attrib(request, "from", client->jid->full);
3499  iks_insert_attrib(request, "type", type);
3500  ast_aji_increment_mid(client->mid);
3501  iks_insert_attrib(request, "id", client->mid);
3502  return request;
3503 }
3504 
3505 static int aji_handle_pubsub_error(void *data, ikspak *pak)
3506 {
3507  char *node_name;
3508  char *error;
3509  int error_num;
3510  iks *orig_request;
3511  iks *orig_pubsub = iks_find(pak->x, "pubsub");
3512  struct aji_client *client;
3513  if (!orig_pubsub) {
3514  ast_debug(1, "Error isn't a PubSub error, why are we here?\n");
3515  return IKS_FILTER_EAT;
3516  }
3517  orig_request = iks_child(orig_pubsub);
3518  error = iks_find_attrib(iks_find(pak->x, "error"), "code");
3519  node_name = iks_find_attrib(orig_request, "node");
3520  if (!sscanf(error, "%30d", &error_num)) {
3521  return IKS_FILTER_EAT;
3522  }
3523  if (error_num > 399 && error_num < 500 && error_num != 404) {
3525  "Error performing operation on PubSub node %s, %s.\n", node_name, error);
3526  return IKS_FILTER_EAT;
3527  } else if (error_num > 499 && error_num < 600) {
3528  ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
3529  return IKS_FILTER_EAT;
3530  }
3531 
3532  client = ASTOBJ_REF((struct aji_client *) data);
3533 
3534  if (!strcasecmp(iks_name(orig_request), "publish")) {
3535  iks *request;
3536  if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3537  if (iks_find(iks_find(orig_request, "item"), "state")) {
3538  aji_create_pubsub_leaf(client, "device_state", node_name);
3539  } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
3540  aji_create_pubsub_leaf(client, "message_waiting", node_name);
3541  }
3542  } else {
3543  aji_create_pubsub_node(client, NULL, node_name, NULL);
3544  }
3545  request = aji_pubsub_iq_create(client, "set");
3546  iks_insert_node(request, orig_pubsub);
3547  ast_aji_send(client, request);
3548  iks_delete(request);
3550  return IKS_FILTER_EAT;
3551  } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
3552  if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3553  aji_create_pubsub_collection(client, node_name);
3554  } else {
3555  aji_create_pubsub_node(client, NULL, node_name, NULL);
3556  }
3557  }
3559  return IKS_FILTER_EAT;
3560 }
3561 
3562 /*!
3563  * \brief Request item list from pubsub
3564  * \param client the configured XMPP client we use to connect to a XMPP server
3565  * \param collection name of the collection for request
3566  * \return void
3567  */
3568 static void aji_request_pubsub_nodes(struct aji_client *client, const char *collection)
3569 {
3570  iks *request = aji_build_node_request(client, collection);
3571 
3572  iks_filter_add_rule(client->f, aji_receive_node_list, client, IKS_RULE_TYPE,
3573  IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
3574  IKS_RULE_DONE);
3575  ast_aji_send(client, request);
3576  iks_delete(request);
3577 
3578 }
3579 
3580 /*!
3581  * \brief Build the a node request
3582  * \param client the configured XMPP client we use to connect to a XMPP server
3583  * \param collection name of the collection for request
3584  * \return iks*
3585  */
3586 static iks* aji_build_node_request(struct aji_client *client, const char *collection)
3587 {
3588  iks *request = aji_pubsub_iq_create(client, "get");
3589  iks *query;
3590  query = iks_insert(request, "query");
3591  iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
3592  if (collection) {
3593  iks_insert_attrib(query, "node", collection);
3594  }
3595  return request;
3596 }
3597 
3598 /*!
3599  * \brief Receive pubsub item lists
3600  * \param data pointer to aji_client structure
3601  * \param pak response from pubsub diso#items query
3602  * \return IKS_FILTER_EAT
3603  */
3604 static int aji_receive_node_list(void *data, ikspak* pak)
3605 {
3606 
3607  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3608  iks *item = NULL;
3609  if (iks_has_children(pak->query)) {
3610  item = iks_first_tag(pak->query);
3611  ast_verbose("Connection %s: %s\nNode name: %s\n", client->name, client->jid->partial,
3612  iks_find_attrib(item, "node"));
3613  while ((item = iks_next_tag(item))) {
3614  ast_verbose("Node name: %s\n", iks_find_attrib(item, "node"));
3615  }
3616  }
3617  if (item) {
3618  iks_delete(item);
3619  }
3621  return IKS_FILTER_EAT;
3622 }
3623 
3624 
3625 /*!
3626  * \brief Method to expose PubSub node list via CLI.
3627  * \param e pointer to ast_cli_entry structure
3628  * \param cmd
3629  * \param a pointer to ast_cli_args structure
3630  * \return char *
3631  */
3632 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
3633 ast_cli_args *a)
3634 {
3635  struct aji_client *client;
3636  const char *name = NULL;
3637  const char *collection = NULL;
3638 
3639  switch (cmd) {
3640  case CLI_INIT:
3641  e->command = "jabber list nodes";
3642  e->usage =
3643  "Usage: jabber list nodes <connection> [collection]\n"
3644  " Lists the user's nodes on the respective connection\n"
3645  " ([connection] as configured in jabber.conf.)\n";
3646  return NULL;
3647  case CLI_GENERATE:
3648  return NULL;
3649  }
3650 
3651  if (a->argc > 5 || a->argc < 4) {
3652  return CLI_SHOWUSAGE;
3653  } else if (a->argc == 4 || a->argc == 5) {
3654  name = a->argv[3];
3655  }
3656  if (a->argc == 5) {
3657  collection = a->argv[4];
3658  }
3659  if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
3660  ast_cli(a->fd, "Unable to find client '%s'!\n", name);
3661  return CLI_FAILURE;
3662  }
3663 
3664  ast_cli(a->fd, "Listing pubsub nodes.\n");
3665  aji_request_pubsub_nodes(client, collection);
3667  return CLI_SUCCESS;
3668 }
3669 
3670 /*!
3671  * \brief Method to purge PubSub nodes via CLI.
3672  * \param e pointer to ast_cli_entry structure
3673  * \param cmd
3674  * \param a pointer to ast_cli_args structure
3675  * \return char *
3676  */
3677 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
3678  ast_cli_args *a)
3679 {
3680  struct aji_client *client;
3681  const char *name;
3682 
3683  switch (cmd) {
3684  case CLI_INIT:
3685  e->command = "jabber purge nodes";
3686  e->usage =
3687  "Usage: jabber purge nodes <connection> <node>\n"
3688  " Purges nodes on PubSub server\n"
3689  " as configured in jabber.conf.\n";
3690  return NULL;
3691  case CLI_GENERATE:
3692  return NULL;
3693  }
3694 
3695  if (a->argc != 5) {
3696  return CLI_SHOWUSAGE;
3697  }
3698  name = a->argv[3];
3699 
3700  if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
3701  ast_cli(a->fd, "Unable to find client '%s'!\n", name);
3702  return CLI_FAILURE;
3703  }
3704  if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3705  aji_pubsub_purge_nodes(client, a->argv[4]);
3706  } else {
3707  aji_delete_pubsub_node(client, a->argv[4]);
3708  }
3710  return CLI_SUCCESS;
3711 }
3712 
3713 static void aji_pubsub_purge_nodes(struct aji_client *client, const char* collection_name)
3714 {
3715  iks *request = aji_build_node_request(client, collection_name);
3716  ast_aji_send(client, request);
3717  iks_filter_add_rule(client->f, aji_delete_node_list, client, IKS_RULE_TYPE,
3718  IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
3719  IKS_RULE_DONE);
3720  ast_aji_send(client, request);
3721  iks_delete(request);
3722 }
3723 
3724 /*!
3725  * \brief Delete pubsub item lists
3726  * \param data pointer to aji_client structure
3727  * \param pak response from pubsub diso#items query
3728  * \return IKS_FILTER_EAT
3729  */
3730 static int aji_delete_node_list(void *data, ikspak* pak)
3731 {
3732 
3733  struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3734  iks *item = NULL;
3735  if (iks_has_children(pak->query)) {
3736  item = iks_first_tag(pak->query);
3737  ast_log(LOG_WARNING, "Connection: %s Node name: %s\n", client->jid->partial,
3738  iks_find_attrib(item, "node"));
3739  while ((item = iks_next_tag(item))) {
3740  aji_delete_pubsub_node(client, iks_find_attrib(item, "node"));
3741  }
3742  }
3743  if (item) {
3744  iks_delete(item);
3745  }
3746  return IKS_FILTER_EAT;
3747 }
3748 
3749 
3750 /*!
3751  * \brief Method to expose PubSub node deletion via CLI.
3752  * \param e pointer to ast_cli_entry structure
3753  * \param cmd
3754  * \param a pointer to ast_cli_args structure
3755  * \return char *
3756  */
3757 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
3758  ast_cli_args *a)
3759 {
3760  struct aji_client *client;
3761  const char *name;
3762 
3763  switch (cmd) {
3764  case CLI_INIT:
3765  e->command = "jabber delete node";
3766  e->usage =
3767  "Usage: jabber delete node <connection> <node>\n"
3768  " Deletes a node on PubSub server\n"
3769  " as configured in jabber.conf.\n";
3770  return NULL;
3771  case CLI_GENERATE:
3772  return NULL;
3773  }
3774 
3775  if (a->argc != 5) {
3776  return CLI_SHOWUSAGE;
3777  }
3778  name = a->argv[3];
3779 
3780  if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
3781  ast_cli(a->fd, "Unable to find client '%s'!\n", name);
3782  return CLI_FAILURE;
3783  }
3784  aji_delete_pubsub_node(client, a->argv[4]);
3786  return CLI_SUCCESS;
3787 }
3788 
3789 /*!
3790  * \brief Delete a PubSub node
3791  * \param client the configured XMPP client we use to connect to a XMPP server
3792  * \param node_name the name of the node to delete
3793  * return void
3794  */
3795 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name)
3796 {
3797  iks *request = aji_pubsub_iq_create(client, "set");
3798  iks *pubsub, *delete;
3799  pubsub = iks_insert(request, "pubsub");
3800  iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
3801  delete = iks_insert(pubsub, "delete");
3802  iks_insert_attrib(delete, "node", node_name);
3803  ast_aji_send(client, request);
3804 }
3805 
3806 /*!
3807  * \brief Create a PubSub collection node.
3808  * \param client the configured XMPP client we use to connect to a XMPP server
3809  * \param collection_name The name to use for this collection
3810  * \return void.
3811  */
3812 static void aji_create_pubsub_collection(struct aji_client *client, const char
3813 *collection_name)
3814 {
3815  aji_create_pubsub_node(client, "collection", collection_name, NULL);
3816 }
3817 
3818 
3819 /*!
3820  * \brief Create a PubSub leaf node.
3821  * \param client the configured XMPP client we use to connect to a XMPP server
3822  * \param leaf_name The name to use for this collection
3823  * \return void.
3824  */
3825 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
3826 const char *leaf_name)
3827 {
3828  aji_create_pubsub_node(client, "leaf", leaf_name, collection_name);
3829 }
3830 
3831 /*!
3832  * \brief Create a pubsub node
3833  * \param client the configured XMPP client we use to connect to a XMPP server
3834  * \param node_type the type of node to create
3835  * \param name the name of the node to create
3836  * \return iks*
3837  */
3838 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type, const
3839  char *name, const char *collection_name)
3840 {
3841  iks *node = aji_pubsub_iq_create(client, "set");
3842  iks *pubsub, *create;
3843  pubsub = iks_insert(node, "pubsub");
3844  iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3845  create = iks_insert(pubsub, "create");
3846  iks_insert_attrib(create, "node", name);
3847  aji_build_node_config(pubsub, node_type, collection_name);
3848  ast_aji_send(client, node);
3849  aji_create_affiliations(client, name);
3850  iks_delete(node);
3851  return 0;
3852 }
3853 
3854 
3855 
3856 static iks* aji_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
3857 {
3858  iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
3859  *field_deliver_payload, *field_persist_items, *field_access_model,
3860  *field_pubsub_collection;
3861  configure = iks_insert(pubsub, "configure");
3862  x = iks_insert(configure, "x");
3863  iks_insert_attrib(x, "xmlns", "jabber:x:data");
3864  iks_insert_attrib(x, "type", "submit");
3865  field_owner = iks_insert(x, "field");
3866  iks_insert_attrib(field_owner, "var", "FORM_TYPE");
3867  iks_insert_attrib(field_owner, "type", "hidden");
3868  iks_insert_cdata(iks_insert(field_owner, "value"),
3869  "http://jabber.org/protocol/pubsub#owner", 39);
3870  if (node_type) {
3871  field_node_type = iks_insert(x, "field");
3872  iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
3873  iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
3874  }
3875  field_node_config = iks_insert(x, "field");
3876  iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
3877  iks_insert_attrib(field_node_config, "type", "hidden");
3878  iks_insert_cdata(iks_insert(field_node_config, "value"),
3879  "http://jabber.org/protocol/pubsub#node_config", 45);
3880  field_deliver_payload = iks_insert(x, "field");
3881  iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
3882  iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
3883  field_persist_items = iks_insert(x, "field");
3884  iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
3885  iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
3886  field_access_model = iks_insert(x, "field");
3887  iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
3888  iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
3889  if (node_type && !strcasecmp(node_type, "leaf")) {
3890  field_pubsub_collection = iks_insert(x, "field");
3891  iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
3892  iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
3893  strlen(collection_name));
3894  }
3895  return configure;
3896 }
3897 
3898 
3899 
3900 /*!
3901  * \brief Method to expose PubSub collection node creation via CLI.
3902  * \return char *.
3903  */
3904 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3905 {
3906  struct aji_client *client;
3907  const char *name;
3908  const char *collection_name;
3909 
3910  switch (cmd) {
3911  case CLI_INIT:
3912  e->command = "jabber create collection";
3913  e->usage =
3914  "Usage: jabber create collection <connection> <collection>\n"
3915  " Creates a PubSub collection node using the account\n"
3916  " as configured in jabber.conf.\n";
3917  return NULL;
3918  case CLI_GENERATE:
3919  return NULL;
3920  }
3921 
3922  if (a->argc != 5) {
3923  return CLI_SHOWUSAGE;
3924  }
3925  name = a->argv[3];
3926  collection_name = a->argv[4];
3927 
3928  if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
3929  ast_cli(a->fd, "Unable to find client '%s'!\n", name);
3930  return CLI_FAILURE;
3931  }
3932 
3933  ast_cli(a->fd, "Creating test PubSub node collection.\n");
3934  aji_create_pubsub_collection(client, collection_name);
3936  return CLI_SUCCESS;
3937 }
3938 
3939 /*!
3940  * \brief Method to expose PubSub leaf node creation via CLI.
3941  * \return char *.
3942  */
3943 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3944 {
3945  struct aji_client *client;
3946  const char *name;
3947  const char *collection_name;
3948  const char *leaf_name;
3949 
3950  switch (cmd) {
3951  case CLI_INIT:
3952  e->command = "jabber create leaf";
3953  e->usage =
3954  "Usage: jabber create leaf <connection> <collection> <leaf>\n"
3955  " Creates a PubSub leaf node using the account\n"
3956  " as configured in jabber.conf.\n";
3957  return NULL;
3958  case CLI_GENERATE:
3959  return NULL;
3960  }
3961 
3962  if (a->argc != 6) {
3963  return CLI_SHOWUSAGE;
3964  }
3965  name = a->argv[3];
3966  collection_name = a->argv[4];
3967  leaf_name = a->argv[5];
3968 
3969  if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
3970  ast_cli(a->fd, "Unable to find client '%s'!\n", name);
3971  return CLI_FAILURE;
3972  }
3973 
3974  ast_cli(a->fd, "Creating test PubSub node collection.\n");
3975  aji_create_pubsub_leaf(client, collection_name, leaf_name);
3977  return CLI_SUCCESS;
3978 }
3979 
3980 
3981 
3982 /*!
3983  * \internal
3984  * \brief set presence of client.
3985  * \param client the configured XMPP client we use to connect to a XMPP server
3986  * \param to user send it to
3987  * \param from user it came from
3988  * \param level
3989  * \param desc
3990  * \return void.
3991  */
3992 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
3993 {
3994  iks *presence = iks_make_pres(level, desc);
3995  iks *cnode = iks_new("c");
3996  iks *priority = iks_new("priority");
3997  char priorityS[10];
3998 
3999  if (presence && cnode && client && priority) {
4000  if (to) {
4001  iks_insert_attrib(presence, "to", to);
4002  }
4003  if (from) {
4004  iks_insert_attrib(presence, "from", from);
4005  }
4006  snprintf(priorityS, sizeof(priorityS), "%d", client->priority);
4007  iks_insert_cdata(priority, priorityS, strlen(priorityS));
4008  iks_insert_node(presence, priority);
4009  iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
4010  iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
4011  iks_insert_attrib(cnode, "ext", "voice-v1");
4012  iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
4013  iks_insert_node(presence, cnode);
4014  ast_aji_send(client, presence);
4015  } else {
4016  ast_log(LOG_ERROR, "Out of memory.\n");
4017  }
4018 
4019  iks_delete(cnode);
4020  iks_delete(presence);
4021  iks_delete(priority);
4022 }
4023 
4024 /*
4025 * \brief set the presence of the client in a groupchat context.
4026 * \param client the configured XMPP client we use to connect to a XMPP server
4027 * \param room the groupchat identifier in the from roomname@service
4028 * \param from user it came from
4029 * \param level the type of action, i.e. join or leave the chatroom
4030 * \param nick the nickname to use in the chatroom
4031 * \param desc a text that details the action to be taken
4032 * \return res.
4033 */
4034 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc)
4035 {
4036  int res = 0;
4037  iks *presence = NULL, *x = NULL;
4038  char from[AJI_MAX_JIDLEN];
4039  char roomid[AJI_MAX_JIDLEN];
4040 
4041  presence = iks_make_pres(level, NULL);
4042  x = iks_new("x");
4043 
4044  if (client->component) {
4045  snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
4046  snprintf(roomid, AJI_MAX_JIDLEN, "%s/%s", room, nick);
4047  } else {
4048  snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
4049  snprintf(roomid, AJI_MAX_JIDLEN, "%s/%s", room, nick ? nick : client->jid->user);
4050  }
4051 
4052  if (!presence || !x || !client) {
4053  ast_log(LOG_ERROR, "Out of memory.\n");
4054  res = -1;
4055  goto safeout;
4056  } else {
4057  iks_insert_attrib(presence, "to", roomid);
4058  iks_insert_attrib(presence, "from", from);
4059  iks_insert_attrib(x, "xmlns", MUC_NS);
4060  iks_insert_node(presence, x);
4061  res = ast_aji_send(client, presence);
4062  }
4063 
4064 safeout:
4065  iks_delete(presence);
4066  iks_delete(x);
4067  return res;
4068 }
4069 
4070 /*!
4071  * \internal
4072  * \brief Turn on/off console debugging.
4073  * \return CLI_SUCCESS.
4074  */
4075 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4076 {
4077  switch (cmd) {
4078  case CLI_INIT:
4079  e->command = "jabber set debug {on|off}";
4080  e->usage =
4081  "Usage: jabber set debug {on|off}\n"
4082  " Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
4083  return NULL;
4084  case CLI_GENERATE:
4085  return NULL;
4086  }
4087 
4088  if (a->argc != e->args) {
4089  return CLI_SHOWUSAGE;
4090  }
4091 
4092  if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
4094  ASTOBJ_RDLOCK(iterator);
4095  iterator->debug = 1;
4096  ASTOBJ_UNLOCK(iterator);
4097  });
4098  ast_cli(a->fd, "Jabber Debugging Enabled.\n");
4099  return CLI_SUCCESS;
4100  } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
4102  ASTOBJ_RDLOCK(iterator);
4103  iterator->debug = 0;
4104  ASTOBJ_UNLOCK(iterator);
4105  });
4106  ast_cli(a->fd, "Jabber Debugging Disabled.\n");
4107  return CLI_SUCCESS;
4108  }
4109  return CLI_SHOWUSAGE; /* defaults to invalid */
4110 }
4111 
4112 /*!
4113  * \internal
4114  * \brief Reload jabber module.
4115  * \return CLI_SUCCESS.
4116  */
4117 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4118 {
4119  switch (cmd) {
4120  case CLI_INIT:
4121  e->command = "jabber reload";
4122  e->usage =
4123  "Usage: jabber reload\n"
4124  " Reloads the Jabber module.\n";
4125  return NULL;
4126  case CLI_GENERATE:
4127  return NULL;
4128  }
4129 
4130  aji_reload(1);
4131  ast_cli(a->fd, "Jabber Reloaded.\n");
4132  return CLI_SUCCESS;
4133 }
4134 
4135 /*!
4136  * \internal
4137  * \brief Show client status.
4138  * \return CLI_SUCCESS.
4139  */
4140 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4141 {
4142  char *status;
4143  int count = 0;
4144 
4145  switch (cmd) {
4146  case CLI_INIT:
4147  e->command = "jabber show connections";
4148  e->usage =
4149  "Usage: jabber show connections\n"
4150  " Shows state of client and component connections\n";
4151  return NULL;
4152  case CLI_GENERATE:
4153  return NULL;
4154  }
4155 
4156  ast_cli(a->fd, "Jabber Users and their status:\n");
4158  ASTOBJ_RDLOCK(iterator);
4159  count++;
4160  switch (iterator->state) {
4161  case AJI_DISCONNECTED:
4162  status = "Disconnected";
4163  break;
4164  case AJI_CONNECTING:
4165  status = "Connecting";
4166  break;
4167  case AJI_CONNECTED:
4168  status = "Connected";
4169  break;
4170  default:
4171  status = "Unknown";
4172  }
4173  ast_cli(a->fd, " [%s] %s - %s\n", iterator->name, iterator->user, status);
4174  ASTOBJ_UNLOCK(iterator);
4175  });
4176  ast_cli(a->fd, "----\n");
4177  ast_cli(a->fd, " Number of users: %d\n", count);
4178  return CLI_SUCCESS;
4179 }
4180 
4181 /*!
4182  * \internal
4183  * \brief Show buddy lists
4184  * \return CLI_SUCCESS.
4185  */
4186 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
4187 {
4188  struct aji_resource *resource;
4189  struct aji_client *client;
4190 
4191  switch (cmd) {
4192  case CLI_INIT:
4193  e->command = "jabber show buddies";
4194  e->usage =
4195  "Usage: jabber show buddies\n"
4196  " Shows buddy lists of our clients\n";
4197  return NULL;
4198  case CLI_GENERATE:
4199  return NULL;
4200  }
4201 
4202  ast_cli(a->fd, "Jabber buddy lists\n");
4204  ast_cli(a->fd, "Client: %s\n", iterator->user);
4205  client = iterator;
4206  ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
4207  ASTOBJ_RDLOCK(iterator);
4208  ast_cli(a->fd, "\tBuddy:\t%s\n", iterator->name);
4209  if (!iterator->resources)
4210  ast_cli(a->fd, "\t\tResource: None\n");
4211  for (resource = iterator->resources; resource; resource = resource->next) {
4212  ast_cli(a->fd, "\t\tResource: %s\n", resource->resource);
4213  if (resource->cap) {
4214  ast_cli(a->fd, "\t\t\tnode: %s\n", resource->cap->parent->node);
4215  ast_cli(a->fd, "\t\t\tversion: %s\n", resource->cap->version);
4216  ast_cli(a->fd, "\t\t\tJingle capable: %s\n", resource->cap->jingle ? "yes" : "no");
4217  }
4218  ast_cli(a->fd, "\t\tStatus: %d\n", resource->status);
4219  ast_cli(a->fd, "\t\tPriority: %d\n", resource->priority);
4220  }
4221  ASTOBJ_UNLOCK(iterator);
4222  });
4223  iterator = client;
4224  });
4225  return CLI_SUCCESS;
4226 }
4227 
4228 /*!
4229  * \internal
4230  * \brief creates aji_client structure.
4231  * \param label
4232  * \param var ast_variable
4233  * \param debug
4234  * \return 0.
4235  */
4236 static int aji_create_client(char *label, struct ast_variable *var, int debug)
4237 {
4238  char *resource;
4239  struct aji_client *client = NULL;
4240  int flag = 0;
4241 
4242  client = ASTOBJ_CONTAINER_FIND(&clients, label);
4243  if (!client) {
4244  flag = 1;
4245  client = ast_calloc(1, sizeof(*client));
4246  if (!client) {
4247  ast_log(LOG_ERROR, "Out of memory!\n");
4248  return 0;
4249  }
4250  ASTOBJ_INIT(client);
4251  ASTOBJ_WRLOCK(client);
4252  ASTOBJ_CONTAINER_INIT(&client->buddies);
4253  } else {
4254  ASTOBJ_WRLOCK(client);
4255  ASTOBJ_UNMARK(client);
4256  }
4258  ast_copy_string(client->name, label, sizeof(client->name));
4259  ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
4260 
4261  /* Set default values for the client object */
4262  client->debug = debug;
4263  ast_copy_flags(&client->flags, &globalflags, AST_FLAGS_ALL);
4264  client->port = 5222;
4265  client->usetls = 1;
4266  client->usesasl = 1;
4267  client->forcessl = 0;
4268  client->keepalive = 1;
4269  client->timeout = 50;
4270  client->message_timeout = 5;
4271  client->distribute_events = 0;
4272  AST_LIST_HEAD_INIT(&client->messages);
4273  client->component = 0;
4274  ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage));
4275  client->priority = 0;
4276  client->status = IKS_SHOW_AVAILABLE;
4277 
4278  if (flag) {
4279  client->authorized = 0;
4280  client->state = AJI_DISCONNECTED;
4281  }
4282  while (var) {
4283  if (!strcasecmp(var->name, "username")) {
4284  ast_copy_string(client->user, var->value, sizeof(client->user));
4285  } else if (!strcasecmp(var->name, "serverhost")) {
4286  ast_copy_string(client->serverhost, var->value, sizeof(client->serverhost));
4287  } else if (!strcasecmp(var->name, "secret")) {
4288  ast_copy_string(client->password, var->value, sizeof(client->password));
4289  } else if (!strcasecmp(var->name, "statusmessage")) {
4290  ast_copy_string(client->statusmessage, var->value, sizeof(client->statusmessage));
4291  } else if (!strcasecmp(var->name, "port")) {
4292  client->port = atoi(var->value);
4293  } else if (!strcasecmp(var->name, "timeout")) {
4294  client->message_timeout = atoi(var->value);
4295  } else if (!strcasecmp(var->name, "debug")) {
4296  client->debug = (ast_false(var->value)) ? 0 : 1;
4297  } else if (!strcasecmp(var->name, "type")) {
4298  if (!strcasecmp(var->value, "component")) {
4299  client->component = 1;
4300  if (client->distribute_events) {
4301  ast_log(LOG_ERROR, "Client cannot be configured to be both a component and to distribute events! Event distribution will be disabled.\n");
4302  client->distribute_events = 0;
4303  }
4304  }
4305  } else if (!strcasecmp(var->name, "distribute_events")) {
4306  if (ast_true(var->value)) {
4307  if (client->component) {
4308  ast_log(LOG_ERROR, "Client cannot be configured to be both a component and to distribute events! Event distribution will be disabled.\n");
4309  } else {
4310  if (ast_test_flag(&pubsubflags, AJI_PUBSUB)) {
4311  ast_log(LOG_ERROR, "Only one connection can be configured for distributed events.\n");
4312  } else {
4313  ast_set_flag(&pubsubflags, AJI_PUBSUB);
4314  client->distribute_events = 1;
4315  }
4316  }
4317  }
4318  } else if (!strcasecmp(var->name, "pubsub_node")) {
4319  ast_copy_string(client->pubsub_node, var->value, sizeof(client->pubsub_node));
4320  } else if (!strcasecmp(var->name, "usetls")) {
4321  client->usetls = (ast_false(var->value)) ? 0 : 1;
4322  } else if (!strcasecmp(var->name, "usesasl")) {
4323  client->usesasl = (ast_false(var->value)) ? 0 : 1;
4324  } else if (!strcasecmp(var->name, "forceoldssl")) {
4325  client->forcessl = (ast_false(var->value)) ? 0 : 1;
4326  } else if (!strcasecmp(var->name, "keepalive")) {
4327  client->keepalive = (ast_false(var->value)) ? 0 : 1;
4328  } else if (!strcasecmp(var->name, "autoprune")) {
4329  ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOPRUNE);
4330  } else if (!strcasecmp(var->name, "autoregister")) {
4332  } else if (!strcasecmp(var->name, "auth_policy")) {
4333  if (!strcasecmp(var->value, "accept")) {
4334  ast_set_flag(&client->flags, AJI_AUTOACCEPT);
4335  } else {
4336  ast_clear_flag(&client->flags, AJI_AUTOACCEPT);
4337  }
4338  } else if (!strcasecmp(var->name, "buddy")) {
4339  aji_create_buddy((char *)var->value, client);
4340  } else if (!strcasecmp(var->name, "priority")) {
4341  client->priority = atoi(var->value);
4342  } else if (!strcasecmp(var->name, "status")) {
4343  if (!strcasecmp(var->value, "unavailable")) {
4344  client->status = IKS_SHOW_UNAVAILABLE;
4345  } else if (!strcasecmp(var->value, "available")
4346  || !strcasecmp(var->value, "online")) {
4347  client->status = IKS_SHOW_AVAILABLE;
4348  } else if (!strcasecmp(var->value, "chat")
4349  || !strcasecmp(var->value, "chatty")) {
4350  client->status = IKS_SHOW_CHAT;
4351  } else if (!strcasecmp(var->value, "away")) {
4352  client->status = IKS_SHOW_AWAY;
4353  } else if (!strcasecmp(var->value, "xa")
4354  || !strcasecmp(var->value, "xaway")) {
4355  client->status = IKS_SHOW_XA;
4356  } else if (!strcasecmp(var->value, "dnd")) {
4357  client->status = IKS_SHOW_DND;
4358  } else if (!strcasecmp(var->value, "invisible")) {
4359  #ifdef IKS_SHOW_INVISIBLE
4360  client->status = IKS_SHOW_INVISIBLE;
4361  #else
4362  ast_log(LOG_WARNING, "Your iksemel doesn't support invisible status: falling back to DND\n");
4363  client->status = IKS_SHOW_DND;
4364  #endif
4365  } else {
4366  ast_log(LOG_WARNING, "Unknown presence status: %s\n", var->value);
4367  }
4368  }
4369  /* no transport support in this version */
4370  /* else if (!strcasecmp(var->name, "transport"))
4371  aji_create_transport(var->value, client);
4372  */
4373  var = var->next;
4374  }
4375  if (!flag) {
4376  ASTOBJ_UNLOCK(client);
4378  return 1;
4379  }
4380 
4381  ast_copy_string(client->name_space, (client->component) ? "jabber:component:accept" : "jabber:client", sizeof(client->name_space));
4382  client->p = iks_stream_new(client->name_space, client, aji_act_hook);
4383  if (!client->p) {
4384  ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name);
4385  return 0;
4386  }
4387  client->stack = iks_stack_new(8192, 8192);
4388  if (!client->stack) {
4389  ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name);
4390  return 0;
4391  }
4392  client->f = iks_filter_new();
4393  if (!client->f) {
4394  ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name);
4395  return 0;
4396  }
4397  if (!strchr(client->user, '/') && !client->component) { /*client */
4398  if (ast_asprintf(&resource, "%s/asterisk", client->user) >= 0) {
4399  client->jid = iks_id_new(client->stack, resource);
4400  ast_free(resource);
4401  }
4402  } else {
4403  client->jid = iks_id_new(client->stack, client->user);
4404  }
4405  if (client->component) {
4406  iks_filter_add_rule(client->f, aji_dinfo_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
4407  iks_filter_add_rule(client->f, aji_ditems_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
4408  iks_filter_add_rule(client->f, aji_register_query_handler, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
4409  iks_filter_add_rule(client->f, aji_register_approve_handler, client, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
4410  } else {
4411  iks_filter_add_rule(client->f, aji_client_info_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
4412  }
4413 
4414  iks_set_log_hook(client->p, aji_log_hook);
4415  ASTOBJ_UNLOCK(client);
4416  ASTOBJ_CONTAINER_LINK(&clients, client);
4417  return 1;
4418 }
4419 
4420 
4421 
4422 #if 0
4423 /*!
4424  * \brief creates transport.
4425  * \param label, buddy to dump it into.
4426  * \return 0.
4427  */
4428 /* no connecting to transports today */
4429 static int aji_create_transport(char *label, struct aji_client *client)
4430 {
4431  char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
4432  struct aji_buddy *buddy = NULL;
4433  int needs_unref = 1;
4434 
4435  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
4436  if (!buddy) {
4437  needs_unref = 0;
4438  buddy = ast_calloc(1, sizeof(*buddy));
4439  if (!buddy) {
4440  ast_log(LOG_WARNING, "Out of memory\n");
4441  return 0;
4442  }
4443  ASTOBJ_INIT(buddy);
4444  }
4445  ASTOBJ_WRLOCK(buddy);
4446  server = label;
4447  if ((buddyname = strchr(label, ','))) {
4448  *buddyname = '\0';
4449  buddyname++;
4450  if (buddyname && buddyname[0] != '\0') {
4451  if ((user = strchr(buddyname, ','))) {
4452  *user = '\0';
4453  user++;
4454  if (user && user[0] != '\0') {
4455  if ((pass = strchr(user, ','))) {
4456  *pass = '\0';
4457  pass++;
4458  ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
4459  ast_copy_string(buddy->user, user, sizeof(buddy->user));
4460  ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
4461  ast_copy_string(buddy->server, server, sizeof(buddy->server));
4462  if (needs_unref) {
4463  ASTOBJ_UNMARK(buddy);
4465  }
4466  return 1;
4467  }
4468  }
4469  }
4470  }
4471  }
4472  ASTOBJ_UNLOCK(buddy);
4473  if (needs_unref) {
4474  ASTOBJ_UNMARK(buddy);
4476  } else {
4477  ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
4478  }
4479  return 0;
4480 }
4481 #endif
4482 
4483 /*!
4484  * \internal
4485  * \brief creates buddy.
4486  * \param label char.
4487  * \param client the configured XMPP client we use to connect to a XMPP server
4488  * \return 1 on success, 0 on failure.
4489  */
4490 static int aji_create_buddy(char *label, struct aji_client *client)
4491 {
4492  struct aji_buddy *buddy = NULL;
4493  int needs_unref = 1;
4494  buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, label);
4495  if (!buddy) {
4496  needs_unref = 0;
4497  buddy = ast_calloc(1, sizeof(*buddy));
4498  if (!buddy) {
4499  ast_log(LOG_WARNING, "Out of memory\n");
4500  return 0;
4501  }
4502  ASTOBJ_INIT(buddy);
4503  }
4504  ASTOBJ_WRLOCK(buddy);
4505  ast_copy_string(buddy->name, label, sizeof(buddy->name));
4506  ASTOBJ_UNLOCK(buddy);
4507  if (needs_unref) {
4508  ASTOBJ_UNMARK(buddy);
4510  } else {
4511  ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
4512  }
4513  return 1;
4514 }
4515 
4516 /*!< load config file. \return 1. */
4517 static int aji_load_config(int reload)
4518 {
4519  char *cat = NULL;
4520  int debug = 0;
4521  struct ast_config *cfg = NULL;
4522  struct ast_variable *var = NULL;
4523  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
4524 
4525  if ((cfg = ast_config_load(JABBER_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
4526  return -1;
4527  }
4528 
4529  /* Reset flags to default value */
4530  ast_set_flag(&globalflags, AJI_AUTOREGISTER | AJI_AUTOACCEPT);
4531 
4533  ast_log(LOG_WARNING, "No such configuration file %s\n", JABBER_CONFIG);
4534  return 0;
4535  }
4536 
4537  cat = ast_category_browse(cfg, NULL);
4538  for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
4539  if (!strcasecmp(var->name, "debug")) {
4540  debug = (ast_false(ast_variable_retrieve(cfg, "general", "debug"))) ? 0 : 1;
4541  } else if (!strcasecmp(var->name, "autoprune")) {
4542  ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOPRUNE);
4543  } else if (!strcasecmp(var->name, "autoregister")) {
4544  ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOREGISTER);
4545  } else if (!strcasecmp(var->name, "collection_nodes")) {
4546  ast_set2_flag(&pubsubflags, ast_true(var->value), AJI_XEP0248);
4547  } else if (!strcasecmp(var->name, "pubsub_autocreate")) {
4548  ast_set2_flag(&pubsubflags, ast_true(var->value), AJI_PUBSUB_AUTOCREATE);
4549  } else if (!strcasecmp(var->name, "auth_policy")) {
4550  if (!strcasecmp(var->value, "accept")) {
4551  ast_set_flag(&globalflags, AJI_AUTOACCEPT);
4552  } else {
4553  ast_clear_flag(&globalflags, AJI_AUTOACCEPT);
4554  }
4555  }
4556  }
4557 
4558  while (cat) {
4559  if (strcasecmp(cat, "general")) {
4560  var = ast_variable_browse(cfg, cat);
4561  aji_create_client(cat, var, debug);
4562  }
4563  cat = ast_category_browse(cfg, cat);
4564  }
4565  ast_config_destroy(cfg); /* or leak memory */
4566  return 1;
4567 }
4568 
4569 /*!
4570  * \brief grab a aji_client structure by label name or JID. Bumps the refcount.
4571  * (without the resource string)
4572  * \param name label or JID
4573  * \return aji_client.
4574  */
4575 struct aji_client *ast_aji_get_client(const char *name)
4576 {
4577  struct aji_client *client = NULL;
4578  char *aux = NULL;
4579 
4580  client = ASTOBJ_CONTAINER_FIND(&clients, name);
4581  if (!client && strchr(name, '@')) {
4583  aux = ast_strdupa(iterator->user);
4584  if (strchr(aux, '/')) {
4585  /* strip resource for comparison */
4586  aux = strsep(&aux, "/");
4587  }
4588  if (!strncasecmp(aux, name, strlen(aux))) {
4589  client = ASTOBJ_REF(iterator);
4590  }
4591  });
4592  }
4593 
4594  return client;
4595 }
4596 
4598 {
4599  return &clients;
4600 }
4601 
4602 /*!
4603  * \internal
4604  * \brief Send a Jabber Message via call from the Manager
4605  * \param s mansession Manager session
4606  * \param m message Message to send
4607  * \return 0
4608 */
4609 static int manager_jabber_send(struct mansession *s, const struct message *m)
4610 {
4611  struct aji_client *client = NULL;
4612  const char *id = astman_get_header(m, "ActionID");
4613  const char *jabber = astman_get_header(m, "Jabber");
4614  const char *screenname = astman_get_header(m, "ScreenName");
4615  const char *message = astman_get_header(m, "Message");
4616 
4617  if (ast_strlen_zero(jabber)) {
4618  astman_send_error(s, m, "No transport specified");
4619  return 0;
4620  }
4621  if (ast_strlen_zero(screenname)) {
4622  astman_send_error(s, m, "No ScreenName specified");
4623  return 0;
4624  }
4625  if (ast_strlen_zero(message)) {
4626  astman_send_error(s, m, "No Message specified");
4627  return 0;
4628  }
4629 
4630  astman_send_ack(s, m, "Attempting to send Jabber Message");
4631  client = ast_aji_get_client(jabber);
4632  if (!client) {
4633  astman_send_error(s, m, "Could not find Sender");
4634  return 0;
4635  }
4636  if (strchr(screenname, '@') && message) {
4637  ast_aji_send_chat(client, screenname, message);
4638  astman_append(s, "Response: Success\r\n");
4639  } else {
4640  astman_append(s, "Response: Error\r\n");
4641  }
4643  if (!ast_strlen_zero(id)) {
4644  astman_append(s, "ActionID: %s\r\n", id);
4645  }
4646  astman_append(s, "\r\n");
4647  return 0;
4648 }
4649 
4650 /*!
4651  * \internal
4652  * \brief Reload the jabber module
4653  */
4654 static int aji_reload(int reload)
4655 {
4656  int res;
4657 
4659  if (!(res = aji_load_config(reload))) {
4660  ast_log(LOG_ERROR, "JABBER: Failed to load config.\n");
4661  return 0;
4662  } else if (res == -1)
4663  return 1;
4664 
4667  ASTOBJ_RDLOCK(iterator);
4668  if (iterator->state == AJI_DISCONNECTED) {
4669  if (!iterator->thread)
4670  ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator);
4671  } else if (iterator->state == AJI_CONNECTING) {
4672  aji_get_roster(iterator);
4673  if (iterator->distribute_events) {
4674  aji_init_event_distribution(iterator);
4675  }
4676  }
4677 
4678  ASTOBJ_UNLOCK(iterator);
4679  });
4680 
4681  return 1;
4682 }
4683 
4684 /*!
4685  * \internal
4686  * \brief Unload the jabber module
4687  */
4688 static int unload_module(void)
4689 {
4690 
4691  ast_cli_unregister_multiple(aji_cli, ARRAY_LEN(aji_cli));
4692  ast_unregister_application(app_ajisend);
4693  ast_unregister_application(app_ajisendgroup);
4694  ast_unregister_application(app_ajistatus);
4695  ast_unregister_application(app_ajijoin);
4696  ast_unregister_application(app_ajileave);
4697  ast_manager_unregister("JabberSend");
4698  ast_custom_function_unregister(&jabberstatus_function);
4699  if (mwi_sub) {
4700  ast_event_unsubscribe(mwi_sub);
4701  }
4702  if (device_state_sub) {
4703  ast_event_unsubscribe(device_state_sub);
4704  }
4705  ast_custom_function_unregister(&jabberreceive_function);
4706 
4708  ASTOBJ_WRLOCK(iterator);
4709  ast_debug(3, "JABBER: Releasing and disconnecting client: %s\n", iterator->name);
4710  iterator->state = AJI_DISCONNECTING;
4711  ASTOBJ_UNLOCK(iterator);
4712  pthread_join(iterator->thread, NULL);
4713  ast_aji_disconnect(iterator);
4714  });
4715 
4718 
4719  ast_cond_destroy(&message_received_condition);
4720  ast_mutex_destroy(&messagelock);
4721 
4722  return 0;
4723 }
4724 
4725 /*!
4726  * \internal
4727  * \brief Unload the jabber module
4728  */
4729 static int load_module(void)
4730 {
4732  if (!aji_reload(0))
4733  return AST_MODULE_LOAD_DECLINE;
4740  ast_cli_register_multiple(aji_cli, ARRAY_LEN(aji_cli));
4741  ast_custom_function_register(&jabberstatus_function);
4742  ast_custom_function_register(&jabberreceive_function);
4743 
4744  ast_mutex_init(&messagelock);
4745  ast_cond_init(&message_received_condition, NULL);
4746  return 0;
4747 }
4748 
4749 /*!
4750  * \internal
4751  * \brief Wrapper for aji_reload
4752  */
4753 static int reload(void)
4754 {
4755  aji_reload(1);
4756  return 0;
4757 }
4758 
4760  .load = load_module,
4761  .unload = unload_module,
4762  .reload = reload,
4763  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
4764  );
static int aji_reconnect(struct aji_client *client)
Definition: res_jabber.c:3041
static char * app_ajijoin
Definition: res_jabber.c:389
static char pass[512]
int status
Definition: jabber.h:118
static char * aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to purge PubSub nodes via CLI.
Definition: res_jabber.c:3677
static void aji_request_pubsub_nodes(struct aji_client *client, const char *collection)
Request item list from pubsub.
Definition: res_jabber.c:3568
enum sip_cc_notify_state state
Definition: chan_sip.c:842
char version[50]
Definition: jabber.h:105
static struct ast_custom_function jabberstatus_function
Definition: res_jabber.c:753
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
static int aji_send_raw(struct aji_client *client, const char *xmlstr)
Definition: res_jabber.c:1453
static iks * aji_pubsub_iq_create(struct aji_client *client, const char *type)
Create an IQ packet.
Definition: res_jabber.c:3493
static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
Definition: res_jabber.c:1522
static int aji_client_connect(void *data, ikspak *pak)
Definition: res_jabber.c:3090
char * description
Definition: jabber.h:120
An event.
Definition: event.c:85
static int aji_act_hook(void *data, int type, iks *node)
Definition: res_jabber.c:1572
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
uint32_t version
static int aji_initialize(struct aji_client *client)
Definition: res_jabber.c:3125
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:2068
#define AJI_MAX_RESJIDLEN
Definition: jabber.h:74
Asterisk main include file. File version handling, generic pbx functions.
void ast_aji_buddy_destroy(struct aji_buddy *obj)
Definition: res_jabber.c:432
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
char statusmessage[256]
Definition: jabber.h:156
int authorized
Definition: jabber.h:180
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:179
char * strsep(char **str, const char *delims)
static int aji_send_exec(struct ast_channel *chan, const char *data)
Definition: res_jabber.c:1107
struct aji_version * next
Definition: jabber.h:108
#define ast_strdup(a)
Definition: astmm.h:109
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
static char * aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: res_jabber.c:4186
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
int ast_aji_send(struct aji_client *client, iks *x)
Wraps raw sending.
Definition: res_jabber.c:1439
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
char * ast_eid_to_str(char *s, int maxlen, struct ast_eid *eid)
Definition: netsock.c:222
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_test_flag(p, flag)
Definition: utils.h:63
int option_debug
Definition: asterisk.c:182
#define JABBER_CONFIG
Definition: res_jabber.c:285
Device state management.
static int aji_join_exec(struct ast_channel *chan, const char *data)
Application to join a chat room.
Definition: res_jabber.c:988
static void aji_init_event_distribution(struct aji_client *client)
Initialize collections for event distribution.
Definition: res_jabber.c:3235
static int delete_old_messages_all(struct aji_client *client)
Definition: res_jabber.c:976
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ASTOBJ_MARK(object)
Mark an ASTOBJ by adding the ASTOBJ_FLAG_MARKED flag to its objflags mask.
Definition: astobj.h:241
int ast_event_queue_and_cache(struct ast_event *event)
Queue and cache an event.
Definition: event.c:1465
#define VERBOSE_PREFIX_3
Definition: logger.h:43
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
int usetls
Definition: jabber.h:173
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
int64_t ast_tvdiff_sec(struct timeval end, struct timeval start)
Computes the difference (in seconds) between two struct timeval instances.
Definition: time.h:56
static struct aji_capabilities * capabilities
Definition: res_jabber.c:393
int keepalive
Definition: jabber.h:176
static struct ast_flags globalflags
Global flags, initialized to default values.
Definition: res_jabber.c:400
SSL_CTX * ssl_context
Definition: jabber.h:165
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
int ast_aji_disconnect(struct aji_client *client)
disconnect from jabber server.
Definition: res_jabber.c:3153
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
const SSL_METHOD * ssl_method
Definition: jabber.h:167
#define ASTOBJ_UNMARK(object)
Unmark an ASTOBJ by subtracting the ASTOBJ_FLAG_MARKED flag from its objflags mask.
Definition: astobj.h:251
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
join a chatroom.
Definition: res_jabber.c:2674
#define var
Definition: ast_expr2f.c:606
static int manager_jabber_send(struct mansession *s, const struct message *m)
Definition: res_jabber.c:4609
static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
Definition: res_jabber.c:2531
char name[3071]
Definition: jabber.h:135
enum aji_state state
Definition: jabber.h:170
iksparser * p
Definition: jabber.h:161
static void aji_handle_message(struct aji_client *client, ikspak *pak)
Definition: res_jabber.c:2237
An MWI subscription.
int ast_enable_distributed_devstate(void)
Enable distributed device state processing.
Definition: devicestate.c:796
Definition: cli.h:146
static char * aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: res_jabber.c:4117
Configuration File Parser.
int option_verbose
Definition: asterisk.c:181
static iks * aji_build_node_request(struct aji_client *client, const char *collection)
Build the a node request.
Definition: res_jabber.c:3586
static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
Definition: res_jabber.c:696
int message_timeout
Definition: jabber.h:179
int usesasl
Definition: jabber.h:175
char id[25]
Definition: jabber.h:129
static struct ast_flags pubsubflags
PubSub flags, initialized to default values.
Definition: res_jabber.c:403
char node[200]
Definition: jabber.h:112
#define ast_cond_init(cond, attr)
Definition: lock.h:167
static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
sends messages.
Definition: res_jabber.c:2609
char * message
Definition: jabber.h:128
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
#define ASTOBJ_WRLOCK(object)
Lock an ASTOBJ for writing.
Definition: astobj.h:104
Number of new messages Used by: AST_EVENT_MWI Payload type: UINT.
Definition: event_defs.h:71
char serverhost[AJI_MAX_RESJIDLEN]
Definition: jabber.h:154
Number of Used by: AST_EVENT_MWI Payload type: UINT.
Definition: event_defs.h:77
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2135
#define ast_mutex_lock(a)
Definition: lock.h:155
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:90
static int aji_is_secure(struct aji_client *client)
Definition: res_jabber.c:1203
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
#define AJI_MAX_ATTRLEN
Definition: jabber.h:75
static int aji_reload(int reload)
Definition: res_jabber.c:4654
struct ast_flags flags
Definition: jabber.h:182
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
static char * app_ajisendgroup
Definition: res_jabber.c:387
AJI - The Asterisk Jabber Interface.
#define LOG_DEBUG
Definition: logger.h:122
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
An Entity ID is essentially a MAC address, brief and unique.
Definition: utils.h:808
static char * aji_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_jabber.c:3904
#define ast_verb(level,...)
Definition: logger.h:243
static iks * aji_create_pubsub_node(struct aji_client *client, const char *node_type, const char *name, const char *collection_name)
Create a pubsub node.
Definition: res_jabber.c:3838
static void aji_pubsub_purge_nodes(struct aji_client *client, const char *collection_name)
Definition: res_jabber.c:3713
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
static ast_cond_t message_received_condition
Definition: res_jabber.c:396
struct aji_capabilities * next
Definition: jabber.h:114
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
Entity ID Used by All events Payload type: RAW This IE indicates which server the event originated fr...
Definition: event_defs.h:266
static char * app_ajileave
Definition: res_jabber.c:390
int ast_eid_cmp(const struct ast_eid *eid1, const struct ast_eid *eid2)
Compare two EIDs.
Definition: netsock.c:320
static int reload(void)
Definition: res_jabber.c:4753
int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
sends messages.
Definition: res_jabber.c:2582
static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
Subscribe to a PubSub node.
Definition: res_jabber.c:3357
Utility functions.
int args
This gets set in ast_cli_register()
Definition: cli.h:179
static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
Definition: res_jabber.c:3992
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:1860
static int aji_status_exec(struct ast_channel *chan, const char *data)
Definition: res_jabber.c:620
int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
create a chatroom.
Definition: res_jabber.c:2646
pthread_cond_t ast_cond_t
Definition: lock.h:144
char * from
Definition: jabber.h:127
static iks * aji_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
Definition: res_jabber.c:3856
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:638
int component
Definition: jabber.h:183
#define ast_pthread_create_background(a, b, c, d)
Definition: utils.h:426
#define CONFIG_STATUS_FILEMISSING
Definition: config.h:50
#define ASTOBJ_CONTAINER_INIT(container)
Initialize a container.
Definition: astobj.h:752
static int aji_create_client(char *label, struct ast_variable *var, int debug)
Definition: res_jabber.c:4236
char resource[AJI_MAX_RESJIDLEN]
Definition: jabber.h:119
#define ast_asprintf(a, b, c...)
Definition: astmm.h:121
static int aji_client_info_handler(void *data, ikspak *pak)
Definition: res_jabber.c:2003
static char * aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to expose PubSub leaf node creation via CLI.
Definition: res_jabber.c:3943
static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name)
Delete a PubSub node.
Definition: res_jabber.c:3795
static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
Definition: res_jabber.c:1487
static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
Definition: res_jabber.c:1296
#define EVENT_FLAG_SYSTEM
Definition: manager.h:71
static void aji_message_destroy(struct aji_message *obj)
Definition: res_jabber.c:451
static char * aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Method to expose PubSub node list via CLI.
Definition: res_jabber.c:3632
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
int ast_event_queue(struct ast_event *event)
Queue an event.
Definition: event.c:1517
Context IE Used by AST_EVENT_MWI Payload type: str.
Definition: event_defs.h:121
const char * value
Definition: config.h:79
static int delete_old_messages(struct aji_client *client, char *from)
Definition: res_jabber.c:929
unsigned int stream_flags
Definition: jabber.h:168
enum ast_device_state ast_devstate_val(const char *val)
Convert device state from text to integer value.
Definition: devicestate.c:244
General Asterisk PBX channel definitions.
struct aji_buddy_container buddies
Definition: jabber.h:184
const int fd
Definition: cli.h:153
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
#define ast_manager_register_xml(a, b, c)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:172
static char base64[64]
Definition: utils.c:78
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
struct sla_ringing_trunk * last
Definition: app_meetme.c:965
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
Access Control of various sorts.
#define AST_MAX_EXTENSION
Definition: channel.h:135
struct aji_version * versions
Definition: jabber.h:113
static int aji_register_query_handler(void *data, ikspak *pak)
Definition: res_jabber.c:1833
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
static int aji_ditems_handler(void *data, ikspak *pak)
Definition: res_jabber.c:1906
static int unload_module(void)
Definition: res_jabber.c:4688
static struct agi_command commands[]
AGI commands list.
Definition: res_agi.c:3046
node_type
Definition: ast_expr2.c:333
int ast_str_to_eid(struct ast_eid *eid, const char *s)
Convert a string into an EID.
Definition: netsock.c:305
static iks * aji_build_publish_skeleton(struct aji_client *client, const char *node, const char *event_type, unsigned int cachable)
Build the skeleton of a publish.
Definition: res_jabber.c:3396
#define ASTOBJ_INIT(object)
Initialize an object.
Definition: astobj.h:264
int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
invite to a chatroom.
Definition: res_jabber.c:2698
int port
Definition: jabber.h:171
const char * name
Definition: config.h:77
static int aji_delete_node_list(void *data, ikspak *pak)
Delete pubsub item lists.
Definition: res_jabber.c:3730
#define ast_cond_broadcast(cond)
Definition: lock.h:170
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
struct aji_resource * next
Definition: jabber.h:123
char name[80]
Definition: jabber.h:151
Event non-cachability flag Used by: All events Payload type: UINT.
Definition: event_defs.h:291
Core PBX routines and definitions.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:238
const void * ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type)
Get the value of an information element that has a raw payload.
Definition: event.c:1111
#define ASTOBJ_UNREF(object, destructor)
Decrement the reference count on an object.
Definition: astobj.h:218
void ast_sha1_hash(char *output, const char *input)
Produces SHA1 hash based on input string.
Definition: utils.c:261
const char *const * argv
Definition: cli.h:155
const char * ast_devstate_str(enum ast_device_state devstate) attribute_pure
Convert device state to text string that is easier to parse.
Definition: devicestate.c:239
static struct ast_cli_entry aji_cli[]
Definition: res_jabber.c:374
static int aji_handle_pubsub_event(void *data, ikspak *pak)
Callback for handling PubSub events.
Definition: res_jabber.c:3264
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
void ast_event_dump_cache(const struct ast_event_sub *event_sub)
Dump the event cache for the subscriber.
Definition: event.c:654
static const char desc[]
Definition: cdr_radius.c:85
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
static char * aji_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_jabber.c:3757
#define ASTOBJ_CONTAINER_DESTROY(container)
Destroy a container.
Definition: astobj.h:765
#define LOG_ERROR
Definition: logger.h:155
#define ASTOBJ_CONTAINER_DESTROYALL(container, destructor)
Empty a container.
Definition: astobj.h:453
A set of macros implementing objects and containers. Macros are used for maximum performance, to support multiple inheritance, and to be easily integrated into existing structures without additional malloc calls, etc.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
char user[AJI_MAX_JIDLEN]
Definition: jabber.h:153
static int aji_get_roster(struct aji_client *client)
Definition: res_jabber.c:3067
static void aji_handle_presence(struct aji_client *client, ikspak *pak)
Definition: res_jabber.c:2287
#define ASTOBJ_CONTAINER_LINK(container, newobj)
Add an object to a container.
Definition: astobj.h:776
int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
Encode data in base64.
Definition: utils.c:357
static struct @350 args
#define CLI_SHOWUSAGE
Definition: cli.h:44
int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
leave a chatroom.
Definition: res_jabber.c:2686
int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message)
sends message to a groupchat Prior to sending messages to a groupchat, one must be connected to it...
Definition: res_jabber.c:2596
iksid * jid
Definition: jabber.h:160
Event subscription.
Definition: event.c:124
int distribute_events
Definition: jabber.h:181
#define EVENT_FLAG_USER
Definition: manager.h:77
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: utils.c:1587
const ast_string_field name
Definition: channel.h:787
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static void aji_create_pubsub_collection(struct aji_client *client, const char *collection_name)
Create a PubSub collection node.
Definition: res_jabber.c:3812
#define AJI_MAX_JIDLEN
Definition: jabber.h:73
#define ast_cond_destroy(cond)
Definition: lock.h:168
#define ASTOBJ_CONTAINER_TRAVERSE(container, continue, eval)
Iterate through the objects in a container.
Definition: astobj.h:376
#define LOG_NOTICE
Definition: logger.h:133
static int aji_receive_node_list(void *data, ikspak *pak)
Receive pubsub item lists.
Definition: res_jabber.c:3604
int priority
Definition: jabber.h:122
static void aji_publish_device_state(struct aji_client *client, const char *device, const char *device_state, unsigned int cachable)
Publish device state to a PubSub node.
Definition: res_jabber.c:3438
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:696
enum ikshowtype status
Definition: jabber.h:189
struct aji_client * ast_aji_get_client(const char *name)
grab a aji_client structure by label name or JID. Bumps the refcount. (without the resource string) ...
Definition: res_jabber.c:4575
#define CLI_FAILURE
Definition: cli.h:45
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
#define AST_MAX_CONTEXT
Definition: channel.h:136
static const char name[]
#define ASTOBJ_UNLOCK(object)
Unlock a locked object.
Definition: astobj.h:109
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:611
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
#define ASTOBJ_CONTAINER_FIND(container, namestr)
Find an object in a container.
Definition: astobj.h:401
#define AST_FLAGS_ALL
Definition: utils.h:196
static struct aji_version * aji_find_version(char *node, char *version, ikspak *pak)
Definition: res_jabber.c:472
static char * aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: res_jabber.c:4140
static int load_module(void)
Definition: res_jabber.c:4729
static char * app_ajistatus
Definition: res_jabber.c:388
#define IKS_NET_EXPIRED
Definition: jabber.h:57
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
int debug
Definition: jabber.h:172
static const char type[]
Definition: chan_nbs.c:57
structure to hold users read from users.conf
Structure used to handle boolean flags.
Definition: utils.h:200
static struct aji_client_container clients
Definition: res_jabber.c:392
#define ast_clear_flag(p, flag)
Definition: utils.h:77
const char * usage
Definition: cli.h:171
static struct ast_event_sub * device_state_sub
Definition: res_jabber.c:395
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type)
Get the value of an information element that has an integer payload.
Definition: event.c:1075
static iks * jabber_make_auth(iksid *id, const char *pass, const char *sid)
Definition: res_jabber.c:590
char pubsub_node[AJI_MAX_RESJIDLEN]
Definition: jabber.h:155
static int aji_dinfo_handler(void *data, ikspak *pak)
Definition: res_jabber.c:2075
void ast_aji_client_destroy(struct aji_client *obj)
Definition: res_jabber.c:410
struct ast_eid ast_eid_default
Global EID.
Definition: asterisk.c:192
struct aji_version * cap
Definition: jabber.h:121
#define CLI_SUCCESS
Definition: cli.h:43
static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
Callback function for device state events.
Definition: res_jabber.c:3209
static void aji_create_affiliations(struct aji_client *client, const char *node)
Add Owner affiliations for pubsub node.
Definition: res_jabber.c:3332
static void aji_mwi_cb(const struct ast_event *ast_event, void *data)
Callback function for MWI events.
Definition: res_jabber.c:3178
static char secret[50]
Definition: chan_h323.c:148
static int aji_load_config(int reload)
Definition: res_jabber.c:4517
static int aji_filter_roster(void *data, ikspak *pak)
Definition: res_jabber.c:2953
Standard Command Line Interface.
#define ASTOBJ_RDLOCK(object)
Lock an ASTOBJ for reading.
Definition: astobj.h:100
struct ast_event * ast_event_new(enum ast_event_type event_type,...)
Create a new event.
Definition: event.c:1202
static int aji_register_approve_handler(void *data, ikspak *pak)
Definition: res_jabber.c:1790
#define ast_calloc(a, b)
Definition: astmm.h:82
struct ast_event_sub * ast_event_subscribe(enum ast_event_type event_type, ast_event_cb_t cb, const char *description, void *userdata,...)
Subscribe to events.
Definition: event.c:909
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
static int aji_create_buddy(char *label, struct aji_client *client)
Definition: res_jabber.c:4490
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
struct timeval ast_tv(ast_time_t sec, ast_suseconds_t usec)
Returns a timeval from sec, usec.
Definition: time.h:179
struct aji_message::@188 list
static int aji_recv(struct aji_client *client, int timeout)
Definition: res_jabber.c:1341
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is &quot;false&quot;...
Definition: utils.c:1550
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
static ast_mutex_t messagelock
Definition: res_jabber.c:397
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
char password[160]
Definition: jabber.h:151
static int aji_tls_handshake(struct aji_client *client)
Definition: res_jabber.c:1239
static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc)
Definition: res_jabber.c:4034
int timeout
Definition: jabber.h:178
static int aji_handle_pubsub_error(void *data, ikspak *pak)
Definition: res_jabber.c:3505
static void * aji_recv_loop(void *data)
Definition: res_jabber.c:2733
#define ASTOBJ_CONTAINER_MARKALL(container)
Mark all the objects in a container.
Definition: astobj.h:782
static int connected
Definition: cdr_pgsql.c:57
const char * name
Definition: pbx.h:96
static struct aji_resource * aji_find_resource(struct aji_buddy *buddy, char *name)
Definition: res_jabber.c:544
static int gtalk_yuck(iks *node)
Definition: res_jabber.c:566
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
struct ast_variable * next
Definition: config.h:82
#define ast_mutex_init(pmutex)
Definition: lock.h:152
static void aji_publish_mwi(struct aji_client *client, const char *mailbox, const char *context, const char *oldmsgs, const char *newmsgs)
Publish MWI to a PubSub node.
Definition: res_jabber.c:3469
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:107
#define ast_mutex_destroy(a)
Definition: lock.h:154
Generic State IE Used by AST_EVENT_DEVICE_STATE_CHANGE Payload type: UINT The actual state values dep...
Definition: event_defs.h:115
const char * ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type)
Get the value of an information element that has a string payload.
Definition: event.c:1102
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
Definition: app.h:619
#define MUC_NS
Definition: jabber.h:77
static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
Application to send a message to a groupchat.
Definition: res_jabber.c:1147
static int aji_leave_exec(struct ast_channel *chan, const char *data)
Application to leave a chat room.
Definition: res_jabber.c:1049
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
#define manager_event(category, event, contents,...)
External routines may send asterisk manager events this way.
Definition: manager.h:219
#define ASTOBJ_REF(object)
Increment an object reference count.
Definition: astobj.h:201
#define ast_malloc(a)
Definition: astmm.h:91
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:2130
Asterisk module definitions.
char name_space[256]
Definition: jabber.h:157
MD5 digest functions.
#define NET_IO_BUF_SIZE
Definition: jabber.h:55
Device Name Used by AST_EVENT_DEVICE_STATE_CHANGE Payload type: STR.
Definition: event_defs.h:107
static struct ast_custom_function jabberreceive_function
Definition: res_jabber.c:915
struct aji_capabilities * parent
Definition: jabber.h:107
struct timeval arrived
Definition: jabber.h:130
Persistant data storage (akin to *doze registry)
static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name, const char *leaf_name)
Create a PubSub leaf node.
Definition: res_jabber.c:3825
static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
Definition: res_jabber.c:767
struct ast_event_sub * ast_event_unsubscribe(struct ast_event_sub *event_sub)
Un-subscribe from events.
Definition: event.c:987
ikstack * stack
Definition: jabber.h:163
struct aji_client::messages messages
char mid[6]
Definition: jabber.h:159
SSL * ssl_session
Definition: jabber.h:166
struct aji_resource * resources
Definition: jabber.h:137
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:172
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
int priority
Definition: jabber.h:188
int forcessl
Definition: jabber.h:174
static int aji_send_header(struct aji_client *client, const char *to)
Definition: res_jabber.c:1413
iksfilter * f
Definition: jabber.h:162
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
int jingle
Definition: jabber.h:106
static void aji_handle_iq(struct aji_client *client, iks *node)
Definition: res_jabber.c:2225
struct ast_flags flags
Definition: jabber.h:139
Structure for mutex and tracking information.
Definition: lock.h:121
void ast_aji_increment_mid(char *mid)
increments the mid field for messages and other events.
Definition: res_jabber.c:2790
static char mailbox[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:197
int ast_manager_unregister(char *action)
Unregister a registered manager command.
Definition: manager.c:5355
jack_status_t status
Definition: app_jack.c:143
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
static char * app_ajisend
Definition: res_jabber.c:386
Mailbox name.
Definition: event_defs.h:83
#define ASTOBJ_CONTAINER_PRUNE_MARKED(container, destructor)
Prune marked objects from a container.
Definition: astobj.h:651
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
static int aji_start_tls(struct aji_client *client)
Definition: res_jabber.c:1220
#define ast_mutex_unlock(a)
Definition: lock.h:156
static char * aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Definition: res_jabber.c:4075
struct aji_client_container * ast_aji_get_clients(void)
Definition: res_jabber.c:4597
static void aji_pruneregister(struct aji_client *client)
Definition: res_jabber.c:2899