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