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