Wed Apr 6 11:29:47 2011

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

Generated on Wed Apr 6 11:29:47 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7