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