b808e51ba2dd35e46425c29bb07e9261df6a4a97
[asterisk/asterisk.git] / res / res_jabber.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2010, Digium, Inc.
5  *
6  * Matt O'Gorman <mogorman@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  * \brief A resource for interfacing Asterisk directly as a client
21  * or a component to a XMPP/Jabber compliant server.
22  *
23  * References:
24  * - http://www.xmpp.org - The XMPP standards foundation
25  *
26  * \extref Iksemel http://code.google.com/p/iksemel/
27  *
28  * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
29  * \todo Dialplan applications need RETURN variable, like JABBERSENDSTATUS
30  *
31  */
32
33 /*** MODULEINFO
34         <depend>iksemel</depend>
35         <use type="external">openssl</use>
36         <support_level>extended</support_level>
37  ***/
38
39 #include "asterisk.h"
40
41 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
42
43 #include <ctype.h>
44 #include <iksemel.h>
45
46 #include "asterisk/channel.h"
47 #include "asterisk/jabber.h"
48 #include "asterisk/file.h"
49 #include "asterisk/config.h"
50 #include "asterisk/callerid.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/app.h"
54 #include "asterisk/pbx.h"
55 #include "asterisk/md5.h"
56 #include "asterisk/acl.h"
57 #include "asterisk/utils.h"
58 #include "asterisk/module.h"
59 #include "asterisk/astobj.h"
60 #include "asterisk/astdb.h"
61 #include "asterisk/manager.h"
62 #include "asterisk/event.h"
63 #include "asterisk/devicestate.h"
64 #include "asterisk/message.h"
65
66 /*** DOCUMENTATION
67         <application name="JabberSend" language="en_US">
68                 <synopsis>
69                         Sends an XMPP message to a buddy.
70                 </synopsis>
71                 <syntax>
72                         <parameter name="account" required="true">
73                                 <para>The local named account to listen on (specified in
74                                 jabber.conf)</para>
75                         </parameter>
76                         <parameter name="jid" required="true">
77                                 <para>Jabber ID of the buddy to send the message to. It can be a
78                                 bare JID (username@domain) or a full JID (username@domain/resource).</para>
79                         </parameter>
80                         <parameter name="message" required="true">
81                                 <para>The message to send.</para>
82                         </parameter>
83                 </syntax>
84                 <description>
85                         <para>Sends the content of <replaceable>message</replaceable> as text message
86                         from the given <replaceable>account</replaceable> to the buddy identified by
87                         <replaceable>jid</replaceable></para>
88                         <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
89                         to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
90                         <replaceable>asterisk</replaceable>, configured in jabber.conf.</para>
91                 </description>
92                 <see-also>
93                         <ref type="function">JABBER_STATUS</ref>
94                         <ref type="function">JABBER_RECEIVE</ref>
95                 </see-also>
96         </application>
97         <function name="JABBER_RECEIVE" language="en_US">
98                 <synopsis>
99                         Reads XMPP messages.
100                 </synopsis>
101                 <syntax>
102                         <parameter name="account" required="true">
103                                 <para>The local named account to listen on (specified in
104                                 jabber.conf)</para>
105                         </parameter>
106                         <parameter name="jid" required="true">
107                                 <para>Jabber ID of the buddy to receive message from. It can be a
108                                 bare JID (username@domain) or a full JID (username@domain/resource).</para>
109                         </parameter>
110                         <parameter name="timeout">
111                                 <para>In seconds, defaults to <literal>20</literal>.</para>
112                         </parameter>
113                 </syntax>
114                 <description>
115                         <para>Receives a text message on the given <replaceable>account</replaceable>
116                         from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
117                         <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
118                         sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
119                         the <replaceable>asterisk</replaceable> XMPP account configured in jabber.conf.</para>
120                 </description>
121                 <see-also>
122                         <ref type="function">JABBER_STATUS</ref>
123                         <ref type="application">JabberSend</ref>
124                 </see-also>
125         </function>
126         <function name="JABBER_STATUS" language="en_US">
127                 <synopsis>
128                         Retrieves a buddy's status.
129                 </synopsis>
130                 <syntax>
131                         <parameter name="account" required="true">
132                                 <para>The local named account to listen on (specified in
133                                 jabber.conf)</para>
134                         </parameter>
135                         <parameter name="jid" required="true">
136                                 <para>Jabber ID of the buddy to receive message from. It can be a
137                                 bare JID (username@domain) or a full JID (username@domain/resource).</para>
138                         </parameter>
139                 </syntax>
140                 <description>
141                         <para>Retrieves the numeric status associated with the buddy identified
142                         by <replaceable>jid</replaceable>.
143                         If the buddy does not exist in the buddylist, returns 7.</para>
144                         <para>Status will be 1-7.</para>
145                         <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
146                         <para>If not in roster variable will be set to 7.</para>
147                         <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
148                         <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
149                         the associated XMPP account configured in jabber.conf.</para>
150                 </description>
151                 <see-also>
152                         <ref type="function">JABBER_RECEIVE</ref>
153                         <ref type="application">JabberSend</ref>
154                 </see-also>
155         </function>
156         <application name="JabberSendGroup" language="en_US">
157                 <synopsis>
158                         Send a Jabber Message to a specified chat room
159                 </synopsis>
160                 <syntax>
161                         <parameter name="Jabber" required="true">
162                                 <para>Client or transport Asterisk uses to connect to Jabber.</para>
163                         </parameter>
164                         <parameter name="RoomJID" required="true">
165                                 <para>XMPP/Jabber JID (Name) of chat room.</para>
166                         </parameter>
167                         <parameter name="Message" required="true">
168                                 <para>Message to be sent to the chat room.</para>
169                         </parameter>
170                         <parameter name="Nickname" required="false">
171                                 <para>The nickname Asterisk uses in the chat room.</para>
172                         </parameter>
173                 </syntax>
174                 <description>
175                         <para>Allows user to send a message to a chat room via XMPP.</para>
176                         <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>
177                 </description>
178         </application>
179         <application name="JabberJoin" language="en_US">
180                 <synopsis>
181                         Join a chat room
182                 </synopsis>
183                 <syntax>
184                         <parameter name="Jabber" required="true">
185                                 <para>Client or transport Asterisk uses to connect to Jabber.</para>
186                         </parameter>
187                         <parameter name="RoomJID" required="true">
188                                 <para>XMPP/Jabber JID (Name) of chat room.</para>
189                         </parameter>
190                         <parameter name="Nickname" required="false">
191                                 <para>The nickname Asterisk will use in the chat room.</para>
192                                 <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>
193                         </parameter>
194                 </syntax>
195                 <description>
196                         <para>Allows Asterisk to join a chat room.</para>
197                 </description>
198         </application>
199         <application name="JabberLeave" language="en_US">
200                 <synopsis>
201                         Leave a chat room
202                 </synopsis>
203                 <syntax>
204                         <parameter name="Jabber" required="true">
205                                 <para>Client or transport Asterisk uses to connect to Jabber.</para>
206                         </parameter>
207                         <parameter name="RoomJID" required="true">
208                                 <para>XMPP/Jabber JID (Name) of chat room.</para>
209                         </parameter>
210                         <parameter name="Nickname" required="false">
211                                 <para>The nickname Asterisk uses in the chat room.</para>
212                         </parameter>
213                 </syntax>
214                 <description>
215                         <para>Allows Asterisk to leave a chat room.</para>
216                 </description>
217         </application>
218         <application name="JabberStatus" language="en_US">
219                 <synopsis>
220                         Retrieve the status of a jabber list member
221                 </synopsis>
222                 <syntax>
223                         <parameter name="Jabber" required="true">
224                                 <para>Client or transport Asterisk users to connect to Jabber.</para>
225                         </parameter>
226                         <parameter name="JID" required="true">
227                                 <para>XMPP/Jabber JID (Name) of recipient.</para>
228                         </parameter>
229                         <parameter name="Variable" required="true">
230                                 <para>Variable to store the status of requested user.</para>
231                         </parameter>
232                 </syntax>
233                 <description>
234                         <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
235                         <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
236                         The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
237                         <enumlist>
238                                 <enum name="1">
239                                         <para>Online.</para>
240                                 </enum>
241                                 <enum name="2">
242                                         <para>Chatty.</para>
243                                 </enum>
244                                 <enum name="3">
245                                         <para>Away.</para>
246                                 </enum>
247                                 <enum name="4">
248                                         <para>Extended Away.</para>
249                                 </enum>
250                                 <enum name="5">
251                                         <para>Do Not Disturb.</para>
252                                 </enum>
253                                 <enum name="6">
254                                         <para>Offline.</para>
255                                 </enum>
256                                 <enum name="7">
257                                         <para>Not In Roster.</para>
258                                 </enum>
259                         </enumlist>
260                 </description>
261         </application>
262         <manager name="JabberSend" language="en_US">
263                 <synopsis>
264                         Sends a message to a Jabber Client.
265                 </synopsis>
266                 <syntax>
267                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
268                         <parameter name="Jabber" required="true">
269                                 <para>Client or transport Asterisk uses to connect to JABBER.</para>
270                         </parameter>
271                         <parameter name="JID" required="true">
272                                 <para>XMPP/Jabber JID (Name) of recipient.</para>
273                         </parameter>
274                         <parameter name="Message" required="true">
275                                 <para>Message to be sent to the buddy.</para>
276                         </parameter>
277                 </syntax>
278                 <description>
279                         <para>Sends a message to a Jabber Client.</para>
280                 </description>
281         </manager>
282  ***/
283
284 /*!\todo This should really be renamed to xmpp.conf. For backwards compatibility, we
285  * need to read both files */
286 #define JABBER_CONFIG "jabber.conf"
287
288 /*-- Forward declarations */
289 static void aji_message_destroy(struct aji_message *obj);
290 static int aji_is_secure(struct aji_client *client);
291 #ifdef HAVE_OPENSSL
292 static int aji_start_tls(struct aji_client *client);
293 static int aji_tls_handshake(struct aji_client *client);
294 #endif
295 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
296 static int aji_recv(struct aji_client *client, int timeout);
297 static int aji_send_header(struct aji_client *client, const char *to);
298 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
299 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
300 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
301 static int aji_act_hook(void *data, int type, iks *node);
302 static void aji_handle_iq(struct aji_client *client, iks *node);
303 static void aji_handle_message(struct aji_client *client, ikspak *pak);
304 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
305 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
306 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
307 static void *aji_recv_loop(void *data);
308 static int aji_initialize(struct aji_client *client);
309 static int aji_client_connect(void *data, ikspak *pak);
310 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
311 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
312 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
313 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
314 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
315 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
316 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
317 static int aji_create_client(char *label, struct ast_variable *var, int debug);
318 static int aji_create_buddy(char *label, struct aji_client *client);
319 static int aji_reload(int reload);
320 static int aji_load_config(int reload);
321 static void aji_pruneregister(struct aji_client *client);
322 static int aji_filter_roster(void *data, ikspak *pak);
323 static int aji_get_roster(struct aji_client *client);
324 static int aji_client_info_handler(void *data, ikspak *pak);
325 static int aji_dinfo_handler(void *data, ikspak *pak);
326 static int aji_ditems_handler(void *data, ikspak *pak);
327 static int aji_register_query_handler(void *data, ikspak *pak);
328 static int aji_register_approve_handler(void *data, ikspak *pak);
329 static int aji_reconnect(struct aji_client *client);
330 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd,
331         struct ast_cli_args *a);
332 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd,
333         struct ast_cli_args *a);
334 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
335         ast_cli_args *a);
336 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
337                 ast_cli_args *a);
338 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
339 static int aji_receive_node_list(void *data, ikspak* pak);
340 static void aji_init_event_distribution(struct aji_client *client);
341 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type,
342         const char *name, const char *collection_name);
343 static iks* aji_build_node_config(iks *pubsub, const char *node_type,
344         const char *collection_name);
345 static void aji_create_pubsub_collection(struct aji_client *client,
346         const char *collection_name);
347 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
348    const char *leaf_name);
349 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
350         struct ast_cli_args *a);
351 static void aji_create_affiliations(struct aji_client *client, const char *node);
352 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
353 static void aji_publish_device_state(struct aji_client *client, const char * device,
354         const char *device_state);
355 static int aji_handle_pubsub_error(void *data, ikspak *pak);
356 static int aji_handle_pubsub_event(void *data, ikspak *pak);
357 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
358 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name);
359 static iks* aji_build_node_request(struct aji_client *client, const char *collection);
360 static int aji_delete_node_list(void *data, ikspak* pak);
361 static void aji_pubsub_purge_nodes(struct aji_client *client,
362         const char* collection_name);
363 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
364         const char *context, const char *oldmsgs, const char *newmsgs);
365 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
366 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
367 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
368         const char *event_type);
369 /* No transports in this version */
370 /*
371 static int aji_create_transport(char *label, struct aji_client *client);
372 static int aji_register_transport(void *data, ikspak *pak);
373 static int aji_register_transport2(void *data, ikspak *pak);
374 */
375
376 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from);
377
378 static const struct ast_msg_tech msg_tech = {
379         .name = "xmpp",
380         .msg_send = msg_send_cb,
381 };
382
383 static struct ast_cli_entry aji_cli[] = {
384         AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
385         AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
386         AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
387         AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
388         AST_CLI_DEFINE(aji_test, "Shows roster, but is generally used for mog's debugging."),
389         AST_CLI_DEFINE(aji_cli_create_collection, "Creates a PubSub node collection."),
390         AST_CLI_DEFINE(aji_cli_list_pubsub_nodes, "Lists PubSub nodes"),
391         AST_CLI_DEFINE(aji_cli_create_leafnode, "Creates a PubSub leaf node"),
392         AST_CLI_DEFINE(aji_cli_delete_pubsub_node, "Deletes a PubSub node"),
393         AST_CLI_DEFINE(aji_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
394 };
395
396 static char *app_ajisend = "JabberSend";
397 static char *app_ajisendgroup = "JabberSendGroup";
398 static char *app_ajistatus = "JabberStatus";
399 static char *app_ajijoin = "JabberJoin";
400 static char *app_ajileave = "JabberLeave";
401
402 static struct aji_client_container clients;
403 static struct aji_capabilities *capabilities = NULL;
404 static struct ast_event_sub *mwi_sub = NULL;
405 static struct ast_event_sub *device_state_sub = NULL;
406 static ast_cond_t message_received_condition;
407 static ast_mutex_t messagelock;
408
409 /*! \brief Global flags, initialized to default values */
410 static struct ast_flags globalflags = { AJI_AUTOREGISTER | AJI_AUTOACCEPT };
411
412 /*! \brief PubSub flags, initialized to default values */
413 static struct ast_flags pubsubflags = { 0 };
414 /*!
415  * \internal
416  * \brief Deletes the aji_client data structure.
417  * \param obj aji_client The structure we will delete.
418  * \return void.
419  */
420 void ast_aji_client_destroy(struct aji_client *obj)
421 {
422         struct aji_message *tmp;
423         ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, ast_aji_buddy_destroy);
424         ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
425         iks_filter_delete(obj->f);
426         iks_parser_delete(obj->p);
427         iks_stack_delete(obj->stack);
428         AST_LIST_LOCK(&obj->messages);
429         while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
430                 aji_message_destroy(tmp);
431         }
432         AST_LIST_HEAD_DESTROY(&obj->messages);
433         ast_free(obj);
434 }
435
436 /*!
437  * \internal
438  * \brief Deletes the aji_buddy data structure.
439  * \param obj aji_buddy The structure we will delete.
440  * \return void.
441  */
442 void ast_aji_buddy_destroy(struct aji_buddy *obj)
443 {
444         struct aji_resource *tmp;
445
446         while ((tmp = obj->resources)) {
447                 obj->resources = obj->resources->next;
448                 ast_free(tmp->description);
449                 ast_free(tmp);
450         }
451
452         ast_free(obj);
453 }
454
455 /*!
456  * \internal
457  * \brief Deletes the aji_message data structure.
458  * \param obj aji_message The structure we will delete.
459  * \return void.
460  */
461 static void aji_message_destroy(struct aji_message *obj)
462 {
463         if (obj->from) {
464                 ast_free(obj->from);
465         }
466         if (obj->message) {
467                 ast_free(obj->message);
468         }
469         ast_free(obj);
470 }
471
472 /*!
473  * \internal
474  * \brief Find version in XML stream and populate our capabilities list
475  * \param node the node attribute in the caps element we'll look for or add to
476  * our list
477  * \param version the version attribute in the caps element we'll look for or
478  * add to our list
479  * \param pak struct The XML stanza we're processing
480  * \return a pointer to the added or found aji_version structure
481  */
482 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
483 {
484         struct aji_capabilities *list = NULL;
485         struct aji_version *res = NULL;
486
487         list = capabilities;
488
489         if (!node) {
490                 node = pak->from->full;
491         }
492         if (!version) {
493                 version = "none supplied.";
494         }
495         while (list) {
496                 if (!strcasecmp(list->node, node)) {
497                         res = list->versions;
498                         while(res) {
499                                 if (!strcasecmp(res->version, version)) {
500                                         return res;
501                                 }
502                                 res = res->next;
503                         }
504                         /* Specified version not found. Let's add it to
505                            this node in our capabilities list */
506                         if (!res) {
507                                 res = ast_malloc(sizeof(*res));
508                                 if (!res) {
509                                         ast_log(LOG_ERROR, "Out of memory!\n");
510                                         return NULL;
511                                 }
512                                 res->jingle = 0;
513                                 res->parent = list;
514                                 ast_copy_string(res->version, version, sizeof(res->version));
515                                 res->next = list->versions;
516                                 list->versions = res;
517                                 return res;
518                         }
519                 }
520                 list = list->next;
521         }
522         /* Specified node not found. Let's add it our capabilities list */
523         if (!list) {
524                 list = ast_malloc(sizeof(*list));
525                 if (!list) {
526                         ast_log(LOG_ERROR, "Out of memory!\n");
527                         return NULL;
528                 }
529                 res = ast_malloc(sizeof(*res));
530                 if (!res) {
531                         ast_log(LOG_ERROR, "Out of memory!\n");
532                         ast_free(list);
533                         return NULL;
534                 }
535                 ast_copy_string(list->node, node, sizeof(list->node));
536                 ast_copy_string(res->version, version, sizeof(res->version));
537                 res->jingle = 0;
538                 res->parent = list;
539                 res->next = NULL;
540                 list->versions = res;
541                 list->next = capabilities;
542                 capabilities = list;
543         }
544         return res;
545 }
546
547 /*!
548  * \internal
549  * \brief Find the aji_resource we want
550  * \param buddy aji_buddy A buddy
551  * \param name
552  * \return aji_resource object
553 */
554 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
555 {
556         struct aji_resource *res = NULL;
557         if (!buddy || !name) {
558                 return res;
559         }
560         res = buddy->resources;
561         while (res) {
562                 if (!strcasecmp(res->resource, name)) {
563                         break;
564                 }
565                 res = res->next;
566         }
567         return res;
568 }
569
570 /*!
571  * \internal
572  * \brief Jabber GTalk function
573  * \param node iks
574  * \return 1 on success, 0 on failure.
575 */
576 static int gtalk_yuck(iks *node)
577 {
578         if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) {
579                 ast_debug(1, "Found resource with Googletalk voice capabilities\n");
580                 return 1;
581         } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1")) {
582                 ast_debug(1, "Found resource with Gmail voice/video chat capabilities\n");
583                 return 1;
584         } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 video-v1 voice-v1")) {
585                 ast_debug(1, "Found resource with Gmail voice/video chat capabilities (no camera)\n");
586                 return 1;
587         }
588
589         return 0;
590 }
591
592 /*!
593  * \internal
594  * \brief Setup the authentication struct
595  * \param id iksid 
596  * \param pass password
597  * \param sid
598  * \return x iks
599 */
600 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
601 {
602         iks *x, *y;
603         x = iks_new("iq");
604         iks_insert_attrib(x, "type", "set");
605         y = iks_insert(x, "query");
606         iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
607         iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
608         iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
609         if (sid) {
610                 char buf[41];
611                 char sidpass[100];
612                 snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
613                 ast_sha1_hash(buf, sidpass);
614                 iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
615         } else {
616                 iks_insert_cdata(iks_insert(y, "password"), pass, 0);
617         }
618         return x;
619 }
620
621 /*!
622  * \internal
623  * \brief Dial plan function status(). puts the status of watched user 
624  * into a channel variable.
625  * \param chan ast_channel
626  * \param data
627  * \retval 0 success
628  * \retval -1 error
629  */
630 static int aji_status_exec(struct ast_channel *chan, const char *data)
631 {
632         struct aji_client *client = NULL;
633         struct aji_buddy *buddy = NULL;
634         struct aji_resource *r = NULL;
635         char *s = NULL;
636         int stat = 7;
637         char status[2];
638         static int deprecation_warning = 0;
639         AST_DECLARE_APP_ARGS(args,
640                 AST_APP_ARG(sender);
641                 AST_APP_ARG(jid);
642                 AST_APP_ARG(variable);
643         );
644         AST_DECLARE_APP_ARGS(jid,
645                 AST_APP_ARG(screenname);
646                 AST_APP_ARG(resource);
647         );
648
649         if (deprecation_warning++ % 10 == 0) {
650                 ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
651         }
652
653         if (!data) {
654                 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
655                 return 0;
656         }
657         s = ast_strdupa(data);
658         AST_STANDARD_APP_ARGS(args, s);
659
660         if (args.argc != 3) {
661                 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
662                 return -1;
663         }
664
665         AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
666         if (jid.argc < 1 || jid.argc > 2) {
667                 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
668                 return -1;
669         }
670
671         if (!(client = ast_aji_get_client(args.sender))) {
672                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
673                 return -1;
674         }
675         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
676         if (!buddy) {
677                 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
678                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
679                 return -1;
680         }
681         r = aji_find_resource(buddy, jid.resource);
682         if (!r && buddy->resources) {
683                 r = buddy->resources;
684         }
685         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
686         ASTOBJ_UNREF(client, ast_aji_client_destroy);
687         if (!r) {
688                 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
689         } else {
690                 stat = r->status;
691         }
692         snprintf(status, sizeof(status), "%d", stat);
693         pbx_builtin_setvar_helper(chan, args.variable, status);
694         return 0;
695 }
696
697 /*!
698  * \internal
699  * \brief Dial plan funtcion to retrieve the status of a buddy.
700  * \param channel The associated ast_channel, if there is one
701  * \param data The account, buddy JID, and optional timeout
702  * timeout.
703  * \retval 0 success
704  * \retval -1 failure
705  */
706 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
707 {
708         struct aji_client *client = NULL;
709         struct aji_buddy *buddy = NULL;
710         struct aji_resource *r = NULL;
711         int stat = 7;
712         AST_DECLARE_APP_ARGS(args,
713                 AST_APP_ARG(sender);
714                 AST_APP_ARG(jid);
715         );
716         AST_DECLARE_APP_ARGS(jid,
717                 AST_APP_ARG(screenname);
718                 AST_APP_ARG(resource);
719         );
720
721         if (!data) {
722                 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
723                 return 0;
724         }
725         AST_STANDARD_APP_ARGS(args, data);
726
727         if (args.argc != 2) {
728                 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
729                 return -1;
730         }
731
732         AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
733         if (jid.argc < 1 || jid.argc > 2) {
734                 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
735                 return -1;
736         }
737
738         if (!(client = ast_aji_get_client(args.sender))) {
739                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
740                 return -1;
741         }
742         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
743         if (!buddy) {
744                 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
745                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
746                 return -1;
747         }
748         r = aji_find_resource(buddy, jid.resource);
749         if (!r && buddy->resources) {
750                 r = buddy->resources;
751         }
752         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
753         ASTOBJ_UNREF(client, ast_aji_client_destroy);
754         if (!r) {
755                 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
756         } else {
757                 stat = r->status;
758         }
759         snprintf(buf, buflen, "%d", stat);
760         return 0;
761 }
762
763 static struct ast_custom_function jabberstatus_function = {
764         .name = "JABBER_STATUS",
765         .read = acf_jabberstatus_read,
766 };
767
768 /*!
769  * \internal
770  * \brief Dial plan function to receive a message.
771  * \param channel The associated ast_channel, if there is one
772  * \param data The account, JID, and optional timeout
773  * timeout.
774  * \retval 0 success
775  * \retval -1 failure
776  */
777 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
778 {
779         char *aux = NULL, *parse = NULL;
780         int timeout;
781         int jidlen, resourcelen;
782         struct timeval start;
783         long diff = 0;
784         struct aji_client *client = NULL;
785         int found = 0;
786         struct aji_message *tmp = NULL;
787         AST_DECLARE_APP_ARGS(args,
788                         AST_APP_ARG(account);
789                         AST_APP_ARG(jid);
790                         AST_APP_ARG(timeout);
791                         );
792         AST_DECLARE_APP_ARGS(jid,
793                         AST_APP_ARG(screenname);
794                         AST_APP_ARG(resource);
795         );
796
797         if (ast_strlen_zero(data)) {
798                 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
799                 return -1;
800         }
801
802         parse = ast_strdupa(data);
803         AST_STANDARD_APP_ARGS(args, parse);
804
805         if (args.argc < 2 || args.argc > 3) {
806                 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
807                 return -1;
808         }
809
810         parse = ast_strdupa(args.jid);
811         AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
812         if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
813                 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
814                 return -1;
815         }
816
817         if (ast_strlen_zero(args.timeout)) {
818                 timeout = 20;
819         } else {
820                 sscanf(args.timeout, "%d", &timeout);
821                 if (timeout <= 0) {
822                         ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
823                         return -1;
824                 }
825         }
826
827         jidlen = strlen(jid.screenname);
828         resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
829
830         client = ast_aji_get_client(args.account);
831         if (!client) {
832                 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
833                 return -1;
834         }
835
836         ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
837
838         start = ast_tvnow();
839
840         if (ast_autoservice_start(chan) < 0) {
841                 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
842                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
843                 return -1;
844         }
845
846         /* search the messages list, grab the first message that matches with
847          * the from JID we're expecting, and remove it from the messages list */
848         while (diff < timeout) {
849                 struct timespec ts = { 0, };
850                 struct timeval wait;
851                 int res;
852
853                 wait = ast_tvadd(start, ast_tv(timeout, 0));
854                 ts.tv_sec = wait.tv_sec;
855                 ts.tv_nsec = wait.tv_usec * 1000;
856
857                 /* wait up to timeout seconds for an incoming message */
858                 ast_mutex_lock(&messagelock);
859                 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
860                 ast_mutex_unlock(&messagelock);
861                 if (res == ETIMEDOUT) {
862                         ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
863                         break;
864                 }
865
866                 AST_LIST_LOCK(&client->messages);
867                 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
868                         if (jid.argc == 1) {
869                                 /* no resource provided, compare bare JIDs */
870                                 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
871                                         continue;
872                                 }
873                         } else {
874                                 /* resource appended, compare bare JIDs and resources */
875                                 char *resource = strchr(tmp->from, '/');
876                                 if (!resource || strlen(resource) == 0) {
877                                         ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
878                                         if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
879                                                 continue;
880                                         }
881                                 } else {
882                                         resource ++;
883                                         if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
884                                                 continue;
885                                         }
886                                 }
887                         }
888                         /* check if the message is not too old */
889                         if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
890                                 ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
891                                 AST_LIST_REMOVE_CURRENT(list);
892                                 aji_message_destroy(tmp);
893                                 continue;
894                         }
895                         found = 1;
896                         aux = ast_strdupa(tmp->message);
897                         AST_LIST_REMOVE_CURRENT(list);
898                         aji_message_destroy(tmp);
899                         break;
900                 }
901                 AST_LIST_TRAVERSE_SAFE_END;
902                 AST_LIST_UNLOCK(&client->messages);
903                 if (found) {
904                         break;
905                 }
906
907                 /* check timeout */
908                 diff = ast_tvdiff_ms(ast_tvnow(), start);
909         }
910
911         ASTOBJ_UNREF(client, ast_aji_client_destroy);
912         if (ast_autoservice_stop(chan) < 0) {
913                 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
914         }
915
916         /* return if we timed out */
917         if (!found) {
918                 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
919                 return -1;
920         }
921         ast_copy_string(buf, aux, buflen);
922
923         return 0;
924 }
925
926 static struct ast_custom_function jabberreceive_function = {
927         .name = "JABBER_RECEIVE",
928         .read = acf_jabberreceive_read,
929 };
930
931 /*!
932  * \internal
933  * \brief Delete old messages from a given JID
934  * Messages stored during more than client->message_timeout are deleted
935  * \param client Asterisk's XMPP client
936  * \param from the JID we received messages from
937  * \retval the number of deleted messages
938  * \retval -1 failure
939  */
940 static int delete_old_messages(struct aji_client *client, char *from)
941 {
942         int deleted = 0;
943         int isold = 0;
944         struct aji_message *tmp = NULL;
945         if (!client) {
946                 ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
947                 return -1;
948         }
949
950         /* remove old messages */
951         AST_LIST_LOCK(&client->messages);
952         if (AST_LIST_EMPTY(&client->messages)) {
953                 AST_LIST_UNLOCK(&client->messages);
954                 return 0;
955         }
956
957         AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
958                 if (isold) {
959                         if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
960                                 AST_LIST_REMOVE_CURRENT(list);
961                                 aji_message_destroy(tmp);
962                                 deleted ++;
963                         }
964                 } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
965                         isold = 1;
966                         if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
967                                 AST_LIST_REMOVE_CURRENT(list);
968                                 aji_message_destroy(tmp);
969                                 deleted ++;
970                         }
971                 }
972         }
973         AST_LIST_TRAVERSE_SAFE_END;
974         AST_LIST_UNLOCK(&client->messages);
975
976         return deleted;
977 }
978
979 /*!
980  * \internal
981  * \brief Delete old messages
982  * Messages stored during more than client->message_timeout are deleted
983  * \param client Asterisk's XMPP client
984  * \retval the number of deleted messages
985  * \retval -1 failure
986  */
987 static int delete_old_messages_all(struct aji_client *client)
988 {
989         return delete_old_messages(client, NULL);
990 }
991
992 /*!
993 * \brief Application to join a chat room
994 * \param chan ast_channel
995 * \param data  Data is sender|jid|nickname.
996 * \retval 0 success
997 * \retval -1 error
998 */
999 static int aji_join_exec(struct ast_channel *chan, const char *data)
1000 {
1001         struct aji_client *client = NULL;
1002         char *s;
1003         char nick[AJI_MAX_RESJIDLEN];
1004
1005         AST_DECLARE_APP_ARGS(args,
1006                 AST_APP_ARG(sender);
1007                 AST_APP_ARG(jid);
1008                 AST_APP_ARG(nick);
1009         );
1010
1011         if (!data) {
1012                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1013                 return -1;
1014         }
1015         s = ast_strdupa(data);
1016
1017         AST_STANDARD_APP_ARGS(args, s);
1018         if (args.argc < 2 || args.argc > 3) {
1019                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1020                 return -1;
1021         }
1022
1023         if (strchr(args.jid, '/')) {
1024                 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1025                 return -1;
1026         }
1027
1028         if (!(client = ast_aji_get_client(args.sender))) {
1029                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1030                 return -1;
1031         }
1032
1033         if (!ast_strlen_zero(args.nick)) {
1034                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1035         } else {
1036                 if (client->component) {
1037                         sprintf(nick, "asterisk");
1038                 } else {
1039                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1040                 }
1041         }
1042
1043         if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1044                 ast_aji_join_chat(client, args.jid, nick);
1045         } else {
1046                 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1047         }
1048
1049         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1050         return 0;
1051 }
1052
1053 /*!
1054 * \brief Application to leave a chat room
1055 * \param chan ast_channel
1056 * \param data  Data is sender|jid|nickname.
1057 * \retval 0 success
1058 * \retval -1 error
1059 */
1060 static int aji_leave_exec(struct ast_channel *chan, const char *data)
1061 {
1062         struct aji_client *client = NULL;
1063         char *s;
1064         char nick[AJI_MAX_RESJIDLEN];
1065         AST_DECLARE_APP_ARGS(args,
1066                 AST_APP_ARG(sender);
1067                 AST_APP_ARG(jid);
1068                 AST_APP_ARG(nick);
1069         );
1070
1071         if (!data) {
1072                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1073                 return -1;
1074         }
1075         s = ast_strdupa(data);
1076
1077         AST_STANDARD_APP_ARGS(args, s);
1078         if (args.argc < 2 || args.argc > 3) {
1079                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1080                 return -1;
1081         }
1082
1083         if (strchr(args.jid, '/')) {
1084                 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1085                 return -1;
1086         }
1087
1088         if (!(client = ast_aji_get_client(args.sender))) {
1089                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1090                 return -1;
1091         }
1092
1093         if (!ast_strlen_zero(args.nick)) {
1094                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1095         } else {
1096                 if (client->component) {
1097                         sprintf(nick, "asterisk");
1098                 } else {
1099                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1100                 }
1101         }
1102
1103         if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1104                 ast_aji_leave_chat(client, args.jid, nick);
1105         }
1106         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1107         return 0;
1108 }
1109
1110 /*!
1111  * \internal
1112  * \brief Dial plan function to send a message.
1113  * \param chan ast_channel
1114  * \param data  Data is account,jid,message.
1115  * \retval 0 success
1116  * \retval -1 failure
1117  */
1118 static int aji_send_exec(struct ast_channel *chan, const char *data)
1119 {
1120         struct aji_client *client = NULL;
1121         char *s;
1122         AST_DECLARE_APP_ARGS(args,
1123                 AST_APP_ARG(sender);
1124                 AST_APP_ARG(recipient);
1125                 AST_APP_ARG(message);
1126         );
1127
1128         if (!data) {
1129                 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1130                 return -1;
1131         }
1132         s = ast_strdupa(data);
1133
1134         AST_STANDARD_APP_ARGS(args, s);
1135         if (args.argc < 3) {
1136                 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1137                 return -1;
1138         }
1139
1140         if (!(client = ast_aji_get_client(args.sender))) {
1141                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1142                 return -1;
1143         }
1144         if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
1145                 ast_aji_send_chat(client, args.recipient, args.message);
1146         }
1147         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1148         return 0;
1149 }
1150
1151 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from)
1152 {
1153         struct aji_client *client;
1154         char *sender;
1155         char *dest;
1156         int res;
1157
1158         sender = ast_strdupa(from);
1159         strsep(&sender, ":");
1160         dest = ast_strdupa(to);
1161         strsep(&dest, ":");
1162
1163         if (ast_strlen_zero(sender)) {
1164                 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for xmpp\n", from);
1165                 return -1;
1166         }
1167
1168         if (!(client = ast_aji_get_client(sender))) {
1169                 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
1170                 return -1;
1171         }
1172
1173         ast_debug(1, "Sending message to '%s' from '%s'\n", dest, client->name);
1174
1175         res = ast_aji_send_chat(client, dest, ast_msg_get_body(msg));
1176         if (res != IKS_OK) {
1177                 ast_log(LOG_WARNING, "Failed to send xmpp message (%d).\n", res);
1178         }
1179
1180         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1181         return res == IKS_OK ? 0 : -1;
1182 }
1183
1184 /*!
1185 * \brief Application to send a message to a groupchat.
1186 * \param chan ast_channel
1187 * \param data  Data is sender|groupchat|message.
1188 * \retval 0 success
1189 * \retval -1 error
1190 */
1191 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
1192 {
1193         struct aji_client *client = NULL;
1194         char *s;
1195         char nick[AJI_MAX_RESJIDLEN];
1196         int res = 0;
1197         AST_DECLARE_APP_ARGS(args,
1198                 AST_APP_ARG(sender);
1199                 AST_APP_ARG(groupchat);
1200                 AST_APP_ARG(message);
1201                 AST_APP_ARG(nick);
1202         );
1203
1204         if (!data) {
1205                 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1206                 return -1;
1207         }
1208         s = ast_strdupa(data);
1209
1210         AST_STANDARD_APP_ARGS(args, s);
1211         if (args.argc < 3 || args.argc > 4) {
1212                 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1213                 return -1;
1214         }
1215
1216         if (!(client = ast_aji_get_client(args.sender))) {
1217                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1218                 return -1;
1219         }
1220
1221         if (ast_strlen_zero(args.nick) || args.argc == 3) {
1222                 if (client->component) {
1223                         sprintf(nick, "asterisk");
1224                 } else {
1225                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1226                 }
1227         } else {
1228                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1229         }
1230
1231         if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
1232                 res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
1233         }
1234
1235         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1236         if (res != IKS_OK) {
1237                 return -1;
1238         }
1239         return 0;
1240 }
1241
1242 /*!
1243  * \internal
1244  * \brief Tests whether the connection is secured or not
1245  * \return 0 if the connection is not secured
1246  */
1247 static int aji_is_secure(struct aji_client *client)
1248 {
1249 #ifdef HAVE_OPENSSL
1250         return client->stream_flags & SECURE;
1251 #else
1252         return 0;
1253 #endif
1254 }
1255
1256 #ifdef HAVE_OPENSSL
1257 /*!
1258  * \internal
1259  * \brief Starts the TLS procedure
1260  * \param client the configured XMPP client we use to connect to a XMPP server
1261  * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
1262  * if OpenSSL is not installed
1263  */
1264 static int aji_start_tls(struct aji_client *client)
1265 {
1266         int ret;
1267
1268         /* This is sent not encrypted */
1269         if ((ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"))) {
1270                 return ret;
1271         }
1272
1273         client->stream_flags |= TRY_SECURE;
1274         return IKS_OK;
1275 }
1276
1277 /*!
1278  * \internal
1279  * \brief TLS handshake, OpenSSL initialization
1280  * \param client the configured XMPP client we use to connect to a XMPP server
1281  * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
1282  */
1283 static int aji_tls_handshake(struct aji_client *client)
1284 {
1285         int ret;
1286         int sock;
1287
1288         ast_debug(1, "Starting TLS handshake\n");
1289
1290         /* Choose an SSL/TLS protocol version, create SSL_CTX */
1291         client->ssl_method = SSLv3_method();
1292         if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
1293                 return IKS_NET_TLSFAIL;
1294         }
1295
1296         /* Create new SSL session */
1297         if (!(client->ssl_session = SSL_new(client->ssl_context))) {
1298                 return IKS_NET_TLSFAIL;
1299         }
1300
1301         /* Enforce TLS on our XMPP connection */
1302         sock = iks_fd(client->p);
1303         if (!(ret = SSL_set_fd(client->ssl_session, sock))) {
1304                 return IKS_NET_TLSFAIL;
1305         }
1306
1307         /* Perform SSL handshake */
1308         if (!(ret = SSL_connect(client->ssl_session))) {
1309                 return IKS_NET_TLSFAIL;
1310         }
1311
1312         client->stream_flags &= (~TRY_SECURE);
1313         client->stream_flags |= SECURE;
1314
1315         /* Sent over the established TLS connection */
1316         if ((ret = aji_send_header(client, client->jid->server)) != IKS_OK) {
1317                 return IKS_NET_TLSFAIL;
1318         }
1319
1320         ast_debug(1, "TLS started with server\n");
1321
1322         return IKS_OK;
1323 }
1324 #endif /* HAVE_OPENSSL */
1325
1326 /*!
1327  * \internal
1328  * \brief Secured or unsecured IO socket receiving function
1329  * \param client the configured XMPP client we use to connect to a XMPP server
1330  * \param buffer the reception buffer
1331  * \param buf_len the size of the buffer
1332  * \param timeout the select timer
1333  * \retval the number of read bytes
1334  * \retval 0 timeout expiration
1335  * \retval -1 error
1336  */
1337 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
1338 {
1339         struct pollfd pfd = { .events = POLLIN };
1340         int len, res;
1341
1342 #ifdef HAVE_OPENSSL
1343         if (aji_is_secure(client)) {
1344                 pfd.fd = SSL_get_fd(client->ssl_session);
1345                 if (pfd.fd < 0) {
1346                         return -1;
1347                 }
1348         } else
1349 #endif /* HAVE_OPENSSL */
1350                 pfd.fd = iks_fd(client->p);
1351
1352         res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
1353         if (res > 0) {
1354 #ifdef HAVE_OPENSSL
1355                 if (aji_is_secure(client)) {
1356                         len = SSL_read(client->ssl_session, buffer, buf_len);
1357                 } else
1358 #endif /* HAVE_OPENSSL */
1359                         len = recv(pfd.fd, buffer, buf_len, 0);
1360
1361                 if (len > 0) {
1362                         return len;
1363                 } else if (len <= 0) {
1364                         return -1;
1365                 }
1366         }
1367         return res;
1368 }
1369
1370 /*!
1371  * \internal
1372  * \brief Tries to receive data from the Jabber server
1373  * \param client the configured XMPP client we use to connect to a XMPP server
1374  * \param timeout the timeout value
1375  * This function receives (encrypted or unencrypted) data from the XMPP server,
1376  * and passes it to the parser.
1377  * \retval IKS_OK success
1378  * \retval IKS_NET_RWERR IO error
1379  * \retval IKS_NET_NOCONN no connection available
1380  * \retval IKS_NET_EXPIRED timeout expiration
1381  */
1382 static int aji_recv (struct aji_client *client, int timeout)
1383 {
1384         int len, ret;
1385         char buf[NET_IO_BUF_SIZE - 1];
1386         char newbuf[NET_IO_BUF_SIZE - 1];
1387         int pos = 0;
1388         int newbufpos = 0;
1389         unsigned char c;
1390
1391         memset(buf, 0, sizeof(buf));
1392         memset(newbuf, 0, sizeof(newbuf));
1393
1394         while (1) {
1395                 len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
1396                 if (len < 0) return IKS_NET_RWERR;
1397                 if (len == 0) return IKS_NET_EXPIRED;
1398                 buf[len] = '\0';
1399
1400                 /* our iksemel parser won't work as expected if we feed
1401                    it with XML packets that contain multiple whitespace
1402                    characters between tags */
1403                 while (pos < len) {
1404                         c = buf[pos];
1405                         /* if we stumble on the ending tag character,
1406                            we skip any whitespace that follows it*/
1407                         if (c == '>') {
1408                                 while (isspace(buf[pos+1])) {
1409                                         pos++;
1410                                 }
1411                         }
1412                         newbuf[newbufpos] = c;
1413                         newbufpos ++;
1414                         pos++;
1415                 }
1416                 pos = 0;
1417                 newbufpos = 0;
1418
1419                 /* Log the message here, because iksemel's logHook is
1420                    unaccessible */
1421                 aji_log_hook(client, buf, len, 1);
1422
1423                 /* let iksemel deal with the string length,
1424                    and reset our buffer */
1425                 ret = iks_parse(client->p, newbuf, 0, 0);
1426                 memset(newbuf, 0, sizeof(newbuf));
1427
1428                 switch (ret) {
1429                 case IKS_NOMEM:
1430                         ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
1431                         break;
1432                 case IKS_BADXML:
1433                         ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
1434                         break;
1435                 case IKS_HOOK:
1436                         ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
1437                         break;
1438                 }
1439                 if (ret != IKS_OK) {
1440                         return ret;
1441                 }
1442                 ast_debug(3, "XML parsing successful\n");
1443         }
1444         return IKS_OK;
1445 }
1446
1447 /*!
1448  * \internal
1449  * \brief Sends XMPP header to the server
1450  * \param client the configured XMPP client we use to connect to a XMPP server
1451  * \param to the target XMPP server
1452  * \return IKS_OK on success, any other value on failure
1453  */
1454 static int aji_send_header(struct aji_client *client, const char *to)
1455 {
1456         char *msg;
1457         int len, err;
1458
1459         len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
1460         msg = iks_malloc(len);
1461         if (!msg)
1462                 return IKS_NOMEM;
1463         sprintf(msg, "<?xml version='1.0'?>"
1464                 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
1465                 "%s' to='%s' version='1.0'>", client->name_space, to);
1466         err = aji_send_raw(client, msg);
1467         iks_free(msg);
1468         if (err != IKS_OK)
1469                 return err;
1470
1471         return IKS_OK;
1472 }
1473
1474 /*!
1475  * \brief Wraps raw sending
1476  * \param client the configured XMPP client we use to connect to a XMPP server
1477  * \param x the XMPP packet to send
1478  * \return IKS_OK on success, any other value on failure
1479  */
1480 int ast_aji_send(struct aji_client *client, iks *x)
1481 {
1482         return aji_send_raw(client, iks_string(iks_stack(x), x));
1483 }
1484
1485 /*!
1486  * \internal
1487  * \brief Sends an XML string over an XMPP connection
1488  * \param client the configured XMPP client we use to connect to a XMPP server
1489  * \param xmlstr the XML string to send
1490  * The XML data is sent whether the connection is secured or not. In the
1491  * latter case, we just call iks_send_raw().
1492  * \return IKS_OK on success, any other value on failure
1493  */
1494 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
1495 {
1496         int ret;
1497 #ifdef HAVE_OPENSSL
1498         int len = strlen(xmlstr);
1499
1500         if (aji_is_secure(client)) {
1501                 ret = SSL_write(client->ssl_session, xmlstr, len);
1502                 if (ret) {
1503                         /* Log the message here, because iksemel's logHook is
1504                            unaccessible */
1505                         aji_log_hook(client, xmlstr, len, 0);
1506                         return IKS_OK;
1507                 }
1508         }
1509 #endif
1510         /* If needed, data will be sent unencrypted, and logHook will
1511            be called inside iks_send_raw */
1512         ret = iks_send_raw(client->p, xmlstr);
1513         if (ret != IKS_OK) {
1514                 return ret;
1515         }
1516
1517         return IKS_OK;
1518 }
1519
1520 /*!
1521  * \internal
1522  * \brief the debug loop.
1523  * \param data void
1524  * \param xmpp xml data as string
1525  * \param size size of string
1526  * \param is_incoming direction of packet 1 for inbound 0 for outbound.
1527  */
1528 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
1529 {
1530         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1531
1532         if (!ast_strlen_zero(xmpp)) {
1533                 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
1534         }
1535
1536         if (client->debug) {
1537                 if (is_incoming) {
1538                         ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
1539                 } else {
1540                         if (strlen(xmpp) == 1) {
1541                                 if (option_debug > 2  && xmpp[0] == ' ') {
1542                                         ast_verbose("\nJABBER: Keep alive packet\n");
1543                                 }
1544                         } else {
1545                                 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
1546                         }
1547                 }
1548
1549         }
1550         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1551 }
1552
1553 /*!
1554  * \internal
1555  * \brief A wrapper function for iks_start_sasl
1556  * \param client the configured XMPP client we use to connect to a XMPP server
1557  * \param type the SASL authentication type. Supported types are PLAIN and MD5
1558  * \param username
1559  * \param pass password.
1560  *
1561  * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
1562  */
1563 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
1564 {
1565         iks *x = NULL;
1566         int len;
1567         char *s;
1568         char *base64;
1569
1570         /* trigger SASL DIGEST-MD5 only over an unsecured connection.
1571            iks_start_sasl is an iksemel API function and relies on GnuTLS,
1572            whereas we use OpenSSL */
1573         if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
1574                 return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass); 
1575         if (!(type & IKS_STREAM_SASL_PLAIN)) {
1576                 ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
1577                 return IKS_NET_NOTSUPP;
1578         }
1579
1580         x = iks_new("auth"); 
1581         if (!x) {
1582                 ast_log(LOG_ERROR, "Out of memory.\n");
1583                 return IKS_NET_NOTSUPP;
1584         }
1585
1586         iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
1587         len = strlen(username) + strlen(pass) + 3;
1588         s = alloca(len);
1589         base64 = alloca((len + 2) * 4 / 3);
1590         iks_insert_attrib(x, "mechanism", "PLAIN");
1591         snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
1592
1593         /* exclude the NULL training byte from the base64 encoding operation
1594            as some XMPP servers will refuse it.
1595            The format for authentication is [authzid]\0authcid\0password
1596            not [authzid]\0authcid\0password\0 */
1597         ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
1598         iks_insert_cdata(x, base64, 0);
1599         ast_aji_send(client, x);
1600         iks_delete(x);
1601
1602         return IKS_OK;
1603 }
1604
1605 /*!
1606  * \internal
1607  * \brief The action hook parses the inbound packets, constantly running.
1608  * \param data aji client structure 
1609  * \param type type of packet 
1610  * \param node the actual packet.
1611  * \return IKS_OK or IKS_HOOK .
1612  */
1613 static int aji_act_hook(void *data, int type, iks *node)
1614 {
1615         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1616         ikspak *pak = NULL;
1617         iks *auth = NULL;
1618         int features = 0;
1619
1620         if (!node) {
1621                 ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
1622                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1623                 return IKS_HOOK;
1624         }
1625
1626         if (client->state == AJI_DISCONNECTING) {
1627                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1628                 return IKS_HOOK;
1629         }
1630
1631         pak = iks_packet(node);
1632
1633         /* work around iksemel's impossibility to recognize node names
1634          * containing a semicolon. Set the namespace of the corresponding
1635          * node accordingly. */
1636         if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
1637                 char *node_ns = NULL;
1638                 char attr[AJI_MAX_ATTRLEN];
1639                 char *node_name = iks_name(iks_child(node));
1640                 char *aux = strchr(node_name, ':') + 1;
1641                 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
1642                 node_ns = iks_find_attrib(iks_child(node), attr);
1643                 if (node_ns) {
1644                         pak->ns = node_ns;
1645                         pak->query = iks_child(node);
1646                 }
1647         }
1648
1649
1650         if (!client->component) { /*client */
1651                 switch (type) {
1652                 case IKS_NODE_START:
1653                         if (client->usetls && !aji_is_secure(client)) {
1654 #ifndef HAVE_OPENSSL
1655                                 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");
1656                                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
1657                                 return IKS_HOOK;
1658 #else
1659                                 if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
1660                                         ast_log(LOG_ERROR, "Could not start TLS\n");
1661                                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1662                                         return IKS_HOOK;
1663                                 }
1664 #endif
1665                                 break;
1666                         }
1667                         if (!client->usesasl) {
1668                                 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);
1669                                 auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
1670                                 if (auth) {
1671                                         iks_insert_attrib(auth, "id", client->mid);
1672                                         iks_insert_attrib(auth, "to", client->jid->server);
1673                                         ast_aji_increment_mid(client->mid);
1674                                         ast_aji_send(client, auth);
1675                                         iks_delete(auth);
1676                                 } else {
1677                                         ast_log(LOG_ERROR, "Out of memory.\n");
1678                                 }
1679                         }
1680                         break;
1681
1682                 case IKS_NODE_NORMAL:
1683 #ifdef HAVE_OPENSSL
1684                         if (client->stream_flags & TRY_SECURE) {
1685                                 if (!strcmp("proceed", iks_name(node))) {
1686                                         return aji_tls_handshake(client);
1687                                 }
1688                         }
1689 #endif
1690                         if (!strcmp("stream:features", iks_name(node))) {
1691                                 features = iks_stream_features(node);
1692                                 if (client->usesasl) {
1693                                         if (client->usetls && !aji_is_secure(client)) {
1694                                                 break;
1695                                         }
1696                                         if (client->authorized) {
1697                                                 if (features & IKS_STREAM_BIND) {
1698                                                         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);
1699                                                         auth = iks_make_resource_bind(client->jid);
1700                                                         if (auth) {
1701                                                                 iks_insert_attrib(auth, "id", client->mid);
1702                                                                 ast_aji_increment_mid(client->mid);
1703                                                                 ast_aji_send(client, auth);
1704                                                                 iks_delete(auth);
1705                                                         } else {
1706                                                                 ast_log(LOG_ERROR, "Out of memory.\n");
1707                                                                 break;
1708                                                         }
1709                                                 }
1710                                                 if (features & IKS_STREAM_SESSION) {
1711                                                         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);
1712                                                         auth = iks_make_session();
1713                                                         if (auth) {
1714                                                                 iks_insert_attrib(auth, "id", "auth");
1715                                                                 ast_aji_increment_mid(client->mid);
1716                                                                 ast_aji_send(client, auth);
1717                                                                 iks_delete(auth);
1718                                                         } else {
1719                                                                 ast_log(LOG_ERROR, "Out of memory.\n");
1720                                                         }
1721                                                 }
1722                                         } else {
1723                                                 int ret;
1724                                                 if (!client->jid->user) {
1725                                                         ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
1726                                                         break;
1727                                                 }
1728
1729                                                 ret = aji_start_sasl(client, features, client->jid->user, client->password);
1730                                                 if (ret != IKS_OK) {
1731                                                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1732                                                         return IKS_HOOK;
1733                                                 }
1734                                                 break;
1735                                         }
1736                                 }
1737                         } else if (!strcmp("failure", iks_name(node))) {
1738                                 ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
1739                         } else if (!strcmp("success", iks_name(node))) {
1740                                 client->authorized = 1;
1741                                 aji_send_header(client, client->jid->server);
1742                         }
1743                         break;
1744                 case IKS_NODE_ERROR:
1745                         ast_log(LOG_ERROR, "JABBER: Node Error\n");
1746                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1747                         return IKS_HOOK;
1748                         break;
1749                 case IKS_NODE_STOP:
1750                         ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1751                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1752                         return IKS_HOOK;
1753                         break;
1754                 }
1755         } else if (client->state != AJI_CONNECTED && client->component) {
1756                 switch (type) {
1757                 case IKS_NODE_START:
1758                         if (client->state == AJI_DISCONNECTED) {
1759                                 char secret[160], shasum[320], *handshake;
1760
1761                                 sprintf(secret, "%s%s", pak->id, client->password);
1762                                 ast_sha1_hash(shasum, secret);
1763                                 handshake = NULL;
1764                                 if (asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
1765                                         aji_send_raw(client, handshake);
1766                                         ast_free(handshake);
1767                                         handshake = NULL;
1768                                 }
1769                                 client->state = AJI_CONNECTING;
1770                                 if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
1771                                         client->state = AJI_CONNECTED;
1772                                 else
1773                                         ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1774                                 break;
1775                         }
1776                         break;
1777
1778                 case IKS_NODE_NORMAL:
1779                         break;
1780
1781                 case IKS_NODE_ERROR:
1782                         ast_log(LOG_ERROR, "JABBER: Node Error\n");
1783                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1784                         return IKS_HOOK;
1785
1786                 case IKS_NODE_STOP:
1787                         ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1788                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1789                         return IKS_HOOK;
1790                 }
1791         }
1792
1793         switch (pak->type) {
1794         case IKS_PAK_NONE:
1795                 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1796                 break;
1797         case IKS_PAK_MESSAGE:
1798                 aji_handle_message(client, pak);
1799                 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1800                 break;
1801         case IKS_PAK_PRESENCE:
1802                 aji_handle_presence(client, pak);
1803                 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1804                 break;
1805         case IKS_PAK_S10N:
1806                 aji_handle_subscribe(client, pak);
1807                 ast_debug(1, "JABBER: Handling paktype S10N\n");
1808                 break;
1809         case IKS_PAK_IQ:
1810                 ast_debug(1, "JABBER: Handling paktype IQ\n");
1811                 aji_handle_iq(client, node);
1812                 break;
1813         default:
1814                 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
1815                 break;
1816         }
1817
1818         iks_filter_packet(client->f, pak);
1819
1820         if (node)
1821                 iks_delete(node);
1822
1823         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1824         return IKS_OK;
1825 }
1826 /*!
1827  * \internal
1828  * \brief Unknown
1829  * \param data void
1830  * \param pak ikspak
1831  * \return IKS_FILTER_EAT.
1832 */
1833 static int aji_register_approve_handler(void *data, ikspak *pak)
1834 {
1835         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1836         iks *iq = NULL, *presence = NULL, *x = NULL;
1837
1838         iq = iks_new("iq");
1839         presence = iks_new("presence");
1840         x = iks_new("x");
1841         if (client && iq && presence && x) {
1842                 if (!iks_find(pak->query, "remove")) {
1843                         iks_insert_attrib(iq, "from", client->jid->full);
1844                         iks_insert_attrib(iq, "to", pak->from->full);
1845                         iks_insert_attrib(iq, "id", pak->id);
1846                         iks_insert_attrib(iq, "type", "result");
1847                         ast_aji_send(client, iq);
1848
1849                         iks_insert_attrib(presence, "from", client->jid->full);
1850                         iks_insert_attrib(presence, "to", pak->from->partial);
1851                         iks_insert_attrib(presence, "id", client->mid);
1852                         ast_aji_increment_mid(client->mid);
1853                         iks_insert_attrib(presence, "type", "subscribe");
1854                         iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1855                         iks_insert_node(presence, x);
1856                         ast_aji_send(client, presence);
1857                 }
1858         } else {
1859                 ast_log(LOG_ERROR, "Out of memory.\n");
1860         }
1861
1862         iks_delete(iq);
1863         iks_delete(presence);
1864         iks_delete(x);
1865
1866         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1867         return IKS_FILTER_EAT;
1868 }
1869 /*!
1870  * \internal
1871  * \brief register handler for incoming querys (IQ's)
1872  * \param data incoming aji_client request
1873  * \param pak ikspak
1874  * \return IKS_FILTER_EAT.
1875 */
1876 static int aji_register_query_handler(void *data, ikspak *pak)
1877 {
1878         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1879         struct aji_buddy *buddy = NULL;
1880         char *node = NULL;
1881         iks *iq = NULL, *query = NULL;
1882
1883         client = (struct aji_client *) data;
1884
1885         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1886         if (!buddy) {
1887                 iks  *error = NULL, *notacceptable = NULL;
1888
1889                 ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1890                 iq = iks_new("iq");
1891                 query = iks_new("query");
1892                 error = iks_new("error");
1893                 notacceptable = iks_new("not-acceptable");
1894                 if (iq && query && error && notacceptable) {
1895                         iks_insert_attrib(iq, "type", "error");
1896                         iks_insert_attrib(iq, "from", client->user);
1897                         iks_insert_attrib(iq, "to", pak->from->full);
1898                         iks_insert_attrib(iq, "id", pak->id);
1899                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1900                         iks_insert_attrib(error, "code" , "406");
1901                         iks_insert_attrib(error, "type", "modify");
1902                         iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1903                         iks_insert_node(iq, query);
1904                         iks_insert_node(iq, error);
1905                         iks_insert_node(error, notacceptable);
1906                         ast_aji_send(client, iq);
1907                 } else {
1908                         ast_log(LOG_ERROR, "Out of memory.\n");
1909                 }
1910
1911                 iks_delete(error);
1912                 iks_delete(notacceptable);
1913         } else if (!(node = iks_find_attrib(pak->query, "node"))) {
1914                 iks *instructions = NULL;
1915                 char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1916                 iq = iks_new("iq");
1917                 query = iks_new("query");
1918                 instructions = iks_new("instructions");
1919                 if (iq && query && instructions && client) {
1920                         iks_insert_attrib(iq, "from", client->user);
1921                         iks_insert_attrib(iq, "to", pak->from->full);
1922                         iks_insert_attrib(iq, "id", pak->id);
1923                         iks_insert_attrib(iq, "type", "result");
1924                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1925                         iks_insert_cdata(instructions, explain, 0);
1926                         iks_insert_node(iq, query);
1927                         iks_insert_node(query, instructions);
1928                         ast_aji_send(client, iq);
1929                 } else {
1930                         ast_log(LOG_ERROR, "Out of memory.\n");
1931                 }
1932
1933                 iks_delete(instructions);
1934         }
1935         iks_delete(iq);
1936         iks_delete(query);
1937         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
1938         ASTOBJ_UNREF(client, ast_aji_client_destroy);
1939         return IKS_FILTER_EAT;
1940 }
1941
1942 /*!
1943  * \internal
1944  * \brief Handles stuff
1945  * \param data void
1946  * \param pak ikspak
1947  * \return IKS_FILTER_EAT.
1948 */
1949 static int aji_ditems_handler(void *data, ikspak *pak)
1950 {
1951         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1952         char *node = NULL;
1953
1954         if (!(node = iks_find_attrib(pak->query, "node"))) {
1955                 iks *iq = NULL, *query = NULL, *item = NULL;
1956                 iq = iks_new("iq");
1957                 query = iks_new("query");
1958                 item = iks_new("item");
1959
1960                 if (iq && query && item) {
1961                         iks_insert_attrib(iq, "from", client->user);
1962                         iks_insert_attrib(iq, "to", pak->from->full);
1963                         iks_insert_attrib(iq, "id", pak->id);
1964                         iks_insert_attrib(iq, "type", "result");
1965                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1966                         iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1967                         iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1968                         iks_insert_attrib(item, "jid", client->user);
1969
1970                         iks_insert_node(iq, query);
1971                         iks_insert_node(query, item);
1972                         ast_aji_send(client, iq);
1973                 } else {
1974                         ast_log(LOG_ERROR, "Out of memory.\n");
1975                 }
1976
1977                 iks_delete(iq);
1978                 iks_delete(query);
1979                 iks_delete(item);
1980
1981         } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1982                 iks *iq, *query, *confirm;
1983                 iq = iks_new("iq");
1984                 query = iks_new("query");
1985                 confirm = iks_new("item");
1986                 if (iq && query && confirm && client) {
1987                         iks_insert_attrib(iq, "from", client->user);
1988                         iks_insert_attrib(iq, "to", pak->from->full);
1989                         iks_insert_attrib(iq, "id", pak->id);
1990                         iks_insert_attrib(iq, "type", "result");
1991                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1992                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1993                         iks_insert_attrib(confirm, "node", "confirmaccount");
1994                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
1995                         iks_insert_attrib(confirm, "jid", "blog.astjab.org");
1996
1997                         iks_insert_node(iq, query);
1998                         iks_insert_node(query, confirm);
1999                         ast_aji_send(client, iq);
2000                 } else {
2001                         ast_log(LOG_ERROR, "Out of memory.\n");
2002                 }
2003
2004                 iks_delete(iq);
2005                 iks_delete(query);
2006                 iks_delete(confirm);
2007
2008         } else if (!strcasecmp(node, "confirmaccount")) {
2009                 iks *iq = NULL, *query = NULL, *feature = NULL;
2010
2011                 iq = iks_new("iq");
2012                 query = iks_new("query");
2013                 feature = iks_new("feature");
2014
2015                 if (iq && query && feature && client) {
2016                         iks_insert_attrib(iq, "from", client->user);
2017                         iks_insert_attrib(iq, "to", pak->from->full);
2018                         iks_insert_attrib(iq, "id", pak->id);
2019                         iks_insert_attrib(iq, "type", "result");
2020                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2021                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2022                         iks_insert_node(iq, query);
2023                         iks_insert_node(query, feature);
2024                         ast_aji_send(client, iq);
2025                 } else {
2026                         ast_log(LOG_ERROR, "Out of memory.\n");
2027                 }
2028
2029                 iks_delete(iq);
2030                 iks_delete(query);
2031                 iks_delete(feature);
2032         }
2033
2034         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2035         return IKS_FILTER_EAT;
2036
2037 }
2038
2039 /*!
2040  * \internal
2041  * \brief Handle add extra info
2042  * \param data void
2043  * \param pak ikspak
2044  * \return IKS_FILTER_EAT
2045 */
2046 static int aji_client_info_handler(void *data, ikspak *pak)
2047 {
2048         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2049         struct aji_resource *resource = NULL;
2050         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2051
2052         resource = aji_find_resource(buddy, pak->from->resource);
2053         if (pak->subtype == IKS_TYPE_RESULT) {
2054                 if (!resource) {
2055                         ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2056                         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2057                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2058                         return IKS_FILTER_EAT;
2059                 }
2060                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2061                         resource->cap->jingle = 1;
2062                 } else {
2063                         resource->cap->jingle = 0;
2064                 }
2065         } else if (pak->subtype == IKS_TYPE_GET) {
2066                 iks *iq, *disco, *ident, *google, *query;
2067                 iq = iks_new("iq");
2068                 query = iks_new("query");
2069                 ident = iks_new("identity");
2070                 disco = iks_new("feature");
2071                 google = iks_new("feature");
2072                 if (iq && ident && disco && google) {
2073                         iks_insert_attrib(iq, "from", client->jid->full);
2074                         iks_insert_attrib(iq, "to", pak->from->full);
2075                         iks_insert_attrib(iq, "type", "result");
2076                         iks_insert_attrib(iq, "id", pak->id);
2077                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2078                         iks_insert_attrib(ident, "category", "client");
2079                         iks_insert_attrib(ident, "type", "pc");
2080                         iks_insert_attrib(ident, "name", "asterisk");
2081                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
2082                         iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
2083                         iks_insert_node(iq, query);
2084                         iks_insert_node(query, ident);
2085                         iks_insert_node(query, google);
2086                         iks_insert_node(query, disco);
2087                         ast_aji_send(client, iq);
2088                 } else {
2089                         ast_log(LOG_ERROR, "Out of Memory.\n");
2090                 }
2091
2092                 iks_delete(iq);
2093                 iks_delete(query);
2094                 iks_delete(ident);
2095                 iks_delete(google);
2096                 iks_delete(disco);
2097         } else if (pak->subtype == IKS_TYPE_ERROR) {
2098                 ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
2099         }
2100         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2101         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2102         return IKS_FILTER_EAT;
2103 }
2104
2105 /*!
2106  * \internal
2107  * \brief Handler of the return info packet
2108  * \param data aji_client
2109  * \param pak ikspak
2110  * \return IKS_FILTER_EAT
2111 */
2112 static int aji_dinfo_handler(void *data, ikspak *pak)
2113 {
2114         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2115         char *node = NULL;
2116         struct aji_resource *resource = NULL;
2117         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2118
2119         if (pak->subtype == IKS_TYPE_ERROR) {
2120                 ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
2121                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
2122                 return IKS_FILTER_EAT;
2123         }
2124         resource = aji_find_resource(buddy, pak->from->resource);
2125         if (pak->subtype == IKS_TYPE_RESULT) {
2126                 if (!resource) {
2127                         ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2128                         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2129                         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2130                         return IKS_FILTER_EAT;
2131                 }
2132                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2133                         resource->cap->jingle = 1;
2134                 } else {
2135                         resource->cap->jingle = 0;
2136                 }
2137         } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
2138                 iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
2139
2140                 iq = iks_new("iq");
2141                 query = iks_new("query");
2142                 identity = iks_new("identity");
2143                 disco = iks_new("feature");
2144                 reg = iks_new("feature");
2145                 commands = iks_new("feature");
2146                 gateway = iks_new("feature");
2147                 version = iks_new("feature");
2148                 vcard = iks_new("feature");
2149                 search = iks_new("feature");
2150                 if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
2151                         iks_insert_attrib(iq, "from", client->user);
2152                         iks_insert_attrib(iq, "to", pak->from->full);
2153                         iks_insert_attrib(iq, "id", pak->id);
2154                         iks_insert_attrib(iq, "type", "result");
2155                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2156                         iks_insert_attrib(identity, "category", "gateway");
2157                         iks_insert_attrib(identity, "type", "pstn");
2158                         iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2159                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2160                         iks_insert_attrib(reg, "var", "jabber:iq:register");
2161                         iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2162                         iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2163                         iks_insert_attrib(version, "var", "jabber:iq:version");
2164                         iks_insert_attrib(vcard, "var", "vcard-temp");
2165                         iks_insert_attrib(search, "var", "jabber:iq:search");
2166
2167                         iks_insert_node(iq, query);
2168                         iks_insert_node(query, identity);
2169                         iks_insert_node(query, disco);
2170                         iks_insert_node(query, reg);
2171                         iks_insert_node(query, commands);
2172                         iks_insert_node(query, gateway);
2173                         iks_insert_node(query, version);
2174                         iks_insert_node(query, vcard);
2175                         iks_insert_node(query, search);
2176                         ast_aji_send(client, iq);
2177                 } else {
2178                         ast_log(LOG_ERROR, "Out of memory.\n");
2179                 }
2180
2181                 iks_delete(iq);
2182                 iks_delete(query);
2183                 iks_delete(identity);
2184                 iks_delete(disco);
2185                 iks_delete(reg);
2186                 iks_delete(commands);
2187                 iks_delete(gateway);
2188                 iks_delete(version);
2189                 iks_delete(vcard);
2190                 iks_delete(search);
2191         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
2192                 iks *iq, *query, *confirm;
2193                 iq = iks_new("iq");
2194                 query = iks_new("query");
2195                 confirm = iks_new("item");
2196
2197                 if (iq && query && confirm && client) {
2198                         iks_insert_attrib(iq, "from", client->user);
2199                         iks_insert_attrib(iq, "to", pak->from->full);
2200                         iks_insert_attrib(iq, "id", pak->id);
2201                         iks_insert_attrib(iq, "type", "result");
2202                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2203                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2204                         iks_insert_attrib(confirm, "node", "confirmaccount");
2205                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
2206                         iks_insert_attrib(confirm, "jid", client->user);
2207                         iks_insert_node(iq, query);
2208                         iks_insert_node(query, confirm);
2209                         ast_aji_send(client, iq);
2210                 } else {
2211                         ast_log(LOG_ERROR, "Out of memory.\n");
2212                 }
2213
2214                 iks_delete(iq);
2215                 iks_delete(query);
2216                 iks_delete(confirm);
2217
2218         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
2219                 iks *iq, *query, *feature;
2220
2221                 iq = iks_new("iq");
2222                 query = iks_new("query");
2223                 feature = iks_new("feature");
2224
2225                 if (iq && query && feature && client) {
2226                         iks_insert_attrib(iq, "from", client->user);
2227                         iks_insert_attrib(iq, "to", pak->from->full);
2228                         iks_insert_attrib(iq, "id", pak->id);
2229                         iks_insert_attrib(iq, "type", "result");
2230                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2231                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2232                         iks_insert_node(iq, query);
2233                         iks_insert_node(query, feature);
2234                         ast_aji_send(client, iq);
2235                 } else {
2236                         ast_log(LOG_ERROR, "Out of memory.\n");
2237                 }
2238
2239                 iks_delete(iq);
2240                 iks_delete(query);
2241                 iks_delete(feature);
2242         }
2243
2244         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2245         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2246         return IKS_FILTER_EAT;
2247 }
2248
2249 /*!
2250  * \internal
2251  * \brief Handles \verbatim <iq> \endverbatim stanzas.
2252  * \param client the configured XMPP client we use to connect to a XMPP server
2253  * \param node iks
2254  * \return void.
2255  */
2256 static void aji_handle_iq(struct aji_client *client, iks *node)
2257 {
2258         /*Nothing to see here */
2259 }
2260
2261 /*!
2262  * \internal
2263  * \brief Handles \verbatim <message>\endverbatim stanzas.
2264  * Adds the incoming message to the client's message list.
2265  * \param client the configured XMPP client we use to connect to a XMPP server
2266  * \param pak ikspak the node
2267  */
2268 static void aji_handle_message(struct aji_client *client, ikspak *pak)
2269 {
2270         struct aji_message *insert;
2271         int deleted = 0;
2272         struct ast_msg *msg;
2273
2274         ast_debug(3, "client %s received a message\n", client->name);
2275
2276         if (!(insert = ast_calloc(1, sizeof(*insert)))) {
2277                 return;
2278         }
2279
2280         insert->arrived = ast_tvnow();
2281
2282         /* wake up threads waiting for messages */
2283         ast_mutex_lock(&messagelock);
2284         ast_cond_broadcast(&message_received_condition);
2285         ast_mutex_unlock(&messagelock);
2286
2287         if (iks_find_cdata(pak->x, "body")) {
2288                 insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2289         }
2290         if (pak->id) {
2291                 ast_copy_string(insert->id, pak->id, sizeof(insert->id));
2292         }
2293         if (pak->from){
2294                 /* insert will furtherly be added to message list */
2295                 insert->from = ast_strdup(pak->from->full);
2296                 if (!insert->from) {
2297                         ast_log(LOG_ERROR, "Memory allocation failure\n");
2298                         return;
2299                 }
2300                 ast_debug(3, "message comes from %s\n", insert->from);
2301         }
2302
2303         if (client->send_to_dialplan) {
2304                 if ((msg = ast_msg_alloc())) {
2305                         int res;
2306
2307                         res = ast_msg_set_to(msg, "xmpp:%s", client->user);
2308                         res |= ast_msg_set_from(msg, "xmpp:%s", insert->from);
2309                         res |= ast_msg_set_body(msg, "%s", insert->message);
2310                         res |= ast_msg_set_context(msg, "%s", client->context);
2311
2312                         if (res) {
2313                                 ast_msg_destroy(msg);
2314                         } else {
2315                                 ast_msg_queue(msg);
2316                         }
2317
2318                         msg = NULL;
2319                 }
2320         }
2321
2322         /* remove old messages received from this JID
2323          * and insert received message */
2324         deleted = delete_old_messages(client, pak->from->partial);
2325         ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2326         AST_LIST_LOCK(&client->messages);
2327         AST_LIST_INSERT_HEAD(&client->messages, insert, list);
2328         AST_LIST_UNLOCK(&client->messages);
2329 }
2330
2331 /*!
2332  * \internal
2333  * \brief handles \verbatim <presence>\endverbatim stanzas.
2334  * \param client the configured XMPP client we use to connect to a XMPP server
2335  * \param pak ikspak
2336  */
2337 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
2338 {
2339         int status, priority;
2340         struct aji_buddy *buddy;
2341         struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
2342         char *ver, *node, *descrip, *type;
2343
2344         if (client->state != AJI_CONNECTED)
2345                 aji_create_buddy(pak->from->partial, client);
2346
2347         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2348         if (!buddy && pak->from->partial) {
2349                 /* allow our jid to be used to log in with another resource */
2350                 if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
2351                         aji_create_buddy(pak->from->partial, client);
2352                 else
2353                         ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
2354                 return;
2355         }
2356         type = iks_find_attrib(pak->x, "type");
2357         if (client->component && type &&!strcasecmp("probe", type)) {
2358                 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2359                 ast_verbose("what i was looking for \n");
2360         }
2361         ASTOBJ_WRLOCK(buddy);
2362         status = (pak->show) ? pak->show : 6;
2363         priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
2364         tmp = buddy->resources;
2365         descrip = ast_strdup(iks_find_cdata(pak->x, "status"));
2366
2367         while (tmp && pak->from->resource) {
2368                 if (!strcasecmp(tmp->resource, pak->from->resource)) {
2369                         tmp->status = status;
2370                         if (tmp->description) {
2371                                 ast_free(tmp->description);
2372                         }
2373                         tmp->description = descrip;
2374                         found = tmp;
2375                         if (status == 6) {      /* Sign off Destroy resource */
2376                                 if (last && found->next) {
2377                                         last->next = found->next;
2378                                 } else if (!last) {
2379                                         if (found->next) {
2380                                                 buddy->resources = found->next;
2381                                         } else {
2382                                                 buddy->resources = NULL;
2383                                         }
2384                                 } else if (!found->next) {
2385                                         if (last) {
2386                                                 last->next = NULL;
2387                                         } else {
2388                                                 buddy->resources = NULL;
2389                                         }
2390                                 }
2391                                 ast_free(found);
2392                                 found = NULL;
2393                                 break;
2394                         }
2395                         /* resource list is sorted by descending priority */
2396                         if (tmp->priority != priority) {
2397                                 found->priority = priority;
2398                                 if (!last && !found->next) {
2399                                         /* resource was found to be unique,
2400                                            leave loop */
2401                                         break;
2402                                 }
2403                                 /* search for resource in our list
2404                                    and take it out for the moment */
2405                                 if (last) {
2406                                         last->next = found->next;
2407                                 } else {
2408                                         buddy->resources = found->next;
2409                                 }
2410
2411                                 last = NULL;
2412                                 tmp = buddy->resources;
2413                                 if (!buddy->resources) {
2414                                         buddy->resources = found;
2415                                 }
2416                                 /* priority processing */
2417                                 while (tmp) {
2418                                         /* insert resource back according to
2419                                            its priority value */
2420                                         if (found->priority > tmp->priority) {
2421                                                 if (last) {
2422                                                         /* insert within list */
2423                                                         last->next = found;
2424                                                 }
2425                                                 found->next = tmp;
2426                                                 if (!last) {
2427                                                         /* insert on top */
2428                                                         buddy->resources = found;
2429                                                 }
2430                                                 break;
2431                                         }
2432                                         if (!tmp->next) {
2433                                                 /* insert at the end of the list */
2434                                                 tmp->next = found;
2435                                                 found->next = NULL;
2436                                                 break;
2437                                         }
2438                                         last = tmp;
2439                                         tmp = tmp->next;
2440                                 }
2441                         }
2442                         break;
2443                 }
2444                 last = tmp;
2445                 tmp = tmp->next;
2446         }
2447
2448         /* resource not found in our list, create it */
2449         if (!found && status != 6 && pak->from->resource) {
2450                 found = ast_calloc(1, sizeof(*found));
2451
2452                 if (!found) {
2453                         ast_log(LOG_ERROR, "Out of memory!\n");
2454                         ASTOBJ_UNLOCK(buddy);
2455                         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2456                         return;
2457                 }
2458                 ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
2459                 found->status = status;
2460                 found->description = descrip;
2461                 found->priority = priority;
2462                 found->next = NULL;
2463                 last = NULL;
2464                 tmp = buddy->resources;
2465                 while (tmp) {
2466                         if (found->priority > tmp->priority) {
2467                                 if (last) {
2468                                         last->next = found;
2469                                 }
2470                                 found->next = tmp;
2471                                 if (!last) {
2472                                         buddy->resources = found;
2473                                 }
2474                                 break;
2475                         }
2476                         if (!tmp->next) {
2477                                 tmp->next = found;
2478                                 break;
2479                         }
2480                         last = tmp;
2481                         tmp = tmp->next;
2482                 }
2483                 if (!tmp) {
2484                         buddy->resources = found;
2485                 }
2486         }
2487
2488         ASTOBJ_UNLOCK(buddy);
2489         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2490
2491         node = iks_find_attrib(iks_find(pak->x, "c"), "node");
2492         ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
2493
2494         /* handle gmail client's special caps:c tag */
2495         if (!node && !ver) {
2496                 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
2497                 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
2498         }
2499
2500         /* retrieve capabilites of the new resource */
2501         if (status != 6 && found && !found->cap) {
2502                 found->cap = aji_find_version(node, ver, pak);
2503                 if (gtalk_yuck(pak->x)) { /* gtalk should do discover */
2504                         found->cap->jingle = 1;
2505                 }
2506                 if (found->cap->jingle) {
2507                         ast_debug(1, "Special case for google till they support discover.\n");
2508                 } else {
2509                         iks *iq, *query;
2510                         iq = iks_new("iq");
2511                         query = iks_new("query");
2512                         if (query && iq) {
2513                                 iks_insert_attrib(iq, "type", "get");
2514                                 iks_insert_attrib(iq, "to", pak->from->full);
2515                                 iks_insert_attrib(iq, "from", client->jid->full);
2516                                 iks_insert_attrib(iq, "id", client->mid);
2517                                 ast_aji_increment_mid(client->mid);
2518                                 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2519                                 iks_insert_node(iq, query);
2520                                 ast_aji_send(client, iq);
2521                         } else {
2522                                 ast_log(LOG_ERROR, "Out of memory.\n");
2523                         }
2524                         iks_delete(query);
2525                         iks_delete(iq);
2526                 }
2527         }
2528         switch (pak->subtype) {
2529         case IKS_TYPE_AVAILABLE:
2530                 ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
2531                 break;
2532         case IKS_TYPE_UNAVAILABLE:
2533                 ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
2534                 break;
2535         default:
2536                 ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
2537         }
2538         switch (pak->show) {
2539         case IKS_SHOW_UNAVAILABLE:
2540                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2541                 break;
2542         case IKS_SHOW_AVAILABLE:
2543                 ast_debug(3, "JABBER: type is available\n");
2544                 break;
2545         case IKS_SHOW_CHAT:
2546                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2547                 break;
2548         case IKS_SHOW_AWAY:
2549                 ast_debug(3, "JABBER: type is away\n");
2550                 break;
2551         case IKS_SHOW_XA:
2552                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2553                 break;
2554         case IKS_SHOW_DND:
2555                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2556                 break;
2557         default:
2558                 ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
2559         }
2560
2561         if (found) {
2562                 manager_event(EVENT_FLAG_USER, "JabberStatus",
2563                         "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
2564                         "\r\nDescription: %s\r\n",
2565                         client->name, pak->from->partial, found->resource, found->status,
2566                         found->priority, S_OR(found->description, ""));
2567         } else {
2568                 manager_event(EVENT_FLAG_USER, "JabberStatus",
2569                         "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
2570                         client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
2571         }
2572 }
2573
2574 /*!
2575  * \internal
2576  * \brief handles subscription requests.
2577  * \param client the configured XMPP client we use to connect to a XMPP server
2578  * \param pak ikspak iksemel packet.
2579  * \return void.
2580  */
2581 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
2582 {
2583         iks *presence = NULL, *status = NULL;
2584         struct aji_buddy* buddy = NULL;
2585
2586         switch (pak->subtype) {
2587         case IKS_TYPE_SUBSCRIBE:
2588                 if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
2589                         presence = iks_new("presence");
2590                         status = iks_new("status");
2591                         if (presence && status) {
2592                                 iks_insert_attrib(presence, "type", "subscribed");
2593                                 iks_insert_attrib(presence, "to", pak->from->full);
2594                                 iks_insert_attrib(presence, "from", client->jid->full);
2595                                 if (pak->id)
2596                                         iks_insert_attrib(presence, "id", pak->id);
2597                                 iks_insert_cdata(status, "Asterisk has approved subscription", 0);
2598                                 iks_insert_node(presence, status);
2599                                 ast_aji_send(client, presence);
2600                         } else {
2601                                 ast_log(LOG_ERROR, "Unable to allocate nodes\n");
2602                         }
2603
2604                         iks_delete(presence);
2605                         iks_delete(status);
2606                 }
2607
2608                 if (client->component)
2609                         aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2610         case IKS_TYPE_SUBSCRIBED:
2611                 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2612                 if (!buddy && pak->from->partial) {
2613                         aji_create_buddy(pak->from->partial, client);
2614                 } else if (buddy) {
2615                         ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
2616                 }
2617         default:
2618                 ast_verb(5, "JABBER: This is a subcription of type %i\n", pak->subtype);
2619         }
2620 }
2621
2622 /*!
2623  * \brief sends messages.
2624  * \param client the configured XMPP client we use to connect to a XMPP server
2625  * \param address
2626  * \param message
2627  * \retval IKS_OK success
2628  * \retval -1 failure
2629  */
2630 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
2631 {
2632         return aji_send_raw_chat(client, 0, NULL, address, message);
2633 }
2634
2635 /*!
2636 * \brief sends message to a groupchat
2637 * Prior to sending messages to a groupchat, one must be connected to it.
2638 * \param client the configured XMPP client we use to connect to a XMPP server
2639 * \param nick the nickname we use in the chatroom
2640 * \param address the user the messages must be sent to
2641 * \param message the message to send
2642 * \return IKS_OK on success, any other value on failure
2643 */
2644 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
2645         return aji_send_raw_chat(client, 1, nick, address, message);
2646 }
2647
2648 /*!
2649 * \brief sends messages.
2650 * \param client the configured XMPP client we use to connect to a XMPP server
2651 * \param groupchat 
2652 * \param nick the nickname we use in chatrooms
2653 * \param address
2654 * \param message
2655 * \return IKS_OK on success, any other value on failure
2656 */
2657 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
2658 {
2659         int res = 0;
2660         iks *message_packet = NULL;
2661         char from[AJI_MAX_JIDLEN];
2662         /* the nickname is used only in component mode */
2663         if (nick && client->component) {
2664                 snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
2665         } else {
2666                 snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
2667         }
2668
2669         if (client->state != AJI_CONNECTED) {
2670                 ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
2671                 return -1;
2672         }
2673
2674         message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
2675         if (!message_packet) {
2676                 ast_log(LOG_ERROR, "Out of memory.\n");
2677                 return -1;
2678         }
2679         iks_insert_attrib(message_packet, "from", from);
2680         res = ast_aji_send(client, message_packet);
2681         iks_delete(message_packet);
2682
2683         return res;
2684 }
2685
2686 /*!
2687  * \brief create a chatroom.
2688  * \param client the configured XMPP client we use to connect to a XMPP server
2689  * \param room name of room
2690  * \param server name of server
2691  * \param topic topic for the room.
2692  * \return 0.
2693  */
2694 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
2695 {
2696         int res = 0;
2697         iks *iq = NULL;
2698         iq = iks_new("iq");
2699
2700         if (iq && client) {
2701                 iks_insert_attrib(iq, "type", "get");
2702                 iks_insert_attrib(iq, "to", server);
2703                 iks_insert_attrib(iq, "id", client->mid);
2704                 ast_aji_increment_mid(client->mid);
2705                 ast_aji_send(client, iq);
2706         } else {
2707                 ast_log(LOG_ERROR, "Out of memory.\n");
2708         }
2709
2710         iks_delete(iq);
2711
2712         return res;
2713 }
2714
2715 /*!
2716  * \brief join a chatroom.
2717  * \param client the configured XMPP client we use to connect to a XMPP server
2718  * \param room room to join
2719  * \param nick the nickname to use in this room
2720  * \return IKS_OK on success, any other value on failure.
2721  */
2722 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
2723 {
2724         return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
2725 }
2726
2727 /*!
2728  * \brief leave a chatroom.
2729  * \param client the configured XMPP client we use to connect to a XMPP server
2730  * \param room room to leave
2731  * \param nick the nickname used in this room
2732  * \return IKS_OK on success, any other value on failure.
2733  */
2734 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
2735 {
2736         return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
2737 }
2738 /*!
2739  * \brief invite to a chatroom.
2740  * \param client the configured XMPP client we use to connect to a XMPP server
2741  * \param user
2742  * \param room
2743  * \param message
2744  * \return res.
2745  */
2746 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
2747 {
2748         int res = 0;
2749         iks *invite, *body, *namespace;
2750
2751         invite = iks_new("message");
2752         body = iks_new("body");
2753         namespace = iks_new("x");
2754         if (client && invite && body && namespace) {
2755                 iks_insert_attrib(invite, "to", user);
2756                 iks_insert_attrib(invite, "id", client->mid);
2757                 ast_aji_increment_mid(client->mid);
2758                 iks_insert_cdata(body, message, 0);
2759                 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
2760                 iks_insert_attrib(namespace, "jid", room);
2761                 iks_insert_node(invite, body);
2762                 iks_insert_node(invite, namespace);
2763                 res = ast_aji_send(client, invite);
2764         } else {
2765                 ast_log(LOG_ERROR, "Out of memory.\n");
2766         }
2767
2768         iks_delete(body);
2769         iks_delete(namespace);
2770         iks_delete(invite);
2771
2772         return res;
2773 }
2774
2775 /*!
2776  * \internal
2777  * \brief receive message loop.
2778  * \param data void
2779  * \return void.
2780  */
2781 static void *aji_recv_loop(void *data)
2782 {
2783         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2784         int res = IKS_HOOK;
2785
2786         while (res != IKS_OK) {
2787                 ast_debug(3, "JABBER: Connecting.\n");
2788                 res = aji_reconnect(client);
2789                 sleep(4);
2790         }
2791
2792         do {
2793                 if (res == IKS_NET_RWERR || client->timeout == 0) {
2794                         while (res != IKS_OK) {
2795                                 ast_debug(3, "JABBER: reconnecting.\n");
2796                                 res = aji_reconnect(client);
2797                                 sleep(4);
2798                         }
2799                 }
2800
2801                 res = aji_recv(client, 1);
2802
2803                 if (client->state == AJI_DISCONNECTING) {
2804                         ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
2805                         pthread_exit(NULL);
2806                 }
2807
2808                 /* Decrease timeout if no data received, and delete
2809                  * old messages globally */
2810                 if (res == IKS_NET_EXPIRED) {
2811                         client->timeout--;
2812                         delete_old_messages_all(client);
2813                 }
2814                 if (res == IKS_HOOK) {
2815                         ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
2816                 } else if (res == IKS_NET_TLSFAIL) {
2817                         ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
2818                 } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
2819                         res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
2820                         if (res == IKS_OK) {
2821                                 client->timeout = 50;
2822                         } else {
2823                                 ast_log(LOG_WARNING, "JABBER:  Network Timeout\n");
2824                         }
2825                 } else if (res == IKS_NET_RWERR) {
2826                         ast_log(LOG_WARNING, "JABBER: socket read error\n");
2827                 }
2828         } while (client);
2829         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2830         return 0;
2831 }
2832
2833 /*!
2834  * \brief increments the mid field for messages and other events.
2835  * \param mid char.
2836  * \return void.
2837  */
2838 void ast_aji_increment_mid(char *mid)
2839 {
2840         int i = 0;
2841
2842         for (i = strlen(mid) - 1; i >= 0; i--) {
2843                 if (mid[i] != 'z') {
2844                         mid[i] = mid[i] + 1;
2845                         i = 0;
2846                 } else
2847                         mid[i] = 'a';
2848         }
2849 }
2850
2851 #if 0
2852 /*!
2853  * \brief attempts to register to a transport.
2854  * \param aji_client struct, and xml packet.
2855  * \return IKS_FILTER_EAT.
2856  */
2857 /*allows for registering to transport , was too sketch and is out for now. */
2858 static int aji_register_transport(void *data, ikspak *pak)
2859 {
2860         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2861         int res = 0;
2862         struct aji_buddy *buddy = NULL;
2863         iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
2864
2865         if (client && send) {
2866                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2867                         ASTOBJ_RDLOCK(iterator); 
2868                         if (iterator->btype == AJI_TRANS) {
2869                                   buddy = iterator;
2870                         }
2871                         ASTOBJ_UNLOCK(iterator);
2872                 });
2873                 iks_filter_remove_hook(client->f, aji_register_transport);
2874                 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);
2875                 iks_insert_attrib(send, "to", buddy->host);
2876                 iks_insert_attrib(send, "id", client->mid);
2877                 ast_aji_increment_mid(client->mid);
2878                 iks_insert_attrib(send, "from", client->user);
2879                 res = ast_aji_send(client, send);
2880         } else 
2881                 ast_log(LOG_ERROR, "Out of memory.\n");
2882
2883         if (send)
2884                 iks_delete(send);
2885         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2886         return IKS_FILTER_EAT;
2887
2888 }
2889 /*!
2890  * \brief attempts to register to a transport step 2.
2891  * \param aji_client struct, and xml packet.
2892  * \return IKS_FILTER_EAT.
2893  */
2894 /* more of the same blob of code, too wonky for now*/
2895 static int aji_register_transport2(void *data, ikspak *pak)
2896 {
2897         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2898         int res = 0;
2899         struct aji_buddy *buddy = NULL;
2900
2901         iks *regiq = iks_new("iq");
2902         iks *regquery = iks_new("query");
2903         iks *reguser = iks_new("username");
2904         iks *regpass = iks_new("password");
2905
2906         if (client && regquery && reguser && regpass && regiq) {
2907                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2908                         ASTOBJ_RDLOCK(iterator);
2909                         if (iterator->btype == AJI_TRANS)
2910                                 buddy = iterator; ASTOBJ_UNLOCK(iterator);
2911                 });
2912                 iks_filter_remove_hook(client->f, aji_register_transport2);
2913                 iks_insert_attrib(regiq, "to", buddy->host);
2914                 iks_insert_attrib(regiq, "type", "set");
2915                 iks_insert_attrib(regiq, "id", client->mid);
2916                 ast_aji_increment_mid(client->mid);
2917                 iks_insert_attrib(regiq, "from", client->user);
2918                 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2919                 iks_insert_cdata(reguser, buddy->user, 0);
2920                 iks_insert_cdata(regpass, buddy->pass, 0);
2921                 iks_insert_node(regiq, regquery);
2922                 iks_insert_node(regquery, reguser);
2923                 iks_insert_node(regquery, regpass);
2924                 res = ast_aji_send(client, regiq);
2925         } else
2926                 ast_log(LOG_ERROR, "Out of memory.\n");
2927         if (regiq)
2928                 iks_delete(regiq);
2929         if (regquery)
2930                 iks_delete(regquery);
2931         if (reguser)
2932                 iks_delete(reguser);
2933         if (regpass)
2934                 iks_delete(regpass);
2935         ASTOBJ_UNREF(client, ast_aji_client_destroy);
2936         return IKS_FILTER_EAT;
2937 }
2938 #endif
2939
2940 /*!
2941  * \internal
2942  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2943  * \param client the configured XMPP client we use to connect to a XMPP server
2944  * \return void.
2945  * \note The messages here should be configurable.
2946  */
2947 static void aji_pruneregister(struct aji_client *client)
2948 {
2949         iks *removeiq = iks_new("iq");
2950         iks *removequery = iks_new("query");
2951         iks *removeitem = iks_new("item");
2952         iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2953         if (!client || !removeiq || !removequery || !removeitem || !send) {
2954                 ast_log(LOG_ERROR, "Out of memory.\n");
2955                 goto safeout;
2956         }
2957
2958         iks_insert_node(removeiq, removequery);
2959         iks_insert_node(removequery, removeitem);
2960         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2961                 ASTOBJ_RDLOCK(iterator);
2962                 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2963                  * be called at the same time */
2964                 if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
2965                         ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2966                                                                  "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2967                                                                  " so I am no longer subscribing to your presence.\n"));
2968                         ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2969                                                                  "GoodBye.  You are no longer in the Asterisk config file so I am removing"
2970                                                                  " your access to my presence.\n"));
2971                         iks_insert_attrib(removeiq, "from", client->jid->full);
2972                         iks_insert_attrib(removeiq, "type", "set");
2973                         iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2974                         iks_insert_attrib(removeitem, "jid", iterator->name);
2975                         iks_insert_attrib(removeitem, "subscription", "remove");
2976                         ast_aji_send(client, removeiq);
2977                 } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2978                         ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
2979                                                                  "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2980                         ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2981                 }
2982                 ASTOBJ_UNLOCK(iterator);
2983         });
2984
2985  safeout:
2986         iks_delete(removeiq);
2987         iks_delete(removequery);
2988         iks_delete(removeitem);
2989         iks_delete(send);
2990
2991         ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, ast_aji_buddy_destroy);
2992 }
2993
2994 /*!
2995  * \internal
2996  * \brief filters the roster packet we get back from server.
2997  * \param data void
2998  * \param pak ikspak iksemel packet.
2999  * \return IKS_FILTER_EAT.
3000  */
3001 static int aji_filter_roster(void *data, ikspak *pak)
3002 {
3003         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3004         int flag = 0;
3005         iks *x = NULL;
3006         struct aji_buddy *buddy;
3007
3008         client->state = AJI_CONNECTED;
3009         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3010                 ASTOBJ_RDLOCK(iterator);
3011                 x = iks_child(pak->query);
3012                 flag = 0;
3013                 while (x) {
3014                         if (!iks_strcmp(iks_name(x), "item")) {
3015                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
3016                                         flag = 1;
3017                                         ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
3018                                 }
3019                         }
3020                         x = iks_next(x);
3021                 }
3022                 if (!flag) {
3023                         ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
3024                 }
3025                 iks_delete(x);
3026
3027                 ASTOBJ_UNLOCK(iterator);
3028         });
3029
3030         x = iks_child(pak->query);
3031         while (x) {
3032                 flag = 0;
3033                 if (iks_strcmp(iks_name(x), "item") == 0) {
3034                         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3035                                 ASTOBJ_RDLOCK(iterator);
3036                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
3037                                         flag = 1;
3038                                 ASTOBJ_UNLOCK(iterator);
3039                         });
3040
3041                         if (flag) {
3042                                 /* found buddy, don't create a new one */
3043                                 x = iks_next(x);
3044                                 continue;
3045                         }
3046
3047                         buddy = ast_calloc(1, sizeof(*buddy));
3048                         if (!buddy) {
3049                                 ast_log(LOG_WARNING, "Out of memory\n");
3050                                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3051                                 return 0;
3052                         }
3053                         ASTOBJ_INIT(buddy);
3054                         ASTOBJ_WRLOCK(buddy);
3055                         ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
3056                         ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
3057                         if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
3058                                 ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
3059                                 ASTOBJ_MARK(buddy);
3060                         } else if (ast_test_flag(&client->flags, AJI_AUTOREGISTER)) {
3061                                 if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
3062                                         /* subscribe to buddy's presence only
3063                                            if we really need to */
3064                                         ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
3065                                 }
3066                         }
3067                         ASTOBJ_UNLOCK(buddy);
3068                         if (buddy) {
3069                                 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
3070                                 ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
3071                         }
3072                 }
3073                 x = iks_next(x);
3074         }
3075
3076         iks_delete(x);
3077         aji_pruneregister(client);
3078
3079         ASTOBJ_UNREF(client, ast_aji_client_destroy);
3080         return IKS_FILTER_EAT;
3081 }
3082
3083 /*!
3084  * \internal
3085  * \brief reconnect to jabber server
3086  * \param client the configured XMPP client we use to connect to a XMPP server
3087  * \return res.
3088 */
3089 static int aji_reconnect(struct aji_client *client)
3090 {
3091         int res = 0;
3092
3093         if (client->state) {
3094                 client->state = AJI_DISCONNECTED;
3095         }
3096         client->timeout = 50;
3097         if (client->p) {
3098                 iks_parser_reset(client->p);
3099         }
3100         if (client->authorized) {
3101                 client->authorized = 0;
3102         }
3103
3104         res = aji_initialize(client);
3105
3106         return res;
3107 }
3108
3109 /*!
3110  * \internal
3111  * \brief Get the roster of jabber users
3112  * \param client the configured XMPP client we use to connect to a XMPP server
3113  * \return 1.
3114 */
3115 static int aji_get_roster(struct aji_client *client)
3116 {
3117         iks *roster = NULL;
3118         roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
3119
3120         if (roster) {
3121                 iks_insert_attrib(roster, "id", "roster");
3122                 aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
3123                 ast_aji_send(client, roster);
3124         }
3125
3126         iks_delete(roster);
3127
3128         return 1;
3129 }
3130
3131 /*!
3132  * \internal
3133  * \brief connects as a client to jabber server.
3134  * \param data void
3135  * \param pak ikspak iksemel packet
3136  * \return res.
3137  */
3138 static int aji_client_connect(void *data, ikspak *pak)
3139 {
3140         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
3141         int res = IKS_FILTER_PASS;
3142
3143         if (client) {
3144                 if (client->state == AJI_DISCONNECTED) {
3145                         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);
3146                         client->state = AJI_CONNECTING;
3147                         client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
3148                         if (!client->component) { /*client*/
3149                                 aji_get_roster(client);
3150                         }
3151                         if (client->distribute_events) {
3152                                 aji_init_event_distribution(client);
3153                         }
3154
3155                         iks_filter_remove_hook(client->f, aji_client_connect);
3156                         /* Once we remove the hook for this routine, we must return EAT or we will crash or corrupt memory */
3157                         res = IKS_FILTER_EAT;
3158                 }
3159         } else {
3160                 ast_log(LOG_ERROR, "Out of memory.\n");
3161         }
3162
3163         ASTOBJ_UNREF(client, ast_aji_client_destroy);
3164         return res;
3165 }
3166
3167 /*!
3168  * \internal
3169  * \brief prepares client for connect.
3170  * \param client the configured XMPP client we use to connect to a XMPP server
3171  * \return 1.
3172  */
3173 static int aji_initialize(struct aji_client *client)
3174 {
3175         int connected = IKS_NET_NOCONN;
3176
3177 #ifdef HAVE_OPENSSL
3178         /* reset stream flags */
3179         client->stream_flags = 0;
3180 #endif
3181         /* If it's a component, connect to user, otherwise, connect to server */
3182         connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
3183
3184         if (connected == IKS_NET_NOCONN) {
3185                 ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
3186                 return IKS_HOOK;
3187         } else if (connected == IKS_NET_NODNS) {
3188                 ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to  %s\n", client->name,
3189                         S_OR(client->serverhost, client->jid->server));
3190                 return IKS_HOOK;
3191         }
3192
3193         return IKS_OK;
3194 }
3195
3196 /*!
3197  * \brief disconnect from jabber server.
3198  * \param client the configured XMPP client we use to connect to a XMPP server
3199  * \return 1.
3200  */
3201 int ast_aji_disconnect(struct aji_client *client)
3202 {
3203         if (client) {
3204                 ast_verb(4, "JABBER: Disconnecting\n");
3205 #ifdef HAVE_OPENSSL
3206                 if (client->stream_flags & SECURE) {
3207                         SSL_shutdown(client->ssl_session);
3208                         SSL_CTX_free(client->ssl_context);
3209                         SSL_free(client->ssl_session);
3210                 }
3211 #endif
3212                 iks_disconnect(client->p);
3213                 iks_parser_delete(client->p);
3214                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3215         }
3216
3217         return 1;
3218 }
3219
3220 /*!
3221  * \brief Callback function for MWI events
3222  * \param ast_event
3223  * \param data void pointer to ast_client structure
3224  * \return void
3225  */
3226 static void aji_mwi_cb(const struct ast_event *ast_event, void *data)
3227 {
3228         const char *mailbox;
3229         const char *context;
3230         char oldmsgs[10];
3231         char newmsgs[10];
3232         struct aji_client *client;
3233         if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
3234         {
3235                 /* If the event didn't originate from this server, don't send it back out. */
3236                 ast_debug(1, "Returning here\n");
3237                 return;
3238         }
3239
3240         client = ASTOBJ_REF((struct aji_client *) data);
3241         mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
3242         context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
3243         snprintf(oldmsgs, sizeof(oldmsgs), "%d",
3244                 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
3245         snprintf(newmsgs, sizeof(newmsgs), "%d",
3246                 ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
3247         aji_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
3248         ASTOBJ_UNREF(client, ast_aji_client_destroy);
3249
3250 }
3251 /*!
3252  * \brief Callback function for device state events
3253  * \param ast_event
3254  * \param data void pointer to ast_client structure
3255  * \return void
3256  */
3257 static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
3258 {
3259         const char *device;
3260         const char *device_state;
3261         struct aji_client *client;
3262         if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
3263         {
3264                 /* If the event didn't originate from this server, don't send it back out. */
3265                 ast_debug(1, "Returning here\n");
3266                 return;
3267         }
3268
3269         client = ASTOBJ_REF((struct aji_client *) data);
3270         device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
3271         device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
3272         aji_publish_device_state(client, device, device_state);
3273         ASTOBJ_UNREF(client, ast_aji_client_destroy);
3274 }
3275
3276 /*!
3277  * \brief Initialize collections for event distribution
3278  * \param client the configured XMPP client we use to connect to a XMPP server
3279  * \return void
3280  */
3281 static void aji_init_event_distribution(struct aji_client *client)
3282 {
3283         if (!mwi_sub) {
3284                 mwi_sub = ast_event_subscribe(AST_EVENT_MWI, aji_mwi_cb, "aji_mwi_subscription",
3285                         client, AST_EVENT_IE_END);
3286         }
3287         if (!device_state_sub) {
3288                 if (ast_enable_distributed_devstate()) {
3289                         return;
3290                 }
3291                 device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
3292                         aji_devstate_cb, "aji_devstate_subscription", client, AST_EVENT_IE_END);
3293                 ast_event_dump_cache(device_state_sub);
3294         }
3295
3296         aji_pubsub_subscribe(client, "device_state");
3297         aji_pubsub_subscribe(client, "message_waiting");
3298         iks_filter_add_rule(client->f, aji_handle_pubsub_event, client, IKS_RULE_TYPE,
3299                 IKS_PAK_MESSAGE, IKS_RULE_FROM, client->pubsub_node, IKS_RULE_DONE);
3300         iks_filter_add_rule(client->f, aji_handle_pubsub_error, client, IKS_RULE_TYPE,
3301                 IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
3302
3303 }
3304
3305 /*!
3306  * \brief Callback for handling PubSub events
3307  * \param data void pointer to aji_client structure
3308  * \return IKS_FILTER_EAT
3309  */
3310 static int aji_handle_pubsub_event(void *data, ikspak *pak)
3311 {
3312         char *item_id, *device_state, *context;
3313         int oldmsgs, newmsgs;
3314         iks *item, *item_content;
3315         struct ast_eid pubsub_eid;
3316         struct ast_event *event;
3317         item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
3318         if (!item) {
3319                 ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
3320                 return IKS_FILTER_EAT;
3321         }
3322         item_id = iks_find_attrib(item, "id");
3323         item_content = iks_child(item);
3324         ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
3325         if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
3326                 ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
3327                 return IKS_FILTER_EAT;
3328         }
3329         if (!strcasecmp(iks_name(item_content), "state")) {
3330                 device_state = iks_find_cdata(item, "state");
3331                 if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
3332                         AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
3333                         AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
3334                         AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
3335                         AST_EVENT_IE_END))) {
3336                         return IKS_FILTER_EAT;
3337                 }
3338         } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
3339                 context = strsep(&item_id, "@");
3340                 sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
3341                 sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
3342                 if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
3343                         AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
3344                         AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
3345                         AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
3346                         AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
3347                         &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
3348                         return IKS_FILTER_EAT;
3349                 }
3350         } else {
3351                 ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
3352                         iks_name(item_content));
3353                 return IKS_FILTER_EAT;
3354         }
3355         ast_event_queue_and_cache(event);
3356         return IKS_FILTER_EAT;
3357 }
3358
3359 /*!
3360  * \brief Add Owner affiliations for pubsub node
3361  * \param client the configured XMPP client we use to connect to a XMPP server
3362  * \param node the name of the node to which to add affiliations
3363  * \return void
3364  */
3365 static void aji_create_affiliations(struct aji_client *client, const char *node)
3366 {
3367         iks *modify_affiliates = aji_pubsub_iq_create(client, "set");
3368         iks *pubsub, *affiliations, *affiliate;
3369         pubsub = iks_insert(modify_affiliates, "pubsub");
3370         iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
3371         affiliations = iks_insert(pubsub, "affiliations");
3372         iks_insert_attrib(affiliations, "node", node);
3373         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3374                 ASTOBJ_RDLOCK(iterator);
3375                 affiliate = iks_insert(affiliations, "affiliation");
3376                 iks_insert_attrib(affiliate, "jid", iterator->name);
3377                 iks_insert_attrib(affiliate, "affiliation", "owner");
3378                 ASTOBJ_UNLOCK(iterator);
3379         });
3380         ast_aji_send(client, modify_affiliates);
3381         iks_delete(modify_affiliates);
3382 }
3383
3384 /*!
3385  * \brief Subscribe to a PubSub node
3386  * \param client the configured XMPP client we use to connect to a XMPP server
3387  * \param node the name of the node to which to subscribe
3388  * \return void
3389  */
3390 static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
3391 {
3392         iks *request = aji_pubsub_iq_create(client, "set");
3393         iks *pubsub, *subscribe;
3394
3395         pubsub = iks_insert(request, "pubsub");
3396         iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3397         subscribe = iks_insert(pubsub, "subscribe");
3398         iks_insert_attrib(subscribe, "jid", client->jid->partial);
3399         iks_insert_attrib(subscribe, "node", node);
3400         if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3401                 iks *options, *x, *sub_options, *sub_type, *sub_depth;
3402                 options = iks_insert(pubsub, "options");
3403                 x = iks_insert(options, "x");
3404                 iks_insert_attrib(x, "xmlns", "jabber:x:data");
3405                 iks_insert_attrib(x, "type", "submit");
3406                 sub_options = iks_insert(x, "field");
3407                 iks_insert_attrib(sub_options, "var", "FORM_TYPE");
3408                 iks_insert_attrib(sub_options, "type", "hidden");
3409                 iks_insert_cdata(iks_insert(sub_options, "value"),
3410                         "http://jabber.org/protocol/pubsub#subscribe_options", 51);
3411                 sub_type = iks_insert(x, "field");
3412                 iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
3413                 iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
3414                 sub_depth = iks_insert(x, "field");
3415                 iks_insert_attrib(sub_type, "var", "pubsub#subscription_depth");
3416                 iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
3417         }
3418         ast_aji_send(client, request);
3419         iks_delete(request);
3420 }
3421
3422 /*!
3423  * \brief Build the skeleton of a publish
3424  * \param client the configured XMPP client we use to connect to a XMPP server
3425  * \param node Name of the node that will be published to
3426  * \param event_type
3427  * \return iks *
3428  */
3429 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
3430         const char *event_type)
3431 {
3432         iks *request = aji_pubsub_iq_create(client, "set");
3433         iks *pubsub, *publish, *item;
3434         pubsub = iks_insert(request, "pubsub");
3435         iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
3436         publish = iks_insert(pubsub, "publish");
3437         if (ast_test_flag(&globalflags, AJI_XEP0248)) {
3438                 iks_insert_attrib(publish, "node", node);
3439         } else {
3440                 iks_insert_attrib(publish, "node", event_type);
3441         }
3442         item = iks_insert(publish, "item");
3443         iks_insert_attrib(item, "id", node);
3444         return item;
3445
3446 }
3447
3448 /*!
3449  * \brief Publish device state to a PubSub node
3450  * \param client the configured XMPP client we use to connect to a XMPP server
3451  * \param device the name of the device whose state to publish
3452  * \param device_state the state to publish
3453  * \return void
3454  */
3455 static void aji_publish_device_state(struct aji_client *client, const char *device,
3456         const char *device_state)
3457 {
3458         iks *request = aji_build_publish_skeleton(client, device, "device_state");
3459         iks *state;
3460         char eid_str[20];
3461         if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
3462                 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3463                         aji_create_pubsub_node(client, "leaf", device, "device_state");
3464                 } else {
3465                         aji_create_pubsub_node(client, NULL, device, NULL);
3466                 }
3467         }
3468         ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3469         state = iks_insert(request, "state");
3470         iks_insert_attrib(state, "xmlns", "http://asterisk.org");
3471         iks_insert_attrib(state, "eid", eid_str);
3472         iks_insert_cdata(state, device_state, strlen(device_state));
3473         ast_aji_send(client, iks_root(request));
3474         iks_delete(request);
3475 }
3476
3477 /*!
3478  * \brief Publish MWI to a PubSub node
3479  * \param client the configured XMPP client we use to connect to a XMPP server
3480  * \param device the name of the device whose state to publish
3481  * \param device_state the state to publish
3482  * \return void
3483  */
3484 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
3485         const char *context, const char *oldmsgs, const char *newmsgs)
3486 {
3487         char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT];
3488         char eid_str[20];
3489         iks *mailbox_node, *request;
3490         snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
3491         request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting");
3492         ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
3493         mailbox_node = iks_insert(request, "mailbox");
3494         iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
3495         iks_insert_attrib(mailbox_node, "eid", eid_str);
3496         iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
3497         iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
3498         ast_aji_send(client, iks_root(request));
3499         iks_delete(request);
3500 }
3501
3502 /*!
3503  * \brief Create an IQ packet
3504  * \param client the configured XMPP client we use to connect to a XMPP server
3505  * \param type the type of IQ packet to create
3506  * \return iks*
3507  */
3508 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type)
3509 {
3510         iks *request = iks_new("iq");
3511
3512         iks_insert_attrib(request, "to", client->pubsub_node);
3513         iks_insert_attrib(request, "from", client->jid->full);
3514         iks_insert_attrib(request, "type", type);
3515         ast_aji_increment_mid(client->mid);
3516         iks_insert_attrib(request, "id", client->mid);
3517         return request;
3518 }
3519
3520 static int aji_handle_pubsub_error(void *data, ikspak *pak)
3521 {
3522         char *node_name;
3523         char *error;
3524         int error_num;
3525         iks *orig_request;
3526         iks *orig_pubsub = iks_find(pak->x, "pubsub");
3527         struct aji_client *client;
3528         if (!orig_pubsub) {
3529                 ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
3530                 return IKS_FILTER_EAT;
3531         }
3532         orig_request = iks_child(orig_pubsub);
3533         error = iks_find_attrib(iks_find(pak->x, "error"), "code");
3534         node_name = iks_find_attrib(orig_request, "node");
3535         if (!sscanf(error, "%30d", &error_num)) {
3536                 return IKS_FILTER_EAT;
3537         }
3538         if (error_num > 399 && error_num < 500 && error_num != 404) {
3539                 ast_log(LOG_ERROR,
3540                         "Error performing operation on PubSub node %s, %s.\n", node_name, error);
3541                 return IKS_FILTER_EAT;
3542         } else if (error_num > 499 && error_num < 600) {
3543                 ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
3544                 return IKS_FILTER_EAT;
3545         }
3546
3547         client = ASTOBJ_REF((struct aji_client *) data);
3548
3549         if (!strcasecmp(iks_name(orig_request), "publish")) {
3550                 iks *request;
3551                 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3552                         if (iks_find(iks_find(orig_request, "item"), "state")) {
3553                                 aji_create_pubsub_leaf(client, "device_state", node_name);
3554                         } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
3555                                 aji_create_pubsub_leaf(client, "message_waiting", node_name);
3556                         }
3557                 } else {
3558                         aji_create_pubsub_node(client, NULL, node_name, NULL);
3559                 }
3560                 request = aji_pubsub_iq_create(client, "set");
3561                 iks_insert_node(request, orig_pubsub);
3562                 ast_aji_send(client, request);
3563                 iks_delete(request);
3564                 ASTOBJ_UNREF(client, ast_aji_client_destroy);
3565                 return IKS_FILTER_EAT;
3566         } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
3567                 if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
3568            &n