Merged revisions 328247 via svnmerge from
[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 void aji_buddy_destroy(struct aji_buddy *obj);
291 static void aji_client_destroy(struct aji_client *obj);
292 static int aji_is_secure(struct aji_client *client);
293 #ifdef HAVE_OPENSSL
294 static int aji_start_tls(struct aji_client *client);
295 static int aji_tls_handshake(struct aji_client *client);
296 #endif
297 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
298 static int aji_recv(struct aji_client *client, int timeout);
299 static int aji_send_header(struct aji_client *client, const char *to);
300 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
301 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
302 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
303 static int aji_act_hook(void *data, int type, iks *node);
304 static void aji_handle_iq(struct aji_client *client, iks *node);
305 static void aji_handle_message(struct aji_client *client, ikspak *pak);
306 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
307 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
308 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
309 static void *aji_recv_loop(void *data);
310 static int aji_initialize(struct aji_client *client);
311 static int aji_client_connect(void *data, ikspak *pak);
312 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
313 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
314 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
315 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
316 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
317 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
318 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
319 static int aji_create_client(char *label, struct ast_variable *var, int debug);
320 static int aji_create_buddy(char *label, struct aji_client *client);
321 static int aji_reload(int reload);
322 static int aji_load_config(int reload);
323 static void aji_pruneregister(struct aji_client *client);
324 static int aji_filter_roster(void *data, ikspak *pak);
325 static int aji_get_roster(struct aji_client *client);
326 static int aji_client_info_handler(void *data, ikspak *pak);
327 static int aji_dinfo_handler(void *data, ikspak *pak);
328 static int aji_ditems_handler(void *data, ikspak *pak);
329 static int aji_register_query_handler(void *data, ikspak *pak);
330 static int aji_register_approve_handler(void *data, ikspak *pak);
331 static int aji_reconnect(struct aji_client *client);
332 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd,
333         struct ast_cli_args *a);
334 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd,
335         struct ast_cli_args *a);
336 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
337         ast_cli_args *a);
338 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
339                 ast_cli_args *a);
340 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
341 static int aji_receive_node_list(void *data, ikspak* pak);
342 static void aji_init_event_distribution(struct aji_client *client);
343 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type,
344         const char *name, const char *collection_name);
345 static iks* aji_build_node_config(iks *pubsub, const char *node_type,
346         const char *collection_name);
347 static void aji_create_pubsub_collection(struct aji_client *client,
348         const char *collection_name);
349 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
350    const char *leaf_name);
351 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
352         struct ast_cli_args *a);
353 static void aji_create_affiliations(struct aji_client *client, const char *node);
354 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
355 static void aji_publish_device_state(struct aji_client *client, const char * device,
356         const char *device_state);
357 static int aji_handle_pubsub_error(void *data, ikspak *pak);
358 static int aji_handle_pubsub_event(void *data, ikspak *pak);
359 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
360 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name);
361 static iks* aji_build_node_request(struct aji_client *client, const char *collection);
362 static int aji_delete_node_list(void *data, ikspak* pak);
363 static void aji_pubsub_purge_nodes(struct aji_client *client,
364         const char* collection_name);
365 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
366         const char *context, const char *oldmsgs, const char *newmsgs);
367 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
368 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
369 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
370         const char *event_type);
371 /* No transports in this version */
372 /*
373 static int aji_create_transport(char *label, struct aji_client *client);
374 static int aji_register_transport(void *data, ikspak *pak);
375 static int aji_register_transport2(void *data, ikspak *pak);
376 */
377
378 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from);
379
380 static const struct ast_msg_tech msg_tech = {
381         .name = "xmpp",
382         .msg_send = msg_send_cb,
383 };
384
385 static struct ast_cli_entry aji_cli[] = {
386         AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
387         AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
388         AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
389         AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
390         AST_CLI_DEFINE(aji_test, "Shows roster, but is generally used for mog's debugging."),
391         AST_CLI_DEFINE(aji_cli_create_collection, "Creates a PubSub node collection."),
392         AST_CLI_DEFINE(aji_cli_list_pubsub_nodes, "Lists PubSub nodes"),
393         AST_CLI_DEFINE(aji_cli_create_leafnode, "Creates a PubSub leaf node"),
394         AST_CLI_DEFINE(aji_cli_delete_pubsub_node, "Deletes a PubSub node"),
395         AST_CLI_DEFINE(aji_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
396 };
397
398 static char *app_ajisend = "JabberSend";
399 static char *app_ajisendgroup = "JabberSendGroup";
400 static char *app_ajistatus = "JabberStatus";
401 static char *app_ajijoin = "JabberJoin";
402 static char *app_ajileave = "JabberLeave";
403
404 static struct aji_client_container clients;
405 static struct aji_capabilities *capabilities = NULL;
406 static struct ast_event_sub *mwi_sub = NULL;
407 static struct ast_event_sub *device_state_sub = NULL;
408 static ast_cond_t message_received_condition;
409 static ast_mutex_t messagelock;
410
411 /*! \brief Global flags, initialized to default values */
412 static struct ast_flags globalflags = { AJI_AUTOREGISTER | AJI_AUTOACCEPT };
413
414 /*! \brief PubSub flags, initialized to default values */
415 static struct ast_flags pubsubflags = { 0 };
416 /*!
417  * \internal
418  * \brief Deletes the aji_client data structure.
419  * \param obj aji_client The structure we will delete.
420  * \return void.
421  */
422 static void aji_client_destroy(struct aji_client *obj)
423 {
424         struct aji_message *tmp;
425         ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, aji_buddy_destroy);
426         ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
427         iks_filter_delete(obj->f);
428         iks_parser_delete(obj->p);
429         iks_stack_delete(obj->stack);
430         AST_LIST_LOCK(&obj->messages);
431         while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
432                 aji_message_destroy(tmp);
433         }
434         AST_LIST_HEAD_DESTROY(&obj->messages);
435         ast_free(obj);
436 }
437
438 /*!
439  * \internal
440  * \brief Deletes the aji_buddy data structure.
441  * \param obj aji_buddy The structure we will delete.
442  * \return void.
443  */
444 static void aji_buddy_destroy(struct aji_buddy *obj)
445 {
446         struct aji_resource *tmp;
447
448         while ((tmp = obj->resources)) {
449                 obj->resources = obj->resources->next;
450                 ast_free(tmp->description);
451                 ast_free(tmp);
452         }
453
454         ast_free(obj);
455 }
456
457 /*!
458  * \internal
459  * \brief Deletes the aji_message data structure.
460  * \param obj aji_message The structure we will delete.
461  * \return void.
462  */
463 static void aji_message_destroy(struct aji_message *obj)
464 {
465         if (obj->from) {
466                 ast_free(obj->from);
467         }
468         if (obj->message) {
469                 ast_free(obj->message);
470         }
471         ast_free(obj);
472 }
473
474 /*!
475  * \internal
476  * \brief Find version in XML stream and populate our capabilities list
477  * \param node the node attribute in the caps element we'll look for or add to
478  * our list
479  * \param version the version attribute in the caps element we'll look for or
480  * add to our list
481  * \param pak struct The XML stanza we're processing
482  * \return a pointer to the added or found aji_version structure
483  */
484 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
485 {
486         struct aji_capabilities *list = NULL;
487         struct aji_version *res = NULL;
488
489         list = capabilities;
490
491         if (!node) {
492                 node = pak->from->full;
493         }
494         if (!version) {
495                 version = "none supplied.";
496         }
497         while (list) {
498                 if (!strcasecmp(list->node, node)) {
499                         res = list->versions;
500                         while(res) {
501                                 if (!strcasecmp(res->version, version)) {
502                                         return res;
503                                 }
504                                 res = res->next;
505                         }
506                         /* Specified version not found. Let's add it to
507                            this node in our capabilities list */
508                         if (!res) {
509                                 res = ast_malloc(sizeof(*res));
510                                 if (!res) {
511                                         ast_log(LOG_ERROR, "Out of memory!\n");
512                                         return NULL;
513                                 }
514                                 res->jingle = 0;
515                                 res->parent = list;
516                                 ast_copy_string(res->version, version, sizeof(res->version));
517                                 res->next = list->versions;
518                                 list->versions = res;
519                                 return res;
520                         }
521                 }
522                 list = list->next;
523         }
524         /* Specified node not found. Let's add it our capabilities list */
525         if (!list) {
526                 list = ast_malloc(sizeof(*list));
527                 if (!list) {
528                         ast_log(LOG_ERROR, "Out of memory!\n");
529                         return NULL;
530                 }
531                 res = ast_malloc(sizeof(*res));
532                 if (!res) {
533                         ast_log(LOG_ERROR, "Out of memory!\n");
534                         ast_free(list);
535                         return NULL;
536                 }
537                 ast_copy_string(list->node, node, sizeof(list->node));
538                 ast_copy_string(res->version, version, sizeof(res->version));
539                 res->jingle = 0;
540                 res->parent = list;
541                 res->next = NULL;
542                 list->versions = res;
543                 list->next = capabilities;
544                 capabilities = list;
545         }
546         return res;
547 }
548
549 /*!
550  * \internal
551  * \brief Find the aji_resource we want
552  * \param buddy aji_buddy A buddy
553  * \param name
554  * \return aji_resource object
555 */
556 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
557 {
558         struct aji_resource *res = NULL;
559         if (!buddy || !name) {
560                 return res;
561         }
562         res = buddy->resources;
563         while (res) {
564                 if (!strcasecmp(res->resource, name)) {
565                         break;
566                 }
567                 res = res->next;
568         }
569         return res;
570 }
571
572 /*!
573  * \internal
574  * \brief Jabber GTalk function
575  * \param node iks
576  * \return 1 on success, 0 on failure.
577 */
578 static int gtalk_yuck(iks *node)
579 {
580         if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) {
581                 ast_debug(1, "Found resource with Googletalk voice capabilities\n");
582                 return 1;
583         } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1")) {
584                 ast_debug(1, "Found resource with Gmail voice/video chat capabilities\n");
585                 return 1;
586         } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 video-v1 voice-v1")) {
587                 ast_debug(1, "Found resource with Gmail voice/video chat capabilities (no camera)\n");
588                 return 1;
589         }
590
591         return 0;
592 }
593
594 /*!
595  * \internal
596  * \brief Setup the authentication struct
597  * \param id iksid 
598  * \param pass password
599  * \param sid
600  * \return x iks
601 */
602 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
603 {
604         iks *x, *y;
605         x = iks_new("iq");
606         iks_insert_attrib(x, "type", "set");
607         y = iks_insert(x, "query");
608         iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
609         iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
610         iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
611         if (sid) {
612                 char buf[41];
613                 char sidpass[100];
614                 snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
615                 ast_sha1_hash(buf, sidpass);
616                 iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
617         } else {
618                 iks_insert_cdata(iks_insert(y, "password"), pass, 0);
619         }
620         return x;
621 }
622
623 /*!
624  * \internal
625  * \brief Dial plan function status(). puts the status of watched user 
626  * into a channel variable.
627  * \param chan ast_channel
628  * \param data
629  * \retval 0 success
630  * \retval -1 error
631  */
632 static int aji_status_exec(struct ast_channel *chan, const char *data)
633 {
634         struct aji_client *client = NULL;
635         struct aji_buddy *buddy = NULL;
636         struct aji_resource *r = NULL;
637         char *s = NULL;
638         int stat = 7;
639         char status[2];
640         static int deprecation_warning = 0;
641         AST_DECLARE_APP_ARGS(args,
642                 AST_APP_ARG(sender);
643                 AST_APP_ARG(jid);
644                 AST_APP_ARG(variable);
645         );
646         AST_DECLARE_APP_ARGS(jid,
647                 AST_APP_ARG(screenname);
648                 AST_APP_ARG(resource);
649         );
650
651         if (deprecation_warning++ % 10 == 0) {
652                 ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
653         }
654
655         if (!data) {
656                 ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
657                 return 0;
658         }
659         s = ast_strdupa(data);
660         AST_STANDARD_APP_ARGS(args, s);
661
662         if (args.argc != 3) {
663                 ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
664                 return -1;
665         }
666
667         AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
668         if (jid.argc < 1 || jid.argc > 2) {
669                 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
670                 return -1;
671         }
672
673         if (!(client = ast_aji_get_client(args.sender))) {
674                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
675                 return -1;
676         }
677         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
678         if (!buddy) {
679                 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
680                 return -1;
681         }
682         r = aji_find_resource(buddy, jid.resource);
683         if (!r && buddy->resources) {
684                 r = buddy->resources;
685         }
686         if (!r) {
687                 ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
688         } else {
689                 stat = r->status;
690         }
691         snprintf(status, sizeof(status), "%d", stat);
692         pbx_builtin_setvar_helper(chan, args.variable, status);
693         return 0;
694 }
695
696 /*!
697  * \internal
698  * \brief Dial plan funtcion to retrieve the status of a buddy.
699  * \param channel The associated ast_channel, if there is one
700  * \param data The account, buddy JID, and optional timeout
701  * timeout.
702  * \retval 0 success
703  * \retval -1 failure
704  */
705 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
706 {
707         struct aji_client *client = NULL;
708         struct aji_buddy *buddy = NULL;
709         struct aji_resource *r = NULL;
710         int stat = 7;
711         AST_DECLARE_APP_ARGS(args,
712                 AST_APP_ARG(sender);
713                 AST_APP_ARG(jid);
714         );
715         AST_DECLARE_APP_ARGS(jid,
716                 AST_APP_ARG(screenname);
717                 AST_APP_ARG(resource);
718         );
719
720         if (!data) {
721                 ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
722                 return 0;
723         }
724         AST_STANDARD_APP_ARGS(args, data);
725
726         if (args.argc != 2) {
727                 ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
728                 return -1;
729         }
730
731         AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
732         if (jid.argc < 1 || jid.argc > 2) {
733                 ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
734                 return -1;
735         }
736
737         if (!(client = ast_aji_get_client(args.sender))) {
738                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
739                 return -1;
740         }
741         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
742         if (!buddy) {
743                 ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
744                 return -1;
745         }
746         r = aji_find_resource(buddy, jid.resource);
747         if (!r && buddy->resources) {
748                 r = buddy->resources;
749         }
750         if (!r) {
751                 ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
752         } else {
753                 stat = r->status;
754         }
755         snprintf(buf, buflen, "%d", stat);
756         return 0;
757 }
758
759 static struct ast_custom_function jabberstatus_function = {
760         .name = "JABBER_STATUS",
761         .read = acf_jabberstatus_read,
762 };
763
764 /*!
765  * \internal
766  * \brief Dial plan function to receive a message.
767  * \param channel The associated ast_channel, if there is one
768  * \param data The account, JID, and optional timeout
769  * timeout.
770  * \retval 0 success
771  * \retval -1 failure
772  */
773 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
774 {
775         char *aux = NULL, *parse = NULL;
776         int timeout;
777         int jidlen, resourcelen;
778         struct timeval start;
779         long diff = 0;
780         struct aji_client *client = NULL;
781         int found = 0;
782         struct aji_message *tmp = NULL;
783         AST_DECLARE_APP_ARGS(args,
784                         AST_APP_ARG(account);
785                         AST_APP_ARG(jid);
786                         AST_APP_ARG(timeout);
787                         );
788         AST_DECLARE_APP_ARGS(jid,
789                         AST_APP_ARG(screenname);
790                         AST_APP_ARG(resource);
791         );
792
793         if (ast_strlen_zero(data)) {
794                 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
795                 return -1;
796         }
797
798         parse = ast_strdupa(data);
799         AST_STANDARD_APP_ARGS(args, parse);
800
801         if (args.argc < 2 || args.argc > 3) {
802                 ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
803                 return -1;
804         }
805
806         client = ast_aji_get_client(args.account);
807         if (!client) {
808                 ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
809                 return -1;
810         }
811
812         parse = ast_strdupa(args.jid);
813         AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
814         if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
815                 ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
816                 ASTOBJ_UNREF(client, aji_client_destroy);
817                 return -1;
818         }
819
820         if (ast_strlen_zero(args.timeout)) {
821                 timeout = 20;
822         } else {
823                 sscanf(args.timeout, "%d", &timeout);
824                 if (timeout <= 0) {
825                         ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
826                         ASTOBJ_UNREF(client, aji_client_destroy);
827                         return -1;
828                 }
829         }
830
831         jidlen = strlen(jid.screenname);
832         resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
833
834         ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
835
836         start = ast_tvnow();
837
838         if (ast_autoservice_start(chan) < 0) {
839                 ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", chan->name);
840                 return -1;
841         }
842
843         /* search the messages list, grab the first message that matches with
844          * the from JID we're expecting, and remove it from the messages list */
845         while (diff < timeout) {
846                 struct timespec ts = { 0, };
847                 struct timeval wait;
848                 int res;
849
850                 wait = ast_tvadd(start, ast_tv(timeout, 0));
851                 ts.tv_sec = wait.tv_sec;
852                 ts.tv_nsec = wait.tv_usec * 1000;
853
854                 /* wait up to timeout seconds for an incoming message */
855                 ast_mutex_lock(&messagelock);
856                 res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
857                 ast_mutex_unlock(&messagelock);
858                 if (res == ETIMEDOUT) {
859                         ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
860                         break;
861                 }
862
863                 AST_LIST_LOCK(&client->messages);
864                 AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
865                         if (jid.argc == 1) {
866                                 /* no resource provided, compare bare JIDs */
867                                 if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
868                                         continue;
869                                 }
870                         } else {
871                                 /* resource appended, compare bare JIDs and resources */
872                                 char *resource = strchr(tmp->from, '/');
873                                 if (!resource || strlen(resource) == 0) {
874                                         ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
875                                         if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
876                                                 continue;
877                                         }
878                                 } else {
879                                         resource ++;
880                                         if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
881                                                 continue;
882                                         }
883                                 }
884                         }
885                         /* check if the message is not too old */
886                         if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
887                                 ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
888                                 AST_LIST_REMOVE_CURRENT(list);
889                                 aji_message_destroy(tmp);
890                                 continue;
891                         }
892                         found = 1;
893                         aux = ast_strdupa(tmp->message);
894                         AST_LIST_REMOVE_CURRENT(list);
895                         aji_message_destroy(tmp);
896                         break;
897                 }
898                 AST_LIST_TRAVERSE_SAFE_END;
899                 AST_LIST_UNLOCK(&client->messages);
900                 if (found) {
901                         break;
902                 }
903
904                 /* check timeout */
905                 diff = ast_tvdiff_ms(ast_tvnow(), start);
906         }
907
908         ASTOBJ_UNREF(client, aji_client_destroy);
909         if (ast_autoservice_stop(chan) < 0) {
910                 ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", chan->name);
911         }
912
913         /* return if we timed out */
914         if (!found) {
915                 ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
916                 return -1;
917         }
918         ast_copy_string(buf, aux, buflen);
919
920         return 0;
921 }
922
923 static struct ast_custom_function jabberreceive_function = {
924         .name = "JABBER_RECEIVE",
925         .read = acf_jabberreceive_read,
926 };
927
928 /*!
929  * \internal
930  * \brief Delete old messages from a given JID
931  * Messages stored during more than client->message_timeout are deleted
932  * \param client Asterisk's XMPP client
933  * \param from the JID we received messages from
934  * \retval the number of deleted messages
935  * \retval -1 failure
936  */
937 static int delete_old_messages(struct aji_client *client, char *from)
938 {
939         int deleted = 0;
940         int isold = 0;
941         struct aji_message *tmp = NULL;
942         if (!client) {
943                 ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
944                 return -1;
945         }
946
947         /* remove old messages */
948         AST_LIST_LOCK(&client->messages);
949         if (AST_LIST_EMPTY(&client->messages)) {
950                 AST_LIST_UNLOCK(&client->messages);
951                 return 0;
952         }
953
954         AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
955                 if (isold) {
956                         if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
957                                 AST_LIST_REMOVE_CURRENT(list);
958                                 aji_message_destroy(tmp);
959                                 deleted ++;
960                         }
961                 } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
962                         isold = 1;
963                         if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
964                                 AST_LIST_REMOVE_CURRENT(list);
965                                 aji_message_destroy(tmp);
966                                 deleted ++;
967                         }
968                 }
969         }
970         AST_LIST_TRAVERSE_SAFE_END;
971         AST_LIST_UNLOCK(&client->messages);
972
973         return deleted;
974 }
975
976 /*!
977  * \internal
978  * \brief Delete old messages
979  * Messages stored during more than client->message_timeout are deleted
980  * \param client Asterisk's XMPP client
981  * \retval the number of deleted messages
982  * \retval -1 failure
983  */
984 static int delete_old_messages_all(struct aji_client *client)
985 {
986         return delete_old_messages(client, NULL);
987 }
988
989 /*!
990 * \brief Application to join a chat room
991 * \param chan ast_channel
992 * \param data  Data is sender|jid|nickname.
993 * \retval 0 success
994 * \retval -1 error
995 */
996 static int aji_join_exec(struct ast_channel *chan, const char *data)
997 {
998         struct aji_client *client = NULL;
999         char *s;
1000         char nick[AJI_MAX_RESJIDLEN];
1001
1002         AST_DECLARE_APP_ARGS(args,
1003                 AST_APP_ARG(sender);
1004                 AST_APP_ARG(jid);
1005                 AST_APP_ARG(nick);
1006         );
1007
1008         if (!data) {
1009                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1010                 return -1;
1011         }
1012         s = ast_strdupa(data);
1013
1014         AST_STANDARD_APP_ARGS(args, s);
1015         if (args.argc < 2 || args.argc > 3) {
1016                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
1017                 return -1;
1018         }
1019
1020         if (!(client = ast_aji_get_client(args.sender))) {
1021                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1022                 return -1;
1023         }
1024
1025         if (strchr(args.jid, '/')) {
1026                 ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
1027                 ASTOBJ_UNREF(client, aji_client_destroy);
1028                 return -1;
1029         }
1030
1031         if (!ast_strlen_zero(args.nick)) {
1032                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1033         } else {
1034                 if (client->component) {
1035                         sprintf(nick, "asterisk");
1036                 } else {
1037                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1038                 }
1039         }
1040
1041         if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1042                 ast_aji_join_chat(client, args.jid, nick);
1043         } else {
1044                 ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
1045         }
1046
1047         ASTOBJ_UNREF(client, aji_client_destroy);
1048         return 0;
1049 }
1050
1051 /*!
1052 * \brief Application to leave a chat room
1053 * \param chan ast_channel
1054 * \param data  Data is sender|jid|nickname.
1055 * \retval 0 success
1056 * \retval -1 error
1057 */
1058 static int aji_leave_exec(struct ast_channel *chan, const char *data)
1059 {
1060         struct aji_client *client = NULL;
1061         char *s;
1062         char nick[AJI_MAX_RESJIDLEN];
1063         AST_DECLARE_APP_ARGS(args,
1064                 AST_APP_ARG(sender);
1065                 AST_APP_ARG(jid);
1066                 AST_APP_ARG(nick);
1067         );
1068
1069         if (!data) {
1070                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1071                 return -1;
1072         }
1073         s = ast_strdupa(data);
1074
1075         AST_STANDARD_APP_ARGS(args, s);
1076         if (args.argc < 2 || args.argc > 3) {
1077                 ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
1078                 return -1;
1079         }
1080
1081         if (!(client = ast_aji_get_client(args.sender))) {
1082                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1083                 return -1;
1084         }
1085
1086         if (strchr(args.jid, '/')) {
1087                 ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
1088                 ASTOBJ_UNREF(client, aji_client_destroy);
1089                 return -1;
1090         }
1091         if (!ast_strlen_zero(args.nick)) {
1092                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1093         } else {
1094                 if (client->component) {
1095                         sprintf(nick, "asterisk");
1096                 } else {
1097                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1098                 }
1099         }
1100
1101         if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
1102                 ast_aji_leave_chat(client, args.jid, nick);
1103         }
1104         ASTOBJ_UNREF(client, aji_client_destroy);
1105         return 0;
1106 }
1107
1108 /*!
1109  * \internal
1110  * \brief Dial plan function to send a message.
1111  * \param chan ast_channel
1112  * \param data  Data is account,jid,message.
1113  * \retval 0 success
1114  * \retval -1 failure
1115  */
1116 static int aji_send_exec(struct ast_channel *chan, const char *data)
1117 {
1118         struct aji_client *client = NULL;
1119         char *s;
1120         AST_DECLARE_APP_ARGS(args,
1121                 AST_APP_ARG(sender);
1122                 AST_APP_ARG(recipient);
1123                 AST_APP_ARG(message);
1124         );
1125
1126         if (!data) {
1127                 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1128                 return -1;
1129         }
1130         s = ast_strdupa(data);
1131
1132         AST_STANDARD_APP_ARGS(args, s);
1133         if (args.argc < 3) {
1134                 ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
1135                 return -1;
1136         }
1137
1138         if (!(client = ast_aji_get_client(args.sender))) {
1139                 ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
1140                 return -1;
1141         }
1142         if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
1143                 ast_aji_send_chat(client, args.recipient, args.message);
1144         }
1145         return 0;
1146 }
1147
1148 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from)
1149 {
1150         struct aji_client *client;
1151         char *sender;
1152         char *dest;
1153         int res;
1154
1155         sender = ast_strdupa(from);
1156         strsep(&sender, ":");
1157         dest = ast_strdupa(to);
1158         strsep(&dest, ":");
1159
1160         if (ast_strlen_zero(sender)) {
1161                 ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for xmpp\n", from);
1162                 return -1;
1163         }
1164
1165         if (!(client = ast_aji_get_client(sender))) {
1166                 ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
1167                 return -1;
1168         }
1169
1170
1171         ast_debug(1, "Sending message to '%s' from '%s'\n", dest, client->name);
1172
1173         res = ast_aji_send_chat(client, dest, ast_msg_get_body(msg));
1174         if (res != IKS_OK) {
1175                 ast_log(LOG_WARNING, "Failed to send xmpp message (%d).\n", res);
1176         }
1177
1178         /* 
1179          * XXX Reference leak here.  See note with ast_aji_get_client() about the problems
1180          * with that function.
1181          */
1182
1183         return res == IKS_OK ? 0 : -1;
1184 }
1185
1186 /*!
1187 * \brief Application to send a message to a groupchat.
1188 * \param chan ast_channel
1189 * \param data  Data is sender|groupchat|message.
1190 * \retval 0 success
1191 * \retval -1 error
1192 */
1193 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
1194 {
1195         struct aji_client *client = NULL;
1196         char *s;
1197         char nick[AJI_MAX_RESJIDLEN];
1198         int res = 0;
1199         AST_DECLARE_APP_ARGS(args,
1200                 AST_APP_ARG(sender);
1201                 AST_APP_ARG(groupchat);
1202                 AST_APP_ARG(message);
1203                 AST_APP_ARG(nick);
1204         );
1205
1206         if (!data) {
1207                 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1208                 return -1;
1209         }
1210         s = ast_strdupa(data);
1211
1212         AST_STANDARD_APP_ARGS(args, s);
1213         if (args.argc < 3 || args.argc > 4) {
1214                 ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
1215                 return -1;
1216         }
1217
1218         if (!(client = ast_aji_get_client(args.sender))) {
1219                 ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
1220                 return -1;
1221         }
1222
1223         if (ast_strlen_zero(args.nick) || args.argc == 3) {
1224                 if (client->component) {
1225                         sprintf(nick, "asterisk");
1226                 } else {
1227                         snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
1228                 }
1229         } else {
1230                 snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
1231         }
1232
1233         if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
1234                 res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
1235         }
1236
1237         ASTOBJ_UNREF(client, aji_client_destroy);
1238         if (res != IKS_OK) {
1239                 return -1;
1240         }
1241         return 0;
1242 }
1243
1244 /*!
1245  * \internal
1246  * \brief Tests whether the connection is secured or not
1247  * \return 0 if the connection is not secured
1248  */
1249 static int aji_is_secure(struct aji_client *client)
1250 {
1251 #ifdef HAVE_OPENSSL
1252         return client->stream_flags & SECURE;
1253 #else
1254         return 0;
1255 #endif
1256 }
1257
1258 #ifdef HAVE_OPENSSL
1259 /*!
1260  * \internal
1261  * \brief Starts the TLS procedure
1262  * \param client the configured XMPP client we use to connect to a XMPP server
1263  * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
1264  * if OpenSSL is not installed
1265  */
1266 static int aji_start_tls(struct aji_client *client)
1267 {
1268         int ret;
1269
1270         /* This is sent not encrypted */
1271         if ((ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"))) {
1272                 return ret;
1273         }
1274
1275         client->stream_flags |= TRY_SECURE;
1276         return IKS_OK;
1277 }
1278
1279 /*!
1280  * \internal
1281  * \brief TLS handshake, OpenSSL initialization
1282  * \param client the configured XMPP client we use to connect to a XMPP server
1283  * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
1284  */
1285 static int aji_tls_handshake(struct aji_client *client)
1286 {
1287         int ret;
1288         int sock;
1289
1290         ast_debug(1, "Starting TLS handshake\n");
1291
1292         /* Choose an SSL/TLS protocol version, create SSL_CTX */
1293         client->ssl_method = SSLv3_method();
1294         if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
1295                 return IKS_NET_TLSFAIL;
1296         }
1297
1298         /* Create new SSL session */
1299         if (!(client->ssl_session = SSL_new(client->ssl_context))) {
1300                 return IKS_NET_TLSFAIL;
1301         }
1302
1303         /* Enforce TLS on our XMPP connection */
1304         sock = iks_fd(client->p);
1305         if (!(ret = SSL_set_fd(client->ssl_session, sock))) {
1306                 return IKS_NET_TLSFAIL;
1307         }
1308
1309         /* Perform SSL handshake */
1310         if (!(ret = SSL_connect(client->ssl_session))) {
1311                 return IKS_NET_TLSFAIL;
1312         }
1313
1314         client->stream_flags &= (~TRY_SECURE);
1315         client->stream_flags |= SECURE;
1316
1317         /* Sent over the established TLS connection */
1318         if ((ret = aji_send_header(client, client->jid->server)) != IKS_OK) {
1319                 return IKS_NET_TLSFAIL;
1320         }
1321
1322         ast_debug(1, "TLS started with server\n");
1323
1324         return IKS_OK;
1325 }
1326 #endif /* HAVE_OPENSSL */
1327
1328 /*!
1329  * \internal
1330  * \brief Secured or unsecured IO socket receiving function
1331  * \param client the configured XMPP client we use to connect to a XMPP server
1332  * \param buffer the reception buffer
1333  * \param buf_len the size of the buffer
1334  * \param timeout the select timer
1335  * \retval the number of read bytes
1336  * \retval 0 timeout expiration
1337  * \retval -1 error
1338  */
1339 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
1340 {
1341         struct pollfd pfd = { .events = POLLIN };
1342         int len, res;
1343
1344 #ifdef HAVE_OPENSSL
1345         if (aji_is_secure(client)) {
1346                 pfd.fd = SSL_get_fd(client->ssl_session);
1347                 if (pfd.fd < 0) {
1348                         return -1;
1349                 }
1350         } else
1351 #endif /* HAVE_OPENSSL */
1352                 pfd.fd = iks_fd(client->p);
1353
1354         res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
1355         if (res > 0) {
1356 #ifdef HAVE_OPENSSL
1357                 if (aji_is_secure(client)) {
1358                         len = SSL_read(client->ssl_session, buffer, buf_len);
1359                 } else
1360 #endif /* HAVE_OPENSSL */
1361                         len = recv(pfd.fd, buffer, buf_len, 0);
1362
1363                 if (len > 0) {
1364                         return len;
1365                 } else if (len <= 0) {
1366                         return -1;
1367                 }
1368         }
1369         return res;
1370 }
1371
1372 /*!
1373  * \internal
1374  * \brief Tries to receive data from the Jabber server
1375  * \param client the configured XMPP client we use to connect to a XMPP server
1376  * \param timeout the timeout value
1377  * This function receives (encrypted or unencrypted) data from the XMPP server,
1378  * and passes it to the parser.
1379  * \retval IKS_OK success
1380  * \retval IKS_NET_RWERR IO error
1381  * \retval IKS_NET_NOCONN no connection available
1382  * \retval IKS_NET_EXPIRED timeout expiration
1383  */
1384 static int aji_recv (struct aji_client *client, int timeout)
1385 {
1386         int len, ret;
1387         char buf[NET_IO_BUF_SIZE - 1];
1388         char newbuf[NET_IO_BUF_SIZE - 1];
1389         int pos = 0;
1390         int newbufpos = 0;
1391         unsigned char c;
1392
1393         memset(buf, 0, sizeof(buf));
1394         memset(newbuf, 0, sizeof(newbuf));
1395
1396         while (1) {
1397                 len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
1398                 if (len < 0) return IKS_NET_RWERR;
1399                 if (len == 0) return IKS_NET_EXPIRED;
1400                 buf[len] = '\0';
1401
1402                 /* our iksemel parser won't work as expected if we feed
1403                    it with XML packets that contain multiple whitespace
1404                    characters between tags */
1405                 while (pos < len) {
1406                         c = buf[pos];
1407                         /* if we stumble on the ending tag character,
1408                            we skip any whitespace that follows it*/
1409                         if (c == '>') {
1410                                 while (isspace(buf[pos+1])) {
1411                                         pos++;
1412                                 }
1413                         }
1414                         newbuf[newbufpos] = c;
1415                         newbufpos ++;
1416                         pos++;
1417                 }
1418                 pos = 0;
1419                 newbufpos = 0;
1420
1421                 /* Log the message here, because iksemel's logHook is
1422                    unaccessible */
1423                 aji_log_hook(client, buf, len, 1);
1424
1425                 /* let iksemel deal with the string length,
1426                    and reset our buffer */
1427                 ret = iks_parse(client->p, newbuf, 0, 0);
1428                 memset(newbuf, 0, sizeof(newbuf));
1429
1430                 switch (ret) {
1431                 case IKS_NOMEM:
1432                         ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
1433                         break;
1434                 case IKS_BADXML:
1435                         ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
1436                         break;
1437                 case IKS_HOOK:
1438                         ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
1439                         break;
1440                 }
1441                 if (ret != IKS_OK) {
1442                         return ret;
1443                 }
1444                 ast_debug(3, "XML parsing successful\n");
1445         }
1446         return IKS_OK;
1447 }
1448
1449 /*!
1450  * \internal
1451  * \brief Sends XMPP header to the server
1452  * \param client the configured XMPP client we use to connect to a XMPP server
1453  * \param to the target XMPP server
1454  * \return IKS_OK on success, any other value on failure
1455  */
1456 static int aji_send_header(struct aji_client *client, const char *to)
1457 {
1458         char *msg;
1459         int len, err;
1460
1461         len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
1462         msg = iks_malloc(len);
1463         if (!msg)
1464                 return IKS_NOMEM;
1465         sprintf(msg, "<?xml version='1.0'?>"
1466                 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
1467                 "%s' to='%s' version='1.0'>", client->name_space, to);
1468         err = aji_send_raw(client, msg);
1469         iks_free(msg);
1470         if (err != IKS_OK)
1471                 return err;
1472
1473         return IKS_OK;
1474 }
1475
1476 /*!
1477  * \brief Wraps raw sending
1478  * \param client the configured XMPP client we use to connect to a XMPP server
1479  * \param x the XMPP packet to send
1480  * \return IKS_OK on success, any other value on failure
1481  */
1482 int ast_aji_send(struct aji_client *client, iks *x)
1483 {
1484         return aji_send_raw(client, iks_string(iks_stack(x), x));
1485 }
1486
1487 /*!
1488  * \internal
1489  * \brief Sends an XML string over an XMPP connection
1490  * \param client the configured XMPP client we use to connect to a XMPP server
1491  * \param xmlstr the XML string to send
1492  * The XML data is sent whether the connection is secured or not. In the
1493  * latter case, we just call iks_send_raw().
1494  * \return IKS_OK on success, any other value on failure
1495  */
1496 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
1497 {
1498         int ret;
1499 #ifdef HAVE_OPENSSL
1500         int len = strlen(xmlstr);
1501
1502         if (aji_is_secure(client)) {
1503                 ret = SSL_write(client->ssl_session, xmlstr, len);
1504                 if (ret) {
1505                         /* Log the message here, because iksemel's logHook is
1506                            unaccessible */
1507                         aji_log_hook(client, xmlstr, len, 0);
1508                         return IKS_OK;
1509                 }
1510         }
1511 #endif
1512         /* If needed, data will be sent unencrypted, and logHook will
1513            be called inside iks_send_raw */
1514         ret = iks_send_raw(client->p, xmlstr);
1515         if (ret != IKS_OK) {
1516                 return ret;
1517         }
1518
1519         return IKS_OK;
1520 }
1521
1522 /*!
1523  * \internal
1524  * \brief the debug loop.
1525  * \param data void
1526  * \param xmpp xml data as string
1527  * \param size size of string
1528  * \param is_incoming direction of packet 1 for inbound 0 for outbound.
1529  */
1530 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
1531 {
1532         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1533
1534         if (!ast_strlen_zero(xmpp)) {
1535                 manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
1536         }
1537
1538         if (client->debug) {
1539                 if (is_incoming) {
1540                         ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
1541                 } else {
1542                         if (strlen(xmpp) == 1) {
1543                                 if (option_debug > 2  && xmpp[0] == ' ') {
1544                                         ast_verbose("\nJABBER: Keep alive packet\n");
1545                                 }
1546                         } else {
1547                                 ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
1548                         }
1549                 }
1550
1551         }
1552         ASTOBJ_UNREF(client, aji_client_destroy);
1553 }
1554
1555 /*!
1556  * \internal
1557  * \brief A wrapper function for iks_start_sasl
1558  * \param client the configured XMPP client we use to connect to a XMPP server
1559  * \param type the SASL authentication type. Supported types are PLAIN and MD5
1560  * \param username
1561  * \param pass password.
1562  *
1563  * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
1564  */
1565 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
1566 {
1567         iks *x = NULL;
1568         int len;
1569         char *s;
1570         char *base64;
1571
1572         /* trigger SASL DIGEST-MD5 only over an unsecured connection.
1573            iks_start_sasl is an iksemel API function and relies on GnuTLS,
1574            whereas we use OpenSSL */
1575         if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
1576                 return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass); 
1577         if (!(type & IKS_STREAM_SASL_PLAIN)) {
1578                 ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
1579                 return IKS_NET_NOTSUPP;
1580         }
1581
1582         x = iks_new("auth"); 
1583         if (!x) {
1584                 ast_log(LOG_ERROR, "Out of memory.\n");
1585                 return IKS_NET_NOTSUPP;
1586         }
1587
1588         iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
1589         len = strlen(username) + strlen(pass) + 3;
1590         s = alloca(len);
1591         base64 = alloca((len + 2) * 4 / 3);
1592         iks_insert_attrib(x, "mechanism", "PLAIN");
1593         snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
1594
1595         /* exclude the NULL training byte from the base64 encoding operation
1596            as some XMPP servers will refuse it.
1597            The format for authentication is [authzid]\0authcid\0password
1598            not [authzid]\0authcid\0password\0 */
1599         ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
1600         iks_insert_cdata(x, base64, 0);
1601         ast_aji_send(client, x);
1602         iks_delete(x);
1603
1604         return IKS_OK;
1605 }
1606
1607 /*!
1608  * \internal
1609  * \brief The action hook parses the inbound packets, constantly running.
1610  * \param data aji client structure 
1611  * \param type type of packet 
1612  * \param node the actual packet.
1613  * \return IKS_OK or IKS_HOOK .
1614  */
1615 static int aji_act_hook(void *data, int type, iks *node)
1616 {
1617         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1618         ikspak *pak = NULL;
1619         iks *auth = NULL;
1620         int features = 0;
1621
1622         if (!node) {
1623                 ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
1624                 ASTOBJ_UNREF(client, aji_client_destroy);
1625                 return IKS_HOOK;
1626         }
1627
1628         if (client->state == AJI_DISCONNECTING) {
1629                 ASTOBJ_UNREF(client, aji_client_destroy);
1630                 return IKS_HOOK;
1631         }
1632
1633         pak = iks_packet(node);
1634
1635         /* work around iksemel's impossibility to recognize node names
1636          * containing a semicolon. Set the namespace of the corresponding
1637          * node accordingly. */
1638         if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
1639                 char *node_ns = NULL;
1640                 char attr[AJI_MAX_ATTRLEN];
1641                 char *node_name = iks_name(iks_child(node));
1642                 char *aux = strchr(node_name, ':') + 1;
1643                 snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
1644                 node_ns = iks_find_attrib(iks_child(node), attr);
1645                 if (node_ns) {
1646                         pak->ns = node_ns;
1647                         pak->query = iks_child(node);
1648                 }
1649         }
1650
1651
1652         if (!client->component) { /*client */
1653                 switch (type) {
1654                 case IKS_NODE_START:
1655                         if (client->usetls && !aji_is_secure(client)) {
1656 #ifndef HAVE_OPENSSL
1657                                 ast_log(LOG_ERROR, "OpenSSL not installed. You need to install OpenSSL on this system, or disable the TLS option in your configuration file\n");
1658                                 ASTOBJ_UNREF(client, aji_client_destroy);
1659                                 return IKS_HOOK;
1660 #else
1661                                 if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
1662                                         ast_log(LOG_ERROR, "Could not start TLS\n");
1663                                         ASTOBJ_UNREF(client, aji_client_destroy);
1664                                         return IKS_HOOK;                
1665                                 }
1666 #endif
1667                                 break;
1668                         }
1669                         if (!client->usesasl) {
1670                                 iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE);
1671                                 auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
1672                                 if (auth) {
1673                                         iks_insert_attrib(auth, "id", client->mid);
1674                                         iks_insert_attrib(auth, "to", client->jid->server);
1675                                         ast_aji_increment_mid(client->mid);
1676                                         ast_aji_send(client, auth);
1677                                         iks_delete(auth);
1678                                 } else {
1679                                         ast_log(LOG_ERROR, "Out of memory.\n");
1680                                 }
1681                         }
1682                         break;
1683
1684                 case IKS_NODE_NORMAL:
1685 #ifdef HAVE_OPENSSL
1686                         if (client->stream_flags & TRY_SECURE) {
1687                                 if (!strcmp("proceed", iks_name(node))) {
1688                                         return aji_tls_handshake(client);
1689                                 }
1690                         }
1691 #endif
1692                         if (!strcmp("stream:features", iks_name(node))) {
1693                                 features = iks_stream_features(node);
1694                                 if (client->usesasl) {
1695                                         if (client->usetls && !aji_is_secure(client)) {
1696                                                 break;
1697                                         }
1698                                         if (client->authorized) {
1699                                                 if (features & IKS_STREAM_BIND) {
1700                                                         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);
1701                                                         auth = iks_make_resource_bind(client->jid);
1702                                                         if (auth) {
1703                                                                 iks_insert_attrib(auth, "id", client->mid);
1704                                                                 ast_aji_increment_mid(client->mid);
1705                                                                 ast_aji_send(client, auth);
1706                                                                 iks_delete(auth);
1707                                                         } else {
1708                                                                 ast_log(LOG_ERROR, "Out of memory.\n");
1709                                                                 break;
1710                                                         }
1711                                                 }
1712                                                 if (features & IKS_STREAM_SESSION) {
1713                                                         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);
1714                                                         auth = iks_make_session();
1715                                                         if (auth) {
1716                                                                 iks_insert_attrib(auth, "id", "auth");
1717                                                                 ast_aji_increment_mid(client->mid);
1718                                                                 ast_aji_send(client, auth);
1719                                                                 iks_delete(auth);
1720                                                         } else {
1721                                                                 ast_log(LOG_ERROR, "Out of memory.\n");
1722                                                         }
1723                                                 }
1724                                         } else {
1725                                                 int ret;
1726                                                 if (!client->jid->user) {
1727                                                         ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
1728                                                         break;
1729                                                 }
1730
1731                                                 ret = aji_start_sasl(client, features, client->jid->user, client->password);
1732                                                 if (ret != IKS_OK) {
1733                                                         ASTOBJ_UNREF(client, aji_client_destroy);
1734                                                         return IKS_HOOK;
1735                                                 }
1736                                                 break;
1737                                         }
1738                                 }
1739                         } else if (!strcmp("failure", iks_name(node))) {
1740                                 ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
1741                         } else if (!strcmp("success", iks_name(node))) {
1742                                 client->authorized = 1;
1743                                 aji_send_header(client, client->jid->server);
1744                         }
1745                         break;
1746                 case IKS_NODE_ERROR:
1747                         ast_log(LOG_ERROR, "JABBER: Node Error\n");
1748                         ASTOBJ_UNREF(client, aji_client_destroy);
1749                         return IKS_HOOK;
1750                         break;
1751                 case IKS_NODE_STOP:
1752                         ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1753                         ASTOBJ_UNREF(client, aji_client_destroy);
1754                         return IKS_HOOK;
1755                         break;
1756                 }
1757         } else if (client->state != AJI_CONNECTED && client->component) {
1758                 switch (type) {
1759                 case IKS_NODE_START:
1760                         if (client->state == AJI_DISCONNECTED) {
1761                                 char secret[160], shasum[320], *handshake;
1762
1763                                 sprintf(secret, "%s%s", pak->id, client->password);
1764                                 ast_sha1_hash(shasum, secret);
1765                                 handshake = NULL;
1766                                 if (asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
1767                                         aji_send_raw(client, handshake);
1768                                         ast_free(handshake);
1769                                         handshake = NULL;
1770                                 }
1771                                 client->state = AJI_CONNECTING;
1772                                 if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
1773                                         client->state = AJI_CONNECTED;
1774                                 else
1775                                         ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1776                                 break;
1777                         }
1778                         break;
1779
1780                 case IKS_NODE_NORMAL:
1781                         break;
1782
1783                 case IKS_NODE_ERROR:
1784                         ast_log(LOG_ERROR, "JABBER: Node Error\n");
1785                         ASTOBJ_UNREF(client, aji_client_destroy);
1786                         return IKS_HOOK;
1787
1788                 case IKS_NODE_STOP:
1789                         ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1790                         ASTOBJ_UNREF(client, aji_client_destroy);
1791                         return IKS_HOOK;
1792                 }
1793         }
1794
1795         switch (pak->type) {
1796         case IKS_PAK_NONE:
1797                 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1798                 break;
1799         case IKS_PAK_MESSAGE:
1800                 aji_handle_message(client, pak);
1801                 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1802                 break;
1803         case IKS_PAK_PRESENCE:
1804                 aji_handle_presence(client, pak);
1805                 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1806                 break;
1807         case IKS_PAK_S10N:
1808                 aji_handle_subscribe(client, pak);
1809                 ast_debug(1, "JABBER: Handling paktype S10N\n");
1810                 break;
1811         case IKS_PAK_IQ:
1812                 ast_debug(1, "JABBER: Handling paktype IQ\n");
1813                 aji_handle_iq(client, node);
1814                 break;
1815         default:
1816                 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
1817                 break;
1818         }
1819
1820         iks_filter_packet(client->f, pak);
1821
1822         if (node)
1823                 iks_delete(node);
1824
1825         ASTOBJ_UNREF(client, aji_client_destroy);
1826         return IKS_OK;
1827 }
1828 /*!
1829  * \internal
1830  * \brief Unknown
1831  * \param data void
1832  * \param pak ikspak
1833  * \return IKS_FILTER_EAT.
1834 */
1835 static int aji_register_approve_handler(void *data, ikspak *pak)
1836 {
1837         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1838         iks *iq = NULL, *presence = NULL, *x = NULL;
1839
1840         iq = iks_new("iq");
1841         presence = iks_new("presence");
1842         x = iks_new("x");
1843         if (client && iq && presence && x) {
1844                 if (!iks_find(pak->query, "remove")) {
1845                         iks_insert_attrib(iq, "from", client->jid->full);
1846                         iks_insert_attrib(iq, "to", pak->from->full);
1847                         iks_insert_attrib(iq, "id", pak->id);
1848                         iks_insert_attrib(iq, "type", "result");
1849                         ast_aji_send(client, iq);
1850
1851                         iks_insert_attrib(presence, "from", client->jid->full);
1852                         iks_insert_attrib(presence, "to", pak->from->partial);
1853                         iks_insert_attrib(presence, "id", client->mid);
1854                         ast_aji_increment_mid(client->mid);
1855                         iks_insert_attrib(presence, "type", "subscribe");
1856                         iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1857                         iks_insert_node(presence, x);
1858                         ast_aji_send(client, presence);
1859                 }
1860         } else {
1861                 ast_log(LOG_ERROR, "Out of memory.\n");
1862         }
1863
1864         iks_delete(iq);
1865         iks_delete(presence);
1866         iks_delete(x);
1867
1868         ASTOBJ_UNREF(client, aji_client_destroy);
1869         return IKS_FILTER_EAT;
1870 }
1871 /*!
1872  * \internal
1873  * \brief register handler for incoming querys (IQ's)
1874  * \param data incoming aji_client request
1875  * \param pak ikspak
1876  * \return IKS_FILTER_EAT.
1877 */
1878 static int aji_register_query_handler(void *data, ikspak *pak)
1879 {
1880         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1881         struct aji_buddy *buddy = NULL;
1882         char *node = NULL;
1883         iks *iq = NULL, *query = NULL;
1884
1885         client = (struct aji_client *) data;
1886
1887         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1888         if (!buddy) {
1889                 iks  *error = NULL, *notacceptable = NULL;
1890
1891                 ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1892                 iq = iks_new("iq");
1893                 query = iks_new("query");
1894                 error = iks_new("error");
1895                 notacceptable = iks_new("not-acceptable");
1896                 if (iq && query && error && notacceptable) {
1897                         iks_insert_attrib(iq, "type", "error");
1898                         iks_insert_attrib(iq, "from", client->user);
1899                         iks_insert_attrib(iq, "to", pak->from->full);
1900                         iks_insert_attrib(iq, "id", pak->id);
1901                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1902                         iks_insert_attrib(error, "code" , "406");
1903                         iks_insert_attrib(error, "type", "modify");
1904                         iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1905                         iks_insert_node(iq, query);
1906                         iks_insert_node(iq, error);
1907                         iks_insert_node(error, notacceptable);
1908                         ast_aji_send(client, iq);
1909                 } else {
1910                         ast_log(LOG_ERROR, "Out of memory.\n");
1911                 }
1912
1913                 iks_delete(error);
1914                 iks_delete(notacceptable);
1915         } else if (!(node = iks_find_attrib(pak->query, "node"))) {
1916                 iks *instructions = NULL;
1917                 char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1918                 iq = iks_new("iq");
1919                 query = iks_new("query");
1920                 instructions = iks_new("instructions");
1921                 if (iq && query && instructions && client) {
1922                         iks_insert_attrib(iq, "from", client->user);
1923                         iks_insert_attrib(iq, "to", pak->from->full);
1924                         iks_insert_attrib(iq, "id", pak->id);
1925                         iks_insert_attrib(iq, "type", "result");
1926                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1927                         iks_insert_cdata(instructions, explain, 0);
1928                         iks_insert_node(iq, query);
1929                         iks_insert_node(query, instructions);
1930                         ast_aji_send(client, iq);
1931                 } else {
1932                         ast_log(LOG_ERROR, "Out of memory.\n");
1933                 }
1934
1935                 iks_delete(instructions);
1936         }
1937         iks_delete(iq);
1938         iks_delete(query);
1939         ASTOBJ_UNREF(client, aji_client_destroy);
1940         return IKS_FILTER_EAT;
1941 }
1942
1943 /*!
1944  * \internal
1945  * \brief Handles stuff
1946  * \param data void
1947  * \param pak ikspak
1948  * \return IKS_FILTER_EAT.
1949 */
1950 static int aji_ditems_handler(void *data, ikspak *pak)
1951 {
1952         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1953         char *node = NULL;
1954
1955         if (!(node = iks_find_attrib(pak->query, "node"))) {
1956                 iks *iq = NULL, *query = NULL, *item = NULL;
1957                 iq = iks_new("iq");
1958                 query = iks_new("query");
1959                 item = iks_new("item");
1960
1961                 if (iq && query && item) {
1962                         iks_insert_attrib(iq, "from", client->user);
1963                         iks_insert_attrib(iq, "to", pak->from->full);
1964                         iks_insert_attrib(iq, "id", pak->id);
1965                         iks_insert_attrib(iq, "type", "result");
1966                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1967                         iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1968                         iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1969                         iks_insert_attrib(item, "jid", client->user);
1970
1971                         iks_insert_node(iq, query);
1972                         iks_insert_node(query, item);
1973                         ast_aji_send(client, iq);
1974                 } else {
1975                         ast_log(LOG_ERROR, "Out of memory.\n");
1976                 }
1977
1978                 iks_delete(iq);
1979                 iks_delete(query);
1980                 iks_delete(item);
1981
1982         } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1983                 iks *iq, *query, *confirm;
1984                 iq = iks_new("iq");
1985                 query = iks_new("query");
1986                 confirm = iks_new("item");
1987                 if (iq && query && confirm && client) {
1988                         iks_insert_attrib(iq, "from", client->user);
1989                         iks_insert_attrib(iq, "to", pak->from->full);
1990                         iks_insert_attrib(iq, "id", pak->id);
1991                         iks_insert_attrib(iq, "type", "result");
1992                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1993                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1994                         iks_insert_attrib(confirm, "node", "confirmaccount");
1995                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
1996                         iks_insert_attrib(confirm, "jid", "blog.astjab.org");
1997
1998                         iks_insert_node(iq, query);
1999                         iks_insert_node(query, confirm);
2000                         ast_aji_send(client, iq);
2001                 } else {
2002                         ast_log(LOG_ERROR, "Out of memory.\n");
2003                 }
2004
2005                 iks_delete(iq);
2006                 iks_delete(query);
2007                 iks_delete(confirm);
2008
2009         } else if (!strcasecmp(node, "confirmaccount")) {
2010                 iks *iq = NULL, *query = NULL, *feature = NULL;
2011
2012                 iq = iks_new("iq");
2013                 query = iks_new("query");
2014                 feature = iks_new("feature");
2015
2016                 if (iq && query && feature && client) {
2017                         iks_insert_attrib(iq, "from", client->user);
2018                         iks_insert_attrib(iq, "to", pak->from->full);
2019                         iks_insert_attrib(iq, "id", pak->id);
2020                         iks_insert_attrib(iq, "type", "result");
2021                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2022                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2023                         iks_insert_node(iq, query);
2024                         iks_insert_node(query, feature);
2025                         ast_aji_send(client, iq);
2026                 } else {
2027                         ast_log(LOG_ERROR, "Out of memory.\n");
2028                 }
2029
2030                 iks_delete(iq);
2031                 iks_delete(query);
2032                 iks_delete(feature);
2033         }
2034
2035         ASTOBJ_UNREF(client, aji_client_destroy);
2036         return IKS_FILTER_EAT;
2037
2038 }
2039
2040 /*!
2041  * \internal
2042  * \brief Handle add extra info
2043  * \param data void
2044  * \param pak ikspak
2045  * \return IKS_FILTER_EAT
2046 */
2047 static int aji_client_info_handler(void *data, ikspak *pak)
2048 {
2049         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2050         struct aji_resource *resource = NULL;
2051         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2052
2053         resource = aji_find_resource(buddy, pak->from->resource);
2054         if (pak->subtype == IKS_TYPE_RESULT) {
2055                 if (!resource) {
2056                         ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2057                         ASTOBJ_UNREF(client, 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(client, aji_client_destroy);
2101         return IKS_FILTER_EAT;
2102 }
2103
2104 /*!
2105  * \internal
2106  * \brief Handler of the return info packet
2107  * \param data aji_client
2108  * \param pak ikspak
2109  * \return IKS_FILTER_EAT
2110 */
2111 static int aji_dinfo_handler(void *data, ikspak *pak)
2112 {
2113         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2114         char *node = NULL;
2115         struct aji_resource *resource = NULL;
2116         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2117
2118         resource = aji_find_resource(buddy, pak->from->resource);
2119         if (pak->subtype == IKS_TYPE_ERROR) {
2120                 ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
2121                 return IKS_FILTER_EAT;
2122         }
2123         if (pak->subtype == IKS_TYPE_RESULT) {
2124                 if (!resource) {
2125                         ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
2126                         ASTOBJ_UNREF(client, aji_client_destroy);
2127                         return IKS_FILTER_EAT;
2128                 }
2129                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
2130                         resource->cap->jingle = 1;
2131                 } else {
2132                         resource->cap->jingle = 0;
2133                 }
2134         } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
2135                 iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
2136
2137                 iq = iks_new("iq");
2138                 query = iks_new("query");
2139                 identity = iks_new("identity");
2140                 disco = iks_new("feature");
2141                 reg = iks_new("feature");
2142                 commands = iks_new("feature");
2143                 gateway = iks_new("feature");
2144                 version = iks_new("feature");
2145                 vcard = iks_new("feature");
2146                 search = iks_new("feature");
2147                 if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
2148                         iks_insert_attrib(iq, "from", client->user);
2149                         iks_insert_attrib(iq, "to", pak->from->full);
2150                         iks_insert_attrib(iq, "id", pak->id);
2151                         iks_insert_attrib(iq, "type", "result");
2152                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2153                         iks_insert_attrib(identity, "category", "gateway");
2154                         iks_insert_attrib(identity, "type", "pstn");
2155                         iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
2156                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
2157                         iks_insert_attrib(reg, "var", "jabber:iq:register");
2158                         iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
2159                         iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
2160                         iks_insert_attrib(version, "var", "jabber:iq:version");
2161                         iks_insert_attrib(vcard, "var", "vcard-temp");
2162                         iks_insert_attrib(search, "var", "jabber:iq:search");
2163
2164                         iks_insert_node(iq, query);
2165                         iks_insert_node(query, identity);
2166                         iks_insert_node(query, disco);
2167                         iks_insert_node(query, reg);
2168                         iks_insert_node(query, commands);
2169                         iks_insert_node(query, gateway);
2170                         iks_insert_node(query, version);
2171                         iks_insert_node(query, vcard);
2172                         iks_insert_node(query, search);
2173                         ast_aji_send(client, iq);
2174                 } else {
2175                         ast_log(LOG_ERROR, "Out of memory.\n");
2176                 }
2177
2178                 iks_delete(iq);
2179                 iks_delete(query);
2180                 iks_delete(identity);
2181                 iks_delete(disco);
2182                 iks_delete(reg);
2183                 iks_delete(commands);
2184                 iks_delete(gateway);
2185                 iks_delete(version);
2186                 iks_delete(vcard);
2187                 iks_delete(search);
2188         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
2189                 iks *iq, *query, *confirm;
2190                 iq = iks_new("iq");
2191                 query = iks_new("query");
2192                 confirm = iks_new("item");
2193
2194                 if (iq && query && confirm && client) {
2195                         iks_insert_attrib(iq, "from", client->user);
2196                         iks_insert_attrib(iq, "to", pak->from->full);
2197                         iks_insert_attrib(iq, "id", pak->id);
2198                         iks_insert_attrib(iq, "type", "result");
2199                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
2200                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
2201                         iks_insert_attrib(confirm, "node", "confirmaccount");
2202                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
2203                         iks_insert_attrib(confirm, "jid", client->user);
2204                         iks_insert_node(iq, query);
2205                         iks_insert_node(query, confirm);
2206                         ast_aji_send(client, iq);
2207                 } else {
2208                         ast_log(LOG_ERROR, "Out of memory.\n");
2209                 }
2210
2211                 iks_delete(iq);
2212                 iks_delete(query);
2213                 iks_delete(confirm);
2214
2215         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
2216                 iks *iq, *query, *feature;
2217
2218                 iq = iks_new("iq");
2219                 query = iks_new("query");
2220                 feature = iks_new("feature");
2221
2222                 if (iq && query && feature && client) {
2223                         iks_insert_attrib(iq, "from", client->user);
2224                         iks_insert_attrib(iq, "to", pak->from->full);
2225                         iks_insert_attrib(iq, "id", pak->id);
2226                         iks_insert_attrib(iq, "type", "result");
2227                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2228                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
2229                         iks_insert_node(iq, query);
2230                         iks_insert_node(query, feature);
2231                         ast_aji_send(client, iq);
2232                 } else {
2233                         ast_log(LOG_ERROR, "Out of memory.\n");
2234                 }
2235
2236                 iks_delete(iq);
2237                 iks_delete(query);
2238                 iks_delete(feature);
2239         }
2240
2241         ASTOBJ_UNREF(client, aji_client_destroy);
2242         return IKS_FILTER_EAT;
2243 }
2244
2245 /*!
2246  * \internal
2247  * \brief Handles \verbatim <iq> \endverbatim stanzas.
2248  * \param client the configured XMPP client we use to connect to a XMPP server
2249  * \param node iks
2250  * \return void.
2251  */
2252 static void aji_handle_iq(struct aji_client *client, iks *node)
2253 {
2254         /*Nothing to see here */
2255 }
2256
2257 /*!
2258  * \internal
2259  * \brief Handles \verbatim <message>\endverbatim stanzas.
2260  * Adds the incoming message to the client's message list.
2261  * \param client the configured XMPP client we use to connect to a XMPP server
2262  * \param pak ikspak the node
2263  */
2264 static void aji_handle_message(struct aji_client *client, ikspak *pak)
2265 {
2266         struct aji_message *insert;
2267         int deleted = 0;
2268         struct ast_msg *msg;
2269
2270         ast_debug(3, "client %s received a message\n", client->name);
2271
2272         if (!(insert = ast_calloc(1, sizeof(*insert)))) {
2273                 return;
2274         }
2275
2276         insert->arrived = ast_tvnow();
2277
2278         /* wake up threads waiting for messages */
2279         ast_mutex_lock(&messagelock);
2280         ast_cond_broadcast(&message_received_condition);
2281         ast_mutex_unlock(&messagelock);
2282
2283         if (iks_find_cdata(pak->x, "body")) {
2284                 insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
2285         }
2286         if (pak->id) {
2287                 ast_copy_string(insert->id, pak->id, sizeof(insert->id));
2288         }
2289         if (pak->from){
2290                 /* insert will furtherly be added to message list */
2291                 insert->from = ast_strdup(pak->from->full);
2292                 if (!insert->from) {
2293                         ast_log(LOG_ERROR, "Memory allocation failure\n");
2294                         return;
2295                 }
2296                 ast_debug(3, "message comes from %s\n", insert->from);
2297         }
2298
2299         if (client->send_to_dialplan) {
2300                 if ((msg = ast_msg_alloc())) {
2301                         int res;
2302
2303                         res = ast_msg_set_to(msg, "xmpp:%s", client->user);
2304                         res |= ast_msg_set_from(msg, "xmpp:%s", insert->from);
2305                         res |= ast_msg_set_body(msg, "%s", insert->message);
2306                         res |= ast_msg_set_context(msg, "%s", client->context);
2307
2308                         if (res) {
2309                                 ast_msg_destroy(msg);
2310                         } else {
2311                                 ast_msg_queue(msg);
2312                         }
2313
2314                         msg = NULL;
2315                 }
2316         }
2317
2318         /* remove old messages received from this JID
2319          * and insert received message */
2320         deleted = delete_old_messages(client, pak->from->partial);
2321         ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
2322         AST_LIST_LOCK(&client->messages);
2323         AST_LIST_INSERT_HEAD(&client->messages, insert, list);
2324         AST_LIST_UNLOCK(&client->messages);
2325 }
2326
2327 /*!
2328  * \internal
2329  * \brief handles \verbatim <presence>\endverbatim stanzas.
2330  * \param client the configured XMPP client we use to connect to a XMPP server
2331  * \param pak ikspak
2332  */
2333 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
2334 {
2335         int status, priority;
2336         struct aji_buddy *buddy;
2337         struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
2338         char *ver, *node, *descrip, *type;
2339
2340         if (client->state != AJI_CONNECTED)
2341                 aji_create_buddy(pak->from->partial, client);
2342
2343         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2344         if (!buddy && pak->from->partial) {
2345                 /* allow our jid to be used to log in with another resource */
2346                 if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
2347                         aji_create_buddy(pak->from->partial, client);
2348                 else
2349                         ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
2350                 return;
2351         }
2352         type = iks_find_attrib(pak->x, "type");
2353         if (client->component && type &&!strcasecmp("probe", type)) {
2354                 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2355                 ast_verbose("what i was looking for \n");
2356         }
2357         ASTOBJ_WRLOCK(buddy);
2358         status = (pak->show) ? pak->show : 6;
2359         priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
2360         tmp = buddy->resources;
2361         descrip = ast_strdup(iks_find_cdata(pak->x, "status"));
2362
2363         while (tmp && pak->from->resource) {
2364                 if (!strcasecmp(tmp->resource, pak->from->resource)) {
2365                         tmp->status = status;
2366                         if (tmp->description) {
2367                                 ast_free(tmp->description);
2368                         }
2369                         tmp->description = descrip;
2370                         found = tmp;
2371                         if (status == 6) {      /* Sign off Destroy resource */
2372                                 if (last && found->next) {
2373                                         last->next = found->next;
2374                                 } else if (!last) {
2375                                         if (found->next) {
2376                                                 buddy->resources = found->next;
2377                                         } else {
2378                                                 buddy->resources = NULL;
2379                                         }
2380                                 } else if (!found->next) {
2381                                         if (last) {
2382                                                 last->next = NULL;
2383                                         } else {
2384                                                 buddy->resources = NULL;
2385                                         }
2386                                 }
2387                                 ast_free(found);
2388                                 found = NULL;
2389                                 break;
2390                         }
2391                         /* resource list is sorted by descending priority */
2392                         if (tmp->priority != priority) {
2393                                 found->priority = priority;
2394                                 if (!last && !found->next) {
2395                                         /* resource was found to be unique,
2396                                            leave loop */
2397                                         break;
2398                                 }
2399                                 /* search for resource in our list
2400                                    and take it out for the moment */
2401                                 if (last) {
2402                                         last->next = found->next;
2403                                 } else {
2404                                         buddy->resources = found->next;
2405                                 }
2406
2407                                 last = NULL;
2408                                 tmp = buddy->resources;
2409                                 if (!buddy->resources) {
2410                                         buddy->resources = found;
2411                                 }
2412                                 /* priority processing */
2413                                 while (tmp) {
2414                                         /* insert resource back according to
2415                                            its priority value */
2416                                         if (found->priority > tmp->priority) {
2417                                                 if (last) {
2418                                                         /* insert within list */
2419                                                         last->next = found;
2420                                                 }
2421                                                 found->next = tmp;
2422                                                 if (!last) {
2423                                                         /* insert on top */
2424                                                         buddy->resources = found;
2425                                                 }
2426                                                 break;
2427                                         }
2428                                         if (!tmp->next) {
2429                                                 /* insert at the end of the list */
2430                                                 tmp->next = found;
2431                                                 found->next = NULL;
2432                                                 break;
2433                                         }
2434                                         last = tmp;
2435                                         tmp = tmp->next;
2436                                 }
2437                         }
2438                         break;
2439                 }
2440                 last = tmp;
2441                 tmp = tmp->next;
2442         }
2443
2444         /* resource not found in our list, create it */
2445         if (!found && status != 6 && pak->from->resource) {
2446                 found = ast_calloc(1, sizeof(*found));
2447
2448                 if (!found) {
2449                         ast_log(LOG_ERROR, "Out of memory!\n");
2450                         return;
2451                 }
2452                 ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
2453                 found->status = status;
2454                 found->description = descrip;
2455                 found->priority = priority;
2456                 found->next = NULL;
2457                 last = NULL;
2458                 tmp = buddy->resources;
2459                 while (tmp) {
2460                         if (found->priority > tmp->priority) {
2461                                 if (last) {
2462                                         last->next = found;
2463                                 }
2464                                 found->next = tmp;
2465                                 if (!last) {
2466                                         buddy->resources = found;
2467                                 }
2468                                 break;
2469                         }
2470                         if (!tmp->next) {
2471                                 tmp->next = found;
2472                                 break;
2473                         }
2474                         last = tmp;
2475                         tmp = tmp->next;
2476                 }
2477                 if (!tmp) {
2478                         buddy->resources = found;
2479                 }
2480         }
2481
2482         ASTOBJ_UNLOCK(buddy);
2483         ASTOBJ_UNREF(buddy, aji_buddy_destroy);
2484
2485         node = iks_find_attrib(iks_find(pak->x, "c"), "node");
2486         ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
2487
2488         /* handle gmail client's special caps:c tag */
2489         if (!node && !ver) {
2490                 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
2491                 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
2492         }
2493
2494         /* retrieve capabilites of the new resource */
2495         if (status != 6 && found && !found->cap) {
2496                 found->cap = aji_find_version(node, ver, pak);
2497                 if (gtalk_yuck(pak->x)) { /* gtalk should do discover */
2498                         found->cap->jingle = 1;
2499                 }
2500                 if (found->cap->jingle) {
2501                         ast_debug(1, "Special case for google till they support discover.\n");
2502                 } else {
2503                         iks *iq, *query;
2504                         iq = iks_new("iq");
2505                         query = iks_new("query");
2506                         if (query && iq) {
2507                                 iks_insert_attrib(iq, "type", "get");
2508                                 iks_insert_attrib(iq, "to", pak->from->full);
2509                                 iks_insert_attrib(iq, "from", client->jid->full);
2510                                 iks_insert_attrib(iq, "id", client->mid);
2511                                 ast_aji_increment_mid(client->mid);
2512                                 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
2513                                 iks_insert_node(iq, query);
2514                                 ast_aji_send(client, iq);
2515                         } else {
2516                                 ast_log(LOG_ERROR, "Out of memory.\n");
2517                         }
2518                         iks_delete(query);
2519                         iks_delete(iq);
2520                 }
2521         }
2522         switch (pak->subtype) {
2523         case IKS_TYPE_AVAILABLE:
2524                 ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
2525                 break;
2526         case IKS_TYPE_UNAVAILABLE:
2527                 ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
2528                 break;
2529         default:
2530                 ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
2531         }
2532         switch (pak->show) {
2533         case IKS_SHOW_UNAVAILABLE:
2534                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2535                 break;
2536         case IKS_SHOW_AVAILABLE:
2537                 ast_debug(3, "JABBER: type is available\n");
2538                 break;
2539         case IKS_SHOW_CHAT:
2540                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2541                 break;
2542         case IKS_SHOW_AWAY:
2543                 ast_debug(3, "JABBER: type is away\n");
2544                 break;
2545         case IKS_SHOW_XA:
2546                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2547                 break;
2548         case IKS_SHOW_DND:
2549                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
2550                 break;
2551         default:
2552                 ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
2553         }
2554
2555         if (found) {
2556                 manager_event(EVENT_FLAG_USER, "JabberStatus",
2557                         "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
2558                         "\r\nDescription: %s\r\n",
2559                         client->name, pak->from->partial, found->resource, found->status,
2560                         found->priority, S_OR(found->description, ""));
2561         } else {
2562                 manager_event(EVENT_FLAG_USER, "JabberStatus",
2563                         "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
2564                         client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
2565         }
2566 }
2567
2568 /*!
2569  * \internal
2570  * \brief handles subscription requests.
2571  * \param client the configured XMPP client we use to connect to a XMPP server
2572  * \param pak ikspak iksemel packet.
2573  * \return void.
2574  */
2575 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
2576 {
2577         iks *presence = NULL, *status = NULL;
2578         struct aji_buddy* buddy = NULL;
2579
2580         switch (pak->subtype) {
2581         case IKS_TYPE_SUBSCRIBE:
2582                 if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
2583                         presence = iks_new("presence");
2584                         status = iks_new("status");
2585                         if (presence && status) {
2586                                 iks_insert_attrib(presence, "type", "subscribed");
2587                                 iks_insert_attrib(presence, "to", pak->from->full);
2588                                 iks_insert_attrib(presence, "from", client->jid->full);
2589                                 if (pak->id)
2590                                         iks_insert_attrib(presence, "id", pak->id);
2591                                 iks_insert_cdata(status, "Asterisk has approved subscription", 0);
2592                                 iks_insert_node(presence, status);
2593                                 ast_aji_send(client, presence);
2594                         } else {
2595                                 ast_log(LOG_ERROR, "Unable to allocate nodes\n");
2596                         }
2597
2598                         iks_delete(presence);
2599                         iks_delete(status);
2600                 }
2601
2602                 if (client->component)
2603                         aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
2604         case IKS_TYPE_SUBSCRIBED:
2605                 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
2606                 if (!buddy && pak->from->partial) {
2607                         aji_create_buddy(pak->from->partial, client);
2608                 }
2609         default:
2610                 if (option_verbose > 4) {
2611                         ast_verbose(VERBOSE_PREFIX_3 "JABBER: This is a subcription of type %i\n", pak->subtype);
2612                 }
2613         }
2614 }
2615
2616 /*!
2617  * \brief sends messages.
2618  * \param client the configured XMPP client we use to connect to a XMPP server
2619  * \param address
2620  * \param message
2621  * \retval IKS_OK success
2622  * \retval -1 failure
2623  */
2624 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
2625 {
2626         return aji_send_raw_chat(client, 0, NULL, address, message);
2627 }
2628
2629 /*!
2630 * \brief sends message to a groupchat
2631 * Prior to sending messages to a groupchat, one must be connected to it.
2632 * \param client the configured XMPP client we use to connect to a XMPP server
2633 * \param nick the nickname we use in the chatroom
2634 * \param address the user the messages must be sent to
2635 * \param message the message to send
2636 * \return IKS_OK on success, any other value on failure
2637 */
2638 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
2639         return aji_send_raw_chat(client, 1, nick, address, message);
2640 }
2641
2642 /*!
2643 * \brief sends messages.
2644 * \param client the configured XMPP client we use to connect to a XMPP server
2645 * \param groupchat 
2646 * \param nick the nickname we use in chatrooms
2647 * \param address
2648 * \param message
2649 * \return IKS_OK on success, any other value on failure
2650 */
2651 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
2652 {
2653         int res = 0;
2654         iks *message_packet = NULL;
2655         char from[AJI_MAX_JIDLEN];
2656         /* the nickname is used only in component mode */
2657         if (nick && client->component) {
2658                 snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
2659         } else {
2660                 snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
2661         }
2662
2663         if (client->state != AJI_CONNECTED) {
2664                 ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
2665                 return -1;
2666         }
2667
2668         message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
2669         if (!message_packet) {
2670                 ast_log(LOG_ERROR, "Out of memory.\n");
2671                 return -1;
2672         }
2673         iks_insert_attrib(message_packet, "from", from);
2674         res = ast_aji_send(client, message_packet);
2675         iks_delete(message_packet);
2676
2677         return res;
2678 }
2679
2680 /*!
2681  * \brief create a chatroom.
2682  * \param client the configured XMPP client we use to connect to a XMPP server
2683  * \param room name of room
2684  * \param server name of server
2685  * \param topic topic for the room.
2686  * \return 0.
2687  */
2688 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
2689 {
2690         int res = 0;
2691         iks *iq = NULL;
2692         iq = iks_new("iq");
2693
2694         if (iq && client) {
2695                 iks_insert_attrib(iq, "type", "get");
2696                 iks_insert_attrib(iq, "to", server);
2697                 iks_insert_attrib(iq, "id", client->mid);
2698                 ast_aji_increment_mid(client->mid);
2699                 ast_aji_send(client, iq);
2700         } else {
2701                 ast_log(LOG_ERROR, "Out of memory.\n");
2702         }
2703
2704         iks_delete(iq);
2705
2706         return res;
2707 }
2708
2709 /*!
2710  * \brief join a chatroom.
2711  * \param client the configured XMPP client we use to connect to a XMPP server
2712  * \param room room to join
2713  * \param nick the nickname to use in this room
2714  * \return IKS_OK on success, any other value on failure.
2715  */
2716 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
2717 {
2718         return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
2719 }
2720
2721 /*!
2722  * \brief leave a chatroom.
2723  * \param client the configured XMPP client we use to connect to a XMPP server
2724  * \param room room to leave
2725  * \param nick the nickname used in this room
2726  * \return IKS_OK on success, any other value on failure.
2727  */
2728 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
2729 {
2730         return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
2731 }
2732 /*!
2733  * \brief invite to a chatroom.
2734  * \param client the configured XMPP client we use to connect to a XMPP server
2735  * \param user
2736  * \param room
2737  * \param message
2738  * \return res.
2739  */
2740 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
2741 {
2742         int res = 0;
2743         iks *invite, *body, *namespace;
2744
2745         invite = iks_new("message");
2746         body = iks_new("body");
2747         namespace = iks_new("x");
2748         if (client && invite && body && namespace) {
2749                 iks_insert_attrib(invite, "to", user);
2750                 iks_insert_attrib(invite, "id", client->mid);
2751                 ast_aji_increment_mid(client->mid);
2752                 iks_insert_cdata(body, message, 0);
2753                 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
2754                 iks_insert_attrib(namespace, "jid", room);
2755                 iks_insert_node(invite, body);
2756                 iks_insert_node(invite, namespace);
2757                 res = ast_aji_send(client, invite);
2758         } else {
2759                 ast_log(LOG_ERROR, "Out of memory.\n");
2760         }
2761
2762         iks_delete(body);
2763         iks_delete(namespace);
2764         iks_delete(invite);
2765
2766         return res;
2767 }
2768
2769 /*!
2770  * \internal
2771  * \brief receive message loop.
2772  * \param data void
2773  * \return void.
2774  */
2775 static void *aji_recv_loop(void *data)
2776 {
2777         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2778         int res = IKS_HOOK;
2779
2780         while (res != IKS_OK) {
2781                 ast_debug(3, "JABBER: Connecting.\n");
2782                 res = aji_reconnect(client);
2783                 sleep(4);
2784         }
2785
2786         do {
2787                 if (res == IKS_NET_RWERR || client->timeout == 0) {
2788                         while (res != IKS_OK) {
2789                                 ast_debug(3, "JABBER: reconnecting.\n");
2790                                 res = aji_reconnect(client);
2791                                 sleep(4);
2792                         }
2793                 }
2794
2795                 res = aji_recv(client, 1);
2796
2797                 if (client->state == AJI_DISCONNECTING) {
2798                         ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
2799                         pthread_exit(NULL);
2800                 }
2801
2802                 /* Decrease timeout if no data received, and delete
2803                  * old messages globally */
2804                 if (res == IKS_NET_EXPIRED) {
2805                         client->timeout--;
2806                         delete_old_messages_all(client);
2807                 }
2808                 if (res == IKS_HOOK) {
2809                         ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
2810                 } else if (res == IKS_NET_TLSFAIL) {
2811                         ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
2812                 } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
2813                         res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
2814                         if (res == IKS_OK) {
2815                                 client->timeout = 50;
2816                         } else {
2817                                 ast_log(LOG_WARNING, "JABBER:  Network Timeout\n");
2818                         }
2819                 } else if (res == IKS_NET_RWERR) {
2820                         ast_log(LOG_WARNING, "JABBER: socket read error\n");
2821                 }
2822         } while (client);
2823         ASTOBJ_UNREF(client, aji_client_destroy);
2824         return 0;
2825 }
2826
2827 /*!
2828  * \brief increments the mid field for messages and other events.
2829  * \param mid char.
2830  * \return void.
2831  */
2832 void ast_aji_increment_mid(char *mid)
2833 {
2834         int i = 0;
2835
2836         for (i = strlen(mid) - 1; i >= 0; i--) {
2837                 if (mid[i] != 'z') {
2838                         mid[i] = mid[i] + 1;
2839                         i = 0;
2840                 } else
2841                         mid[i] = 'a';
2842         }
2843 }
2844
2845 #if 0
2846 /*!
2847  * \brief attempts to register to a transport.
2848  * \param aji_client struct, and xml packet.
2849  * \return IKS_FILTER_EAT.
2850  */
2851 /*allows for registering to transport , was too sketch and is out for now. */
2852 static int aji_register_transport(void *data, ikspak *pak)
2853 {
2854         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2855         int res = 0;
2856         struct aji_buddy *buddy = NULL;
2857         iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
2858
2859         if (client && send) {
2860                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2861                         ASTOBJ_RDLOCK(iterator); 
2862                         if (iterator->btype == AJI_TRANS) {
2863                                   buddy = iterator;
2864                         }
2865                         ASTOBJ_UNLOCK(iterator);
2866                 });
2867                 iks_filter_remove_hook(client->f, aji_register_transport);
2868                 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);
2869                 iks_insert_attrib(send, "to", buddy->host);
2870                 iks_insert_attrib(send, "id", client->mid);
2871                 ast_aji_increment_mid(client->mid);
2872                 iks_insert_attrib(send, "from", client->user);
2873                 res = ast_aji_send(client, send);
2874         } else 
2875                 ast_log(LOG_ERROR, "Out of memory.\n");
2876
2877         if (send)
2878                 iks_delete(send);
2879         ASTOBJ_UNREF(client, aji_client_destroy);
2880         return IKS_FILTER_EAT;
2881
2882 }
2883 /*!
2884  * \brief attempts to register to a transport step 2.
2885  * \param aji_client struct, and xml packet.
2886  * \return IKS_FILTER_EAT.
2887  */
2888 /* more of the same blob of code, too wonky for now*/
2889 static int aji_register_transport2(void *data, ikspak *pak)
2890 {
2891         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2892         int res = 0;
2893         struct aji_buddy *buddy = NULL;
2894
2895         iks *regiq = iks_new("iq");
2896         iks *regquery = iks_new("query");
2897         iks *reguser = iks_new("username");
2898         iks *regpass = iks_new("password");
2899
2900         if (client && regquery && reguser && regpass && regiq) {
2901                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2902                         ASTOBJ_RDLOCK(iterator);
2903                         if (iterator->btype == AJI_TRANS)
2904                                 buddy = iterator; ASTOBJ_UNLOCK(iterator);
2905                 });
2906                 iks_filter_remove_hook(client->f, aji_register_transport2);
2907                 iks_insert_attrib(regiq, "to", buddy->host);
2908                 iks_insert_attrib(regiq, "type", "set");
2909                 iks_insert_attrib(regiq, "id", client->mid);
2910                 ast_aji_increment_mid(client->mid);
2911                 iks_insert_attrib(regiq, "from", client->user);
2912                 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2913                 iks_insert_cdata(reguser, buddy->user, 0);
2914                 iks_insert_cdata(regpass, buddy->pass, 0);
2915                 iks_insert_node(regiq, regquery);
2916                 iks_insert_node(regquery, reguser);
2917                 iks_insert_node(regquery, regpass);
2918                 res = ast_aji_send(client, regiq);
2919         } else
2920                 ast_log(LOG_ERROR, "Out of memory.\n");
2921         if (regiq)
2922                 iks_delete(regiq);
2923         if (regquery)
2924                 iks_delete(regquery);
2925         if (reguser)
2926                 iks_delete(reguser);
2927         if (regpass)
2928                 iks_delete(regpass);
2929         ASTOBJ_UNREF(client, aji_client_destroy);
2930         return IKS_FILTER_EAT;
2931 }
2932 #endif
2933
2934 /*!
2935  * \internal
2936  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2937  * \param client the configured XMPP client we use to connect to a XMPP server
2938  * \return void.
2939  * \note The messages here should be configurable.
2940  */
2941 static void aji_pruneregister(struct aji_client *client)
2942 {
2943         iks *removeiq = iks_new("iq");
2944         iks *removequery = iks_new("query");
2945         iks *removeitem = iks_new("item");
2946         iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2947         if (!client || !removeiq || !removequery || !removeitem || !send) {
2948                 ast_log(LOG_ERROR, "Out of memory.\n");
2949                 goto safeout;
2950         }
2951
2952         iks_insert_node(removeiq, removequery);
2953         iks_insert_node(removequery, removeitem);
2954         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2955                 ASTOBJ_RDLOCK(iterator);
2956                 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2957                  * be called at the same time */
2958                 if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
2959                         ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2960                                                                  "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2961                                                                  " so I am no longer subscribing to your presence.\n"));
2962                         ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2963                                                                  "GoodBye.  You are no longer in the Asterisk config file so I am removing"
2964                                                                  " your access to my presence.\n"));
2965                         iks_insert_attrib(removeiq, "from", client->jid->full);
2966                         iks_insert_attrib(removeiq, "type", "set");
2967                         iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2968                         iks_insert_attrib(removeitem, "jid", iterator->name);
2969                         iks_insert_attrib(removeitem, "subscription", "remove");
2970                         ast_aji_send(client, removeiq);
2971                 } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2972                         ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
2973                                                                  "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2974                         ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2975                 }
2976                 ASTOBJ_UNLOCK(iterator);
2977         });
2978
2979  safeout:
2980         iks_delete(removeiq);
2981         iks_delete(removequery);
2982         iks_delete(removeitem);
2983         iks_delete(send);
2984
2985         ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy);
2986 }
2987
2988 /*!
2989  * \internal
2990  * \brief filters the roster packet we get back from server.
2991  * \param data void
2992  * \param pak ikspak iksemel packet.
2993  * \return IKS_FILTER_EAT.
2994  */
2995 static int aji_filter_roster(void *data, ikspak *pak)
2996 {
2997         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2998         int flag = 0;
2999         iks *x = NULL;
3000         struct aji_buddy *buddy;
3001
3002         client->state = AJI_CONNECTED;
3003         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3004                 ASTOBJ_RDLOCK(iterator);
3005                 x = iks_child(pak->query);
3006                 flag = 0;
3007                 while (x) {
3008                         if (!iks_strcmp(iks_name(x), "item")) {
3009                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
3010                                         flag = 1;
3011                                         ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
3012                                 }
3013                         }
3014                         x = iks_next(x);
3015                 }
3016                 if (!flag) {
3017                         ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
3018                 }
3019                 iks_delete(x);
3020
3021                 ASTOBJ_UNLOCK(iterator);
3022         });
3023
3024         x = iks_child(pak->query);
3025         while (x) {
3026                 flag = 0;
3027                 if (iks_strcmp(iks_name(x), "item") == 0) {
3028                         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
3029                                 ASTOBJ_RDLOCK(iterator);
3030                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
3031                                         flag = 1;
3032                                 ASTOBJ_UNLOCK(iterator);
3033                         });
3034
3035                         if (flag) {
3036                                 /* found buddy, don't create a new one */
3037                                 x = iks_next(x);
3038                                 continue;
3039                         }
3040
3041                         buddy = ast_calloc(1, sizeof(*buddy));
3042                         if (!buddy) {
3043                                 ast_log(LOG_WARNING, "Out of memory\n");
3044                                 return 0;
3045                         }
3046                         ASTOBJ_INIT(buddy);
3047                         ASTOBJ_WRLOCK(buddy);
3048                         ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
3049                         ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
3050                         if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
3051                                 ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
3052                                 ASTOBJ_MARK(buddy);
3053                         } else if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
3054                                 /* subscribe to buddy's presence only
3055                                    if we really need to */
3056                                 ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
3057                         }
3058                         ASTOBJ_UNLOCK(buddy);
3059                         if (buddy) {
3060                                 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
3061                                 ASTOBJ_UNREF(buddy, aji_buddy_destroy);
3062                         }
3063                 }
3064                 x = iks_next(x);
3065         }
3066
3067         iks_delete(x);
3068         aji_pruneregister(client);
3069
3070         ASTOBJ_UNREF(client, aji_client_destroy);
3071         return IKS_FILTER_EAT;