Thu Oct 11 06:47:20 2012

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

Generated on Thu Oct 11 06:47:20 2012 for Asterisk - the Open Source PBX by  doxygen 1.5.6