Sat Feb 11 06:33:22 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 - 2010, 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 type="external">openssl</use>
00036    <support_level>extended</support_level>
00037  ***/
00038 
00039 #include "asterisk.h"
00040 
00041 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 350223 $")
00042 
00043 #include <ctype.h>
00044 #include <iksemel.h>
00045 
00046 #include "asterisk/channel.h"
00047 #include "asterisk/jabber.h"
00048 #include "asterisk/file.h"
00049 #include "asterisk/config.h"
00050 #include "asterisk/callerid.h"
00051 #include "asterisk/lock.h"
00052 #include "asterisk/cli.h"
00053 #include "asterisk/app.h"
00054 #include "asterisk/pbx.h"
00055 #include "asterisk/md5.h"
00056 #include "asterisk/acl.h"
00057 #include "asterisk/utils.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/astobj.h"
00060 #include "asterisk/astdb.h"
00061 #include "asterisk/manager.h"
00062 #include "asterisk/event.h"
00063 #include "asterisk/devicestate.h"
00064 #include "asterisk/message.h"
00065 
00066 /*** DOCUMENTATION
00067    <application name="JabberSend" language="en_US">
00068       <synopsis>
00069          Sends an XMPP message to a buddy.
00070       </synopsis>
00071       <syntax>
00072          <parameter name="account" required="true">
00073             <para>The local named account to listen on (specified in
00074             jabber.conf)</para>
00075          </parameter>
00076          <parameter name="jid" required="true">
00077             <para>Jabber ID of the buddy to send the message to. It can be a
00078             bare JID (username@domain) or a full JID (username@domain/resource).</para>
00079          </parameter>
00080          <parameter name="message" required="true">
00081             <para>The message to send.</para>
00082          </parameter>
00083       </syntax>
00084       <description>
00085          <para>Sends the content of <replaceable>message</replaceable> as text message
00086          from the given <replaceable>account</replaceable> to the buddy identified by
00087          <replaceable>jid</replaceable></para>
00088          <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
00089          to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
00090          <replaceable>asterisk</replaceable>, configured in jabber.conf.</para>
00091       </description>
00092       <see-also>
00093          <ref type="function">JABBER_STATUS</ref>
00094          <ref type="function">JABBER_RECEIVE</ref>
00095       </see-also>
00096    </application>
00097    <function name="JABBER_RECEIVE" language="en_US">
00098       <synopsis>
00099          Reads XMPP messages.
00100       </synopsis>
00101       <syntax>
00102          <parameter name="account" required="true">
00103             <para>The local named account to listen on (specified in
00104             jabber.conf)</para>
00105          </parameter>
00106          <parameter name="jid" required="true">
00107             <para>Jabber ID of the buddy to receive message from. It can be a
00108             bare JID (username@domain) or a full JID (username@domain/resource).</para>
00109          </parameter>
00110          <parameter name="timeout">
00111             <para>In seconds, defaults to <literal>20</literal>.</para>
00112          </parameter>
00113       </syntax>
00114       <description>
00115          <para>Receives a text message on the given <replaceable>account</replaceable>
00116          from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
00117          <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
00118          sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
00119          the <replaceable>asterisk</replaceable> XMPP account configured in jabber.conf.</para>
00120       </description>
00121       <see-also>
00122          <ref type="function">JABBER_STATUS</ref>
00123          <ref type="application">JabberSend</ref>
00124       </see-also>
00125    </function>
00126    <function name="JABBER_STATUS" language="en_US">
00127       <synopsis>
00128          Retrieves a buddy's status.
00129       </synopsis>
00130       <syntax>
00131          <parameter name="account" required="true">
00132             <para>The local named account to listen on (specified in
00133             jabber.conf)</para>
00134          </parameter>
00135          <parameter name="jid" required="true">
00136             <para>Jabber ID of the buddy to receive message from. It can be a
00137             bare JID (username@domain) or a full JID (username@domain/resource).</para>
00138          </parameter>
00139       </syntax>
00140       <description>
00141          <para>Retrieves the numeric status associated with the buddy identified
00142          by <replaceable>jid</replaceable>.
00143          If the buddy does not exist in the buddylist, returns 7.</para>
00144          <para>Status will be 1-7.</para>
00145          <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
00146          <para>If not in roster variable will be set to 7.</para>
00147          <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
00148          <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
00149          the associated XMPP account configured in jabber.conf.</para>
00150       </description>
00151       <see-also>
00152          <ref type="function">JABBER_RECEIVE</ref>
00153          <ref type="application">JabberSend</ref>
00154       </see-also>
00155    </function>
00156    <application name="JabberSendGroup" language="en_US">
00157       <synopsis>
00158          Send a Jabber Message to a specified chat room
00159       </synopsis>
00160       <syntax>
00161          <parameter name="Jabber" required="true">
00162             <para>Client or transport Asterisk uses to connect to Jabber.</para>
00163          </parameter>
00164          <parameter name="RoomJID" required="true">
00165             <para>XMPP/Jabber JID (Name) of chat room.</para>
00166          </parameter>
00167          <parameter name="Message" required="true">
00168             <para>Message to be sent to the chat room.</para>
00169          </parameter>
00170          <parameter name="Nickname" required="false">
00171             <para>The nickname Asterisk uses in the chat room.</para>
00172          </parameter>
00173       </syntax>
00174       <description>
00175          <para>Allows user to send a message to a chat room via XMPP.</para>
00176          <note><para>To be able to send messages to a chat room, a user must have previously joined it. Use the <replaceable>JabberJoin</replaceable> function to do so.</para></note>
00177       </description>
00178    </application>
00179    <application name="JabberJoin" language="en_US">
00180       <synopsis>
00181          Join a chat room
00182       </synopsis>
00183       <syntax>
00184          <parameter name="Jabber" required="true">
00185             <para>Client or transport Asterisk uses to connect to Jabber.</para>
00186          </parameter>
00187          <parameter name="RoomJID" required="true">
00188             <para>XMPP/Jabber JID (Name) of chat room.</para>
00189          </parameter>
00190          <parameter name="Nickname" required="false">
00191             <para>The nickname Asterisk will use in the chat room.</para>
00192             <note><para>If a different nickname is supplied to an already joined room, the old nick will be changed to the new one.</para></note>
00193          </parameter>
00194       </syntax>
00195       <description>
00196          <para>Allows Asterisk to join a chat room.</para>
00197       </description>
00198    </application>
00199    <application name="JabberLeave" language="en_US">
00200       <synopsis>
00201          Leave a chat room
00202       </synopsis>
00203       <syntax>
00204          <parameter name="Jabber" required="true">
00205             <para>Client or transport Asterisk uses to connect to Jabber.</para>
00206          </parameter>
00207          <parameter name="RoomJID" required="true">
00208             <para>XMPP/Jabber JID (Name) of chat room.</para>
00209          </parameter>
00210          <parameter name="Nickname" required="false">
00211             <para>The nickname Asterisk uses in the chat room.</para>
00212          </parameter>
00213       </syntax>
00214       <description>
00215          <para>Allows Asterisk to leave a chat room.</para>
00216       </description>
00217    </application>
00218    <application name="JabberStatus" language="en_US">
00219       <synopsis>
00220          Retrieve the status of a jabber list member
00221       </synopsis>
00222       <syntax>
00223          <parameter name="Jabber" required="true">
00224             <para>Client or transport Asterisk users to connect to Jabber.</para>
00225          </parameter>
00226          <parameter name="JID" required="true">
00227             <para>XMPP/Jabber JID (Name) of recipient.</para>
00228          </parameter>
00229          <parameter name="Variable" required="true">
00230             <para>Variable to store the status of requested user.</para>
00231          </parameter>
00232       </syntax>
00233       <description>
00234          <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
00235          <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
00236          The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
00237          <enumlist>
00238             <enum name="1">
00239                <para>Online.</para>
00240             </enum>
00241             <enum name="2">
00242                <para>Chatty.</para>
00243             </enum>
00244             <enum name="3">
00245                <para>Away.</para>
00246             </enum>
00247             <enum name="4">
00248                <para>Extended Away.</para>
00249             </enum>
00250             <enum name="5">
00251                <para>Do Not Disturb.</para>
00252             </enum>
00253             <enum name="6">
00254                <para>Offline.</para>
00255             </enum>
00256             <enum name="7">
00257                <para>Not In Roster.</para>
00258             </enum>
00259          </enumlist>
00260       </description>
00261         </application>
00262    <manager name="JabberSend" language="en_US">
00263       <synopsis>
00264          Sends a message to a Jabber Client.
00265       </synopsis>
00266       <syntax>
00267          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00268          <parameter name="Jabber" required="true">
00269             <para>Client or transport Asterisk uses to connect to JABBER.</para>
00270          </parameter>
00271          <parameter name="JID" required="true">
00272             <para>XMPP/Jabber JID (Name) of recipient.</para>
00273          </parameter>
00274          <parameter name="Message" required="true">
00275             <para>Message to be sent to the buddy.</para>
00276          </parameter>
00277       </syntax>
00278       <description>
00279          <para>Sends a message to a Jabber Client.</para>
00280       </description>
00281    </manager>
00282  ***/
00283 
00284 /*!\todo This should really be renamed to xmpp.conf. For backwards compatibility, we
00285  * need to read both files */
00286 #define JABBER_CONFIG "jabber.conf"
00287 
00288 /*-- Forward declarations */
00289 static void aji_message_destroy(struct aji_message *obj);
00290 static int aji_is_secure(struct aji_client *client);
00291 #ifdef HAVE_OPENSSL
00292 static int aji_start_tls(struct aji_client *client);
00293 static int aji_tls_handshake(struct aji_client *client);
00294 #endif
00295 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
00296 static int aji_recv(struct aji_client *client, int timeout);
00297 static int aji_send_header(struct aji_client *client, const char *to);
00298 static int aji_send_raw(struct aji_client *client, const char *xmlstr);
00299 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
00300 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
00301 static int aji_act_hook(void *data, int type, iks *node);
00302 static void aji_handle_iq(struct aji_client *client, iks *node);
00303 static void aji_handle_message(struct aji_client *client, ikspak *pak);
00304 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
00305 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
00306 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message);
00307 static void *aji_recv_loop(void *data);
00308 static int aji_initialize(struct aji_client *client);
00309 static int aji_client_connect(void *data, ikspak *pak);
00310 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
00311 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc);
00312 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00313 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00314 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00315 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00316 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
00317 static int aji_create_client(char *label, struct ast_variable *var, int debug);
00318 static int aji_create_buddy(char *label, struct aji_client *client);
00319 static int aji_reload(int reload);
00320 static int aji_load_config(int reload);
00321 static void aji_pruneregister(struct aji_client *client);
00322 static int aji_filter_roster(void *data, ikspak *pak);
00323 static int aji_get_roster(struct aji_client *client);
00324 static int aji_client_info_handler(void *data, ikspak *pak);
00325 static int aji_dinfo_handler(void *data, ikspak *pak);
00326 static int aji_ditems_handler(void *data, ikspak *pak);
00327 static int aji_register_query_handler(void *data, ikspak *pak);
00328 static int aji_register_approve_handler(void *data, ikspak *pak);
00329 static int aji_reconnect(struct aji_client *client);
00330 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd,
00331    struct ast_cli_args *a);
00332 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd,
00333    struct ast_cli_args *a);
00334 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
00335    ast_cli_args *a);
00336 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
00337       ast_cli_args *a);
00338 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid);
00339 static int aji_receive_node_list(void *data, ikspak* pak);
00340 static void aji_init_event_distribution(struct aji_client *client);
00341 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type,
00342    const char *name, const char *collection_name);
00343 static iks* aji_build_node_config(iks *pubsub, const char *node_type,
00344    const char *collection_name);
00345 static void aji_create_pubsub_collection(struct aji_client *client,
00346    const char *collection_name);
00347 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
00348    const char *leaf_name);
00349 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd,
00350    struct ast_cli_args *a);
00351 static void aji_create_affiliations(struct aji_client *client, const char *node);
00352 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type);
00353 static void aji_publish_device_state(struct aji_client *client, const char * device,
00354    const char *device_state);
00355 static int aji_handle_pubsub_error(void *data, ikspak *pak);
00356 static int aji_handle_pubsub_event(void *data, ikspak *pak);
00357 static void aji_pubsub_subscribe(struct aji_client *client, const char *node);
00358 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name);
00359 static iks* aji_build_node_request(struct aji_client *client, const char *collection);
00360 static int aji_delete_node_list(void *data, ikspak* pak);
00361 static void aji_pubsub_purge_nodes(struct aji_client *client,
00362    const char* collection_name);
00363 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
00364    const char *context, const char *oldmsgs, const char *newmsgs);
00365 static void aji_devstate_cb(const struct ast_event *ast_event, void *data);
00366 static void aji_mwi_cb(const struct ast_event *ast_event, void *data);
00367 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
00368    const char *event_type);
00369 /* No transports in this version */
00370 /*
00371 static int aji_create_transport(char *label, struct aji_client *client);
00372 static int aji_register_transport(void *data, ikspak *pak);
00373 static int aji_register_transport2(void *data, ikspak *pak);
00374 */
00375 
00376 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from);
00377 
00378 static const struct ast_msg_tech msg_tech = {
00379    .name = "xmpp",
00380    .msg_send = msg_send_cb,
00381 };
00382 
00383 static struct ast_cli_entry aji_cli[] = {
00384    AST_CLI_DEFINE(aji_do_set_debug, "Enable/Disable Jabber debug"),
00385    AST_CLI_DEFINE(aji_do_reload, "Reload Jabber configuration"),
00386    AST_CLI_DEFINE(aji_show_clients, "Show state of clients and components"),
00387    AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
00388    AST_CLI_DEFINE(aji_test, "Shows roster, but is generally used for mog's debugging."),
00389    AST_CLI_DEFINE(aji_cli_create_collection, "Creates a PubSub node collection."),
00390    AST_CLI_DEFINE(aji_cli_list_pubsub_nodes, "Lists PubSub nodes"),
00391    AST_CLI_DEFINE(aji_cli_create_leafnode, "Creates a PubSub leaf node"),
00392    AST_CLI_DEFINE(aji_cli_delete_pubsub_node, "Deletes a PubSub node"),
00393    AST_CLI_DEFINE(aji_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
00394 };
00395 
00396 static char *app_ajisend = "JabberSend";
00397 static char *app_ajisendgroup = "JabberSendGroup";
00398 static char *app_ajistatus = "JabberStatus";
00399 static char *app_ajijoin = "JabberJoin";
00400 static char *app_ajileave = "JabberLeave";
00401 
00402 static struct aji_client_container clients;
00403 static struct aji_capabilities *capabilities = NULL;
00404 static struct ast_event_sub *mwi_sub = NULL;
00405 static struct ast_event_sub *device_state_sub = NULL;
00406 static ast_cond_t message_received_condition;
00407 static ast_mutex_t messagelock;
00408 
00409 /*! \brief Global flags, initialized to default values */
00410 static struct ast_flags globalflags = { AJI_AUTOREGISTER | AJI_AUTOACCEPT };
00411 
00412 /*! \brief PubSub flags, initialized to default values */
00413 static struct ast_flags pubsubflags = { 0 };
00414 /*!
00415  * \internal
00416  * \brief Deletes the aji_client data structure.
00417  * \param obj aji_client The structure we will delete.
00418  * \return void.
00419  */
00420 void ast_aji_client_destroy(struct aji_client *obj)
00421 {
00422    struct aji_message *tmp;
00423    ASTOBJ_CONTAINER_DESTROYALL(&obj->buddies, ast_aji_buddy_destroy);
00424    ASTOBJ_CONTAINER_DESTROY(&obj->buddies);
00425    iks_filter_delete(obj->f);
00426    iks_parser_delete(obj->p);
00427    iks_stack_delete(obj->stack);
00428    AST_LIST_LOCK(&obj->messages);
00429    while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
00430       aji_message_destroy(tmp);
00431    }
00432    AST_LIST_HEAD_DESTROY(&obj->messages);
00433    ast_free(obj);
00434 }
00435 
00436 /*!
00437  * \internal
00438  * \brief Deletes the aji_buddy data structure.
00439  * \param obj aji_buddy The structure we will delete.
00440  * \return void.
00441  */
00442 void ast_aji_buddy_destroy(struct aji_buddy *obj)
00443 {
00444    struct aji_resource *tmp;
00445 
00446    while ((tmp = obj->resources)) {
00447       obj->resources = obj->resources->next;
00448       ast_free(tmp->description);
00449       ast_free(tmp);
00450    }
00451 
00452    ast_free(obj);
00453 }
00454 
00455 /*!
00456  * \internal
00457  * \brief Deletes the aji_message data structure.
00458  * \param obj aji_message The structure we will delete.
00459  * \return void.
00460  */
00461 static void aji_message_destroy(struct aji_message *obj)
00462 {
00463    if (obj->from) {
00464       ast_free(obj->from);
00465    }
00466    if (obj->message) {
00467       ast_free(obj->message);
00468    }
00469    ast_free(obj);
00470 }
00471 
00472 /*!
00473  * \internal
00474  * \brief Find version in XML stream and populate our capabilities list
00475  * \param node the node attribute in the caps element we'll look for or add to
00476  * our list
00477  * \param version the version attribute in the caps element we'll look for or
00478  * add to our list
00479  * \param pak struct The XML stanza we're processing
00480  * \return a pointer to the added or found aji_version structure
00481  */
00482 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
00483 {
00484    struct aji_capabilities *list = NULL;
00485    struct aji_version *res = NULL;
00486 
00487    list = capabilities;
00488 
00489    if (!node) {
00490       node = pak->from->full;
00491    }
00492    if (!version) {
00493       version = "none supplied.";
00494    }
00495    while (list) {
00496       if (!strcasecmp(list->node, node)) {
00497          res = list->versions;
00498          while(res) {
00499             if (!strcasecmp(res->version, version)) {
00500                return res;
00501             }
00502             res = res->next;
00503          }
00504          /* Specified version not found. Let's add it to
00505             this node in our capabilities list */
00506          if (!res) {
00507             res = ast_malloc(sizeof(*res));
00508             if (!res) {
00509                ast_log(LOG_ERROR, "Out of memory!\n");
00510                return NULL;
00511             }
00512             res->jingle = 0;
00513             res->parent = list;
00514             ast_copy_string(res->version, version, sizeof(res->version));
00515             res->next = list->versions;
00516             list->versions = res;
00517             return res;
00518          }
00519       }
00520       list = list->next;
00521    }
00522    /* Specified node not found. Let's add it our capabilities list */
00523    if (!list) {
00524       list = ast_malloc(sizeof(*list));
00525       if (!list) {
00526          ast_log(LOG_ERROR, "Out of memory!\n");
00527          return NULL;
00528       }
00529       res = ast_malloc(sizeof(*res));
00530       if (!res) {
00531          ast_log(LOG_ERROR, "Out of memory!\n");
00532          ast_free(list);
00533          return NULL;
00534       }
00535       ast_copy_string(list->node, node, sizeof(list->node));
00536       ast_copy_string(res->version, version, sizeof(res->version));
00537       res->jingle = 0;
00538       res->parent = list;
00539       res->next = NULL;
00540       list->versions = res;
00541       list->next = capabilities;
00542       capabilities = list;
00543    }
00544    return res;
00545 }
00546 
00547 /*!
00548  * \internal
00549  * \brief Find the aji_resource we want
00550  * \param buddy aji_buddy A buddy
00551  * \param name
00552  * \return aji_resource object
00553 */
00554 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
00555 {
00556    struct aji_resource *res = NULL;
00557    if (!buddy || !name) {
00558       return res;
00559    }
00560    res = buddy->resources;
00561    while (res) {
00562       if (!strcasecmp(res->resource, name)) {
00563          break;
00564       }
00565       res = res->next;
00566    }
00567    return res;
00568 }
00569 
00570 /*!
00571  * \internal
00572  * \brief Jabber GTalk function
00573  * \param node iks
00574  * \return 1 on success, 0 on failure.
00575 */
00576 static int gtalk_yuck(iks *node)
00577 {
00578    if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps")) {
00579       ast_debug(1, "Found resource with Googletalk voice capabilities\n");
00580       return 1;
00581    } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 camera-v1 video-v1 voice-v1")) {
00582       ast_debug(1, "Found resource with Gmail voice/video chat capabilities\n");
00583       return 1;
00584    } else if (iks_find_with_attrib(node, "caps:c", "ext", "pmuc-v1 sms-v1 video-v1 voice-v1")) {
00585       ast_debug(1, "Found resource with Gmail voice/video chat capabilities (no camera)\n");
00586       return 1;
00587    }
00588 
00589    return 0;
00590 }
00591 
00592 /*!
00593  * \internal
00594  * \brief Setup the authentication struct
00595  * \param id iksid 
00596  * \param pass password
00597  * \param sid
00598  * \return x iks
00599 */
00600 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
00601 {
00602    iks *x, *y;
00603    x = iks_new("iq");
00604    iks_insert_attrib(x, "type", "set");
00605    y = iks_insert(x, "query");
00606    iks_insert_attrib(y, "xmlns", IKS_NS_AUTH);
00607    iks_insert_cdata(iks_insert(y, "username"), id->user, 0);
00608    iks_insert_cdata(iks_insert(y, "resource"), id->resource, 0);
00609    if (sid) {
00610       char buf[41];
00611       char sidpass[100];
00612       snprintf(sidpass, sizeof(sidpass), "%s%s", sid, pass);
00613       ast_sha1_hash(buf, sidpass);
00614       iks_insert_cdata(iks_insert(y, "digest"), buf, 0);
00615    } else {
00616       iks_insert_cdata(iks_insert(y, "password"), pass, 0);
00617    }
00618    return x;
00619 }
00620 
00621 /*!
00622  * \internal
00623  * \brief Dial plan function status(). puts the status of watched user 
00624  * into a channel variable.
00625  * \param chan ast_channel
00626  * \param data
00627  * \retval 0 success
00628  * \retval -1 error
00629  */
00630 static int aji_status_exec(struct ast_channel *chan, const char *data)
00631 {
00632    struct aji_client *client = NULL;
00633    struct aji_buddy *buddy = NULL;
00634    struct aji_resource *r = NULL;
00635    char *s = NULL;
00636    int stat = 7;
00637    char status[2];
00638    static int deprecation_warning = 0;
00639    AST_DECLARE_APP_ARGS(args,
00640       AST_APP_ARG(sender);
00641       AST_APP_ARG(jid);
00642       AST_APP_ARG(variable);
00643    );
00644    AST_DECLARE_APP_ARGS(jid,
00645       AST_APP_ARG(screenname);
00646       AST_APP_ARG(resource);
00647    );
00648 
00649    if (deprecation_warning++ % 10 == 0) {
00650       ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
00651    }
00652 
00653    if (!data) {
00654       ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
00655       return 0;
00656    }
00657    s = ast_strdupa(data);
00658    AST_STANDARD_APP_ARGS(args, s);
00659 
00660    if (args.argc != 3) {
00661       ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
00662       return -1;
00663    }
00664 
00665    AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
00666    if (jid.argc < 1 || jid.argc > 2) {
00667       ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
00668       return -1;
00669    }
00670 
00671    if (!(client = ast_aji_get_client(args.sender))) {
00672       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
00673       return -1;
00674    }
00675    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
00676    if (!buddy) {
00677       ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
00678       ASTOBJ_UNREF(client, ast_aji_client_destroy);
00679       return -1;
00680    }
00681    r = aji_find_resource(buddy, jid.resource);
00682    if (!r && buddy->resources) {
00683       r = buddy->resources;
00684    }
00685    ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
00686    ASTOBJ_UNREF(client, ast_aji_client_destroy);
00687    if (!r) {
00688       ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
00689    } else {
00690       stat = r->status;
00691    }
00692    snprintf(status, sizeof(status), "%d", stat);
00693    pbx_builtin_setvar_helper(chan, args.variable, status);
00694    return 0;
00695 }
00696 
00697 /*!
00698  * \internal
00699  * \brief Dial plan funtcion to retrieve the status of a buddy.
00700  * \param channel The associated ast_channel, if there is one
00701  * \param data The account, buddy JID, and optional timeout
00702  * timeout.
00703  * \retval 0 success
00704  * \retval -1 failure
00705  */
00706 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
00707 {
00708    struct aji_client *client = NULL;
00709    struct aji_buddy *buddy = NULL;
00710    struct aji_resource *r = NULL;
00711    int stat = 7;
00712    AST_DECLARE_APP_ARGS(args,
00713       AST_APP_ARG(sender);
00714       AST_APP_ARG(jid);
00715    );
00716    AST_DECLARE_APP_ARGS(jid,
00717       AST_APP_ARG(screenname);
00718       AST_APP_ARG(resource);
00719    );
00720 
00721    if (!data) {
00722       ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
00723       return 0;
00724    }
00725    AST_STANDARD_APP_ARGS(args, data);
00726 
00727    if (args.argc != 2) {
00728       ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
00729       return -1;
00730    }
00731 
00732    AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
00733    if (jid.argc < 1 || jid.argc > 2) {
00734       ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
00735       return -1;
00736    }
00737 
00738    if (!(client = ast_aji_get_client(args.sender))) {
00739       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
00740       return -1;
00741    }
00742    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
00743    if (!buddy) {
00744       ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
00745       ASTOBJ_UNREF(client, ast_aji_client_destroy);
00746       return -1;
00747    }
00748    r = aji_find_resource(buddy, jid.resource);
00749    if (!r && buddy->resources) {
00750       r = buddy->resources;
00751    }
00752    ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
00753    ASTOBJ_UNREF(client, ast_aji_client_destroy);
00754    if (!r) {
00755       ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
00756    } else {
00757       stat = r->status;
00758    }
00759    snprintf(buf, buflen, "%d", stat);
00760    return 0;
00761 }
00762 
00763 static struct ast_custom_function jabberstatus_function = {
00764    .name = "JABBER_STATUS",
00765    .read = acf_jabberstatus_read,
00766 };
00767 
00768 /*!
00769  * \internal
00770  * \brief Dial plan function to receive a message.
00771  * \param channel The associated ast_channel, if there is one
00772  * \param data The account, JID, and optional timeout
00773  * timeout.
00774  * \retval 0 success
00775  * \retval -1 failure
00776  */
00777 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
00778 {
00779    char *aux = NULL, *parse = NULL;
00780    int timeout;
00781    int jidlen, resourcelen;
00782    struct timeval start;
00783    long diff = 0;
00784    struct aji_client *client = NULL;
00785    int found = 0;
00786    struct aji_message *tmp = NULL;
00787    AST_DECLARE_APP_ARGS(args,
00788          AST_APP_ARG(account);
00789          AST_APP_ARG(jid);
00790          AST_APP_ARG(timeout);
00791          );
00792    AST_DECLARE_APP_ARGS(jid,
00793          AST_APP_ARG(screenname);
00794          AST_APP_ARG(resource);
00795    );
00796 
00797    if (ast_strlen_zero(data)) {
00798       ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
00799       return -1;
00800    }
00801 
00802    parse = ast_strdupa(data);
00803    AST_STANDARD_APP_ARGS(args, parse);
00804 
00805    if (args.argc < 2 || args.argc > 3) {
00806       ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
00807       return -1;
00808    }
00809 
00810    parse = ast_strdupa(args.jid);
00811    AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
00812    if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > AJI_MAX_JIDLEN) {
00813       ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
00814       return -1;
00815    }
00816 
00817    if (ast_strlen_zero(args.timeout)) {
00818       timeout = 20;
00819    } else {
00820       sscanf(args.timeout, "%d", &timeout);
00821       if (timeout <= 0) {
00822          ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
00823          return -1;
00824       }
00825    }
00826 
00827    jidlen = strlen(jid.screenname);
00828    resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
00829 
00830    client = ast_aji_get_client(args.account);
00831    if (!client) {
00832       ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
00833       return -1;
00834    }
00835 
00836    ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
00837 
00838    start = ast_tvnow();
00839 
00840    if (ast_autoservice_start(chan) < 0) {
00841       ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
00842       ASTOBJ_UNREF(client, ast_aji_client_destroy);
00843       return -1;
00844    }
00845 
00846    /* search the messages list, grab the first message that matches with
00847     * the from JID we're expecting, and remove it from the messages list */
00848    while (diff < timeout) {
00849       struct timespec ts = { 0, };
00850       struct timeval wait;
00851       int res;
00852 
00853       wait = ast_tvadd(start, ast_tv(timeout, 0));
00854       ts.tv_sec = wait.tv_sec;
00855       ts.tv_nsec = wait.tv_usec * 1000;
00856 
00857       /* wait up to timeout seconds for an incoming message */
00858       ast_mutex_lock(&messagelock);
00859       res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
00860       ast_mutex_unlock(&messagelock);
00861       if (res == ETIMEDOUT) {
00862          ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
00863          break;
00864       }
00865 
00866       AST_LIST_LOCK(&client->messages);
00867       AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
00868          if (jid.argc == 1) {
00869             /* no resource provided, compare bare JIDs */
00870             if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
00871                continue;
00872             }
00873          } else {
00874             /* resource appended, compare bare JIDs and resources */
00875             char *resource = strchr(tmp->from, '/');
00876             if (!resource || strlen(resource) == 0) {
00877                ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", tmp->from);
00878                if (strncasecmp(jid.screenname, tmp->from, jidlen)) {
00879                   continue;
00880                }
00881             } else {
00882                resource ++;
00883                if (strncasecmp(jid.screenname, tmp->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
00884                   continue;
00885                }
00886             }
00887          }
00888          /* check if the message is not too old */
00889          if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
00890             ast_debug(3, "Found old message from %s, deleting it\n", tmp->from);
00891             AST_LIST_REMOVE_CURRENT(list);
00892             aji_message_destroy(tmp);
00893             continue;
00894          }
00895          found = 1;
00896          aux = ast_strdupa(tmp->message);
00897          AST_LIST_REMOVE_CURRENT(list);
00898          aji_message_destroy(tmp);
00899          break;
00900       }
00901       AST_LIST_TRAVERSE_SAFE_END;
00902       AST_LIST_UNLOCK(&client->messages);
00903       if (found) {
00904          break;
00905       }
00906 
00907       /* check timeout */
00908       diff = ast_tvdiff_ms(ast_tvnow(), start);
00909    }
00910 
00911    ASTOBJ_UNREF(client, ast_aji_client_destroy);
00912    if (ast_autoservice_stop(chan) < 0) {
00913       ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
00914    }
00915 
00916    /* return if we timed out */
00917    if (!found) {
00918       ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
00919       return -1;
00920    }
00921    ast_copy_string(buf, aux, buflen);
00922 
00923    return 0;
00924 }
00925 
00926 static struct ast_custom_function jabberreceive_function = {
00927    .name = "JABBER_RECEIVE",
00928    .read = acf_jabberreceive_read,
00929 };
00930 
00931 /*!
00932  * \internal
00933  * \brief Delete old messages from a given JID
00934  * Messages stored during more than client->message_timeout are deleted
00935  * \param client Asterisk's XMPP client
00936  * \param from the JID we received messages from
00937  * \retval the number of deleted messages
00938  * \retval -1 failure
00939  */
00940 static int delete_old_messages(struct aji_client *client, char *from)
00941 {
00942    int deleted = 0;
00943    int isold = 0;
00944    struct aji_message *tmp = NULL;
00945    if (!client) {
00946       ast_log(LOG_ERROR, "Cannot find our XMPP client\n");
00947       return -1;
00948    }
00949 
00950    /* remove old messages */
00951    AST_LIST_LOCK(&client->messages);
00952    if (AST_LIST_EMPTY(&client->messages)) {
00953       AST_LIST_UNLOCK(&client->messages);
00954       return 0;
00955    }
00956 
00957    AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, tmp, list) {
00958       if (isold) {
00959          if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
00960             AST_LIST_REMOVE_CURRENT(list);
00961             aji_message_destroy(tmp);
00962             deleted ++;
00963          }
00964       } else if (ast_tvdiff_sec(ast_tvnow(), tmp->arrived) >= client->message_timeout) {
00965          isold = 1;
00966          if (!from || !strncasecmp(from, tmp->from, strlen(from))) {
00967             AST_LIST_REMOVE_CURRENT(list);
00968             aji_message_destroy(tmp);
00969             deleted ++;
00970          }
00971       }
00972    }
00973    AST_LIST_TRAVERSE_SAFE_END;
00974    AST_LIST_UNLOCK(&client->messages);
00975 
00976    return deleted;
00977 }
00978 
00979 /*!
00980  * \internal
00981  * \brief Delete old messages
00982  * Messages stored during more than client->message_timeout are deleted
00983  * \param client Asterisk's XMPP client
00984  * \retval the number of deleted messages
00985  * \retval -1 failure
00986  */
00987 static int delete_old_messages_all(struct aji_client *client)
00988 {
00989    return delete_old_messages(client, NULL);
00990 }
00991 
00992 /*!
00993 * \brief Application to join a chat room
00994 * \param chan ast_channel
00995 * \param data  Data is sender|jid|nickname.
00996 * \retval 0 success
00997 * \retval -1 error
00998 */
00999 static int aji_join_exec(struct ast_channel *chan, const char *data)
01000 {
01001    struct aji_client *client = NULL;
01002    char *s;
01003    char nick[AJI_MAX_RESJIDLEN];
01004 
01005    AST_DECLARE_APP_ARGS(args,
01006       AST_APP_ARG(sender);
01007       AST_APP_ARG(jid);
01008       AST_APP_ARG(nick);
01009    );
01010 
01011    if (!data) {
01012       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
01013       return -1;
01014    }
01015    s = ast_strdupa(data);
01016 
01017    AST_STANDARD_APP_ARGS(args, s);
01018    if (args.argc < 2 || args.argc > 3) {
01019       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
01020       return -1;
01021    }
01022 
01023    if (strchr(args.jid, '/')) {
01024       ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
01025       return -1;
01026    }
01027 
01028    if (!(client = ast_aji_get_client(args.sender))) {
01029       ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
01030       return -1;
01031    }
01032 
01033    if (!ast_strlen_zero(args.nick)) {
01034       snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
01035    } else {
01036       if (client->component) {
01037          sprintf(nick, "asterisk");
01038       } else {
01039          snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
01040       }
01041    }
01042 
01043    if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
01044       ast_aji_join_chat(client, args.jid, nick);
01045    } else {
01046       ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
01047    }
01048 
01049    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01050    return 0;
01051 }
01052 
01053 /*!
01054 * \brief Application to leave a chat room
01055 * \param chan ast_channel
01056 * \param data  Data is sender|jid|nickname.
01057 * \retval 0 success
01058 * \retval -1 error
01059 */
01060 static int aji_leave_exec(struct ast_channel *chan, const char *data)
01061 {
01062    struct aji_client *client = NULL;
01063    char *s;
01064    char nick[AJI_MAX_RESJIDLEN];
01065    AST_DECLARE_APP_ARGS(args,
01066       AST_APP_ARG(sender);
01067       AST_APP_ARG(jid);
01068       AST_APP_ARG(nick);
01069    );
01070 
01071    if (!data) {
01072       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
01073       return -1;
01074    }
01075    s = ast_strdupa(data);
01076 
01077    AST_STANDARD_APP_ARGS(args, s);
01078    if (args.argc < 2 || args.argc > 3) {
01079       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
01080       return -1;
01081    }
01082 
01083    if (strchr(args.jid, '/')) {
01084       ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
01085       return -1;
01086    }
01087 
01088    if (!(client = ast_aji_get_client(args.sender))) {
01089       ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
01090       return -1;
01091    }
01092 
01093    if (!ast_strlen_zero(args.nick)) {
01094       snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
01095    } else {
01096       if (client->component) {
01097          sprintf(nick, "asterisk");
01098       } else {
01099          snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
01100       }
01101    }
01102 
01103    if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
01104       ast_aji_leave_chat(client, args.jid, nick);
01105    }
01106    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01107    return 0;
01108 }
01109 
01110 /*!
01111  * \internal
01112  * \brief Dial plan function to send a message.
01113  * \param chan ast_channel
01114  * \param data  Data is account,jid,message.
01115  * \retval 0 success
01116  * \retval -1 failure
01117  */
01118 static int aji_send_exec(struct ast_channel *chan, const char *data)
01119 {
01120    struct aji_client *client = NULL;
01121    char *s;
01122    AST_DECLARE_APP_ARGS(args,
01123       AST_APP_ARG(sender);
01124       AST_APP_ARG(recipient);
01125       AST_APP_ARG(message);
01126    );
01127 
01128    if (!data) {
01129       ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
01130       return -1;
01131    }
01132    s = ast_strdupa(data);
01133 
01134    AST_STANDARD_APP_ARGS(args, s);
01135    if (args.argc < 3) {
01136       ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
01137       return -1;
01138    }
01139 
01140    if (!(client = ast_aji_get_client(args.sender))) {
01141       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
01142       return -1;
01143    }
01144    if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message)) {
01145       ast_aji_send_chat(client, args.recipient, args.message);
01146    }
01147    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01148    return 0;
01149 }
01150 
01151 static int msg_send_cb(const struct ast_msg *msg, const char *to, const char *from)
01152 {
01153    struct aji_client *client;
01154    char *sender;
01155    char *dest;
01156    int res;
01157 
01158    sender = ast_strdupa(from);
01159    strsep(&sender, ":");
01160    dest = ast_strdupa(to);
01161    strsep(&dest, ":");
01162 
01163    if (ast_strlen_zero(sender)) {
01164       ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for xmpp\n", from);
01165       return -1;
01166    }
01167 
01168    if (!(client = ast_aji_get_client(sender))) {
01169       ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
01170       return -1;
01171    }
01172 
01173    ast_debug(1, "Sending message to '%s' from '%s'\n", dest, client->name);
01174 
01175    res = ast_aji_send_chat(client, dest, ast_msg_get_body(msg));
01176    if (res != IKS_OK) {
01177       ast_log(LOG_WARNING, "Failed to send xmpp message (%d).\n", res);
01178    }
01179 
01180    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01181    return res == IKS_OK ? 0 : -1;
01182 }
01183 
01184 /*!
01185 * \brief Application to send a message to a groupchat.
01186 * \param chan ast_channel
01187 * \param data  Data is sender|groupchat|message.
01188 * \retval 0 success
01189 * \retval -1 error
01190 */
01191 static int aji_sendgroup_exec(struct ast_channel *chan, const char *data)
01192 {
01193    struct aji_client *client = NULL;
01194    char *s;
01195    char nick[AJI_MAX_RESJIDLEN];
01196    int res = 0;
01197    AST_DECLARE_APP_ARGS(args,
01198       AST_APP_ARG(sender);
01199       AST_APP_ARG(groupchat);
01200       AST_APP_ARG(message);
01201       AST_APP_ARG(nick);
01202    );
01203 
01204    if (!data) {
01205       ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
01206       return -1;
01207    }
01208    s = ast_strdupa(data);
01209 
01210    AST_STANDARD_APP_ARGS(args, s);
01211    if (args.argc < 3 || args.argc > 4) {
01212       ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
01213       return -1;
01214    }
01215 
01216    if (!(client = ast_aji_get_client(args.sender))) {
01217       ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
01218       return -1;
01219    }
01220 
01221    if (ast_strlen_zero(args.nick) || args.argc == 3) {
01222       if (client->component) {
01223          sprintf(nick, "asterisk");
01224       } else {
01225          snprintf(nick, AJI_MAX_RESJIDLEN, "%s", client->jid->user);
01226       }
01227    } else {
01228       snprintf(nick, AJI_MAX_RESJIDLEN, "%s", args.nick);
01229    }
01230 
01231    if (strchr(args.groupchat, '@') && !ast_strlen_zero(args.message)) {
01232       res = ast_aji_send_groupchat(client, nick, args.groupchat, args.message);
01233    }
01234 
01235    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01236    if (res != IKS_OK) {
01237       return -1;
01238    }
01239    return 0;
01240 }
01241 
01242 /*!
01243  * \internal
01244  * \brief Tests whether the connection is secured or not
01245  * \return 0 if the connection is not secured
01246  */
01247 static int aji_is_secure(struct aji_client *client)
01248 {
01249 #ifdef HAVE_OPENSSL
01250    return client->stream_flags & SECURE;
01251 #else
01252    return 0;
01253 #endif
01254 }
01255 
01256 #ifdef HAVE_OPENSSL
01257 /*!
01258  * \internal
01259  * \brief Starts the TLS procedure
01260  * \param client the configured XMPP client we use to connect to a XMPP server
01261  * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
01262  * if OpenSSL is not installed
01263  */
01264 static int aji_start_tls(struct aji_client *client)
01265 {
01266    int ret;
01267 
01268    /* This is sent not encrypted */
01269    if ((ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"))) {
01270       return ret;
01271    }
01272 
01273    client->stream_flags |= TRY_SECURE;
01274    return IKS_OK;
01275 }
01276 
01277 /*!
01278  * \internal
01279  * \brief TLS handshake, OpenSSL initialization
01280  * \param client the configured XMPP client we use to connect to a XMPP server
01281  * \return IKS_OK on success, IKS_NET_TLSFAIL on failure
01282  */
01283 static int aji_tls_handshake(struct aji_client *client)
01284 {
01285    int ret;
01286    int sock;
01287 
01288    ast_debug(1, "Starting TLS handshake\n");
01289 
01290    /* Choose an SSL/TLS protocol version, create SSL_CTX */
01291    client->ssl_method = SSLv3_method();
01292    if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
01293       return IKS_NET_TLSFAIL;
01294    }
01295 
01296    /* Create new SSL session */
01297    if (!(client->ssl_session = SSL_new(client->ssl_context))) {
01298       return IKS_NET_TLSFAIL;
01299    }
01300 
01301    /* Enforce TLS on our XMPP connection */
01302    sock = iks_fd(client->p);
01303    if (!(ret = SSL_set_fd(client->ssl_session, sock))) {
01304       return IKS_NET_TLSFAIL;
01305    }
01306 
01307    /* Perform SSL handshake */
01308    if (!(ret = SSL_connect(client->ssl_session))) {
01309       return IKS_NET_TLSFAIL;
01310    }
01311 
01312    client->stream_flags &= (~TRY_SECURE);
01313    client->stream_flags |= SECURE;
01314 
01315    /* Sent over the established TLS connection */
01316    if ((ret = aji_send_header(client, client->jid->server)) != IKS_OK) {
01317       return IKS_NET_TLSFAIL;
01318    }
01319 
01320    ast_debug(1, "TLS started with server\n");
01321 
01322    return IKS_OK;
01323 }
01324 #endif /* HAVE_OPENSSL */
01325 
01326 /*!
01327  * \internal
01328  * \brief Secured or unsecured IO socket receiving function
01329  * \param client the configured XMPP client we use to connect to a XMPP server
01330  * \param buffer the reception buffer
01331  * \param buf_len the size of the buffer
01332  * \param timeout the select timer
01333  * \retval the number of read bytes
01334  * \retval 0 timeout expiration
01335  * \retval -1 error
01336  */
01337 static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
01338 {
01339    struct pollfd pfd = { .events = POLLIN };
01340    int len, res;
01341 
01342 #ifdef HAVE_OPENSSL
01343    if (aji_is_secure(client)) {
01344       pfd.fd = SSL_get_fd(client->ssl_session);
01345       if (pfd.fd < 0) {
01346          return -1;
01347       }
01348    } else
01349 #endif /* HAVE_OPENSSL */
01350       pfd.fd = iks_fd(client->p);
01351 
01352    res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
01353    if (res > 0) {
01354 #ifdef HAVE_OPENSSL
01355       if (aji_is_secure(client)) {
01356          len = SSL_read(client->ssl_session, buffer, buf_len);
01357       } else
01358 #endif /* HAVE_OPENSSL */
01359          len = recv(pfd.fd, buffer, buf_len, 0);
01360 
01361       if (len > 0) {
01362          return len;
01363       } else if (len <= 0) {
01364          return -1;
01365       }
01366    }
01367    return res;
01368 }
01369 
01370 /*!
01371  * \internal
01372  * \brief Tries to receive data from the Jabber server
01373  * \param client the configured XMPP client we use to connect to a XMPP server
01374  * \param timeout the timeout value
01375  * This function receives (encrypted or unencrypted) data from the XMPP server,
01376  * and passes it to the parser.
01377  * \retval IKS_OK success
01378  * \retval IKS_NET_RWERR IO error
01379  * \retval IKS_NET_NOCONN no connection available
01380  * \retval IKS_NET_EXPIRED timeout expiration
01381  */
01382 static int aji_recv (struct aji_client *client, int timeout)
01383 {
01384    int len, ret;
01385    char buf[NET_IO_BUF_SIZE - 1];
01386    char newbuf[NET_IO_BUF_SIZE - 1];
01387    int pos = 0;
01388    int newbufpos = 0;
01389    unsigned char c;
01390 
01391    memset(buf, 0, sizeof(buf));
01392    memset(newbuf, 0, sizeof(newbuf));
01393 
01394    while (1) {
01395       len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
01396       if (len < 0) return IKS_NET_RWERR;
01397       if (len == 0) return IKS_NET_EXPIRED;
01398       buf[len] = '\0';
01399 
01400       /* our iksemel parser won't work as expected if we feed
01401          it with XML packets that contain multiple whitespace
01402          characters between tags */
01403       while (pos < len) {
01404          c = buf[pos];
01405          /* if we stumble on the ending tag character,
01406             we skip any whitespace that follows it*/
01407          if (c == '>') {
01408             while (isspace(buf[pos+1])) {
01409                pos++;
01410             }
01411          }
01412          newbuf[newbufpos] = c;
01413          newbufpos ++;
01414          pos++;
01415       }
01416       pos = 0;
01417       newbufpos = 0;
01418 
01419       /* Log the message here, because iksemel's logHook is
01420          unaccessible */
01421       aji_log_hook(client, buf, len, 1);
01422 
01423       /* let iksemel deal with the string length,
01424          and reset our buffer */
01425       ret = iks_parse(client->p, newbuf, 0, 0);
01426       memset(newbuf, 0, sizeof(newbuf));
01427 
01428       switch (ret) {
01429       case IKS_NOMEM:
01430          ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
01431          break;
01432       case IKS_BADXML:
01433          ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
01434          break;
01435       case IKS_HOOK:
01436          ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
01437          break;
01438       }
01439       if (ret != IKS_OK) {
01440          return ret;
01441       }
01442       ast_debug(3, "XML parsing successful\n");
01443    }
01444    return IKS_OK;
01445 }
01446 
01447 /*!
01448  * \internal
01449  * \brief Sends XMPP header to the server
01450  * \param client the configured XMPP client we use to connect to a XMPP server
01451  * \param to the target XMPP server
01452  * \return IKS_OK on success, any other value on failure
01453  */
01454 static int aji_send_header(struct aji_client *client, const char *to)
01455 {
01456    char *msg;
01457    int len, err;
01458 
01459    len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
01460    msg = iks_malloc(len);
01461    if (!msg)
01462       return IKS_NOMEM;
01463    sprintf(msg, "<?xml version='1.0'?>"
01464       "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
01465       "%s' to='%s' version='1.0'>", client->name_space, to);
01466    err = aji_send_raw(client, msg);
01467    iks_free(msg);
01468    if (err != IKS_OK)
01469       return err;
01470 
01471    return IKS_OK;
01472 }
01473 
01474 /*!
01475  * \brief Wraps raw sending
01476  * \param client the configured XMPP client we use to connect to a XMPP server
01477  * \param x the XMPP packet to send
01478  * \return IKS_OK on success, any other value on failure
01479  */
01480 int ast_aji_send(struct aji_client *client, iks *x)
01481 {
01482    return aji_send_raw(client, iks_string(iks_stack(x), x));
01483 }
01484 
01485 /*!
01486  * \internal
01487  * \brief Sends an XML string over an XMPP connection
01488  * \param client the configured XMPP client we use to connect to a XMPP server
01489  * \param xmlstr the XML string to send
01490  * The XML data is sent whether the connection is secured or not. In the
01491  * latter case, we just call iks_send_raw().
01492  * \return IKS_OK on success, any other value on failure
01493  */
01494 static int aji_send_raw(struct aji_client *client, const char *xmlstr)
01495 {
01496    int ret;
01497 #ifdef HAVE_OPENSSL
01498    int len = strlen(xmlstr);
01499 
01500    if (aji_is_secure(client)) {
01501       ret = SSL_write(client->ssl_session, xmlstr, len);
01502       if (ret) {
01503          /* Log the message here, because iksemel's logHook is
01504             unaccessible */
01505          aji_log_hook(client, xmlstr, len, 0);
01506          return IKS_OK;
01507       }
01508    }
01509 #endif
01510    /* If needed, data will be sent unencrypted, and logHook will
01511       be called inside iks_send_raw */
01512    ret = iks_send_raw(client->p, xmlstr);
01513    if (ret != IKS_OK) {
01514       return ret;
01515    }
01516 
01517    return IKS_OK;
01518 }
01519 
01520 /*!
01521  * \internal
01522  * \brief the debug loop.
01523  * \param data void
01524  * \param xmpp xml data as string
01525  * \param size size of string
01526  * \param is_incoming direction of packet 1 for inbound 0 for outbound.
01527  */
01528 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
01529 {
01530    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01531 
01532    if (!ast_strlen_zero(xmpp)) {
01533       manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
01534    }
01535 
01536    if (client->debug) {
01537       if (is_incoming) {
01538          ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
01539       } else {
01540          if (strlen(xmpp) == 1) {
01541             if (option_debug > 2  && xmpp[0] == ' ') {
01542                ast_verbose("\nJABBER: Keep alive packet\n");
01543             }
01544          } else {
01545             ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
01546          }
01547       }
01548 
01549    }
01550    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01551 }
01552 
01553 /*!
01554  * \internal
01555  * \brief A wrapper function for iks_start_sasl
01556  * \param client the configured XMPP client we use to connect to a XMPP server
01557  * \param type the SASL authentication type. Supported types are PLAIN and MD5
01558  * \param username
01559  * \param pass password.
01560  *
01561  * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
01562  */
01563 static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
01564 {
01565    iks *x = NULL;
01566    int len;
01567    char *s;
01568    char *base64;
01569 
01570    /* trigger SASL DIGEST-MD5 only over an unsecured connection.
01571       iks_start_sasl is an iksemel API function and relies on GnuTLS,
01572       whereas we use OpenSSL */
01573    if ((type & IKS_STREAM_SASL_MD5) && !aji_is_secure(client))
01574       return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass); 
01575    if (!(type & IKS_STREAM_SASL_PLAIN)) {
01576       ast_log(LOG_ERROR, "Server does not support SASL PLAIN authentication\n");
01577       return IKS_NET_NOTSUPP;
01578    }
01579 
01580    x = iks_new("auth"); 
01581    if (!x) {
01582       ast_log(LOG_ERROR, "Out of memory.\n");
01583       return IKS_NET_NOTSUPP;
01584    }
01585 
01586    iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
01587    len = strlen(username) + strlen(pass) + 3;
01588    s = alloca(len);
01589    base64 = alloca((len + 2) * 4 / 3);
01590    iks_insert_attrib(x, "mechanism", "PLAIN");
01591    snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
01592 
01593    /* exclude the NULL training byte from the base64 encoding operation
01594       as some XMPP servers will refuse it.
01595       The format for authentication is [authzid]\0authcid\0password
01596       not [authzid]\0authcid\0password\0 */
01597    ast_base64encode(base64, (const unsigned char *) s, len - 1, (len + 2) * 4 / 3);
01598    iks_insert_cdata(x, base64, 0);
01599    ast_aji_send(client, x);
01600    iks_delete(x);
01601 
01602    return IKS_OK;
01603 }
01604 
01605 /*!
01606  * \internal
01607  * \brief The action hook parses the inbound packets, constantly running.
01608  * \param data aji client structure 
01609  * \param type type of packet 
01610  * \param node the actual packet.
01611  * \return IKS_OK or IKS_HOOK .
01612  */
01613 static int aji_act_hook(void *data, int type, iks *node)
01614 {
01615    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01616    ikspak *pak = NULL;
01617    iks *auth = NULL;
01618    int features = 0;
01619 
01620    if (!node) {
01621       ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
01622       ASTOBJ_UNREF(client, ast_aji_client_destroy);
01623       return IKS_HOOK;
01624    }
01625 
01626    if (client->state == AJI_DISCONNECTING) {
01627       ASTOBJ_UNREF(client, ast_aji_client_destroy);
01628       return IKS_HOOK;
01629    }
01630 
01631    pak = iks_packet(node);
01632 
01633    /* work around iksemel's impossibility to recognize node names
01634     * containing a semicolon. Set the namespace of the corresponding
01635     * node accordingly. */
01636    if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
01637       char *node_ns = NULL;
01638       char attr[AJI_MAX_ATTRLEN];
01639       char *node_name = iks_name(iks_child(node));
01640       char *aux = strchr(node_name, ':') + 1;
01641       snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
01642       node_ns = iks_find_attrib(iks_child(node), attr);
01643       if (node_ns) {
01644          pak->ns = node_ns;
01645          pak->query = iks_child(node);
01646       }
01647    }
01648 
01649 
01650    if (!client->component) { /*client */
01651       switch (type) {
01652       case IKS_NODE_START:
01653          if (client->usetls && !aji_is_secure(client)) {
01654 #ifndef HAVE_OPENSSL
01655             ast_log(LOG_ERROR, "TLS connection cannot be established. Please install OpenSSL and its development libraries on this system, or disable the TLS option in your configuration file\n");
01656             ASTOBJ_UNREF(client, ast_aji_client_destroy);
01657             return IKS_HOOK;
01658 #else
01659             if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
01660                ast_log(LOG_ERROR, "Could not start TLS\n");
01661                ASTOBJ_UNREF(client, ast_aji_client_destroy);
01662                return IKS_HOOK;
01663             }
01664 #endif
01665             break;
01666          }
01667          if (!client->usesasl) {
01668             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);
01669             auth = jabber_make_auth(client->jid, client->password, iks_find_attrib(node, "id"));
01670             if (auth) {
01671                iks_insert_attrib(auth, "id", client->mid);
01672                iks_insert_attrib(auth, "to", client->jid->server);
01673                ast_aji_increment_mid(client->mid);
01674                ast_aji_send(client, auth);
01675                iks_delete(auth);
01676             } else {
01677                ast_log(LOG_ERROR, "Out of memory.\n");
01678             }
01679          }
01680          break;
01681 
01682       case IKS_NODE_NORMAL:
01683 #ifdef HAVE_OPENSSL
01684          if (client->stream_flags & TRY_SECURE) {
01685             if (!strcmp("proceed", iks_name(node))) {
01686                return aji_tls_handshake(client);
01687             }
01688          }
01689 #endif
01690          if (!strcmp("stream:features", iks_name(node))) {
01691             features = iks_stream_features(node);
01692             if (client->usesasl) {
01693                if (client->usetls && !aji_is_secure(client)) {
01694                   break;
01695                }
01696                if (client->authorized) {
01697                   if (features & IKS_STREAM_BIND) {
01698                      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);
01699                      auth = iks_make_resource_bind(client->jid);
01700                      if (auth) {
01701                         iks_insert_attrib(auth, "id", client->mid);
01702                         ast_aji_increment_mid(client->mid);
01703                         ast_aji_send(client, auth);
01704                         iks_delete(auth);
01705                      } else {
01706                         ast_log(LOG_ERROR, "Out of memory.\n");
01707                         break;
01708                      }
01709                   }
01710                   if (features & IKS_STREAM_SESSION) {
01711                      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);
01712                      auth = iks_make_session();
01713                      if (auth) {
01714                         iks_insert_attrib(auth, "id", "auth");
01715                         ast_aji_increment_mid(client->mid);
01716                         ast_aji_send(client, auth);
01717                         iks_delete(auth);
01718                      } else {
01719                         ast_log(LOG_ERROR, "Out of memory.\n");
01720                      }
01721                   }
01722                } else {
01723                   int ret;
01724                   if (!client->jid->user) {
01725                      ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
01726                      break;
01727                   }
01728 
01729                   ret = aji_start_sasl(client, features, client->jid->user, client->password);
01730                   if (ret != IKS_OK) {
01731                      ASTOBJ_UNREF(client, ast_aji_client_destroy);
01732                      return IKS_HOOK;
01733                   }
01734                   break;
01735                }
01736             }
01737          } else if (!strcmp("failure", iks_name(node))) {
01738             ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
01739          } else if (!strcmp("success", iks_name(node))) {
01740             client->authorized = 1;
01741             aji_send_header(client, client->jid->server);
01742          }
01743          break;
01744       case IKS_NODE_ERROR:
01745          ast_log(LOG_ERROR, "JABBER: Node Error\n");
01746          ASTOBJ_UNREF(client, ast_aji_client_destroy);
01747          return IKS_HOOK;
01748          break;
01749       case IKS_NODE_STOP:
01750          ast_log(LOG_WARNING, "JABBER: Disconnected\n");
01751          ASTOBJ_UNREF(client, ast_aji_client_destroy);
01752          return IKS_HOOK;
01753          break;
01754       }
01755    } else if (client->state != AJI_CONNECTED && client->component) {
01756       switch (type) {
01757       case IKS_NODE_START:
01758          if (client->state == AJI_DISCONNECTED) {
01759             char secret[160], shasum[320], *handshake;
01760 
01761             sprintf(secret, "%s%s", pak->id, client->password);
01762             ast_sha1_hash(shasum, secret);
01763             handshake = NULL;
01764             if (asprintf(&handshake, "<handshake>%s</handshake>", shasum) >= 0) {
01765                aji_send_raw(client, handshake);
01766                ast_free(handshake);
01767                handshake = NULL;
01768             }
01769             client->state = AJI_CONNECTING;
01770             if (aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
01771                client->state = AJI_CONNECTED;
01772             else
01773                ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
01774             break;
01775          }
01776          break;
01777 
01778       case IKS_NODE_NORMAL:
01779          break;
01780 
01781       case IKS_NODE_ERROR:
01782          ast_log(LOG_ERROR, "JABBER: Node Error\n");
01783          ASTOBJ_UNREF(client, ast_aji_client_destroy);
01784          return IKS_HOOK;
01785 
01786       case IKS_NODE_STOP:
01787          ast_log(LOG_WARNING, "JABBER: Disconnected\n");
01788          ASTOBJ_UNREF(client, ast_aji_client_destroy);
01789          return IKS_HOOK;
01790       }
01791    }
01792 
01793    switch (pak->type) {
01794    case IKS_PAK_NONE:
01795       ast_debug(1, "JABBER: I don't know what to do with paktype NONE.\n");
01796       break;
01797    case IKS_PAK_MESSAGE:
01798       aji_handle_message(client, pak);
01799       ast_debug(1, "JABBER: Handling paktype MESSAGE.\n");
01800       break;
01801    case IKS_PAK_PRESENCE:
01802       aji_handle_presence(client, pak);
01803       ast_debug(1, "JABBER: Handling paktype PRESENCE\n");
01804       break;
01805    case IKS_PAK_S10N:
01806       aji_handle_subscribe(client, pak);
01807       ast_debug(1, "JABBER: Handling paktype S10N\n");
01808       break;
01809    case IKS_PAK_IQ:
01810       ast_debug(1, "JABBER: Handling paktype IQ\n");
01811       aji_handle_iq(client, node);
01812       break;
01813    default:
01814       ast_debug(1, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
01815       break;
01816    }
01817 
01818    iks_filter_packet(client->f, pak);
01819 
01820    if (node)
01821       iks_delete(node);
01822 
01823    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01824    return IKS_OK;
01825 }
01826 /*!
01827  * \internal
01828  * \brief Unknown
01829  * \param data void
01830  * \param pak ikspak
01831  * \return IKS_FILTER_EAT.
01832 */
01833 static int aji_register_approve_handler(void *data, ikspak *pak)
01834 {
01835    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01836    iks *iq = NULL, *presence = NULL, *x = NULL;
01837 
01838    iq = iks_new("iq");
01839    presence = iks_new("presence");
01840    x = iks_new("x");
01841    if (client && iq && presence && x) {
01842       if (!iks_find(pak->query, "remove")) {
01843          iks_insert_attrib(iq, "from", client->jid->full);
01844          iks_insert_attrib(iq, "to", pak->from->full);
01845          iks_insert_attrib(iq, "id", pak->id);
01846          iks_insert_attrib(iq, "type", "result");
01847          ast_aji_send(client, iq);
01848 
01849          iks_insert_attrib(presence, "from", client->jid->full);
01850          iks_insert_attrib(presence, "to", pak->from->partial);
01851          iks_insert_attrib(presence, "id", client->mid);
01852          ast_aji_increment_mid(client->mid);
01853          iks_insert_attrib(presence, "type", "subscribe");
01854          iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
01855          iks_insert_node(presence, x);
01856          ast_aji_send(client, presence);
01857       }
01858    } else {
01859       ast_log(LOG_ERROR, "Out of memory.\n");
01860    }
01861 
01862    iks_delete(iq);
01863    iks_delete(presence);
01864    iks_delete(x);
01865 
01866    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01867    return IKS_FILTER_EAT;
01868 }
01869 /*!
01870  * \internal
01871  * \brief register handler for incoming querys (IQ's)
01872  * \param data incoming aji_client request
01873  * \param pak ikspak
01874  * \return IKS_FILTER_EAT.
01875 */
01876 static int aji_register_query_handler(void *data, ikspak *pak)
01877 {
01878    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01879    struct aji_buddy *buddy = NULL;
01880    char *node = NULL;
01881    iks *iq = NULL, *query = NULL;
01882 
01883    client = (struct aji_client *) data;
01884 
01885    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
01886    if (!buddy) {
01887       iks  *error = NULL, *notacceptable = NULL;
01888 
01889       ast_log(LOG_ERROR, "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
01890       iq = iks_new("iq");
01891       query = iks_new("query");
01892       error = iks_new("error");
01893       notacceptable = iks_new("not-acceptable");
01894       if (iq && query && error && notacceptable) {
01895          iks_insert_attrib(iq, "type", "error");
01896          iks_insert_attrib(iq, "from", client->user);
01897          iks_insert_attrib(iq, "to", pak->from->full);
01898          iks_insert_attrib(iq, "id", pak->id);
01899          iks_insert_attrib(query, "xmlns", "jabber:iq:register");
01900          iks_insert_attrib(error, "code" , "406");
01901          iks_insert_attrib(error, "type", "modify");
01902          iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
01903          iks_insert_node(iq, query);
01904          iks_insert_node(iq, error);
01905          iks_insert_node(error, notacceptable);
01906          ast_aji_send(client, iq);
01907       } else {
01908          ast_log(LOG_ERROR, "Out of memory.\n");
01909       }
01910 
01911       iks_delete(error);
01912       iks_delete(notacceptable);
01913    } else if (!(node = iks_find_attrib(pak->query, "node"))) {
01914       iks *instructions = NULL;
01915       char *explain = "Welcome to Asterisk - the Open Source PBX.\n";
01916       iq = iks_new("iq");
01917       query = iks_new("query");
01918       instructions = iks_new("instructions");
01919       if (iq && query && instructions && client) {
01920          iks_insert_attrib(iq, "from", client->user);
01921          iks_insert_attrib(iq, "to", pak->from->full);
01922          iks_insert_attrib(iq, "id", pak->id);
01923          iks_insert_attrib(iq, "type", "result");
01924          iks_insert_attrib(query, "xmlns", "jabber:iq:register");
01925          iks_insert_cdata(instructions, explain, 0);
01926          iks_insert_node(iq, query);
01927          iks_insert_node(query, instructions);
01928          ast_aji_send(client, iq);
01929       } else {
01930          ast_log(LOG_ERROR, "Out of memory.\n");
01931       }
01932 
01933       iks_delete(instructions);
01934    }
01935    iks_delete(iq);
01936    iks_delete(query);
01937    ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
01938    ASTOBJ_UNREF(client, ast_aji_client_destroy);
01939    return IKS_FILTER_EAT;
01940 }
01941 
01942 /*!
01943  * \internal
01944  * \brief Handles stuff
01945  * \param data void
01946  * \param pak ikspak
01947  * \return IKS_FILTER_EAT.
01948 */
01949 static int aji_ditems_handler(void *data, ikspak *pak)
01950 {
01951    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
01952    char *node = NULL;
01953 
01954    if (!(node = iks_find_attrib(pak->query, "node"))) {
01955       iks *iq = NULL, *query = NULL, *item = NULL;
01956       iq = iks_new("iq");
01957       query = iks_new("query");
01958       item = iks_new("item");
01959 
01960       if (iq && query && item) {
01961          iks_insert_attrib(iq, "from", client->user);
01962          iks_insert_attrib(iq, "to", pak->from->full);
01963          iks_insert_attrib(iq, "id", pak->id);
01964          iks_insert_attrib(iq, "type", "result");
01965          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
01966          iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
01967          iks_insert_attrib(item, "name", "Million Dollar Asterisk Commands");
01968          iks_insert_attrib(item, "jid", client->user);
01969 
01970          iks_insert_node(iq, query);
01971          iks_insert_node(query, item);
01972          ast_aji_send(client, iq);
01973       } else {
01974          ast_log(LOG_ERROR, "Out of memory.\n");
01975       }
01976 
01977       iks_delete(iq);
01978       iks_delete(query);
01979       iks_delete(item);
01980 
01981    } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
01982       iks *iq, *query, *confirm;
01983       iq = iks_new("iq");
01984       query = iks_new("query");
01985       confirm = iks_new("item");
01986       if (iq && query && confirm && client) {
01987          iks_insert_attrib(iq, "from", client->user);
01988          iks_insert_attrib(iq, "to", pak->from->full);
01989          iks_insert_attrib(iq, "id", pak->id);
01990          iks_insert_attrib(iq, "type", "result");
01991          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
01992          iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
01993          iks_insert_attrib(confirm, "node", "confirmaccount");
01994          iks_insert_attrib(confirm, "name", "Confirm AIM account");
01995          iks_insert_attrib(confirm, "jid", "blog.astjab.org");
01996 
01997          iks_insert_node(iq, query);
01998          iks_insert_node(query, confirm);
01999          ast_aji_send(client, iq);
02000       } else {
02001          ast_log(LOG_ERROR, "Out of memory.\n");
02002       }
02003 
02004       iks_delete(iq);
02005       iks_delete(query);
02006       iks_delete(confirm);
02007 
02008    } else if (!strcasecmp(node, "confirmaccount")) {
02009       iks *iq = NULL, *query = NULL, *feature = NULL;
02010 
02011       iq = iks_new("iq");
02012       query = iks_new("query");
02013       feature = iks_new("feature");
02014 
02015       if (iq && query && feature && client) {
02016          iks_insert_attrib(iq, "from", client->user);
02017          iks_insert_attrib(iq, "to", pak->from->full);
02018          iks_insert_attrib(iq, "id", pak->id);
02019          iks_insert_attrib(iq, "type", "result");
02020          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
02021          iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
02022          iks_insert_node(iq, query);
02023          iks_insert_node(query, feature);
02024          ast_aji_send(client, iq);
02025       } else {
02026          ast_log(LOG_ERROR, "Out of memory.\n");
02027       }
02028 
02029       iks_delete(iq);
02030       iks_delete(query);
02031       iks_delete(feature);
02032    }
02033 
02034    ASTOBJ_UNREF(client, ast_aji_client_destroy);
02035    return IKS_FILTER_EAT;
02036 
02037 }
02038 
02039 /*!
02040  * \internal
02041  * \brief Handle add extra info
02042  * \param data void
02043  * \param pak ikspak
02044  * \return IKS_FILTER_EAT
02045 */
02046 static int aji_client_info_handler(void *data, ikspak *pak)
02047 {
02048    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02049    struct aji_resource *resource = NULL;
02050    struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
02051 
02052    resource = aji_find_resource(buddy, pak->from->resource);
02053    if (pak->subtype == IKS_TYPE_RESULT) {
02054       if (!resource) {
02055          ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
02056          ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02057          ASTOBJ_UNREF(client, ast_aji_client_destroy);
02058          return IKS_FILTER_EAT;
02059       }
02060       if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
02061          resource->cap->jingle = 1;
02062       } else {
02063          resource->cap->jingle = 0;
02064       }
02065    } else if (pak->subtype == IKS_TYPE_GET) {
02066       iks *iq, *disco, *ident, *google, *query;
02067       iq = iks_new("iq");
02068       query = iks_new("query");
02069       ident = iks_new("identity");
02070       disco = iks_new("feature");
02071       google = iks_new("feature");
02072       if (iq && ident && disco && google) {
02073          iks_insert_attrib(iq, "from", client->jid->full);
02074          iks_insert_attrib(iq, "to", pak->from->full);
02075          iks_insert_attrib(iq, "type", "result");
02076          iks_insert_attrib(iq, "id", pak->id);
02077          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02078          iks_insert_attrib(ident, "category", "client");
02079          iks_insert_attrib(ident, "type", "pc");
02080          iks_insert_attrib(ident, "name", "asterisk");
02081          iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
02082          iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
02083          iks_insert_node(iq, query);
02084          iks_insert_node(query, ident);
02085          iks_insert_node(query, google);
02086          iks_insert_node(query, disco);
02087          ast_aji_send(client, iq);
02088       } else {
02089          ast_log(LOG_ERROR, "Out of Memory.\n");
02090       }
02091 
02092       iks_delete(iq);
02093       iks_delete(query);
02094       iks_delete(ident);
02095       iks_delete(google);
02096       iks_delete(disco);
02097    } else if (pak->subtype == IKS_TYPE_ERROR) {
02098       ast_log(LOG_NOTICE, "User %s does not support discovery.\n", pak->from->full);
02099    }
02100    ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02101    ASTOBJ_UNREF(client, ast_aji_client_destroy);
02102    return IKS_FILTER_EAT;
02103 }
02104 
02105 /*!
02106  * \internal
02107  * \brief Handler of the return info packet
02108  * \param data aji_client
02109  * \param pak ikspak
02110  * \return IKS_FILTER_EAT
02111 */
02112 static int aji_dinfo_handler(void *data, ikspak *pak)
02113 {
02114    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02115    char *node = NULL;
02116    struct aji_resource *resource = NULL;
02117    struct aji_buddy *buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
02118 
02119    if (pak->subtype == IKS_TYPE_ERROR) {
02120       ast_log(LOG_WARNING, "Received error from a client, turn on jabber debug!\n");
02121       ASTOBJ_UNREF(client, ast_aji_client_destroy);
02122       return IKS_FILTER_EAT;
02123    }
02124    resource = aji_find_resource(buddy, pak->from->resource);
02125    if (pak->subtype == IKS_TYPE_RESULT) {
02126       if (!resource) {
02127          ast_log(LOG_NOTICE, "JABBER: Received client info from %s when not requested.\n", pak->from->full);
02128          ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02129          ASTOBJ_UNREF(client, ast_aji_client_destroy);
02130          return IKS_FILTER_EAT;
02131       }
02132       if (iks_find_with_attrib(pak->query, "feature", "var", "http://www.google.com/xmpp/protocol/voice/v1")) {
02133          resource->cap->jingle = 1;
02134       } else {
02135          resource->cap->jingle = 0;
02136       }
02137    } else if (pak->subtype == IKS_TYPE_GET && !(node = iks_find_attrib(pak->query, "node"))) {
02138       iks *iq, *query, *identity, *disco, *reg, *commands, *gateway, *version, *vcard, *search;
02139 
02140       iq = iks_new("iq");
02141       query = iks_new("query");
02142       identity = iks_new("identity");
02143       disco = iks_new("feature");
02144       reg = iks_new("feature");
02145       commands = iks_new("feature");
02146       gateway = iks_new("feature");
02147       version = iks_new("feature");
02148       vcard = iks_new("feature");
02149       search = iks_new("feature");
02150       if (iq && query && identity && disco && reg && commands && gateway && version && vcard && search && client) {
02151          iks_insert_attrib(iq, "from", client->user);
02152          iks_insert_attrib(iq, "to", pak->from->full);
02153          iks_insert_attrib(iq, "id", pak->id);
02154          iks_insert_attrib(iq, "type", "result");
02155          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02156          iks_insert_attrib(identity, "category", "gateway");
02157          iks_insert_attrib(identity, "type", "pstn");
02158          iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
02159          iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
02160          iks_insert_attrib(reg, "var", "jabber:iq:register");
02161          iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
02162          iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
02163          iks_insert_attrib(version, "var", "jabber:iq:version");
02164          iks_insert_attrib(vcard, "var", "vcard-temp");
02165          iks_insert_attrib(search, "var", "jabber:iq:search");
02166 
02167          iks_insert_node(iq, query);
02168          iks_insert_node(query, identity);
02169          iks_insert_node(query, disco);
02170          iks_insert_node(query, reg);
02171          iks_insert_node(query, commands);
02172          iks_insert_node(query, gateway);
02173          iks_insert_node(query, version);
02174          iks_insert_node(query, vcard);
02175          iks_insert_node(query, search);
02176          ast_aji_send(client, iq);
02177       } else {
02178          ast_log(LOG_ERROR, "Out of memory.\n");
02179       }
02180 
02181       iks_delete(iq);
02182       iks_delete(query);
02183       iks_delete(identity);
02184       iks_delete(disco);
02185       iks_delete(reg);
02186       iks_delete(commands);
02187       iks_delete(gateway);
02188       iks_delete(version);
02189       iks_delete(vcard);
02190       iks_delete(search);
02191    } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "http://jabber.org/protocol/commands")) {
02192       iks *iq, *query, *confirm;
02193       iq = iks_new("iq");
02194       query = iks_new("query");
02195       confirm = iks_new("item");
02196 
02197       if (iq && query && confirm && client) {
02198          iks_insert_attrib(iq, "from", client->user);
02199          iks_insert_attrib(iq, "to", pak->from->full);
02200          iks_insert_attrib(iq, "id", pak->id);
02201          iks_insert_attrib(iq, "type", "result");
02202          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
02203          iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
02204          iks_insert_attrib(confirm, "node", "confirmaccount");
02205          iks_insert_attrib(confirm, "name", "Confirm AIM account");
02206          iks_insert_attrib(confirm, "jid", client->user);
02207          iks_insert_node(iq, query);
02208          iks_insert_node(query, confirm);
02209          ast_aji_send(client, iq);
02210       } else {
02211          ast_log(LOG_ERROR, "Out of memory.\n");
02212       }
02213 
02214       iks_delete(iq);
02215       iks_delete(query);
02216       iks_delete(confirm);
02217 
02218    } else if (pak->subtype == IKS_TYPE_GET && !strcasecmp(node, "confirmaccount")) {
02219       iks *iq, *query, *feature;
02220 
02221       iq = iks_new("iq");
02222       query = iks_new("query");
02223       feature = iks_new("feature");
02224 
02225       if (iq && query && feature && client) {
02226          iks_insert_attrib(iq, "from", client->user);
02227          iks_insert_attrib(iq, "to", pak->from->full);
02228          iks_insert_attrib(iq, "id", pak->id);
02229          iks_insert_attrib(iq, "type", "result");
02230          iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02231          iks_insert_attrib(feature, "var", "http://jabber.org/protocol/commands");
02232          iks_insert_node(iq, query);
02233          iks_insert_node(query, feature);
02234          ast_aji_send(client, iq);
02235       } else {
02236          ast_log(LOG_ERROR, "Out of memory.\n");
02237       }
02238 
02239       iks_delete(iq);
02240       iks_delete(query);
02241       iks_delete(feature);
02242    }
02243 
02244    ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02245    ASTOBJ_UNREF(client, ast_aji_client_destroy);
02246    return IKS_FILTER_EAT;
02247 }
02248 
02249 /*!
02250  * \internal
02251  * \brief Handles \verbatim <iq> \endverbatim stanzas.
02252  * \param client the configured XMPP client we use to connect to a XMPP server
02253  * \param node iks
02254  * \return void.
02255  */
02256 static void aji_handle_iq(struct aji_client *client, iks *node)
02257 {
02258    /*Nothing to see here */
02259 }
02260 
02261 /*!
02262  * \internal
02263  * \brief Handles \verbatim <message>\endverbatim stanzas.
02264  * Adds the incoming message to the client's message list.
02265  * \param client the configured XMPP client we use to connect to a XMPP server
02266  * \param pak ikspak the node
02267  */
02268 static void aji_handle_message(struct aji_client *client, ikspak *pak)
02269 {
02270    struct aji_message *insert;
02271    int deleted = 0;
02272    struct ast_msg *msg;
02273 
02274    ast_debug(3, "client %s received a message\n", client->name);
02275 
02276    if (!(insert = ast_calloc(1, sizeof(*insert)))) {
02277       return;
02278    }
02279 
02280    insert->arrived = ast_tvnow();
02281 
02282    /* wake up threads waiting for messages */
02283    ast_mutex_lock(&messagelock);
02284    ast_cond_broadcast(&message_received_condition);
02285    ast_mutex_unlock(&messagelock);
02286 
02287    if (iks_find_cdata(pak->x, "body")) {
02288       insert->message = ast_strdup(iks_find_cdata(pak->x, "body"));
02289    }
02290    if (pak->id) {
02291       ast_copy_string(insert->id, pak->id, sizeof(insert->id));
02292    }
02293    if (pak->from){
02294       /* insert will furtherly be added to message list */
02295       insert->from = ast_strdup(pak->from->full);
02296       if (!insert->from) {
02297          ast_log(LOG_ERROR, "Memory allocation failure\n");
02298          return;
02299       }
02300       ast_debug(3, "message comes from %s\n", insert->from);
02301    }
02302 
02303    if (client->send_to_dialplan) {
02304       if ((msg = ast_msg_alloc())) {
02305          int res;
02306 
02307          res = ast_msg_set_to(msg, "xmpp:%s", client->user);
02308          res |= ast_msg_set_from(msg, "xmpp:%s", insert->from);
02309          res |= ast_msg_set_body(msg, "%s", insert->message);
02310          res |= ast_msg_set_context(msg, "%s", client->context);
02311 
02312          if (res) {
02313             ast_msg_destroy(msg);
02314          } else {
02315             ast_msg_queue(msg);
02316          }
02317 
02318          msg = NULL;
02319       }
02320    }
02321 
02322    /* remove old messages received from this JID
02323     * and insert received message */
02324    deleted = delete_old_messages(client, pak->from->partial);
02325    ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
02326    AST_LIST_LOCK(&client->messages);
02327    AST_LIST_INSERT_HEAD(&client->messages, insert, list);
02328    AST_LIST_UNLOCK(&client->messages);
02329 }
02330 
02331 /*!
02332  * \internal
02333  * \brief handles \verbatim <presence>\endverbatim stanzas.
02334  * \param client the configured XMPP client we use to connect to a XMPP server
02335  * \param pak ikspak
02336  */
02337 static void aji_handle_presence(struct aji_client *client, ikspak *pak)
02338 {
02339    int status, priority;
02340    struct aji_buddy *buddy;
02341    struct aji_resource *tmp = NULL, *last = NULL, *found = NULL;
02342    char *ver, *node, *descrip, *type;
02343 
02344    if (client->state != AJI_CONNECTED)
02345       aji_create_buddy(pak->from->partial, client);
02346 
02347    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
02348    if (!buddy && pak->from->partial) {
02349       /* allow our jid to be used to log in with another resource */
02350       if (!strcmp((const char *)pak->from->partial, (const char *)client->jid->partial))
02351          aji_create_buddy(pak->from->partial, client);
02352       else
02353          ast_log(LOG_NOTICE, "Got presence packet from %s, someone not in our roster!!!!\n", pak->from->partial);
02354       return;
02355    }
02356    type = iks_find_attrib(pak->x, "type");
02357    if (client->component && type &&!strcasecmp("probe", type)) {
02358       aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
02359       ast_verbose("what i was looking for \n");
02360    }
02361    ASTOBJ_WRLOCK(buddy);
02362    status = (pak->show) ? pak->show : 6;
02363    priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
02364    tmp = buddy->resources;
02365    descrip = ast_strdup(iks_find_cdata(pak->x, "status"));
02366 
02367    while (tmp && pak->from->resource) {
02368       if (!strcasecmp(tmp->resource, pak->from->resource)) {
02369          tmp->status = status;
02370          if (tmp->description) {
02371             ast_free(tmp->description);
02372          }
02373          tmp->description = descrip;
02374          found = tmp;
02375          if (status == 6) {   /* Sign off Destroy resource */
02376             if (last && found->next) {
02377                last->next = found->next;
02378             } else if (!last) {
02379                if (found->next) {
02380                   buddy->resources = found->next;
02381                } else {
02382                   buddy->resources = NULL;
02383                }
02384             } else if (!found->next) {
02385                if (last) {
02386                   last->next = NULL;
02387                } else {
02388                   buddy->resources = NULL;
02389                }
02390             }
02391             ast_free(found);
02392             found = NULL;
02393             break;
02394          }
02395          /* resource list is sorted by descending priority */
02396          if (tmp->priority != priority) {
02397             found->priority = priority;
02398             if (!last && !found->next) {
02399                /* resource was found to be unique,
02400                   leave loop */
02401                break;
02402             }
02403             /* search for resource in our list
02404                and take it out for the moment */
02405             if (last) {
02406                last->next = found->next;
02407             } else {
02408                buddy->resources = found->next;
02409             }
02410 
02411             last = NULL;
02412             tmp = buddy->resources;
02413             if (!buddy->resources) {
02414                buddy->resources = found;
02415             }
02416             /* priority processing */
02417             while (tmp) {
02418                /* insert resource back according to
02419                   its priority value */
02420                if (found->priority > tmp->priority) {
02421                   if (last) {
02422                      /* insert within list */
02423                      last->next = found;
02424                   }
02425                   found->next = tmp;
02426                   if (!last) {
02427                      /* insert on top */
02428                      buddy->resources = found;
02429                   }
02430                   break;
02431                }
02432                if (!tmp->next) {
02433                   /* insert at the end of the list */
02434                   tmp->next = found;
02435                   found->next = NULL;
02436                   break;
02437                }
02438                last = tmp;
02439                tmp = tmp->next;
02440             }
02441          }
02442          break;
02443       }
02444       last = tmp;
02445       tmp = tmp->next;
02446    }
02447 
02448    /* resource not found in our list, create it */
02449    if (!found && status != 6 && pak->from->resource) {
02450       found = ast_calloc(1, sizeof(*found));
02451 
02452       if (!found) {
02453          ast_log(LOG_ERROR, "Out of memory!\n");
02454          ASTOBJ_UNLOCK(buddy);
02455          ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02456          return;
02457       }
02458       ast_copy_string(found->resource, pak->from->resource, sizeof(found->resource));
02459       found->status = status;
02460       found->description = descrip;
02461       found->priority = priority;
02462       found->next = NULL;
02463       last = NULL;
02464       tmp = buddy->resources;
02465       while (tmp) {
02466          if (found->priority > tmp->priority) {
02467             if (last) {
02468                last->next = found;
02469             }
02470             found->next = tmp;
02471             if (!last) {
02472                buddy->resources = found;
02473             }
02474             break;
02475          }
02476          if (!tmp->next) {
02477             tmp->next = found;
02478             break;
02479          }
02480          last = tmp;
02481          tmp = tmp->next;
02482       }
02483       if (!tmp) {
02484          buddy->resources = found;
02485       }
02486    }
02487 
02488    ASTOBJ_UNLOCK(buddy);
02489    ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02490 
02491    node = iks_find_attrib(iks_find(pak->x, "c"), "node");
02492    ver = iks_find_attrib(iks_find(pak->x, "c"), "ver");
02493 
02494    /* handle gmail client's special caps:c tag */
02495    if (!node && !ver) {
02496       node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
02497       ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
02498    }
02499 
02500    /* retrieve capabilites of the new resource */
02501    if (status != 6 && found && !found->cap) {
02502       found->cap = aji_find_version(node, ver, pak);
02503       if (gtalk_yuck(pak->x)) { /* gtalk should do discover */
02504          found->cap->jingle = 1;
02505       }
02506       if (found->cap->jingle) {
02507          ast_debug(1, "Special case for google till they support discover.\n");
02508       } else {
02509          iks *iq, *query;
02510          iq = iks_new("iq");
02511          query = iks_new("query");
02512          if (query && iq) {
02513             iks_insert_attrib(iq, "type", "get");
02514             iks_insert_attrib(iq, "to", pak->from->full);
02515             iks_insert_attrib(iq, "from", client->jid->full);
02516             iks_insert_attrib(iq, "id", client->mid);
02517             ast_aji_increment_mid(client->mid);
02518             iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02519             iks_insert_node(iq, query);
02520             ast_aji_send(client, iq);
02521          } else {
02522             ast_log(LOG_ERROR, "Out of memory.\n");
02523          }
02524          iks_delete(query);
02525          iks_delete(iq);
02526       }
02527    }
02528    switch (pak->subtype) {
02529    case IKS_TYPE_AVAILABLE:
02530       ast_debug(3, "JABBER: I am available ^_* %i\n", pak->subtype);
02531       break;
02532    case IKS_TYPE_UNAVAILABLE:
02533       ast_debug(3, "JABBER: I am unavailable ^_* %i\n", pak->subtype);
02534       break;
02535    default:
02536       ast_debug(3, "JABBER: Ohh sexy and the wrong type: %i\n", pak->subtype);
02537    }
02538    switch (pak->show) {
02539    case IKS_SHOW_UNAVAILABLE:
02540       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
02541       break;
02542    case IKS_SHOW_AVAILABLE:
02543       ast_debug(3, "JABBER: type is available\n");
02544       break;
02545    case IKS_SHOW_CHAT:
02546       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
02547       break;
02548    case IKS_SHOW_AWAY:
02549       ast_debug(3, "JABBER: type is away\n");
02550       break;
02551    case IKS_SHOW_XA:
02552       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
02553       break;
02554    case IKS_SHOW_DND:
02555       ast_debug(3, "JABBER: type: %i subtype %i\n", pak->subtype, pak->show);
02556       break;
02557    default:
02558       ast_debug(3, "JABBER: Kinky! how did that happen %i\n", pak->show);
02559    }
02560 
02561    if (found) {
02562       manager_event(EVENT_FLAG_USER, "JabberStatus",
02563          "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
02564          "\r\nDescription: %s\r\n",
02565          client->name, pak->from->partial, found->resource, found->status,
02566          found->priority, S_OR(found->description, ""));
02567    } else {
02568       manager_event(EVENT_FLAG_USER, "JabberStatus",
02569          "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
02570          client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
02571    }
02572 }
02573 
02574 /*!
02575  * \internal
02576  * \brief handles subscription requests.
02577  * \param client the configured XMPP client we use to connect to a XMPP server
02578  * \param pak ikspak iksemel packet.
02579  * \return void.
02580  */
02581 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak)
02582 {
02583    iks *presence = NULL, *status = NULL;
02584    struct aji_buddy* buddy = NULL;
02585 
02586    switch (pak->subtype) {
02587    case IKS_TYPE_SUBSCRIBE:
02588       if (ast_test_flag(&client->flags, AJI_AUTOACCEPT)) {
02589          presence = iks_new("presence");
02590          status = iks_new("status");
02591          if (presence && status) {
02592             iks_insert_attrib(presence, "type", "subscribed");
02593             iks_insert_attrib(presence, "to", pak->from->full);
02594             iks_insert_attrib(presence, "from", client->jid->full);
02595             if (pak->id)
02596                iks_insert_attrib(presence, "id", pak->id);
02597             iks_insert_cdata(status, "Asterisk has approved subscription", 0);
02598             iks_insert_node(presence, status);
02599             ast_aji_send(client, presence);
02600          } else {
02601             ast_log(LOG_ERROR, "Unable to allocate nodes\n");
02602          }
02603 
02604          iks_delete(presence);
02605          iks_delete(status);
02606       }
02607 
02608       if (client->component)
02609          aji_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), client->status, client->statusmessage);
02610    case IKS_TYPE_SUBSCRIBED:
02611       buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
02612       if (!buddy && pak->from->partial) {
02613          aji_create_buddy(pak->from->partial, client);
02614       } else if (buddy) {
02615          ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
02616       }
02617    default:
02618       ast_verb(5, "JABBER: This is a subcription of type %i\n", pak->subtype);
02619    }
02620 }
02621 
02622 /*!
02623  * \brief sends messages.
02624  * \param client the configured XMPP client we use to connect to a XMPP server
02625  * \param address
02626  * \param message
02627  * \retval IKS_OK success
02628  * \retval -1 failure
02629  */
02630 int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message)
02631 {
02632    return aji_send_raw_chat(client, 0, NULL, address, message);
02633 }
02634 
02635 /*!
02636 * \brief sends message to a groupchat
02637 * Prior to sending messages to a groupchat, one must be connected to it.
02638 * \param client the configured XMPP client we use to connect to a XMPP server
02639 * \param nick the nickname we use in the chatroom
02640 * \param address the user the messages must be sent to
02641 * \param message the message to send
02642 * \return IKS_OK on success, any other value on failure
02643 */
02644 int ast_aji_send_groupchat(struct aji_client *client, const char *nick, const char *address, const char *message) {
02645    return aji_send_raw_chat(client, 1, nick, address, message);
02646 }
02647 
02648 /*!
02649 * \brief sends messages.
02650 * \param client the configured XMPP client we use to connect to a XMPP server
02651 * \param groupchat 
02652 * \param nick the nickname we use in chatrooms
02653 * \param address
02654 * \param message
02655 * \return IKS_OK on success, any other value on failure
02656 */
02657 static int aji_send_raw_chat(struct aji_client *client, int groupchat, const char *nick, const char *address, const char *message)
02658 {
02659    int res = 0;
02660    iks *message_packet = NULL;
02661    char from[AJI_MAX_JIDLEN];
02662    /* the nickname is used only in component mode */
02663    if (nick && client->component) {
02664       snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
02665    } else {
02666       snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
02667    }
02668 
02669    if (client->state != AJI_CONNECTED) {
02670       ast_log(LOG_WARNING, "JABBER: Not connected can't send\n");
02671       return -1;
02672    }
02673 
02674    message_packet = iks_make_msg(groupchat ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message);
02675    if (!message_packet) {
02676       ast_log(LOG_ERROR, "Out of memory.\n");
02677       return -1;
02678    }
02679    iks_insert_attrib(message_packet, "from", from);
02680    res = ast_aji_send(client, message_packet);
02681    iks_delete(message_packet);
02682 
02683    return res;
02684 }
02685 
02686 /*!
02687  * \brief create a chatroom.
02688  * \param client the configured XMPP client we use to connect to a XMPP server
02689  * \param room name of room
02690  * \param server name of server
02691  * \param topic topic for the room.
02692  * \return 0.
02693  */
02694 int ast_aji_create_chat(struct aji_client *client, char *room, char *server, char *topic)
02695 {
02696    int res = 0;
02697    iks *iq = NULL;
02698    iq = iks_new("iq");
02699 
02700    if (iq && client) {
02701       iks_insert_attrib(iq, "type", "get");
02702       iks_insert_attrib(iq, "to", server);
02703       iks_insert_attrib(iq, "id", client->mid);
02704       ast_aji_increment_mid(client->mid);
02705       ast_aji_send(client, iq);
02706    } else {
02707       ast_log(LOG_ERROR, "Out of memory.\n");
02708    }
02709 
02710    iks_delete(iq);
02711 
02712    return res;
02713 }
02714 
02715 /*!
02716  * \brief join a chatroom.
02717  * \param client the configured XMPP client we use to connect to a XMPP server
02718  * \param room room to join
02719  * \param nick the nickname to use in this room
02720  * \return IKS_OK on success, any other value on failure.
02721  */
02722 int ast_aji_join_chat(struct aji_client *client, char *room, char *nick)
02723 {
02724    return aji_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nick, NULL);
02725 }
02726 
02727 /*!
02728  * \brief leave a chatroom.
02729  * \param client the configured XMPP client we use to connect to a XMPP server
02730  * \param room room to leave
02731  * \param nick the nickname used in this room
02732  * \return IKS_OK on success, any other value on failure.
02733  */
02734 int ast_aji_leave_chat(struct aji_client *client, char *room, char *nick)
02735 {
02736    return aji_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nick, NULL);
02737 }
02738 /*!
02739  * \brief invite to a chatroom.
02740  * \param client the configured XMPP client we use to connect to a XMPP server
02741  * \param user
02742  * \param room
02743  * \param message
02744  * \return res.
02745  */
02746 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message)
02747 {
02748    int res = 0;
02749    iks *invite, *body, *namespace;
02750 
02751    invite = iks_new("message");
02752    body = iks_new("body");
02753    namespace = iks_new("x");
02754    if (client && invite && body && namespace) {
02755       iks_insert_attrib(invite, "to", user);
02756       iks_insert_attrib(invite, "id", client->mid);
02757       ast_aji_increment_mid(client->mid);
02758       iks_insert_cdata(body, message, 0);
02759       iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
02760       iks_insert_attrib(namespace, "jid", room);
02761       iks_insert_node(invite, body);
02762       iks_insert_node(invite, namespace);
02763       res = ast_aji_send(client, invite);
02764    } else {
02765       ast_log(LOG_ERROR, "Out of memory.\n");
02766    }
02767 
02768    iks_delete(body);
02769    iks_delete(namespace);
02770    iks_delete(invite);
02771 
02772    return res;
02773 }
02774 
02775 /*!
02776  * \internal
02777  * \brief receive message loop.
02778  * \param data void
02779  * \return void.
02780  */
02781 static void *aji_recv_loop(void *data)
02782 {
02783    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02784    int res = IKS_HOOK;
02785 
02786    while (res != IKS_OK) {
02787       ast_debug(3, "JABBER: Connecting.\n");
02788       res = aji_reconnect(client);
02789       sleep(4);
02790    }
02791 
02792    do {
02793       if (res == IKS_NET_RWERR || client->timeout == 0) {
02794          while (res != IKS_OK) {
02795             ast_debug(3, "JABBER: reconnecting.\n");
02796             res = aji_reconnect(client);
02797             sleep(4);
02798          }
02799       }
02800 
02801       res = aji_recv(client, 1);
02802 
02803       if (client->state == AJI_DISCONNECTING) {
02804          ast_debug(2, "Ending our Jabber client's thread due to a disconnect\n");
02805          pthread_exit(NULL);
02806       }
02807 
02808       /* Decrease timeout if no data received, and delete
02809        * old messages globally */
02810       if (res == IKS_NET_EXPIRED) {
02811          client->timeout--;
02812          delete_old_messages_all(client);
02813       }
02814       if (res == IKS_HOOK) {
02815          ast_log(LOG_WARNING, "JABBER: Got hook event.\n");
02816       } else if (res == IKS_NET_TLSFAIL) {
02817          ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
02818       } else if (client->timeout == 0 && client->state == AJI_CONNECTED) {
02819          res = client->keepalive ? aji_send_raw(client, " ") : IKS_OK;
02820          if (res == IKS_OK) {
02821             client->timeout = 50;
02822          } else {
02823             ast_log(LOG_WARNING, "JABBER:  Network Timeout\n");
02824          }
02825       } else if (res == IKS_NET_RWERR) {
02826          ast_log(LOG_WARNING, "JABBER: socket read error\n");
02827       }
02828    } while (client);
02829    ASTOBJ_UNREF(client, ast_aji_client_destroy);
02830    return 0;
02831 }
02832 
02833 /*!
02834  * \brief increments the mid field for messages and other events.
02835  * \param mid char.
02836  * \return void.
02837  */
02838 void ast_aji_increment_mid(char *mid)
02839 {
02840    int i = 0;
02841 
02842    for (i = strlen(mid) - 1; i >= 0; i--) {
02843       if (mid[i] != 'z') {
02844          mid[i] = mid[i] + 1;
02845          i = 0;
02846       } else
02847          mid[i] = 'a';
02848    }
02849 }
02850 
02851 #if 0
02852 /*!
02853  * \brief attempts to register to a transport.
02854  * \param aji_client struct, and xml packet.
02855  * \return IKS_FILTER_EAT.
02856  */
02857 /*allows for registering to transport , was too sketch and is out for now. */
02858 static int aji_register_transport(void *data, ikspak *pak)
02859 {
02860    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02861    int res = 0;
02862    struct aji_buddy *buddy = NULL;
02863    iks *send = iks_make_iq(IKS_TYPE_GET, "jabber:iq:register");
02864 
02865    if (client && send) {
02866       ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02867          ASTOBJ_RDLOCK(iterator); 
02868          if (iterator->btype == AJI_TRANS) {
02869               buddy = iterator;
02870          }
02871          ASTOBJ_UNLOCK(iterator);
02872       });
02873       iks_filter_remove_hook(client->f, aji_register_transport);
02874       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);
02875       iks_insert_attrib(send, "to", buddy->host);
02876       iks_insert_attrib(send, "id", client->mid);
02877       ast_aji_increment_mid(client->mid);
02878       iks_insert_attrib(send, "from", client->user);
02879       res = ast_aji_send(client, send);
02880    } else 
02881       ast_log(LOG_ERROR, "Out of memory.\n");
02882 
02883    if (send)
02884       iks_delete(send);
02885    ASTOBJ_UNREF(client, ast_aji_client_destroy);
02886    return IKS_FILTER_EAT;
02887 
02888 }
02889 /*!
02890  * \brief attempts to register to a transport step 2.
02891  * \param aji_client struct, and xml packet.
02892  * \return IKS_FILTER_EAT.
02893  */
02894 /* more of the same blob of code, too wonky for now*/
02895 static int aji_register_transport2(void *data, ikspak *pak)
02896 {
02897    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
02898    int res = 0;
02899    struct aji_buddy *buddy = NULL;
02900 
02901    iks *regiq = iks_new("iq");
02902    iks *regquery = iks_new("query");
02903    iks *reguser = iks_new("username");
02904    iks *regpass = iks_new("password");
02905 
02906    if (client && regquery && reguser && regpass && regiq) {
02907       ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02908          ASTOBJ_RDLOCK(iterator);
02909          if (iterator->btype == AJI_TRANS)
02910             buddy = iterator; ASTOBJ_UNLOCK(iterator);
02911       });
02912       iks_filter_remove_hook(client->f, aji_register_transport2);
02913       iks_insert_attrib(regiq, "to", buddy->host);
02914       iks_insert_attrib(regiq, "type", "set");
02915       iks_insert_attrib(regiq, "id", client->mid);
02916       ast_aji_increment_mid(client->mid);
02917       iks_insert_attrib(regiq, "from", client->user);
02918       iks_insert_attrib(regquery, "xmlns", "jabber:iq:register");
02919       iks_insert_cdata(reguser, buddy->user, 0);
02920       iks_insert_cdata(regpass, buddy->pass, 0);
02921       iks_insert_node(regiq, regquery);
02922       iks_insert_node(regquery, reguser);
02923       iks_insert_node(regquery, regpass);
02924       res = ast_aji_send(client, regiq);
02925    } else
02926       ast_log(LOG_ERROR, "Out of memory.\n");
02927    if (regiq)
02928       iks_delete(regiq);
02929    if (regquery)
02930       iks_delete(regquery);
02931    if (reguser)
02932       iks_delete(reguser);
02933    if (regpass)
02934       iks_delete(regpass);
02935    ASTOBJ_UNREF(client, ast_aji_client_destroy);
02936    return IKS_FILTER_EAT;
02937 }
02938 #endif
02939 
02940 /*!
02941  * \internal
02942  * \brief goes through roster and prunes users not needed in list, or adds them accordingly.
02943  * \param client the configured XMPP client we use to connect to a XMPP server
02944  * \return void.
02945  * \note The messages here should be configurable.
02946  */
02947 static void aji_pruneregister(struct aji_client *client)
02948 {
02949    iks *removeiq = iks_new("iq");
02950    iks *removequery = iks_new("query");
02951    iks *removeitem = iks_new("item");
02952    iks *send = iks_make_iq(IKS_TYPE_GET, "http://jabber.org/protocol/disco#items");
02953    if (!client || !removeiq || !removequery || !removeitem || !send) {
02954       ast_log(LOG_ERROR, "Out of memory.\n");
02955       goto safeout;
02956    }
02957 
02958    iks_insert_node(removeiq, removequery);
02959    iks_insert_node(removequery, removeitem);
02960    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
02961       ASTOBJ_RDLOCK(iterator);
02962       /* For an aji_buddy, both AUTOPRUNE and AUTOREGISTER will never
02963        * be called at the same time */
02964       if (ast_test_flag(&iterator->flags, AJI_AUTOPRUNE)) { /* If autoprune is set on jabber.conf */
02965          ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, iterator->name,
02966                          "GoodBye. Your status is no longer needed by Asterisk the Open Source PBX"
02967                          " so I am no longer subscribing to your presence.\n"));
02968          ast_aji_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBED, iterator->name,
02969                          "GoodBye.  You are no longer in the Asterisk config file so I am removing"
02970                          " your access to my presence.\n"));
02971          iks_insert_attrib(removeiq, "from", client->jid->full);
02972          iks_insert_attrib(removeiq, "type", "set");
02973          iks_insert_attrib(removequery, "xmlns", "jabber:iq:roster");
02974          iks_insert_attrib(removeitem, "jid", iterator->name);
02975          iks_insert_attrib(removeitem, "subscription", "remove");
02976          ast_aji_send(client, removeiq);
02977       } else if (ast_test_flag(&iterator->flags, AJI_AUTOREGISTER)) {
02978          ast_aji_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, iterator->name,
02979                          "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"));
02980          ast_clear_flag(&iterator->flags, AJI_AUTOREGISTER);
02981       }
02982       ASTOBJ_UNLOCK(iterator);
02983    });
02984 
02985  safeout:
02986    iks_delete(removeiq);
02987    iks_delete(removequery);
02988    iks_delete(removeitem);
02989    iks_delete(send);
02990 
02991    ASTOBJ_CONTAINER_PRUNE_MARKED(&client->buddies, ast_aji_buddy_destroy);
02992 }
02993 
02994 /*!
02995  * \internal
02996  * \brief filters the roster packet we get back from server.
02997  * \param data void
02998  * \param pak ikspak iksemel packet.
02999  * \return IKS_FILTER_EAT.
03000  */
03001 static int aji_filter_roster(void *data, ikspak *pak)
03002 {
03003    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
03004    int flag = 0;
03005    iks *x = NULL;
03006    struct aji_buddy *buddy;
03007 
03008    client->state = AJI_CONNECTED;
03009    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
03010       ASTOBJ_RDLOCK(iterator);
03011       x = iks_child(pak->query);
03012       flag = 0;
03013       while (x) {
03014          if (!iks_strcmp(iks_name(x), "item")) {
03015             if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid"))) {
03016                flag = 1;
03017                ast_clear_flag(&iterator->flags, AJI_AUTOPRUNE | AJI_AUTOREGISTER);
03018             }
03019          }
03020          x = iks_next(x);
03021       }
03022       if (!flag) {
03023          ast_copy_flags(&iterator->flags, &client->flags, AJI_AUTOREGISTER);
03024       }
03025       iks_delete(x);
03026 
03027       ASTOBJ_UNLOCK(iterator);
03028    });
03029 
03030    x = iks_child(pak->query);
03031    while (x) {
03032       flag = 0;
03033       if (iks_strcmp(iks_name(x), "item") == 0) {
03034          ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
03035             ASTOBJ_RDLOCK(iterator);
03036             if (!strcasecmp(iterator->name, iks_find_attrib(x, "jid")))
03037                flag = 1;
03038             ASTOBJ_UNLOCK(iterator);
03039          });
03040 
03041          if (flag) {
03042             /* found buddy, don't create a new one */
03043             x = iks_next(x);
03044             continue;
03045          }
03046 
03047          buddy = ast_calloc(1, sizeof(*buddy));
03048          if (!buddy) {
03049             ast_log(LOG_WARNING, "Out of memory\n");
03050             ASTOBJ_UNREF(client, ast_aji_client_destroy);
03051             return 0;
03052          }
03053          ASTOBJ_INIT(buddy);
03054          ASTOBJ_WRLOCK(buddy);
03055          ast_copy_string(buddy->name, iks_find_attrib(x, "jid"), sizeof(buddy->name));
03056          ast_clear_flag(&buddy->flags, AST_FLAGS_ALL);
03057          if (ast_test_flag(&client->flags, AJI_AUTOPRUNE)) {
03058             ast_set_flag(&buddy->flags, AJI_AUTOPRUNE);
03059             ASTOBJ_MARK(buddy);
03060          } else if (ast_test_flag(&client->flags, AJI_AUTOREGISTER)) {
03061             if (!iks_strcmp(iks_find_attrib(x, "subscription"), "none") || !iks_strcmp(iks_find_attrib(x, "subscription"), "from")) {
03062                /* subscribe to buddy's presence only
03063                   if we really need to */
03064                ast_set_flag(&buddy->flags, AJI_AUTOREGISTER);
03065             }
03066          }
03067          ASTOBJ_UNLOCK(buddy);
03068          if (buddy) {
03069             ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
03070             ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
03071          }
03072       }
03073       x = iks_next(x);
03074    }
03075 
03076    iks_delete(x);
03077    aji_pruneregister(client);
03078 
03079    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03080    return IKS_FILTER_EAT;
03081 }
03082 
03083 /*!
03084  * \internal
03085  * \brief reconnect to jabber server
03086  * \param client the configured XMPP client we use to connect to a XMPP server
03087  * \return res.
03088 */
03089 static int aji_reconnect(struct aji_client *client)
03090 {
03091    int res = 0;
03092 
03093    if (client->state) {
03094       client->state = AJI_DISCONNECTED;
03095    }
03096    client->timeout = 50;
03097    if (client->p) {
03098       iks_parser_reset(client->p);
03099    }
03100    if (client->authorized) {
03101       client->authorized = 0;
03102    }
03103 
03104    res = aji_initialize(client);
03105 
03106    return res;
03107 }
03108 
03109 /*!
03110  * \internal
03111  * \brief Get the roster of jabber users
03112  * \param client the configured XMPP client we use to connect to a XMPP server
03113  * \return 1.
03114 */
03115 static int aji_get_roster(struct aji_client *client)
03116 {
03117    iks *roster = NULL;
03118    roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
03119 
03120    if (roster) {
03121       iks_insert_attrib(roster, "id", "roster");
03122       aji_set_presence(client, NULL, client->jid->full, client->status, client->statusmessage);
03123       ast_aji_send(client, roster);
03124    }
03125 
03126    iks_delete(roster);
03127 
03128    return 1;
03129 }
03130 
03131 /*!
03132  * \internal
03133  * \brief connects as a client to jabber server.
03134  * \param data void
03135  * \param pak ikspak iksemel packet
03136  * \return res.
03137  */
03138 static int aji_client_connect(void *data, ikspak *pak)
03139 {
03140    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
03141    int res = IKS_FILTER_PASS;
03142 
03143    if (client) {
03144       if (client->state == AJI_DISCONNECTED) {
03145          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);
03146          client->state = AJI_CONNECTING;
03147          client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
03148          if (!client->component) { /*client*/
03149             aji_get_roster(client);
03150          }
03151          if (client->distribute_events) {
03152             aji_init_event_distribution(client);
03153          }
03154 
03155          iks_filter_remove_hook(client->f, aji_client_connect);
03156          /* Once we remove the hook for this routine, we must return EAT or we will crash or corrupt memory */
03157          res = IKS_FILTER_EAT;
03158       }
03159    } else {
03160       ast_log(LOG_ERROR, "Out of memory.\n");
03161    }
03162 
03163    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03164    return res;
03165 }
03166 
03167 /*!
03168  * \internal
03169  * \brief prepares client for connect.
03170  * \param client the configured XMPP client we use to connect to a XMPP server
03171  * \return 1.
03172  */
03173 static int aji_initialize(struct aji_client *client)
03174 {
03175    int connected = IKS_NET_NOCONN;
03176 
03177 #ifdef HAVE_OPENSSL
03178    /* reset stream flags */
03179    client->stream_flags = 0;
03180 #endif
03181    /* If it's a component, connect to user, otherwise, connect to server */
03182    connected = iks_connect_via(client->p, S_OR(client->serverhost, client->jid->server), client->port, client->component ? client->user : client->jid->server);
03183 
03184    if (connected == IKS_NET_NOCONN) {
03185       ast_log(LOG_ERROR, "JABBER ERROR: No Connection\n");
03186       return IKS_HOOK;
03187    } else if (connected == IKS_NET_NODNS) {
03188       ast_log(LOG_ERROR, "JABBER ERROR: No DNS %s for client to  %s\n", client->name,
03189          S_OR(client->serverhost, client->jid->server));
03190       return IKS_HOOK;
03191    }
03192 
03193    return IKS_OK;
03194 }
03195 
03196 /*!
03197  * \brief disconnect from jabber server.
03198  * \param client the configured XMPP client we use to connect to a XMPP server
03199  * \return 1.
03200  */
03201 int ast_aji_disconnect(struct aji_client *client)
03202 {
03203    if (client) {
03204       ast_verb(4, "JABBER: Disconnecting\n");
03205 #ifdef HAVE_OPENSSL
03206       if (client->stream_flags & SECURE) {
03207          SSL_shutdown(client->ssl_session);
03208          SSL_CTX_free(client->ssl_context);
03209          SSL_free(client->ssl_session);
03210       }
03211 #endif
03212       iks_disconnect(client->p);
03213       iks_parser_delete(client->p);
03214       ASTOBJ_UNREF(client, ast_aji_client_destroy);
03215    }
03216 
03217    return 1;
03218 }
03219 
03220 /*!
03221  * \brief Callback function for MWI events
03222  * \param ast_event
03223  * \param data void pointer to ast_client structure
03224  * \return void
03225  */
03226 static void aji_mwi_cb(const struct ast_event *ast_event, void *data)
03227 {
03228    const char *mailbox;
03229    const char *context;
03230    char oldmsgs[10];
03231    char newmsgs[10];
03232    struct aji_client *client;
03233    if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
03234    {
03235       /* If the event didn't originate from this server, don't send it back out. */
03236       ast_debug(1, "Returning here\n");
03237       return;
03238    }
03239 
03240    client = ASTOBJ_REF((struct aji_client *) data);
03241    mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
03242    context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
03243    snprintf(oldmsgs, sizeof(oldmsgs), "%d",
03244       ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
03245    snprintf(newmsgs, sizeof(newmsgs), "%d",
03246       ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
03247    aji_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
03248    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03249 
03250 }
03251 /*!
03252  * \brief Callback function for device state events
03253  * \param ast_event
03254  * \param data void pointer to ast_client structure
03255  * \return void
03256  */
03257 static void aji_devstate_cb(const struct ast_event *ast_event, void *data)
03258 {
03259    const char *device;
03260    const char *device_state;
03261    struct aji_client *client;
03262    if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID)))
03263    {
03264       /* If the event didn't originate from this server, don't send it back out. */
03265       ast_debug(1, "Returning here\n");
03266       return;
03267    }
03268 
03269    client = ASTOBJ_REF((struct aji_client *) data);
03270    device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
03271    device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
03272    aji_publish_device_state(client, device, device_state);
03273    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03274 }
03275 
03276 /*!
03277  * \brief Initialize collections for event distribution
03278  * \param client the configured XMPP client we use to connect to a XMPP server
03279  * \return void
03280  */
03281 static void aji_init_event_distribution(struct aji_client *client)
03282 {
03283    if (!mwi_sub) {
03284       mwi_sub = ast_event_subscribe(AST_EVENT_MWI, aji_mwi_cb, "aji_mwi_subscription",
03285          client, AST_EVENT_IE_END);
03286    }
03287    if (!device_state_sub) {
03288       if (ast_enable_distributed_devstate()) {
03289          return;
03290       }
03291       device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
03292          aji_devstate_cb, "aji_devstate_subscription", client, AST_EVENT_IE_END);
03293       ast_event_dump_cache(device_state_sub);
03294    }
03295 
03296    aji_pubsub_subscribe(client, "device_state");
03297    aji_pubsub_subscribe(client, "message_waiting");
03298    iks_filter_add_rule(client->f, aji_handle_pubsub_event, client, IKS_RULE_TYPE,
03299       IKS_PAK_MESSAGE, IKS_RULE_FROM, client->pubsub_node, IKS_RULE_DONE);
03300    iks_filter_add_rule(client->f, aji_handle_pubsub_error, client, IKS_RULE_TYPE,
03301       IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
03302 
03303 }
03304 
03305 /*!
03306  * \brief Callback for handling PubSub events
03307  * \param data void pointer to aji_client structure
03308  * \return IKS_FILTER_EAT
03309  */
03310 static int aji_handle_pubsub_event(void *data, ikspak *pak)
03311 {
03312    char *item_id, *device_state, *context;
03313    int oldmsgs, newmsgs;
03314    iks *item, *item_content;
03315    struct ast_eid pubsub_eid;
03316    struct ast_event *event;
03317    item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
03318    if (!item) {
03319       ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
03320       return IKS_FILTER_EAT;
03321    }
03322    item_id = iks_find_attrib(item, "id");
03323    item_content = iks_child(item);
03324    ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
03325    if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
03326       ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
03327       return IKS_FILTER_EAT;
03328    }
03329    if (!strcasecmp(iks_name(item_content), "state")) {
03330       device_state = iks_find_cdata(item, "state");
03331       if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
03332          AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
03333          AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
03334          AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
03335          AST_EVENT_IE_END))) {
03336          return IKS_FILTER_EAT;
03337       }
03338    } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
03339       context = strsep(&item_id, "@");
03340       sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
03341       sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
03342       if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
03343          AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
03344          AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
03345          AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
03346          AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
03347          &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
03348          return IKS_FILTER_EAT;
03349       }
03350    } else {
03351       ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
03352          iks_name(item_content));
03353       return IKS_FILTER_EAT;
03354    }
03355    ast_event_queue_and_cache(event);
03356    return IKS_FILTER_EAT;
03357 }
03358 
03359 /*!
03360  * \brief Add Owner affiliations for pubsub node
03361  * \param client the configured XMPP client we use to connect to a XMPP server
03362  * \param node the name of the node to which to add affiliations
03363  * \return void
03364  */
03365 static void aji_create_affiliations(struct aji_client *client, const char *node)
03366 {
03367    iks *modify_affiliates = aji_pubsub_iq_create(client, "set");
03368    iks *pubsub, *affiliations, *affiliate;
03369    pubsub = iks_insert(modify_affiliates, "pubsub");
03370    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
03371    affiliations = iks_insert(pubsub, "affiliations");
03372    iks_insert_attrib(affiliations, "node", node);
03373    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
03374       ASTOBJ_RDLOCK(iterator);
03375       affiliate = iks_insert(affiliations, "affiliation");
03376       iks_insert_attrib(affiliate, "jid", iterator->name);
03377       iks_insert_attrib(affiliate, "affiliation", "owner");
03378       ASTOBJ_UNLOCK(iterator);
03379    });
03380    ast_aji_send(client, modify_affiliates);
03381    iks_delete(modify_affiliates);
03382 }
03383 
03384 /*!
03385  * \brief Subscribe to a PubSub node
03386  * \param client the configured XMPP client we use to connect to a XMPP server
03387  * \param node the name of the node to which to subscribe
03388  * \return void
03389  */
03390 static void aji_pubsub_subscribe(struct aji_client *client, const char *node)
03391 {
03392    iks *request = aji_pubsub_iq_create(client, "set");
03393    iks *pubsub, *subscribe;
03394 
03395    pubsub = iks_insert(request, "pubsub");
03396    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
03397    subscribe = iks_insert(pubsub, "subscribe");
03398    iks_insert_attrib(subscribe, "jid", client->jid->partial);
03399    iks_insert_attrib(subscribe, "node", node);
03400    if (ast_test_flag(&globalflags, AJI_XEP0248)) {
03401       iks *options, *x, *sub_options, *sub_type, *sub_depth;
03402       options = iks_insert(pubsub, "options");
03403       x = iks_insert(options, "x");
03404       iks_insert_attrib(x, "xmlns", "jabber:x:data");
03405       iks_insert_attrib(x, "type", "submit");
03406       sub_options = iks_insert(x, "field");
03407       iks_insert_attrib(sub_options, "var", "FORM_TYPE");
03408       iks_insert_attrib(sub_options, "type", "hidden");
03409       iks_insert_cdata(iks_insert(sub_options, "value"),
03410          "http://jabber.org/protocol/pubsub#subscribe_options", 51);
03411       sub_type = iks_insert(x, "field");
03412       iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
03413       iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
03414       sub_depth = iks_insert(x, "field");
03415       iks_insert_attrib(sub_type, "var", "pubsub#subscription_depth");
03416       iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
03417    }
03418    ast_aji_send(client, request);
03419    iks_delete(request);
03420 }
03421 
03422 /*!
03423  * \brief Build the skeleton of a publish
03424  * \param client the configured XMPP client we use to connect to a XMPP server
03425  * \param node Name of the node that will be published to
03426  * \param event_type
03427  * \return iks *
03428  */
03429 static iks* aji_build_publish_skeleton(struct aji_client *client, const char *node,
03430    const char *event_type)
03431 {
03432    iks *request = aji_pubsub_iq_create(client, "set");
03433    iks *pubsub, *publish, *item;
03434    pubsub = iks_insert(request, "pubsub");
03435    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
03436    publish = iks_insert(pubsub, "publish");
03437    if (ast_test_flag(&globalflags, AJI_XEP0248)) {
03438       iks_insert_attrib(publish, "node", node);
03439    } else {
03440       iks_insert_attrib(publish, "node", event_type);
03441    }
03442    item = iks_insert(publish, "item");
03443    iks_insert_attrib(item, "id", node);
03444    return item;
03445 
03446 }
03447 
03448 /*!
03449  * \brief Publish device state to a PubSub node
03450  * \param client the configured XMPP client we use to connect to a XMPP server
03451  * \param device the name of the device whose state to publish
03452  * \param device_state the state to publish
03453  * \return void
03454  */
03455 static void aji_publish_device_state(struct aji_client *client, const char *device,
03456    const char *device_state)
03457 {
03458    iks *request = aji_build_publish_skeleton(client, device, "device_state");
03459    iks *state;
03460    char eid_str[20];
03461    if (ast_test_flag(&pubsubflags, AJI_PUBSUB_AUTOCREATE)) {
03462       if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
03463          aji_create_pubsub_node(client, "leaf", device, "device_state");
03464       } else {
03465          aji_create_pubsub_node(client, NULL, device, NULL);
03466       }
03467    }
03468    ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
03469    state = iks_insert(request, "state");
03470    iks_insert_attrib(state, "xmlns", "http://asterisk.org");
03471    iks_insert_attrib(state, "eid", eid_str);
03472    iks_insert_cdata(state, device_state, strlen(device_state));
03473    ast_aji_send(client, iks_root(request));
03474    iks_delete(request);
03475 }
03476 
03477 /*!
03478  * \brief Publish MWI to a PubSub node
03479  * \param client the configured XMPP client we use to connect to a XMPP server
03480  * \param device the name of the device whose state to publish
03481  * \param device_state the state to publish
03482  * \return void
03483  */
03484 static void aji_publish_mwi(struct aji_client *client, const char *mailbox,
03485    const char *context, const char *oldmsgs, const char *newmsgs)
03486 {
03487    char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT];
03488    char eid_str[20];
03489    iks *mailbox_node, *request;
03490    snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
03491    request = aji_build_publish_skeleton(client, full_mailbox, "message_waiting");
03492    ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
03493    mailbox_node = iks_insert(request, "mailbox");
03494    iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
03495    iks_insert_attrib(mailbox_node, "eid", eid_str);
03496    iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
03497    iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
03498    ast_aji_send(client, iks_root(request));
03499    iks_delete(request);
03500 }
03501 
03502 /*!
03503  * \brief Create an IQ packet
03504  * \param client the configured XMPP client we use to connect to a XMPP server
03505  * \param type the type of IQ packet to create
03506  * \return iks*
03507  */
03508 static iks* aji_pubsub_iq_create(struct aji_client *client, const char *type)
03509 {
03510    iks *request = iks_new("iq");
03511 
03512    iks_insert_attrib(request, "to", client->pubsub_node);
03513    iks_insert_attrib(request, "from", client->jid->full);
03514    iks_insert_attrib(request, "type", type);
03515    ast_aji_increment_mid(client->mid);
03516    iks_insert_attrib(request, "id", client->mid);
03517    return request;
03518 }
03519 
03520 static int aji_handle_pubsub_error(void *data, ikspak *pak)
03521 {
03522    char *node_name;
03523    char *error;
03524    int error_num;
03525    iks *orig_request;
03526    iks *orig_pubsub = iks_find(pak->x, "pubsub");
03527    struct aji_client *client;
03528    if (!orig_pubsub) {
03529       ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
03530       return IKS_FILTER_EAT;
03531    }
03532    orig_request = iks_child(orig_pubsub);
03533    error = iks_find_attrib(iks_find(pak->x, "error"), "code");
03534    node_name = iks_find_attrib(orig_request, "node");
03535    if (!sscanf(error, "%30d", &error_num)) {
03536       return IKS_FILTER_EAT;
03537    }
03538    if (error_num > 399 && error_num < 500 && error_num != 404) {
03539       ast_log(LOG_ERROR,
03540          "Error performing operation on PubSub node %s, %s.\n", node_name, error);
03541       return IKS_FILTER_EAT;
03542    } else if (error_num > 499 && error_num < 600) {
03543       ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
03544       return IKS_FILTER_EAT;
03545    }
03546 
03547    client = ASTOBJ_REF((struct aji_client *) data);
03548 
03549    if (!strcasecmp(iks_name(orig_request), "publish")) {
03550       iks *request;
03551       if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
03552          if (iks_find(iks_find(orig_request, "item"), "state")) {
03553             aji_create_pubsub_leaf(client, "device_state", node_name);
03554          } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
03555             aji_create_pubsub_leaf(client, "message_waiting", node_name);
03556          }
03557       } else {
03558          aji_create_pubsub_node(client, NULL, node_name, NULL);
03559       }
03560       request = aji_pubsub_iq_create(client, "set");
03561       iks_insert_node(request, orig_pubsub);
03562       ast_aji_send(client, request);
03563       iks_delete(request);
03564       ASTOBJ_UNREF(client, ast_aji_client_destroy);
03565       return IKS_FILTER_EAT;
03566    } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
03567       if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
03568          aji_create_pubsub_collection(client, node_name);
03569       } else {
03570          aji_create_pubsub_node(client, NULL, node_name, NULL);
03571       }
03572    }
03573    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03574    return IKS_FILTER_EAT;
03575 }
03576 
03577 /*!
03578  * \brief Request item list from pubsub
03579  * \param client the configured XMPP client we use to connect to a XMPP server
03580  * \param collection name of the collection for request
03581  * \return void
03582  */
03583 static void aji_request_pubsub_nodes(struct aji_client *client, const char *collection)
03584 {
03585    iks *request = aji_build_node_request(client, collection);
03586 
03587    iks_filter_add_rule(client->f, aji_receive_node_list, client, IKS_RULE_TYPE,
03588       IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
03589       IKS_RULE_DONE);
03590    ast_aji_send(client, request);
03591    iks_delete(request);
03592 
03593 }
03594 
03595 /*!
03596  * \brief Build the a node request
03597  * \param client the configured XMPP client we use to connect to a XMPP server
03598  * \param collection name of the collection for request
03599  * \return iks*
03600  */
03601 static iks* aji_build_node_request(struct aji_client *client, const char *collection)
03602 {
03603    iks *request = aji_pubsub_iq_create(client, "get");
03604    iks *query;
03605    query = iks_insert(request, "query");
03606    iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
03607    if (collection) {
03608       iks_insert_attrib(query, "node", collection);
03609    }
03610    return request;
03611 }
03612 
03613 /*!
03614  * \brief Receive pubsub item lists
03615  * \param data pointer to aji_client structure
03616  * \param pak response from pubsub diso#items query
03617  * \return IKS_FILTER_EAT
03618  */
03619 static int aji_receive_node_list(void *data, ikspak* pak)
03620 {
03621 
03622    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
03623    iks *item = NULL;
03624    if (iks_has_children(pak->query)) {
03625       item = iks_first_tag(pak->query);
03626       ast_verbose("Connection %s: %s\nNode name: %s\n", client->name, client->jid->partial,
03627          iks_find_attrib(item, "node"));
03628       while ((item = iks_next_tag(item))) {
03629          ast_verbose("Node name: %s\n", iks_find_attrib(item, "node"));
03630       }
03631    }
03632    if (item) {
03633       iks_delete(item);
03634    }
03635    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03636    return IKS_FILTER_EAT;
03637 }
03638 
03639 
03640 /*!
03641  * \brief Method to expose PubSub node list via CLI.
03642  * \param e pointer to ast_cli_entry structure
03643  * \param cmd
03644  * \param a pointer to ast_cli_args structure
03645  * \return char *
03646  */
03647 static char *aji_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
03648 ast_cli_args *a)
03649 {
03650       struct aji_client *client;
03651       const char *name = NULL;
03652       const char *collection = NULL;
03653 
03654       switch (cmd) {
03655       case CLI_INIT:
03656             e->command = "jabber list nodes";
03657             e->usage =
03658                "Usage: jabber list nodes <connection> [collection]\n"
03659                "       Lists the user's nodes on the respective connection\n"
03660                "       ([connection] as configured in jabber.conf.)\n";
03661          return NULL;
03662       case CLI_GENERATE:
03663          return NULL;
03664       }
03665 
03666       if (a->argc > 5 || a->argc < 4) {
03667          return CLI_SHOWUSAGE;
03668       } else if (a->argc == 4 || a->argc == 5) {
03669          name = a->argv[3];
03670       }
03671       if (a->argc == 5) {
03672          collection = a->argv[4];
03673       }
03674       if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
03675          ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03676          return CLI_FAILURE;
03677       }
03678 
03679       ast_cli(a->fd, "Listing pubsub nodes.\n");
03680       aji_request_pubsub_nodes(client, collection);
03681       ASTOBJ_UNREF(client, ast_aji_client_destroy);
03682       return CLI_SUCCESS;
03683 }
03684 
03685 /*!
03686  * \brief Method to purge PubSub nodes via CLI.
03687  * \param e pointer to ast_cli_entry structure
03688  * \param cmd
03689  * \param a pointer to ast_cli_args structure
03690  * \return char *
03691  */
03692 static char *aji_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
03693    ast_cli_args *a)
03694 {
03695    struct aji_client *client;
03696    const char *name;
03697 
03698    switch (cmd) {
03699       case CLI_INIT:
03700          e->command = "jabber purge nodes";
03701          e->usage =
03702                "Usage: jabber purge nodes <connection> <node>\n"
03703                "       Purges nodes on PubSub server\n"
03704                "       as configured in jabber.conf.\n";
03705          return NULL;
03706       case CLI_GENERATE:
03707          return NULL;
03708    }
03709 
03710    if (a->argc != 5) {
03711       return CLI_SHOWUSAGE;
03712    }
03713    name = a->argv[3];
03714 
03715    if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
03716       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03717       return CLI_FAILURE;
03718    }
03719    if (ast_test_flag(&pubsubflags, AJI_XEP0248)) {
03720       aji_pubsub_purge_nodes(client, a->argv[4]);
03721    } else {
03722       aji_delete_pubsub_node(client, a->argv[4]);
03723    }
03724    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03725    return CLI_SUCCESS;
03726 }
03727 
03728 static void aji_pubsub_purge_nodes(struct aji_client *client, const char* collection_name)
03729 {
03730    iks *request = aji_build_node_request(client, collection_name);
03731    ast_aji_send(client, request);
03732    iks_filter_add_rule(client->f, aji_delete_node_list, client, IKS_RULE_TYPE,
03733       IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
03734       IKS_RULE_DONE);
03735    ast_aji_send(client, request);
03736    iks_delete(request);
03737 }
03738 
03739 /*!
03740  * \brief Delete pubsub item lists
03741  * \param data pointer to aji_client structure
03742  * \param pak response from pubsub diso#items query
03743  * \return IKS_FILTER_EAT
03744  */
03745 static int aji_delete_node_list(void *data, ikspak* pak)
03746 {
03747 
03748    struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
03749    iks *item = NULL;
03750    if (iks_has_children(pak->query)) {
03751       item = iks_first_tag(pak->query);
03752       ast_log(LOG_WARNING, "Connection: %s  Node name: %s\n", client->jid->partial,
03753             iks_find_attrib(item, "node"));
03754       while ((item = iks_next_tag(item))) {
03755          aji_delete_pubsub_node(client, iks_find_attrib(item, "node"));
03756       }
03757    }
03758    if (item) {
03759       iks_delete(item);
03760    }
03761    return IKS_FILTER_EAT;
03762 }
03763 
03764 
03765 /*!
03766  * \brief Method to expose PubSub node deletion via CLI.
03767  * \param e pointer to ast_cli_entry structure
03768  * \param cmd
03769  * \param a pointer to ast_cli_args structure
03770  * \return char *
03771  */
03772 static char *aji_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
03773    ast_cli_args *a)
03774 {
03775    struct aji_client *client;
03776    const char *name;
03777 
03778    switch (cmd) {
03779       case CLI_INIT:
03780          e->command = "jabber delete node";
03781          e->usage =
03782                "Usage: jabber delete node <connection> <node>\n"
03783                "       Deletes a node on PubSub server\n"
03784                "       as configured in jabber.conf.\n";
03785          return NULL;
03786       case CLI_GENERATE:
03787          return NULL;
03788    }
03789 
03790    if (a->argc != 5) {
03791       return CLI_SHOWUSAGE;
03792    }
03793    name = a->argv[3];
03794 
03795    if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
03796       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03797       return CLI_FAILURE;
03798    }
03799    aji_delete_pubsub_node(client, a->argv[4]);
03800    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03801    return CLI_SUCCESS;
03802 }
03803 
03804 /*!
03805  * \brief Delete a PubSub node
03806  * \param client the configured XMPP client we use to connect to a XMPP server
03807  * \param node_name the name of the node to delete
03808  * return void
03809  */
03810 static void aji_delete_pubsub_node(struct aji_client *client, const char *node_name)
03811 {
03812    iks *request = aji_pubsub_iq_create(client, "set");
03813    iks *pubsub, *delete;
03814    pubsub = iks_insert(request, "pubsub");
03815    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
03816    delete = iks_insert(pubsub, "delete");
03817    iks_insert_attrib(delete, "node", node_name);
03818    ast_aji_send(client, request);
03819 }
03820 
03821 /*!
03822  * \brief Create a PubSub collection node.
03823  * \param client the configured XMPP client we use to connect to a XMPP server
03824  * \param collection_name The name to use for this collection
03825  * \return void.
03826  */
03827 static void aji_create_pubsub_collection(struct aji_client *client, const char
03828 *collection_name)
03829 {
03830    aji_create_pubsub_node(client, "collection", collection_name, NULL);
03831 }
03832 
03833 
03834 /*!
03835  * \brief Create a PubSub leaf node.
03836  * \param client the configured XMPP client we use to connect to a XMPP server
03837  * \param leaf_name The name to use for this collection
03838  * \return void.
03839  */
03840 static void aji_create_pubsub_leaf(struct aji_client *client, const char *collection_name,
03841 const char *leaf_name)
03842 {
03843    aji_create_pubsub_node(client, "leaf", leaf_name, collection_name);
03844 }
03845 
03846 /*!
03847  * \brief Create a pubsub node
03848  * \param client the configured XMPP client we use to connect to a XMPP server
03849  * \param node_type the type of node to create
03850  * \param name the name of the node to create
03851  * \return iks*
03852  */
03853 static iks* aji_create_pubsub_node(struct aji_client *client, const char *node_type, const
03854       char *name, const char *collection_name)
03855 {
03856    iks *node = aji_pubsub_iq_create(client, "set");
03857    iks *pubsub, *create;
03858    pubsub = iks_insert(node, "pubsub");
03859    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
03860    create = iks_insert(pubsub, "create");
03861    iks_insert_attrib(create, "node", name);
03862    aji_build_node_config(pubsub, node_type, collection_name);
03863    ast_aji_send(client, node);
03864    aji_create_affiliations(client, name);
03865    iks_delete(node);
03866    return 0;
03867 }
03868 
03869 
03870 
03871 static iks* aji_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
03872 {
03873    iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
03874       *field_deliver_payload, *field_persist_items, *field_access_model,
03875       *field_pubsub_collection;
03876    configure = iks_insert(pubsub, "configure");
03877    x = iks_insert(configure, "x");
03878    iks_insert_attrib(x, "xmlns", "jabber:x:data");
03879    iks_insert_attrib(x, "type", "submit");
03880    field_owner = iks_insert(x, "field");
03881    iks_insert_attrib(field_owner, "var", "FORM_TYPE");
03882    iks_insert_attrib(field_owner, "type", "hidden");
03883    iks_insert_cdata(iks_insert(field_owner, "value"),
03884       "http://jabber.org/protocol/pubsub#owner", 39);
03885    if (node_type) {
03886       field_node_type = iks_insert(x, "field");
03887       iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
03888       iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
03889    }
03890    field_node_config = iks_insert(x, "field");
03891    iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
03892    iks_insert_attrib(field_node_config, "type", "hidden");
03893    iks_insert_cdata(iks_insert(field_node_config, "value"),
03894       "http://jabber.org/protocol/pubsub#node_config", 45);
03895    field_deliver_payload = iks_insert(x, "field");
03896    iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
03897    iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
03898    field_persist_items = iks_insert(x, "field");
03899    iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
03900    iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
03901    field_access_model = iks_insert(x, "field");
03902    iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
03903    iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
03904    if (node_type && !strcasecmp(node_type, "leaf")) {
03905       field_pubsub_collection = iks_insert(x, "field");
03906       iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
03907       iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
03908          strlen(collection_name));
03909    }
03910    return configure;
03911 }
03912 
03913 
03914 
03915 /*!
03916  * \brief Method to expose PubSub collection node creation via CLI.
03917  * \return char *.
03918  */
03919 static char *aji_cli_create_collection(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03920 {
03921       struct aji_client *client;
03922       const char *name;
03923       const char *collection_name;
03924 
03925       switch (cmd) {
03926       case CLI_INIT:
03927             e->command = "jabber create collection";
03928             e->usage =
03929                "Usage: jabber create collection <connection> <collection>\n"
03930                "       Creates a PubSub collection node using the account\n"
03931                "       as configured in jabber.conf.\n";
03932          return NULL;
03933       case CLI_GENERATE:
03934          return NULL;
03935       }
03936 
03937       if (a->argc != 5) {
03938          return CLI_SHOWUSAGE;
03939       }
03940       name = a->argv[3];
03941       collection_name = a->argv[4];
03942 
03943       if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
03944          ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03945          return CLI_FAILURE;
03946       }
03947 
03948       ast_cli(a->fd, "Creating test PubSub node collection.\n");
03949       aji_create_pubsub_collection(client, collection_name);
03950       ASTOBJ_UNREF(client, ast_aji_client_destroy);
03951       return CLI_SUCCESS;
03952 }
03953 
03954 /*!
03955  * \brief Method to expose PubSub leaf node creation via CLI.
03956  * \return char *.
03957  */
03958 static char *aji_cli_create_leafnode(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03959 {
03960    struct aji_client *client;
03961    const char *name;
03962    const char *collection_name;
03963    const char *leaf_name;
03964 
03965    switch (cmd) {
03966       case CLI_INIT:
03967          e->command = "jabber create leaf";
03968          e->usage =
03969                "Usage: jabber create leaf <connection> <collection> <leaf>\n"
03970                "       Creates a PubSub leaf node using the account\n"
03971                "       as configured in jabber.conf.\n";
03972          return NULL;
03973       case CLI_GENERATE:
03974          return NULL;
03975    }
03976 
03977    if (a->argc != 6) {
03978       return CLI_SHOWUSAGE;
03979    }
03980    name = a->argv[3];
03981    collection_name = a->argv[4];
03982    leaf_name = a->argv[5];
03983 
03984    if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
03985       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03986       return CLI_FAILURE;
03987    }
03988 
03989    ast_cli(a->fd, "Creating test PubSub node collection.\n");
03990    aji_create_pubsub_leaf(client, collection_name, leaf_name);
03991    ASTOBJ_UNREF(client, ast_aji_client_destroy);
03992    return CLI_SUCCESS;
03993 }
03994 
03995 
03996 
03997 /*!
03998  * \internal
03999  * \brief set presence of client.
04000  * \param client the configured XMPP client we use to connect to a XMPP server
04001  * \param to user send it to
04002  * \param from user it came from
04003  * \param level
04004  * \param desc
04005  * \return void.
04006  */
04007 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc)
04008 {
04009    iks *presence = iks_make_pres(level, desc);
04010    iks *cnode = iks_new("c");
04011    iks *priority = iks_new("priority");
04012    char priorityS[10];
04013 
04014    if (presence && cnode && client && priority) {
04015       if (to) {
04016          iks_insert_attrib(presence, "to", to);
04017       }
04018       if (from) {
04019          iks_insert_attrib(presence, "from", from);
04020       }
04021       snprintf(priorityS, sizeof(priorityS), "%d", client->priority);
04022       iks_insert_cdata(priority, priorityS, strlen(priorityS));
04023       iks_insert_node(presence, priority);
04024       iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
04025       iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
04026       iks_insert_attrib(cnode, "ext", "voice-v1");
04027       iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
04028       iks_insert_node(presence, cnode);
04029       ast_aji_send(client, presence);
04030    } else {
04031       ast_log(LOG_ERROR, "Out of memory.\n");
04032    }
04033 
04034    iks_delete(cnode);
04035    iks_delete(presence);
04036    iks_delete(priority);
04037 }
04038 
04039 /*
04040 * \brief set the presence of the client in a groupchat context.
04041 * \param client the configured XMPP client we use to connect to a XMPP server
04042 * \param room the groupchat identifier in the from roomname@service
04043 * \param from user it came from
04044 * \param level the type of action, i.e. join or leave the chatroom
04045 * \param nick the nickname to use in the chatroom
04046 * \param desc a text that details the action to be taken
04047 * \return res.
04048 */
04049 static int aji_set_group_presence(struct aji_client *client, char *room, int level, char *nick, char *desc)
04050 {
04051    int res = 0;
04052    iks *presence = NULL, *x = NULL;
04053    char from[AJI_MAX_JIDLEN];
04054    char roomid[AJI_MAX_JIDLEN];
04055 
04056    presence = iks_make_pres(level, NULL);
04057    x = iks_new("x");
04058 
04059    if (client->component) {
04060       snprintf(from, AJI_MAX_JIDLEN, "%s@%s/%s", nick, client->jid->full, nick);
04061       snprintf(roomid, AJI_MAX_JIDLEN, "%s/%s", room, nick);
04062    } else {
04063       snprintf(from, AJI_MAX_JIDLEN, "%s", client->jid->full);
04064       snprintf(roomid, AJI_MAX_JIDLEN, "%s/%s", room, nick ? nick : client->jid->user);
04065    }
04066 
04067    if (!presence || !x || !client) {
04068       ast_log(LOG_ERROR, "Out of memory.\n");
04069       res = -1;
04070       goto safeout;
04071    } else {
04072       iks_insert_attrib(presence, "to", roomid);
04073       iks_insert_attrib(presence, "from", from);
04074       iks_insert_attrib(x, "xmlns", MUC_NS);
04075       iks_insert_node(presence, x);
04076       res = ast_aji_send(client, presence);
04077    }
04078 
04079 safeout:
04080    iks_delete(presence);
04081    iks_delete(x);
04082    return res;
04083 }
04084 
04085 /*!
04086  * \internal
04087  * \brief Turn on/off console debugging.
04088  * \return CLI_SUCCESS.
04089  */
04090 static char *aji_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04091 {
04092    switch (cmd) {
04093    case CLI_INIT:
04094       e->command = "jabber set debug {on|off}";
04095       e->usage =
04096          "Usage: jabber set debug {on|off}\n"
04097          "       Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
04098       return NULL;
04099    case CLI_GENERATE:
04100       return NULL;
04101    }
04102 
04103    if (a->argc != e->args) {
04104       return CLI_SHOWUSAGE;
04105    }
04106 
04107    if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
04108       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04109          ASTOBJ_RDLOCK(iterator);
04110          iterator->debug = 1;
04111          ASTOBJ_UNLOCK(iterator);
04112       });
04113       ast_cli(a->fd, "Jabber Debugging Enabled.\n");
04114       return CLI_SUCCESS;
04115    } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
04116       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04117          ASTOBJ_RDLOCK(iterator);
04118          iterator->debug = 0;
04119          ASTOBJ_UNLOCK(iterator);
04120       });
04121       ast_cli(a->fd, "Jabber Debugging Disabled.\n");
04122       return CLI_SUCCESS;
04123    }
04124    return CLI_SHOWUSAGE; /* defaults to invalid */
04125 }
04126 
04127 /*!
04128  * \internal
04129  * \brief Reload jabber module.
04130  * \return CLI_SUCCESS.
04131  */
04132 static char *aji_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04133 {
04134    switch (cmd) {
04135    case CLI_INIT:
04136       e->command = "jabber reload";
04137       e->usage =
04138          "Usage: jabber reload\n"
04139          "       Reloads the Jabber module.\n";
04140       return NULL;
04141    case CLI_GENERATE:
04142       return NULL;
04143    }
04144 
04145    aji_reload(1);
04146    ast_cli(a->fd, "Jabber Reloaded.\n");
04147    return CLI_SUCCESS;
04148 }
04149 
04150 /*!
04151  * \internal
04152  * \brief Show client status.
04153  * \return CLI_SUCCESS.
04154  */
04155 static char *aji_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04156 {
04157    char *status;
04158    int count = 0;
04159 
04160    switch (cmd) {
04161    case CLI_INIT:
04162       e->command = "jabber show connections";
04163       e->usage =
04164          "Usage: jabber show connections\n"
04165          "       Shows state of client and component connections\n";
04166       return NULL;
04167    case CLI_GENERATE:
04168       return NULL;
04169    }
04170 
04171    ast_cli(a->fd, "Jabber Users and their status:\n");
04172    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04173       ASTOBJ_RDLOCK(iterator);
04174       count++;
04175       switch (iterator->state) {
04176       case AJI_DISCONNECTED:
04177          status = "Disconnected";
04178          break;
04179       case AJI_CONNECTING:
04180          status = "Connecting";
04181          break;
04182       case AJI_CONNECTED:
04183          status = "Connected";
04184          break;
04185       default:
04186          status = "Unknown";
04187       }
04188       ast_cli(a->fd, "       [%s] %s     - %s\n", iterator->name, iterator->user, status);
04189       ASTOBJ_UNLOCK(iterator);
04190    });
04191    ast_cli(a->fd, "----\n");
04192    ast_cli(a->fd, "   Number of users: %d\n", count);
04193    return CLI_SUCCESS;
04194 }
04195 
04196 /*!
04197  * \internal
04198  * \brief Show buddy lists
04199  * \return CLI_SUCCESS.
04200  */
04201 static char *aji_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04202 {
04203    struct aji_resource *resource;
04204    struct aji_client *client;
04205 
04206    switch (cmd) {
04207    case CLI_INIT:
04208       e->command = "jabber show buddies";
04209       e->usage =
04210          "Usage: jabber show buddies\n"
04211          "       Shows buddy lists of our clients\n";
04212       return NULL;
04213    case CLI_GENERATE:
04214       return NULL;
04215    }
04216 
04217    ast_cli(a->fd, "Jabber buddy lists\n");
04218    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04219       ast_cli(a->fd, "Client: %s\n", iterator->user);
04220       client = iterator;
04221       ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
04222          ASTOBJ_RDLOCK(iterator);
04223          ast_cli(a->fd, "\tBuddy:\t%s\n", iterator->name);
04224          if (!iterator->resources)
04225             ast_cli(a->fd, "\t\tResource: None\n");
04226          for (resource = iterator->resources; resource; resource = resource->next) {
04227             ast_cli(a->fd, "\t\tResource: %s\n", resource->resource);
04228             if (resource->cap) {
04229                ast_cli(a->fd, "\t\t\tnode: %s\n", resource->cap->parent->node);
04230                ast_cli(a->fd, "\t\t\tversion: %s\n", resource->cap->version);
04231                ast_cli(a->fd, "\t\t\tJingle capable: %s\n", resource->cap->jingle ? "yes" : "no");
04232             }
04233             ast_cli(a->fd, "\t\tStatus: %d\n", resource->status);
04234             ast_cli(a->fd, "\t\tPriority: %d\n", resource->priority);
04235          }
04236          ASTOBJ_UNLOCK(iterator);
04237       });
04238       iterator = client;
04239    });
04240    return CLI_SUCCESS;
04241 }
04242 
04243 /*!
04244  * \internal
04245  * \brief Send test message for debugging.
04246  * \return CLI_SUCCESS,CLI_FAILURE.
04247  */
04248 static char *aji_test(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04249 {
04250    struct aji_client *client;
04251    struct aji_resource *resource;
04252    const char *name;
04253    struct aji_message *tmp;
04254 
04255    switch (cmd) {
04256    case CLI_INIT:
04257       e->command = "jabber test";
04258       e->usage =
04259          "Usage: jabber test <connection>\n"
04260          "       Sends test message for debugging purposes.  A specific client\n"
04261          "       as configured in jabber.conf must be specified.\n";
04262       return NULL;
04263    case CLI_GENERATE:
04264       return NULL;
04265    }
04266 
04267    if (a->argc != 3) {
04268       return CLI_SHOWUSAGE;
04269    }
04270    name = a->argv[2];
04271 
04272    if (!(client = ASTOBJ_CONTAINER_FIND(&clients, name))) {
04273       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
04274       return CLI_FAILURE;
04275    }
04276 
04277    /* XXX Does Matt really want everyone to use his personal address for tests? */ /* XXX yes he does */
04278    ast_aji_send_chat(client, "mogorman@astjab.org", "blahblah");
04279    ASTOBJ_CONTAINER_TRAVERSE(&client->buddies, 1, {
04280       ASTOBJ_RDLOCK(iterator);
04281       ast_verbose("User: %s\n", iterator->name);
04282       for (resource = iterator->resources; resource; resource = resource->next) {
04283          ast_verbose("Resource: %s\n", resource->resource);
04284          if (resource->cap) {
04285             ast_verbose("   client: %s\n", resource->cap->parent->node);
04286             ast_verbose("   version: %s\n", resource->cap->version);
04287             ast_verbose("   Jingle Capable: %d\n", resource->cap->jingle);
04288          }
04289          ast_verbose("  Priority: %d\n", resource->priority);
04290          ast_verbose("  Status: %d\n", resource->status);
04291          ast_verbose("  Message: %s\n", S_OR(resource->description, ""));
04292       }
04293       ASTOBJ_UNLOCK(iterator);
04294    });
04295    ast_verbose("\nOooh a working message stack!\n");
04296    AST_LIST_LOCK(&client->messages);
04297    AST_LIST_TRAVERSE(&client->messages, tmp, list) {
04298       //ast_verbose("   Message from: %s with id %s @ %s %s\n",tmp->from, S_OR(tmp->id,""), ctime(&tmp->arrived), S_OR(tmp->message, ""));
04299    }
04300    AST_LIST_UNLOCK(&client->messages);
04301    ASTOBJ_UNREF(client, ast_aji_client_destroy);
04302 
04303    return CLI_SUCCESS;
04304 }
04305 
04306 /*!
04307  * \internal
04308  * \brief creates aji_client structure.
04309  * \param label
04310  * \param var ast_variable
04311  * \param debug
04312  * \return 0.
04313  */
04314 static int aji_create_client(char *label, struct ast_variable *var, int debug)
04315 {
04316    char *resource;
04317    struct aji_client *client = NULL;
04318    int flag = 0;
04319 
04320    client = ASTOBJ_CONTAINER_FIND(&clients, label);
04321    if (!client) {
04322       flag = 1;
04323       client = ast_calloc(1, sizeof(*client));
04324       if (!client) {
04325          ast_log(LOG_ERROR, "Out of memory!\n");
04326          return 0;
04327       }
04328       ASTOBJ_INIT(client);
04329       ASTOBJ_WRLOCK(client);
04330       ASTOBJ_CONTAINER_INIT(&client->buddies);
04331    } else {
04332       ASTOBJ_WRLOCK(client);
04333       ASTOBJ_UNMARK(client);
04334    }
04335    ASTOBJ_CONTAINER_MARKALL(&client->buddies);
04336    ast_copy_string(client->name, label, sizeof(client->name));
04337    ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
04338    ast_copy_string(client->context, "default", sizeof(client->context));
04339 
04340    /* Set default values for the client object */
04341    client->debug = debug;
04342    ast_copy_flags(&client->flags, &globalflags, AST_FLAGS_ALL);
04343    client->port = 5222;
04344    client->usetls = 1;
04345    client->usesasl = 1;
04346    client->forcessl = 0;
04347    client->keepalive = 1;
04348    client->timeout = 50;
04349    client->message_timeout = 5;
04350    client->distribute_events = 0;
04351    AST_LIST_HEAD_INIT(&client->messages);
04352    client->component = 0;
04353    ast_copy_string(client->statusmessage, "Online and Available", sizeof(client->statusmessage));
04354    client->priority = 0;
04355    client->status = IKS_SHOW_AVAILABLE;
04356    client->send_to_dialplan = 0;
04357 
04358    if (flag) {
04359       client->authorized = 0;
04360       client->state = AJI_DISCONNECTED;
04361    }
04362    while (var) {
04363       if (!strcasecmp(var->name, "username")) {
04364          ast_copy_string(client->user, var->value, sizeof(client->user));
04365       } else if (!strcasecmp(var->name, "serverhost")) {
04366          ast_copy_string(client->serverhost, var->value, sizeof(client->serverhost));
04367       } else if (!strcasecmp(var->name, "secret")) {
04368          ast_copy_string(client->password, var->value, sizeof(client->password));
04369       } else if (!strcasecmp(var->name, "statusmessage")) {
04370          ast_copy_string(client->statusmessage, var->value, sizeof(client->statusmessage));
04371       } else if (!strcasecmp(var->name, "port")) {
04372          client->port = atoi(var->value);
04373       } else if (!strcasecmp(var->name, "timeout")) {
04374          client->message_timeout = atoi(var->value);
04375       } else if (!strcasecmp(var->name, "debug")) {
04376          client->debug = (ast_false(var->value)) ? 0 : 1;
04377       } else if (!strcasecmp(var->name, "type")) {
04378          if (!strcasecmp(var->value, "component")) {
04379             client->component = 1;
04380             if (client->distribute_events) {
04381                ast_log(LOG_ERROR, "Client cannot be configured to be both a component and to distribute events!  Event distribution will be disabled.\n");
04382                client->distribute_events = 0;
04383             }
04384          }
04385       } else if (!strcasecmp(var->name, "distribute_events")) {
04386          if (ast_true(var->value)) {
04387             if (client->component) {
04388                ast_log(LOG_ERROR, "Client cannot be configured to be both a component and to distribute events!  Event distribution will be disabled.\n");
04389             } else {
04390                if (ast_test_flag(&pubsubflags, AJI_PUBSUB)) {
04391                   ast_log(LOG_ERROR, "Only one connection can be configured for distributed events.\n");
04392                } else {
04393                   ast_set_flag(&pubsubflags, AJI_PUBSUB);
04394                   client->distribute_events = 1;
04395                }
04396             }
04397          }
04398       } else if (!strcasecmp(var->name, "pubsub_node")) {
04399          ast_copy_string(client->pubsub_node, var->value, sizeof(client->pubsub_node));
04400       } else if (!strcasecmp(var->name, "usetls")) {
04401          client->usetls = (ast_false(var->value)) ? 0 : 1;
04402       } else if (!strcasecmp(var->name, "usesasl")) {
04403          client->usesasl = (ast_false(var->value)) ? 0 : 1;
04404       } else if (!strcasecmp(var->name, "forceoldssl")) {
04405          client->forcessl = (ast_false(var->value)) ? 0 : 1;
04406       } else if (!strcasecmp(var->name, "keepalive")) {
04407          client->keepalive = (ast_false(var->value)) ? 0 : 1;
04408       } else if (!strcasecmp(var->name, "autoprune")) {
04409          ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOPRUNE);
04410       } else if (!strcasecmp(var->name, "autoregister")) {
04411          ast_set2_flag(&client->flags, ast_true(var->value), AJI_AUTOREGISTER);
04412       } else if (!strcasecmp(var->name, "auth_policy")) {
04413          if (!strcasecmp(var->value, "accept")) {
04414             ast_set_flag(&client->flags, AJI_AUTOACCEPT);
04415          } else {
04416             ast_clear_flag(&client->flags, AJI_AUTOACCEPT);
04417          }
04418       } else if (!strcasecmp(var->name, "buddy")) {
04419          aji_create_buddy((char *)var->value, client);
04420       } else if (!strcasecmp(var->name, "priority")) {
04421          client->priority = atoi(var->value);
04422       } else if (!strcasecmp(var->name, "status")) {
04423          if (!strcasecmp(var->value, "unavailable")) {
04424             client->status = IKS_SHOW_UNAVAILABLE;
04425          } else if (!strcasecmp(var->value, "available")
04426           || !strcasecmp(var->value, "online")) {
04427             client->status = IKS_SHOW_AVAILABLE;
04428          } else if (!strcasecmp(var->value, "chat")
04429           || !strcasecmp(var->value, "chatty")) {
04430             client->status = IKS_SHOW_CHAT;
04431          } else if (!strcasecmp(var->value, "away")) {
04432             client->status = IKS_SHOW_AWAY;
04433          } else if (!strcasecmp(var->value, "xa")
04434           || !strcasecmp(var->value, "xaway")) {
04435             client->status = IKS_SHOW_XA;
04436          } else if (!strcasecmp(var->value, "dnd")) {
04437             client->status = IKS_SHOW_DND;
04438          } else if (!strcasecmp(var->value, "invisible")) {
04439          #ifdef IKS_SHOW_INVISIBLE
04440             client->status = IKS_SHOW_INVISIBLE;
04441          #else
04442             ast_log(LOG_WARNING, "Your iksemel doesn't support invisible status: falling back to DND\n");
04443             client->status = IKS_SHOW_DND;
04444          #endif
04445          } else {
04446             ast_log(LOG_WARNING, "Unknown presence status: %s\n", var->value);
04447          }
04448       } else if (!strcasecmp(var->name, "context")) {
04449          ast_copy_string(client->context, var->value, sizeof(client->context));
04450       } else if (!strcasecmp(var->name, "sendtodialplan")) {
04451          client->send_to_dialplan = ast_true(var->value) ? 1 : 0;
04452       }
04453    /* no transport support in this version */
04454    /* else if (!strcasecmp(var->name, "transport"))
04455             aji_create_transport(var->value, client);
04456    */
04457       var = var->next;
04458    }
04459    if (!flag) {
04460       ASTOBJ_UNLOCK(client);
04461       ASTOBJ_UNREF(client, ast_aji_client_destroy);
04462       return 1;
04463    }
04464 
04465    ast_copy_string(client->name_space, (client->component) ? "jabber:component:accept" : "jabber:client", sizeof(client->name_space));
04466    client->p = iks_stream_new(client->name_space, client, aji_act_hook);
04467    if (!client->p) {
04468       ast_log(LOG_ERROR, "Failed to create stream for client '%s'!\n", client->name);
04469       return 0;
04470    }
04471    client->stack = iks_stack_new(8192, 8192);
04472    if (!client->stack) {
04473       ast_log(LOG_ERROR, "Failed to allocate stack for client '%s'\n", client->name);
04474       return 0;
04475    }
04476    client->f = iks_filter_new();
04477    if (!client->f) {
04478       ast_log(LOG_ERROR, "Failed to create filter for client '%s'\n", client->name);
04479       return 0;
04480    }
04481    if (!strchr(client->user, '/') && !client->component) { /*client */
04482       resource = NULL;
04483       if (asprintf(&resource, "%s/asterisk", client->user) >= 0) {
04484          client->jid = iks_id_new(client->stack, resource);
04485          ast_free(resource);
04486       }
04487    } else {
04488       client->jid = iks_id_new(client->stack, client->user);
04489    }
04490    if (client->component) {
04491       iks_filter_add_rule(client->f, aji_dinfo_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
04492       iks_filter_add_rule(client->f, aji_ditems_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
04493       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);
04494       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);
04495    } else {
04496       iks_filter_add_rule(client->f, aji_client_info_handler, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
04497    }
04498 
04499    iks_set_log_hook(client->p, aji_log_hook);
04500    ASTOBJ_UNLOCK(client);
04501    ASTOBJ_CONTAINER_LINK(&clients, client);
04502    return 1;
04503 }
04504 
04505 
04506 
04507 #if 0
04508 /*!
04509  * \brief creates transport.
04510  * \param label, buddy to dump it into. 
04511  * \return 0.
04512  */
04513 /* no connecting to transports today */
04514 static int aji_create_transport(char *label, struct aji_client *client)
04515 {
04516    char *server = NULL, *buddyname = NULL, *user = NULL, *pass = NULL;
04517    struct aji_buddy *buddy = NULL;
04518    int needs_unref = 1;
04519 
04520    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies,label);
04521    if (!buddy) {
04522       needs_unref = 0;
04523       buddy = ast_calloc(1, sizeof(*buddy));
04524       if (!buddy) {
04525          ast_log(LOG_WARNING, "Out of memory\n");
04526          return 0;
04527       }
04528       ASTOBJ_INIT(buddy);
04529    }
04530    ASTOBJ_WRLOCK(buddy);
04531    server = label;
04532    if ((buddyname = strchr(label, ','))) {
04533       *buddyname = '\0';
04534       buddyname++;
04535       if (buddyname && buddyname[0] != '\0') {
04536          if ((user = strchr(buddyname, ','))) {
04537             *user = '\0';
04538             user++;
04539             if (user && user[0] != '\0') {
04540                if ((pass = strchr(user, ','))) {
04541                   *pass = '\0';
04542                   pass++;
04543                   ast_copy_string(buddy->pass, pass, sizeof(buddy->pass));
04544                   ast_copy_string(buddy->user, user, sizeof(buddy->user));
04545                   ast_copy_string(buddy->name, buddyname, sizeof(buddy->name));
04546                   ast_copy_string(buddy->server, server, sizeof(buddy->server));
04547                   if (needs_unref) {
04548                      ASTOBJ_UNMARK(buddy);
04549                      ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
04550                   }
04551                   return 1;
04552                }
04553             }
04554          }
04555       }
04556    }
04557    ASTOBJ_UNLOCK(buddy);
04558    if (needs_unref) {
04559       ASTOBJ_UNMARK(buddy);
04560       ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
04561    } else {
04562       ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
04563    }
04564    return 0;
04565 }
04566 #endif
04567 
04568 /*!
04569  * \internal
04570  * \brief creates buddy.
04571  * \param label char.
04572  * \param client the configured XMPP client we use to connect to a XMPP server 
04573  * \return 1 on success, 0 on failure.
04574  */
04575 static int aji_create_buddy(char *label, struct aji_client *client)
04576 {
04577    struct aji_buddy *buddy = NULL;
04578    int needs_unref = 1;
04579    buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, label);
04580    if (!buddy) {
04581       needs_unref = 0;
04582       buddy = ast_calloc(1, sizeof(*buddy));
04583       if (!buddy) {
04584          ast_log(LOG_WARNING, "Out of memory\n");
04585          return 0;
04586       }
04587       ASTOBJ_INIT(buddy);
04588    }
04589    ASTOBJ_WRLOCK(buddy);
04590    ast_copy_string(buddy->name, label, sizeof(buddy->name));
04591    ASTOBJ_UNLOCK(buddy);
04592    if (needs_unref) {
04593       ASTOBJ_UNMARK(buddy);
04594       ASTOBJ_UNREF(buddy, ast_aji_buddy_destroy);
04595    } else {
04596       ASTOBJ_CONTAINER_LINK(&client->buddies, buddy);
04597    }
04598    return 1;
04599 }
04600 
04601 /*!< load config file. \return 1. */
04602 static int aji_load_config(int reload)
04603 {
04604    char *cat = NULL;
04605    int debug = 0;
04606    struct ast_config *cfg = NULL;
04607    struct ast_variable *var = NULL;
04608    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
04609 
04610    if ((cfg = ast_config_load(JABBER_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
04611       return -1;
04612    }
04613 
04614    /* Reset flags to default value */
04615    ast_set_flag(&globalflags, AJI_AUTOREGISTER | AJI_AUTOACCEPT);
04616 
04617    if (cfg == CONFIG_STATUS_FILEMISSING || cfg == CONFIG_STATUS_FILEINVALID) {
04618       ast_log(LOG_WARNING, "No such configuration file %s\n", JABBER_CONFIG);
04619       return 0;
04620    }
04621 
04622    cat = ast_category_browse(cfg, NULL);
04623    for (var = ast_variable_browse(cfg, "general"); var; var = var->next) {
04624       if (!strcasecmp(var->name, "debug")) {
04625          debug = (ast_false(ast_variable_retrieve(cfg, "general", "debug"))) ? 0 : 1;
04626       } else if (!strcasecmp(var->name, "autoprune")) {
04627          ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOPRUNE);
04628       } else if (!strcasecmp(var->name, "autoregister")) {
04629          ast_set2_flag(&globalflags, ast_true(var->value), AJI_AUTOREGISTER);
04630       } else if (!strcasecmp(var->name, "collection_nodes")) {
04631          ast_set2_flag(&pubsubflags, ast_true(var->value), AJI_XEP0248);
04632       } else if (!strcasecmp(var->name, "pubsub_autocreate")) {
04633          ast_set2_flag(&pubsubflags, ast_true(var->value), AJI_PUBSUB_AUTOCREATE);
04634       } else if (!strcasecmp(var->name, "auth_policy")) {
04635          if (!strcasecmp(var->value, "accept")) {
04636             ast_set_flag(&globalflags, AJI_AUTOACCEPT);
04637          } else {
04638             ast_clear_flag(&globalflags, AJI_AUTOACCEPT);
04639          }
04640       }
04641    }
04642 
04643    while (cat) {
04644       if (strcasecmp(cat, "general")) {
04645          var = ast_variable_browse(cfg, cat);
04646          aji_create_client(cat, var, debug);
04647       }
04648       cat = ast_category_browse(cfg, cat);
04649    }
04650    ast_config_destroy(cfg); /* or leak memory */
04651    return 1;
04652 }
04653 
04654 /*!
04655  * \brief grab a aji_client structure by label name or JID. Bumps the refcount.
04656  * (without the resource string)
04657  * \param name label or JID
04658  * \return aji_client.
04659  */
04660 struct aji_client *ast_aji_get_client(const char *name)
04661 {
04662    struct aji_client *client = NULL;
04663    char *aux = NULL;
04664 
04665    client = ASTOBJ_CONTAINER_FIND(&clients, name);
04666    if (!client && strchr(name, '@')) {
04667       ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04668          aux = ast_strdupa(iterator->user);
04669          if (strchr(aux, '/')) {
04670             /* strip resource for comparison */
04671             aux = strsep(&aux, "/");
04672          }
04673          if (!strncasecmp(aux, name, strlen(aux))) {
04674             client = ASTOBJ_REF(iterator);
04675          }
04676       });
04677    }
04678 
04679    return client;
04680 }
04681 
04682 struct aji_client_container *ast_aji_get_clients(void)
04683 {
04684    return &clients;
04685 }
04686 
04687 /*!
04688  * \internal
04689  * \brief  Send a Jabber Message via call from the Manager
04690  * \param s mansession Manager session
04691  * \param m message Message to send
04692  * \return  0
04693 */
04694 static int manager_jabber_send(struct mansession *s, const struct message *m)
04695 {
04696    struct aji_client *client = NULL;
04697    const char *id = astman_get_header(m, "ActionID");
04698    const char *jabber = astman_get_header(m, "Jabber");
04699    const char *screenname = astman_get_header(m, "ScreenName");
04700    const char *message = astman_get_header(m, "Message");
04701 
04702    if (ast_strlen_zero(jabber)) {
04703       astman_send_error(s, m, "No transport specified");
04704       return 0;
04705    }
04706    if (ast_strlen_zero(screenname)) {
04707       astman_send_error(s, m, "No ScreenName specified");
04708       return 0;
04709    }
04710    if (ast_strlen_zero(message)) {
04711       astman_send_error(s, m, "No Message specified");
04712       return 0;
04713    }
04714 
04715    astman_send_ack(s, m, "Attempting to send Jabber Message");
04716    client = ast_aji_get_client(jabber);
04717    if (!client) {
04718       astman_send_error(s, m, "Could not find Sender");
04719       return 0;
04720    }
04721    if (strchr(screenname, '@') && message) {
04722       ast_aji_send_chat(client, screenname, message);
04723       astman_append(s, "Response: Success\r\n");
04724    } else {
04725       astman_append(s, "Response: Error\r\n");
04726    }
04727    ASTOBJ_UNREF(client, ast_aji_client_destroy);
04728    if (!ast_strlen_zero(id)) {
04729       astman_append(s, "ActionID: %s\r\n", id);
04730    }
04731    astman_append(s, "\r\n");
04732    return 0;
04733 }
04734 
04735 /*!
04736  * \internal
04737  * \brief Reload the jabber module
04738  */
04739 static int aji_reload(int reload)
04740 {
04741    int res;
04742 
04743    ASTOBJ_CONTAINER_MARKALL(&clients);
04744    if (!(res = aji_load_config(reload))) {
04745       ast_log(LOG_ERROR, "JABBER: Failed to load config.\n");
04746       return 0;
04747    } else if (res == -1)
04748       return 1;
04749 
04750    ASTOBJ_CONTAINER_PRUNE_MARKED(&clients, ast_aji_client_destroy);
04751    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04752       ASTOBJ_RDLOCK(iterator);
04753       if (iterator->state == AJI_DISCONNECTED) {
04754          if (!iterator->thread)
04755             ast_pthread_create_background(&iterator->thread, NULL, aji_recv_loop, iterator);
04756       } else if (iterator->state == AJI_CONNECTING) {
04757          aji_get_roster(iterator);
04758          if (iterator->distribute_events) {
04759             aji_init_event_distribution(iterator);
04760          }
04761       }
04762 
04763       ASTOBJ_UNLOCK(iterator);
04764    });
04765 
04766    return 1;
04767 }
04768 
04769 /*!
04770  * \internal
04771  * \brief Unload the jabber module
04772  */
04773 static int unload_module(void)
04774 {
04775    ast_msg_tech_unregister(&msg_tech);
04776    ast_cli_unregister_multiple(aji_cli, ARRAY_LEN(aji_cli));
04777    ast_unregister_application(app_ajisend);
04778    ast_unregister_application(app_ajisendgroup);
04779    ast_unregister_application(app_ajistatus);
04780    ast_unregister_application(app_ajijoin);
04781    ast_unregister_application(app_ajileave);
04782    ast_manager_unregister("JabberSend");
04783    ast_custom_function_unregister(&jabberstatus_function);
04784    if (mwi_sub) {
04785       ast_event_unsubscribe(mwi_sub);
04786    }
04787    if (device_state_sub) {
04788       ast_event_unsubscribe(device_state_sub);
04789    }
04790    ast_custom_function_unregister(&jabberreceive_function);
04791 
04792    ASTOBJ_CONTAINER_TRAVERSE(&clients, 1, {
04793       ASTOBJ_WRLOCK(iterator);
04794       ast_debug(3, "JABBER: Releasing and disconnecting client: %s\n", iterator->name);
04795       iterator->state = AJI_DISCONNECTING;
04796       ASTOBJ_UNLOCK(iterator);
04797       pthread_join(iterator->thread, NULL);
04798       ast_aji_disconnect(iterator);
04799    });
04800 
04801    ASTOBJ_CONTAINER_DESTROYALL(&clients, ast_aji_client_destroy);
04802    ASTOBJ_CONTAINER_DESTROY(&clients);
04803 
04804    ast_cond_destroy(&message_received_condition);
04805    ast_mutex_destroy(&messagelock);
04806 
04807    return 0;
04808 }
04809 
04810 /*!
04811  * \internal
04812  * \brief Unload the jabber module
04813  */
04814 static int load_module(void)
04815 {
04816    ASTOBJ_CONTAINER_INIT(&clients);
04817    if (!aji_reload(0))
04818       return AST_MODULE_LOAD_DECLINE;
04819    ast_manager_register_xml("JabberSend", EVENT_FLAG_SYSTEM, manager_jabber_send);
04820    ast_register_application_xml(app_ajisend, aji_send_exec);
04821    ast_register_application_xml(app_ajisendgroup, aji_sendgroup_exec);
04822    ast_register_application_xml(app_ajistatus, aji_status_exec);
04823    ast_register_application_xml(app_ajijoin, aji_join_exec);
04824    ast_register_application_xml(app_ajileave, aji_leave_exec);
04825    ast_cli_register_multiple(aji_cli, ARRAY_LEN(aji_cli));
04826    ast_custom_function_register(&jabberstatus_function);
04827    ast_custom_function_register(&jabberreceive_function);
04828    ast_msg_tech_register(&msg_tech);
04829 
04830    ast_mutex_init(&messagelock);
04831    ast_cond_init(&message_received_condition, NULL);
04832    return 0;
04833 }
04834 
04835 /*!
04836  * \internal
04837  * \brief Wrapper for aji_reload
04838  */
04839 static int reload(void)
04840 {
04841    aji_reload(1);
04842    return 0;
04843 }
04844 
04845 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "AJI - Asterisk Jabber Interface",
04846       .load = load_module,
04847       .unload = unload_module,
04848       .reload = reload,
04849       .load_pri = AST_MODPRI_CHANNEL_DEPEND,
04850           );

Generated on Sat Feb 11 06:33:22 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6