Fri Jul 24 00:41:02 2009

Asterisk developer's documentation


res_jabber.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Matt O'Gorman <mogorman@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  * \brief A resource for interfacing Asterisk directly as a client
00021  * or a component to a XMPP/Jabber compliant server.
00022  *
00023  * References:
00024  * - http://www.xmpp.org - The XMPP standards foundation
00025  *
00026  * \extref Iksemel http://code.google.com/p/iksemel/
00027  *
00028  * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
00029  * \todo Dialplan applications need RETURN variable, like JABBERSENDSTATUS
00030  *
00031  */
00032 
00033 /*** MODULEINFO
00034    <depend>iksemel</depend>
00035    <use>openssl</use>
00036  ***/
00037 
00038 #include "asterisk.h"
00039 
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 153710 $")
00041 
00042 #include <ctype.h>
00043 #include <iksemel.h>
00044 
00045 #include "asterisk/channel.h"
00046 #include "asterisk/jabber.h"
00047 #include "asterisk/file.h"
00048 #include "asterisk/config.h"
00049 #include "asterisk/callerid.h"
00050 #include "asterisk/lock.h"
00051 #include "asterisk/cli.h"
00052 #include "asterisk/app.h"
00053 #include "asterisk/pbx.h"
00054 #include "asterisk/md5.h"
00055 #include "asterisk/acl.h"
00056 #include "asterisk/utils.h"
00057 #include "asterisk/module.h"
00058 #include "asterisk/astobj.h"
00059 #include "asterisk/astdb.h"
00060 #include "asterisk/manager.h"
00061 
00062 /*! \todo This should really be renamed to xmpp.conf. For backwards compatibility, we
00063    need to read both files */
00064 #define JABBER_CONFIG "jabber.conf"
00065 
00066 #ifndef FALSE
00067 #define FALSE 0
00068 #endif
00069 
00070 #ifndef TRUE
00071 #define TRUE 1
00072 #endif
00073 
00074 /*-- Forward declarations */
00075 static void aji_buddy_destroy(struct aji_buddy *obj);
00076 static void aji_client_destroy(struct aji_client *obj);
00077 static int aji_send_exec(struct ast_channel *chan, void *data);
00078 static int aji_status_exec(struct ast_channel *chan, void *data);
00079 static int aji_is_secure(struct aji_client *client);
00080 #ifdef HAVE_OPENSSL
00081 static int aji_start_tls(struct aji_client *client);
00082 static int aji_tls_handshake(struct aji_client *client);
00083 #endif
00084 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
00085 static int aji_recv(struct aji_client *client, int timeout);
00086 static int aji_send_header(struct aji_client *client, const char *to);
00087 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
00088 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
00089 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
00090 static int aji_act_hook(void *data, int type, iks *node);
00091 static void aji_handle_iq(struct aji_client *client, iks *node);
00092 static void aji_handle_message(struct aji_client *client, ikspak *pak);
00093 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
00094 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
00095 static void *aji_recv_loop(void *data);
00096 static int aji_initialize(struct aji_client *client);
00097 static int aji_client_connect(void *data, ikspak *pak);
00098 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
00099 static char *aji_do_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00100 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00101 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00102 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00103 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00104 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00105 static int aji_create_client(char *label, struct ast_variable *var, int debug);
00106 static int aji_create_buddy(char *label, struct aji_client *client);
00107 static int aji_reload(int reload);
00108 static int aji_load_config(int reload);
00109 static void aji_pruneregister(struct aji_client *client);
00110 static int aji_filter_roster(void *data, ikspak *pak);
00111 static int aji_get_roster(struct aji_client *client);
00112 static int aji_client_info_handler(void *data, ikspak *pak);
00113 static int aji_dinfo_handler(void *data, ikspak *pak);
00114 static int aji_ditems_handler(void *data, ikspak *pak);
00115 static int aji_register_query_handler(void *data, ikspak *pak);
00116 static int aji_register_approve_handler(void *data, ikspak *pak);
00117 static int aji_reconnect(struct aji_client *client);
00118 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
00119 /* No transports in this version */
00120 /*
00121 static int aji_create_transport(char *label, struct aji_client *client);
00122 static int aji_register_transport(void *data, ikspak *pak);
00123 static int aji_register_transport2(void *data, ikspak *pak);
00124 */
00125 
00126 static struct ast_cli_entry cli_aji_do_debug_deprecated = AST_CLI_DEFINE(aji_do_debug_deprecated, "Enable/disable jabber debugging");
00127 static struct ast_cli_entry aji_cli[] = {
00128    AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug", .deprecate_cmd = &cli_aji_do_debug_deprecated),
00129    AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
00130    AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
00131    AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
00132    AST_CLI_DEFINE(aji_test, "Shows roster, but is generally used for mog's debugging."),
00133 };
00134 
00135 static char *app_ajisend = "JabberSend";
00136 
00137 static char *ajisend_synopsis = "JabberSend(jabber,screenname,message)";
00138 
00139 static char *ajisend_descrip =
00140 "JabberSend(Jabber,ScreenName,Message)\n"
00141 "  Jabber - Client or transport Asterisk uses to connect to Jabber\n" 
00142 "  ScreenName - XMPP/Jabber JID (Name) of recipient\n" 
00143 "  Message - Message to be sent to the budd (UTF8)y\n";
00144 
00145 static char *app_ajistatus = "JabberStatus";
00146 
00147 static char *ajistatus_synopsis = "JabberStatus(Jabber,ScreenName,Variable)";
00148 
00149 static char *ajistatus_descrip =
00150 "JabberStatus(Jabber,ScreenName,Variable)\n"
00151 "  Jabber - Client or transport Asterisk uses to connect to Jabber\n"
00152 "  ScreenName - User Name to retrieve status from.\n"
00153 "  Variable - Variable to store presence in will be 1-6.\n" 
00154 "             In order, 1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline\n" 
00155 "             If not in roster variable will be set to 7\n\n"
00156 "Note: This application is deprecated. Please use the JABBER_STATUS() function instead.\n";
00157 
00158 struct aji_client_container clients;
00159 struct aji_capabilities *capabilities = NULL;
00160 
00161 /*! \brief Global flags, initialized to default values */
00162 static struct ast_flags globalflags = { AJI_AUTOPRUNE | AJI_AUTOREGISTER };
00163 
00164 /*!
00165  * \brief Deletes the aji_client data structure.
00166  * \param obj aji_client The structure we will delete.
00167  * \return void.
00168  */
00169 static void aji_client_destroy(struct aji_client *obj)
00170 {
00171    struct aji_message *tmp;
00172    ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, aji_buddy_destroy);
00173    ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
00174    iks_filter_delete(obj->f);
00175    iks_parser_delete(obj->p);
00176    iks_stack_delete(obj->stack);
00177    AST_LIST_LOCK(&obj->messages);
00178    while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
00179       if (tmp->from)
00180          ast_free(tmp->from);
00181       if (tmp->message)
00182          ast_free(tmp->message);
00183    }
00184    AST_LIST_HEAD_DESTROY(&obj->messages);
00185    ast_free(obj);
00186 }
00187 
00188 /*!
00189  * \brief Deletes the aji_buddy data structure.
00190  * \param obj aji_buddy The structure we will delete.
00191  * \return void.
00192  */
00193 static void aji_buddy_destroy(struct aji_buddy *obj)
00194 {
00195    struct aji_resource *tmp;
00196 
00197    while ((tmp = obj->resources)) {
00198       obj->resources = obj->resources->next;
00199       ast_free(tmp->description);
00200       ast_free(tmp);
00201    }
00202 
00203    ast_free(obj);
00204 }
00205 
00206 /*!
00207  * \brief Find version in XML stream and populate our capabilities list
00208  * \param node the node attribute in the caps element we'll look for or add to 
00209  * our list
00210  * \param version the version attribute in the caps element we'll look for or 
00211  * add to our list
00212  * \param pak struct The XML stanza we're processing
00213  * \return a pointer to the added or found aji_version structure
00214  */ 
00215 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
00216 {
00217    struct aji_capabilities *list = NULL;
00218    struct aji_version *res = NULL;
00219 
00220    list = capabilities;
00221 
00222    if(!node)
00223       node = pak->from->full;
00224    if(!version)
00225       version = "none supplied.";
00226    while(list) {
00227       if(!strcasecmp(list->node, node)) {
00228          res = list->versions;
00229          while(res) {
00230              if(!strcasecmp(res->version, version))
00231                 return res;
00232              res = res->next;
00233          }
00234          /* Specified version not found. Let's add it to 
00235             this node in our capabilities list */
00236          if(!res) {
00237             res = ast_malloc(sizeof(*res));
00238             if(!res) {
00239                ast_log(LOG_ERROR, "Out of memory!\n");
00240                return NULL;
00241             }
00242             res->jingle = 0;
00243             res->parent = list;
00244             ast_copy_string(res->version, version, sizeof(res->version));
00245             res->next = list->versions;
00246             list->versions = res;
00247             return res;
00248          }
00249       }
00250       list = list->next;
00251    }
00252    /* Specified node not found. Let's add it our capabilities list */
00253    if(!list) {
00254       list = ast_malloc(sizeof(*list));
00255       if(!list) {
00256          ast_log(LOG_ERROR, "Out of memory!\n");
00257          return NULL;
00258       }
00259       res = ast_malloc(sizeof(*res));
00260       if(!res) {
00261          ast_log(LOG_ERROR, "Out of memory!\n");
00262          ast_free(list);
00263          return NULL;
00264       }
00265       ast_copy_string(list->node, node, sizeof(list->node));
00266       ast_copy_string(res->version, version, sizeof(res->version));
00267       res->jingle = 0;
00268       res->parent = list;
00269       res->next = NULL;
00270       list->versions = res;
00271       list->next = capabilities;
00272       capabilities = list;
00273    }
00274    return res;
00275 }
00276 /*!
00277  * \brief Find the aji_resource we want
00278  * \param buddy aji_buddy A buddy
00279  * \param name 
00280  * \return aji_resource object
00281 */
00282 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
00283 {
00284    struct aji_resource *res = NULL;
00285    if (!buddy || !name)
00286       return res;
00287    res = buddy->resources;
00288    while (res) {
00289       if (!strcasecmp(res->resource, name)) {
00290          break;
00291       }
00292       res = res->next;
00293    }
00294    return res;
00295 }
00296 
00297 /*!
00298  * \brief Jabber GTalk function
00299  * \param node iks
00300  * \return 1 on success, 0 on failure.
00301 */
00302 static int gtalk_yuck(iks *node)
00303 {
00304    if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps"))
00305       return 1;
00306    return 0;
00307 }
00308 
00309 /*!
00310  * \brief Setup the authentication struct
00311  * \param id iksid 
00312  * \param pass password
00313  * \param sid
00314  * \return x iks
00315 */
00316 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
00317 {
00318    iks *x, *y;
00319    x = iks_new("iq");
00320    iks_insert_attrib(x, "type", "set");
00321    y = iks_insert(x, "query");
00322    iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
00323    iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
00324    iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
00325    if (sid) {
00326       char buf[41];
00327       char sidpass[100];
00328       snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
00329       ast_sha1_hash(buf, sidpass);
00330       iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
00331    } else {
00332       iks_insert_cdata(iks_insert(y, "password"), pass, 0);
00333    }
00334    return x;
00335 }
00336 
00337 /*!
00338  * \brief Dial plan function status(). puts the status of watched user 
00339    into a channel variable.
00340  * \param chan ast_channel
00341  * \param data
00342  * \return 0 on success, -1 on error
00343  */
00344 static int aji_status_exec(struct ast_channel *chan, void *data)
00345 {
00346    struct aji_client *client = NULL;
00347    struct aji_buddy *buddy = NULL;
00348    struct aji_resource *r = NULL;
00349    char *s = NULL;
00350    int stat = 7;
00351    char status[2];
00352    static int deprecation_warning = 0;
00353    AST_DECLARE_APP_ARGS(args,
00354       AST_APP_ARG(sender);
00355       AST_APP_ARG(jid);
00356       AST_APP_ARG(variable);
00357    );
00358    AST_DECLARE_APP_ARGS(jid,
00359       AST_APP_ARG(screenname);
00360       AST_APP_ARG(resource);
00361    );
00362 
00363    if (deprecation_warning++ % 10 == 0)
00364       ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
00365 
00366    if (!data) {
00367       ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<screenname>[/<resource>],<varname>\n");
00368       return 0;
00369    }
00370    s = ast_strdupa(data);
00371    AST_STANDARD_APP_ARGS(args, s);
00372 
00373    if (args.argc != 3) {
00374       ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
00375       return -1;
00376    }
00377 
00378    AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
00379 
00380    if (!(client = ast_aji_get_client(args.sender))) {
00381       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
00382       return -1;
00383    }
00384    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
00385    if (!buddy) {
00386       ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
00387       return -1;
00388    }
00389    r = aji_find_resource(buddy, jid.resource);
00390    if (!r && buddy->resources) 
00391       r = buddy->resources;
00392    if (!r)
00393       ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
00394    else
00395       stat = r->status;
00396    snprintf(status, sizeof(status), "%d", stat);
00397    pbx_builtin_setvar_helper(chan, args.variable, status);
00398    return 0;
00399 }
00400 
00401 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
00402 {
00403    struct aji_client *client = NULL;
00404    struct aji_buddy *buddy = NULL;
00405    struct aji_resource *r = NULL;
00406    int stat = 7;
00407    AST_DECLARE_APP_ARGS(args,
00408       AST_APP_ARG(sender);
00409       AST_APP_ARG(jid);
00410    );
00411    AST_DECLARE_APP_ARGS(jid,
00412       AST_APP_ARG(screenname);
00413       AST_APP_ARG(resource);
00414    );
00415 
00416    if (!data) {
00417       ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
00418       return 0;
00419    }
00420    AST_STANDARD_APP_ARGS(args, data);
00421 
00422    if (args.argc != 2) {
00423       ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
00424       return -1;
00425    }
00426 
00427    AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
00428 
00429    if (!(client = ast_aji_get_client(args.sender))) {
00430       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
00431       return -1;
00432    }
00433    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
00434    if (!buddy) {
00435       ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
00436       return -1;
00437    }
00438    r = aji_find_resource(buddy, jid.resource);
00439    if (!r && buddy->resources) 
00440       r = buddy->resources;
00441    if (!r)
00442       ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
00443    else
00444       stat = r->status;
00445    snprintf(buf, buflen, "%d", stat);
00446    return 0;
00447 }
00448 
00449 static struct ast_custom_function jabberstatus_function = {
00450    .name = "JABBER_STATUS",
00451    .synopsis = "Retrieve buddy status",
00452    .syntax = "JABBER_STATUS(<sender>,<buddy>[/<resource>])",
00453    .read = acf_jabberstatus_read,
00454    .desc =
00455 "Retrieves the numeric status associated with the specified buddy (jid). If the\n"
00456 "buddy does not exist in the buddylist, returns 7.\n"
00457 "Status will be 1-7.\n" 
00458 "             1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline\n" 
00459 "             If not in roster variable will be set to 7\n\n",
00460 };
00461 
00462 /*!
00463  * \brief Dial plan function to send a message.
00464  * \param chan ast_channel
00465  * \param data  Data is sender|reciever|message.
00466  * \return 0 on success,-1 on error.
00467  */
00468 static int aji_send_exec(struct ast_channel *chan, void *data)
00469 {
00470    struct aji_client *client = NULL;
00471    char *s;
00472    AST_DECLARE_APP_ARGS(args,
00473       AST_APP_ARG(sender);
00474       AST_APP_ARG(recipient);
00475       AST_APP_ARG(message);
00476    );
00477 
00478    if (!data) {
00479       ast_log(LOG_ERROR, "Usage:  JabberSend(<sender>,<recipient>,<message>)\n");
00480       return 0;
00481    }
00482    s = ast_strdupa(data);
00483 
00484    AST_STANDARD_APP_ARGS(args, s);
00485    if (args.argc < 3) {
00486       ast_log(LOG_ERROR, "JabberSend requires 3 arguments: '%s'\n", (char *) data);
00487       return -1;
00488    }
00489 
00490    if (!(client = ast_aji_get_client(args.sender))) {
00491       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
00492       return -1;
00493    }
00494    if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message))
00495       ast_aji_send_chat(client, args.recipient, args.message);
00496    return 0;
00497 }
00498 
00499 /*! 
00500  * \brief Tests whether the connection is secured or not
00501  * \return 0 if the connection is not secured
00502  */
00503 static int aji_is_secure(struct aji_client *client)
00504 {
00505 #ifdef HAVE_OPENSSL
00506    return client->stream_flags & SECURE;
00507 #else
00508    return 0;
00509 #endif
00510 }
00511 
00512 #ifdef HAVE_OPENSSL
00513 /*!
00514  * \brief Starts the TLS procedure
00515  * \param client the configured XMPP client we use to connect to a XMPP server
00516  * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
00517  * if OpenSSL is not installed
00518  */
00519 static int aji_start_tls(struct aji_client *client)
00520 {
00521    int ret;
00522 
00523    /* This is sent not encrypted */
00524    ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
00525    if (ret)
00526       return ret;
00527 
00528    client->stream_flags |= TRY_SECURE;
00529    return IKS_OK;
00530 }
00531 
00532 /*! 
00533  * \brief TLS handshake, OpenSSL initialization
00534  * \param client the configured XMPP client we use to connect to a XMPP server
00535  * \return IKS_OK on success, IKS_NET_TLSFAIL on failure 
00536  */
00537 static int aji_tls_handshake(struct aji_client *client)
00538 {
00539    int ret;
00540    int sock;
00541    
00542    ast_debug(1, "Starting TLS handshake\n"); 
00543 
00544    /* Load encryption, hashing algorithms and error strings */
00545    SSL_library_init();
00546    SSL_load_error_strings();
00547 
00548    /* Choose an SSL/TLS protocol version, create SSL_CTX */
00549    client->ssl_method = SSLv3_method();
00550    client->ssl_context = SSL_CTX_new(client->ssl_method);                
00551    if (!client->ssl_context)
00552       return IKS_NET_TLSFAIL;
00553 
00554    /* Create new SSL session */
00555    client->ssl_session = SSL_new(client->ssl_context);
00556    if (!client->ssl_session)
00557       return IKS_NET_TLSFAIL;
00558 
00559    /* Enforce TLS on our XMPP connection */
00560    sock = iks_fd(client->p);
00561    ret = SSL_set_fd(client->ssl_session, sock);
00562    if (!ret)
00563       return IKS_NET_TLSFAIL;
00564 
00565    /* Perform SSL handshake */
00566    ret = SSL_connect(client->ssl_session);
00567    if (!ret)
00568       return IKS_NET_TLSFAIL;
00569 
00570    client->stream_flags &= (~TRY_SECURE);
00571    client->stream_flags |= SECURE;
00572 
00573    /* Sent over the established TLS connection */
00574    ret = aji_send_header(client, client->jid->server);
00575    if (ret != IKS_OK)
00576       return IKS_NET_TLSFAIL;
00577 
00578    ast_debug(1, "TLS started with server\n"); 
00579 
00580    return IKS_OK;
00581 }
00582 #endif /* HAVE_OPENSSL */
00583 
00584 /*! 
00585  * \brief Secured or unsecured IO socket receiving function
00586  * \param client the configured XMPP client we use to connect to a XMPP server
00587  * \param buffer the reception buffer
00588  * \param buf_len the size of the buffer
00589  * \param timeout the select timer
00590  * \return the number of read bytes on success, 0 on timeout expiration, 
00591  * -1 on  error
00592  */
00593 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
00594 {
00595    int sock;
00596    fd_set fds;
00597    struct timeval tv, *tvptr = NULL;
00598    int len, res;
00599 
00600 #ifdef HAVE_OPENSSL
00601    if (aji_is_secure(client)) {
00602       sock = SSL_get_fd(client->ssl_session);
00603       if (sock < 0)
00604          return -1;     
00605    } else
00606 #endif /* HAVE_OPENSSL */
00607       sock = iks_fd(client->p);  
00608 
00609    memset(&tv, 0, sizeof(struct timeval));
00610    FD_ZERO(&fds);
00611    FD_SET(sock, &fds);
00612    tv.tv_sec = timeout;
00613 
00614    /* NULL value for tvptr makes ast_select wait indefinitely */
00615    tvptr = (timeout != -1) ? &tv : NULL;
00616 
00617    /* ast_select emulates linux behaviour in terms of timeout handling */
00618    res = ast_select(sock + 1, &fds, NULL, NULL, tvptr);
00619    if (res > 0) {
00620 #ifdef HAVE_OPENSSL
00621       if (aji_is_secure(client)) {
00622          len = SSL_read(client->ssl_session, buffer, buf_len);
00623       } else
00624 #endif /* HAVE_OPENSSL */
00625          len = recv(sock, buffer, buf_len, 0);
00626 
00627       if (len > 0) {
00628          return len;
00629       } else if (len <= 0) {
00630          return -1;
00631       }
00632    }
00633    return res;
00634 }
00635 
00636 /*! 
00637  * \brief Tries to receive data from the Jabber server
00638  * \param client the configured XMPP client we use to connect to a XMPP server
00639  * \param timeout the timeout value
00640  * This function receives (encrypted or unencrypted) data from the XMPP server,
00641  * and passes it to the parser.
00642  * \return IKS_OK on success, IKS_NET_RWERR on IO error, IKS_NET_NOCONN, if no
00643  * connection available, IKS_NET_EXPIRED on timeout expiration
00644  */
00645 static int aji_recv (struct aji_client *client, int timeout)
00646 {
00647    int len, ret;
00648    char buf[NET_IO_BUF_SIZE -1];
00649    char newbuf[NET_IO_BUF_SIZE -1];
00650    int pos = 0;
00651    int newbufpos = 0;
00652    unsigned char c;
00653 
00654    memset(buf, 0, sizeof(buf));
00655    memset(newbuf, 0, sizeof(newbuf));
00656 
00657    while (1) {
00658       len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 1, timeout);
00659       if (len < 0) return IKS_NET_RWERR;
00660       if (len == 0) return IKS_NET_EXPIRED;
00661       buf[len] = '\0';
00662 
00663       /* our iksemel parser won't work as expected if we feed
00664          it with XML packets that contain multiple whitespace 
00665          characters between tags */
00666       while (pos < len) {
00667          c = buf[pos];
00668          /* if we stumble on the ending tag character,
00669             we skip any whitespace that follows it*/
00670          if (c == '>') {
00671             while (isspace(buf[pos+1])) {
00672                pos++;
00673             }
00674          }
00675          newbuf[newbufpos] = c;
00676          newbufpos ++;
00677          pos++;
00678       }
00679       pos = 0;
00680       newbufpos = 0;
00681 
00682       /* Log the message here, because iksemel's logHook is 
00683          unaccessible */
00684       aji_log_hook(client, buf, len, 1);
00685 
00686       /* let iksemel deal with the string length, 
00687          and reset our buffer */
00688       ret = iks_parse(client->p, newbuf, 0, 0);
00689       memset(newbuf, 0, sizeof(newbuf));
00690 
00691       if (ret != IKS_OK) {
00692          ast_log(LOG_WARNING, "XML parsing failed\n");
00693          return ret;
00694       }
00695       ast_debug(3, "XML parsing successful\n"); 
00696    }
00697    return IKS_OK;
00698 }
00699 
00700 /*! 
00701  * \brief Sends XMPP header to the server
00702  * \param client the configured XMPP client we use to connect to a XMPP server
00703  * \param to the target XMPP server
00704  * \return IKS_OK on success, any other value on failure
00705  */
00706 static int aji_send_header(struct aji_client *client, const char *to)
00707 {
00708    char *msg;
00709    int len, err;
00710 
00711    len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
00712    msg = iks_malloc(len);
00713    if (!msg)
00714       return IKS_NOMEM;
00715    sprintf(msg, "<?xml version='1.0'?>"
00716       "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
00717       "%s' to='%s' version='1.0'>", client->name_space, to);
00718    err = aji_send_raw(client, msg);
00719    iks_free(msg);
00720    if (err != IKS_OK)
00721       return err;
00722 
00723    return IKS_OK;
00724 }
00725 
00726 /*! 
00727  * \brief Wraps raw sending
00728  * \param client the configured XMPP client we use to connect to a XMPP server
00729  * \param x the XMPP packet to send
00730  * \return IKS_OK on success, any other value on failure
00731  */
00732 int ast_aji_send(struct aji_client *client, iks *x)
00733 {
00734    return aji_send_raw(client, iks_string(iks_stack(x), x));
00735 }
00736 
00737 /*! 
00738  * \brief Sends an XML string over an XMPP connection
00739  * \param client the configured XMPP client we use to connect to a XMPP server
00740  * \param xmlstr the XML string to send
00741  * The XML data is sent whether the connection is secured or not. In the 
00742  * latter case, we just call iks_send_raw().
00743  * \return IKS_OK on success, any other value on failure
00744  */
00745 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
00746 {
00747    int ret;
00748 #ifdef HAVE_OPENSSL
00749    int len = strlen(xmlstr);
00750 
00751    if (aji_is_secure(client)) {
00752       ret = SSL_write(client->ssl_session, xmlstr, len);
00753       if (ret) {
00754          /* Log the message here, because iksemel's logHook is 
00755             unaccessible */
00756          aji_log_hook(client, xmlstr, len, 0);
00757          return IKS_OK;
00758       }
00759    }
00760 #endif
00761    /* If needed, data will be sent unencrypted, and logHook will 
00762       be called inside iks_send_raw */
00763    ret = iks_send_raw(client->p, xmlstr);
00764    if (ret != IKS_OK)
00765       return ret; 
00766 
00767    return IKS_OK;
00768 }
00769 
00770 /*!
00771  * \brief the debug loop.
00772  * \param data void
00773  * \param xmpp xml data as string
00774  * \param size size of string
00775  * \param is_incoming direction of packet 1 for inbound 0 for outbound.
00776  */
00777 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
00778 {
00779    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
00780 
00781    if (!ast_strlen_zero(xmpp))
00782       manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
00783 
00784    if (client->debug) {
00785       if (is_incoming)
00786          ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
00787       else {
00788          if( strlen(xmpp) == 1) {
00789             if(option_debug > 2  && xmpp[0] == ' ') {
00790                ast_verbose("\nJABBER: Keep alive packet\n");
00791             }
00792          } else
00793             ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
00794       }
00795 
00796    }
00797    ASTOBJ_UNREF(client, aji_client_destroy);
00798 }
00799 
00800 /*!
00801  * \brief A wrapper function for iks_start_sasl
00802  * \param client the configured XMPP client we use to connect to a XMPP server
00803  * \param type the SASL authentication type. Supported types are PLAIN and MD5
00804  * \param username
00805  * \param pass password.
00806  *
00807  * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
00808  */
00809 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
00810 {
00811    iks *x = NULL;
00812    int len;
00813    char *s;
00814    char *base64;
00815 
00816    /* trigger SASL DIGEST-MD5 only over an unsecured connection.
00817       iks_start_sasl is an iksemel API function and relies on GnuTLS,
00818       whereas we use OpenSSL */
00819    if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
00820       return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass); 
00821    if (!(type & IKS_STREAM_SASL_PLAIN)) {
00822       ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
00823       return IKS_NET_NOTSUPP;
00824    }
00825 
00826    x = iks_new("auth"); 
00827    if (!x) {
00828       ast_log(LOG_ERROR, "Out of memory.\n");
00829       return IKS_NET_NOTSUPP;
00830    }
00831 
00832    iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
00833    len = strlen(username) + strlen(pass) + 3;
00834    s = alloca(len);
00835    base64 = alloca((len + 2) * 4 / 3);
00836    iks_insert_attrib(x, "mechanism", "PLAIN");
00837    snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
00838 
00839    /* exclude the NULL training byte from the base64 encoding operation
00840       as some XMPP servers will refuse it.
00841       The format for authentication is [authzid]\0authcid\0password
00842       not [authzid]\0authcid\0password\0 */
00843    ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
00844    iks_insert_cdata(x, base64, 0);
00845    ast_aji_send(client, x);
00846    iks_delete(x);
00847 
00848    return IKS_OK;
00849 }
00850 
00851 /*!
00852  * \brief The action hook parses the inbound packets, constantly running.
00853  * \param data aji client structure 
00854  * \param type type of packet 
00855  * \param node the actual packet.
00856  * \return IKS_OK or IKS_HOOK .
00857  */
00858 static int aji_act_hook(void *data, int type, iks *node)
00859 {
00860    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
00861    ikspak *pak = NULL;
00862    iks *auth = NULL;
00863    int features = 0;
00864 
00865    if(!node) {
00866       ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
00867       ASTOBJ_UNREF(client, aji_client_destroy);
00868       return IKS_HOOK;
00869    }
00870 
00871    if (client->state == AJI_DISCONNECTING) {
00872       ASTOBJ_UNREF(client, aji_client_destroy);
00873       return IKS_HOOK;
00874    }
00875 
00876    pak = iks_packet(node);
00877 
00878    if (!client->component) { /*client */
00879       switch (type) {
00880       case IKS_NODE_START:
00881          if (client->usetls && !aji_is_secure(client)) {
00882 #ifndef HAVE_OPENSSL
00883             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");
00884             ASTOBJ_UNREF(client, aji_client_destroy);
00885             return IKS_HOOK;
00886 #else
00887             if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
00888                ast_log(LOG_ERROR, "Could not start TLS\n");
00889                ASTOBJ_UNREF(client, aji_client_destroy);
00890                return IKS_HOOK;     
00891             }
00892 #endif
00893             break;
00894          }
00895          if (!client->usesasl) {
00896             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);
00897             auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
00898             if (auth) {
00899                iks_insert_attrib(auth, "id", client->mid);
00900                iks_insert_attrib(auth, "to", client->jid->server);
00901                ast_aji_increment_mid(client->mid);
00902                ast_aji_send(client, auth);
00903                iks_delete(auth);
00904             } else
00905                ast_log(LOG_ERROR, "Out of memory.\n");
00906          }
00907          break;
00908 
00909       case IKS_NODE_NORMAL:
00910 #ifdef HAVE_OPENSSL
00911          if (client->stream_flags & TRY_SECURE) {
00912             if (!strcmp("proceed", iks_name(node))) {
00913                return aji_tls_handshake(client);
00914             }
00915          }
00916 #endif
00917          if (!strcmp("stream:features", iks_name(node))) {
00918             features = iks_stream_features(node);
00919             if (client->usesasl) {
00920                if (client->usetls && !aji_is_secure(client))
00921                   break;
00922                if (client->authorized) {
00923                   if (features & IKS_STREAM_BIND) {
00924                      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);
00925                      auth = iks_make_resource_bind(client->jid);
00926                      if (auth) {
00927                         iks_insert_attrib(auth, "id", client->mid);
00928                         ast_aji_increment_mid(client->mid);
00929                         ast_aji_send(client, auth);
00930                         iks_delete(auth);
00931                      } else {
00932                         ast_log(LOG_ERROR, "Out of memory.\n");
00933                         break;
00934                      }
00935                   }
00936                   if (features & IKS_STREAM_SESSION) {
00937                      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);
00938                      auth = iks_make_session();
00939                      if (auth) {
00940                         iks_insert_attrib(auth, "id", "auth");
00941                         ast_aji_increment_mid(client->mid);
00942                         ast_aji_send(client, auth);
00943                         iks_delete(auth);
00944                      } else {
00945                         ast_log(LOG_ERROR, "Out of memory.\n");
00946                      }
00947                   }
00948                } else {
00949                   int ret;
00950                   if (!client->jid->user) {
00951                      ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
00952                      break;
00953                   }
00954 
00955                   ret = aji_start_sasl(client, features, client->jid->user, client->password);
00956                   if (ret != IKS_OK) {
00957                      ASTOBJ_UNREF(client, aji_client_destroy);
00958                      return IKS_HOOK;
00959                   }
00960                   break;
00961                }
00962             }
00963          } else if (!strcmp("failure", iks_name(node))) {
00964             ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
00965          } else if (!strcmp("success", iks_name(node))) {
00966             client->authorized = 1;
00967             aji_send_header(client, client->jid->server);
00968          }
00969          break;
00970       case IKS_NODE_ERROR: 
00971             ast_log(LOG_ERROR, "JABBER: Node Error\n");
00972             ASTOBJ_UNREF(client, aji_client_destroy);
00973             return IKS_HOOK;
00974             break;
00975       case IKS_NODE_STOP: 
00976             ast_log(LOG_WARNING, "JABBER: Disconnected\n");
00977             ASTOBJ_UNREF(client, aji_client_destroy);
00978             return IKS_HOOK;
00979             break;
00980       }
00981    } else if (client->state != AJI_CONNECTED && client->component) {
00982       switch (type) {
00983       case IKS_NODE_START:
00984          if (client->state == AJI_DISCONNECTED) {
00985             char secret[160], shasum[320], *handshake;
00986 
00987             sprintf(secret, "%s%s", pak->id, client->password);
00988             ast_sha1_hash(shasum, secret);
00989             handshake = NULL;
00990             if (asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
00991                aji_send_raw(client, handshake);
00992                ast_free(handshake);
00993                handshake = NULL;
00994             }
00995             client->state = AJI_CONNECTING;
00996             if(aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
00997                client->state = AJI_CONNECTED;
00998             else
00999                ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
01000             break;
01001          }
01002          break;
01003 
01004       case IKS_NODE_NORMAL:
01005          break;
01006 
01007       case IKS_NODE_ERROR:
01008          ast_log(LOG_ERROR, "JABBER: Node Error\n");
01009          ASTOBJ_UNREF(client, aji_client_destroy);
01010          return IKS_HOOK;
01011 
01012       case IKS_NODE_STOP:
01013          ast_log(LOG_WARNING, "JABBER: Disconnected\n");
01014          ASTOBJ_UNREF(client, aji_client_destroy);
01015          return IKS_HOOK;
01016       }
01017    }
01018 
01019    switch (pak->type) {
01020    case IKS_PAK_NONE:
01021       ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
01022       break;
01023    case IKS_PAK_MESSAGE:
01024       aji_handle_message(client, pak);
01025       ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
01026       break;
01027    case IKS_PAK_PRESENCE:
01028       aji_handle_presence(client, pak);
01029       ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
01030       break;
01031    case IKS_PAK_S10N:
01032       aji_handle_subscribe(client, pak);
01033       ast_debug(1, "JABBER: Handling paktype S10N\n");
01034       break;
01035    case IKS_PAK_IQ:
01036       ast_debug(1, "JABBER: Handling paktype IQ\n");
01037       aji_handle_iq(client, node);
01038       break;
01039    default:
01040       ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
01041       break;
01042    }
01043    
01044    iks_filter_packet(client->f, pak);
01045 
01046    if (node)
01047       iks_delete(node);
01048 
01049    ASTOBJ_UNREF(client, aji_client_destroy);
01050    return IKS_OK;
01051 }
01052 /*!
01053  * \brief Unknown
01054  * \param data void
01055  * \param pak ikspak
01056  * \return IKS_FILTER_EAT.
01057 */
01058 static int aji_register_approve_handler(void *data, ikspak *pak)
01059 {
01060    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01061    iks *iq = NULL, *presence = NULL, *x = NULL;
01062 
01063    iq = iks_new("iq");
01064    presence = iks_new("presence");
01065    x = iks_new("x");
01066    if (client && iq && presence && x) {
01067       if (!iks_find(pak->query, "remove")) {
01068          iks_insert_attrib(iq, "from", client->jid->full);
01069          iks_insert_attrib(iq, "to", pak->from->full);
01070          iks_insert_attrib(iq, "id", pak->id);
01071          iks_insert_attrib(iq, "type", "result");
01072          ast_aji_send(client, iq);
01073 
01074          iks_insert_attrib(presence, "from", client->jid->full);
01075          iks_insert_attrib(presence, "to", pak->from->partial);
01076          iks_insert_attrib(presence, "id", client->mid);
01077          ast_aji_increment_mid(client->mid);
01078          iks_insert_attrib(presence, "type", "subscribe");
01079          iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
01080          iks_insert_node(presence, x);
01081          ast_aji_send(client, presence); 
01082       }
01083    } else {
01084       ast_log(LOG_ERROR, "Out of memory.\n");
01085    }
01086 
01087 
01088    iks_delete(iq);
01089    iks_delete(presence);
01090    iks_delete(x);
01091    
01092    ASTOBJ_UNREF(client, aji_client_destroy);
01093    return IKS_FILTER_EAT;
01094 }
01095 /*!
01096  * \brief register handler for incoming querys (IQ's)
01097  * \param data incoming aji_client request
01098  * \param pak ikspak
01099  * \return IKS_FILTER_EAT.
01100 */
01101 static int aji_register_query_handler(void *data, ikspak *pak)
01102 {
01103    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01104    struct aji_buddy *buddy = NULL; 
01105    char *node = NULL;
01106    iks *iq = NULL, *query = NULL;
01107 
01108    client = (struct aji_client *) data;
01109 
01110    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
01111    if (!buddy) {
01112       iks  *error = NULL, *notacceptable = NULL;
01113 
01114       ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
01115       iq = iks_new("iq");
01116       query = iks_new("query");
01117       error = iks_new("error");
01118       notacceptable = iks_new("not-acceptable");
01119       if(iq && query && error && notacceptable) {
01120          iks_insert_attrib(iq, "type", "error");
01121          iks_insert_attrib(iq, "from", client->user);
01122          iks_insert_attrib(iq, "to", pak->from->full);
01123          iks_insert_attrib(iq, "id", pak->id);
01124          iks_insert_attrib(query, "xmlns", "jabber:iq:register");
01125          iks_insert_attrib(error, "code" , "406");
01126          iks_insert_attrib(error, "type", "modify");
01127          iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
01128          iks_insert_node(iq, query);
01129          iks_insert_node(iq, error);
01130          iks_insert_node(error, notacceptable);
01131          ast_aji_send(client, iq);
01132       } else {
01133          ast_log(LOG_ERROR, "Out of memory.\n");
01134       }
01135 
01136       iks_delete(error);
01137       iks_delete(notacceptable);
01138    } else   if (!(node = iks_find_attrib(pak->query, "node"))) {
01139       iks *instructions = NULL;
01140       char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
01141       iq = iks_new("iq");
01142       query = iks_new("query");
01143       instructions = iks_new("instructions");
01144       if (iq && query && instructions && client) {
01145          iks_insert_attrib(iq, "from", client->user);
01146          iks_insert_attrib(iq, "to", pak->from->full);
01147          iks_insert_attrib(iq, "id", pak->id);
01148          iks_insert_attrib(iq, "type", "result");
01149          iks_insert_attrib(query, "xmlns", "jabber:iq:register");
01150          iks_insert_cdata(instructions, explain, 0);
01151          iks_insert_node(iq, query);
01152          iks_insert_node(query, instructions);
01153          ast_aji_send(client, iq);
01154       } else {
01155          ast_log(LOG_ERROR, "Out of memory.\n");
01156       }
01157 
01158       iks_delete(instructions);
01159    }
01160    iks_delete(iq);
01161    iks_delete(query);
01162    ASTOBJ_UNREF(client, aji_client_destroy);
01163    return IKS_FILTER_EAT;
01164 }
01165 
01166 /*!
01167  * \brief Handles stuff
01168  * \param data void
01169  * \param pak ikspak 
01170  * \return IKS_FILTER_EAT.
01171 */
01172 static int aji_ditems_handler(void *data, ikspak *pak)
01173 {
01174    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01175    char *node = NULL;
01176 
01177    if (!(node = iks_find_attrib(pak->query, "node"))) {
01178       iks *iq = NULL, *query = NULL, *item = NULL;
01179       iq = iks_new("iq");
01180       query = iks_new("query");
01181       item = iks_new("item");
01182 
01183       if (iq && query && item) {
01184          iks_insert_attrib(iq, "from", client->user);
01185          iks_insert_attrib(iq, "to", pak->from->full);
01186          iks_insert_attrib(iq, "id", pak->id);
01187          iks_insert_attrib(iq, "type", "result");
01188          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
01189          iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
01190          iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
01191          iks_insert_attrib(item, "jid", client->user);
01192 
01193          iks_insert_node(iq, query);
01194          iks_insert_node(query, item);
01195          ast_aji_send(client, iq);
01196       } else {
01197          ast_log(LOG_ERROR, "Out of memory.\n");
01198       }
01199 
01200       iks_delete(iq);
01201       iks_delete(query);
01202       iks_delete(item);
01203 
01204    } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
01205       iks *iq, *query, *confirm;
01206       iq = iks_new("iq");
01207       query = iks_new("query");
01208       confirm = iks_new("item");
01209       if (iq && query && confirm && client) {
01210          iks_insert_attrib(iq, "from", client->user);
01211          iks_insert_attrib(iq, "to", pak->from->full);
01212          iks_insert_attrib(iq, "id", pak->id);
01213          iks_insert_attrib(iq, "type", "result");
01214          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
01215          iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
01216          iks_insert_attrib(confirm, "node", "confirmaccount");
01217          iks_insert_attrib(confirm, "name", "Confirm AIM account");
01218          iks_insert_attrib(confirm, "jid", "blog.astjab.org");
01219 
01220          iks_insert_node(iq, query);
01221          iks_insert_node(query, confirm);
01222          ast_aji_send(client, iq);
01223       } else {
01224          ast_log(LOG_ERROR, "Out of memory.\n");
01225       }
01226 
01227       iks_delete(iq);
01228       iks_delete(query);
01229       iks_delete(confirm);
01230 
01231    } else if (!strcasecmp(node, "confirmaccount")) {
01232       iks *iq = NULL, *query = NULL, *feature = NULL;
01233 
01234       iq = iks_new("iq");
01235       query = iks_new("query");
01236       feature = iks_new("feature");
01237 
01238       if (iq && query && feature && client) {
01239          iks_insert_attrib(iq, "from", client->user);
01240          iks_insert_attrib(iq, "to", pak->from->full);
01241          iks_insert_attrib(iq, "id", pak->id);
01242          iks_insert_attrib(iq, "type", "result");
01243          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
01244          iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
01245          iks_insert_node(iq, query);
01246          iks_insert_node(query, feature);
01247          ast_aji_send(client, iq);
01248       } else {
01249          ast_log(LOG_ERROR, "Out of memory.\n");
01250       }
01251 
01252       iks_delete(iq);
01253       iks_delete(query);
01254       iks_delete(feature);
01255    }
01256 
01257    ASTOBJ_UNREF(client, aji_client_destroy);
01258    return IKS_FILTER_EAT;
01259 
01260 }
01261 /*!
01262  * \brief Handle add extra info
01263  * \param data void
01264  * \param pak ikspak
01265  * \return IKS_FILTER_EAT
01266 */
01267 static int aji_client_info_handler(void *data, ikspak *pak)
01268 {
01269    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01270    struct aji_resource *resource = NULL;
01271    struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
01272 
01273    resource = aji_find_resource(buddy, pak->from->resource);
01274    if (pak->subtype == IKS_TYPE_RESULT) {
01275       if (!resource) {
01276          ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
01277          ASTOBJ_UNREF(client, aji_client_destroy);
01278          return IKS_FILTER_EAT;
01279       }
01280       if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
01281          resource->cap->jingle = 1;
01282       } else
01283          resource->cap->jingle = 0;
01284    } else if (pak->subtype == IKS_TYPE_GET) {
01285       iks *iq, *disco, *ident, *google, *query;
01286       iq = iks_new("iq");
01287       query = iks_new("query");
01288       ident = iks_new("identity");
01289       disco = iks_new("feature");
01290       google = iks_new("feature");
01291       if (iq && ident && disco && google) {
01292          iks_insert_attrib(iq, "from", client->jid->full);
01293          iks_insert_attrib(iq, "to", pak->from->full);
01294          iks_insert_attrib(iq, "type", "result");
01295          iks_insert_attrib(iq, "id", pak->id);
01296          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
01297          iks_insert_attrib(ident, "category", "client");
01298          iks_insert_attrib(ident, "type", "pc");
01299          iks_insert_attrib(ident, "name", "asterisk");
01300          iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
01301          iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
01302          iks_insert_node(iq, query);
01303          iks_insert_node(query, ident);
01304          iks_insert_node(query, google);
01305          iks_insert_node(query, disco);
01306          ast_aji_send(client, iq);
01307       } else
01308          ast_log(LOG_ERROR, "Out of Memory.\n");
01309 
01310       iks_delete(iq);
01311       iks_delete(query);
01312       iks_delete(ident);
01313       iks_delete(google);
01314       iks_delete(disco);
01315    } else if (pak->subtype == IKS_TYPE_ERROR) {
01316       ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
01317    }
01318    ASTOBJ_UNREF(client, aji_client_destroy);
01319    return IKS_FILTER_EAT;
01320 }
01321 /*!
01322  * \brief Handler of the return info packet
01323  * \param data aji_client
01324  * \param pak ikspak
01325  * \return IKS_FILTER_EAT
01326 */
01327 static int aji_dinfo_handler(void *data, ikspak *pak)
01328 {
01329    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01330    char *node = NULL;
01331    struct aji_resource *resource = NULL;
01332    struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
01333 
01334    resource = aji_find_resource(buddy, pak->from->resource);
01335    if (pak->subtype == IKS_TYPE_ERROR) {
01336       ast_log(LOG_WARNING, "Recieved error from a client, turn on jabber debug!\n");
01337       return IKS_FILTER_EAT;
01338    }
01339    if (pak->subtype == IKS_TYPE_RESULT) {
01340       if (!resource) {
01341          ast_log(LOG_NOTICE,"JABBER: Received client info from %s when not requested.\n", pak->from->full);
01342          ASTOBJ_UNREF(client, aji_client_destroy);
01343          return IKS_FILTER_EAT;
01344       }
01345       if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
01346          resource->cap->jingle = 1;
01347       } else
01348          resource->cap->jingle = 0;
01349    } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
01350       iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
01351 
01352       iq = iks_new("iq");
01353       query = iks_new("query");
01354       identity = iks_new("identity");
01355       disco = iks_new("feature");
01356       reg = iks_new("feature");
01357       commands = iks_new("feature");
01358       gateway = iks_new("feature");
01359       version = iks_new("feature");
01360       vcard = iks_new("feature");
01361       search = iks_new("feature");
01362 
01363       if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
01364          iks_insert_attrib(iq, "from", client->user);
01365          iks_insert_attrib(iq, "to", pak->from->full);
01366          iks_insert_attrib(iq, "id", pak->id);
01367          iks_insert_attrib(iq, "type", "result");
01368          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
01369          iks_insert_attrib(identity, "category", "gateway");
01370          iks_insert_attrib(identity, "type", "pstn");
01371          iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
01372          iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
01373          iks_insert_attrib(reg, "var", "jabber:iq:register");
01374          iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
01375          iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
01376          iks_insert_attrib(version, "var", "jabber:iq:version");
01377          iks_insert_attrib(vcard, "var", "vcard-temp");
01378          iks_insert_attrib(search, "var", "jabber:iq:search");
01379 
01380          iks_insert_node(iq, query);
01381          iks_insert_node(query, identity);
01382          iks_insert_node(query, disco);
01383          iks_insert_node(query, reg);
01384          iks_insert_node(query, commands);
01385          iks_insert_node(query, gateway);
01386          iks_insert_node(query, version);
01387          iks_insert_node(query, vcard);
01388          iks_insert_node(query, search);
01389          ast_aji_send(client, iq);
01390       } else {
01391          ast_log(LOG_ERROR, "Out of memory.\n");
01392       }
01393 
01394       iks_delete(iq);
01395       iks_delete(query);
01396       iks_delete(identity);
01397       iks_delete(disco);
01398       iks_delete(reg);
01399       iks_delete(commands);
01400       iks_delete(gateway);
01401       iks_delete(version);
01402       iks_delete(vcard);
01403       iks_delete(search);
01404 
01405    } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
01406       iks *iq, *query, *confirm;
01407       iq = iks_new("iq");
01408       query = iks_new("query");
01409       confirm = iks_new("item");
01410 
01411       if (iq && query && confirm && client) {
01412          iks_insert_attrib(iq, "from", client->user);
01413          iks_insert_attrib(iq, "to", pak->from->full);
01414          iks_insert_attrib(iq, "id", pak->id);
01415          iks_insert_attrib(iq, "type", "result");
01416          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
01417          iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
01418          iks_insert_attrib(confirm, "node", "confirmaccount");
01419          iks_insert_attrib(confirm, "name", "Confirm AIM account");
01420          iks_insert_attrib(confirm, "jid", client->user);
01421          iks_insert_node(iq, query);
01422          iks_insert_node(query, confirm);
01423          ast_aji_send(client, iq);
01424       } else {
01425          ast_log(LOG_ERROR, "Out of memory.\n");
01426       }
01427 
01428       iks_delete(iq);
01429       iks_delete(query);
01430       iks_delete(confirm);
01431 
01432    } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
01433       iks *iq, *query, *feature;
01434 
01435       iq = iks_new("iq");
01436       query = iks_new("query");
01437       feature = iks_new("feature");
01438 
01439       if (iq && query && feature && client) {
01440          iks_insert_attrib(iq, "from", client->user);
01441          iks_insert_attrib(iq, "to", pak->from->full);
01442          iks_insert_attrib(iq, "id", pak->id);
01443          iks_insert_attrib(iq, "type", "result");
01444          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
01445          iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
01446          iks_insert_node(iq, query);
01447          iks_insert_node(query, feature);
01448          ast_aji_send(client, iq);
01449       } else {
01450          ast_log(LOG_ERROR, "Out of memory.\n");
01451       }
01452 
01453       iks_delete(iq);
01454       iks_delete(query);
01455       iks_delete(feature);
01456    }
01457 
01458    ASTOBJ_UNREF(client, aji_client_destroy);
01459    return IKS_FILTER_EAT;
01460 }
01461 
01462 /*!
01463  * \brief Handles \verbatim <iq> \endverbatim tags.
01464  * \param client the configured XMPP client we use to connect to a XMPP server
01465  * \param node iks 
01466  * \return void.
01467  */
01468 static void aji_handle_iq(struct aji_client *client, iks *node)
01469 {
01470    /*Nothing to see here */
01471 }
01472 
01473 /*!
01474  * \brief Handles presence packets.
01475  * \param client the configured XMPP client we use to connect to a XMPP server
01476  * \param pak ikspak the node
01477  */
01478 static void aji_handle_message(struct aji_client *client, ikspak *pak)
01479 {
01480    struct aji_message *insert, *tmp;
01481    int flag = 0;
01482    
01483    if (!(insert = ast_calloc(1, sizeof(*insert))))
01484       return;
01485    time(&insert->arrived);
01486    if (iks_find_cdata(pak->x, "body"))
01487       insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
01488    if (pak->id)
01489       ast_copy_string(insert->id, pak->id, sizeof(insert->message));
01490    if (pak->from)
01491       insert->from = ast_strdup(pak->from->full);
01492    AST_LIST_LOCK(&client->messages);
01493    AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
01494       if (flag) {
01495          AST_LIST_REMOVE_CURRENT(list);
01496          if (tmp->from)
01497             ast_free(tmp->from);
01498          if (tmp->message)
01499             ast_free(tmp->message);
01500       } else if (difftime(time(NULL), tmp->arrived) >= client->message_timeout) {
01501          flag = 1;
01502          AST_LIST_REMOVE_CURRENT(list);
01503          if (tmp->from)
01504             ast_free(tmp->from);
01505          if (tmp->message)
01506             ast_free(tmp->message);
01507       }
01508    }
01509    AST_LIST_TRAVERSE_SAFE_END;
01510    AST_LIST_INSERT_HEAD(&client->messages, insert, list);
01511    AST_LIST_UNLOCK(&client->messages);
01512 }
01513 /*!
01514  * \brief Check the presence info
01515  * \param client the configured XMPP client we use to connect to a XMPP server
01516  * \param pak ikspak
01517 */
01518 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
01519 {
01520    int status, priority;
01521    struct aji_buddy *buddy;
01522    struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
01523    char *ver, *node, *descrip, *type;
01524    
01525    if(client->state != AJI_CONNECTED)
01526       aji_create_buddy(pak->from->partial, client);
01527 
01528    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
01529    if (!buddy && pak->from->partial) {
01530       /* allow our jid to be used to log in with another resource */
01531       if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
01532          aji_create_buddy(pak->from->partial, client);
01533       else
01534          ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
01535       return;
01536    }
01537    type = iks_find_attrib(pak->x, "type");
01538    if(client->component && type &&!strcasecmp("probe", type)) {
01539       aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
01540       ast_verbose("what i was looking for \n");
01541    }
01542    ASTOBJ_WRLOCK(buddy);
01543    status = (pak->show) ? pak->show : 6;
01544    priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
01545    tmp = buddy->resources;
01546    descrip = ast_strdup(iks_find_cdata(pak->x,"status"));
01547 
01548    while (tmp && pak->from->resource) {
01549       if (!strcasecmp(tmp->resource, pak->from->resource)) {
01550          tmp->status = status;
01551          if (tmp->description) ast_free(tmp->description);
01552          tmp->description = descrip;
01553          found = tmp;
01554          if (status == 6) {   /* Sign off Destroy resource */
01555             if (last && found->next) {
01556                last->next = found->next;
01557             } else if (!last) {
01558                if (found->next)
01559                   buddy->resources = found->next;
01560                else
01561                   buddy->resources = NULL;
01562             } else if (!found->next) {
01563                if (last)
01564                   last->next = NULL;
01565                else
01566                   buddy->resources = NULL;
01567             }
01568             ast_free(found);
01569             found = NULL;
01570             break;
01571          }
01572          /* resource list is sorted by descending priority */
01573          if (tmp->priority != priority) {
01574             found->priority = priority;
01575             if (!last && !found->next)
01576                /* resource was found to be unique,
01577                   leave loop */
01578                break;
01579             /* search for resource in our list
01580                and take it out for the moment */
01581             if (last)
01582                last->next = found->next;
01583             else
01584                buddy->resources = found->next;
01585 
01586             last = NULL;
01587             tmp = buddy->resources;
01588             if (!buddy->resources)
01589                buddy->resources = found;
01590             /* priority processing */
01591             while (tmp) {
01592                /* insert resource back according to 
01593                   its priority value */
01594                if (found->priority > tmp->priority) {
01595                   if (last)
01596                      /* insert within list */
01597                      last->next = found;
01598                   found->next = tmp;
01599                   if (!last)
01600                      /* insert on top */
01601                      buddy->resources = found;
01602                   break;
01603                }
01604                if (!tmp->next) {
01605                   /* insert at the end of the list */
01606                   tmp->next = found;
01607                   found->next = NULL;
01608                   break;
01609                }
01610                last = tmp;
01611                tmp = tmp->next;
01612             }
01613          }
01614          break;
01615       }
01616       last = tmp;
01617       tmp = tmp->next;
01618    }
01619 
01620    /* resource not found in our list, create it */
01621    if (!found && status != 6 && pak->from->resource) {
01622       found = ast_calloc(1, sizeof(*found));
01623 
01624       if (!found) {
01625          ast_log(LOG_ERROR, "Out of memory!\n");
01626          return;
01627       }
01628       ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
01629       found->status = status;
01630       found->description = descrip;
01631       found->priority = priority;
01632       found->next = NULL;
01633       last = NULL;
01634       tmp = buddy->resources;
01635       while (tmp) {
01636          if (found->priority > tmp->priority) {
01637             if (last)
01638                last->next = found;
01639             found->next = tmp;
01640             if (!last)
01641                buddy->resources = found;
01642             break;
01643          }
01644          if (!tmp->next) {
01645             tmp->next = found;
01646             break;
01647          }
01648          last = tmp;
01649          tmp = tmp->next;
01650       }
01651       if (!tmp)
01652          buddy->resources = found;
01653    }
01654    
01655    ASTOBJ_UNLOCK(buddy);
01656    ASTOBJ_UNREF(buddy, aji_buddy_destroy);
01657 
01658    node = iks_find_attrib(iks_find(pak->x, "c"), "node");
01659    ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
01660 
01661    /* handle gmail client's special caps:c tag */
01662    if (!node && !ver) {
01663       node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
01664       ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
01665    }
01666 
01667    /* retrieve capabilites of the new resource */
01668    if(status !=6 && found && !found->cap) {
01669       found->cap = aji_find_version(node, ver, pak);
01670       if(gtalk_yuck(pak->x)) /* gtalk should do discover */
01671          found->cap->jingle = 1;
01672       if(found->cap->jingle && option_debug > 4) {
01673          ast_debug(1,"Special case for google till they support discover.\n");
01674       }
01675       else {
01676          iks *iq, *query;
01677          iq = iks_new("iq");
01678          query = iks_new("query");
01679          if(query && iq)  {
01680             iks_insert_attrib(iq, "type", "get");
01681             iks_insert_attrib(iq, "to", pak->from->full);
01682             iks_insert_attrib(iq,"from", client->jid->full);
01683             iks_insert_attrib(iq, "id", client->mid);
01684             ast_aji_increment_mid(client->mid);
01685             iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
01686             iks_insert_node(iq, query);
01687             ast_aji_send(client, iq);
01688             
01689          } else
01690             ast_log(LOG_ERROR, "Out of memory.\n");
01691          
01692          iks_delete(query);
01693          iks_delete(iq);
01694       }
01695    }
01696    switch (pak->subtype) {
01697    case IKS_TYPE_AVAILABLE:
01698       ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
01699       break;
01700    case IKS_TYPE_UNAVAILABLE:
01701       ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
01702       break;
01703    default:
01704       ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
01705    }
01706    switch (pak->show) {
01707    case IKS_SHOW_UNAVAILABLE:
01708       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
01709       break;
01710    case IKS_SHOW_AVAILABLE:
01711       ast_debug(3, "JABBER: type is available\n");
01712       break;
01713    case IKS_SHOW_CHAT:
01714       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
01715       break;
01716    case IKS_SHOW_AWAY:
01717       ast_debug(3, "JABBER: type is away\n");
01718       break;
01719    case IKS_SHOW_XA:
01720       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
01721       break;
01722    case IKS_SHOW_DND:
01723       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
01724       break;
01725    default:
01726       ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
01727    }
01728 }
01729 
01730 /*!
01731  * \brief handles subscription requests.
01732  * \param client the configured XMPP client we use to connect to a XMPP server
01733  * \param pak ikspak iksemel packet.
01734  * \return void.
01735  */
01736 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
01737 {
01738    iks *presence = NULL, *status = NULL;
01739    struct aji_buddy* buddy = NULL;
01740 
01741    switch (pak->subtype) { 
01742    case IKS_TYPE_SUBSCRIBE:
01743       presence = iks_new("presence");
01744       status = iks_new("status");
01745       if (presence && status) {
01746          iks_insert_attrib(presence, "type", "subscribed");
01747          iks_insert_attrib(presence, "to", pak->from->full);
01748          iks_insert_attrib(presence, "from", client->jid->full);
01749          if (pak->id)
01750             iks_insert_attrib(presence, "id", pak->id);
01751          iks_insert_cdata(status, "Asterisk has approved subscription", 0);
01752          iks_insert_node(presence, status);
01753          ast_aji_send(client, presence);
01754       } else
01755          ast_log(LOG_ERROR, "Unable to allocate nodes\n");
01756 
01757       iks_delete(presence);
01758       iks_delete(status);
01759 
01760       if (client->component)
01761          aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
01762    case IKS_TYPE_SUBSCRIBED:
01763       buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
01764       if (!buddy && pak->from->partial) {
01765          aji_create_buddy(pak->from->partial, client);
01766       }
01767    default:
01768       if (option_verbose > 4) {
01769          ast_verbose(VERBOSE_PREFIX_3 "JABBER: This is a subcription of type %i\n", pak->subtype);
01770       }
01771    }
01772 }
01773 
01774 /*!
01775  * \brief sends messages.
01776  * \param client the configured XMPP client we use to connect to a XMPP server
01777  * \param address
01778  * \param message
01779  * \return 1.
01780  */
01781 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
01782 {
01783    int res = 0;
01784    iks *message_packet = NULL;
01785    if (client->state == AJI_CONNECTED) {
01786       message_packet = iks_make_msg(IKS_TYPE_CHAT, address, message);
01787       if (message_packet) {
01788          iks_insert_attrib(message_packet, "from", client->jid->full);
01789          res = ast_aji_send(client, message_packet);
01790       } else {
01791          ast_log(LOG_ERROR, "Out of memory.\n");
01792       }
01793 
01794       iks_delete(message_packet);
01795    } else
01796       ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
01797    return 1;
01798 }
01799 
01800 /*!
01801  * \brief create a chatroom.
01802  * \param client the configured XMPP client we use to connect to a XMPP server
01803  * \param room name of room
01804  * \param server name of server
01805  * \param topic topic for the room.
01806  * \return 0.
01807  */
01808 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
01809 {
01810    int res = 0;
01811    iks *iq = NULL;
01812    iq = iks_new("iq");
01813 
01814    if (iq && client) {
01815       iks_insert_attrib(iq, "type", "get");
01816       iks_insert_attrib(iq, "to", server);
01817       iks_insert_attrib(iq, "id", client->mid);
01818       ast_aji_increment_mid(client->mid);
01819       ast_aji_send(client, iq);
01820    } else 
01821       ast_log(LOG_ERROR, "Out of memory.\n");
01822 
01823    iks_delete(iq);
01824 
01825    return res;
01826 }
01827 
01828 /*!
01829  * \brief join a chatroom.
01830  * \param client the configured XMPP client we use to connect to a XMPP server
01831  * \param room room to join
01832  * \return res.
01833  */
01834 int ast_aji_join_chat(struct aji_client *client, char *room)
01835 {
01836    int res = 0;
01837    iks *presence = NULL, *priority = NULL;
01838    presence = iks_new("presence");
01839    priority = iks_new("priority");
01840    if (presence && priority && client) {
01841       iks_insert_cdata(priority, "0", 1);
01842       iks_insert_attrib(presence, "to", room);
01843       iks_insert_node(presence, priority);
01844       res = ast_aji_send(client, presence);
01845       iks_insert_cdata(priority, "5", 1);
01846       iks_insert_attrib(presence, "to", room);
01847       res = ast_aji_send(client, presence);
01848    } else 
01849       ast_log(LOG_ERROR, "Out of memory.\n");
01850    
01851    iks_delete(presence);
01852    iks_delete(priority);
01853    
01854    return res;
01855 }
01856 
01857 /*!
01858  * \brief invite to a chatroom.
01859  * \param client the configured XMPP client we use to connect to a XMPP server
01860  * \param user 
01861  * \param room
01862  * \param message
01863  * \return res.
01864  */
01865 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
01866 {
01867    int res = 0;
01868    iks *invite, *body, *namespace;
01869 
01870    invite = iks_new("message");
01871    body = iks_new("body");
01872    namespace = iks_new("x");
01873    if (client && invite && body && namespace) {
01874       iks_insert_attrib(invite, "to", user);
01875       iks_insert_attrib(invite, "id", client->mid);
01876       ast_aji_increment_mid(client->mid);
01877       iks_insert_cdata(body, message, 0);
01878       iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
01879       iks_insert_attrib(namespace, "jid", room);
01880       iks_insert_node(invite, body);
01881       iks_insert_node(invite, namespace);
01882       res = ast_aji_send(client, invite);
01883    } else 
01884       ast_log(LOG_ERROR, "Out of memory.\n");
01885 
01886    iks_delete(body);
01887    iks_delete(namespace);
01888    iks_delete(invite);
01889    
01890    return res;
01891 }
01892 
01893 
01894 /*!
01895  * \brief receive message loop.
01896  * \param data void
01897  * \return void.
01898  */
01899 static void *aji_recv_loop(void *data)
01900 {
01901    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01902    int res = IKS_HOOK;
01903 
01904    while(res != IKS_OK) {
01905       ast_debug(3, "JABBER: Connecting.\n");
01906       res = aji_reconnect(client);
01907       sleep(4);
01908    }
01909 
01910    do {
01911       if (res == IKS_NET_RWERR || client->timeout == 0) {
01912          while(res != IKS_OK) {
01913             ast_debug(3, "JABBER: reconnecting.\n");
01914             res = aji_reconnect(client);
01915             sleep(4);
01916          }
01917       }
01918 
01919       res = aji_recv(client, 1);
01920       
01921       if (client->state == AJI_DISCONNECTING) {
01922          ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
01923          pthread_exit(NULL);
01924       }
01925 
01926       /* Decrease timeout if no data received */
01927       if (res == IKS_NET_EXPIRED)
01928          client->timeout--;
01929 
01930       if (res == IKS_HOOK) 
01931          ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
01932       else if (res == IKS_NET_TLSFAIL)
01933          ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
01934       else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
01935          res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
01936          if(res == IKS_OK)
01937             client->timeout = 50;
01938          else
01939             ast_log(LOG_WARNING, "JABBER:  Network Timeout\n");
01940       } else if (res == IKS_NET_RWERR)
01941          ast_log(LOG_WARNING, "JABBER: socket read error\n");
01942    } while (client);
01943    ASTOBJ_UNREF(client, aji_client_destroy);
01944    return 0;
01945 }
01946 
01947 /*!
01948  * \brief increments the mid field for messages and other events.
01949  * \param mid char.
01950  * \return void.
01951  */
01952 void ast_aji_increment_mid(char *mid)
01953 {
01954    int i = 0;
01955 
01956    for (i = strlen(mid) - 1; i >= 0; i--) {
01957       if (mid[i] != 'z') {
01958          mid[i] = mid[i] + 1;
01959          i = 0;
01960       } else
01961          mid[i] = 'a';
01962    }
01963 }
01964 
01965 #if 0
01966 /*!
01967  * \brief attempts to register to a transport.
01968  * \param aji_client struct, and xml packet.
01969  * \return IKS_FILTER_EAT.
01970  */
01971 /*allows for registering to transport , was too sketch and is out for now. */
01972 static int aji_register_transport(void *data, ikspak *pak)
01973 {
01974    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01975    int res = 0;
01976    struct aji_buddy *buddy = NULL;
01977    iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
01978 
01979    if (client && send) {
01980       ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
01981          ASTOBJ_RDLOCK(iterator); 
01982          if (iterator->btype == AJI_TRANS) {
01983               buddy = iterator;
01984          }
01985          ASTOBJ_UNLOCK(iterator);
01986       });
01987       iks_filter_remove_hook(client->f, aji_register_transport);
01988       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);
01989       iks_insert_attrib(send, "to", buddy->host);
01990       iks_insert_attrib(send, "id", client->mid);
01991       ast_aji_increment_mid(client->mid);
01992       iks_insert_attrib(send, "from", client->user);
01993       res = ast_aji_send(client, send);
01994    } else 
01995       ast_log(LOG_ERROR, "Out of memory.\n");
01996 
01997    if (send)
01998       iks_delete(send);
01999    ASTOBJ_UNREF(client, aji_client_destroy);
02000    return IKS_FILTER_EAT;
02001 
02002 }
02003 /*!
02004  * \brief attempts to register to a transport step 2.
02005  * \param aji_client struct, and xml packet.
02006  * \return IKS_FILTER_EAT.
02007  */
02008 /* more of the same blob of code, too wonky for now*/
02009 static int aji_register_transport2(void *data, ikspak *pak)
02010 {
02011    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02012    int res = 0;
02013    struct aji_buddy *buddy = NULL;
02014 
02015    iks *regiq = iks_new("iq");
02016    iks *regquery = iks_new("query");
02017    iks *reguser = iks_new("username");
02018    iks *regpass = iks_new("password");
02019 
02020    if (client && regquery && reguser && regpass && regiq) {
02021       ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02022          ASTOBJ_RDLOCK(iterator);
02023          if (iterator->btype == AJI_TRANS)
02024             buddy = iterator; ASTOBJ_UNLOCK(iterator);
02025       });
02026       iks_filter_remove_hook(client->f, aji_register_transport2);
02027       iks_insert_attrib(regiq, "to", buddy->host);
02028       iks_insert_attrib(regiq, "type", "set");
02029       iks_insert_attrib(regiq, "id", client->mid);
02030       ast_aji_increment_mid(client->mid);
02031       iks_insert_attrib(regiq, "from", client->user);
02032       iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
02033       iks_insert_cdata(reguser, buddy->user, 0);
02034       iks_insert_cdata(regpass, buddy->pass, 0);
02035       iks_insert_node(regiq, regquery);
02036       iks_insert_node(regquery, reguser);
02037       iks_insert_node(regquery, regpass);
02038       res = ast_aji_send(client, regiq);
02039    } else
02040       ast_log(LOG_ERROR, "Out of memory.\n");
02041    if (regiq)
02042       iks_delete(regiq);
02043    if (regquery)
02044       iks_delete(regquery);
02045    if (reguser)
02046       iks_delete(reguser);
02047    if (regpass)
02048       iks_delete(regpass);
02049    ASTOBJ_UNREF(client, aji_client_destroy);
02050    return IKS_FILTER_EAT;
02051 }
02052 #endif
02053 
02054 /*!
02055  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
02056  * \param client the configured XMPP client we use to connect to a XMPP server
02057  * \return void.
02058  * \note The messages here should be configurable.
02059  */
02060 static void aji_pruneregister(struct aji_client *client)
02061 {
02062    int res = 0;
02063    iks *removeiq = iks_new("iq");
02064    iks *removequery = iks_new("query");
02065    iks *removeitem = iks_new("item");
02066    iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
02067    if (!client || !removeiq || !removequery || !removeitem || !send) {
02068       ast_log(LOG_ERROR, "Out of memory.\n");
02069       goto safeout;
02070    }
02071 
02072    iks_insert_node(removeiq, removequery);
02073    iks_insert_node(removequery, removeitem);
02074    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02075       ASTOBJ_RDLOCK(iterator);
02076       /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
02077        * be called at the same time */
02078       if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) {
02079          res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
02080                          "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
02081                          " so I am no longer subscribing to your presence.\n"));
02082          res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
02083                          "GoodBye.  You are no longer in the Asterisk config file so I am removing"
02084                          " your access to my presence.\n"));
02085          iks_insert_attrib(removeiq, "from", client->jid->full); 
02086          iks_insert_attrib(removeiq, "type", "set"); 
02087          iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
02088          iks_insert_attrib(removeitem, "jid", iterator->name);
02089          iks_insert_attrib(removeitem, "subscription", "remove");
02090          res = ast_aji_send(client, removeiq);
02091       } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
02092          res = ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name, 
02093                          "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
02094          ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
02095       }
02096       ASTOBJ_UNLOCK(iterator);
02097    });
02098 
02099  safeout:
02100    iks_delete(removeiq);
02101    iks_delete(removequery);
02102    iks_delete(removeitem);
02103    iks_delete(send);
02104    
02105    ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, aji_buddy_destroy);
02106 }
02107 
02108 /*!
02109  * \brief filters the roster packet we get back from server.
02110  * \param data void
02111  * \param pak ikspak iksemel packet.
02112  * \return IKS_FILTER_EAT.
02113  */
02114 static int aji_filter_roster(void *data, ikspak *pak)
02115 {
02116    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02117    int flag = 0;
02118    iks *x = NULL;
02119    struct aji_buddy *buddy;
02120    
02121    client->state = AJI_CONNECTED;
02122    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02123       ASTOBJ_RDLOCK(iterator);
02124       x = iks_child(pak->query);
02125       flag = 0;
02126       while (x) {
02127          if (!iks_strcmp(iks_name(x), "item")) {
02128             if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
02129                flag = 1;
02130                ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
02131             }
02132          }
02133          x = iks_next(x);
02134       }
02135       if (!flag)
02136          ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
02137       iks_delete(x);
02138       
02139       ASTOBJ_UNLOCK(iterator);
02140    });
02141 
02142    x = iks_child(pak->query);
02143    while (x) {
02144       flag = 0;
02145       if (iks_strcmp(iks_name(x), "item") == 0) {
02146          ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02147             ASTOBJ_RDLOCK(iterator);
02148             if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
02149                flag = 1;
02150             ASTOBJ_UNLOCK(iterator);
02151          });
02152 
02153          if (flag) {
02154             /* found buddy, don't create a new one */
02155             x = iks_next(x);
02156             continue;
02157          }
02158          
02159          buddy = ast_calloc(1, sizeof(*buddy));
02160          if (!buddy) {
02161             ast_log(LOG_WARNING, "Out of memory\n");
02162             return 0;
02163          }
02164          ASTOBJ_INIT(buddy);
02165          ASTOBJ_WRLOCK(buddy);
02166          ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
02167          ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
02168          if(ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
02169             ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
02170             ASTOBJ_MARK(buddy);
02171          } else if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
02172             /* subscribe to buddy's presence only 
02173                if we really need to */
02174             ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
02175          }
02176          ASTOBJ_UNLOCK(buddy);
02177          if (buddy) {
02178             ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
02179             ASTOBJ_UNREF(buddy, aji_buddy_destroy);
02180          }
02181       }
02182       x = iks_next(x);
02183    }
02184 
02185    iks_delete(x);
02186    aji_pruneregister(client);
02187 
02188    ASTOBJ_UNREF(client, aji_client_destroy);
02189    return IKS_FILTER_EAT;
02190 }
02191 
02192 /*!
02193  * \brief reconnect to jabber server
02194  * \param client the configured XMPP client we use to connect to a XMPP server
02195  * \return res.
02196 */
02197 static int aji_reconnect(struct aji_client *client)
02198 {
02199    int res = 0;
02200 
02201    if (client->state)
02202       client->state = AJI_DISCONNECTED;
02203    client->timeout=50;
02204    if (client->p)
02205       iks_parser_reset(client->p);
02206    if (client->authorized)
02207       client->authorized = 0;
02208 
02209    res = aji_initialize(client);
02210 
02211    return res;
02212 }
02213 
02214 /*!
02215  * \brief Get the roster of jabber users
02216  * \param client the configured XMPP client we use to connect to a XMPP server
02217  * \return 1.
02218 */
02219 static int aji_get_roster(struct aji_client *client)
02220 {
02221    iks *roster = NULL;
02222    roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
02223 
02224    if(roster) {
02225       iks_insert_attrib(roster, "id", "roster");
02226       aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
02227       ast_aji_send(client, roster);
02228    }
02229 
02230    iks_delete(roster);
02231    
02232    return 1;
02233 }
02234 
02235 /*!
02236  * \brief connects as a client to jabber server.
02237  * \param data void
02238  * \param pak ikspak iksemel packet
02239  * \return res.
02240  */
02241 static int aji_client_connect(void *data, ikspak *pak)
02242 {
02243    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02244    int res = 0;
02245 
02246    if (client) {
02247       if (client->state == AJI_DISCONNECTED) {
02248          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);
02249          client->state = AJI_CONNECTING;
02250          client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
02251          iks_filter_remove_hook(client->f, aji_client_connect);
02252          if(!client->component) /*client*/
02253             aji_get_roster(client);
02254       }
02255    } else
02256       ast_log(LOG_ERROR, "Out of memory.\n");
02257 
02258    ASTOBJ_UNREF(client, aji_client_destroy);
02259    return res;
02260 }
02261 
02262 /*!
02263  * \brief prepares client for connect.
02264  * \param client the configured XMPP client we use to connect to a XMPP server
02265  * \return 1.
02266  */
02267 static int aji_initialize(struct aji_client *client)
02268 {
02269    int connected = IKS_NET_NOCONN;
02270 
02271 #ifdef HAVE_OPENSSL  
02272    /* reset stream flags */
02273    client->stream_flags = 0;
02274 #endif
02275    /* If it's a component, connect to user, otherwise, connect to server */
02276    connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
02277 
02278    if (connected == IKS_NET_NOCONN) {
02279       ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
02280       return IKS_HOOK;
02281    } else   if (connected == IKS_NET_NODNS) {
02282       ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to  %s\n", client->name, S_OR(client->serverhost, client->jid->server));
02283       return IKS_HOOK;
02284    }
02285 
02286    return IKS_OK;
02287 }
02288 
02289 /*!
02290  * \brief disconnect from jabber server.
02291  * \param client the configured XMPP client we use to connect to a XMPP server
02292  * \return 1.
02293  */
02294 int ast_aji_disconnect(struct aji_client *client)
02295 {
02296    if (client) {
02297       ast_verb(4, "JABBER: Disconnecting\n");
02298 #ifdef HAVE_OPENSSL
02299       if (client->stream_flags & SECURE) {
02300          SSL_shutdown(client->ssl_session);
02301          SSL_CTX_free(client->ssl_context);
02302          SSL_free(client->ssl_session);
02303       }
02304 #endif
02305       iks_disconnect(client->p);
02306       iks_parser_delete(client->p);
02307       ASTOBJ_UNREF(client, aji_client_destroy);
02308    }
02309 
02310    return 1;
02311 }
02312 
02313 /*!
02314  * \brief set presence of client.
02315  * \param client the configured XMPP client we use to connect to a XMPP server
02316  * \param to user send it to
02317  * \param from user it came from
02318  * \param level
02319  * \param desc
02320  * \return void.
02321  */
02322 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
02323 {
02324    int res = 0;
02325    iks *presence = iks_make_pres(level, desc);
02326    iks *cnode = iks_new("c");
02327    iks *priority = iks_new("priority");
02328    char priorityS[10];
02329 
02330    if (presence && cnode && client && priority) {
02331       if(to)
02332          iks_insert_attrib(presence, "to", to);
02333       if(from)
02334          iks_insert_attrib(presence, "from", from);
02335       snprintf(priorityS, sizeof(priorityS), "%d", client->priority);
02336       iks_insert_cdata(priority, priorityS, strlen(priorityS));
02337       iks_insert_node(presence, priority);
02338       iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
02339       iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
02340       iks_insert_attrib(cnode, "ext", "voice-v1");
02341       iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
02342       iks_insert_node(presence, cnode);
02343       res = ast_aji_send(client, presence);
02344    } else
02345       ast_log(LOG_ERROR, "Out of memory.\n");
02346 
02347    iks_delete(cnode);
02348    iks_delete(presence);
02349    iks_delete(priority);
02350 }
02351 
02352 /*!
02353  * \brief Turn on/off console debugging.
02354  * \return CLI_SUCCESS.
02355  */
02356 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02357 {
02358    switch (cmd) {
02359    case CLI_INIT:
02360       e->command = "jabber set debug {on|off}";
02361       e->usage =
02362          "Usage: jabber set debug {on|off}\n"
02363          "       Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
02364       return NULL;
02365    case CLI_GENERATE:
02366       return NULL;
02367    }
02368 
02369    if (a->argc != e->args)
02370       return CLI_SHOWUSAGE;
02371 
02372    if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
02373       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02374          ASTOBJ_RDLOCK(iterator); 
02375          iterator->debug = 1;
02376          ASTOBJ_UNLOCK(iterator);
02377       });
02378       ast_cli(a->fd, "Jabber Debugging Enabled.\n");
02379       return CLI_SUCCESS;
02380    } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
02381       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02382          ASTOBJ_RDLOCK(iterator); 
02383          iterator->debug = 0;
02384          ASTOBJ_UNLOCK(iterator);
02385       });
02386       ast_cli(a->fd, "Jabber Debugging Disabled.\n");
02387       return CLI_SUCCESS;
02388    }
02389    return CLI_SHOWUSAGE; /* defaults to invalid */
02390 }
02391 
02392 /*!
02393  * \brief Turn on/off console debugging (deprecated, use aji_do_set_debug).
02394  * \return CLI_SUCCESS.
02395  */
02396 static char *aji_do_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02397 {
02398 
02399    switch (cmd) {
02400    case CLI_INIT:
02401       e->command = "jabber debug [off]";
02402       e->usage =
02403          "Usage: jabber debug [off]\n"
02404          "       Enables/disables dumping of Jabber packets for debugging purposes.\n";
02405       return NULL;
02406    case CLI_GENERATE:
02407       return NULL;
02408    }
02409 
02410    if (a->argc == 2) {
02411       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02412          ASTOBJ_RDLOCK(iterator); 
02413          iterator->debug = 1;
02414          ASTOBJ_UNLOCK(iterator);
02415       });
02416       ast_cli(a->fd, "Jabber Debugging Enabled.\n");
02417       return CLI_SUCCESS;
02418    } else if (a->argc == 3) {
02419       if (!strncasecmp(a->argv[2], "off", 3)) {
02420          ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02421             ASTOBJ_RDLOCK(iterator); 
02422             iterator->debug = 0;
02423             ASTOBJ_UNLOCK(iterator);
02424          });
02425          ast_cli(a->fd, "Jabber Debugging Disabled.\n");
02426          return CLI_SUCCESS;
02427       }
02428    }
02429    return CLI_SHOWUSAGE; /* defaults to invalid */
02430 }
02431 
02432 /*!
02433  * \brief Reload jabber module.
02434  * \return CLI_SUCCESS.
02435  */
02436 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02437 {
02438    switch (cmd) {
02439    case CLI_INIT:
02440       e->command = "jabber reload";
02441       e->usage =
02442          "Usage: jabber reload\n"
02443          "       Reloads the Jabber module.\n";
02444       return NULL;
02445    case CLI_GENERATE:
02446       return NULL;
02447    }
02448 
02449    aji_reload(1);
02450    ast_cli(a->fd, "Jabber Reloaded.\n");
02451    return CLI_SUCCESS;
02452 }
02453 
02454 /*!
02455  * \brief Show client status.
02456  * \return CLI_SUCCESS.
02457  */
02458 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02459 {
02460    char *status;
02461    int count = 0;
02462    
02463    switch (cmd) {
02464    case CLI_INIT:
02465       e->command = "jabber show connected";
02466       e->usage =
02467          "Usage: jabber show connected\n"
02468          "       Shows state of clients and components\n";
02469       return NULL;
02470    case CLI_GENERATE:
02471       return NULL;
02472    }
02473 
02474    ast_cli(a->fd, "Jabber Users and their status:\n");
02475    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02476       ASTOBJ_RDLOCK(iterator);
02477       count++;
02478       switch (iterator->state) {
02479       case AJI_DISCONNECTED:
02480          status = "Disconnected";
02481          break;
02482       case AJI_CONNECTING:
02483          status = "Connecting";
02484          break;
02485       case AJI_CONNECTED:
02486          status = "Connected";
02487          break;
02488       default:
02489          status = "Unknown";
02490       }
02491       ast_cli(a->fd, "       User: %s     - %s\n", iterator->user, status);
02492       ASTOBJ_UNLOCK(iterator);
02493    });
02494    ast_cli(a->fd, "----\n");
02495    ast_cli(a->fd, "   Number of users: %d\n", count);
02496    return CLI_SUCCESS;
02497 }
02498 
02499 /*!
02500  * \brief Show buddy lists
02501  * \return CLI_SUCCESS.
02502  */
02503 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02504 {
02505    struct aji_resource *resource;
02506    struct aji_client *client;
02507 
02508    switch (cmd) {
02509    case CLI_INIT:
02510       e->command = "jabber show buddies";
02511       e->usage =
02512          "Usage: jabber show buddies\n"
02513          "       Shows buddy lists of our clients\n";
02514       return NULL;
02515    case CLI_GENERATE:
02516       return NULL;
02517    }
02518 
02519    ast_cli(a->fd, "Jabber buddy lists\n");
02520    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02521       ast_cli(a->fd,"Client: %s\n", iterator->user);
02522       client = iterator;
02523       ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02524          ASTOBJ_RDLOCK(iterator);
02525          ast_cli(a->fd,"\tBuddy:\t%s\n", iterator->name);
02526          if (!iterator->resources)
02527             ast_cli(a->fd,"\t\tResource: None\n"); 
02528          for (resource = iterator->resources; resource; resource = resource->next) {
02529             ast_cli(a->fd,"\t\tResource: %s\n", resource->resource);
02530             if(resource->cap) {
02531                ast_cli(a->fd,"\t\t\tnode: %s\n", resource->cap->parent->node);
02532                ast_cli(a->fd,"\t\t\tversion: %s\n", resource->cap->version);
02533                ast_cli(a->fd,"\t\t\tJingle capable: %s\n", resource->cap->jingle ? "yes" : "no");
02534             }
02535             ast_cli(a->fd,"\t\tStatus: %d\n", resource->status);
02536             ast_cli(a->fd,"\t\tPriority: %d\n", resource->priority);
02537          }
02538          ASTOBJ_UNLOCK(iterator);
02539       });
02540       iterator = client;
02541    });
02542    return CLI_SUCCESS;
02543 }
02544 
02545 /*!
02546  * \brief Send test message for debugging.
02547  * \return CLI_SUCCESS,CLI_FAILURE.
02548  */
02549 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
02550 {
02551    struct aji_client *client;
02552    struct aji_resource *resource;
02553    const char *name = "asterisk";
02554    struct aji_message *tmp;
02555 
02556    switch (cmd) {
02557    case CLI_INIT:
02558       e->command = "jabber test";
02559       e->usage =
02560          "Usage: jabber test [client]\n"
02561          "       Sends test message for debugging purposes.  A specific client\n"
02562          "       as configured in jabber.conf can be optionally specified.\n";
02563       return NULL;
02564    case CLI_GENERATE:
02565       return NULL;
02566    }
02567 
02568    if (a->argc > 3)
02569       return CLI_SHOWUSAGE;
02570    else if (a->argc == 3)
02571       name = a->argv[2];
02572 
02573    if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
02574       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
02575       return CLI_FAILURE;
02576    }
02577 
02578    /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
02579    ast_aji_send_chat(client, "mogorman@astjab.org", "blahblah");
02580    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02581       ASTOBJ_RDLOCK(iterator);
02582       ast_verbose("User: %s\n", iterator->name);
02583       for (resource = iterator->resources; resource; resource = resource->next) {
02584          ast_verbose("Resource: %s\n", resource->resource);
02585          if(resource->cap) {
02586             ast_verbose("   client: %s\n", resource->cap->parent->node);
02587             ast_verbose("   version: %s\n", resource->cap->version);
02588             ast_verbose("   Jingle Capable: %d\n", resource->cap->jingle);
02589          }
02590          ast_verbose("  Priority: %d\n", resource->priority);
02591          ast_verbose("  Status: %d\n", resource->status); 
02592          ast_verbose("  Message: %s\n", S_OR(resource->description,"")); 
02593       }
02594       ASTOBJ_UNLOCK(iterator);
02595    });
02596    ast_verbose("\nOooh a working message stack!\n");
02597    AST_LIST_LOCK(&client->messages);
02598    AST_LIST_TRAVERSE(&client->messages, tmp, list) {
02599       ast_verbose("  Message from: %s with id %s @ %s %s\n",tmp->from, S_OR(tmp->id,""), ctime(&tmp->arrived), S_OR(tmp->message, ""));
02600    }
02601    AST_LIST_UNLOCK(&client->messages);
02602    ASTOBJ_UNREF(client, aji_client_destroy);
02603 
02604    return CLI_SUCCESS;
02605 }
02606 
02607 /*!
02608  * \brief creates aji_client structure.
02609  * \param label
02610  * \param var ast_variable
02611  * \param debug 
02612  * \return 0.
02613  */
02614 static int aji_create_client(char *label, struct ast_variable *var, int debug)
02615 {
02616    char *resource;
02617    struct aji_client *client = NULL;
02618    int flag = 0;
02619 
02620    client = ASTOBJ_CONTAINER_FIND(&clients,label);
02621    if (!client) {
02622       flag = 1;
02623       client = ast_calloc(1, sizeof(*client));
02624       if (!client) {
02625          ast_log(LOG_ERROR, "Out of memory!\n");
02626          return 0;
02627       }
02628       ASTOBJ_INIT(client);
02629       ASTOBJ_WRLOCK(client);
02630       ASTOBJ_CONTAINER_INIT(&client->buddies);
02631    } else {
02632       ASTOBJ_WRLOCK(client);
02633       ASTOBJ_UNMARK(client);
02634    }
02635    ASTOBJ_CONTAINER_MARKALL(&client->buddies);
02636    ast_copy_string(client->name, label, sizeof(client->name));
02637    ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
02638 
02639    /* Set default values for the client object */
02640    client->debug = debug;
02641    ast_copy_flags(&client->flags, &globalflags, AST_FLAGS_ALL);
02642    client->port = 5222;
02643    client->usetls = 1;
02644    client->usesasl = 1;
02645    client->forcessl = 0;
02646    client->keepalive = 1;
02647    client->timeout = 50;
02648    client->message_timeout = 100;
02649    AST_LIST_HEAD_INIT(&client->messages);
02650    client->component = 0;
02651    ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage));
02652    client->priority = 0;
02653    client->status = IKS_SHOW_AVAILABLE;
02654 
02655    if (flag) {
02656       client->authorized = 0;
02657       client->state = AJI_DISCONNECTED;
02658    }
02659    while (var) {
02660       if (!strcasecmp(var->name, "username"))
02661          ast_copy_string(client->user, var->value, sizeof(client->user));
02662       else if (!strcasecmp(var->name, "serverhost"))
02663          ast_copy_string(client->serverhost, var->value, sizeof(client->serverhost));
02664       else if (!strcasecmp(var->name, "secret"))
02665          ast_copy_string(client->password, var->value, sizeof(client->password));
02666       else if (!strcasecmp(var->name, "statusmessage"))
02667          ast_copy_string(client->statusmessage, var->value, sizeof(client->statusmessage));
02668       else if (!strcasecmp(var->name, "port"))
02669          client->port = atoi(var->value);
02670       else if (!strcasecmp(var->name, "timeout"))
02671          client->message_timeout = atoi(var->value);
02672       else if (!strcasecmp(var->name, "debug"))
02673          client->debug = (ast_false(var->value)) ? 0 : 1;
02674       else if (!strcasecmp(var->name, "type")) {
02675          if (!strcasecmp(var->value, "component"))
02676             client->component = 1;
02677       } else if (!strcasecmp(var->name, "usetls")) {
02678          client->usetls = (ast_false(var->value)) ? 0 : 1;
02679       } else if (!strcasecmp(var->name, "usesasl")) {
02680          client->usesasl = (ast_false(var->value)) ? 0 : 1;
02681       } else if (!strcasecmp(var->name, "forceoldssl"))
02682          client->forcessl = (ast_false(var->value)) ? 0 : 1;
02683       else if (!strcasecmp(var->name, "keepalive"))
02684          client->keepalive = (ast_false(var->value)) ? 0 : 1;
02685       else if (!strcasecmp(var->name, "autoprune"))
02686          ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOPRUNE);
02687       else if (!strcasecmp(var->name, "autoregister"))
02688          ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOREGISTER);
02689       else if (!strcasecmp(var->name, "buddy"))
02690          aji_create_buddy((char *)var->value, client);
02691       else if (!strcasecmp(var->name, "priority"))
02692          client->priority = atoi(var->value);
02693       else if (!strcasecmp(var->name, "status")) {
02694          if (!strcasecmp(var->value, "unavailable"))
02695             client->status = IKS_SHOW_UNAVAILABLE;
02696          else
02697          if (!strcasecmp(var->value, "available")
02698           || !strcasecmp(var->value, "online"))
02699             client->status = IKS_SHOW_AVAILABLE;
02700          else
02701          if (!strcasecmp(var->value, "chat")
02702           || !strcasecmp(var->value, "chatty"))
02703             client->status = IKS_SHOW_CHAT;
02704          else
02705          if (!strcasecmp(var->value, "away"))
02706             client->status = IKS_SHOW_AWAY;
02707          else
02708          if (!strcasecmp(var->value, "xa")
02709           || !strcasecmp(var->value, "xaway"))
02710             client->status = IKS_SHOW_XA;
02711          else
02712          if (!strcasecmp(var->value, "dnd"))
02713             client->status = IKS_SHOW_DND;
02714          else
02715          if (!strcasecmp(var->value, "invisible"))
02716          #ifdef IKS_SHOW_INVISIBLE
02717             client->status = IKS_SHOW_INVISIBLE;
02718          #else
02719          {
02720             ast_log(LOG_WARNING, "Your iksemel doesn't support invisible status: falling back to DND\n");
02721             client->status = IKS_SHOW_DND;
02722          }
02723          #endif
02724          else
02725             ast_log(LOG_WARNING, "Unknown presence status: %s\n", var->value);
02726       }
02727    /* no transport support in this version */
02728    /* else if (!strcasecmp(var->name, "transport"))
02729             aji_create_transport(var->value, client);
02730    */
02731       var = var->next;
02732    }
02733    if (!flag) {
02734       ASTOBJ_UNLOCK(client);
02735       ASTOBJ_UNREF(client, aji_client_destroy);
02736       return 1;
02737    }
02738 
02739    ast_copy_string(client->name_space, (client->component) ? "jabber:component:accept" : "jabber:client", sizeof(client->name_space));
02740    client->p = iks_stream_new(client->name_space, client, aji_act_hook);
02741    if (!client->p) {
02742       ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name);
02743       return 0;
02744    }
02745    client->stack = iks_stack_new(8192, 8192);
02746    if (!client->stack) {
02747       ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name);
02748       return 0;
02749    }
02750    client->f = iks_filter_new();
02751    if (!client->f) {
02752       ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name);
02753       return 0;
02754    }
02755    if (!strchr(client->user, '/') && !client->component) { /*client */
02756       resource = NULL;
02757       if (asprintf(&resource, "%s/asterisk", client->user) >= 0) {
02758          client->jid = iks_id_new(client->stack, resource);
02759          ast_free(resource);
02760       }
02761    } else
02762       client->jid = iks_id_new(client->stack, client->user);
02763    if (client->component) {
02764       iks_filter_add_rule(client->f, aji_dinfo_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
02765       iks_filter_add_rule(client->f, aji_ditems_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
02766       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);
02767       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);
02768    } else {
02769       iks_filter_add_rule(client->f, aji_client_info_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
02770    }
02771    if (!strchr(client->user, '/') && !client->component) { /*client */
02772       resource = NULL;
02773       if (asprintf(&resource, "%s/asterisk", client->user) >= 0) {
02774          client->jid = iks_id_new(client->stack, resource);
02775          ast_free(resource);
02776       }
02777    } else
02778       client->jid = iks_id_new(client->stack, client->user);
02779    iks_set_log_hook(client->p, aji_log_hook);
02780    ASTOBJ_UNLOCK(client);
02781    ASTOBJ_CONTAINER_LINK(&clients,client);
02782    return 1;
02783 }
02784 
02785 #if 0
02786 /*!
02787  * \brief creates transport.
02788  * \param label, buddy to dump it into. 
02789  * \return 0.
02790  */
02791 /* no connecting to transports today */
02792 static int aji_create_transport(char *label, struct aji_client *client)
02793 {
02794    char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
02795    struct aji_buddy *buddy = NULL;
02796 
02797    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
02798    if (!buddy) {
02799       buddy = ast_calloc(1, sizeof(*buddy));
02800       if(!buddy) {
02801          ast_log(LOG_WARNING, "Out of memory\n");
02802          return 0;
02803       }
02804       ASTOBJ_INIT(buddy);
02805    }
02806    ASTOBJ_WRLOCK(buddy);
02807    server = label;
02808    if ((buddyname = strchr(label, ','))) {
02809       *buddyname = '\0';
02810       buddyname++;
02811       if (buddyname && buddyname[0] != '\0') {
02812          if ((user = strchr(buddyname, ','))) {
02813             *user = '\0';
02814             user++;
02815             if (user && user[0] != '\0') {
02816                if ((pass = strchr(user, ','))) {
02817                   *pass = '\0';
02818                   pass++;
02819                   ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
02820                   ast_copy_string(buddy->user, user, sizeof(buddy->user));
02821                   ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
02822                   ast_copy_string(buddy->server, server, sizeof(buddy->server));
02823                   return 1;
02824                }
02825             }
02826          }
02827       }
02828    }
02829    ASTOBJ_UNLOCK(buddy);
02830    ASTOBJ_UNMARK(buddy);
02831    ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
02832    return 0;
02833 }
02834 #endif
02835 
02836 /*!
02837  * \brief creates buddy.
02838  * \param label char.
02839  * \param client the configured XMPP client we use to connect to a XMPP server 
02840  * \return 1 on success, 0 on failure.
02841  */
02842 static int aji_create_buddy(char *label, struct aji_client *client)
02843 {
02844    struct aji_buddy *buddy = NULL;
02845    int flag = 0;
02846    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
02847    if (!buddy) {
02848       flag = 1;
02849       buddy = ast_calloc(1, sizeof(*buddy));
02850       if(!buddy) {
02851          ast_log(LOG_WARNING, "Out of memory\n");
02852          return 0;
02853       }
02854       ASTOBJ_INIT(buddy);
02855    }
02856    ASTOBJ_WRLOCK(buddy);
02857    ast_copy_string(buddy->name, label, sizeof(buddy->name));
02858    ASTOBJ_UNLOCK(buddy);
02859    if(flag)
02860       ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
02861    else {
02862       ASTOBJ_UNMARK(buddy);
02863       ASTOBJ_UNREF(buddy, aji_buddy_destroy);
02864    }
02865    return 1;
02866 }
02867 
02868 /*!< load config file. \return 1. */
02869 static int aji_load_config(int reload)
02870 {
02871    char *cat = NULL;
02872    int debug = 1;
02873    struct ast_config *cfg = NULL;
02874    struct ast_variable *var = NULL;
02875    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
02876 
02877    if ((cfg = ast_config_load(JABBER_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
02878       return -1;
02879 
02880    /* Reset flags to default value */
02881    ast_set_flag(&globalflags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
02882 
02883    if (!cfg) {
02884       ast_log(LOG_WARNING, "No such configuration file %s\n", JABBER_CONFIG);
02885       return 0;
02886    }
02887 
02888    cat = ast_category_browse(cfg, NULL);
02889    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
02890       if (!strcasecmp(var->name, "debug"))
02891          debug = (ast_false(ast_variable_retrieve(cfg, "general", "debug"))) ? 0 : 1;
02892       else if (!strcasecmp(var->name, "autoprune"))
02893          ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOPRUNE);
02894       else if (!strcasecmp(var->name, "autoregister"))
02895          ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOREGISTER);
02896    }
02897 
02898    while (cat) {
02899       if (strcasecmp(cat, "general")) {
02900             var = ast_variable_browse(cfg, cat);
02901             aji_create_client(cat, var, debug);
02902       }
02903       cat = ast_category_browse(cfg, cat);
02904    }
02905    ast_config_destroy(cfg); /* or leak memory */
02906    return 1;
02907 }
02908 
02909 /*!
02910  * \brief grab a aji_client structure by label name or JID 
02911  * (without the resource string)
02912  * \param name label or JID 
02913  * \return aji_client.
02914  */
02915 struct aji_client *ast_aji_get_client(const char *name)
02916 {
02917    struct aji_client *client = NULL;
02918    char *aux = NULL;
02919 
02920    client = ASTOBJ_CONTAINER_FIND(&clients, name);
02921    if (!client && strchr(name, '@')) {
02922       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
02923          aux = ast_strdupa(iterator->user);
02924          if (strchr(aux, '/')) {
02925             /* strip resource for comparison */
02926             aux = strsep(&aux, "/");
02927          }
02928          if (!strncasecmp(aux, name, strlen(aux))) {
02929             client = iterator;
02930          }           
02931       });
02932    }
02933 
02934    return client;
02935 }
02936 
02937 struct aji_client_container *ast_aji_get_clients(void)
02938 {
02939    return &clients;
02940 }
02941 
02942 static char mandescr_jabber_send[] =
02943 "Description: Sends a message to a Jabber Client.\n"
02944 "Variables: \n"
02945 "  Jabber:  Client or transport Asterisk uses to connect to JABBER.\n"
02946 "  ScreenName: User Name to message.\n"
02947 "  Message: Message to be sent to the buddy\n";
02948 
02949 /*! 
02950  * \brief  Send a Jabber Message via call from the Manager 
02951  * \param s mansession Manager session
02952  * \param m message Message to send
02953  * \return  0
02954 */
02955 static int manager_jabber_send(struct mansession *s, const struct message *m)
02956 {
02957    struct aji_client *client = NULL;
02958    const char *id = astman_get_header(m,"ActionID");
02959    const char *jabber = astman_get_header(m,"Jabber");
02960    const char *screenname = astman_get_header(m,"ScreenName");
02961    const char *message = astman_get_header(m,"Message");
02962 
02963    if (ast_strlen_zero(jabber)) {
02964       astman_send_error(s, m, "No transport specified");
02965       return 0;
02966    }
02967    if (ast_strlen_zero(screenname)) {
02968       astman_send_error(s, m, "No ScreenName specified");
02969       return 0;
02970    }
02971    if (ast_strlen_zero(message)) {
02972       astman_send_error(s, m, "No Message specified");
02973       return 0;
02974    }
02975 
02976    astman_send_ack(s, m, "Attempting to send Jabber Message");
02977    client = ast_aji_get_client(jabber);
02978    if (!client) {
02979       astman_send_error(s, m, "Could not find Sender");
02980       return 0;
02981    }  
02982    if (strchr(screenname, '@') && message){
02983       ast_aji_send_chat(client, screenname, message); 
02984       astman_append(s, "Response: Success\r\n");
02985       if (!ast_strlen_zero(id))
02986          astman_append(s, "ActionID: %s\r\n",id);
02987       return 0;
02988    }
02989    astman_append(s, "Response: Error\r\n");
02990    if (!ast_strlen_zero(id))
02991       astman_append(s, "ActionID: %s\r\n",id);
02992    return 0;
02993 }
02994 
02995 /*! \brief Reload the jabber module */
02996 static int aji_reload(int reload)
02997 {
02998    int res;
02999 
03000    ASTOBJ_CONTAINER_MARKALL(&clients);
03001    if (!(res = aji_load_config(reload))) {
03002       ast_log(LOG_ERROR, "JABBER: Failed to load config.\n");
03003       return 0;
03004    } else if (res == -1)
03005       return 1;
03006 
03007    ASTOBJ_CONTAINER_PRUNE_MARKED(&clients, aji_client_destroy);
03008    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
03009       ASTOBJ_RDLOCK(iterator);
03010       if(iterator->state == AJI_DISCONNECTED) {
03011          if (!iterator->thread)
03012             ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator);
03013       } else if (iterator->state == AJI_CONNECTING)
03014          aji_get_roster(iterator);
03015       ASTOBJ_UNLOCK(iterator);
03016    });
03017    
03018    return 1;
03019 }
03020 
03021 /*! \brief Unload the jabber module */
03022 static int unload_module(void)
03023 {
03024 
03025    ast_cli_unregister_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry));
03026    ast_unregister_application(app_ajisend);
03027    ast_unregister_application(app_ajistatus);
03028    ast_manager_unregister("JabberSend");
03029    ast_custom_function_unregister(&jabberstatus_function);
03030    
03031    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
03032       ASTOBJ_RDLOCK(iterator);
03033       ast_debug(3, "JABBER: Releasing and disconnecting client: %s\n", iterator->name);
03034       iterator->state = AJI_DISCONNECTING;
03035       ast_aji_disconnect(iterator);
03036       pthread_join(iterator->thread, NULL);
03037       ASTOBJ_UNLOCK(iterator);
03038    });
03039 
03040    ASTOBJ_CONTAINER_DESTROYALL(&clients, aji_client_destroy);
03041    ASTOBJ_CONTAINER_DESTROY(&clients);
03042    return 0;
03043 }
03044 
03045 /*! \brief Unload the jabber module */
03046 static int load_module(void)
03047 {
03048    ASTOBJ_CONTAINER_INIT(&clients);
03049    if(!aji_reload(0))
03050       return AST_MODULE_LOAD_DECLINE;
03051    ast_manager_register2("JabberSend", EVENT_FLAG_SYSTEM, manager_jabber_send,
03052          "Sends a message to a Jabber Client", mandescr_jabber_send);
03053    ast_register_application(app_ajisend, aji_send_exec, ajisend_synopsis, ajisend_descrip);
03054    ast_register_application(app_ajistatus, aji_status_exec, ajistatus_synopsis, ajistatus_descrip);
03055    ast_cli_register_multiple(aji_cli, sizeof(aji_cli) / sizeof(struct ast_cli_entry));
03056    ast_custom_function_register(&jabberstatus_function);
03057 
03058    return 0;
03059 }
03060 
03061 /*! \brief Wrapper for aji_reload */
03062 static int reload(void)
03063 {
03064    aji_reload(1);
03065    return 0;
03066 }
03067 
03068 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "AJI - Asterisk Jabber Interface",
03069       .load = load_module,
03070       .unload = unload_module,
03071       .reload = reload,
03072           );

Generated on Fri Jul 24 00:41:02 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7