432b4a292f403e7536b733e99ed143c23376a546
[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_AUTOPRUNE | 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                                 asprintf(&handshake, "<handshake>%s</handshake>", shasum);
991                                 if (handshake) {
992                                         aji_send_raw(client, handshake);
993                                         ast_free(handshake);
994                                         handshake = NULL;
995                                 }
996                                 client->state = AJI_CONNECTING;
997                                 if(aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
998                                         client->state = AJI_CONNECTED;
999                                 else
1000                                         ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
1001                                 break;
1002                         }
1003                         break;
1004
1005                 case IKS_NODE_NORMAL:
1006                         break;
1007
1008                 case IKS_NODE_ERROR:
1009                         ast_log(LOG_ERROR, "JABBER: Node Error\n");
1010                         ASTOBJ_UNREF(client, aji_client_destroy);
1011                         return IKS_HOOK;
1012
1013                 case IKS_NODE_STOP:
1014                         ast_log(LOG_WARNING, "JABBER: Disconnected\n");
1015                         ASTOBJ_UNREF(client, aji_client_destroy);
1016                         return IKS_HOOK;
1017                 }
1018         }
1019
1020         switch (pak->type) {
1021         case IKS_PAK_NONE:
1022                 ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
1023                 break;
1024         case IKS_PAK_MESSAGE:
1025                 aji_handle_message(client, pak);
1026                 ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
1027                 break;
1028         case IKS_PAK_PRESENCE:
1029                 aji_handle_presence(client, pak);
1030                 ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
1031                 break;
1032         case IKS_PAK_S10N:
1033                 aji_handle_subscribe(client, pak);
1034                 ast_debug(1, "JABBER: Handling paktype S10N\n");
1035                 break;
1036         case IKS_PAK_IQ:
1037                 ast_debug(1, "JABBER: Handling paktype IQ\n");
1038                 aji_handle_iq(client, node);
1039                 break;
1040         default:
1041                 ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
1042                 break;
1043         }
1044         
1045         iks_filter_packet(client->f, pak);
1046
1047         if (node)
1048                 iks_delete(node);
1049
1050         ASTOBJ_UNREF(client, aji_client_destroy);
1051         return IKS_OK;
1052 }
1053 /*!
1054  * \brief Unknown
1055  * \param data void
1056  * \param pak ikspak
1057  * \return IKS_FILTER_EAT.
1058 */
1059 static int aji_register_approve_handler(void *data, ikspak *pak)
1060 {
1061         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1062         iks *iq = NULL, *presence = NULL, *x = NULL;
1063
1064         iq = iks_new("iq");
1065         presence = iks_new("presence");
1066         x = iks_new("x");
1067         if (client && iq && presence && x) {
1068                 if (!iks_find(pak->query, "remove")) {
1069                         iks_insert_attrib(iq, "from", client->jid->full);
1070                         iks_insert_attrib(iq, "to", pak->from->full);
1071                         iks_insert_attrib(iq, "id", pak->id);
1072                         iks_insert_attrib(iq, "type", "result");
1073                         ast_aji_send(client, iq);
1074
1075                         iks_insert_attrib(presence, "from", client->jid->full);
1076                         iks_insert_attrib(presence, "to", pak->from->partial);
1077                         iks_insert_attrib(presence, "id", client->mid);
1078                         ast_aji_increment_mid(client->mid);
1079                         iks_insert_attrib(presence, "type", "subscribe");
1080                         iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
1081                         iks_insert_node(presence, x);
1082                         ast_aji_send(client, presence); 
1083                 }
1084         } else {
1085                 ast_log(LOG_ERROR, "Out of memory.\n");
1086         }
1087
1088
1089         iks_delete(iq);
1090         iks_delete(presence);
1091         iks_delete(x);
1092         
1093         ASTOBJ_UNREF(client, aji_client_destroy);
1094         return IKS_FILTER_EAT;
1095 }
1096 /*!
1097  * \brief register handler for incoming querys (IQ's)
1098  * \param data incoming aji_client request
1099  * \param pak ikspak
1100  * \return IKS_FILTER_EAT.
1101 */
1102 static int aji_register_query_handler(void *data, ikspak *pak)
1103 {
1104         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1105         struct aji_buddy *buddy = NULL; 
1106         char *node = NULL;
1107         iks *iq = NULL, *query = NULL;
1108
1109         client = (struct aji_client *) data;
1110
1111         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1112         if (!buddy) {
1113                 iks  *error = NULL, *notacceptable = NULL;
1114
1115                 ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
1116                 iq = iks_new("iq");
1117                 query = iks_new("query");
1118                 error = iks_new("error");
1119                 notacceptable = iks_new("not-acceptable");
1120                 if(iq && query && error && notacceptable) {
1121                         iks_insert_attrib(iq, "type", "error");
1122                         iks_insert_attrib(iq, "from", client->user);
1123                         iks_insert_attrib(iq, "to", pak->from->full);
1124                         iks_insert_attrib(iq, "id", pak->id);
1125                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1126                         iks_insert_attrib(error, "code" , "406");
1127                         iks_insert_attrib(error, "type", "modify");
1128                         iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
1129                         iks_insert_node(iq, query);
1130                         iks_insert_node(iq, error);
1131                         iks_insert_node(error, notacceptable);
1132                         ast_aji_send(client, iq);
1133                 } else {
1134                         ast_log(LOG_ERROR, "Out of memory.\n");
1135                 }
1136
1137                 iks_delete(error);
1138                 iks_delete(notacceptable);
1139         } else  if (!(node = iks_find_attrib(pak->query, "node"))) {
1140                 iks *instructions = NULL;
1141                 char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
1142                 iq = iks_new("iq");
1143                 query = iks_new("query");
1144                 instructions = iks_new("instructions");
1145                 if (iq && query && instructions && client) {
1146                         iks_insert_attrib(iq, "from", client->user);
1147                         iks_insert_attrib(iq, "to", pak->from->full);
1148                         iks_insert_attrib(iq, "id", pak->id);
1149                         iks_insert_attrib(iq, "type", "result");
1150                         iks_insert_attrib(query, "xmlns", "jabber:iq:register");
1151                         iks_insert_cdata(instructions, explain, 0);
1152                         iks_insert_node(iq, query);
1153                         iks_insert_node(query, instructions);
1154                         ast_aji_send(client, iq);
1155                 } else {
1156                         ast_log(LOG_ERROR, "Out of memory.\n");
1157                 }
1158
1159                 iks_delete(instructions);
1160         }
1161         iks_delete(iq);
1162         iks_delete(query);
1163         ASTOBJ_UNREF(client, aji_client_destroy);
1164         return IKS_FILTER_EAT;
1165 }
1166
1167 /*!
1168  * \brief Handles stuff
1169  * \param data void
1170  * \param pak ikspak 
1171  * \return IKS_FILTER_EAT.
1172 */
1173 static int aji_ditems_handler(void *data, ikspak *pak)
1174 {
1175         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1176         char *node = NULL;
1177
1178         if (!(node = iks_find_attrib(pak->query, "node"))) {
1179                 iks *iq = NULL, *query = NULL, *item = NULL;
1180                 iq = iks_new("iq");
1181                 query = iks_new("query");
1182                 item = iks_new("item");
1183
1184                 if (iq && query && item) {
1185                         iks_insert_attrib(iq, "from", client->user);
1186                         iks_insert_attrib(iq, "to", pak->from->full);
1187                         iks_insert_attrib(iq, "id", pak->id);
1188                         iks_insert_attrib(iq, "type", "result");
1189                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1190                         iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
1191                         iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
1192                         iks_insert_attrib(item, "jid", client->user);
1193
1194                         iks_insert_node(iq, query);
1195                         iks_insert_node(query, item);
1196                         ast_aji_send(client, iq);
1197                 } else {
1198                         ast_log(LOG_ERROR, "Out of memory.\n");
1199                 }
1200
1201                 iks_delete(iq);
1202                 iks_delete(query);
1203                 iks_delete(item);
1204
1205         } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
1206                 iks *iq, *query, *confirm;
1207                 iq = iks_new("iq");
1208                 query = iks_new("query");
1209                 confirm = iks_new("item");
1210                 if (iq && query && confirm && client) {
1211                         iks_insert_attrib(iq, "from", client->user);
1212                         iks_insert_attrib(iq, "to", pak->from->full);
1213                         iks_insert_attrib(iq, "id", pak->id);
1214                         iks_insert_attrib(iq, "type", "result");
1215                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1216                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1217                         iks_insert_attrib(confirm, "node", "confirmaccount");
1218                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
1219                         iks_insert_attrib(confirm, "jid", "blog.astjab.org");
1220
1221                         iks_insert_node(iq, query);
1222                         iks_insert_node(query, confirm);
1223                         ast_aji_send(client, iq);
1224                 } else {
1225                         ast_log(LOG_ERROR, "Out of memory.\n");
1226                 }
1227
1228                 iks_delete(iq);
1229                 iks_delete(query);
1230                 iks_delete(confirm);
1231
1232         } else if (!strcasecmp(node, "confirmaccount")) {
1233                 iks *iq = NULL, *query = NULL, *feature = NULL;
1234
1235                 iq = iks_new("iq");
1236                 query = iks_new("query");
1237                 feature = iks_new("feature");
1238
1239                 if (iq && query && feature && client) {
1240                         iks_insert_attrib(iq, "from", client->user);
1241                         iks_insert_attrib(iq, "to", pak->from->full);
1242                         iks_insert_attrib(iq, "id", pak->id);
1243                         iks_insert_attrib(iq, "type", "result");
1244                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1245                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
1246                         iks_insert_node(iq, query);
1247                         iks_insert_node(query, feature);
1248                         ast_aji_send(client, iq);
1249                 } else {
1250                         ast_log(LOG_ERROR, "Out of memory.\n");
1251                 }
1252
1253                 iks_delete(iq);
1254                 iks_delete(query);
1255                 iks_delete(feature);
1256         }
1257
1258         ASTOBJ_UNREF(client, aji_client_destroy);
1259         return IKS_FILTER_EAT;
1260
1261 }
1262 /*!
1263  * \brief Handle add extra info
1264  * \param data void
1265  * \param pak ikspak
1266  * \return IKS_FILTER_EAT
1267 */
1268 static int aji_client_info_handler(void *data, ikspak *pak)
1269 {
1270         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1271         struct aji_resource *resource = NULL;
1272         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1273
1274         resource = aji_find_resource(buddy, pak->from->resource);
1275         if (pak->subtype == IKS_TYPE_RESULT) {
1276                 if (!resource) {
1277                         ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
1278                         ASTOBJ_UNREF(client, aji_client_destroy);
1279                         return IKS_FILTER_EAT;
1280                 }
1281                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
1282                         resource->cap->jingle = 1;
1283                 } else
1284                         resource->cap->jingle = 0;
1285         } else if (pak->subtype == IKS_TYPE_GET) {
1286                 iks *iq, *disco, *ident, *google, *query;
1287                 iq = iks_new("iq");
1288                 query = iks_new("query");
1289                 ident = iks_new("identity");
1290                 disco = iks_new("feature");
1291                 google = iks_new("feature");
1292                 if (iq && ident && disco && google) {
1293                         iks_insert_attrib(iq, "from", client->jid->full);
1294                         iks_insert_attrib(iq, "to", pak->from->full);
1295                         iks_insert_attrib(iq, "type", "result");
1296                         iks_insert_attrib(iq, "id", pak->id);
1297                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
1298                         iks_insert_attrib(ident, "category", "client");
1299                         iks_insert_attrib(ident, "type", "pc");
1300                         iks_insert_attrib(ident, "name", "asterisk");
1301                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
1302                         iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
1303                         iks_insert_node(iq, query);
1304                         iks_insert_node(query, ident);
1305                         iks_insert_node(query, google);
1306                         iks_insert_node(query, disco);
1307                         ast_aji_send(client, iq);
1308                 } else
1309                         ast_log(LOG_ERROR, "Out of Memory.\n");
1310
1311                 iks_delete(iq);
1312                 iks_delete(query);
1313                 iks_delete(ident);
1314                 iks_delete(google);
1315                 iks_delete(disco);
1316         } else if (pak->subtype == IKS_TYPE_ERROR) {
1317                 ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
1318         }
1319         ASTOBJ_UNREF(client, aji_client_destroy);
1320         return IKS_FILTER_EAT;
1321 }
1322 /*!
1323  * \brief Handler of the return info packet
1324  * \param data aji_client
1325  * \param pak ikspak
1326  * \return IKS_FILTER_EAT
1327 */
1328 static int aji_dinfo_handler(void *data, ikspak *pak)
1329 {
1330         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1331         char *node = NULL;
1332         struct aji_resource *resource = NULL;
1333         struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1334
1335         resource = aji_find_resource(buddy, pak->from->resource);
1336         if (pak->subtype == IKS_TYPE_ERROR) {
1337                 ast_log(LOG_WARNING, "Recieved error from a client, turn on jabber debug!\n");
1338                 return IKS_FILTER_EAT;
1339         }
1340         if (pak->subtype == IKS_TYPE_RESULT) {
1341                 if (!resource) {
1342                         ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full);
1343                         ASTOBJ_UNREF(client, aji_client_destroy);
1344                         return IKS_FILTER_EAT;
1345                 }
1346                 if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
1347                         resource->cap->jingle = 1;
1348                 } else
1349                         resource->cap->jingle = 0;
1350         } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
1351                 iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
1352
1353                 iq = iks_new("iq");
1354                 query = iks_new("query");
1355                 identity = iks_new("identity");
1356                 disco = iks_new("feature");
1357                 reg = iks_new("feature");
1358                 commands = iks_new("feature");
1359                 gateway = iks_new("feature");
1360                 version = iks_new("feature");
1361                 vcard = iks_new("feature");
1362                 search = iks_new("feature");
1363
1364                 if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
1365                         iks_insert_attrib(iq, "from", client->user);
1366                         iks_insert_attrib(iq, "to", pak->from->full);
1367                         iks_insert_attrib(iq, "id", pak->id);
1368                         iks_insert_attrib(iq, "type", "result");
1369                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
1370                         iks_insert_attrib(identity, "category", "gateway");
1371                         iks_insert_attrib(identity, "type", "pstn");
1372                         iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
1373                         iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
1374                         iks_insert_attrib(reg, "var", "jabber:iq:register");
1375                         iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
1376                         iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
1377                         iks_insert_attrib(version, "var", "jabber:iq:version");
1378                         iks_insert_attrib(vcard, "var", "vcard-temp");
1379                         iks_insert_attrib(search, "var", "jabber:iq:search");
1380
1381                         iks_insert_node(iq, query);
1382                         iks_insert_node(query, identity);
1383                         iks_insert_node(query, disco);
1384                         iks_insert_node(query, reg);
1385                         iks_insert_node(query, commands);
1386                         iks_insert_node(query, gateway);
1387                         iks_insert_node(query, version);
1388                         iks_insert_node(query, vcard);
1389                         iks_insert_node(query, search);
1390                         ast_aji_send(client, iq);
1391                 } else {
1392                         ast_log(LOG_ERROR, "Out of memory.\n");
1393                 }
1394
1395                 iks_delete(iq);
1396                 iks_delete(query);
1397                 iks_delete(identity);
1398                 iks_delete(disco);
1399                 iks_delete(reg);
1400                 iks_delete(commands);
1401                 iks_delete(gateway);
1402                 iks_delete(version);
1403                 iks_delete(vcard);
1404                 iks_delete(search);
1405
1406         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
1407                 iks *iq, *query, *confirm;
1408                 iq = iks_new("iq");
1409                 query = iks_new("query");
1410                 confirm = iks_new("item");
1411
1412                 if (iq && query && confirm && client) {
1413                         iks_insert_attrib(iq, "from", client->user);
1414                         iks_insert_attrib(iq, "to", pak->from->full);
1415                         iks_insert_attrib(iq, "id", pak->id);
1416                         iks_insert_attrib(iq, "type", "result");
1417                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
1418                         iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
1419                         iks_insert_attrib(confirm, "node", "confirmaccount");
1420                         iks_insert_attrib(confirm, "name", "Confirm AIM account");
1421                         iks_insert_attrib(confirm, "jid", client->user);
1422                         iks_insert_node(iq, query);
1423                         iks_insert_node(query, confirm);
1424                         ast_aji_send(client, iq);
1425                 } else {
1426                         ast_log(LOG_ERROR, "Out of memory.\n");
1427                 }
1428
1429                 iks_delete(iq);
1430                 iks_delete(query);
1431                 iks_delete(confirm);
1432
1433         } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
1434                 iks *iq, *query, *feature;
1435
1436                 iq = iks_new("iq");
1437                 query = iks_new("query");
1438                 feature = iks_new("feature");
1439
1440                 if (iq && query && feature && client) {
1441                         iks_insert_attrib(iq, "from", client->user);
1442                         iks_insert_attrib(iq, "to", pak->from->full);
1443                         iks_insert_attrib(iq, "id", pak->id);
1444                         iks_insert_attrib(iq, "type", "result");
1445                         iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
1446                         iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
1447                         iks_insert_node(iq, query);
1448                         iks_insert_node(query, feature);
1449                         ast_aji_send(client, iq);
1450                 } else {
1451                         ast_log(LOG_ERROR, "Out of memory.\n");
1452                 }
1453
1454                 iks_delete(iq);
1455                 iks_delete(query);
1456                 iks_delete(feature);
1457         }
1458
1459         ASTOBJ_UNREF(client, aji_client_destroy);
1460         return IKS_FILTER_EAT;
1461 }
1462
1463 /*!
1464  * \brief Handles \verbatim <iq> \endverbatim tags.
1465  * \param client the configured XMPP client we use to connect to a XMPP server
1466  * \param node iks 
1467  * \return void.
1468  */
1469 static void aji_handle_iq(struct aji_client *client, iks *node)
1470 {
1471         /*Nothing to see here */
1472 }
1473
1474 /*!
1475  * \brief Handles presence packets.
1476  * \param client the configured XMPP client we use to connect to a XMPP server
1477  * \param pak ikspak the node
1478  */
1479 static void aji_handle_message(struct aji_client *client, ikspak *pak)
1480 {
1481         struct aji_message *insert, *tmp;
1482         int flag = 0;
1483         
1484         if (!(insert = ast_calloc(1, sizeof(*insert))))
1485                 return;
1486         time(&insert->arrived);
1487         if (iks_find_cdata(pak->x, "body"))
1488                 insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
1489         if (pak->id)
1490                 ast_copy_string(insert->id, pak->id, sizeof(insert->message));
1491         if (pak->from)
1492                 insert->from = ast_strdup(pak->from->full);
1493         AST_LIST_LOCK(&client->messages);
1494         AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
1495                 if (flag) {
1496                         AST_LIST_REMOVE_CURRENT(list);
1497                         if (tmp->from)
1498                                 ast_free(tmp->from);
1499                         if (tmp->message)
1500                                 ast_free(tmp->message);
1501                 } else if (difftime(time(NULL), tmp->arrived) >= client->message_timeout) {
1502                         flag = 1;
1503                         AST_LIST_REMOVE_CURRENT(list);
1504                         if (tmp->from)
1505                                 ast_free(tmp->from);
1506                         if (tmp->message)
1507                                 ast_free(tmp->message);
1508                 }
1509         }
1510         AST_LIST_TRAVERSE_SAFE_END;
1511         AST_LIST_INSERT_HEAD(&client->messages, insert, list);
1512         AST_LIST_UNLOCK(&client->messages);
1513 }
1514 /*!
1515  * \brief Check the presence info
1516  * \param client the configured XMPP client we use to connect to a XMPP server
1517  * \param pak ikspak
1518 */
1519 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
1520 {
1521         int status, priority;
1522         struct aji_buddy *buddy;
1523         struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
1524         char *ver, *node, *descrip, *type;
1525         
1526         if(client->state != AJI_CONNECTED)
1527                 aji_create_buddy(pak->from->partial, client);
1528
1529         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1530         if (!buddy && pak->from->partial) {
1531                 /* allow our jid to be used to log in with another resource */
1532                 if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
1533                         aji_create_buddy(pak->from->partial, client);
1534                 else
1535                         ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
1536                 return;
1537         }
1538         type = iks_find_attrib(pak->x, "type");
1539         if(client->component && type &&!strcasecmp("probe", type)) {
1540                 aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
1541                 ast_verbose("what i was looking for \n");
1542         }
1543         ASTOBJ_WRLOCK(buddy);
1544         status = (pak->show) ? pak->show : 6;
1545         priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
1546         tmp = buddy->resources;
1547         descrip = ast_strdup(iks_find_cdata(pak->x,"status"));
1548
1549         while (tmp && pak->from->resource) {
1550                 if (!strcasecmp(tmp->resource, pak->from->resource)) {
1551                         tmp->status = status;
1552                         if (tmp->description) ast_free(tmp->description);
1553                         tmp->description = descrip;
1554                         found = tmp;
1555                         if (status == 6) {      /* Sign off Destroy resource */
1556                                 if (last && found->next) {
1557                                         last->next = found->next;
1558                                 } else if (!last) {
1559                                         if (found->next)
1560                                                 buddy->resources = found->next;
1561                                         else
1562                                                 buddy->resources = NULL;
1563                                 } else if (!found->next) {
1564                                         if (last)
1565                                                 last->next = NULL;
1566                                         else
1567                                                 buddy->resources = NULL;
1568                                 }
1569                                 ast_free(found);
1570                                 found = NULL;
1571                                 break;
1572                         }
1573                         /* resource list is sorted by descending priority */
1574                         if (tmp->priority != priority) {
1575                                 found->priority = priority;
1576                                 if (!last && !found->next)
1577                                         /* resource was found to be unique,
1578                                            leave loop */
1579                                         break;
1580                                 /* search for resource in our list
1581                                    and take it out for the moment */
1582                                 if (last)
1583                                         last->next = found->next;
1584                                 else
1585                                         buddy->resources = found->next;
1586
1587                                 last = NULL;
1588                                 tmp = buddy->resources;
1589                                 if (!buddy->resources)
1590                                         buddy->resources = found;
1591                                 /* priority processing */
1592                                 while (tmp) {
1593                                         /* insert resource back according to 
1594                                            its priority value */
1595                                         if (found->priority > tmp->priority) {
1596                                                 if (last)
1597                                                         /* insert within list */
1598                                                         last->next = found;
1599                                                 found->next = tmp;
1600                                                 if (!last)
1601                                                         /* insert on top */
1602                                                         buddy->resources = found;
1603                                                 break;
1604                                         }
1605                                         if (!tmp->next) {
1606                                                 /* insert at the end of the list */
1607                                                 tmp->next = found;
1608                                                 found->next = NULL;
1609                                                 break;
1610                                         }
1611                                         last = tmp;
1612                                         tmp = tmp->next;
1613                                 }
1614                         }
1615                         break;
1616                 }
1617                 last = tmp;
1618                 tmp = tmp->next;
1619         }
1620
1621         /* resource not found in our list, create it */
1622         if (!found && status != 6 && pak->from->resource) {
1623                 found = ast_calloc(1, sizeof(*found));
1624
1625                 if (!found) {
1626                         ast_log(LOG_ERROR, "Out of memory!\n");
1627                         return;
1628                 }
1629                 ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
1630                 found->status = status;
1631                 found->description = descrip;
1632                 found->priority = priority;
1633                 found->next = NULL;
1634                 last = NULL;
1635                 tmp = buddy->resources;
1636                 while (tmp) {
1637                         if (found->priority > tmp->priority) {
1638                                 if (last)
1639                                         last->next = found;
1640                                 found->next = tmp;
1641                                 if (!last)
1642                                         buddy->resources = found;
1643                                 break;
1644                         }
1645                         if (!tmp->next) {
1646                                 tmp->next = found;
1647                                 break;
1648                         }
1649                         last = tmp;
1650                         tmp = tmp->next;
1651                 }
1652                 if (!tmp)
1653                         buddy->resources = found;
1654         }
1655         
1656         ASTOBJ_UNLOCK(buddy);
1657         ASTOBJ_UNREF(buddy, aji_buddy_destroy);
1658
1659         node = iks_find_attrib(iks_find(pak->x, "c"), "node");
1660         ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
1661
1662         /* handle gmail client's special caps:c tag */
1663         if (!node && !ver) {
1664                 node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
1665                 ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
1666         }
1667
1668         /* retrieve capabilites of the new resource */
1669         if(status !=6 && found && !found->cap) {
1670                 found->cap = aji_find_version(node, ver, pak);
1671                 if(gtalk_yuck(pak->x)) /* gtalk should do discover */
1672                         found->cap->jingle = 1;
1673                 if(found->cap->jingle && option_debug > 4) {
1674                         ast_debug(1,"Special case for google till they support discover.\n");
1675                 }
1676                 else {
1677                         iks *iq, *query;
1678                         iq = iks_new("iq");
1679                         query = iks_new("query");
1680                         if(query && iq)  {
1681                                 iks_insert_attrib(iq, "type", "get");
1682                                 iks_insert_attrib(iq, "to", pak->from->full);
1683                                 iks_insert_attrib(iq,"from", client->jid->full);
1684                                 iks_insert_attrib(iq, "id", client->mid);
1685                                 ast_aji_increment_mid(client->mid);
1686                                 iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
1687                                 iks_insert_node(iq, query);
1688                                 ast_aji_send(client, iq);
1689                                 
1690                         } else
1691                                 ast_log(LOG_ERROR, "Out of memory.\n");
1692                         
1693                         iks_delete(query);
1694                         iks_delete(iq);
1695                 }
1696         }
1697         switch (pak->subtype) {
1698         case IKS_TYPE_AVAILABLE:
1699                 ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
1700                 break;
1701         case IKS_TYPE_UNAVAILABLE:
1702                 ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
1703                 break;
1704         default:
1705                 ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
1706         }
1707         switch (pak->show) {
1708         case IKS_SHOW_UNAVAILABLE:
1709                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
1710                 break;
1711         case IKS_SHOW_AVAILABLE:
1712                 ast_debug(3, "JABBER: type is available\n");
1713                 break;
1714         case IKS_SHOW_CHAT:
1715                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
1716                 break;
1717         case IKS_SHOW_AWAY:
1718                 ast_debug(3, "JABBER: type is away\n");
1719                 break;
1720         case IKS_SHOW_XA:
1721                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
1722                 break;
1723         case IKS_SHOW_DND:
1724                 ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
1725                 break;
1726         default:
1727                 ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
1728         }
1729 }
1730
1731 /*!
1732  * \brief handles subscription requests.
1733  * \param client the configured XMPP client we use to connect to a XMPP server
1734  * \param pak ikspak iksemel packet.
1735  * \return void.
1736  */
1737 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
1738 {
1739         iks *presence = NULL, *status = NULL;
1740         struct aji_buddy* buddy = NULL;
1741
1742         switch (pak->subtype) { 
1743         case IKS_TYPE_SUBSCRIBE:
1744                 presence = iks_new("presence");
1745                 status = iks_new("status");
1746                 if (presence && status) {
1747                         iks_insert_attrib(presence, "type", "subscribed");
1748                         iks_insert_attrib(presence, "to", pak->from->full);
1749                         iks_insert_attrib(presence, "from", client->jid->full);
1750                         if (pak->id)
1751                                 iks_insert_attrib(presence, "id", pak->id);
1752                         iks_insert_cdata(status, "Asterisk has approved subscription", 0);
1753                         iks_insert_node(presence, status);
1754                         ast_aji_send(client, presence);
1755                 } else
1756                         ast_log(LOG_ERROR, "Unable to allocate nodes\n");
1757
1758                 iks_delete(presence);
1759                 iks_delete(status);
1760
1761                 if (client->component)
1762                         aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
1763         case IKS_TYPE_SUBSCRIBED:
1764                 buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
1765                 if (!buddy && pak->from->partial) {
1766                         aji_create_buddy(pak->from->partial, client);
1767                 }
1768         default:
1769                 if (option_verbose > 4) {
1770                         ast_verbose(VERBOSE_PREFIX_3 "JABBER: This is a subcription of type %i\n", pak->subtype);
1771                 }
1772         }
1773 }
1774
1775 /*!
1776  * \brief sends messages.
1777  * \param client the configured XMPP client we use to connect to a XMPP server
1778  * \param address
1779  * \param message
1780  * \return 1.
1781  */
1782 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
1783 {
1784         int res = 0;
1785         iks *message_packet = NULL;
1786         if (client->state == AJI_CONNECTED) {
1787                 message_packet = iks_make_msg(IKS_TYPE_CHAT, address, message);
1788                 if (message_packet) {
1789                         iks_insert_attrib(message_packet, "from", client->jid->full);
1790                         res = ast_aji_send(client, message_packet);
1791                 } else {
1792                         ast_log(LOG_ERROR, "Out of memory.\n");
1793                 }
1794
1795                 iks_delete(message_packet);
1796         } else
1797                 ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
1798         return 1;
1799 }
1800
1801 /*!
1802  * \brief create a chatroom.
1803  * \param client the configured XMPP client we use to connect to a XMPP server
1804  * \param room name of room
1805  * \param server name of server
1806  * \param topic topic for the room.
1807  * \return 0.
1808  */
1809 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
1810 {
1811         int res = 0;
1812         iks *iq = NULL;
1813         iq = iks_new("iq");
1814
1815         if (iq && client) {
1816                 iks_insert_attrib(iq, "type", "get");
1817                 iks_insert_attrib(iq, "to", server);
1818                 iks_insert_attrib(iq, "id", client->mid);
1819                 ast_aji_increment_mid(client->mid);
1820                 ast_aji_send(client, iq);
1821         } else 
1822                 ast_log(LOG_ERROR, "Out of memory.\n");
1823
1824         iks_delete(iq);
1825
1826         return res;
1827 }
1828
1829 /*!
1830  * \brief join a chatroom.
1831  * \param client the configured XMPP client we use to connect to a XMPP server
1832  * \param room room to join
1833  * \return res.
1834  */
1835 int ast_aji_join_chat(struct aji_client *client, char *room)
1836 {
1837         int res = 0;
1838         iks *presence = NULL, *priority = NULL;
1839         presence = iks_new("presence");
1840         priority = iks_new("priority");
1841         if (presence && priority && client) {
1842                 iks_insert_cdata(priority, "0", 1);
1843                 iks_insert_attrib(presence, "to", room);
1844                 iks_insert_node(presence, priority);
1845                 res = ast_aji_send(client, presence);
1846                 iks_insert_cdata(priority, "5", 1);
1847                 iks_insert_attrib(presence, "to", room);
1848                 res = ast_aji_send(client, presence);
1849         } else 
1850                 ast_log(LOG_ERROR, "Out of memory.\n");
1851         
1852         iks_delete(presence);
1853         iks_delete(priority);
1854         
1855         return res;
1856 }
1857
1858 /*!
1859  * \brief invite to a chatroom.
1860  * \param client the configured XMPP client we use to connect to a XMPP server
1861  * \param user 
1862  * \param room
1863  * \param message
1864  * \return res.
1865  */
1866 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
1867 {
1868         int res = 0;
1869         iks *invite, *body, *namespace;
1870
1871         invite = iks_new("message");
1872         body = iks_new("body");
1873         namespace = iks_new("x");
1874         if (client && invite && body && namespace) {
1875                 iks_insert_attrib(invite, "to", user);
1876                 iks_insert_attrib(invite, "id", client->mid);
1877                 ast_aji_increment_mid(client->mid);
1878                 iks_insert_cdata(body, message, 0);
1879                 iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
1880                 iks_insert_attrib(namespace, "jid", room);
1881                 iks_insert_node(invite, body);
1882                 iks_insert_node(invite, namespace);
1883                 res = ast_aji_send(client, invite);
1884         } else 
1885                 ast_log(LOG_ERROR, "Out of memory.\n");
1886
1887         iks_delete(body);
1888         iks_delete(namespace);
1889         iks_delete(invite);
1890         
1891         return res;
1892 }
1893
1894
1895 /*!
1896  * \brief receive message loop.
1897  * \param data void
1898  * \return void.
1899  */
1900 static void *aji_recv_loop(void *data)
1901 {
1902         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1903         int res = IKS_HOOK;
1904
1905         while(res != IKS_OK) {
1906                 ast_debug(3, "JABBER: Connecting.\n");
1907                 res = aji_reconnect(client);
1908                 sleep(4);
1909         }
1910
1911         do {
1912                 if (res == IKS_NET_RWERR || client->timeout == 0) {
1913                         while(res != IKS_OK) {
1914                                 ast_debug(3, "JABBER: reconnecting.\n");
1915                                 res = aji_reconnect(client);
1916                                 sleep(4);
1917                         }
1918                 }
1919
1920                 res = aji_recv(client, 1);
1921                 
1922                 if (client->state == AJI_DISCONNECTING) {
1923                         ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
1924                         pthread_exit(NULL);
1925                 }
1926
1927                 /* Decrease timeout if no data received */
1928                 if (res == IKS_NET_EXPIRED)
1929                         client->timeout--;
1930
1931                 if (res == IKS_HOOK) 
1932                         ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
1933                 else if (res == IKS_NET_TLSFAIL)
1934                         ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
1935                 else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
1936                         res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
1937                         if(res == IKS_OK)
1938                                 client->timeout = 50;
1939                         else
1940                                 ast_log(LOG_WARNING, "JABBER:  Network Timeout\n");
1941                 } else if (res == IKS_NET_RWERR)
1942                         ast_log(LOG_WARNING, "JABBER: socket read error\n");
1943         } while (client);
1944         ASTOBJ_UNREF(client, aji_client_destroy);
1945         return 0;
1946 }
1947
1948 /*!
1949  * \brief increments the mid field for messages and other events.
1950  * \param mid char.
1951  * \return void.
1952  */
1953 void ast_aji_increment_mid(char *mid)
1954 {
1955         int i = 0;
1956
1957         for (i = strlen(mid) - 1; i >= 0; i--) {
1958                 if (mid[i] != 'z') {
1959                         mid[i] = mid[i] + 1;
1960                         i = 0;
1961                 } else
1962                         mid[i] = 'a';
1963         }
1964 }
1965
1966 #if 0
1967 /*!
1968  * \brief attempts to register to a transport.
1969  * \param aji_client struct, and xml packet.
1970  * \return IKS_FILTER_EAT.
1971  */
1972 /*allows for registering to transport , was too sketch and is out for now. */
1973 static int aji_register_transport(void *data, ikspak *pak)
1974 {
1975         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
1976         int res = 0;
1977         struct aji_buddy *buddy = NULL;
1978         iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
1979
1980         if (client && send) {
1981                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
1982                         ASTOBJ_RDLOCK(iterator); 
1983                         if (iterator->btype == AJI_TRANS) {
1984                                   buddy = iterator;
1985                         }
1986                         ASTOBJ_UNLOCK(iterator);
1987                 });
1988                 iks_filter_remove_hook(client->f, aji_register_transport);
1989                 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);
1990                 iks_insert_attrib(send, "to", buddy->host);
1991                 iks_insert_attrib(send, "id", client->mid);
1992                 ast_aji_increment_mid(client->mid);
1993                 iks_insert_attrib(send, "from", client->user);
1994                 res = ast_aji_send(client, send);
1995         } else 
1996                 ast_log(LOG_ERROR, "Out of memory.\n");
1997
1998         if (send)
1999                 iks_delete(send);
2000         ASTOBJ_UNREF(client, aji_client_destroy);
2001         return IKS_FILTER_EAT;
2002
2003 }
2004 /*!
2005  * \brief attempts to register to a transport step 2.
2006  * \param aji_client struct, and xml packet.
2007  * \return IKS_FILTER_EAT.
2008  */
2009 /* more of the same blob of code, too wonky for now*/
2010 static int aji_register_transport2(void *data, ikspak *pak)
2011 {
2012         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2013         int res = 0;
2014         struct aji_buddy *buddy = NULL;
2015
2016         iks *regiq = iks_new("iq");
2017         iks *regquery = iks_new("query");
2018         iks *reguser = iks_new("username");
2019         iks *regpass = iks_new("password");
2020
2021         if (client && regquery && reguser && regpass && regiq) {
2022                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2023                         ASTOBJ_RDLOCK(iterator);
2024                         if (iterator->btype == AJI_TRANS)
2025                                 buddy = iterator; ASTOBJ_UNLOCK(iterator);
2026                 });
2027                 iks_filter_remove_hook(client->f, aji_register_transport2);
2028                 iks_insert_attrib(regiq, "to", buddy->host);
2029                 iks_insert_attrib(regiq, "type", "set");
2030                 iks_insert_attrib(regiq, "id", client->mid);
2031                 ast_aji_increment_mid(client->mid);
2032                 iks_insert_attrib(regiq, "from", client->user);
2033                 iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
2034                 iks_insert_cdata(reguser, buddy->user, 0);
2035                 iks_insert_cdata(regpass, buddy->pass, 0);
2036                 iks_insert_node(regiq, regquery);
2037                 iks_insert_node(regquery, reguser);
2038                 iks_insert_node(regquery, regpass);
2039                 res = ast_aji_send(client, regiq);
2040         } else
2041                 ast_log(LOG_ERROR, "Out of memory.\n");
2042         if (regiq)
2043                 iks_delete(regiq);
2044         if (regquery)
2045                 iks_delete(regquery);
2046         if (reguser)
2047                 iks_delete(reguser);
2048         if (regpass)
2049                 iks_delete(regpass);
2050         ASTOBJ_UNREF(client, aji_client_destroy);
2051         return IKS_FILTER_EAT;
2052 }
2053 #endif
2054
2055 /*!
2056  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
2057  * \param client the configured XMPP client we use to connect to a XMPP server
2058  * \return void.
2059  * \note The messages here should be configurable.
2060  */
2061 static void aji_pruneregister(struct aji_client *client)
2062 {
2063         int res = 0;
2064         iks *removeiq = iks_new("iq");
2065         iks *removequery = iks_new("query");
2066         iks *removeitem = iks_new("item");
2067         iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
2068         if (!client || !removeiq || !removequery || !removeitem || !send) {
2069                 ast_log(LOG_ERROR, "Out of memory.\n");
2070                 goto safeout;
2071         }
2072
2073         iks_insert_node(removeiq, removequery);
2074         iks_insert_node(removequery, removeitem);
2075         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2076                 ASTOBJ_RDLOCK(iterator);
2077                 /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
2078                  * be called at the same time */
2079                 if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) {
2080                         res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
2081                                                                  "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
2082                                                                  " so I am no longer subscribing to your presence.\n"));
2083                         res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
2084                                                                  "GoodBye.  You are no longer in the Asterisk config file so I am removing"
2085                                                                  " your access to my presence.\n"));
2086                         iks_insert_attrib(removeiq, "from", client->jid->full); 
2087                         iks_insert_attrib(removeiq, "type", "set"); 
2088                         iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
2089                         iks_insert_attrib(removeitem, "jid", iterator->name);
2090                         iks_insert_attrib(removeitem, "subscription", "remove");
2091                         res = ast_aji_send(client, removeiq);
2092                 } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
2093                         res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name, 
2094                                                                  "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
2095                         ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
2096                 }
2097                 ASTOBJ_UNLOCK(iterator);
2098         });
2099
2100  safeout:
2101         iks_delete(removeiq);
2102         iks_delete(removequery);
2103         iks_delete(removeitem);
2104         iks_delete(send);
2105         
2106         ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy);
2107 }
2108
2109 /*!
2110  * \brief filters the roster packet we get back from server.
2111  * \param data void
2112  * \param pak ikspak iksemel packet.
2113  * \return IKS_FILTER_EAT.
2114  */
2115 static int aji_filter_roster(void *data, ikspak *pak)
2116 {
2117         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2118         int flag = 0;
2119         iks *x = NULL;
2120         struct aji_buddy *buddy;
2121         
2122         client->state = AJI_CONNECTED;
2123         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2124                 ASTOBJ_RDLOCK(iterator);
2125                 x = iks_child(pak->query);
2126                 flag = 0;
2127                 while (x) {
2128                         if (!iks_strcmp(iks_name(x), "item")) {
2129                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
2130                                         flag = 1;
2131                                         ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
2132                                 }
2133                         }
2134                         x = iks_next(x);
2135                 }
2136                 if (!flag)
2137                         ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
2138                 iks_delete(x);
2139                 
2140                 ASTOBJ_UNLOCK(iterator);
2141         });
2142
2143         x = iks_child(pak->query);
2144         while (x) {
2145                 flag = 0;
2146                 if (iks_strcmp(iks_name(x), "item") == 0) {
2147                         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2148                                 ASTOBJ_RDLOCK(iterator);
2149                                 if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
2150                                         flag = 1;
2151                                 ASTOBJ_UNLOCK(iterator);
2152                         });
2153
2154                         if (flag) {
2155                                 /* found buddy, don't create a new one */
2156                                 x = iks_next(x);
2157                                 continue;
2158                         }
2159                         
2160                         buddy = ast_calloc(1, sizeof(*buddy));
2161                         if (!buddy) {
2162                                 ast_log(LOG_WARNING, "Out of memory\n");
2163                                 return 0;
2164                         }
2165                         ASTOBJ_INIT(buddy);
2166                         ASTOBJ_WRLOCK(buddy);
2167                         ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
2168                         ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
2169                         if(ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
2170                                 ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
2171                                 ASTOBJ_MARK(buddy);
2172                         } else if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
2173                                 /* subscribe to buddy's presence only 
2174                                    if we really need to */
2175                                 ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
2176                         }
2177                         ASTOBJ_UNLOCK(buddy);
2178                         if (buddy) {
2179                                 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2180                                 ASTOBJ_UNREF(buddy, aji_buddy_destroy);
2181                         }
2182                 }
2183                 x = iks_next(x);
2184         }
2185
2186         iks_delete(x);
2187         aji_pruneregister(client);
2188
2189         ASTOBJ_UNREF(client, aji_client_destroy);
2190         return IKS_FILTER_EAT;
2191 }
2192
2193 /*!
2194  * \brief reconnect to jabber server
2195  * \param client the configured XMPP client we use to connect to a XMPP server
2196  * \return res.
2197 */
2198 static int aji_reconnect(struct aji_client *client)
2199 {
2200         int res = 0;
2201
2202         if (client->state)
2203                 client->state = AJI_DISCONNECTED;
2204         client->timeout=50;
2205         if (client->p)
2206                 iks_parser_reset(client->p);
2207         if (client->authorized)
2208                 client->authorized = 0;
2209
2210         res = aji_initialize(client);
2211
2212         return res;
2213 }
2214
2215 /*!
2216  * \brief Get the roster of jabber users
2217  * \param client the configured XMPP client we use to connect to a XMPP server
2218  * \return 1.
2219 */
2220 static int aji_get_roster(struct aji_client *client)
2221 {
2222         iks *roster = NULL;
2223         roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
2224
2225         if(roster) {
2226                 iks_insert_attrib(roster, "id", "roster");
2227                 aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
2228                 ast_aji_send(client, roster);
2229         }
2230
2231         iks_delete(roster);
2232         
2233         return 1;
2234 }
2235
2236 /*!
2237  * \brief connects as a client to jabber server.
2238  * \param data void
2239  * \param pak ikspak iksemel packet
2240  * \return res.
2241  */
2242 static int aji_client_connect(void *data, ikspak *pak)
2243 {
2244         struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
2245         int res = 0;
2246
2247         if (client) {
2248                 if (client->state == AJI_DISCONNECTED) {
2249                         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);
2250                         client->state = AJI_CONNECTING;
2251                         client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
2252                         iks_filter_remove_hook(client->f, aji_client_connect);
2253                         if(!client->component) /*client*/
2254                                 aji_get_roster(client);
2255                 }
2256         } else
2257                 ast_log(LOG_ERROR, "Out of memory.\n");
2258
2259         ASTOBJ_UNREF(client, aji_client_destroy);
2260         return res;
2261 }
2262
2263 /*!
2264  * \brief prepares client for connect.
2265  * \param client the configured XMPP client we use to connect to a XMPP server
2266  * \return 1.
2267  */
2268 static int aji_initialize(struct aji_client *client)
2269 {
2270         int connected = IKS_NET_NOCONN;
2271
2272 #ifdef HAVE_OPENSSL     
2273         /* reset stream flags */
2274         client->stream_flags = 0;
2275 #endif
2276         /* If it's a component, connect to user, otherwise, connect to server */
2277         connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
2278
2279         if (connected == IKS_NET_NOCONN) {
2280                 ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
2281                 return IKS_HOOK;
2282         } else  if (connected == IKS_NET_NODNS) {
2283                 ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to  %s\n", client->name, S_OR(client->serverhost, client->jid->server));
2284                 return IKS_HOOK;
2285         }
2286
2287         return IKS_OK;
2288 }
2289
2290 /*!
2291  * \brief disconnect from jabber server.
2292  * \param client the configured XMPP client we use to connect to a XMPP server
2293  * \return 1.
2294  */
2295 int ast_aji_disconnect(struct aji_client *client)
2296 {
2297         if (client) {
2298                 ast_verb(4, "JABBER: Disconnecting\n");
2299 #ifdef HAVE_OPENSSL
2300                 if (client->stream_flags & SECURE) {
2301                         SSL_shutdown(client->ssl_session);
2302                         SSL_CTX_free(client->ssl_context);
2303                         SSL_free(client->ssl_session);
2304                 }
2305 #endif
2306                 iks_disconnect(client->p);
2307                 iks_parser_delete(client->p);
2308                 ASTOBJ_UNREF(client, aji_client_destroy);
2309         }
2310
2311         return 1;
2312 }
2313
2314 /*!
2315  * \brief set presence of client.
2316  * \param client the configured XMPP client we use to connect to a XMPP server
2317  * \param to user send it to
2318  * \param from user it came from
2319  * \param level
2320  * \param desc
2321  * \return void.
2322  */
2323 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
2324 {
2325         int res = 0;
2326         iks *presence = iks_make_pres(level, desc);
2327         iks *cnode = iks_new("c");
2328         iks *priority = iks_new("priority");
2329         char priorityS[10];
2330
2331         if (presence && cnode && client && priority) {
2332                 if(to)
2333                         iks_insert_attrib(presence, "to", to);
2334                 if(from)
2335                         iks_insert_attrib(presence, "from", from);
2336                 snprintf(priorityS, sizeof(priorityS), "%d", client->priority);
2337                 iks_insert_cdata(priority, priorityS, strlen(priorityS));
2338                 iks_insert_node(presence, priority);
2339                 iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
2340                 iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
2341                 iks_insert_attrib(cnode, "ext", "voice-v1");
2342                 iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
2343                 iks_insert_node(presence, cnode);
2344                 res = ast_aji_send(client, presence);
2345         } else
2346                 ast_log(LOG_ERROR, "Out of memory.\n");
2347
2348         iks_delete(cnode);
2349         iks_delete(presence);
2350         iks_delete(priority);
2351 }
2352
2353 /*!
2354  * \brief Turn on/off console debugging.
2355  * \return CLI_SUCCESS.
2356  */
2357 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2358 {
2359         switch (cmd) {
2360         case CLI_INIT:
2361                 e->command = "jabber set debug {on|off}";
2362                 e->usage =
2363                         "Usage: jabber set debug {on|off}\n"
2364                         "       Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
2365                 return NULL;
2366         case CLI_GENERATE:
2367                 return NULL;
2368         }
2369
2370         if (a->argc != e->args)
2371                 return CLI_SHOWUSAGE;
2372
2373         if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
2374                 ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2375                         ASTOBJ_RDLOCK(iterator); 
2376                         iterator->debug = 1;
2377                         ASTOBJ_UNLOCK(iterator);
2378                 });
2379                 ast_cli(a->fd, "Jabber Debugging Enabled.\n");
2380                 return CLI_SUCCESS;
2381         } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
2382                 ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2383                         ASTOBJ_RDLOCK(iterator); 
2384                         iterator->debug = 0;
2385                         ASTOBJ_UNLOCK(iterator);
2386                 });
2387                 ast_cli(a->fd, "Jabber Debugging Disabled.\n");
2388                 return CLI_SUCCESS;
2389         }
2390         return CLI_SHOWUSAGE; /* defaults to invalid */
2391 }
2392
2393 /*!
2394  * \brief Turn on/off console debugging (deprecated, use aji_do_set_debug).
2395  * \return CLI_SUCCESS.
2396  */
2397 static char *aji_do_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2398 {
2399
2400         switch (cmd) {
2401         case CLI_INIT:
2402                 e->command = "jabber debug [off]";
2403                 e->usage =
2404                         "Usage: jabber debug [off]\n"
2405                         "       Enables/disables dumping of Jabber packets for debugging purposes.\n";
2406                 return NULL;
2407         case CLI_GENERATE:
2408                 return NULL;
2409         }
2410
2411         if (a->argc == 2) {
2412                 ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2413                         ASTOBJ_RDLOCK(iterator); 
2414                         iterator->debug = 1;
2415                         ASTOBJ_UNLOCK(iterator);
2416                 });
2417                 ast_cli(a->fd, "Jabber Debugging Enabled.\n");
2418                 return CLI_SUCCESS;
2419         } else if (a->argc == 3) {
2420                 if (!strncasecmp(a->argv[2], "off", 3)) {
2421                         ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2422                                 ASTOBJ_RDLOCK(iterator); 
2423                                 iterator->debug = 0;
2424                                 ASTOBJ_UNLOCK(iterator);
2425                         });
2426                         ast_cli(a->fd, "Jabber Debugging Disabled.\n");
2427                         return CLI_SUCCESS;
2428                 }
2429         }
2430         return CLI_SHOWUSAGE; /* defaults to invalid */
2431 }
2432
2433 /*!
2434  * \brief Reload jabber module.
2435  * \return CLI_SUCCESS.
2436  */
2437 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2438 {
2439         switch (cmd) {
2440         case CLI_INIT:
2441                 e->command = "jabber reload";
2442                 e->usage =
2443                         "Usage: jabber reload\n"
2444                         "       Reloads the Jabber module.\n";
2445                 return NULL;
2446         case CLI_GENERATE:
2447                 return NULL;
2448         }
2449
2450         aji_reload(1);
2451         ast_cli(a->fd, "Jabber Reloaded.\n");
2452         return CLI_SUCCESS;
2453 }
2454
2455 /*!
2456  * \brief Show client status.
2457  * \return CLI_SUCCESS.
2458  */
2459 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2460 {
2461         char *status;
2462         int count = 0;
2463         
2464         switch (cmd) {
2465         case CLI_INIT:
2466                 e->command = "jabber show connected";
2467                 e->usage =
2468                         "Usage: jabber show connected\n"
2469                         "       Shows state of clients and components\n";
2470                 return NULL;
2471         case CLI_GENERATE:
2472                 return NULL;
2473         }
2474
2475         ast_cli(a->fd, "Jabber Users and their status:\n");
2476         ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2477                 ASTOBJ_RDLOCK(iterator);
2478                 count++;
2479                 switch (iterator->state) {
2480                 case AJI_DISCONNECTED:
2481                         status = "Disconnected";
2482                         break;
2483                 case AJI_CONNECTING:
2484                         status = "Connecting";
2485                         break;
2486                 case AJI_CONNECTED:
2487                         status = "Connected";
2488                         break;
2489                 default:
2490                         status = "Unknown";
2491                 }
2492                 ast_cli(a->fd, "       User: %s     - %s\n", iterator->user, status);
2493                 ASTOBJ_UNLOCK(iterator);
2494         });
2495         ast_cli(a->fd, "----\n");
2496         ast_cli(a->fd, "   Number of users: %d\n", count);
2497         return CLI_SUCCESS;
2498 }
2499
2500 /*!
2501  * \brief Show buddy lists
2502  * \return CLI_SUCCESS.
2503  */
2504 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2505 {
2506         struct aji_resource *resource;
2507         struct aji_client *client;
2508
2509         switch (cmd) {
2510         case CLI_INIT:
2511                 e->command = "jabber show buddies";
2512                 e->usage =
2513                         "Usage: jabber show buddies\n"
2514                         "       Shows buddy lists of our clients\n";
2515                 return NULL;
2516         case CLI_GENERATE:
2517                 return NULL;
2518         }
2519
2520         ast_cli(a->fd, "Jabber buddy lists\n");
2521         ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2522                 ast_cli(a->fd,"Client: %s\n", iterator->user);
2523                 client = iterator;
2524                 ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2525                         ASTOBJ_RDLOCK(iterator);
2526                         ast_cli(a->fd,"\tBuddy:\t%s\n", iterator->name);
2527                         if (!iterator->resources)
2528                                 ast_cli(a->fd,"\t\tResource: None\n");  
2529                         for (resource = iterator->resources; resource; resource = resource->next) {
2530                                 ast_cli(a->fd,"\t\tResource: %s\n", resource->resource);
2531                                 if(resource->cap) {
2532                                         ast_cli(a->fd,"\t\t\tnode: %s\n", resource->cap->parent->node);
2533                                         ast_cli(a->fd,"\t\t\tversion: %s\n", resource->cap->version);
2534                                         ast_cli(a->fd,"\t\t\tJingle capable: %s\n", resource->cap->jingle ? "yes" : "no");
2535                                 }
2536                                 ast_cli(a->fd,"\t\tStatus: %d\n", resource->status);
2537                                 ast_cli(a->fd,"\t\tPriority: %d\n", resource->priority);
2538                         }
2539                         ASTOBJ_UNLOCK(iterator);
2540                 });
2541                 iterator = client;
2542         });
2543         return CLI_SUCCESS;
2544 }
2545
2546 /*!
2547  * \brief Send test message for debugging.
2548  * \return CLI_SUCCESS,CLI_FAILURE.
2549  */
2550 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2551 {
2552         struct aji_client *client;
2553         struct aji_resource *resource;
2554         const char *name = "asterisk";
2555         struct aji_message *tmp;
2556
2557         switch (cmd) {
2558         case CLI_INIT:
2559                 e->command = "jabber test";
2560                 e->usage =
2561                         "Usage: jabber test [client]\n"
2562                         "       Sends test message for debugging purposes.  A specific client\n"
2563                         "       as configured in jabber.conf can be optionally specified.\n";
2564                 return NULL;
2565         case CLI_GENERATE:
2566                 return NULL;
2567         }
2568
2569         if (a->argc > 3)
2570                 return CLI_SHOWUSAGE;
2571         else if (a->argc == 3)
2572                 name = a->argv[2];
2573
2574         if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
2575                 ast_cli(a->fd, "Unable to find client '%s'!\n", name);
2576                 return CLI_FAILURE;
2577         }
2578
2579         /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
2580         ast_aji_send_chat(client, "mogorman@astjab.org", "blahblah");
2581         ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
2582                 ASTOBJ_RDLOCK(iterator);
2583                 ast_verbose("User: %s\n", iterator->name);
2584                 for (resource = iterator->resources; resource; resource = resource->next) {
2585                         ast_verbose("Resource: %s\n", resource->resource);
2586                         if(resource->cap) {
2587                                 ast_verbose("   client: %s\n", resource->cap->parent->node);
2588                                 ast_verbose("   version: %s\n", resource->cap->version);
2589                                 ast_verbose("   Jingle Capable: %d\n", resource->cap->jingle);
2590                         }
2591                         ast_verbose("   Priority: %d\n", resource->priority);
2592                         ast_verbose("   Status: %d\n", resource->status); 
2593                         ast_verbose("   Message: %s\n", S_OR(resource->description,"")); 
2594                 }
2595                 ASTOBJ_UNLOCK(iterator);
2596         });
2597         ast_verbose("\nOooh a working message stack!\n");
2598         AST_LIST_LOCK(&client->messages);
2599         AST_LIST_TRAVERSE(&client->messages, tmp, list) {
2600                 ast_verbose("   Message from: %s with id %s @ %s        %s\n",tmp->from, S_OR(tmp->id,""), ctime(&tmp->arrived), S_OR(tmp->message, ""));
2601         }
2602         AST_LIST_UNLOCK(&client->messages);
2603         ASTOBJ_UNREF(client, aji_client_destroy);
2604
2605         return CLI_SUCCESS;
2606 }
2607
2608 /*!
2609  * \brief creates aji_client structure.
2610  * \param label
2611  * \param var ast_variable
2612  * \param debug 
2613  * \return 0.
2614  */
2615 static int aji_create_client(char *label, struct ast_variable *var, int debug)
2616 {
2617         char *resource;
2618         struct aji_client *client = NULL;
2619         int flag = 0;
2620
2621         client = ASTOBJ_CONTAINER_FIND(&clients,label);
2622         if (!client) {
2623                 flag = 1;
2624                 client = ast_calloc(1, sizeof(*client));
2625                 if (!client) {
2626                         ast_log(LOG_ERROR, "Out of memory!\n");
2627                         return 0;
2628                 }
2629                 ASTOBJ_INIT(client);
2630                 ASTOBJ_WRLOCK(client);
2631                 ASTOBJ_CONTAINER_INIT(&client->buddies);
2632         } else {
2633                 ASTOBJ_WRLOCK(client);
2634                 ASTOBJ_UNMARK(client);
2635         }
2636         ASTOBJ_CONTAINER_MARKALL(&client->buddies);
2637         ast_copy_string(client->name, label, sizeof(client->name));
2638         ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
2639
2640         /* Set default values for the client object */
2641         client->debug = debug;
2642         ast_copy_flags(&client->flags, &globalflags, AST_FLAGS_ALL);
2643         client->port = 5222;
2644         client->usetls = 1;
2645         client->usesasl = 1;
2646         client->forcessl = 0;
2647         client->keepalive = 1;
2648         client->timeout = 50;
2649         client->message_timeout = 100;
2650         AST_LIST_HEAD_INIT(&client->messages);
2651         client->component = 0;
2652         ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage));
2653         client->priority = 0;
2654         client->status = IKS_SHOW_AVAILABLE;
2655
2656         if (flag) {
2657                 client->authorized = 0;
2658                 client->state = AJI_DISCONNECTED;
2659         }
2660         while (var) {
2661                 if (!strcasecmp(var->name, "username"))
2662                         ast_copy_string(client->user, var->value, sizeof(client->user));
2663                 else if (!strcasecmp(var->name, "serverhost"))
2664                         ast_copy_string(client->serverhost, var->value, sizeof(client->serverhost));
2665                 else if (!strcasecmp(var->name, "secret"))
2666                         ast_copy_string(client->password, var->value, sizeof(client->password));
2667                 else if (!strcasecmp(var->name, "statusmessage"))
2668                         ast_copy_string(client->statusmessage, var->value, sizeof(client->statusmessage));
2669                 else if (!strcasecmp(var->name, "port"))
2670                         client->port = atoi(var->value);
2671                 else if (!strcasecmp(var->name, "timeout"))
2672                         client->message_timeout = atoi(var->value);
2673                 else if (!strcasecmp(var->name, "debug"))
2674                         client->debug = (ast_false(var->value)) ? 0 : 1;
2675                 else if (!strcasecmp(var->name, "type")) {
2676                         if (!strcasecmp(var->value, "component"))
2677                                 client->component = 1;
2678                 } else if (!strcasecmp(var->name, "usetls")) {
2679                         client->usetls = (ast_false(var->value)) ? 0 : 1;
2680                 } else if (!strcasecmp(var->name, "usesasl")) {
2681                         client->usesasl = (ast_false(var->value)) ? 0 : 1;
2682                 } else if (!strcasecmp(var->name, "forceoldssl"))
2683                         client->forcessl = (ast_false(var->value)) ? 0 : 1;
2684                 else if (!strcasecmp(var->name, "keepalive"))
2685                         client->keepalive = (ast_false(var->value)) ? 0 : 1;
2686                 else if (!strcasecmp(var->name, "autoprune"))
2687                         ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOPRUNE);
2688                 else if (!strcasecmp(var->name, "autoregister"))
2689                         ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOREGISTER);
2690                 else if (!strcasecmp(var->name, "buddy"))
2691                         aji_create_buddy((char *)var->value, client);
2692                 else if (!strcasecmp(var->name, "priority"))
2693                         client->priority = atoi(var->value);
2694                 else if (!strcasecmp(var->name, "status")) {
2695                         if (!strcasecmp(var->value, "unavailable"))
2696                                 client->status = IKS_SHOW_UNAVAILABLE;
2697                         else
2698                         if (!strcasecmp(var->value, "available")
2699                          || !strcasecmp(var->value, "online"))
2700                                 client->status = IKS_SHOW_AVAILABLE;
2701                         else
2702                         if (!strcasecmp(var->value, "chat")
2703                          || !strcasecmp(var->value, "chatty"))
2704                                 client->status = IKS_SHOW_CHAT;
2705                         else
2706                         if (!strcasecmp(var->value, "away"))
2707                                 client->status = IKS_SHOW_AWAY;
2708                         else
2709                         if (!strcasecmp(var->value, "xa")
2710                          || !strcasecmp(var->value, "xaway"))
2711                                 client->status = IKS_SHOW_XA;
2712                         else
2713                         if (!strcasecmp(var->value, "dnd"))
2714                                 client->status = IKS_SHOW_DND;
2715                         else
2716                         if (!strcasecmp(var->value, "invisible"))
2717                         #ifdef IKS_SHOW_INVISIBLE
2718                                 client->status = IKS_SHOW_INVISIBLE;
2719                         #else
2720                         {
2721                                 ast_log(LOG_WARNING, "Your iksemel doesn't support invisible status: falling back to DND\n");
2722                                 client->status = IKS_SHOW_DND;
2723                         }
2724                         #endif
2725                         else
2726                                 ast_log(LOG_WARNING, "Unknown presence status: %s\n", var->value);
2727                 }
2728         /* no transport support in this version */
2729         /*      else if (!strcasecmp(var->name, "transport"))
2730                                 aji_create_transport(var->value, client);
2731         */
2732                 var = var->next;
2733         }
2734         if (!flag) {
2735                 ASTOBJ_UNLOCK(client);
2736                 ASTOBJ_UNREF(client, aji_client_destroy);
2737                 return 1;
2738         }
2739
2740         ast_copy_string(client->name_space, (client->component) ? "jabber:component:accept" : "jabber:client", sizeof(client->name_space));
2741         client->p = iks_stream_new(client->name_space, client, aji_act_hook);
2742         if (!client->p) {
2743                 ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name);
2744                 return 0;
2745         }
2746         client->stack = iks_stack_new(8192, 8192);
2747         if (!client->stack) {
2748                 ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name);
2749                 return 0;
2750         }
2751         client->f = iks_filter_new();
2752         if (!client->f) {
2753                 ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name);
2754                 return 0;
2755         }
2756         if (!strchr(client->user, '/') && !client->component) { /*client */
2757                 resource = NULL;
2758                 asprintf(&resource, "%s/asterisk", client->user);
2759                 if (resource) {
2760                         client->jid = iks_id_new(client->stack, resource);
2761                         ast_free(resource);
2762                 }
2763         } else
2764                 client->jid = iks_id_new(client->stack, client->user);
2765         if (client->component) {
2766                 iks_filter_add_rule(client->f, aji_dinfo_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2767                 iks_filter_add_rule(client->f, aji_ditems_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
2768                 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);
2769                 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);
2770         } else {
2771                 iks_filter_add_rule(client->f, aji_client_info_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
2772         }
2773         if (!strchr(client->user, '/') && !client->component) { /*client */
2774                 resource = NULL;
2775                 asprintf(&resource, "%s/asterisk", client->user);
2776                 if (resource) {
2777                         client->jid = iks_id_new(client->stack, resource);
2778                         ast_free(resource);
2779                 }
2780         } else
2781                 client->jid = iks_id_new(client->stack, client->user);
2782         iks_set_log_hook(client->p, aji_log_hook);
2783         ASTOBJ_UNLOCK(client);
2784         ASTOBJ_CONTAINER_LINK(&clients,client);
2785         return 1;
2786 }
2787
2788 #if 0
2789 /*!
2790  * \brief creates transport.
2791  * \param label, buddy to dump it into. 
2792  * \return 0.
2793  */
2794 /* no connecting to transports today */
2795 static int aji_create_transport(char *label, struct aji_client *client)
2796 {
2797         char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
2798         struct aji_buddy *buddy = NULL;
2799
2800         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
2801         if (!buddy) {
2802                 buddy = ast_calloc(1, sizeof(*buddy));
2803                 if(!buddy) {
2804                         ast_log(LOG_WARNING, "Out of memory\n");
2805                         return 0;
2806                 }
2807                 ASTOBJ_INIT(buddy);
2808         }
2809         ASTOBJ_WRLOCK(buddy);
2810         server = label;
2811         if ((buddyname = strchr(label, ','))) {
2812                 *buddyname = '\0';
2813                 buddyname++;
2814                 if (buddyname && buddyname[0] != '\0') {
2815                         if ((user = strchr(buddyname, ','))) {
2816                                 *user = '\0';
2817                                 user++;
2818                                 if (user && user[0] != '\0') {
2819                                         if ((pass = strchr(user, ','))) {
2820                                                 *pass = '\0';
2821                                                 pass++;
2822                                                 ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
2823                                                 ast_copy_string(buddy->user, user, sizeof(buddy->user));
2824                                                 ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
2825                                                 ast_copy_string(buddy->server, server, sizeof(buddy->server));
2826                                                 return 1;
2827                                         }
2828                                 }
2829                         }
2830                 }
2831         }
2832         ASTOBJ_UNLOCK(buddy);
2833         ASTOBJ_UNMARK(buddy);
2834         ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2835         return 0;
2836 }
2837 #endif
2838
2839 /*!
2840  * \brief creates buddy.
2841  * \param label char.
2842  * \param client the configured XMPP client we use to connect to a XMPP server 
2843  * \return 1 on success, 0 on failure.
2844  */
2845 static int aji_create_buddy(char *label, struct aji_client *client)
2846 {
2847         struct aji_buddy *buddy = NULL;
2848         int flag = 0;
2849         buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
2850         if (!buddy) {
2851                 flag = 1;
2852                 buddy = ast_calloc(1, sizeof(*buddy));
2853                 if(!buddy) {
2854                         ast_log(LOG_WARNING, "Out of memory\n");
2855                         return 0;
2856                 }
2857                 ASTOBJ_INIT(buddy);
2858         }
2859         ASTOBJ_WRLOCK(buddy);
2860         ast_copy_string(buddy->name, label, sizeof(buddy->name));
2861         ASTOBJ_UNLOCK(buddy);
2862         if(flag)
2863                 ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
2864         else {
2865                 ASTOBJ_UNMARK(buddy);
2866                 ASTOBJ_UNREF(buddy, aji_buddy_destroy);
2867         }
2868         return 1;
2869 }
2870
2871 /*!< load config file. \return 1. */
2872 static int aji_load_config(int reload)
2873 {
2874         char *cat = NULL;
2875         int debug = 1;
2876         struct ast_config *cfg = NULL;
2877         struct ast_variable *var = NULL;
2878         struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2879
2880         if ((cfg = ast_config_load(JABBER_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
2881                 return -1;
2882
2883         /* Reset flags to default value */
2884         ast_set_flag(&globalflags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
2885
2886         if (!cfg) {
2887                 ast_log(LOG_WARNING, "No such configuration file %s\n", JABBER_CONFIG);
2888                 return 0;
2889         }
2890
2891         cat = ast_category_browse(cfg, NULL);
2892         for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
2893                 if (!strcasecmp(var->name, "debug"))
2894                         debug = (ast_false(ast_variable_retrieve(cfg, "general", "debug"))) ? 0 : 1;
2895                 else if (!strcasecmp(var->name, "autoprune"))
2896                         ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOPRUNE);
2897                 else if (!strcasecmp(var->name, "autoregister"))
2898                         ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOREGISTER);
2899         }
2900
2901         while (cat) {
2902                 if (strcasecmp(cat, "general")) {
2903                                 var = ast_variable_browse(cfg, cat);
2904                                 aji_create_client(cat, var, debug);
2905                 }
2906                 cat = ast_category_browse(cfg, cat);
2907         }
2908         ast_config_destroy(cfg); /* or leak memory */
2909         return 1;
2910 }
2911
2912 /*!
2913  * \brief grab a aji_client structure by label name or JID 
2914  * (without the resource string)
2915  * \param name label or JID 
2916  * \return aji_client.
2917  */
2918 struct aji_client *ast_aji_get_client(const char *name)
2919 {
2920         struct aji_client *client = NULL;
2921         char *aux = NULL;
2922
2923         client = ASTOBJ_CONTAINER_FIND(&clients, name);
2924         if (!client && strchr(name, '@')) {
2925                 ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
2926                         aux = ast_strdupa(iterator->user);
2927                         if (strchr(aux, '/')) {
2928                                 /* strip resource for comparison */
2929                                 aux = strsep(&aux, "/");
2930                         }
2931                         if (!strncasecmp(aux, name, strlen(aux))) {
2932                                 client = iterator;
2933                         }                               
2934                 });
2935         }
2936
2937         return client;
2938 }
2939
2940 struct aji_client_container *ast_aji_get_clients(void)
2941 {
2942         return &clients;
2943 }
2944
2945 static char mandescr_jabber_send[] =
2946 "Description: Sends a message to a Jabber Client.\n"
2947 "Variables: \n"
2948 "  Jabber:    Client or transport Asterisk uses to connect to JABBER\n"
2949 "  JID:       XMPP/Jabber JID (Name) of recipient\n" 
2950 "  Message:   Message to be sent to the buddy\n";
2951
2952 /*! 
2953  * \brief  Send a Jabber Message via call from the Manager 
2954  * \param s mansession Manager session
2955  * \param m message Message to send
2956  * \return  0
2957 */
2958 static int manager_jabber_send(struct mansession *s, const struct message *m)
2959 {
2960         struct aji_client *client = NULL;
2961         const char *id = astman_get_header(m,"ActionID");
2962         const char *jabber = astman_get_header(m,"Jabber");
2963         const char *screenname = astman_get_header(m,"ScreenName");
2964         const char *message = astman_get_header(m,"Message");
2965
2966         if (ast_strlen_zero(jabber)) {
2967                 astman_send_error(s, m, "No transport specified");
2968                 return 0;
2969         }
2970         if (ast_strlen_zero(screenname)) {
2971                 astman_send_error(s, m, "No ScreenName specified");
2972                 return 0;
2973         }
2974         if (ast_strlen_zero(message)) {
2975                 astman_send_error(s, m, "No Message specified");
2976                 return 0;
2977         }
2978
2979         astman_send_ack(s, m, "Attempting to send Jabber Message");
2980         client = ast_aji_get_client(jabber);
2981         if (!client) {
2982                 astman_send_error(s, m, "Could not find Sender");
2983                 return 0;
2984         }       
2985         if (strchr(screenname, '@') && message){
2986                 ast_aji_send_chat(client, screenname, message); 
2987                 astman_append(s, "Response: Success\r\n");
2988                 if (!ast_strlen_zero(id))
2989                         astman_append(s, "ActionID: %s\r\n",id);
2990                 return 0;
2991         }
2992         astman_append(s, "Response: Error\r\n");
2993         if (!ast_strlen_zero(id))
2994                 astman_append(s, "ActionID: %s\r\n",id);
2995         return 0;
2996 }
2997
2998 /*! \brief Reload the jabber module */
2999 static int aji_reload(int reload)
3000 {
3001         int res;
3002
3003         ASTOBJ_CONTAINER_MARKALL(&clients);
3004         if (!(res = aji_load_config(reload))) {
3005                 ast_log(LOG_ERROR, "JABBER: Failed to load config.\n");
3006                 return 0;
3007         } else if (res == -1)
3008                 return 1;
3009
3010         ASTOBJ_CONTAINER_PRUNE_MARKED(&clients, aji_client_destroy);
3011         ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
3012                 ASTOBJ_RDLOCK(iterator);
3013                 if(iterator->state == AJI_DISCONNECTED) {
3014                         if (!iterator->thread)
3015                                 ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator);
3016                 } else if (iterator->state == AJI_CONNECTING)
3017                         aji_get_roster(iterator);
3018                 ASTOBJ_UNLOCK(iterator);
3019         });
3020         
3021         return 1;
3022 }
3023
3024 /*! \brief Unload the jabber module */
3025 static int unload_module(void)
3026 {
3027
3028         ast_cli_unregister_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry));
3029         ast_unregister_application(app_ajisend);
3030         ast_unregister_application(app_ajistatus);
3031         ast_manager_unregister("JabberSend");
3032         ast_custom_function_unregister(&jabberstatus_function);
3033         
3034         ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
3035                 ASTOBJ_RDLOCK(iterator);
3036                 ast_debug(3, "JABBER: Releasing and disconnecting client: %s\n", iterator->name);
3037                 iterator->state = AJI_DISCONNECTING;
3038                 ast_aji_disconnect(iterator);
3039                 pthread_join(iterator->thread, NULL);
3040                 ASTOBJ_UNLOCK(iterator);
3041         });
3042
3043         ASTOBJ_CONTAINER_DESTROYALL(&clients, aji_client_destroy);
3044         ASTOBJ_CONTAINER_DESTROY(&clients);
3045         return 0;
3046 }
3047
3048 /*! \brief Unload the jabber module */
3049 static int load_module(void)
3050 {
3051         ASTOBJ_CONTAINER_INIT(&clients);
3052         if(!aji_reload(0))
3053                 return AST_MODULE_LOAD_DECLINE;
3054         ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM, manager_jabber_send,
3055                         "Sends a message to a Jabber Client", mandescr_jabber_send);
3056         ast_register_application(app_ajisend, aji_send_exec, ajisend_synopsis, ajisend_descrip);
3057         ast_register_application(app_ajistatus, aji_status_exec, ajistatus_synopsis, ajistatus_descrip);
3058         ast_cli_register_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry));
3059         ast_custom_function_register(&jabberstatus_function);
3060
3061         return 0;
3062 }
3063
3064 /*! \brief Wrapper for aji_reload */
3065 static int reload(void)
3066 {
3067         aji_reload(1);
3068         return 0;
3069 }
3070
3071 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "AJI - Asterisk Jabber Interface",
3072                 .load = load_module,
3073                 .unload = unload_module,
3074                 .reload = reload,
3075                );