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