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
33 /*! \li \ref res_jabber.c uses the configuration file \ref jabber.conf
34 * \addtogroup configuration_file Configuration Files
38 * \page jabber.conf jabber.conf
39 * \verbinclude jabber.conf.sample
43 <defaultenabled>no</defaultenabled>
44 <depend>iksemel</depend>
45 <use type="external">openssl</use>
46 <support_level>deprecated</support_level>
47 <replacement>res_xmpp</replacement>
52 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
57 #include "asterisk/channel.h"
58 #include "asterisk/jabber.h"
59 #include "asterisk/file.h"
60 #include "asterisk/config.h"
61 #include "asterisk/callerid.h"
62 #include "asterisk/lock.h"
63 #include "asterisk/cli.h"
64 #include "asterisk/app.h"
65 #include "asterisk/pbx.h"
66 #include "asterisk/md5.h"
67 #include "asterisk/acl.h"
68 #include "asterisk/utils.h"
69 #include "asterisk/module.h"
70 #include "asterisk/astobj.h"
71 #include "asterisk/astdb.h"
72 #include "asterisk/manager.h"
73 #include "asterisk/event.h"
74 #include "asterisk/devicestate.h"
75 #include "asterisk/message.h"
78 <application name="JabberSend" language="en_US" module="res_jabber">
80 Sends an XMPP message to a buddy.
83 <parameter name="account" required="true">
84 <para>The local named account to listen on (specified in
87 <parameter name="jid" required="true">
88 <para>Jabber ID of the buddy to send the message to. It can be a
89 bare JID (username@domain) or a full JID (username@domain/resource).</para>
91 <parameter name="message" required="true">
92 <para>The message to send.</para>
96 <para>Sends the content of <replaceable>message</replaceable> as text message
97 from the given <replaceable>account</replaceable> to the buddy identified by
98 <replaceable>jid</replaceable></para>
99 <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
100 to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
101 <replaceable>asterisk</replaceable>, configured in jabber.conf.</para>
104 <ref type="function">JABBER_STATUS</ref>
105 <ref type="function">JABBER_RECEIVE</ref>
108 <function name="JABBER_RECEIVE" language="en_US" module="res_jabber">
113 <parameter name="account" required="true">
114 <para>The local named account to listen on (specified in
117 <parameter name="jid" required="true">
118 <para>Jabber ID of the buddy to receive message from. It can be a
119 bare JID (username@domain) or a full JID (username@domain/resource).</para>
121 <parameter name="timeout">
122 <para>In seconds, defaults to <literal>20</literal>.</para>
126 <para>Receives a text message on the given <replaceable>account</replaceable>
127 from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
128 <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
129 sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
130 the <replaceable>asterisk</replaceable> XMPP account configured in jabber.conf.</para>
133 <ref type="function">JABBER_STATUS</ref>
134 <ref type="application">JabberSend</ref>
137 <function name="JABBER_STATUS" language="en_US" module="res_jabber">
139 Retrieves a buddy's status.
142 <parameter name="account" required="true">
143 <para>The local named account to listen on (specified in
146 <parameter name="jid" required="true">
147 <para>Jabber ID of the buddy to receive message from. It can be a
148 bare JID (username@domain) or a full JID (username@domain/resource).</para>
152 <para>Retrieves the numeric status associated with the buddy identified
153 by <replaceable>jid</replaceable>.
154 If the buddy does not exist in the buddylist, returns 7.</para>
155 <para>Status will be 1-7.</para>
156 <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
157 <para>If not in roster variable will be set to 7.</para>
158 <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
159 <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
160 the associated XMPP account configured in jabber.conf.</para>
163 <ref type="function">JABBER_RECEIVE</ref>
164 <ref type="application">JabberSend</ref>
167 <application name="JabberSendGroup" language="en_US" module="res_jabber">
169 Send a Jabber Message to a specified chat room
172 <parameter name="Jabber" required="true">
173 <para>Client or transport Asterisk uses to connect to Jabber.</para>
175 <parameter name="RoomJID" required="true">
176 <para>XMPP/Jabber JID (Name) of chat room.</para>
178 <parameter name="Message" required="true">
179 <para>Message to be sent to the chat room.</para>
181 <parameter name="Nickname" required="false">
182 <para>The nickname Asterisk uses in the chat room.</para>
186 <para>Allows user to send a message to a chat room via XMPP.</para>
187 <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>
190 <application name="JabberJoin" language="en_US" module="res_jabber">
195 <parameter name="Jabber" required="true">
196 <para>Client or transport Asterisk uses to connect to Jabber.</para>
198 <parameter name="RoomJID" required="true">
199 <para>XMPP/Jabber JID (Name) of chat room.</para>
201 <parameter name="Nickname" required="false">
202 <para>The nickname Asterisk will use in the chat room.</para>
203 <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>
207 <para>Allows Asterisk to join a chat room.</para>
210 <application name="JabberLeave" language="en_US" module="res_jabber">
215 <parameter name="Jabber" required="true">
216 <para>Client or transport Asterisk uses to connect to Jabber.</para>
218 <parameter name="RoomJID" required="true">
219 <para>XMPP/Jabber JID (Name) of chat room.</para>
221 <parameter name="Nickname" required="false">
222 <para>The nickname Asterisk uses in the chat room.</para>
226 <para>Allows Asterisk to leave a chat room.</para>
229 <application name="JabberStatus" language="en_US" module="res_jabber">
231 Retrieve the status of a jabber list member
234 <parameter name="Jabber" required="true">
235 <para>Client or transport Asterisk users to connect to Jabber.</para>
237 <parameter name="JID" required="true">
238 <para>XMPP/Jabber JID (Name) of recipient.</para>
240 <parameter name="Variable" required="true">
241 <para>Variable to store the status of requested user.</para>
245 <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
246 <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
247 The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
259 <para>Extended Away.</para>
262 <para>Do Not Disturb.</para>
265 <para>Offline.</para>
268 <para>Not In Roster.</para>
273 <manager name="JabberSend" language="en_US" module="res_jabber">
275 Sends a message to a Jabber Client.
278 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
279 <parameter name="Jabber" required="true">
280 <para>Client or transport Asterisk uses to connect to JABBER.</para>
282 <parameter name="JID" required="true">
283 <para>XMPP/Jabber JID (Name) of recipient.</para>
285 <parameter name="Message" required="true">
286 <para>Message to be sent to the buddy.</para>
290 <para>Sends a message to a Jabber Client.</para>
295 /*!\todo This should really be renamed to xmpp.conf. For backwards compatibility, we
296 * need to read both files */
297 #define JABBER_CONFIG "jabber.conf"
299 /*-- Forward declarations */
300 static void aji_message_destroy(struct aji_message *obj);
301 static int aji_is_secure(struct aji_client *client);
303 static int aji_start_tls(struct aji_client *client);
304 static int aji_tls_handshake(struct aji_client *client);
306 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
307 static int aji_recv(struct aji_client *client, int timeout);
308 static int aji_send_header(struct aji_client *client, const char *to);
309 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
310 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
311 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
312 static int aji_act_hook(void *data, int type, iks *node);
313 static void aji_handle_iq(struct aji_client *client, iks *node);
314 static void aji_handle_message(struct aji_client *client, ikspak *pak);
315 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
316 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
317 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
318 static void *aji_recv_loop(void *data);
319 static int aji_initialize(struct aji_client *client);
320 static int aji_client_connect(void *data, ikspak *pak);
321 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
322 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
323 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
324 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
325 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
326 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
327 static int aji_create_client(char *label, struct ast_variable *var, int debug);
328 static int aji_create_buddy(char *label, struct aji_client *client);
329 static int aji_reload(int reload);
330 static int aji_load_config(int reload);
331 static void aji_pruneregister(struct aji_client *client);
332 static int aji_filter_roster(void *data, ikspak *pak);
333 static int aji_get_roster(struct aji_client *client);
334 static int aji_client_info_handler(void *data, ikspak *pak);
335 static int aji_dinfo_handler(void *data, ikspak *pak);
336 static int aji_ditems_handler(void *data, ikspak *pak);
337 static int aji_register_query_handler(void *data, ikspak *pak);
338 static int aji_register_approve_handler(void *data, ikspak *pak);
339 static int aji_reconnect(struct aji_client *client);
340 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd,
341 struct ast_cli_args *a);
342 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd,
343 struct ast_cli_args *a);
344 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
346 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
348 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
349 static int aji_receive_node_list(void *data, ikspak* pak);
350 static void aji_init_event_distribution(struct aji_client *client);
351 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type,
352 const char *name, const char *collection_name);
353 static iks* aji_build_node_config(iks *pubsub, const char *node_type,
354 const char *collection_name);
355 static void aji_create_pubsub_collection(struct aji_client *client,
356 const char *collection_name);
357 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
358 const char *leaf_name);
359 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
360 struct ast_cli_args *a);
361 static void aji_create_affiliations(struct aji_client *client, const char *node);
362 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
363 static void aji_publish_device_state(struct aji_client *client, const char * device,
364 const char *device_state, unsigned int cachable);
365 static int aji_handle_pubsub_error(void *data, ikspak *pak);
366 static int aji_handle_pubsub_event(void *data, ikspak *pak);
367 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
368 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name);
369 static iks* aji_build_node_request(struct aji_client *client, const char *collection);
370 static int aji_delete_node_list(void *data, ikspak* pak);
371 static void aji_pubsub_purge_nodes(struct aji_client *client,
372 const char* collection_name);
373 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
374 const char *context, const char *oldmsgs, const char *newmsgs);
375 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
376 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
377 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
378 const char *event_type, unsigned int cachable);
379 /* No transports in this version */
381 static int aji_create_transport(char *label, struct aji_client *client);
382 static int aji_register_transport(void *data, ikspak *pak);
383 static int aji_register_transport2(void *data, ikspak *pak);
386 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from);
388 static const struct ast_msg_tech msg_tech = {
390 .msg_send = msg_send_cb,
393 static struct ast_cli_entry aji_cli[] = {
394 AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
395 AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
396 AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
397 AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
398 AST_CLI_DEFINE(aji_cli_create_collection, "Creates a PubSub node collection."),
399 AST_CLI_DEFINE(aji_cli_list_pubsub_nodes, "Lists PubSub nodes"),
400 AST_CLI_DEFINE(aji_cli_create_leafnode, "Creates a PubSub leaf node"),
401 AST_CLI_DEFINE(aji_cli_delete_pubsub_node, "Deletes a PubSub node"),
402 AST_CLI_DEFINE(aji_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
405 static char *app_ajisend = "JabberSend";
406 static char *app_ajisendgroup = "JabberSendGroup";
407 static char *app_ajistatus = "JabberStatus";
408 static char *app_ajijoin = "JabberJoin";
409 static char *app_ajileave = "JabberLeave";
411 static struct aji_client_container clients;
412 static struct aji_capabilities *capabilities = NULL;
413 static struct ast_event_sub *mwi_sub = NULL;
414 static struct ast_event_sub *device_state_sub = NULL;
415 static ast_cond_t message_received_condition;
416 static ast_mutex_t messagelock;
418 /*! \brief Global flags, initialized to default values */
419 static struct ast_flags globalflags = { AJI_AUTOREGISTER | AJI_AUTOACCEPT };
421 /*! \brief PubSub flags, initialized to default values */
422 static struct ast_flags pubsubflags = { 0 };
425 * \brief Deletes the aji_client data structure.
426 * \param obj aji_client The structure we will delete.
429 void ast_aji_client_destroy(struct aji_client *obj)
431 struct aji_message *tmp;
432 ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, ast_aji_buddy_destroy);
433 ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
434 iks_filter_delete(obj->f);
435 iks_parser_delete(obj->p);
436 iks_stack_delete(obj->stack);
437 AST_LIST_LOCK(&obj->messages);
438 while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
439 aji_message_destroy(tmp);
441 AST_LIST_HEAD_DESTROY(&obj->messages);
447 * \brief Deletes the aji_buddy data structure.
448 * \param obj aji_buddy The structure we will delete.
451 void ast_aji_buddy_destroy(struct aji_buddy *obj)
453 struct aji_resource *tmp;
455 while ((tmp = obj->resources)) {
456 obj->resources = obj->resources->next;
457 ast_free(tmp->description);
466 * \brief Deletes the aji_message data structure.
467 * \param obj aji_message The structure we will delete.
470 static void aji_message_destroy(struct aji_message *obj)
476 ast_free(obj->message);
483 * \brief Find version in XML stream and populate our capabilities list
484 * \param node the node attribute in the caps element we'll look for or add to
486 * \param version the version attribute in the caps element we'll look for or
488 * \param pak struct The XML stanza we're processing
489 * \return a pointer to the added or found aji_version structure
491 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
493 struct aji_capabilities *list = NULL;
494 struct aji_version *res = NULL;
499 node = pak->from->full;
502 version = "none supplied.";
505 if (!strcasecmp(list->node, node)) {
506 res = list->versions;
508 if (!strcasecmp(res->version, version)) {
513 /* Specified version not found. Let's add it to
514 this node in our capabilities list */
516 res = ast_malloc(sizeof(*res));
518 ast_log(LOG_ERROR, "Out of memory!\n");
523 ast_copy_string(res->version, version, sizeof(res->version));
524 res->next = list->versions;
525 list->versions = res;
531 /* Specified node not found. Let's add it our capabilities list */
533 list = ast_malloc(sizeof(*list));
535 ast_log(LOG_ERROR, "Out of memory!\n");
538 res = ast_malloc(sizeof(*res));
540 ast_log(LOG_ERROR, "Out of memory!\n");
544 ast_copy_string(list->node, node, sizeof(list->node));
545 ast_copy_string(res->version, version, sizeof(res->version));
549 list->versions = res;
550 list->next = capabilities;
558 * \brief Find the aji_resource we want
559 * \param buddy aji_buddy A buddy
561 * \return aji_resource object
563 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
565 struct aji_resource *res = NULL;
566 if (!buddy || !name) {
569 res = buddy->resources;
571 if (!strcasecmp(res->resource, name)) {
581 * \brief Jabber GTalk function
583 * \return 1 on success, 0 on failure.
585 static int gtalk_yuck(iks *node)
587 if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) {
588 ast_debug(1, "Found resource with Googletalk voice capabilities\n");
590 } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1")) {
591 ast_debug(1, "Found resource with Gmail voice/video chat capabilities\n");
593 } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 video-v1 voice-v1")) {
594 ast_debug(1, "Found resource with Gmail voice/video chat capabilities (no camera)\n");
603 * \brief Setup the authentication struct
605 * \param pass password
609 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
613 iks_insert_attrib(x, "type", "set");
614 y = iks_insert(x, "query");
615 iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
616 iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
617 iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
621 snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
622 ast_sha1_hash(buf, sidpass);
623 iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
625 iks_insert_cdata(iks_insert(y, "password"), pass, 0);
632 * \brief Dial plan function status(). puts the status of watched user
633 * into a channel variable.
634 * \param chan ast_channel
639 static int aji_status_exec(struct ast_channel *chan, const char *data)
641 struct aji_client *client = NULL;
642 struct aji_buddy *buddy = NULL;
643 struct aji_resource *r = NULL;
647 static int deprecation_warning = 0;
648 AST_DECLARE_APP_ARGS(args,
651 AST_APP_ARG(variable);
653 AST_DECLARE_APP_ARGS(jid,
654 AST_APP_ARG(screenname);
655 AST_APP_ARG(resource);
658 if (deprecation_warning++ % 10 == 0) {
659 ast_log(LOG_WARNING, "JabberStatus is deprecated. Please use the JABBER_STATUS dialplan function in the future.\n");
663 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
666 s = ast_strdupa(data);
667 AST_STANDARD_APP_ARGS(args, s);
669 if (args.argc != 3) {
670 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
674 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
675 if (jid.argc < 1 || jid.argc > 2) {
676 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
680 if (!(client = ast_aji_get_client(args.sender))) {
681 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
684 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
686 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
687 ASTOBJ_UNREF(client, ast_aji_client_destroy);
690 r = aji_find_resource(buddy, jid.resource);
691 if (!r && buddy->resources) {
692 r = buddy->resources;
694 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
695 ASTOBJ_UNREF(client, ast_aji_client_destroy);
697 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
701 snprintf(status, sizeof(status), "%d", stat);
702 pbx_builtin_setvar_helper(chan, args.variable, status);
708 * \brief Dial plan funtcion to retrieve the status of a buddy.
709 * \param channel The associated ast_channel, if there is one
710 * \param data The account, buddy JID, and optional timeout
715 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
717 struct aji_client *client = NULL;
718 struct aji_buddy *buddy = NULL;
719 struct aji_resource *r = NULL;
721 AST_DECLARE_APP_ARGS(args,
725 AST_DECLARE_APP_ARGS(jid,
726 AST_APP_ARG(screenname);
727 AST_APP_ARG(resource);
731 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
734 AST_STANDARD_APP_ARGS(args, data);
736 if (args.argc != 2) {
737 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
741 AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
742 if (jid.argc < 1 || jid.argc > 2) {
743 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
747 if (!(client = ast_aji_get_client(args.sender))) {
748 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
751 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
753 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
754 ASTOBJ_UNREF(client, ast_aji_client_destroy);
757 r = aji_find_resource(buddy, jid.resource);
758 if (!r && buddy->resources) {
759 r = buddy->resources;
761 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
762 ASTOBJ_UNREF(client, ast_aji_client_destroy);
764 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
768 snprintf(buf, buflen, "%d", stat);
772 static struct ast_custom_function jabberstatus_function = {
773 .name = "JABBER_STATUS",
774 .read = acf_jabberstatus_read,
779 * \brief Dial plan function to receive a message.
780 * \param channel The associated ast_channel, if there is one
781 * \param data The account, JID, and optional timeout
786 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
790 int jidlen, resourcelen;
791 struct timeval start;
793 struct aji_client *client = NULL;
795 struct aji_message *tmp = NULL;
796 AST_DECLARE_APP_ARGS(args,
797 AST_APP_ARG(account);
799 AST_APP_ARG(timeout);
801 AST_DECLARE_APP_ARGS(jid,
802 AST_APP_ARG(screenname);
803 AST_APP_ARG(resource);
806 if (ast_strlen_zero(data)) {
807 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
811 parse = ast_strdupa(data);
812 AST_STANDARD_APP_ARGS(args, parse);
814 if (args.argc < 2 || args.argc > 3) {
815 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
819 parse = ast_strdupa(args.jid);
820 AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
821 if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
822 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
826 if (ast_strlen_zero(args.timeout)) {
829 sscanf(args.timeout, "%d", &timeout);
831 ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
836 jidlen = strlen(jid.screenname);
837 resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
839 client = ast_aji_get_client(args.account);
841 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
845 ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
849 if (ast_autoservice_start(chan) < 0) {
850 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
851 ASTOBJ_UNREF(client, ast_aji_client_destroy);
855 /* search the messages list, grab the first message that matches with
856 * the from JID we're expecting, and remove it from the messages list */
857 while (diff < timeout) {
858 struct timespec ts = { 0, };
862 wait = ast_tvadd(start, ast_tv(timeout, 0));
863 ts.tv_sec = wait.tv_sec;
864 ts.tv_nsec = wait.tv_usec * 1000;
866 /* wait up to timeout seconds for an incoming message */
867 ast_mutex_lock(&messagelock);
868 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
869 ast_mutex_unlock(&messagelock);
870 if (res == ETIMEDOUT) {
871 ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
875 AST_LIST_LOCK(&client->messages);
876 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
878 /* no resource provided, compare bare JIDs */
879 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
883 /* resource appended, compare bare JIDs and resources */
884 char *resource = strchr(tmp->from, '/');
885 if (!resource || strlen(resource) == 0) {
886 ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
887 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
892 if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
897 /* check if the message is not too old */
898 if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
899 ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
900 AST_LIST_REMOVE_CURRENT(list);
901 aji_message_destroy(tmp);
905 ast_copy_string(buf, tmp->message, buflen);
906 AST_LIST_REMOVE_CURRENT(list);
907 aji_message_destroy(tmp);
910 AST_LIST_TRAVERSE_SAFE_END;
911 AST_LIST_UNLOCK(&client->messages);
917 diff = ast_tvdiff_ms(ast_tvnow(), start);
920 ASTOBJ_UNREF(client, ast_aji_client_destroy);
921 if (ast_autoservice_stop(chan) < 0) {
922 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
925 /* return if we timed out */
927 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
934 static struct ast_custom_function jabberreceive_function = {
935 .name = "JABBER_RECEIVE",
936 .read = acf_jabberreceive_read,
941 * \brief Delete old messages from a given JID
942 * Messages stored during more than client->message_timeout are deleted
943 * \param client Asterisk's XMPP client
944 * \param from the JID we received messages from
945 * \retval the number of deleted messages
948 static int delete_old_messages(struct aji_client *client, char *from)
952 struct aji_message *tmp = NULL;
954 ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
958 /* remove old messages */
959 AST_LIST_LOCK(&client->messages);
960 if (AST_LIST_EMPTY(&client->messages)) {
961 AST_LIST_UNLOCK(&client->messages);
965 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
967 if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
968 AST_LIST_REMOVE_CURRENT(list);
969 aji_message_destroy(tmp);
972 } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
974 if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
975 AST_LIST_REMOVE_CURRENT(list);
976 aji_message_destroy(tmp);
981 AST_LIST_TRAVERSE_SAFE_END;
982 AST_LIST_UNLOCK(&client->messages);
989 * \brief Delete old messages
990 * Messages stored during more than client->message_timeout are deleted
991 * \param client Asterisk's XMPP client
992 * \retval the number of deleted messages
995 static int delete_old_messages_all(struct aji_client *client)
997 return delete_old_messages(client, NULL);
1001 * \brief Application to join a chat room
1002 * \param chan ast_channel
1003 * \param data Data is sender|jid|nickname.
1007 static int aji_join_exec(struct ast_channel *chan, const char *data)
1009 struct aji_client *client = NULL;
1011 char nick[AJI_MAX_RESJIDLEN];
1013 AST_DECLARE_APP_ARGS(args,
1014 AST_APP_ARG(sender);
1020 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1023 s = ast_strdupa(data);
1025 AST_STANDARD_APP_ARGS(args, s);
1026 if (args.argc < 2 || args.argc > 3) {
1027 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1031 if (strchr(args.jid, '/')) {
1032 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1036 if (!(client = ast_aji_get_client(args.sender))) {
1037 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1041 if (!ast_strlen_zero(args.nick)) {
1042 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1044 if (client->component) {
1045 sprintf(nick, "asterisk");
1047 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1051 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1052 ast_aji_join_chat(client, args.jid, nick);
1054 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1057 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1062 * \brief Application to leave a chat room
1063 * \param chan ast_channel
1064 * \param data Data is sender|jid|nickname.
1068 static int aji_leave_exec(struct ast_channel *chan, const char *data)
1070 struct aji_client *client = NULL;
1072 char nick[AJI_MAX_RESJIDLEN];
1073 AST_DECLARE_APP_ARGS(args,
1074 AST_APP_ARG(sender);
1080 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1083 s = ast_strdupa(data);
1085 AST_STANDARD_APP_ARGS(args, s);
1086 if (args.argc < 2 || args.argc > 3) {
1087 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1091 if (strchr(args.jid, '/')) {
1092 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1096 if (!(client = ast_aji_get_client(args.sender))) {
1097 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1101 if (!ast_strlen_zero(args.nick)) {
1102 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1104 if (client->component) {
1105 sprintf(nick, "asterisk");
1107 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1111 if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1112 ast_aji_leave_chat(client, args.jid, nick);
1114 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1120 * \brief Dial plan function to send a message.
1121 * \param chan ast_channel
1122 * \param data Data is account,jid,message.
1124 * \retval -1 failure
1126 static int aji_send_exec(struct ast_channel *chan, const char *data)
1128 struct aji_client *client = NULL;
1130 AST_DECLARE_APP_ARGS(args,
1131 AST_APP_ARG(sender);
1132 AST_APP_ARG(recipient);
1133 AST_APP_ARG(message);
1137 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1140 s = ast_strdupa(data);
1142 AST_STANDARD_APP_ARGS(args, s);
1143 if (args.argc < 3) {
1144 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1148 if (!(client = ast_aji_get_client(args.sender))) {
1149 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1152 if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
1153 ast_aji_send_chat(client, args.recipient, args.message);
1155 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1159 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from)
1161 struct aji_client *client;
1166 sender = ast_strdupa(from);
1167 strsep(&sender, ":");
1168 dest = ast_strdupa(to);
1171 if (ast_strlen_zero(sender)) {
1172 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for xmpp\n", from);
1176 if (!(client = ast_aji_get_client(sender))) {
1177 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
1181 ast_debug(1, "Sending message to '%s' from '%s'\n", dest, client->name);
1183 res = ast_aji_send_chat(client, dest, ast_msg_get_body(msg));
1184 if (res != IKS_OK) {
1185 ast_log(LOG_WARNING, "Failed to send xmpp message (%d).\n", res);
1188 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1189 return res == IKS_OK ? 0 : -1;
1193 * \brief Application to send a message to a groupchat.
1194 * \param chan ast_channel
1195 * \param data Data is sender|groupchat|message.
1199 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
1201 struct aji_client *client = NULL;
1203 char nick[AJI_MAX_RESJIDLEN];
1205 AST_DECLARE_APP_ARGS(args,
1206 AST_APP_ARG(sender);
1207 AST_APP_ARG(groupchat);
1208 AST_APP_ARG(message);
1213 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1216 s = ast_strdupa(data);
1218 AST_STANDARD_APP_ARGS(args, s);
1219 if (args.argc < 3 || args.argc > 4) {
1220 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1224 if (!(client = ast_aji_get_client(args.sender))) {
1225 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1229 if (ast_strlen_zero(args.nick) || args.argc == 3) {
1230 if (client->component) {
1231 sprintf(nick, "asterisk");
1233 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1236 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1239 if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
1240 res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
1243 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1244 if (res != IKS_OK) {
1252 * \brief Tests whether the connection is secured or not
1253 * \return 0 if the connection is not secured
1255 static int aji_is_secure(struct aji_client *client)
1258 return client->stream_flags & SECURE;
1267 * \brief Starts the TLS procedure
1268 * \param client the configured XMPP client we use to connect to a XMPP server
1269 * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
1270 * if OpenSSL is not installed
1272 static int aji_start_tls(struct aji_client *client)
1276 /* This is sent not encrypted */
1277 if ((ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"))) {
1281 client->stream_flags |= TRY_SECURE;
1287 * \brief TLS handshake, OpenSSL initialization
1288 * \param client the configured XMPP client we use to connect to a XMPP server
1289 * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
1291 static int aji_tls_handshake(struct aji_client *client)
1295 ast_debug(1, "Starting TLS handshake\n");
1297 /* Choose an SSL/TLS protocol version, create SSL_CTX */
1298 client->ssl_method = SSLv3_method();
1299 if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
1300 return IKS_NET_TLSFAIL;
1303 /* Create new SSL session */
1304 if (!(client->ssl_session = SSL_new(client->ssl_context))) {
1305 return IKS_NET_TLSFAIL;
1308 /* Enforce TLS on our XMPP connection */
1309 sock = iks_fd(client->p);
1310 if (!SSL_set_fd(client->ssl_session, sock)) {
1311 return IKS_NET_TLSFAIL;
1314 /* Perform SSL handshake */
1315 if (!SSL_connect(client->ssl_session)) {
1316 return IKS_NET_TLSFAIL;
1319 client->stream_flags &= (~TRY_SECURE);
1320 client->stream_flags |= SECURE;
1322 /* Sent over the established TLS connection */
1323 if (aji_send_header(client, client->jid->server) != IKS_OK) {
1324 return IKS_NET_TLSFAIL;
1327 ast_debug(1, "TLS started with server\n");
1331 #endif /* HAVE_OPENSSL */
1335 * \brief Secured or unsecured IO socket receiving function
1336 * \param client the configured XMPP client we use to connect to a XMPP server
1337 * \param buffer the reception buffer
1338 * \param buf_len the size of the buffer
1339 * \param timeout the select timer
1340 * \retval the number of read bytes
1341 * \retval 0 timeout expiration
1344 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
1346 struct pollfd pfd = { .events = POLLIN };
1350 if (aji_is_secure(client)) {
1351 pfd.fd = SSL_get_fd(client->ssl_session);
1356 #endif /* HAVE_OPENSSL */
1357 pfd.fd = iks_fd(client->p);
1359 res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
1362 if (aji_is_secure(client)) {
1363 len = SSL_read(client->ssl_session, buffer, buf_len);
1365 #endif /* HAVE_OPENSSL */
1366 len = recv(pfd.fd, buffer, buf_len, 0);
1370 } else if (len <= 0) {
1379 * \brief Tries to receive data from the Jabber server
1380 * \param client the configured XMPP client we use to connect to a XMPP server
1381 * \param timeout the timeout value
1382 * This function receives (encrypted or unencrypted) data from the XMPP server,
1383 * and passes it to the parser.
1384 * \retval IKS_OK success
1385 * \retval IKS_NET_RWERR IO error
1386 * \retval IKS_NET_NOCONN no connection available
1387 * \retval IKS_NET_EXPIRED timeout expiration
1389 static int aji_recv (struct aji_client *client, int timeout)
1392 char buf[NET_IO_BUF_SIZE - 1];
1393 char newbuf[NET_IO_BUF_SIZE - 1];
1398 memset(buf, 0, sizeof(buf));
1399 memset(newbuf, 0, sizeof(newbuf));
1402 len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
1403 if (len < 0) return IKS_NET_RWERR;
1404 if (len == 0) return IKS_NET_EXPIRED;
1407 /* our iksemel parser won't work as expected if we feed
1408 it with XML packets that contain multiple whitespace
1409 characters between tags */
1412 /* if we stumble on the ending tag character,
1413 we skip any whitespace that follows it*/
1415 while (isspace(buf[pos+1])) {
1419 newbuf[newbufpos] = c;
1426 /* Log the message here, because iksemel's logHook is
1428 aji_log_hook(client, buf, len, 1);
1430 /* let iksemel deal with the string length,
1431 and reset our buffer */
1432 ret = iks_parse(client->p, newbuf, 0, 0);
1433 memset(newbuf, 0, sizeof(newbuf));
1437 ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
1440 ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
1443 ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
1446 if (ret != IKS_OK) {
1449 ast_debug(3, "XML parsing successful\n");
1456 * \brief Sends XMPP header to the server
1457 * \param client the configured XMPP client we use to connect to a XMPP server
1458 * \param to the target XMPP server
1459 * \return IKS_OK on success, any other value on failure
1461 static int aji_send_header(struct aji_client *client, const char *to)
1466 len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
1467 msg = iks_malloc(len);
1470 sprintf(msg, "<?xml version='1.0'?>"
1471 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
1472 "%s' to='%s' version='1.0'>", client->name_space, to);
1473 err = aji_send_raw(client, msg);
1482 * \brief Wraps raw sending
1483 * \param client the configured XMPP client we use to connect to a XMPP server
1484 * \param x the XMPP packet to send
1485 * \return IKS_OK on success, any other value on failure
1487 int ast_aji_send(struct aji_client *client, iks *x)
1489 return aji_send_raw(client, iks_string(iks_stack(x), x));
1494 * \brief Sends an XML string over an XMPP connection
1495 * \param client the configured XMPP client we use to connect to a XMPP server
1496 * \param xmlstr the XML string to send
1497 * The XML data is sent whether the connection is secured or not. In the
1498 * latter case, we just call iks_send_raw().
1499 * \return IKS_OK on success, any other value on failure
1501 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
1505 int len = strlen(xmlstr);
1507 if (aji_is_secure(client)) {
1508 ret = SSL_write(client->ssl_session, xmlstr, len);
1510 /* Log the message here, because iksemel's logHook is
1512 aji_log_hook(client, xmlstr, len, 0);
1517 /* If needed, data will be sent unencrypted, and logHook will
1518 be called inside iks_send_raw */
1519 ret = iks_send_raw(client->p, xmlstr);
1520 if (ret != IKS_OK) {
1529 * \brief the debug loop.
1531 * \param xmpp xml data as string
1532 * \param size size of string
1533 * \param is_incoming direction of packet 1 for inbound 0 for outbound.
1535 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
1537 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1539 if (!ast_strlen_zero(xmpp)) {
1540 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
1543 if (client->debug) {
1545 ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
1547 if (strlen(xmpp) == 1) {
1548 if (option_debug > 2 && xmpp[0] == ' ') {
1549 ast_verbose("\nJABBER: Keep alive packet\n");
1552 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
1557 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1562 * \brief A wrapper function for iks_start_sasl
1563 * \param client the configured XMPP client we use to connect to a XMPP server
1564 * \param type the SASL authentication type. Supported types are PLAIN and MD5
1566 * \param pass password.
1568 * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
1570 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
1577 /* trigger SASL DIGEST-MD5 only over an unsecured connection.
1578 iks_start_sasl is an iksemel API function and relies on GnuTLS,
1579 whereas we use OpenSSL */
1580 if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
1581 return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass);
1582 if (!(type & IKS_STREAM_SASL_PLAIN)) {
1583 ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
1584 return IKS_NET_NOTSUPP;
1587 x = iks_new("auth");
1589 ast_log(LOG_ERROR, "Out of memory.\n");
1590 return IKS_NET_NOTSUPP;
1593 iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
1594 len = strlen(username) + strlen(pass) + 3;
1595 s = ast_alloca(len);
1596 base64 = ast_alloca((len + 2) * 4 / 3);
1597 iks_insert_attrib(x, "mechanism", "PLAIN");
1598 snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
1600 /* exclude the NULL training byte from the base64 encoding operation
1601 as some XMPP servers will refuse it.
1602 The format for authentication is [authzid]\0authcid\0password
1603 not [authzid]\0authcid\0password\0 */
1604 ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
1605 iks_insert_cdata(x, base64, 0);
1606 ast_aji_send(client, x);
1614 * \brief The action hook parses the inbound packets, constantly running.
1615 * \param data aji client structure
1616 * \param type type of packet
1617 * \param node the actual packet.
1618 * \return IKS_OK or IKS_HOOK .
1620 static int aji_act_hook(void *data, int type, iks *node)
1622 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1628 ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
1629 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1633 if (client->state == AJI_DISCONNECTING) {
1634 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1638 pak = iks_packet(node);
1640 /* work around iksemel's impossibility to recognize node names
1641 * containing a semicolon. Set the namespace of the corresponding
1642 * node accordingly. */
1643 if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
1644 char *node_ns = NULL;
1645 char attr[AJI_MAX_ATTRLEN];
1646 char *node_name = iks_name(iks_child(node));
1647 char *aux = strchr(node_name, ':') + 1;
1648 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
1649 node_ns = iks_find_attrib(iks_child(node), attr);
1652 pak->query = iks_child(node);
1657 if (!client->component) { /*client */
1659 case IKS_NODE_START:
1660 if (client->usetls && !aji_is_secure(client)) {
1661 #ifndef HAVE_OPENSSL
1662 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");
1663 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1666 if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
1667 ast_log(LOG_ERROR, "Could not start TLS\n");
1668 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1674 if (!client->usesasl) {
1675 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);
1676 auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
1678 iks_insert_attrib(auth, "id", client->mid);
1679 iks_insert_attrib(auth, "to", client->jid->server);
1680 ast_aji_increment_mid(client->mid);
1681 ast_aji_send(client, auth);
1684 ast_log(LOG_ERROR, "Out of memory.\n");
1689 case IKS_NODE_NORMAL:
1691 if (client->stream_flags & TRY_SECURE) {
1692 if (!strcmp("proceed", iks_name(node))) {
1693 return aji_tls_handshake(client);
1697 if (!strcmp("stream:features", iks_name(node))) {
1698 features = iks_stream_features(node);
1699 if (client->usesasl) {
1700 if (client->usetls && !aji_is_secure(client)) {
1703 if (client->authorized) {
1704 if (features & IKS_STREAM_BIND) {
1705 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);
1706 auth = iks_make_resource_bind(client->jid);
1708 iks_insert_attrib(auth, "id", client->mid);
1709 ast_aji_increment_mid(client->mid);
1710 ast_aji_send(client, auth);
1713 ast_log(LOG_ERROR, "Out of memory.\n");
1717 if (features & IKS_STREAM_SESSION) {
1718 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);
1719 auth = iks_make_session();
1721 iks_insert_attrib(auth, "id", "auth");
1722 ast_aji_increment_mid(client->mid);
1723 ast_aji_send(client, auth);
1726 ast_log(LOG_ERROR, "Out of memory.\n");
1731 if (!client->jid->user) {
1732 ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
1736 ret = aji_start_sasl(client, features, client->jid->user, client->password);
1737 if (ret != IKS_OK) {
1738 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1744 } else if (!strcmp("failure", iks_name(node))) {
1745 ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
1746 } else if (!strcmp("success", iks_name(node))) {
1747 client->authorized = 1;
1748 aji_send_header(client, client->jid->server);
1751 case IKS_NODE_ERROR:
1752 ast_log(LOG_ERROR, "JABBER: Node Error\n");
1753 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1757 ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1758 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1762 } else if (client->state != AJI_CONNECTED && client->component) {
1764 case IKS_NODE_START:
1765 if (client->state == AJI_DISCONNECTED) {
1766 char secret[160], shasum[320], *handshake;
1768 sprintf(secret, "%s%s", pak->id, client->password);
1769 ast_sha1_hash(shasum, secret);
1770 if (ast_asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
1771 aji_send_raw(client, handshake);
1772 ast_free(handshake);
1774 client->state = AJI_CONNECTING;
1775 if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
1776 client->state = AJI_CONNECTED;
1778 ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1783 case IKS_NODE_NORMAL:
1786 case IKS_NODE_ERROR:
1787 ast_log(LOG_ERROR, "JABBER: Node Error\n");
1788 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1792 ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1793 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1798 switch (pak->type) {
1800 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1802 case IKS_PAK_MESSAGE:
1803 aji_handle_message(client, pak);
1804 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1806 case IKS_PAK_PRESENCE:
1807 aji_handle_presence(client, pak);
1808 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1811 aji_handle_subscribe(client, pak);
1812 ast_debug(1, "JABBER: Handling paktype S10N\n");
1815 ast_debug(1, "JABBER: Handling paktype IQ\n");
1816 aji_handle_iq(client, node);
1819 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
1823 iks_filter_packet(client->f, pak);
1828 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1836 * \return IKS_FILTER_EAT.
1838 static int aji_register_approve_handler(void *data, ikspak *pak)
1840 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1841 iks *iq = NULL, *presence = NULL, *x = NULL;
1844 presence = iks_new("presence");
1846 if (client && iq && presence && x) {
1847 if (!iks_find(pak->query, "remove")) {
1848 iks_insert_attrib(iq, "from", client->jid->full);
1849 iks_insert_attrib(iq, "to", pak->from->full);
1850 iks_insert_attrib(iq, "id", pak->id);
1851 iks_insert_attrib(iq, "type", "result");
1852 ast_aji_send(client, iq);
1854 iks_insert_attrib(presence, "from", client->jid->full);
1855 iks_insert_attrib(presence, "to", pak->from->partial);
1856 iks_insert_attrib(presence, "id", client->mid);
1857 ast_aji_increment_mid(client->mid);
1858 iks_insert_attrib(presence, "type", "subscribe");
1859 iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1860 iks_insert_node(presence, x);
1861 ast_aji_send(client, presence);
1864 ast_log(LOG_ERROR, "Out of memory.\n");
1868 iks_delete(presence);
1871 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1872 return IKS_FILTER_EAT;
1876 * \brief register handler for incoming querys (IQ's)
1877 * \param data incoming aji_client request
1879 * \return IKS_FILTER_EAT.
1881 static int aji_register_query_handler(void *data, ikspak *pak)
1883 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1884 struct aji_buddy *buddy = NULL;
1885 iks *iq = NULL, *query = NULL;
1887 client = (struct aji_client *) data;
1889 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1891 iks *error = NULL, *notacceptable = NULL;
1893 ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1895 query = iks_new("query");
1896 error = iks_new("error");
1897 notacceptable = iks_new("not-acceptable");
1898 if (iq && query && error && notacceptable) {
1899 iks_insert_attrib(iq, "type", "error");
1900 iks_insert_attrib(iq, "from", client->user);
1901 iks_insert_attrib(iq, "to", pak->from->full);
1902 iks_insert_attrib(iq, "id", pak->id);
1903 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1904 iks_insert_attrib(error, "code" , "406");
1905 iks_insert_attrib(error, "type", "modify");
1906 iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1907 iks_insert_node(iq, query);
1908 iks_insert_node(iq, error);
1909 iks_insert_node(error, notacceptable);
1910 ast_aji_send(client, iq);
1912 ast_log(LOG_ERROR, "Out of memory.\n");
1916 iks_delete(notacceptable);
1917 } else if (!iks_find_attrib(pak->query, "node")) {
1918 iks *instructions = NULL;
1919 char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1921 query = iks_new("query");
1922 instructions = iks_new("instructions");
1923 if (iq && query && instructions && client) {
1924 iks_insert_attrib(iq, "from", client->user);
1925 iks_insert_attrib(iq, "to", pak->from->full);
1926 iks_insert_attrib(iq, "id", pak->id);
1927 iks_insert_attrib(iq, "type", "result");
1928 iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1929 iks_insert_cdata(instructions, explain, 0);
1930 iks_insert_node(iq, query);
1931 iks_insert_node(query, instructions);
1932 ast_aji_send(client, iq);
1934 ast_log(LOG_ERROR, "Out of memory.\n");
1937 iks_delete(instructions);
1941 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
1942 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1943 return IKS_FILTER_EAT;
1948 * \brief Handles stuff
1951 * \return IKS_FILTER_EAT.
1953 static int aji_ditems_handler(void *data, ikspak *pak)
1955 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1958 if (!(node = iks_find_attrib(pak->query, "node"))) {
1959 iks *iq = NULL, *query = NULL, *item = NULL;
1961 query = iks_new("query");
1962 item = iks_new("item");
1964 if (iq && query && item) {
1965 iks_insert_attrib(iq, "from", client->user);
1966 iks_insert_attrib(iq, "to", pak->from->full);
1967 iks_insert_attrib(iq, "id", pak->id);
1968 iks_insert_attrib(iq, "type", "result");
1969 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1970 iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1971 iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1972 iks_insert_attrib(item, "jid", client->user);
1974 iks_insert_node(iq, query);
1975 iks_insert_node(query, item);
1976 ast_aji_send(client, iq);
1978 ast_log(LOG_ERROR, "Out of memory.\n");
1985 } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1986 iks *iq, *query, *confirm;
1988 query = iks_new("query");
1989 confirm = iks_new("item");
1990 if (iq && query && confirm && client) {
1991 iks_insert_attrib(iq, "from", client->user);
1992 iks_insert_attrib(iq, "to", pak->from->full);
1993 iks_insert_attrib(iq, "id", pak->id);
1994 iks_insert_attrib(iq, "type", "result");
1995 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1996 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1997 iks_insert_attrib(confirm, "node", "confirmaccount");
1998 iks_insert_attrib(confirm, "name", "Confirm AIM account");
1999 iks_insert_attrib(confirm, "jid", "blog.astjab.org");
2001 iks_insert_node(iq, query);
2002 iks_insert_node(query, confirm);
2003 ast_aji_send(client, iq);
2005 ast_log(LOG_ERROR, "Out of memory.\n");
2010 iks_delete(confirm);
2012 } else if (!strcasecmp(node, "confirmaccount")) {
2013 iks *iq = NULL, *query = NULL, *feature = NULL;
2016 query = iks_new("query");
2017 feature = iks_new("feature");
2019 if (iq && query && feature && client) {
2020 iks_insert_attrib(iq, "from", client->user);
2021 iks_insert_attrib(iq, "to", pak->from->full);
2022 iks_insert_attrib(iq, "id", pak->id);
2023 iks_insert_attrib(iq, "type", "result");
2024 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2025 iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2026 iks_insert_node(iq, query);
2027 iks_insert_node(query, feature);
2028 ast_aji_send(client, iq);
2030 ast_log(LOG_ERROR, "Out of memory.\n");
2035 iks_delete(feature);
2038 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2039 return IKS_FILTER_EAT;
2045 * \brief Handle add extra info
2048 * \return IKS_FILTER_EAT
2050 static int aji_client_info_handler(void *data, ikspak *pak)
2052 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2053 struct aji_resource *resource = NULL;
2054 struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2057 ast_log(LOG_NOTICE, "JABBER: Received client info from unknown buddy: %s.\n", pak->from->full);
2058 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2059 return IKS_FILTER_EAT;
2062 resource = aji_find_resource(buddy, pak->from->resource);
2063 if (pak->subtype == IKS_TYPE_RESULT) {
2065 ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2066 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2067 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2068 return IKS_FILTER_EAT;
2070 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2071 resource->cap->jingle = 1;
2073 resource->cap->jingle = 0;
2075 } else if (pak->subtype == IKS_TYPE_GET) {
2076 iks *iq, *disco, *ident, *google, *query;
2078 query = iks_new("query");
2079 ident = iks_new("identity");
2080 disco = iks_new("feature");
2081 google = iks_new("feature");
2082 if (iq && ident && disco && google) {
2083 iks_insert_attrib(iq, "from", client->jid->full);
2084 iks_insert_attrib(iq, "to", pak->from->full);
2085 iks_insert_attrib(iq, "type", "result");
2086 iks_insert_attrib(iq, "id", pak->id);
2087 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2088 iks_insert_attrib(ident, "category", "client");
2089 iks_insert_attrib(ident, "type", "pc");
2090 iks_insert_attrib(ident, "name", "asterisk");
2091 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2092 iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2093 iks_insert_node(iq, query);
2094 iks_insert_node(query, ident);
2095 iks_insert_node(query, google);
2096 iks_insert_node(query, disco);
2097 ast_aji_send(client, iq);
2099 ast_log(LOG_ERROR, "Out of Memory.\n");
2107 } else if (pak->subtype == IKS_TYPE_ERROR) {
2108 ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
2110 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2111 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2112 return IKS_FILTER_EAT;
2117 * \brief Handler of the return info packet
2118 * \param data aji_client
2120 * \return IKS_FILTER_EAT
2122 static int aji_dinfo_handler(void *data, ikspak *pak)
2124 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2126 struct aji_resource *resource = NULL;
2127 struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2130 ast_log(LOG_NOTICE, "JABBER: Received client info from unknown buddy: %s.\n", pak->from->full);
2131 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2132 return IKS_FILTER_EAT;
2135 if (pak->subtype == IKS_TYPE_ERROR) {
2136 ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
2137 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2138 return IKS_FILTER_EAT;
2140 resource = aji_find_resource(buddy, pak->from->resource);
2141 if (pak->subtype == IKS_TYPE_RESULT) {
2143 ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2144 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2145 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2146 return IKS_FILTER_EAT;
2148 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2149 resource->cap->jingle = 1;
2151 resource->cap->jingle = 0;
2153 } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
2154 iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
2157 query = iks_new("query");
2158 identity = iks_new("identity");
2159 disco = iks_new("feature");
2160 reg = iks_new("feature");
2161 commands = iks_new("feature");
2162 gateway = iks_new("feature");
2163 version = iks_new("feature");
2164 vcard = iks_new("feature");
2165 search = iks_new("feature");
2166 if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
2167 iks_insert_attrib(iq, "from", client->user);
2168 iks_insert_attrib(iq, "to", pak->from->full);
2169 iks_insert_attrib(iq, "id", pak->id);
2170 iks_insert_attrib(iq, "type", "result");
2171 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2172 iks_insert_attrib(identity, "category", "gateway");
2173 iks_insert_attrib(identity, "type", "pstn");
2174 iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2175 iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2176 iks_insert_attrib(reg, "var", "jabber:iq:register");
2177 iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2178 iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2179 iks_insert_attrib(version, "var", "jabber:iq:version");
2180 iks_insert_attrib(vcard, "var", "vcard-temp");
2181 iks_insert_attrib(search, "var", "jabber:iq:search");
2183 iks_insert_node(iq, query);
2184 iks_insert_node(query, identity);
2185 iks_insert_node(query, disco);
2186 iks_insert_node(query, reg);
2187 iks_insert_node(query, commands);
2188 iks_insert_node(query, gateway);
2189 iks_insert_node(query, version);
2190 iks_insert_node(query, vcard);
2191 iks_insert_node(query, search);
2192 ast_aji_send(client, iq);
2194 ast_log(LOG_ERROR, "Out of memory.\n");
2199 iks_delete(identity);
2202 iks_delete(commands);
2203 iks_delete(gateway);
2204 iks_delete(version);
2207 } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
2208 iks *iq, *query, *confirm;
2210 query = iks_new("query");
2211 confirm = iks_new("item");
2213 if (iq && query && confirm && client) {
2214 iks_insert_attrib(iq, "from", client->user);
2215 iks_insert_attrib(iq, "to", pak->from->full);
2216 iks_insert_attrib(iq, "id", pak->id);
2217 iks_insert_attrib(iq, "type", "result");
2218 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2219 iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2220 iks_insert_attrib(confirm, "node", "confirmaccount");
2221 iks_insert_attrib(confirm, "name", "Confirm AIM account");
2222 iks_insert_attrib(confirm, "jid", client->user);
2223 iks_insert_node(iq, query);
2224 iks_insert_node(query, confirm);
2225 ast_aji_send(client, iq);
2227 ast_log(LOG_ERROR, "Out of memory.\n");
2232 iks_delete(confirm);
2234 } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
2235 iks *iq, *query, *feature;
2238 query = iks_new("query");
2239 feature = iks_new("feature");
2241 if (iq && query && feature && client) {
2242 iks_insert_attrib(iq, "from", client->user);
2243 iks_insert_attrib(iq, "to", pak->from->full);
2244 iks_insert_attrib(iq, "id", pak->id);
2245 iks_insert_attrib(iq, "type", "result");
2246 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2247 iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2248 iks_insert_node(iq, query);
2249 iks_insert_node(query, feature);
2250 ast_aji_send(client, iq);
2252 ast_log(LOG_ERROR, "Out of memory.\n");
2257 iks_delete(feature);
2260 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2261 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2262 return IKS_FILTER_EAT;
2267 * \brief Handles \verbatim <iq> \endverbatim stanzas.
2268 * \param client the configured XMPP client we use to connect to a XMPP server
2272 static void aji_handle_iq(struct aji_client *client, iks *node)
2274 /*Nothing to see here */
2279 * \brief Handles \verbatim <message>\endverbatim stanzas.
2280 * Adds the incoming message to the client's message list.
2281 * \param client the configured XMPP client we use to connect to a XMPP server
2282 * \param pak ikspak the node
2284 static void aji_handle_message(struct aji_client *client, ikspak *pak)
2286 struct aji_message *insert;
2288 struct ast_msg *msg;
2290 ast_debug(3, "client %s received a message\n", client->name);
2292 if (!(insert = ast_calloc(1, sizeof(*insert)))) {
2296 insert->arrived = ast_tvnow();
2298 /* wake up threads waiting for messages */
2299 ast_mutex_lock(&messagelock);
2300 ast_cond_broadcast(&message_received_condition);
2301 ast_mutex_unlock(&messagelock);
2303 if (iks_find_cdata(pak->x, "body")) {
2304 insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2307 ast_copy_string(insert->id, pak->id, sizeof(insert->id));
2310 /* insert will furtherly be added to message list */
2311 insert->from = ast_strdup(pak->from->full);
2312 if (!insert->from) {
2314 ast_log(LOG_ERROR, "Memory allocation failure\n");
2317 ast_debug(3, "message comes from %s\n", insert->from);
2320 if (client->send_to_dialplan) {
2321 if ((msg = ast_msg_alloc())) {
2324 res = ast_msg_set_to(msg, "xmpp:%s", client->user);
2325 res |= ast_msg_set_from(msg, "xmpp:%s", insert->from);
2326 res |= ast_msg_set_body(msg, "%s", insert->message);
2327 res |= ast_msg_set_context(msg, "%s", client->context);
2330 ast_msg_destroy(msg);
2339 /* remove old messages received from this JID
2340 * and insert received message */
2341 deleted = delete_old_messages(client, pak->from->partial);
2342 ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2343 AST_LIST_LOCK(&client->messages);
2344 AST_LIST_INSERT_HEAD(&client->messages, insert, list);
2345 AST_LIST_UNLOCK(&client->messages);
2350 * \brief handles \verbatim <presence>\endverbatim stanzas.
2351 * \param client the configured XMPP client we use to connect to a XMPP server
2354 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
2356 int status, priority;
2357 struct aji_buddy *buddy;
2358 struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
2359 char *ver, *node, *descrip, *type;
2361 if (client->state != AJI_CONNECTED)
2362 aji_create_buddy(pak->from->partial, client);
2364 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2365 if (!buddy && pak->from->partial) {
2366 /* allow our jid to be used to log in with another resource */
2367 if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
2368 aji_create_buddy(pak->from->partial, client);
2370 ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
2373 type = iks_find_attrib(pak->x, "type");
2374 if (client->component && type &&!strcasecmp("probe", type)) {
2375 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2376 ast_verbose("what i was looking for \n");
2378 ASTOBJ_WRLOCK(buddy);
2379 status = (pak->show) ? pak->show : 6;
2380 priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
2381 tmp = buddy->resources;
2382 descrip = ast_strdup(iks_find_cdata(pak->x, "status"));
2384 while (tmp && pak->from->resource) {
2385 if (!strcasecmp(tmp->resource, pak->from->resource)) {
2386 tmp->status = status;
2387 if (tmp->description) {
2388 ast_free(tmp->description);
2390 tmp->description = descrip;
2392 if (status == 6) { /* Sign off Destroy resource */
2393 if (last && found->next) {
2394 last->next = found->next;
2397 buddy->resources = found->next;
2399 buddy->resources = NULL;
2401 } else if (!found->next) {
2405 buddy->resources = NULL;
2412 /* resource list is sorted by descending priority */
2413 if (tmp->priority != priority) {
2414 found->priority = priority;
2415 if (!last && !found->next) {
2416 /* resource was found to be unique,
2420 /* search for resource in our list
2421 and take it out for the moment */
2423 last->next = found->next;
2425 buddy->resources = found->next;
2429 tmp = buddy->resources;
2430 if (!buddy->resources) {
2431 buddy->resources = found;
2433 /* priority processing */
2435 /* insert resource back according to
2436 its priority value */
2437 if (found->priority > tmp->priority) {
2439 /* insert within list */
2445 buddy->resources = found;
2450 /* insert at the end of the list */
2465 /* resource not found in our list, create it */
2466 if (!found && status != 6 && pak->from->resource) {
2467 found = ast_calloc(1, sizeof(*found));
2470 ast_log(LOG_ERROR, "Out of memory!\n");
2471 ASTOBJ_UNLOCK(buddy);
2472 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2475 ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
2476 found->status = status;
2477 found->description = descrip;
2478 found->priority = priority;
2481 tmp = buddy->resources;
2483 if (found->priority > tmp->priority) {
2489 buddy->resources = found;
2501 buddy->resources = found;
2505 ASTOBJ_UNLOCK(buddy);
2506 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2508 node = iks_find_attrib(iks_find(pak->x, "c"), "node");
2509 ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
2511 /* handle gmail client's special caps:c tag */
2512 if (!node && !ver) {
2513 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
2514 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
2517 /* retrieve capabilites of the new resource */
2518 if (status != 6 && found && !found->cap) {
2519 found->cap = aji_find_version(node, ver, pak);
2520 if (gtalk_yuck(pak->x)) { /* gtalk should do discover */
2521 found->cap->jingle = 1;
2523 if (found->cap->jingle) {
2524 ast_debug(1, "Special case for google till they support discover.\n");
2528 query = iks_new("query");
2530 iks_insert_attrib(iq, "type", "get");
2531 iks_insert_attrib(iq, "to", pak->from->full);
2532 iks_insert_attrib(iq, "from", client->jid->full);
2533 iks_insert_attrib(iq, "id", client->mid);
2534 ast_aji_increment_mid(client->mid);
2535 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2536 iks_insert_node(iq, query);
2537 ast_aji_send(client, iq);
2539 ast_log(LOG_ERROR, "Out of memory.\n");
2545 switch (pak->subtype) {
2546 case IKS_TYPE_AVAILABLE:
2547 ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
2549 case IKS_TYPE_UNAVAILABLE:
2550 ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
2553 ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
2555 switch (pak->show) {
2556 case IKS_SHOW_UNAVAILABLE:
2557 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2559 case IKS_SHOW_AVAILABLE:
2560 ast_debug(3, "JABBER: type is available\n");
2563 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2566 ast_debug(3, "JABBER: type is away\n");
2569 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2572 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2575 ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
2579 manager_event(EVENT_FLAG_USER, "JabberStatus",
2580 "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
2581 "\r\nDescription: %s\r\n",
2582 client->name, pak->from->partial, found->resource, found->status,
2583 found->priority, S_OR(found->description, ""));
2585 manager_event(EVENT_FLAG_USER, "JabberStatus",
2586 "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
2587 client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
2593 * \brief handles subscription requests.
2594 * \param client the configured XMPP client we use to connect to a XMPP server
2595 * \param pak ikspak iksemel packet.
2598 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
2600 iks *presence = NULL, *status = NULL;
2601 struct aji_buddy* buddy = NULL;
2603 switch (pak->subtype) {
2604 case IKS_TYPE_SUBSCRIBE:
2605 if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
2606 presence = iks_new("presence");
2607 status = iks_new("status");
2608 if (presence && status) {
2609 iks_insert_attrib(presence, "type", "subscribed");
2610 iks_insert_attrib(presence, "to", pak->from->full);
2611 iks_insert_attrib(presence, "from", client->jid->full);
2613 iks_insert_attrib(presence, "id", pak->id);
2614 iks_insert_cdata(status, "Asterisk has approved subscription", 0);
2615 iks_insert_node(presence, status);
2616 ast_aji_send(client, presence);
2618 ast_log(LOG_ERROR, "Unable to allocate nodes\n");
2621 iks_delete(presence);
2625 if (client->component)
2626 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2627 case IKS_TYPE_SUBSCRIBED:
2628 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2629 if (!buddy && pak->from->partial) {
2630 aji_create_buddy(pak->from->partial, client);
2632 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2635 ast_verb(5, "JABBER: This is a subcription of type %i\n", pak->subtype);
2640 * \brief sends messages.
2641 * \param client the configured XMPP client we use to connect to a XMPP server
2644 * \retval IKS_OK success
2645 * \retval -1 failure
2647 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
2649 return aji_send_raw_chat(client, 0, NULL, address, message);
2653 * \brief sends message to a groupchat
2654 * Prior to sending messages to a groupchat, one must be connected to it.
2655 * \param client the configured XMPP client we use to connect to a XMPP server
2656 * \param nick the nickname we use in the chatroom
2657 * \param address the user the messages must be sent to
2658 * \param message the message to send
2659 * \return IKS_OK on success, any other value on failure
2661 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
2662 return aji_send_raw_chat(client, 1, nick, address, message);
2666 * \brief sends messages.
2667 * \param client the configured XMPP client we use to connect to a XMPP server
2669 * \param nick the nickname we use in chatrooms
2672 * \return IKS_OK on success, any other value on failure
2674 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
2677 iks *message_packet = NULL;
2678 char from[AJI_MAX_JIDLEN];
2679 /* the nickname is used only in component mode */
2680 if (nick && client->component) {
2681 snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
2683 snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
2686 if (client->state != AJI_CONNECTED) {
2687 ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
2691 message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
2692 if (!message_packet) {
2693 ast_log(LOG_ERROR, "Out of memory.\n");
2696 iks_insert_attrib(message_packet, "from", from);
2697 res = ast_aji_send(client, message_packet);
2698 iks_delete(message_packet);
2704 * \brief create a chatroom.
2705 * \param client the configured XMPP client we use to connect to a XMPP server
2706 * \param room name of room
2707 * \param server name of server
2708 * \param topic topic for the room.
2711 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
2718 iks_insert_attrib(iq, "type", "get");
2719 iks_insert_attrib(iq, "to", server);
2720 iks_insert_attrib(iq, "id", client->mid);
2721 ast_aji_increment_mid(client->mid);
2722 ast_aji_send(client, iq);
2724 ast_log(LOG_ERROR, "Out of memory.\n");
2733 * \brief join a chatroom.
2734 * \param client the configured XMPP client we use to connect to a XMPP server
2735 * \param room room to join
2736 * \param nick the nickname to use in this room
2737 * \return IKS_OK on success, any other value on failure.
2739 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
2741 return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
2745 * \brief leave a chatroom.
2746 * \param client the configured XMPP client we use to connect to a XMPP server
2747 * \param room room to leave
2748 * \param nick the nickname used in this room
2749 * \return IKS_OK on success, any other value on failure.
2751 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
2753 return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
2756 * \brief invite to a chatroom.
2757 * \param client the configured XMPP client we use to connect to a XMPP server
2763 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
2766 iks *invite, *body, *namespace;
2768 invite = iks_new("message");
2769 body = iks_new("body");
2770 namespace = iks_new("x");
2771 if (client && invite && body && namespace) {
2772 iks_insert_attrib(invite, "to", user);
2773 iks_insert_attrib(invite, "id", client->mid);
2774 ast_aji_increment_mid(client->mid);
2775 iks_insert_cdata(body, message, 0);
2776 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
2777 iks_insert_attrib(namespace, "jid", room);
2778 iks_insert_node(invite, body);
2779 iks_insert_node(invite, namespace);
2780 res = ast_aji_send(client, invite);
2782 ast_log(LOG_ERROR, "Out of memory.\n");
2786 iks_delete(namespace);
2794 * \brief receive message loop.
2798 static void *aji_recv_loop(void *data)
2800 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2803 while (res != IKS_OK) {
2804 ast_debug(3, "JABBER: Connecting.\n");
2805 res = aji_reconnect(client);
2810 if (res == IKS_NET_RWERR || client->timeout == 0) {
2811 while (res != IKS_OK) {
2812 ast_debug(3, "JABBER: reconnecting.\n");
2813 res = aji_reconnect(client);
2818 res = aji_recv(client, 1);
2820 if (client->state == AJI_DISCONNECTING) {
2821 ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
2825 /* Decrease timeout if no data received, and delete
2826 * old messages globally */
2827 if (res == IKS_NET_EXPIRED) {
2829 delete_old_messages_all(client);
2831 if (res == IKS_HOOK) {
2832 ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
2833 } else if (res == IKS_NET_TLSFAIL) {
2834 ast_log(LOG_ERROR, "JABBER: Failure in TLS.\n");
2835 } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
2836 res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
2837 if (res == IKS_OK) {
2838 client->timeout = 50;
2840 ast_log(LOG_WARNING, "JABBER: Network Timeout\n");
2842 } else if (res == IKS_NET_RWERR) {
2843 ast_log(LOG_WARNING, "JABBER: socket read error\n");
2846 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2851 * \brief increments the mid field for messages and other events.
2855 void ast_aji_increment_mid(char *mid)
2859 for (i = strlen(mid) - 1; i >= 0; i--) {
2860 if (mid[i] != 'z') {
2861 mid[i] = mid[i] + 1;
2870 * \brief attempts to register to a transport.
2871 * \param aji_client struct, and xml packet.
2872 * \return IKS_FILTER_EAT.
2874 /*allows for registering to transport , was too sketch and is out for now. */
2875 static int aji_register_transport(void *data, ikspak *pak)
2877 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2879 struct aji_buddy *buddy = NULL;
2880 iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
2882 if (client && send) {
2883 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2884 ASTOBJ_RDLOCK(iterator);
2885 if (iterator->btype == AJI_TRANS) {
2888 ASTOBJ_UNLOCK(iterator);
2890 iks_filter_remove_hook(client->f, aji_register_transport);
2891 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);
2892 iks_insert_attrib(send, "to", buddy->host);
2893 iks_insert_attrib(send, "id", client->mid);
2894 ast_aji_increment_mid(client->mid);
2895 iks_insert_attrib(send, "from", client->user);
2896 res = ast_aji_send(client, send);
2898 ast_log(LOG_ERROR, "Out of memory.\n");
2902 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2903 return IKS_FILTER_EAT;
2907 * \brief attempts to register to a transport step 2.
2908 * \param aji_client struct, and xml packet.
2909 * \return IKS_FILTER_EAT.
2911 /* more of the same blob of code, too wonky for now*/
2912 static int aji_register_transport2(void *data, ikspak *pak)
2914 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2916 struct aji_buddy *buddy = NULL;
2918 iks *regiq = iks_new("iq");
2919 iks *regquery = iks_new("query");
2920 iks *reguser = iks_new("username");
2921 iks *regpass = iks_new("password");
2923 if (client && regquery && reguser && regpass && regiq) {
2924 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2925 ASTOBJ_RDLOCK(iterator);
2926 if (iterator->btype == AJI_TRANS)
2927 buddy = iterator; ASTOBJ_UNLOCK(iterator);
2929 iks_filter_remove_hook(client->f, aji_register_transport2);
2930 iks_insert_attrib(regiq, "to", buddy->host);
2931 iks_insert_attrib(regiq, "type", "set");
2932 iks_insert_attrib(regiq, "id", client->mid);
2933 ast_aji_increment_mid(client->mid);
2934 iks_insert_attrib(regiq, "from", client->user);
2935 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2936 iks_insert_cdata(reguser, buddy->user, 0);
2937 iks_insert_cdata(regpass, buddy->pass, 0);
2938 iks_insert_node(regiq, regquery);
2939 iks_insert_node(regquery, reguser);
2940 iks_insert_node(regquery, regpass);
2941 res = ast_aji_send(client, regiq);
2943 ast_log(LOG_ERROR, "Out of memory.\n");
2947 iks_delete(regquery);
2949 iks_delete(reguser);
2951 iks_delete(regpass);
2952 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2953 return IKS_FILTER_EAT;
2959 * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2960 * \param client the configured XMPP client we use to connect to a XMPP server
2962 * \note The messages here should be configurable.
2964 static void aji_pruneregister(struct aji_client *client)
2966 iks *removeiq = iks_new("iq");
2967 iks *removequery = iks_new("query");
2968 iks *removeitem = iks_new("item");
2969 iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2970 if (!client || !removeiq || !removequery || !removeitem || !send) {
2971 ast_log(LOG_ERROR, "Out of memory.\n");
2975 iks_insert_node(removeiq, removequery);
2976 iks_insert_node(removequery, removeitem);
2977 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2978 ASTOBJ_RDLOCK(iterator);
2979 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2980 * be called at the same time */
2981 if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
2982 ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2983 "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2984 " so I am no longer subscribing to your presence.\n"));
2985 ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2986 "GoodBye. You are no longer in the Asterisk config file so I am removing"
2987 " your access to my presence.\n"));
2988 iks_insert_attrib(removeiq, "from", client->jid->full);
2989 iks_insert_attrib(removeiq, "type", "set");
2990 iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2991 iks_insert_attrib(removeitem, "jid", iterator->name);
2992 iks_insert_attrib(removeitem, "subscription", "remove");
2993 ast_aji_send(client, removeiq);
2994 } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2995 ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
2996 "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2997 ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2999 ASTOBJ_UNLOCK(iterator);
3003 iks_delete(removeiq);
3004 iks_delete(removequery);
3005 iks_delete(removeitem);
3008 ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, ast_aji_buddy_destroy);
3013 * \brief filters the roster packet we get back from server.
3015 * \param pak ikspak iksemel packet.
3016 * \return IKS_FILTER_EAT.
3018 static int aji_filter_roster(void *data, ikspak *pak)
3020 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3023 struct aji_buddy *buddy;
3025 client->state = AJI_CONNECTED;
3026 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3027 ASTOBJ_RDLOCK(iterator);
3028 x = iks_child(pak->query);
3031 if (!iks_strcmp(iks_name(x), "item")) {
3032 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
3034 ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
3040 ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
3044 ASTOBJ_UNLOCK(iterator);
3047 x = iks_child(pak->query);
3050 if (iks_strcmp(iks_name(x), "item") == 0) {
3051 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3052 ASTOBJ_RDLOCK(iterator);
3053 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
3055 ASTOBJ_UNLOCK(iterator);
3059 /* found buddy, don't create a new one */
3064 buddy = ast_calloc(1, sizeof(*buddy));
3066 ast_log(LOG_WARNING, "Out of memory\n");
3067 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3071 ASTOBJ_WRLOCK(buddy);
3072 ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
3073 ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
3074 if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
3075 ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
3077 } else if (ast_test_flag(&client->flags, AJI_AUTOREGISTER)) {
3078 if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
3079 /* subscribe to buddy's presence only
3080 if we really need to */
3081 ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
3084 ASTOBJ_UNLOCK(buddy);
3086 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
3087 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
3094 aji_pruneregister(client);
3096 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3097 return IKS_FILTER_EAT;
3102 * \brief reconnect to jabber server
3103 * \param client the configured XMPP client we use to connect to a XMPP server
3106 static int aji_reconnect(struct aji_client *client)
3110 if (client->state) {
3111 client->state = AJI_DISCONNECTED;
3113 client->timeout = 50;
3115 iks_parser_reset(client->p);
3117 if (client->authorized) {
3118 client->authorized = 0;
3121 res = aji_initialize(client);
3128 * \brief Get the roster of jabber users
3129 * \param client the configured XMPP client we use to connect to a XMPP server
3132 static int aji_get_roster(struct aji_client *client)
3135 roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
3138 iks_insert_attrib(roster, "id", "roster");
3139 aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
3140 ast_aji_send(client, roster);
3150 * \brief connects as a client to jabber server.
3152 * \param pak ikspak iksemel packet
3155 static int aji_client_connect(void *data, ikspak *pak)
3157 struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3158 int res = IKS_FILTER_PASS;
3161 if (client->state == AJI_DISCONNECTED) {
3162 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);
3163 client->state = AJI_CONNECTING;
3164 client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
3165 if (!client->component) { /*client*/
3166 aji_get_roster(client);
3168 if (client->distribute_events) {
3169 aji_init_event_distribution(client);
3172 iks_filter_remove_hook(client->f, aji_client_connect);
3173 /* Once we remove the hook for this routine, we must return EAT or we will crash or corrupt memory */
3174 res = IKS_FILTER_EAT;
3177 ast_log(LOG_ERROR, "Out of memory.\n");
3180 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3186 * \brief prepares client for connect.
3187 * \param client the configured XMPP client we use to connect to a XMPP server
3190 static int aji_initialize(struct aji_client *client)
3192 int connected = IKS_NET_NOCONN;
3195 /* reset stream flags */
3196 client->stream_flags = 0;
3198 /* If it's a component, connect to user, otherwise, connect to server */
3199 connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
3201 if (connected == IKS_NET_NOCONN) {
3202 ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
3204 } else if (connected == IKS_NET_NODNS) {
3205 ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to %s\n", client->name,
3206 S_OR(client->serverhost, client->jid->server));
3214 * \brief disconnect from jabber server.
3215 * \param client the configured XMPP client we use to connect to a XMPP server
3218 int ast_aji_disconnect(struct aji_client *client)
3221 ast_verb(4, "JABBER: Disconnecting\n");
3223 if (client->stream_flags & SECURE) {
3224 SSL_shutdown(client->ssl_session);
3225 SSL_CTX_free(client->ssl_context);
3226 SSL_free(client->ssl_session);
3229 iks_disconnect(client->p);
3230 iks_parser_delete(client->p);
3231 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3238 * \brief Callback function for MWI events
3240 * \param data void pointer to ast_client structure
3243 static void aji_mwi_cb(const struct ast_event *ast_event, void *data)
3245 const char *mailbox;
3246 const char *context;
3249 struct aji_client *client;
3250 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
3252 /* If the event didn't originate from this server, don't send it back out. */
3253 ast_debug(1, "Returning here\n");
3257 client = ASTOBJ_REF((struct aji_client *) data);
3258 mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
3259 context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
3260 snprintf(oldmsgs, sizeof(oldmsgs), "%d",
3261 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
3262 snprintf(newmsgs, sizeof(newmsgs), "%d",
3263 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
3264 aji_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
3265 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3269 * \brief Callback function for device state events
3271 * \param data void pointer to ast_client structure
3274 static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
3277 const char *device_state;
3278 unsigned int cachable;
3279 struct aji_client *client;
3280 if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
3282 /* If the event didn't originate from this server, don't send it back out. */
3283 ast_debug(1, "Returning here\n");
3287 client = ASTOBJ_REF((struct aji_client *) data);
3288 device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
3289 device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
3290 cachable = ast_event_get_ie_uint(ast_event, AST_EVENT_IE_CACHABLE);
3291 aji_publish_device_state(client, device, device_state, cachable);
3292 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3296 * \brief Initialize collections for event distribution
3297 * \param client the configured XMPP client we use to connect to a XMPP server
3300 static void aji_init_event_distribution(struct aji_client *client)
3303 mwi_sub = ast_event_subscribe(AST_EVENT_MWI, aji_mwi_cb, "aji_mwi_subscription",
3304 client, AST_EVENT_IE_END);
3306 if (!device_state_sub) {
3307 if (ast_enable_distributed_devstate()) {
3310 device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
3311 aji_devstate_cb, "aji_devstate_subscription", client, AST_EVENT_IE_END);
3312 ast_event_dump_cache(device_state_sub);
3315 aji_pubsub_subscribe(client, "device_state");
3316 aji_pubsub_subscribe(client, "message_waiting");
3317 iks_filter_add_rule(client->f, aji_handle_pubsub_event, client, IKS_RULE_TYPE,
3318 IKS_PAK_MESSAGE, IKS_RULE_FROM, client->pubsub_node, IKS_RULE_DONE);
3319 iks_filter_add_rule(client->f, aji_handle_pubsub_error, client, IKS_RULE_TYPE,
3320 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
3325 * \brief Callback for handling PubSub events
3326 * \param data void pointer to aji_client structure
3328 * \return IKS_FILTER_EAT
3330 static int aji_handle_pubsub_event(void *data, ikspak *pak)
3332 char *item_id, *device_state, *context, *cachable_str;
3333 int oldmsgs, newmsgs;
3334 iks *item, *item_content;
3335 struct ast_eid pubsub_eid;
3336 struct ast_event *event;
3337 unsigned int cachable = AST_DEVSTATE_CACHABLE;
3339 item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
3341 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
3342 return IKS_FILTER_EAT;
3344 item_id = iks_find_attrib(item, "id");
3345 item_content = iks_child(item);
3346 ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
3347 if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
3348 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
3349 return IKS_FILTER_EAT;
3351 if (!strcasecmp(iks_name(item_content), "state")) {
3352 device_state = iks_find_cdata(item, "state");
3353 if ((cachable_str = iks_find_cdata(item, "cachable"))) {
3354 sscanf(cachable_str, "%30d", &cachable);
3356 if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
3357 AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
3358 AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
3359 AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
3360 AST_EVENT_IE_END))) {
3361 return IKS_FILTER_EAT;
3363 } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
3364 context = strsep(&item_id, "@");
3365 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
3366 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
3367 if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
3368 AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
3369 AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
3370 AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
3371 AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
3372 &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
3373 return IKS_FILTER_EAT;
3376 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
3377 iks_name(item_content));
3378 return IKS_FILTER_EAT;
3381 if (cachable == AST_DEVSTATE_CACHABLE) {
3382 ast_event_queue_and_cache(event);
3384 ast_event_queue(event);
3387 return IKS_FILTER_EAT;
3391 * \brief Add Owner affiliations for pubsub node
3392 * \param client the configured XMPP client we use to connect to a XMPP server
3393 * \param node the name of the node to which to add affiliations
3396 static void aji_create_affiliations(struct aji_client *client, const char *node)
3398 iks *modify_affiliates = aji_pubsub_iq_create(client, "set");
3399 iks *pubsub, *affiliations, *affiliate;
3400 pubsub = iks_insert(modify_affiliates, "pubsub");
3401 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
3402 affiliations = iks_insert(pubsub, "affiliations");
3403 iks_insert_attrib(affiliations, "node", node);
3404 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3405 ASTOBJ_RDLOCK(iterator);
3406 affiliate = iks_insert(affiliations, "affiliation");
3407 iks_insert_attrib(affiliate, "jid", iterator->name);
3408 iks_insert_attrib(affiliate, "affiliation", "owner");
3409 ASTOBJ_UNLOCK(iterator);
3411 ast_aji_send(client, modify_affiliates);
3412 iks_delete(modify_affiliates);
3416 * \brief Subscribe to a PubSub node
3417 * \param client the configured XMPP client we use to connect to a XMPP server
3418 * \param node the name of the node to which to subscribe
3421 static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
3423 iks *request = aji_pubsub_iq_create(client, "set");
3424 iks *pubsub, *subscribe;
3426 pubsub = iks_insert(request, "pubsub");
3427 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3428 subscribe = iks_insert(pubsub, "subscribe");
3429 iks_insert_attrib(subscribe, "jid", client->jid->partial);
3430 iks_insert_attrib(subscribe, "node", node);
3431 if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3432 iks *options, *x, *sub_options, *sub_type, *sub_depth;
3433 options = iks_insert(pubsub, "options");
3434 x = iks_insert(options, "x");
3435 iks_insert_attrib(x, "xmlns", "jabber:x:data");
3436 iks_insert_attrib(x, "type", "submit");
3437 sub_options = iks_insert(x, "field");
3438 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
3439 iks_insert_attrib(sub_options, "type", "hidden");
3440 iks_insert_cdata(iks_insert(sub_options, "value"),
3441 "http://jabber.org/protocol/pubsub#subscribe_options", 51);
3442 sub_type = iks_insert(x, "field");
3443 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
3444 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
3445 sub_depth = iks_insert(x, "field");
3446 iks_insert_attrib(sub_type, "var", "pubsub#subscription_depth");
3447 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
3449 ast_aji_send(client, request);
3450 iks_delete(request);
3454 * \brief Build the skeleton of a publish
3455 * \param client the configured XMPP client we use to connect to a XMPP server
3456 * \param node Name of the node that will be published to
3460 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
3461 const char *event_type, unsigned int cachable)
3463 iks *request = aji_pubsub_iq_create(client, "set");
3464 iks *pubsub, *publish, *item;
3465 pubsub = iks_insert(request, "pubsub");
3466 iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3467 publish = iks_insert(pubsub, "publish");
3468 if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3469 iks_insert_attrib(publish, "node", node);
3471 iks_insert_attrib(publish, "node", event_type);
3473 item = iks_insert(publish, "item");
3474 iks_insert_attrib(item, "id", node);
3476 if (cachable == AST_DEVSTATE_NOT_CACHABLE) {
3477 iks *options, *x, *field_form_type, *field_persist;
3479 options = iks_insert(pubsub, "publish-options");
3480 x = iks_insert(options, "x");
3481 iks_insert_attrib(x, "xmlns", "jabber:x:data");
3482 iks_insert_attrib(x, "type", "submit");
3483 field_form_type = iks_insert(x, "field");
3484 iks_insert_attrib(field_form_type, "var", "FORM_TYPE");
3485 iks_insert_attrib(field_form_type, "type", "hidden");
3486 iks_insert_cdata(iks_insert(field_form_type, "value"), "http://jabber.org/protocol/pubsub#publish-options", 0);
3487 field_persist = iks_insert(x, "field");
3488 iks_insert_attrib(field_persist, "var", "pubsub#persist_items");
3489 iks_insert_cdata(iks_insert(field_persist, "value"), "0", 1);
3496 * \brief Publish device state to a PubSub node
3497 * \param client the configured XMPP client we use to connect to a XMPP server
3498 * \param device the name of the device whose state to publish
3499 * \param device_state the state to publish
3502 static void aji_publish_device_state(struct aji_client *client, const char *device,
3503 const char *device_state, unsigned int cachable)
3505 iks *request = aji_build_publish_skeleton(client, device, "device_state", cachable);
3507 char eid_str[20], cachable_str[2];
3508 if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
3509 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3510 aji_create_pubsub_node(client, "leaf", device, "device_state");
3512 aji_create_pubsub_node(client, NULL, device, NULL);
3515 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3516 state = iks_insert(request, "state");
3517 iks_insert_attrib(state, "xmlns", "http://asterisk.org");
3518 iks_insert_attrib(state, "eid", eid_str);
3519 snprintf(cachable_str, sizeof(cachable_str), "%u", cachable);
3520 iks_insert_attrib(state, "cachable", cachable_str);
3521 iks_insert_cdata(state, device_state, strlen(device_state));
3522 ast_aji_send(client, iks_root(request));
3523 iks_delete(request);
3527 * \brief Publish MWI to a PubSub node
3528 * \param client the configured XMPP client we use to connect to a XMPP server
3529 * \param mailbox The mailbox
3530 * \param context The context
3531 * \param oldmsgs Old messages
3532 * \param newmsgs New messages
3535 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
3536 const char *context, const char *oldmsgs, const char *newmsgs)
3538 char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT];
3540 iks *mailbox_node, *request;
3541 snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
3542 request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting", 1);
3543 ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3544 mailbox_node = iks_insert(request, "mailbox");
3545 iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
3546 iks_insert_attrib(mailbox_node, "eid", eid_str);
3547 iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
3548 iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
3549 ast_aji_send(client, iks_root(request));
3550 iks_delete(request);
3554 * \brief Create an IQ packet
3555 * \param client the configured XMPP client we use to connect to a XMPP server
3556 * \param type the type of IQ packet to create
3559 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type)
3561 iks *request = iks_new("iq");
3563 iks_insert_attrib(request, "to", client->pubsub_node);
3564 iks_insert_attrib(request, "from", client->jid->full);
3565 iks_insert_attrib(request, "type", type);
3566 ast_aji_increment_mid(client->mid);
3567 iks_insert_attrib(request, "id", client->mid);