2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2010, Digium, Inc.
6 * Matt O'Gorman <mogorman@digium.com>
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.
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.
20 * \brief A resource for interfacing Asterisk directly as a client
21 * or a component to a XMPP/Jabber compliant server.
24 * - http://www.xmpp.org - The XMPP standards foundation
26 * Iksemel http://code.google.com/p/iksemel/
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
34 <defaultenabled>no</defaultenabled>
35 <depend>iksemel</depend>
36 <use type="external">openssl</use>
37 <support_level>deprecated</support_level>
38 <replacement>res_xmpp</replacement>
43 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48 #include "asterisk/channel.h"
49 #include "asterisk/jabber.h"
50 #include "asterisk/file.h"
51 #include "asterisk/config.h"
52 #include "asterisk/callerid.h"
53 #include "asterisk/lock.h"
54 #include "asterisk/cli.h"
55 #include "asterisk/app.h"
56 #include "asterisk/pbx.h"
57 #include "asterisk/md5.h"
58 #include "asterisk/acl.h"
59 #include "asterisk/utils.h"
60 #include "asterisk/module.h"
61 #include "asterisk/astobj.h"
62 #include "asterisk/astdb.h"
63 #include "asterisk/manager.h"
64 #include "asterisk/event.h"
65 #include "asterisk/devicestate.h"
66 #include "asterisk/message.h"
69 <application name="JabberSend" language="en_US">
71 Sends an XMPP message to a buddy.
74 <parameter name="account" required="true">
75 <para>The local named account to listen on (specified in
78 <parameter name="jid" required="true">
79 <para>Jabber ID of the buddy to send the message to. It can be a
80 bare JID (username@domain) or a full JID (username@domain/resource).</para>
82 <parameter name="message" required="true">
83 <para>The message to send.</para>
87 <para>Sends the content of <replaceable>message</replaceable> as text message
88 from the given <replaceable>account</replaceable> to the buddy identified by
89 <replaceable>jid</replaceable></para>
90 <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
91 to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
92 <replaceable>asterisk</replaceable>, configured in jabber.conf.</para>
95 <ref type="function">JABBER_STATUS</ref>
96 <ref type="function">JABBER_RECEIVE</ref>
99 <function name="JABBER_RECEIVE" language="en_US">
104 <parameter name="account" required="true">
105 <para>The local named account to listen on (specified in
108 <parameter name="jid" required="true">
109 <para>Jabber ID of the buddy to receive message from. It can be a
110 bare JID (username@domain) or a full JID (username@domain/resource).</para>
112 <parameter name="timeout">
113 <para>In seconds, defaults to <literal>20</literal>.</para>
117 <para>Receives a text message on the given <replaceable>account</replaceable>
118 from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
119 <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
120 sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
121 the <replaceable>asterisk</replaceable> XMPP account configured in jabber.conf.</para>
124 <ref type="function">JABBER_STATUS</ref>
125 <ref type="application">JabberSend</ref>
128 <function name="JABBER_STATUS" language="en_US">
130 Retrieves a buddy's status.
133 <parameter name="account" required="true">
134 <para>The local named account to listen on (specified in
137 <parameter name="jid" required="true">
138 <para>Jabber ID of the buddy to receive message from. It can be a
139 bare JID (username@domain) or a full JID (username@domain/resource).</para>
143 <para>Retrieves the numeric status associated with the buddy identified
144 by <replaceable>jid</replaceable>.
145 If the buddy does not exist in the buddylist, returns 7.</para>
146 <para>Status will be 1-7.</para>
147 <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
148 <para>If not in roster variable will be set to 7.</para>
149 <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
150 <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
151 the associated XMPP account configured in jabber.conf.</para>
154 <ref type="function">JABBER_RECEIVE</ref>
155 <ref type="application">JabberSend</ref>
158 <application name="JabberSendGroup" language="en_US">
160 Send a Jabber Message to a specified chat room
163 <parameter name="Jabber" required="true">
164 <para>Client or transport Asterisk uses to connect to Jabber.</para>
166 <parameter name="RoomJID" required="true">
167 <para>XMPP/Jabber JID (Name) of chat room.</para>
169 <parameter name="Message" required="true">
170 <para>Message to be sent to the chat room.</para>
172 <parameter name="Nickname" required="false">
173 <para>The nickname Asterisk uses in the chat room.</para>
177 <para>Allows user to send a message to a chat room via XMPP.</para>
178 <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>
181 <application name="JabberJoin" language="en_US">
186 <parameter name="Jabber" required="true">
187 <para>Client or transport Asterisk uses to connect to Jabber.</para>
189 <parameter name="RoomJID" required="true">
190 <para>XMPP/Jabber JID (Name) of chat room.</para>
192 <parameter name="Nickname" required="false">
193 <para>The nickname Asterisk will use in the chat room.</para>
194 <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>
198 <para>Allows Asterisk to join a chat room.</para>
201 <application name="JabberLeave" language="en_US">
206 <parameter name="Jabber" required="true">
207 <para>Client or transport Asterisk uses to connect to Jabber.</para>
209 <parameter name="RoomJID" required="true">
210 <para>XMPP/Jabber JID (Name) of chat room.</para>
212 <parameter name="Nickname" required="false">
213 <para>The nickname Asterisk uses in the chat room.</para>
217 <para>Allows Asterisk to leave a chat room.</para>
220 <application name="JabberStatus" language="en_US">
222 Retrieve the status of a jabber list member
225 <parameter name="Jabber" required="true">
226 <para>Client or transport Asterisk users to connect to Jabber.</para>
228 <parameter name="JID" required="true">
229 <para>XMPP/Jabber JID (Name) of recipient.</para>
231 <parameter name="Variable" required="true">
232 <para>Variable to store the status of requested user.</para>
236 <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
237 <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
238 The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
250 <para>Extended Away.</para>
253 <para>Do Not Disturb.</para>
256 <para>Offline.</para>
259 <para>Not In Roster.</para>
264 <manager name="JabberSend" language="en_US">
266 Sends a message to a Jabber Client.
269 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
270 <parameter name="Jabber" required="true">
271 <para>Client or transport Asterisk uses to connect to JABBER.</para>
273 <parameter name="JID" required="true">
274 <para>XMPP/Jabber JID (Name) of recipient.</para>
276 <parameter name="Message" required="true">
277 <para>Message to be sent to the buddy.</para>
281 <para>Sends a message to a Jabber Client.</para>
286 /*!\todo This should really be renamed to xmpp.conf. For backwards compatibility, we
287 * need to read both files */
288 #define JABBER_CONFIG "jabber.conf"
290 /*-- Forward declarations */
291 static void aji_message_destroy(struct aji_message *obj);
292 static int aji_is_secure(struct aji_client *client);
294 static int aji_start_tls(struct aji_client *client);
295 static int aji_tls_handshake(struct aji_client *client);
297 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
298 static int aji_recv(struct aji_client *client, int timeout);
299 static int aji_send_header(struct aji_client *client, const char *to);
300 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
301 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
302 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
303 static int aji_act_hook(void *data, int type, iks *node);
304 static void aji_handle_iq(struct aji_client *client, iks *node);
305 static void aji_handle_message(struct aji_client *client, ikspak *pak);
306 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
307 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
308 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
309 static void *aji_recv_loop(void *data);
310 static int aji_initialize(struct aji_client *client);
311 static int aji_client_connect(void *data, ikspak *pak);
312 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
313 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
314 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
315 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
316 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
317 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
318 static int aji_create_client(char *label, struct ast_variable *var, int debug);
319 static int aji_create_buddy(char *label, struct aji_client *client);
320 static int aji_reload(int reload);
321 static int aji_load_config(int reload);
322 static void aji_pruneregister(struct aji_client *client);
323 static int aji_filter_roster(void *data, ikspak *pak);
324 static int aji_get_roster(struct aji_client *client);
325 static int aji_client_info_handler(void *data, ikspak *pak);
326 static int aji_dinfo_handler(void *data, ikspak *pak);
327 static int aji_ditems_handler(void *data, ikspak *pak);
328 static int aji_register_query_handler(void *data, ikspak *pak);
329 static int aji_register_approve_handler(void *data, ikspak *pak);
330 static int aji_reconnect(struct aji_client *client);
331 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd,
332 struct ast_cli_args *a);
333 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd,
334 struct ast_cli_args *a);
335 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
337 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
339 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
340 static int aji_receive_node_list(void *data, ikspak* pak);
341 static void aji_init_event_distribution(struct aji_client *client);
342 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type,
343 const char *name, const char *collection_name);
344 static iks* aji_build_node_config(iks *pubsub, const char *node_type,
345 const char *collection_name);
346 static void aji_create_pubsub_collection(struct aji_client *client,
347 const char *collection_name);
348 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
349 const char *leaf_name);
350 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
351 struct ast_cli_args *a);
352 static void aji_create_affiliations(struct aji_client *client, const char *node);
353 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
354 static void aji_publish_device_state(struct aji_client *client, const char * device,
355 const char *device_state);
356 static int aji_handle_pubsub_error(void *data, ikspak *pak);
357 static int aji_handle_pubsub_event(void *data, ikspak *pak);
358 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
359 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name);
360 static iks* aji_build_node_request(struct aji_client *client, const char *collection);
361 static int aji_delete_node_list(void *data, ikspak* pak);
362 static void aji_pubsub_purge_nodes(struct aji_client *client,
363 const char* collection_name);
364 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
365 const char *context, const char *oldmsgs, const char *newmsgs);
366 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
367 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
368 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
369 const char *event_type);
370 /* No transports in this version */
372 static int aji_create_transport(char *label, struct aji_client *client);
373 static int aji_register_transport(void *data, ikspak *pak);
374 static int aji_register_transport2(void *data, ikspak *pak);
377 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from);
379 static const struct ast_msg_tech msg_tech = {
381 .msg_send = msg_send_cb,
384 static struct ast_cli_entry aji_cli[] = {
385 AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
386 AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
387 AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
388 AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
389 AST_CLI_DEFINE(aji_cli_create_collection, "Creates a PubSub node collection."),
390 AST_CLI_DEFINE(aji_cli_list_pubsub_nodes, "Lists PubSub nodes"),
391 AST_CLI_DEFINE(aji_cli_create_leafnode, "Creates a PubSub leaf node"),
392 AST_CLI_DEFINE(aji_cli_delete_pubsub_node, "Deletes a PubSub node"),
393 AST_CLI_DEFINE(aji_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
396 static char *app_ajisend = "JabberSend";
397 static char *app_ajisendgroup = "JabberSendGroup";
398 static char *app_ajistatus = "JabberStatus";
399 static char *app_ajijoin = "JabberJoin";
400 static char *app_ajileave = "JabberLeave";
402 static struct aji_client_container clients;
403 static struct aji_capabilities *capabilities = NULL;
404 static struct ast_event_sub *mwi_sub = NULL;
405 static struct ast_event_sub *device_state_sub = NULL;
406 static ast_cond_t message_received_condition;
407 static ast_mutex_t messagelock;
409 /*! \brief Global flags, initialized to default values */
410 static struct ast_flags globalflags = { AJI_AUTOREGISTER | AJI_AUTOACCEPT };
412 /*! \brief PubSub flags, initialized to default values */
413 static struct ast_flags pubsubflags = { 0 };
416 * \brief Deletes the aji_client data structure.
417 * \param obj aji_client The structure we will delete.
420 void ast_aji_client_destroy(struct aji_client *obj)
422 struct aji_message *tmp;
423 ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, ast_aji_buddy_destroy);
424 ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
425 iks_filter_delete(obj->f);
426 iks_parser_delete(obj->p);
427 iks_stack_delete(obj->stack);
428 AST_LIST_LOCK(&obj->messages);
429 while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
430 aji_message_destroy(tmp);
432 AST_LIST_HEAD_DESTROY(&obj->messages);
438 * \brief Deletes the aji_buddy data structure.
439 * \param obj aji_buddy The structure we will delete.
442 void ast_aji_buddy_destroy(struct aji_buddy *obj)
444 struct aji_resource *tmp;
446 while ((tmp = obj->resources)) {
447 obj->resources = obj->resources->next;
448 ast_free(tmp->description);
457 * \brief Deletes the aji_message data structure.
458 * \param obj aji_message The structure we will delete.
461 static void aji_message_destroy(struct aji_message *obj)
467 ast_free(obj->message);
474 * \brief Find version in XML stream and populate our capabilities list
475 * \param node the node attribute in the caps element we'll look for or add to
477 * \param version the version attribute in the caps element we'll look for or
479 * \param pak struct The XML stanza we're processing
480 * \return a pointer to the added or found aji_version structure
482 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
484 struct aji_capabilities *list = NULL;
485 struct aji_version *res = NULL;
490 node = pak->from->full;
493 version = "none supplied.";
496 if (!strcasecmp(list->node, node)) {
497 res = list->versions;
499 if (!strcasecmp(res->version, version)) {
504 /* Specified version not found. Let's add it to
505 this node in our capabilities list */
507 res = ast_malloc(sizeof(*res));
509 ast_log(LOG_ERROR, "Out of memory!\n");
514 ast_copy_string(res->version, version, sizeof(res->version));
515 res->next = list->versions;
516 list->versions = res;
522 /* Specified node not found. Let's add it our capabilities list */
524 list = ast_malloc(sizeof(*list));
526 ast_log(LOG_ERROR, "Out of memory!\n");
529 res = ast_malloc(sizeof(*res));
531 ast_log(LOG_ERROR, "Out of memory!\n");
535 ast_copy_string(list->node, node, sizeof(list->node));
536 ast_copy_string(res->version, version, sizeof(res->version));
540 list->versions = res;
541 list->next = capabilities;
549 * \brief Find the aji_resource we want
550 * \param buddy aji_buddy A buddy
552 * \return aji_resource object
554 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
556 struct aji_resource *res = NULL;
557 if (!buddy || !name) {
560 res = buddy->resources;
562 if (!strcasecmp(res->resource, name)) {
572 * \brief Jabber GTalk function
574 * \return 1 on success, 0 on failure.
576 static int gtalk_yuck(iks *node)
578 if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) {
579 ast_debug(1, "Found resource with Googletalk voice capabilities\n");
581 } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1")) {
582 ast_debug(1, "Found resource with Gmail voice/video chat capabilities\n");
584 } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 video-v1 voice-v1")) {
585 ast_debug(1, "Found resource with Gmail voice/video chat capabilities (no camera)\n");
594 * \brief Setup the authentication struct
596 * \param pass password
600 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
604 iks_insert_attrib(x, "type", "set");
605 y = iks_insert(x, "query");
606 iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
607 iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
608 iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
612 snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
613 ast_sha1_hash(buf, sidpass);
614 iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
616 iks_insert_cdata(iks_insert(y, "password"), pass, 0);
623 * \brief Dial plan function status(). puts the status of watched user
624 * into a channel variable.
625 * \param chan ast_channel
630 static int aji_status_exec(struct ast_channel *chan, const char *data)
632 struct aji_client *client = NULL;
633 struct aji_buddy *buddy = NULL;
634 struct aji_resource *r = NULL;
638 static int deprecation_warning = 0;
639 AST_DECLARE_APP_ARGS(args,
642 AST_APP_ARG(variable);
644 AST_DECLARE_APP_ARGS(jid,
645 AST_APP_ARG(screenname);
646 AST_APP_ARG(resource);
649 if (deprecation_warning++ % 10 == 0) {
650 ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
654 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
657 s = ast_strdupa(data);
658 AST_STANDARD_APP_ARGS(args, s);
660 if (args.argc != 3) {
661 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
665 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
666 if (jid.argc < 1 || jid.argc > 2) {
667 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
671 if (!(client = ast_aji_get_client(args.sender))) {
672 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
675 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
677 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
678 ASTOBJ_UNREF(client, ast_aji_client_destroy);
681 r = aji_find_resource(buddy, jid.resource);
682 if (!r && buddy->resources) {
683 r = buddy->resources;
685 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
686 ASTOBJ_UNREF(client, ast_aji_client_destroy);
688 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
692 snprintf(status, sizeof(status), "%d", stat);
693 pbx_builtin_setvar_helper(chan, args.variable, status);
699 * \brief Dial plan funtcion to retrieve the status of a buddy.
700 * \param channel The associated ast_channel, if there is one
701 * \param data The account, buddy JID, and optional timeout
706 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
708 struct aji_client *client = NULL;
709 struct aji_buddy *buddy = NULL;
710 struct aji_resource *r = NULL;
712 AST_DECLARE_APP_ARGS(args,
716 AST_DECLARE_APP_ARGS(jid,
717 AST_APP_ARG(screenname);
718 AST_APP_ARG(resource);
722 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
725 AST_STANDARD_APP_ARGS(args, data);
727 if (args.argc != 2) {
728 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
732 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
733 if (jid.argc < 1 || jid.argc > 2) {
734 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
738 if (!(client = ast_aji_get_client(args.sender))) {
739 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
742 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
744 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
745 ASTOBJ_UNREF(client, ast_aji_client_destroy);
748 r = aji_find_resource(buddy, jid.resource);
749 if (!r && buddy->resources) {
750 r = buddy->resources;
752 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
753 ASTOBJ_UNREF(client, ast_aji_client_destroy);
755 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
759 snprintf(buf, buflen, "%d", stat);
763 static struct ast_custom_function jabberstatus_function = {
764 .name = "JABBER_STATUS",
765 .read = acf_jabberstatus_read,
770 * \brief Dial plan function to receive a message.
771 * \param channel The associated ast_channel, if there is one
772 * \param data The account, JID, and optional timeout
777 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
779 char *aux = NULL, *parse = NULL;
781 int jidlen, resourcelen;
782 struct timeval start;
784 struct aji_client *client = NULL;
786 struct aji_message *tmp = NULL;
787 AST_DECLARE_APP_ARGS(args,
788 AST_APP_ARG(account);
790 AST_APP_ARG(timeout);
792 AST_DECLARE_APP_ARGS(jid,
793 AST_APP_ARG(screenname);
794 AST_APP_ARG(resource);
797 if (ast_strlen_zero(data)) {
798 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
802 parse = ast_strdupa(data);
803 AST_STANDARD_APP_ARGS(args, parse);
805 if (args.argc < 2 || args.argc > 3) {
806 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
810 parse = ast_strdupa(args.jid);
811 AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
812 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
813 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
817 if (ast_strlen_zero(args.timeout)) {
820 sscanf(args.timeout, "%d", &timeout);
822 ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
827 jidlen = strlen(jid.screenname);
828 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
830 client = ast_aji_get_client(args.account);
832 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
836 ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
840 if (ast_autoservice_start(chan) < 0) {
841 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
842 ASTOBJ_UNREF(client, ast_aji_client_destroy);
846 /* search the messages list, grab the first message that matches with
847 * the from JID we're expecting, and remove it from the messages list */
848 while (diff < timeout) {
849 struct timespec ts = { 0, };
853 wait = ast_tvadd(start, ast_tv(timeout, 0));
854 ts.tv_sec = wait.tv_sec;
855 ts.tv_nsec = wait.tv_usec * 1000;
857 /* wait up to timeout seconds for an incoming message */
858 ast_mutex_lock(&messagelock);
859 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
860 ast_mutex_unlock(&messagelock);
861 if (res == ETIMEDOUT) {
862 ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
866 AST_LIST_LOCK(&client->messages);
867 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
869 /* no resource provided, compare bare JIDs */
870 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
874 /* resource appended, compare bare JIDs and resources */
875 char *resource = strchr(tmp->from, '/');
876 if (!resource || strlen(resource) == 0) {
877 ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
878 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
883 if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
888 /* check if the message is not too old */
889 if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
890 ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
891 AST_LIST_REMOVE_CURRENT(list);
892 aji_message_destroy(tmp);
896 aux = ast_strdupa(tmp->message);
897 AST_LIST_REMOVE_CURRENT(list);
898 aji_message_destroy(tmp);
901 AST_LIST_TRAVERSE_SAFE_END;
902 AST_LIST_UNLOCK(&client->messages);
908 diff = ast_tvdiff_ms(ast_tvnow(), start);
911 ASTOBJ_UNREF(client, ast_aji_client_destroy);
912 if (ast_autoservice_stop(chan) < 0) {
913 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
916 /* return if we timed out */
918 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
921 ast_copy_string(buf, aux, buflen);
926 static struct ast_custom_function jabberreceive_function = {
927 .name = "JABBER_RECEIVE",
928 .read = acf_jabberreceive_read,
933 * \brief Delete old messages from a given JID
934 * Messages stored during more than client->message_timeout are deleted
935 * \param client Asterisk's XMPP client
936 * \param from the JID we received messages from
937 * \retval the number of deleted messages
940 static int delete_old_messages(struct aji_client *client, char *from)
944 struct aji_message *tmp = NULL;
946 ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
950 /* remove old messages */
951 AST_LIST_LOCK(&client->messages);
952 if (AST_LIST_EMPTY(&client->messages)) {
953 AST_LIST_UNLOCK(&client->messages);
957 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
959 if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
960 AST_LIST_REMOVE_CURRENT(list);
961 aji_message_destroy(tmp);
964 } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
966 if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
967 AST_LIST_REMOVE_CURRENT(list);
968 aji_message_destroy(tmp);
973 AST_LIST_TRAVERSE_SAFE_END;
974 AST_LIST_UNLOCK(&client->messages);
981 * \brief Delete old messages
982 * Messages stored during more than client->message_timeout are deleted
983 * \param client Asterisk's XMPP client
984 * \retval the number of deleted messages
987 static int delete_old_messages_all(struct aji_client *client)
989 return delete_old_messages(client, NULL);
993 * \brief Application to join a chat room
994 * \param chan ast_channel
995 * \param data Data is sender|jid|nickname.
999 static int aji_join_exec(struct ast_channel *chan, const char *data)
1001 struct aji_client *client = NULL;
1003 char nick[AJI_MAX_RESJIDLEN];
1005 AST_DECLARE_APP_ARGS(args,
1006 AST_APP_ARG(sender);
1012 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1015 s = ast_strdupa(data);
1017 AST_STANDARD_APP_ARGS(args, s);
1018 if (args.argc < 2 || args.argc > 3) {
1019 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1023 if (strchr(args.jid, '/')) {
1024 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1028 if (!(client = ast_aji_get_client(args.sender))) {
1029 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1033 if (!ast_strlen_zero(args.nick)) {
1034 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1036 if (client->component) {
1037 sprintf(nick, "asterisk");
1039 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1043 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1044 ast_aji_join_chat(client, args.jid, nick);
1046 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1049 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1054 * \brief Application to leave a chat room
1055 * \param chan ast_channel
1056 * \param data Data is sender|jid|nickname.
1060 static int aji_leave_exec(struct ast_channel *chan, const char *data)
1062 struct aji_client *client = NULL;
1064 char nick[AJI_MAX_RESJIDLEN];
1065 AST_DECLARE_APP_ARGS(args,
1066 AST_APP_ARG(sender);
1072 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1075 s = ast_strdupa(data);
1077 AST_STANDARD_APP_ARGS(args, s);
1078 if (args.argc < 2 || args.argc > 3) {
1079 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1083 if (strchr(args.jid, '/')) {
1084 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1088 if (!(client = ast_aji_get_client(args.sender))) {
1089 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1093 if (!ast_strlen_zero(args.nick)) {
1094 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1096 if (client->component) {
1097 sprintf(nick, "asterisk");
1099 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1103 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1104 ast_aji_leave_chat(client, args.jid, nick);
1106 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1112 * \brief Dial plan function to send a message.
1113 * \param chan ast_channel
1114 * \param data Data is account,jid,message.
1116 * \retval -1 failure
1118 static int aji_send_exec(struct ast_channel *chan, const char *data)
1120 struct aji_client *client = NULL;
1122 AST_DECLARE_APP_ARGS(args,
1123 AST_APP_ARG(sender);
1124 AST_APP_ARG(recipient);
1125 AST_APP_ARG(message);
1129 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1132 s = ast_strdupa(data);
1134 AST_STANDARD_APP_ARGS(args, s);
1135 if (args.argc < 3) {
1136 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1140 if (!(client = ast_aji_get_client(args.sender))) {
1141 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1144 if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
1145 ast_aji_send_chat(client, args.recipient, args.message);
1147 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1151 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from)
1153 struct aji_client *client;
1158 sender = ast_strdupa(from);
1159 strsep(&sender, ":");
1160 dest = ast_strdupa(to);
1163 if (ast_strlen_zero(sender)) {
1164 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for xmpp\n", from);
1168 if (!(client = ast_aji_get_client(sender))) {
1169 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
1173 ast_debug(1, "Sending message to '%s' from '%s'\n", dest, client->name);
1175 res = ast_aji_send_chat(client, dest, ast_msg_get_body(msg));
1176 if (res != IKS_OK) {
1177 ast_log(LOG_WARNING, "Failed to send xmpp message (%d).\n", res);
1180 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1181 return res == IKS_OK ? 0 : -1;
1185 * \brief Application to send a message to a groupchat.
1186 * \param chan ast_channel
1187 * \param data Data is sender|groupchat|message.
1191 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
1193 struct aji_client *client = NULL;
1195 char nick[AJI_MAX_RESJIDLEN];
1197 AST_DECLARE_APP_ARGS(args,
1198 AST_APP_ARG(sender);
1199 AST_APP_ARG(groupchat);
1200 AST_APP_ARG(message);
1205 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1208 s = ast_strdupa(data);
1210 AST_STANDARD_APP_ARGS(args, s);
1211 if (args.argc < 3 || args.argc > 4) {
1212 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1216 if (!(client = ast_aji_get_client(args.sender))) {
1217 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1221 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1222 if (client->component) {
1223 sprintf(nick, "asterisk");
1225 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1228 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1231 if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
1232 res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
1235 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1236 if (res != IKS_OK) {
1244 * \brief Tests whether the connection is secured or not
1245 * \return 0 if the connection is not secured
1247 static int aji_is_secure(struct aji_client *client)
1250 return client->stream_flags & SECURE;
1259 * \brief Starts the TLS procedure
1260 * \param client the configured XMPP client we use to connect to a XMPP server
1261 * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
1262 * if OpenSSL is not installed
1264 static int aji_start_tls(struct aji_client *client)
1268 /* This is sent not encrypted */
1269 if ((ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"))) {
1273 client->stream_flags |= TRY_SECURE;
1279 * \brief TLS handshake, OpenSSL initialization
1280 * \param client the configured XMPP client we use to connect to a XMPP server
1281 * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
1283 static int aji_tls_handshake(struct aji_client *client)
1287 ast_debug(1, "Starting TLS handshake\n");
1289 /* Choose an SSL/TLS protocol version, create SSL_CTX */
1290 client->ssl_method = SSLv3_method();
1291 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
1292 return IKS_NET_TLSFAIL;
1295 /* Create new SSL session */
1296 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
1297 return IKS_NET_TLSFAIL;
1300 /* Enforce TLS on our XMPP connection */
1301 sock = iks_fd(client->p);
1302 if (!SSL_set_fd(client->ssl_session, sock)) {
1303 return IKS_NET_TLSFAIL;
1306 /* Perform SSL handshake */
1307 if (!SSL_connect(client->ssl_session)) {
1308 return IKS_NET_TLSFAIL;
1311 client->stream_flags &= (~TRY_SECURE);
1312 client->stream_flags |= SECURE;
1314 /* Sent over the established TLS connection */
1315 if (aji_send_header(client, client->jid->server) != IKS_OK) {
1316 return IKS_NET_TLSFAIL;
1319 ast_debug(1, "TLS started with server\n");
1323 #endif /* HAVE_OPENSSL */
1327 * \brief Secured or unsecured IO socket receiving function
1328 * \param client the configured XMPP client we use to connect to a XMPP server
1329 * \param buffer the reception buffer
1330 * \param buf_len the size of the buffer
1331 * \param timeout the select timer
1332 * \retval the number of read bytes
1333 * \retval 0 timeout expiration
1336 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
1338 struct pollfd pfd = { .events = POLLIN };
1342 if (aji_is_secure(client)) {
1343 pfd.fd = SSL_get_fd(client->ssl_session);
1348 #endif /* HAVE_OPENSSL */
1349 pfd.fd = iks_fd(client->p);
1351 res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
1354 if (aji_is_secure(client)) {
1355 len = SSL_read(client->ssl_session, buffer, buf_len);
1357 #endif /* HAVE_OPENSSL */
1358 len = recv(pfd.fd, buffer, buf_len, 0);
1362 } else if (len <= 0) {
1371 * \brief Tries to receive data from the Jabber server
1372 * \param client the configured XMPP client we use to connect to a XMPP server
1373 * \param timeout the timeout value
1374 * This function receives (encrypted or unencrypted) data from the XMPP server,
1375 * and passes it to the parser.
1376 * \retval IKS_OK success
1377 * \retval IKS_NET_RWERR IO error
1378 * \retval IKS_NET_NOCONN no connection available
1379 * \retval IKS_NET_EXPIRED timeout expiration
1381 static int aji_recv (struct aji_client *client, int timeout)
1384 char buf[NET_IO_BUF_SIZE - 1];
1385 char newbuf[NET_IO_BUF_SIZE - 1];
1390 memset(buf, 0, sizeof(buf));
1391 memset(newbuf, 0, sizeof(newbuf));
1394 len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
1395 if (len < 0) return IKS_NET_RWERR;
1396 if (len == 0) return IKS_NET_EXPIRED;
1399 /* our iksemel parser won't work as expected if we feed
1400 it with XML packets that contain multiple whitespace
1401 characters between tags */
1404 /* if we stumble on the ending tag character,
1405 we skip any whitespace that follows it*/
1407 while (isspace(buf[pos+1])) {
1411 newbuf[newbufpos] = c;
1418 /* Log the message here, because iksemel's logHook is
1420 aji_log_hook(client, buf, len, 1);
1422 /* let iksemel deal with the string length,
1423 and reset our buffer */
1424 ret = iks_parse(client->p, newbuf, 0, 0);
1425 memset(newbuf, 0, sizeof(newbuf));
1429 ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
1432 ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
1435 ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
1438 if (ret != IKS_OK) {
1441 ast_debug(3, "XML parsing successful\n");
1448 * \brief Sends XMPP header to the server
1449 * \param client the configured XMPP client we use to connect to a XMPP server
1450 * \param to the target XMPP server
1451 * \return IKS_OK on success, any other value on failure
1453 static int aji_send_header(struct aji_client *client, const char *to)
1458 len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
1459 msg = iks_malloc(len);
1462 sprintf(msg, "<?xml version='1.0'?>"
1463 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
1464 "%s' to='%s' version='1.0'>", client->name_space, to);
1465 err = aji_send_raw(client, msg);
1474 * \brief Wraps raw sending
1475 * \param client the configured XMPP client we use to connect to a XMPP server
1476 * \param x the XMPP packet to send
1477 * \return IKS_OK on success, any other value on failure
1479 int ast_aji_send(struct aji_client *client, iks *x)
1481 return aji_send_raw(client, iks_string(iks_stack(x), x));
1486 * \brief Sends an XML string over an XMPP connection
1487 * \param client the configured XMPP client we use to connect to a XMPP server
1488 * \param xmlstr the XML string to send
1489 * The XML data is sent whether the connection is secured or not. In the
1490 * latter case, we just call iks_send_raw().
1491 * \return IKS_OK on success, any other value on failure
1493 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
1497 int len = strlen(xmlstr);
1499 if (aji_is_secure(client)) {
1500 ret = SSL_write(client->ssl_session, xmlstr, len);
1502 /* Log the message here, because iksemel's logHook is
1504 aji_log_hook(client, xmlstr, len, 0);
1509 /* If needed, data will be sent unencrypted, and logHook will
1510 be called inside iks_send_raw */
1511 ret = iks_send_raw(client->p, xmlstr);
1512 if (ret != IKS_OK) {
1521 * \brief the debug loop.
1523 * \param xmpp xml data as string
1524 * \param size size of string
1525 * \param is_incoming direction of packet 1 for inbound 0 for outbound.
1527 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
1529 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1531 if (!ast_strlen_zero(xmpp)) {
1532 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
1535 if (client->debug) {
1537 ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
1539 if (strlen(xmpp) == 1) {
1540 if (option_debug > 2 && xmpp[0] == ' ') {
1541 ast_verbose("\nJABBER: Keep alive packet\n");
1544 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
1549 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1554 * \brief A wrapper function for iks_start_sasl
1555 * \param client the configured XMPP client we use to connect to a XMPP server
1556 * \param type the SASL authentication type. Supported types are PLAIN and MD5
1558 * \param pass password.
1560 * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
1562 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
1569 /* trigger SASL DIGEST-MD5 only over an unsecured connection.
1570 iks_start_sasl is an iksemel API function and relies on GnuTLS,
1571 whereas we use OpenSSL */
1572 if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
1573 return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass);
1574 if (!(type & IKS_STREAM_SASL_PLAIN)) {
1575 ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
1576 return IKS_NET_NOTSUPP;
1579 x = iks_new("auth");
1581 ast_log(LOG_ERROR, "Out of memory.\n");
1582 return IKS_NET_NOTSUPP;
1585 iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
1586 len = strlen(username) + strlen(pass) + 3;
1587 s = ast_alloca(len);
1588 base64 = ast_alloca((len + 2) * 4 / 3);
1589 iks_insert_attrib(x, "mechanism", "PLAIN");
1590 snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
1592 /* exclude the NULL training byte from the base64 encoding operation
1593 as some XMPP servers will refuse it.
1594 The format for authentication is [authzid]\0authcid\0password
1595 not [authzid]\0authcid\0password\0 */
1596 ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
1597 iks_insert_cdata(x, base64, 0);
1598 ast_aji_send(client, x);
1606 * \brief The action hook parses the inbound packets, constantly running.
1607 * \param data aji client structure
1608 * \param type type of packet
1609 * \param node the actual packet.
1610 * \return IKS_OK or IKS_HOOK .
1612 static int aji_act_hook(void *data, int type, iks *node)
1614 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1620 ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
1621 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1625 if (client->state == AJI_DISCONNECTING) {
1626 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1630 pak = iks_packet(node);
1632 /* work around iksemel's impossibility to recognize node names
1633 * containing a semicolon. Set the namespace of the corresponding
1634 * node accordingly. */
1635 if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
1636 char *node_ns = NULL;
1637 char attr[AJI_MAX_ATTRLEN];
1638 char *node_name = iks_name(iks_child(node));
1639 char *aux = strchr(node_name, ':') + 1;
1640 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
1641 node_ns = iks_find_attrib(iks_child(node), attr);
1644 pak->query = iks_child(node);
1649 if (!client->component) { /*client */
1651 case IKS_NODE_START:
1652 if (client->usetls && !aji_is_secure(client)) {
1653 #ifndef HAVE_OPENSSL
1654 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");
1655 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1658 if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
1659 ast_log(LOG_ERROR, "Could not start TLS\n");
1660 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1666 if (!client->usesasl) {
1667 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);
1668 auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
1670 iks_insert_attrib(auth, "id", client->mid);
1671 iks_insert_attrib(auth, "to", client->jid->server);
1672 ast_aji_increment_mid(client->mid);
1673 ast_aji_send(client, auth);
1676 ast_log(LOG_ERROR, "Out of memory.\n");
1681 case IKS_NODE_NORMAL:
1683 if (client->stream_flags & TRY_SECURE) {
1684 if (!strcmp("proceed", iks_name(node))) {
1685 return aji_tls_handshake(client);
1689 if (!strcmp("stream:features", iks_name(node))) {
1690 features = iks_stream_features(node);
1691 if (client->usesasl) {
1692 if (client->usetls && !aji_is_secure(client)) {
1695 if (client->authorized) {
1696 if (features & IKS_STREAM_BIND) {
1697 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);
1698 auth = iks_make_resource_bind(client->jid);
1700 iks_insert_attrib(auth, "id", client->mid);
1701 ast_aji_increment_mid(client->mid);
1702 ast_aji_send(client, auth);
1705 ast_log(LOG_ERROR, "Out of memory.\n");
1709 if (features & IKS_STREAM_SESSION) {
1710 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);
1711 auth = iks_make_session();
1713 iks_insert_attrib(auth, "id", "auth");
1714 ast_aji_increment_mid(client->mid);
1715 ast_aji_send(client, auth);
1718 ast_log(LOG_ERROR, "Out of memory.\n");
1723 if (!client->jid->user) {
1724 ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
1728 ret = aji_start_sasl(client, features, client->jid->user, client->password);
1729 if (ret != IKS_OK) {
1730 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1736 } else if (!strcmp("failure", iks_name(node))) {
1737 ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
1738 } else if (!strcmp("success", iks_name(node))) {
1739 client->authorized = 1;
1740 aji_send_header(client, client->jid->server);
1743 case IKS_NODE_ERROR:
1744 ast_log(LOG_ERROR, "JABBER: Node Error\n");
1745 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1749 ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1750 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1754 } else if (client->state != AJI_CONNECTED && client->component) {
1756 case IKS_NODE_START:
1757 if (client->state == AJI_DISCONNECTED) {
1758 char secret[160], shasum[320], *handshake;
1760 sprintf(secret, "%s%s", pak->id, client->password);
1761 ast_sha1_hash(shasum, secret);
1762 if (ast_asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
1763 aji_send_raw(client, handshake);
1764 ast_free(handshake);
1766 client->state = AJI_CONNECTING;
1767 if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
1768 client->state = AJI_CONNECTED;
1770 ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1775 case IKS_NODE_NORMAL:
1778 case IKS_NODE_ERROR:
1779 ast_log(LOG_ERROR, "JABBER: Node Error\n");
1780 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1784 ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1785 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1790 switch (pak->type) {
1792 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1794 case IKS_PAK_MESSAGE:
1795 aji_handle_message(client, pak);
1796 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1798 case IKS_PAK_PRESENCE:
1799 aji_handle_presence(client, pak);
1800 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1803 aji_handle_subscribe(client, pak);
1804 ast_debug(1, "JABBER: Handling paktype S10N\n");
1807 ast_debug(1, "JABBER: Handling paktype IQ\n");
1808 aji_handle_iq(client, node);
1811 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
1815 iks_filter_packet(client->f, pak);
1820 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1828 * \return IKS_FILTER_EAT.
1830 static int aji_register_approve_handler(void *data, ikspak *pak)
1832 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1833 iks *iq = NULL, *presence = NULL, *x = NULL;
1836 presence = iks_new("presence");
1838 if (client && iq && presence && x) {
1839 if (!iks_find(pak->query, "remove")) {
1840 iks_insert_attrib(iq, "from", client->jid->full);
1841 iks_insert_attrib(iq, "to", pak->from->full);
1842 iks_insert_attrib(iq, "id", pak->id);
1843 iks_insert_attrib(iq, "type", "result");
1844 ast_aji_send(client, iq);
1846 iks_insert_attrib(presence, "from", client->jid->full);
1847 iks_insert_attrib(presence, "to", pak->from->partial);
1848 iks_insert_attrib(presence, "id", client->mid);
1849 ast_aji_increment_mid(client->mid);
1850 iks_insert_attrib(presence, "type", "subscribe");
1851 iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1852 iks_insert_node(presence, x);
1853 ast_aji_send(client, presence);
1856 ast_log(LOG_ERROR, "Out of memory.\n");
1860 iks_delete(presence);
1863 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1864 return IKS_FILTER_EAT;
1868 * \brief register handler for incoming querys (IQ's)
1869 * \param data incoming aji_client request
1871 * \return IKS_FILTER_EAT.
1873 static int aji_register_query_handler(void *data, ikspak *pak)
1875 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1876 struct aji_buddy *buddy = NULL;
1877 iks *iq = NULL, *query = NULL;
1879 client = (struct aji_client *) data;
1881 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1883 iks *error = NULL, *notacceptable = NULL;
1885 ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1887 query = iks_new("query");
1888 error = iks_new("error");
1889 notacceptable = iks_new("not-acceptable");
1890 if (iq && query && error && notacceptable) {
1891 iks_insert_attrib(iq, "type", "error");
1892 iks_insert_attrib(iq, "from", client->user);
1893 iks_insert_attrib(iq, "to", pak->from->full);
1894 iks_insert_attrib(iq, "id", pak->id);
1895 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1896 iks_insert_attrib(error, "code" , "406");
1897 iks_insert_attrib(error, "type", "modify");
1898 iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1899 iks_insert_node(iq, query);
1900 iks_insert_node(iq, error);
1901 iks_insert_node(error, notacceptable);
1902 ast_aji_send(client, iq);
1904 ast_log(LOG_ERROR, "Out of memory.\n");
1908 iks_delete(notacceptable);
1909 } else if (!iks_find_attrib(pak->query, "node")) {
1910 iks *instructions = NULL;
1911 char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1913 query = iks_new("query");
1914 instructions = iks_new("instructions");
1915 if (iq && query && instructions && client) {
1916 iks_insert_attrib(iq, "from", client->user);
1917 iks_insert_attrib(iq, "to", pak->from->full);
1918 iks_insert_attrib(iq, "id", pak->id);
1919 iks_insert_attrib(iq, "type", "result");
1920 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1921 iks_insert_cdata(instructions, explain, 0);
1922 iks_insert_node(iq, query);
1923 iks_insert_node(query, instructions);
1924 ast_aji_send(client, iq);
1926 ast_log(LOG_ERROR, "Out of memory.\n");
1929 iks_delete(instructions);
1933 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
1934 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1935 return IKS_FILTER_EAT;
1940 * \brief Handles stuff
1943 * \return IKS_FILTER_EAT.
1945 static int aji_ditems_handler(void *data, ikspak *pak)
1947 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1950 if (!(node = iks_find_attrib(pak->query, "node"))) {
1951 iks *iq = NULL, *query = NULL, *item = NULL;
1953 query = iks_new("query");
1954 item = iks_new("item");
1956 if (iq && query && item) {
1957 iks_insert_attrib(iq, "from", client->user);
1958 iks_insert_attrib(iq, "to", pak->from->full);
1959 iks_insert_attrib(iq, "id", pak->id);
1960 iks_insert_attrib(iq, "type", "result");
1961 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1962 iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1963 iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1964 iks_insert_attrib(item, "jid", client->user);
1966 iks_insert_node(iq, query);
1967 iks_insert_node(query, item);
1968 ast_aji_send(client, iq);
1970 ast_log(LOG_ERROR, "Out of memory.\n");
1977 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1978 iks *iq, *query, *confirm;
1980 query = iks_new("query");
1981 confirm = iks_new("item");
1982 if (iq && query && confirm && client) {
1983 iks_insert_attrib(iq, "from", client->user);
1984 iks_insert_attrib(iq, "to", pak->from->full);
1985 iks_insert_attrib(iq, "id", pak->id);
1986 iks_insert_attrib(iq, "type", "result");
1987 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1988 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1989 iks_insert_attrib(confirm, "node", "confirmaccount");
1990 iks_insert_attrib(confirm, "name", "Confirm AIM account");
1991 iks_insert_attrib(confirm, "jid", "blog.astjab.org");
1993 iks_insert_node(iq, query);
1994 iks_insert_node(query, confirm);
1995 ast_aji_send(client, iq);
1997 ast_log(LOG_ERROR, "Out of memory.\n");
2002 iks_delete(confirm);
2004 } else if (!strcasecmp(node, "confirmaccount")) {
2005 iks *iq = NULL, *query = NULL, *feature = NULL;
2008 query = iks_new("query");
2009 feature = iks_new("feature");
2011 if (iq && query && feature && client) {
2012 iks_insert_attrib(iq, "from", client->user);
2013 iks_insert_attrib(iq, "to", pak->from->full);
2014 iks_insert_attrib(iq, "id", pak->id);
2015 iks_insert_attrib(iq, "type", "result");
2016 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2017 iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2018 iks_insert_node(iq, query);
2019 iks_insert_node(query, feature);
2020 ast_aji_send(client, iq);
2022 ast_log(LOG_ERROR, "Out of memory.\n");
2027 iks_delete(feature);
2030 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2031 return IKS_FILTER_EAT;
2037 * \brief Handle add extra info
2040 * \return IKS_FILTER_EAT
2042 static int aji_client_info_handler(void *data, ikspak *pak)
2044 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2045 struct aji_resource *resource = NULL;
2046 struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2048 resource = aji_find_resource(buddy, pak->from->resource);
2049 if (pak->subtype == IKS_TYPE_RESULT) {
2051 ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2052 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2053 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2054 return IKS_FILTER_EAT;
2056 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2057 resource->cap->jingle = 1;
2059 resource->cap->jingle = 0;
2061 } else if (pak->subtype == IKS_TYPE_GET) {
2062 iks *iq, *disco, *ident, *google, *query;
2064 query = iks_new("query");
2065 ident = iks_new("identity");
2066 disco = iks_new("feature");
2067 google = iks_new("feature");
2068 if (iq && ident && disco && google) {
2069 iks_insert_attrib(iq, "from", client->jid->full);
2070 iks_insert_attrib(iq, "to", pak->from->full);
2071 iks_insert_attrib(iq, "type", "result");
2072 iks_insert_attrib(iq, "id", pak->id);
2073 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2074 iks_insert_attrib(ident, "category", "client");
2075 iks_insert_attrib(ident, "type", "pc");
2076 iks_insert_attrib(ident, "name", "asterisk");
2077 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2078 iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2079 iks_insert_node(iq, query);
2080 iks_insert_node(query, ident);
2081 iks_insert_node(query, google);
2082 iks_insert_node(query, disco);
2083 ast_aji_send(client, iq);
2085 ast_log(LOG_ERROR, "Out of Memory.\n");
2093 } else if (pak->subtype == IKS_TYPE_ERROR) {
2094 ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
2096 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2097 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2098 return IKS_FILTER_EAT;
2103 * \brief Handler of the return info packet
2104 * \param data aji_client
2106 * \return IKS_FILTER_EAT
2108 static int aji_dinfo_handler(void *data, ikspak *pak)
2110 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2112 struct aji_resource *resource = NULL;
2113 struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2115 if (pak->subtype == IKS_TYPE_ERROR) {
2116 ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
2117 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2118 return IKS_FILTER_EAT;
2120 resource = aji_find_resource(buddy, pak->from->resource);
2121 if (pak->subtype == IKS_TYPE_RESULT) {
2123 ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2124 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2125 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2126 return IKS_FILTER_EAT;
2128 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2129 resource->cap->jingle = 1;
2131 resource->cap->jingle = 0;
2133 } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
2134 iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
2137 query = iks_new("query");
2138 identity = iks_new("identity");
2139 disco = iks_new("feature");
2140 reg = iks_new("feature");
2141 commands = iks_new("feature");
2142 gateway = iks_new("feature");
2143 version = iks_new("feature");
2144 vcard = iks_new("feature");
2145 search = iks_new("feature");
2146 if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
2147 iks_insert_attrib(iq, "from", client->user);
2148 iks_insert_attrib(iq, "to", pak->from->full);
2149 iks_insert_attrib(iq, "id", pak->id);
2150 iks_insert_attrib(iq, "type", "result");
2151 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2152 iks_insert_attrib(identity, "category", "gateway");
2153 iks_insert_attrib(identity, "type", "pstn");
2154 iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2155 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2156 iks_insert_attrib(reg, "var", "jabber:iq:register");
2157 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2158 iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2159 iks_insert_attrib(version, "var", "jabber:iq:version");
2160 iks_insert_attrib(vcard, "var", "vcard-temp");
2161 iks_insert_attrib(search, "var", "jabber:iq:search");
2163 iks_insert_node(iq, query);
2164 iks_insert_node(query, identity);
2165 iks_insert_node(query, disco);
2166 iks_insert_node(query, reg);
2167 iks_insert_node(query, commands);
2168 iks_insert_node(query, gateway);
2169 iks_insert_node(query, version);
2170 iks_insert_node(query, vcard);
2171 iks_insert_node(query, search);
2172 ast_aji_send(client, iq);
2174 ast_log(LOG_ERROR, "Out of memory.\n");
2179 iks_delete(identity);
2182 iks_delete(commands);
2183 iks_delete(gateway);
2184 iks_delete(version);
2187 } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
2188 iks *iq, *query, *confirm;
2190 query = iks_new("query");
2191 confirm = iks_new("item");
2193 if (iq && query && confirm && client) {
2194 iks_insert_attrib(iq, "from", client->user);
2195 iks_insert_attrib(iq, "to", pak->from->full);
2196 iks_insert_attrib(iq, "id", pak->id);
2197 iks_insert_attrib(iq, "type", "result");
2198 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2199 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2200 iks_insert_attrib(confirm, "node", "confirmaccount");
2201 iks_insert_attrib(confirm, "name", "Confirm AIM account");
2202 iks_insert_attrib(confirm, "jid", client->user);
2203 iks_insert_node(iq, query);
2204 iks_insert_node(query, confirm);
2205 ast_aji_send(client, iq);
2207 ast_log(LOG_ERROR, "Out of memory.\n");
2212 iks_delete(confirm);
2214 } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
2215 iks *iq, *query, *feature;
2218 query = iks_new("query");
2219 feature = iks_new("feature");
2221 if (iq && query && feature && client) {
2222 iks_insert_attrib(iq, "from", client->user);
2223 iks_insert_attrib(iq, "to", pak->from->full);
2224 iks_insert_attrib(iq, "id", pak->id);
2225 iks_insert_attrib(iq, "type", "result");
2226 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2227 iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2228 iks_insert_node(iq, query);
2229 iks_insert_node(query, feature);
2230 ast_aji_send(client, iq);
2232 ast_log(LOG_ERROR, "Out of memory.\n");
2237 iks_delete(feature);
2240 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2241 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2242 return IKS_FILTER_EAT;
2247 * \brief Handles \verbatim <iq> \endverbatim stanzas.
2248 * \param client the configured XMPP client we use to connect to a XMPP server
2252 static void aji_handle_iq(struct aji_client *client, iks *node)
2254 /*Nothing to see here */
2259 * \brief Handles \verbatim <message>\endverbatim stanzas.
2260 * Adds the incoming message to the client's message list.
2261 * \param client the configured XMPP client we use to connect to a XMPP server
2262 * \param pak ikspak the node
2264 static void aji_handle_message(struct aji_client *client, ikspak *pak)
2266 struct aji_message *insert;
2268 struct ast_msg *msg;
2270 ast_debug(3, "client %s received a message\n", client->name);
2272 if (!(insert = ast_calloc(1, sizeof(*insert)))) {
2276 insert->arrived = ast_tvnow();
2278 /* wake up threads waiting for messages */
2279 ast_mutex_lock(&messagelock);
2280 ast_cond_broadcast(&message_received_condition);
2281 ast_mutex_unlock(&messagelock);
2283 if (iks_find_cdata(pak->x, "body")) {
2284 insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2287 ast_copy_string(insert->id, pak->id, sizeof(insert->id));
2290 /* insert will furtherly be added to message list */
2291 insert->from = ast_strdup(pak->from->full);
2292 if (!insert->from) {
2294 ast_log(LOG_ERROR, "Memory allocation failure\n");
2297 ast_debug(3, "message comes from %s\n", insert->from);
2300 if (client->send_to_dialplan) {
2301 if ((msg = ast_msg_alloc())) {
2304 res = ast_msg_set_to(msg, "xmpp:%s", client->user);
2305 res |= ast_msg_set_from(msg, "xmpp:%s", insert->from);
2306 res |= ast_msg_set_body(msg, "%s", insert->message);
2307 res |= ast_msg_set_context(msg, "%s", client->context);
2310 ast_msg_destroy(msg);
2319 /* remove old messages received from this JID
2320 * and insert received message */
2321 deleted = delete_old_messages(client, pak->from->partial);
2322 ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2323 AST_LIST_LOCK(&client->messages);
2324 AST_LIST_INSERT_HEAD(&client->messages, insert, list);
2325 AST_LIST_UNLOCK(&client->messages);
2330 * \brief handles \verbatim <presence>\endverbatim stanzas.
2331 * \param client the configured XMPP client we use to connect to a XMPP server
2334 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
2336 int status, priority;
2337 struct aji_buddy *buddy;
2338 struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
2339 char *ver, *node, *descrip, *type;
2341 if (client->state != AJI_CONNECTED)
2342 aji_create_buddy(pak->from->partial, client);
2344 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2345 if (!buddy && pak->from->partial) {
2346 /* allow our jid to be used to log in with another resource */
2347 if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
2348 aji_create_buddy(pak->from->partial, client);
2350 ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
2353 type = iks_find_attrib(pak->x, "type");
2354 if (client->component && type &&!strcasecmp("probe", type)) {
2355 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2356 ast_verbose("what i was looking for \n");
2358 ASTOBJ_WRLOCK(buddy);
2359 status = (pak->show) ? pak->show : 6;
2360 priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
2361 tmp = buddy->resources;
2362 descrip = ast_strdup(iks_find_cdata(pak->x, "status"));
2364 while (tmp && pak->from->resource) {
2365 if (!strcasecmp(tmp->resource, pak->from->resource)) {
2366 tmp->status = status;
2367 if (tmp->description) {
2368 ast_free(tmp->description);
2370 tmp->description = descrip;
2372 if (status == 6) { /* Sign off Destroy resource */
2373 if (last && found->next) {
2374 last->next = found->next;
2377 buddy->resources = found->next;
2379 buddy->resources = NULL;
2381 } else if (!found->next) {
2385 buddy->resources = NULL;
2392 /* resource list is sorted by descending priority */
2393 if (tmp->priority != priority) {
2394 found->priority = priority;
2395 if (!last && !found->next) {
2396 /* resource was found to be unique,
2400 /* search for resource in our list
2401 and take it out for the moment */
2403 last->next = found->next;
2405 buddy->resources = found->next;
2409 tmp = buddy->resources;
2410 if (!buddy->resources) {
2411 buddy->resources = found;
2413 /* priority processing */
2415 /* insert resource back according to
2416 its priority value */
2417 if (found->priority > tmp->priority) {
2419 /* insert within list */
2425 buddy->resources = found;
2430 /* insert at the end of the list */
2445 /* resource not found in our list, create it */
2446 if (!found && status != 6 && pak->from->resource) {
2447 found = ast_calloc(1, sizeof(*found));
2450 ast_log(LOG_ERROR, "Out of memory!\n");
2451 ASTOBJ_UNLOCK(buddy);
2452 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2455 ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
2456 found->status = status;
2457 found->description = descrip;
2458 found->priority = priority;
2461 tmp = buddy->resources;
2463 if (found->priority > tmp->priority) {
2469 buddy->resources = found;
2481 buddy->resources = found;
2485 ASTOBJ_UNLOCK(buddy);
2486 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2488 node = iks_find_attrib(iks_find(pak->x, "c"), "node");
2489 ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
2491 /* handle gmail client's special caps:c tag */
2492 if (!node && !ver) {
2493 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
2494 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
2497 /* retrieve capabilites of the new resource */
2498 if (status != 6 && found && !found->cap) {
2499 found->cap = aji_find_version(node, ver, pak);
2500 if (gtalk_yuck(pak->x)) { /* gtalk should do discover */
2501 found->cap->jingle = 1;
2503 if (found->cap->jingle) {
2504 ast_debug(1, "Special case for google till they support discover.\n");
2508 query = iks_new("query");
2510 iks_insert_attrib(iq, "type", "get");
2511 iks_insert_attrib(iq, "to", pak->from->full);
2512 iks_insert_attrib(iq, "from", client->jid->full);
2513 iks_insert_attrib(iq, "id", client->mid);
2514 ast_aji_increment_mid(client->mid);
2515 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2516 iks_insert_node(iq, query);
2517 ast_aji_send(client, iq);
2519 ast_log(LOG_ERROR, "Out of memory.\n");
2525 switch (pak->subtype) {
2526 case IKS_TYPE_AVAILABLE:
2527 ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
2529 case IKS_TYPE_UNAVAILABLE:
2530 ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
2533 ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
2535 switch (pak->show) {
2536 case IKS_SHOW_UNAVAILABLE:
2537 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2539 case IKS_SHOW_AVAILABLE:
2540 ast_debug(3, "JABBER: type is available\n");
2543 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2546 ast_debug(3, "JABBER: type is away\n");
2549 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2552 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2555 ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
2559 manager_event(EVENT_FLAG_USER, "JabberStatus",
2560 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
2561 "\r\nDescription: %s\r\n",
2562 client->name, pak->from->partial, found->resource, found->status,
2563 found->priority, S_OR(found->description, ""));
2565 manager_event(EVENT_FLAG_USER, "JabberStatus",
2566 "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
2567 client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
2573 * \brief handles subscription requests.
2574 * \param client the configured XMPP client we use to connect to a XMPP server
2575 * \param pak ikspak iksemel packet.
2578 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
2580 iks *presence = NULL, *status = NULL;
2581 struct aji_buddy* buddy = NULL;
2583 switch (pak->subtype) {
2584 case IKS_TYPE_SUBSCRIBE:
2585 if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
2586 presence = iks_new("presence");
2587 status = iks_new("status");
2588 if (presence && status) {
2589 iks_insert_attrib(presence, "type", "subscribed");
2590 iks_insert_attrib(presence, "to", pak->from->full);
2591 iks_insert_attrib(presence, "from", client->jid->full);
2593 iks_insert_attrib(presence, "id", pak->id);
2594 iks_insert_cdata(status, "Asterisk has approved subscription", 0);
2595 iks_insert_node(presence, status);
2596 ast_aji_send(client, presence);
2598 ast_log(LOG_ERROR, "Unable to allocate nodes\n");
2601 iks_delete(presence);
2605 if (client->component)
2606 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2607 case IKS_TYPE_SUBSCRIBED:
2608 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2609 if (!buddy && pak->from->partial) {
2610 aji_create_buddy(pak->from->partial, client);
2612 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2615 ast_verb(5, "JABBER: This is a subcription of type %i\n", pak->subtype);
2620 * \brief sends messages.
2621 * \param client the configured XMPP client we use to connect to a XMPP server
2624 * \retval IKS_OK success
2625 * \retval -1 failure
2627 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
2629 return aji_send_raw_chat(client, 0, NULL, address, message);
2633 * \brief sends message to a groupchat
2634 * Prior to sending messages to a groupchat, one must be connected to it.
2635 * \param client the configured XMPP client we use to connect to a XMPP server
2636 * \param nick the nickname we use in the chatroom
2637 * \param address the user the messages must be sent to
2638 * \param message the message to send
2639 * \return IKS_OK on success, any other value on failure
2641 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
2642 return aji_send_raw_chat(client, 1, nick, address, message);
2646 * \brief sends messages.
2647 * \param client the configured XMPP client we use to connect to a XMPP server
2649 * \param nick the nickname we use in chatrooms
2652 * \return IKS_OK on success, any other value on failure
2654 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
2657 iks *message_packet = NULL;
2658 char from[AJI_MAX_JIDLEN];
2659 /* the nickname is used only in component mode */
2660 if (nick && client->component) {
2661 snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
2663 snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
2666 if (client->state != AJI_CONNECTED) {
2667 ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
2671 message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
2672 if (!message_packet) {
2673 ast_log(LOG_ERROR, "Out of memory.\n");
2676 iks_insert_attrib(message_packet, "from", from);
2677 res = ast_aji_send(client, message_packet);
2678 iks_delete(message_packet);
2684 * \brief create a chatroom.
2685 * \param client the configured XMPP client we use to connect to a XMPP server
2686 * \param room name of room
2687 * \param server name of server
2688 * \param topic topic for the room.
2691 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
2698 iks_insert_attrib(iq, "type", "get");
2699 iks_insert_attrib(iq, "to", server);
2700 iks_insert_attrib(iq, "id", client->mid);
2701 ast_aji_increment_mid(client->mid);
2702 ast_aji_send(client, iq);
2704 ast_log(LOG_ERROR, "Out of memory.\n");
2713 * \brief join a chatroom.
2714 * \param client the configured XMPP client we use to connect to a XMPP server
2715 * \param room room to join
2716 * \param nick the nickname to use in this room
2717 * \return IKS_OK on success, any other value on failure.
2719 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
2721 return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
2725 * \brief leave a chatroom.
2726 * \param client the configured XMPP client we use to connect to a XMPP server
2727 * \param room room to leave
2728 * \param nick the nickname used in this room
2729 * \return IKS_OK on success, any other value on failure.
2731 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
2733 return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
2736 * \brief invite to a chatroom.
2737 * \param client the configured XMPP client we use to connect to a XMPP server
2743 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
2746 iks *invite, *body, *namespace;
2748 invite = iks_new("message");
2749 body = iks_new("body");
2750 namespace = iks_new("x");
2751 if (client && invite && body && namespace) {
2752 iks_insert_attrib(invite, "to", user);
2753 iks_insert_attrib(invite, "id", client->mid);
2754 ast_aji_increment_mid(client->mid);
2755 iks_insert_cdata(body, message, 0);
2756 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
2757 iks_insert_attrib(namespace, "jid", room);
2758 iks_insert_node(invite, body);
2759 iks_insert_node(invite, namespace);
2760 res = ast_aji_send(client, invite);
2762 ast_log(LOG_ERROR, "Out of memory.\n");
2766 iks_delete(namespace);
2774 * \brief receive message loop.
2778 static void *aji_recv_loop(void *data)
2780 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2783 while (res != IKS_OK) {
2784 ast_debug(3, "JABBER: Connecting.\n");
2785 res = aji_reconnect(client);
2790 if (res == IKS_NET_RWERR || client->timeout == 0) {
2791 while (res != IKS_OK) {
2792 ast_debug(3, "JABBER: reconnecting.\n");
2793 res = aji_reconnect(client);
2798 res = aji_recv(client, 1);
2800 if (client->state == AJI_DISCONNECTING) {
2801 ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
2805 /* Decrease timeout if no data received, and delete
2806 * old messages globally */
2807 if (res == IKS_NET_EXPIRED) {
2809 delete_old_messages_all(client);
2811 if (res == IKS_HOOK) {
2812 ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
2813 } else if (res == IKS_NET_TLSFAIL) {
2814 ast_log(LOG_ERROR, "JABBER: Failure in TLS.\n");
2815 } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
2816 res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
2817 if (res == IKS_OK) {
2818 client->timeout = 50;
2820 ast_log(LOG_WARNING, "JABBER: Network Timeout\n");
2822 } else if (res == IKS_NET_RWERR) {
2823 ast_log(LOG_WARNING, "JABBER: socket read error\n");
2826 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2831 * \brief increments the mid field for messages and other events.
2835 void ast_aji_increment_mid(char *mid)
2839 for (i = strlen(mid) - 1; i >= 0; i--) {
2840 if (mid[i] != 'z') {
2841 mid[i] = mid[i] + 1;
2850 * \brief attempts to register to a transport.
2851 * \param aji_client struct, and xml packet.
2852 * \return IKS_FILTER_EAT.
2854 /*allows for registering to transport , was too sketch and is out for now. */
2855 static int aji_register_transport(void *data, ikspak *pak)
2857 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2859 struct aji_buddy *buddy = NULL;
2860 iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
2862 if (client && send) {
2863 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2864 ASTOBJ_RDLOCK(iterator);
2865 if (iterator->btype == AJI_TRANS) {
2868 ASTOBJ_UNLOCK(iterator);
2870 iks_filter_remove_hook(client->f, aji_register_transport);
2871 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);
2872 iks_insert_attrib(send, "to", buddy->host);
2873 iks_insert_attrib(send, "id", client->mid);
2874 ast_aji_increment_mid(client->mid);
2875 iks_insert_attrib(send, "from", client->user);
2876 res = ast_aji_send(client, send);
2878 ast_log(LOG_ERROR, "Out of memory.\n");
2882 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2883 return IKS_FILTER_EAT;
2887 * \brief attempts to register to a transport step 2.
2888 * \param aji_client struct, and xml packet.
2889 * \return IKS_FILTER_EAT.
2891 /* more of the same blob of code, too wonky for now*/
2892 static int aji_register_transport2(void *data, ikspak *pak)
2894 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2896 struct aji_buddy *buddy = NULL;
2898 iks *regiq = iks_new("iq");
2899 iks *regquery = iks_new("query");
2900 iks *reguser = iks_new("username");
2901 iks *regpass = iks_new("password");
2903 if (client && regquery && reguser && regpass && regiq) {
2904 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2905 ASTOBJ_RDLOCK(iterator);
2906 if (iterator->btype == AJI_TRANS)
2907 buddy = iterator; ASTOBJ_UNLOCK(iterator);
2909 iks_filter_remove_hook(client->f, aji_register_transport2);
2910 iks_insert_attrib(regiq, "to", buddy->host);
2911 iks_insert_attrib(regiq, "type", "set");
2912 iks_insert_attrib(regiq, "id", client->mid);
2913 ast_aji_increment_mid(client->mid);
2914 iks_insert_attrib(regiq, "from", client->user);
2915 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2916 iks_insert_cdata(reguser, buddy->user, 0);
2917 iks_insert_cdata(regpass, buddy->pass, 0);
2918 iks_insert_node(regiq, regquery);
2919 iks_insert_node(regquery, reguser);
2920 iks_insert_node(regquery, regpass);
2921 res = ast_aji_send(client, regiq);
2923 ast_log(LOG_ERROR, "Out of memory.\n");
2927 iks_delete(regquery);
2929 iks_delete(reguser);
2931 iks_delete(regpass);
2932 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2933 return IKS_FILTER_EAT;
2939 * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2940 * \param client the configured XMPP client we use to connect to a XMPP server
2942 * \note The messages here should be configurable.
2944 static void aji_pruneregister(struct aji_client *client)
2946 iks *removeiq = iks_new("iq");
2947 iks *removequery = iks_new("query");
2948 iks *removeitem = iks_new("item");
2949 iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2950 if (!client || !removeiq || !removequery || !removeitem || !send) {
2951 ast_log(LOG_ERROR, "Out of memory.\n");
2955 iks_insert_node(removeiq, removequery);
2956 iks_insert_node(removequery, removeitem);
2957 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2958 ASTOBJ_RDLOCK(iterator);
2959 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2960 * be called at the same time */
2961 if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
2962 ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2963 "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2964 " so I am no longer subscribing to your presence.\n"));
2965 ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2966 "GoodBye. You are no longer in the Asterisk config file so I am removing"
2967 " your access to my presence.\n"));
2968 iks_insert_attrib(removeiq, "from", client->jid->full);
2969 iks_insert_attrib(removeiq, "type", "set");
2970 iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2971 iks_insert_attrib(removeitem, "jid", iterator->name);
2972 iks_insert_attrib(removeitem, "subscription", "remove");
2973 ast_aji_send(client, removeiq);
2974 } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2975 ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
2976 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2977 ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2979 ASTOBJ_UNLOCK(iterator);
2983 iks_delete(removeiq);
2984 iks_delete(removequery);
2985 iks_delete(removeitem);
2988 ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, ast_aji_buddy_destroy);
2993 * \brief filters the roster packet we get back from server.
2995 * \param pak ikspak iksemel packet.
2996 * \return IKS_FILTER_EAT.
2998 static int aji_filter_roster(void *data, ikspak *pak)
3000 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3003 struct aji_buddy *buddy;
3005 client->state = AJI_CONNECTED;
3006 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3007 ASTOBJ_RDLOCK(iterator);
3008 x = iks_child(pak->query);
3011 if (!iks_strcmp(iks_name(x), "item")) {
3012 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
3014 ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
3020 ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
3024 ASTOBJ_UNLOCK(iterator);
3027 x = iks_child(pak->query);
3030 if (iks_strcmp(iks_name(x), "item") == 0) {
3031 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3032 ASTOBJ_RDLOCK(iterator);
3033 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
3035 ASTOBJ_UNLOCK(iterator);
3039 /* found buddy, don't create a new one */
3044 buddy = ast_calloc(1, sizeof(*buddy));
3046 ast_log(LOG_WARNING, "Out of memory\n");
3047 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3051 ASTOBJ_WRLOCK(buddy);
3052 ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
3053 ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
3054 if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
3055 ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
3057 } else if (ast_test_flag(&client->flags, AJI_AUTOREGISTER)) {
3058 if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
3059 /* subscribe to buddy's presence only
3060 if we really need to */
3061 ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
3064 ASTOBJ_UNLOCK(buddy);
3066 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
3067 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
3074 aji_pruneregister(client);
3076 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3077 return IKS_FILTER_EAT;
3082 * \brief reconnect to jabber server
3083 * \param client the configured XMPP client we use to connect to a XMPP server
3086 static int aji_reconnect(struct aji_client *client)
3090 if (client->state) {
3091 client->state = AJI_DISCONNECTED;
3093 client->timeout = 50;
3095 iks_parser_reset(client->p);
3097 if (client->authorized) {
3098 client->authorized = 0;
3101 res = aji_initialize(client);
3108 * \brief Get the roster of jabber users
3109 * \param client the configured XMPP client we use to connect to a XMPP server
3112 static int aji_get_roster(struct aji_client *client)
3115 roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
3118 iks_insert_attrib(roster, "id", "roster");
3119 aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
3120 ast_aji_send(client, roster);
3130 * \brief connects as a client to jabber server.
3132 * \param pak ikspak iksemel packet
3135 static int aji_client_connect(void *data, ikspak *pak)
3137 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3138 int res = IKS_FILTER_PASS;
3141 if (client->state == AJI_DISCONNECTED) {
3142 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);
3143 client->state = AJI_CONNECTING;
3144 client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
3145 if (!client->component) { /*client*/
3146 aji_get_roster(client);
3148 if (client->distribute_events) {
3149 aji_init_event_distribution(client);
3152 iks_filter_remove_hook(client->f, aji_client_connect);
3153 /* Once we remove the hook for this routine, we must return EAT or we will crash or corrupt memory */
3154 res = IKS_FILTER_EAT;
3157 ast_log(LOG_ERROR, "Out of memory.\n");
3160 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3166 * \brief prepares client for connect.
3167 * \param client the configured XMPP client we use to connect to a XMPP server
3170 static int aji_initialize(struct aji_client *client)
3172 int connected = IKS_NET_NOCONN;
3175 /* reset stream flags */
3176 client->stream_flags = 0;
3178 /* If it's a component, connect to user, otherwise, connect to server */
3179 connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
3181 if (connected == IKS_NET_NOCONN) {
3182 ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
3184 } else if (connected == IKS_NET_NODNS) {
3185 ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name,
3186 S_OR(client->serverhost, client->jid->server));
3194 * \brief disconnect from jabber server.
3195 * \param client the configured XMPP client we use to connect to a XMPP server
3198 int ast_aji_disconnect(struct aji_client *client)
3201 ast_verb(4, "JABBER: Disconnecting\n");
3203 if (client->stream_flags & SECURE) {
3204 SSL_shutdown(client->ssl_session);
3205 SSL_CTX_free(client->ssl_context);
3206 SSL_free(client->ssl_session);
3209 iks_disconnect(client->p);
3210 iks_parser_delete(client->p);
3211 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3218 * \brief Callback function for MWI events
3220 * \param data void pointer to ast_client structure
3223 static void aji_mwi_cb(const struct ast_event *ast_event, void *data)
3225 const char *mailbox;
3226 const char *context;
3229 struct aji_client *client;
3230 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
3232 /* If the event didn't originate from this server, don't send it back out. */
3233 ast_debug(1, "Returning here\n");
3237 client = ASTOBJ_REF((struct aji_client *) data);
3238 mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
3239 context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
3240 snprintf(oldmsgs, sizeof(oldmsgs), "%d",
3241 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
3242 snprintf(newmsgs, sizeof(newmsgs), "%d",
3243 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
3244 aji_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
3245 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3249 * \brief Callback function for device state events
3251 * \param data void pointer to ast_client structure
3254 static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
3257 const char *device_state;
3258 struct aji_client *client;
3259 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
3261 /* If the event didn't originate from this server, don't send it back out. */
3262 ast_debug(1, "Returning here\n");
3266 client = ASTOBJ_REF((struct aji_client *) data);
3267 device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
3268 device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
3269 aji_publish_device_state(client, device, device_state);
3270 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3274 * \brief Initialize collections for event distribution
3275 * \param client the configured XMPP client we use to connect to a XMPP server
3278 static void aji_init_event_distribution(struct aji_client *client)
3281 mwi_sub = ast_event_subscribe(AST_EVENT_MWI, aji_mwi_cb, "aji_mwi_subscription",
3282 client, AST_EVENT_IE_END);
3284 if (!device_state_sub) {
3285 if (ast_enable_distributed_devstate()) {
3288 device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
3289 aji_devstate_cb, "aji_devstate_subscription", client, AST_EVENT_IE_END);
3290 ast_event_dump_cache(device_state_sub);
3293 aji_pubsub_subscribe(client, "device_state");
3294 aji_pubsub_subscribe(client, "message_waiting");
3295 iks_filter_add_rule(client->f, aji_handle_pubsub_event, client, IKS_RULE_TYPE,
3296 IKS_PAK_MESSAGE, IKS_RULE_FROM, client->pubsub_node, IKS_RULE_DONE);
3297 iks_filter_add_rule(client->f, aji_handle_pubsub_error, client, IKS_RULE_TYPE,
3298 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
3303 * \brief Callback for handling PubSub events
3304 * \param data void pointer to aji_client structure
3306 * \return IKS_FILTER_EAT
3308 static int aji_handle_pubsub_event(void *data, ikspak *pak)
3310 char *item_id, *device_state, *context;
3311 int oldmsgs, newmsgs;
3312 iks *item, *item_content;
3313 struct ast_eid pubsub_eid;
3314 struct ast_event *event;
3315 item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
3317 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
3318 return IKS_FILTER_EAT;
3320 item_id = iks_find_attrib(item, "id");
3321 item_content = iks_child(item);
3322 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
3323 if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
3324 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
3325 return IKS_FILTER_EAT;
3327 if (!strcasecmp(iks_name(item_content), "state")) {
3328 device_state = iks_find_cdata(item, "state");
3329 if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
3330 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
3331 AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
3332 AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
3333 AST_EVENT_IE_END))) {
3334 return IKS_FILTER_EAT;
3336 } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
3337 context = strsep(&item_id, "@");
3338 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
3339 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
3340 if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
3341 AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
3342 AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
3343 AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
3344 AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
3345 &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
3346 return IKS_FILTER_EAT;
3349 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
3350 iks_name(item_content));
3351 return IKS_FILTER_EAT;
3353 ast_event_queue_and_cache(event);
3354 return IKS_FILTER_EAT;
3358 * \brief Add Owner affiliations for pubsub node
3359 * \param client the configured XMPP client we use to connect to a XMPP server
3360 * \param node the name of the node to which to add affiliations
3363 static void aji_create_affiliations(struct aji_client *client, const char *node)
3365 iks *modify_affiliates = aji_pubsub_iq_create(client, "set");
3366 iks *pubsub, *affiliations, *affiliate;
3367 pubsub = iks_insert(modify_affiliates, "pubsub");
3368 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
3369 affiliations = iks_insert(pubsub, "affiliations");
3370 iks_insert_attrib(affiliations, "node", node);
3371 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3372 ASTOBJ_RDLOCK(iterator);
3373 affiliate = iks_insert(affiliations, "affiliation");
3374 iks_insert_attrib(affiliate, "jid", iterator->name);
3375 iks_insert_attrib(affiliate, "affiliation", "owner");
3376 ASTOBJ_UNLOCK(iterator);
3378 ast_aji_send(client, modify_affiliates);
3379 iks_delete(modify_affiliates);
3383 * \brief Subscribe to a PubSub node
3384 * \param client the configured XMPP client we use to connect to a XMPP server
3385 * \param node the name of the node to which to subscribe
3388 static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
3390 iks *request = aji_pubsub_iq_create(client, "set");
3391 iks *pubsub, *subscribe;
3393 pubsub = iks_insert(request, "pubsub");
3394 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3395 subscribe = iks_insert(pubsub, "subscribe");
3396 iks_insert_attrib(subscribe, "jid", client->jid->partial);
3397 iks_insert_attrib(subscribe, "node", node);
3398 if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3399 iks *options, *x, *sub_options, *sub_type, *sub_depth;
3400 options = iks_insert(pubsub, "options");
3401 x = iks_insert(options, "x");
3402 iks_insert_attrib(x, "xmlns", "jabber:x:data");
3403 iks_insert_attrib(x, "type", "submit");
3404 sub_options = iks_insert(x, "field");
3405 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
3406 iks_insert_attrib(sub_options, "type", "hidden");
3407 iks_insert_cdata(iks_insert(sub_options, "value"),
3408 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
3409 sub_type = iks_insert(x, "field");
3410 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
3411 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
3412 sub_depth = iks_insert(x, "field");
3413 iks_insert_attrib(sub_type, "var", "pubsub#subscription_depth");
3414 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
3416 ast_aji_send(client, request);
3417 iks_delete(request);
3421 * \brief Build the skeleton of a publish
3422 * \param client the configured XMPP client we use to connect to a XMPP server
3423 * \param node Name of the node that will be published to
3427 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
3428 const char *event_type)
3430 iks *request = aji_pubsub_iq_create(client, "set");
3431 iks *pubsub, *publish, *item;
3432 pubsub = iks_insert(request, "pubsub");
3433 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3434 publish = iks_insert(pubsub, "publish");
3435 if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3436 iks_insert_attrib(publish, "node", node);
3438 iks_insert_attrib(publish, "node", event_type);
3440 item = iks_insert(publish, "item");
3441 iks_insert_attrib(item, "id", node);
3447 * \brief Publish device state to a PubSub node
3448 * \param client the configured XMPP client we use to connect to a XMPP server
3449 * \param device the name of the device whose state to publish
3450 * \param device_state the state to publish
3453 static void aji_publish_device_state(struct aji_client *client, const char *device,
3454 const char *device_state)
3456 iks *request = aji_build_publish_skeleton(client, device, "device_state");
3459 if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
3460 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3461 aji_create_pubsub_node(client, "leaf", device, "device_state");
3463 aji_create_pubsub_node(client, NULL, device, NULL);
3466 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3467 state = iks_insert(request, "state");
3468 iks_insert_attrib(state, "xmlns", "http://asterisk.org");
3469 iks_insert_attrib(state, "eid", eid_str);
3470 iks_insert_cdata(state, device_state, strlen(device_state));
3471 ast_aji_send(client, iks_root(request));
3472 iks_delete(request);
3476 * \brief Publish MWI to a PubSub node
3477 * \param client the configured XMPP client we use to connect to a XMPP server
3478 * \param mailbox The mailbox
3479 * \param context The context
3480 * \param oldmsgs Old messages
3481 * \param newmsgs New messages
3484 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
3485 const char *context, const char *oldmsgs, const char *newmsgs)
3487 char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT];
3489 iks *mailbox_node, *request;
3490 snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
3491 request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting");
3492 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3493 mailbox_node = iks_insert(request, "mailbox");
3494 iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
3495 iks_insert_attrib(mailbox_node, "eid", eid_str);
3496 iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
3497 iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
3498 ast_aji_send(client, iks_root(request));
3499 iks_delete(request);
3503 * \brief Create an IQ packet
3504 * \param client the configured XMPP client we use to connect to a XMPP server
3505 * \param type the type of IQ packet to create
3508 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type)
3510 iks *request = iks_new("iq");
3512 iks_insert_attrib(request, "to", client->pubsub_node);
3513 iks_insert_attrib(request, "from", client->jid->full);
3514 iks_insert_attrib(request, "type", type);
3515 ast_aji_increment_mid(client->mid);
3516 iks_insert_attrib(request, "id", client->mid);
3520 static int aji_handle_pubsub_error(void *data, ikspak *pak)
3526 iks *orig_pubsub = iks_find(pak->x, "pubsub");
3527 struct aji_client *client;
3529 ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
3530 return IKS_FILTER_EAT;
3532 orig_request = iks_child(orig_pubsub);
3533 error = iks_find_attrib(iks_find(pak->x, "error"), "code");
3534 node_name = iks_find_attrib(orig_request, "node");
3535 if (!sscanf(error, "%30d", &error_num)) {
3536 return IKS_FILTER_EAT;
3538 if (error_num > 399 && error_num < 500 && error_num != 404) {
3540 "Error performing operation on PubSub node %s, %s.\n", node_name, error);
3541 return IKS_FILTER_EAT;
3542 } else if (error_num > 499 && error_num < 600) {
3543 ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
3544 return IKS_FILTER_EAT;
3547 client = ASTOBJ_REF((struct aji_client *) data);
3549 if (!strcasecmp(iks_name(orig_request), "publish")) {
3551 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3552 if (iks_find(iks_find(orig_request, "item"), "state")) {
3553 aji_create_pubsub_leaf(client, "device_state", node_name);
3554 } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
3555 aji_create_pubsub_leaf(client, "message_waiting", node_name);
3558 aji_create_pubsub_node(client, NULL, node_name, NULL);
3560 request = aji_pubsub_iq_create(client, "set");
3561 iks_insert_node(request, orig_pubsub);
3562 ast_aji_send(client, request);
3563 iks_delete(request);
3564 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3565 return IKS_FILTER_EAT;
3566 } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
3567 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3568 aji_create_pubsub_collection(client, node_name);