Thu Oct 11 06:33:54 2012

Asterisk developer's documentation


res_xmpp.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2012, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@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  *
00021  * \brief XMPP client and component module.
00022  *
00023  * \author Joshua Colp <jcolp@digium.com>
00024  *
00025  * Iksemel http://code.google.com/p/iksemel/
00026  *
00027  * A reference module for interfacting Asterisk directly as a client or component with
00028  * an XMPP/Jabber compliant server.
00029  *
00030  * This module is based upon the original res_jabber as done by Matt O'Gorman.
00031  *
00032  */
00033 
00034 /*! 
00035  * \li The resource res_xmpp uses the configuration file \ref xmpp.conf
00036  * \addtogroup configuration_file Configuration Files
00037  */
00038 
00039 /*!
00040  * \page xmpp.conf xmpp.conf
00041  * \verbinclude xmpp.conf.sample
00042  */
00043 
00044 /*** MODULEINFO
00045    <depend>iksemel</depend>
00046    <use type="external">openssl</use>
00047    <support_level>core</support_level>
00048  ***/
00049 
00050 #include "asterisk.h"
00051 
00052 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 374623 $")
00053 
00054 #include <ctype.h>
00055 #include <iksemel.h>
00056 
00057 #include "asterisk/xmpp.h"
00058 #include "asterisk/module.h"
00059 #include "asterisk/manager.h"
00060 #include "asterisk/app.h"
00061 #include "asterisk/message.h"
00062 #include "asterisk/manager.h"
00063 #include "asterisk/event.h"
00064 #include "asterisk/cli.h"
00065 #include "asterisk/config_options.h"
00066 
00067 /*** DOCUMENTATION
00068    <application name="JabberSend" language="en_US">
00069       <synopsis>
00070          Sends an XMPP message to a buddy.
00071       </synopsis>
00072       <syntax>
00073          <parameter name="account" required="true">
00074             <para>The local named account to listen on (specified in
00075             xmpp.conf)</para>
00076          </parameter>
00077          <parameter name="jid" required="true">
00078             <para>Jabber ID of the buddy to send the message to. It can be a
00079             bare JID (username@domain) or a full JID (username@domain/resource).</para>
00080          </parameter>
00081          <parameter name="message" required="true">
00082             <para>The message to send.</para>
00083          </parameter>
00084       </syntax>
00085       <description>
00086          <para>Sends the content of <replaceable>message</replaceable> as text message
00087          from the given <replaceable>account</replaceable> to the buddy identified by
00088          <replaceable>jid</replaceable></para>
00089          <para>Example: JabberSend(asterisk,bob@domain.com,Hello world) sends "Hello world"
00090          to <replaceable>bob@domain.com</replaceable> as an XMPP message from the account
00091          <replaceable>asterisk</replaceable>, configured in xmpp.conf.</para>
00092       </description>
00093       <see-also>
00094          <ref type="function">JABBER_STATUS</ref>
00095          <ref type="function">JABBER_RECEIVE</ref>
00096       </see-also>
00097    </application>
00098    <function name="JABBER_RECEIVE" language="en_US">
00099       <synopsis>
00100          Reads XMPP messages.
00101       </synopsis>
00102       <syntax>
00103          <parameter name="account" required="true">
00104             <para>The local named account to listen on (specified in
00105             xmpp.conf)</para>
00106          </parameter>
00107          <parameter name="jid" required="true">
00108             <para>Jabber ID of the buddy to receive message from. It can be a
00109             bare JID (username@domain) or a full JID (username@domain/resource).</para>
00110          </parameter>
00111          <parameter name="timeout">
00112             <para>In seconds, defaults to <literal>20</literal>.</para>
00113          </parameter>
00114       </syntax>
00115       <description>
00116          <para>Receives a text message on the given <replaceable>account</replaceable>
00117          from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
00118          <para>Example: ${JABBER_RECEIVE(asterisk,bob@domain.com)} returns an XMPP message
00119          sent from <replaceable>bob@domain.com</replaceable> (or nothing in case of a time out), to
00120          the <replaceable>asterisk</replaceable> XMPP account configured in xmpp.conf.</para>
00121       </description>
00122       <see-also>
00123          <ref type="function">JABBER_STATUS</ref>
00124          <ref type="application">JabberSend</ref>
00125       </see-also>
00126    </function>
00127    <function name="JABBER_STATUS" language="en_US">
00128       <synopsis>
00129          Retrieves a buddy's status.
00130       </synopsis>
00131       <syntax>
00132          <parameter name="account" required="true">
00133             <para>The local named account to listen on (specified in
00134             xmpp.conf)</para>
00135          </parameter>
00136          <parameter name="jid" required="true">
00137             <para>Jabber ID of the buddy to receive message from. It can be a
00138             bare JID (username@domain) or a full JID (username@domain/resource).</para>
00139          </parameter>
00140       </syntax>
00141       <description>
00142          <para>Retrieves the numeric status associated with the buddy identified
00143          by <replaceable>jid</replaceable>.
00144          If the buddy does not exist in the buddylist, returns 7.</para>
00145          <para>Status will be 1-7.</para>
00146          <para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
00147          <para>If not in roster variable will be set to 7.</para>
00148          <para>Example: ${JABBER_STATUS(asterisk,bob@domain.com)} returns 1 if
00149          <replaceable>bob@domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
00150          the associated XMPP account configured in xmpp.conf.</para>
00151       </description>
00152       <see-also>
00153          <ref type="function">JABBER_RECEIVE</ref>
00154          <ref type="application">JabberSend</ref>
00155       </see-also>
00156    </function>
00157    <application name="JabberSendGroup" language="en_US">
00158       <synopsis>
00159          Send a Jabber Message to a specified chat room
00160       </synopsis>
00161       <syntax>
00162          <parameter name="Jabber" required="true">
00163             <para>Client or transport Asterisk uses to connect to Jabber.</para>
00164          </parameter>
00165          <parameter name="RoomJID" required="true">
00166             <para>XMPP/Jabber JID (Name) of chat room.</para>
00167          </parameter>
00168          <parameter name="Message" required="true">
00169             <para>Message to be sent to the chat room.</para>
00170          </parameter>
00171          <parameter name="Nickname" required="false">
00172             <para>The nickname Asterisk uses in the chat room.</para>
00173          </parameter>
00174       </syntax>
00175       <description>
00176          <para>Allows user to send a message to a chat room via XMPP.</para>
00177          <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>
00178       </description>
00179    </application>
00180    <application name="JabberJoin" language="en_US">
00181       <synopsis>
00182          Join a chat room
00183       </synopsis>
00184       <syntax>
00185          <parameter name="Jabber" required="true">
00186             <para>Client or transport Asterisk uses to connect to Jabber.</para>
00187          </parameter>
00188          <parameter name="RoomJID" required="true">
00189             <para>XMPP/Jabber JID (Name) of chat room.</para>
00190          </parameter>
00191          <parameter name="Nickname" required="false">
00192             <para>The nickname Asterisk will use in the chat room.</para>
00193             <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>
00194          </parameter>
00195       </syntax>
00196       <description>
00197          <para>Allows Asterisk to join a chat room.</para>
00198       </description>
00199    </application>
00200    <application name="JabberLeave" language="en_US">
00201       <synopsis>
00202          Leave a chat room
00203       </synopsis>
00204       <syntax>
00205          <parameter name="Jabber" required="true">
00206             <para>Client or transport Asterisk uses to connect to Jabber.</para>
00207          </parameter>
00208          <parameter name="RoomJID" required="true">
00209             <para>XMPP/Jabber JID (Name) of chat room.</para>
00210          </parameter>
00211          <parameter name="Nickname" required="false">
00212             <para>The nickname Asterisk uses in the chat room.</para>
00213          </parameter>
00214       </syntax>
00215       <description>
00216          <para>Allows Asterisk to leave a chat room.</para>
00217       </description>
00218    </application>
00219    <application name="JabberStatus" language="en_US">
00220       <synopsis>
00221          Retrieve the status of a jabber list member
00222       </synopsis>
00223       <syntax>
00224          <parameter name="Jabber" required="true">
00225             <para>Client or transport Asterisk users to connect to Jabber.</para>
00226          </parameter>
00227          <parameter name="JID" required="true">
00228             <para>XMPP/Jabber JID (Name) of recipient.</para>
00229          </parameter>
00230          <parameter name="Variable" required="true">
00231             <para>Variable to store the status of requested user.</para>
00232          </parameter>
00233       </syntax>
00234       <description>
00235          <para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
00236          <para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
00237          The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
00238          <enumlist>
00239             <enum name="1">
00240                <para>Online.</para>
00241             </enum>
00242             <enum name="2">
00243                <para>Chatty.</para>
00244             </enum>
00245             <enum name="3">
00246                <para>Away.</para>
00247             </enum>
00248             <enum name="4">
00249                <para>Extended Away.</para>
00250             </enum>
00251             <enum name="5">
00252                <para>Do Not Disturb.</para>
00253             </enum>
00254             <enum name="6">
00255                <para>Offline.</para>
00256             </enum>
00257             <enum name="7">
00258                <para>Not In Roster.</para>
00259             </enum>
00260          </enumlist>
00261       </description>
00262    </application>
00263    <manager name="JabberSend" language="en_US">
00264       <synopsis>
00265          Sends a message to a Jabber Client.
00266       </synopsis>
00267       <syntax>
00268          <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
00269          <parameter name="Jabber" required="true">
00270             <para>Client or transport Asterisk uses to connect to JABBER.</para>
00271          </parameter>
00272          <parameter name="JID" required="true">
00273             <para>XMPP/Jabber JID (Name) of recipient.</para>
00274          </parameter>
00275          <parameter name="Message" required="true">
00276             <para>Message to be sent to the buddy.</para>
00277          </parameter>
00278       </syntax>
00279       <description>
00280          <para>Sends a message to a Jabber Client.</para>
00281       </description>
00282    </manager>
00283    <info name="XMPPMessageToInfo" language="en_US" tech="XMPP">
00284       <para>Specifying a prefix of <literal>xmpp:</literal> will send the
00285       message as an XMPP chat message.</para>
00286    </info>
00287    <info name="XMPPMessageFromInfo" language="en_US" tech="XMPP">
00288       <para>Specifying a prefix of <literal>xmpp:</literal> will specify the
00289       account defined in <literal>xmpp.conf</literal> to send the message from.
00290       Note that this field is required for XMPP messages.</para>
00291    </info>
00292 ***/
00293 
00294 /*! \brief Supported general configuration flags */
00295 enum {
00296    XMPP_AUTOPRUNE = (1 << 0),
00297    XMPP_AUTOREGISTER = (1 << 1),
00298    XMPP_AUTOACCEPT = (1 << 2),
00299    XMPP_DEBUG = (1 << 3),
00300    XMPP_USETLS = (1 << 4),
00301    XMPP_USESASL = (1 << 5),
00302    XMPP_FORCESSL = (1 << 6),
00303    XMPP_KEEPALIVE = (1 << 7),
00304    XMPP_COMPONENT = (1 << 8),
00305    XMPP_SEND_TO_DIALPLAN = (1 << 9),
00306    XMPP_DISTRIBUTE_EVENTS = (1 << 10),
00307 };
00308 
00309 /*! \brief Supported pubsub configuration flags */
00310 enum {
00311    XMPP_XEP0248 = (1 << 0),
00312    XMPP_PUBSUB = (1 << 1),
00313    XMPP_PUBSUB_AUTOCREATE = (1 << 2),
00314 };
00315 
00316 /*! \brief Number of buckets for client connections */
00317 #define CLIENT_BUCKETS 53
00318 
00319 /*! \brief Number of buckets for buddies (per client) */
00320 #define BUDDY_BUCKETS 53
00321 
00322 /*! \brief Number of buckets for resources (per buddy) */
00323 #define RESOURCE_BUCKETS 53
00324 
00325 /*! \brief Namespace for TLS support */
00326 #define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
00327 
00328 /*! \brief Status for a disappearing buddy */
00329 #define STATUS_DISAPPEAR 6
00330 
00331 /*! \brief Global debug status */
00332 static int debug;
00333 
00334 /*! \brief XMPP Global Configuration */
00335 struct ast_xmpp_global_config {
00336    struct ast_flags general; /*!< General configuration options */
00337    struct ast_flags pubsub;  /*!< Pubsub related configuration options */
00338 };
00339 
00340 /*! \brief XMPP Client Configuration */
00341 struct ast_xmpp_client_config {
00342    AST_DECLARE_STRING_FIELDS(
00343       AST_STRING_FIELD(name);        /*!< Name of the client connection */
00344       AST_STRING_FIELD(user);        /*!< Username to use for authentication */
00345       AST_STRING_FIELD(password);    /*!< Password to use for authentication */
00346       AST_STRING_FIELD(server);      /*!< Server hostname */
00347       AST_STRING_FIELD(statusmsg);   /*!< Status message for presence */
00348       AST_STRING_FIELD(pubsubnode);  /*!< Pubsub node */
00349       AST_STRING_FIELD(context);     /*!< Context for incoming messages */
00350       );
00351    int port;                       /*!< Port to use when connecting to server */
00352    int message_timeout;            /*!< Timeout for messages */
00353    int priority;                   /*!< Resource priority */
00354    struct ast_flags flags;         /*!< Various options that have been set */
00355    enum ikshowtype status;         /*!< Presence status */
00356    struct ast_xmpp_client *client; /*!< Pointer to the client */
00357    struct ao2_container *buddies;  /*!< Configured buddies */
00358 };
00359 
00360 struct xmpp_config {
00361    struct ast_xmpp_global_config *global; /*!< Global configuration options */
00362    struct ao2_container *clients;         /*!< Configured clients */
00363 };
00364 
00365 static AO2_GLOBAL_OBJ_STATIC(globals);
00366 
00367 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00368 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00369 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00370 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00371 
00372 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00373 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00374 
00375 /*! \brief Defined handlers for XMPP client states */
00376 static const struct xmpp_state_handler {
00377    int state;
00378    int component;
00379    int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
00380 } xmpp_state_handlers[] = {
00381    { XMPP_STATE_REQUEST_TLS, 0, xmpp_client_request_tls, },
00382    { XMPP_STATE_REQUESTED_TLS, 0, xmpp_client_requested_tls, },
00383    { XMPP_STATE_AUTHENTICATE, 0, xmpp_client_authenticate, },
00384    { XMPP_STATE_AUTHENTICATING, 0, xmpp_client_authenticating, },
00385    { XMPP_STATE_AUTHENTICATE, 1, xmpp_component_authenticate, },
00386    { XMPP_STATE_AUTHENTICATING, 1, xmpp_component_authenticating, },
00387 };
00388 
00389 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
00390 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
00391 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
00392 
00393 /*! \brief Defined handlers for different PAK types */
00394 static const struct xmpp_pak_handler {
00395    int type;
00396    int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
00397 } xmpp_pak_handlers[] = {
00398    { IKS_PAK_MESSAGE, xmpp_pak_message, },
00399    { IKS_PAK_PRESENCE, xmpp_pak_presence, },
00400    { IKS_PAK_S10N, xmpp_pak_s10n, },
00401 };
00402 
00403 static const char *app_ajisend = "JabberSend";
00404 static const char *app_ajisendgroup = "JabberSendGroup";
00405 static const char *app_ajistatus = "JabberStatus";
00406 static const char *app_ajijoin = "JabberJoin";
00407 static const char *app_ajileave = "JabberLeave";
00408 
00409 static ast_cond_t message_received_condition;
00410 static ast_mutex_t messagelock;
00411 
00412 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
00413 
00414 /*! \brief Destructor function for configuration */
00415 static void ast_xmpp_client_config_destructor(void *obj)
00416 {
00417    struct ast_xmpp_client_config *cfg = obj;
00418    ast_string_field_free_memory(cfg);
00419    ao2_cleanup(cfg->client);
00420    ao2_cleanup(cfg->buddies);
00421 }
00422 
00423 /*! \brief Destroy function for XMPP messages */
00424 static void xmpp_message_destroy(struct ast_xmpp_message *message)
00425 {
00426    if (message->from) {
00427       ast_free(message->from);
00428    }
00429    if (message->message) {
00430       ast_free(message->message);
00431    }
00432 
00433    ast_free(message);
00434 }
00435 
00436 /*! \brief Destructor callback function for XMPP client */
00437 static void xmpp_client_destructor(void *obj)
00438 {
00439    struct ast_xmpp_client *client = obj;
00440    struct ast_xmpp_message *message;
00441 
00442    ast_xmpp_client_disconnect(client);
00443 
00444    if (client->stack) {
00445       iks_stack_delete(client->stack);
00446    }
00447 
00448    ao2_cleanup(client->buddies);
00449 
00450    while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
00451       xmpp_message_destroy(message);
00452    }
00453    AST_LIST_HEAD_DESTROY(&client->messages);
00454 }
00455 
00456 /*! \brief Hashing function for XMPP buddy */
00457 static int xmpp_buddy_hash(const void *obj, const int flags)
00458 {
00459    const struct ast_xmpp_buddy *buddy = obj;
00460    const char *id = obj;
00461 
00462    return ast_str_hash(flags & OBJ_KEY ? id : buddy->id);
00463 }
00464 
00465 /*! \brief Comparator function for XMPP buddy */
00466 static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
00467 {
00468    struct ast_xmpp_buddy *buddy1 = obj, *buddy2 = arg;
00469    const char *id = arg;
00470 
00471    return !strcmp(buddy1->id, flags & OBJ_KEY ? id : buddy2->id) ? CMP_MATCH | CMP_STOP : 0;
00472 }
00473 
00474 /*! \brief Allocator function for ast_xmpp_client */
00475 static struct ast_xmpp_client *xmpp_client_alloc(const char *name)
00476 {
00477    struct ast_xmpp_client *client;
00478 
00479    if (!(client = ao2_alloc(sizeof(*client), xmpp_client_destructor))) {
00480       return NULL;
00481    }
00482 
00483    AST_LIST_HEAD_INIT(&client->messages);
00484    client->thread = AST_PTHREADT_NULL;
00485 
00486    if (!(client->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
00487       ast_log(LOG_ERROR, "Could not initialize buddy container for '%s'\n", name);
00488       ao2_ref(client, -1);
00489       return NULL;
00490    }
00491 
00492    if (ast_string_field_init(client, 512)) {
00493       ast_log(LOG_ERROR, "Could not initialize stringfields for '%s'\n", name);
00494       ao2_ref(client, -1);
00495       return NULL;
00496    }
00497 
00498    if (!(client->stack = iks_stack_new(8192, 8192))) {
00499       ast_log(LOG_ERROR, "Could not create an Iksemel stack for '%s'\n", name);
00500       ao2_ref(client, -1);
00501       return NULL;
00502    }
00503 
00504    ast_string_field_set(client, name, name);
00505 
00506    client->timeout = 50;
00507    client->state = XMPP_STATE_DISCONNECTED;
00508    ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
00509 
00510    return client;
00511 }
00512 
00513 /*! \brief Find function for configuration */
00514 static void *xmpp_config_find(struct ao2_container *tmp_container, const char *category)
00515 {
00516    return ao2_find(tmp_container, category, OBJ_KEY);
00517 }
00518 
00519 /*! \brief Look up existing client or create a new one */
00520 static void *xmpp_client_find_or_create(const char *category)
00521 {
00522    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00523    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
00524 
00525    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, category))) {
00526       return xmpp_client_alloc(category);
00527    }
00528 
00529    ao2_ref(clientcfg->client, +1);
00530    return clientcfg->client;
00531 }
00532 
00533 /*! \brief Allocator function for configuration */
00534 static void *ast_xmpp_client_config_alloc(const char *cat)
00535 {
00536    struct ast_xmpp_client_config *cfg;
00537 
00538    if (!(cfg = ao2_alloc(sizeof(*cfg), ast_xmpp_client_config_destructor))) {
00539       return NULL;
00540    }
00541 
00542    if (ast_string_field_init(cfg, 512)) {
00543       ao2_ref(cfg, -1);
00544       return NULL;
00545    }
00546 
00547    if (!(cfg->client = xmpp_client_find_or_create(cat))) {
00548       ao2_ref(cfg, -1);
00549       return NULL;
00550    }
00551 
00552    if (!(cfg->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
00553       ao2_ref(cfg, -1);
00554       return NULL;
00555    }
00556 
00557    ast_string_field_set(cfg, name, cat);
00558 
00559    return cfg;
00560 }
00561 
00562 /*! \brief Destructor for XMPP configuration */
00563 static void xmpp_config_destructor(void *obj)
00564 {
00565    struct xmpp_config *cfg = obj;
00566    ao2_cleanup(cfg->global);
00567    ao2_cleanup(cfg->clients);
00568 }
00569 
00570 /*! \brief Hashing function for configuration */
00571 static int xmpp_config_hash(const void *obj, const int flags)
00572 {
00573    const struct ast_xmpp_client_config *cfg = obj;
00574    const char *name = (flags & OBJ_KEY) ? obj : cfg->name;
00575    return ast_str_case_hash(name);
00576 }
00577 
00578 /*! \brief Comparator function for configuration */
00579 static int xmpp_config_cmp(void *obj, void *arg, int flags)
00580 {
00581    struct ast_xmpp_client_config *one = obj, *two = arg;
00582    const char *match = (flags & OBJ_KEY) ? arg : two->name;
00583    return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
00584 }
00585 
00586 /*! \brief Allocator for XMPP configuration */
00587 static void *xmpp_config_alloc(void)
00588 {
00589    struct xmpp_config *cfg;
00590 
00591    if (!(cfg = ao2_alloc(sizeof(*cfg), xmpp_config_destructor))) {
00592       return NULL;
00593    }
00594 
00595    if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
00596       goto error;
00597    }
00598 
00599    ast_set_flag(&cfg->global->general, XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE);
00600 
00601    if (!(cfg->clients = ao2_container_alloc(1, xmpp_config_hash, xmpp_config_cmp))) {
00602       goto error;
00603    }
00604 
00605    return cfg;
00606 error:
00607    ao2_ref(cfg, -1);
00608    return NULL;
00609 }
00610 
00611 static int xmpp_config_prelink(void *newitem)
00612 {
00613    struct ast_xmpp_client_config *clientcfg = newitem;
00614    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00615    RAII_VAR(struct ast_xmpp_client_config *, oldclientcfg, NULL, ao2_cleanup);
00616 
00617    if (ast_strlen_zero(clientcfg->user)) {
00618       ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
00619       return -1;
00620    } else if (ast_strlen_zero(clientcfg->password)) {
00621       ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
00622       return -1;
00623    } else if (ast_strlen_zero(clientcfg->server)) {
00624       ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
00625       return -1;
00626    }
00627 
00628    /* If this is a new connection force a reconnect */
00629    if (!cfg || !cfg->clients || !(oldclientcfg = xmpp_config_find(cfg->clients, clientcfg->name))) {
00630       clientcfg->client->reconnect = 1;
00631       return 0;
00632    }
00633 
00634    /* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
00635    if (strcmp(clientcfg->user, oldclientcfg->user) ||
00636        strcmp(clientcfg->password, oldclientcfg->password) ||
00637        strcmp(clientcfg->server, oldclientcfg->server) ||
00638        (clientcfg->port != oldclientcfg->port) ||
00639        (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
00640        (clientcfg->priority != oldclientcfg->priority)) {
00641       clientcfg->client->reconnect = 1;
00642    } else {
00643       clientcfg->client->reconnect = 0;
00644    }
00645 
00646    return 0;
00647 }
00648 
00649 static void xmpp_config_post_apply(void)
00650 {
00651    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00652 
00653    ao2_callback(cfg->clients, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_config_post_apply, NULL);
00654 }
00655 
00656 static struct aco_type global_option = {
00657    .type = ACO_GLOBAL,
00658    .item_offset = offsetof(struct xmpp_config, global),
00659    .category_match = ACO_WHITELIST,
00660    .category = "^general$",
00661 };
00662 
00663 struct aco_type *global_options[] = ACO_TYPES(&global_option);
00664 
00665 static struct aco_type client_option = {
00666    .type = ACO_ITEM,
00667    .category_match = ACO_BLACKLIST,
00668    .category = "^(general)$",
00669    .item_alloc = ast_xmpp_client_config_alloc,
00670    .item_find = xmpp_config_find,
00671    .item_prelink = xmpp_config_prelink,
00672    .item_offset = offsetof(struct xmpp_config, clients),
00673 };
00674 
00675 struct aco_type *client_options[] = ACO_TYPES(&client_option);
00676 
00677 struct aco_file res_xmpp_conf = {
00678    .filename = "xmpp.conf",
00679    .alias = "jabber.conf",
00680    .types = ACO_TYPES(&global_option, &client_option),
00681 };
00682 
00683 CONFIG_INFO_STANDARD(cfg_info, globals, xmpp_config_alloc,
00684            .files = ACO_FILES(&res_xmpp_conf),
00685            .post_apply_config = xmpp_config_post_apply,
00686    );
00687 
00688 /*! \brief Destructor callback function for XMPP resource */
00689 static void xmpp_resource_destructor(void *obj)
00690 {
00691    struct ast_xmpp_resource *resource = obj;
00692 
00693    if (resource->description) {
00694       ast_free(resource->description);
00695    }
00696 }
00697 
00698 /*! \brief Hashing function for XMPP resource */
00699 static int xmpp_resource_hash(const void *obj, const int flags)
00700 {
00701    const struct ast_xmpp_resource *resource = obj;
00702 
00703    return flags & OBJ_KEY ? -1 : resource->priority;
00704 }
00705 
00706 /*! \brief Comparator function for XMPP resource */
00707 static int xmpp_resource_cmp(void *obj, void *arg, int flags)
00708 {
00709    struct ast_xmpp_resource *resource1 = obj, *resource2 = arg;
00710    const char *resource = arg;
00711 
00712    return !strcmp(resource1->resource, flags & OBJ_KEY ? resource : resource2->resource) ? CMP_MATCH | CMP_STOP : 0;
00713 }
00714 
00715 /*! \brief Destructor callback function for XMPP buddy */
00716 static void xmpp_buddy_destructor(void *obj)
00717 {
00718    struct ast_xmpp_buddy *buddy = obj;
00719 
00720    if (buddy->resources) {
00721       ao2_ref(buddy->resources, -1);
00722    }
00723 }
00724 
00725 /*! \brief Helper function which returns whether an XMPP client connection is secure or not */
00726 static int xmpp_is_secure(struct ast_xmpp_client *client)
00727 {
00728 #ifdef HAVE_OPENSSL
00729    return client->stream_flags & SECURE;
00730 #else
00731    return 0;
00732 #endif
00733 }
00734 
00735 struct ast_xmpp_client *ast_xmpp_client_find(const char *name)
00736 {
00737    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00738    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
00739 
00740    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
00741       return NULL;
00742    }
00743 
00744    ao2_ref(clientcfg->client, +1);
00745    return clientcfg->client;
00746 }
00747 
00748 void ast_xmpp_client_unref(struct ast_xmpp_client *client)
00749 {
00750    ao2_ref(client, -1);
00751 }
00752 
00753 void ast_xmpp_client_lock(struct ast_xmpp_client *client)
00754 {
00755    ao2_lock(client);
00756 }
00757 
00758 void ast_xmpp_client_unlock(struct ast_xmpp_client *client)
00759 {
00760    ao2_unlock(client);
00761 }
00762 
00763 /*! \brief Internal function used to send a message to a user or chatroom */
00764 static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
00765 {
00766    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00767    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
00768    int res = 0;
00769    char from[XMPP_MAX_JIDLEN];
00770    iks *message_packet;
00771 
00772    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
00773        !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
00774       return -1;
00775    }
00776 
00777    if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
00778       snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
00779    } else {
00780       snprintf(from, sizeof(from), "%s", client->jid->full);
00781    }
00782 
00783    iks_insert_attrib(message_packet, "from", from);
00784 
00785    res = ast_xmpp_client_send(client, message_packet);
00786 
00787    iks_delete(message_packet);
00788 
00789    return res;
00790 }
00791 
00792 int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
00793 {
00794    return xmpp_client_send_message(client, 0, NULL, user, message);
00795 }
00796 
00797 int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
00798 {
00799    int res = 0;
00800    iks *invite, *body = NULL, *namespace = NULL;
00801 
00802    if (!(invite = iks_new("message")) || !(body = iks_new("body")) || !(namespace = iks_new("x"))) {
00803       res = -1;
00804       goto done;
00805    }
00806 
00807    iks_insert_attrib(invite, "to", user);
00808    ast_xmpp_client_lock(client);
00809    iks_insert_attrib(invite, "id", client->mid);
00810    ast_xmpp_increment_mid(client->mid);
00811    ast_xmpp_client_unlock(client);
00812    iks_insert_cdata(body, message, 0);
00813    iks_insert_node(invite, body);
00814    iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
00815    iks_insert_attrib(namespace, "jid", room);
00816    iks_insert_node(invite, namespace);
00817 
00818    res = ast_xmpp_client_send(client, invite);
00819 
00820 done:
00821    iks_delete(namespace);
00822    iks_delete(body);
00823    iks_delete(invite);
00824 
00825    return res;
00826 }
00827 
00828 static int xmpp_client_set_group_presence(struct ast_xmpp_client *client, const char *room, int level, const char *nick)
00829 {
00830    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00831    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
00832    int res = 0;
00833    iks *presence = NULL, *x = NULL;
00834    char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
00835 
00836    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
00837        !(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
00838       res = -1;
00839       goto done;
00840    }
00841 
00842    if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
00843       snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
00844       snprintf(roomid, sizeof(roomid), "%s/%s", room, nick);
00845    } else {
00846       snprintf(from, sizeof(from), "%s", client->jid->full);
00847       snprintf(roomid, sizeof(roomid), "%s/%s", room, S_OR(nick, client->jid->user));
00848    }
00849 
00850    iks_insert_attrib(presence, "to", roomid);
00851    iks_insert_attrib(presence, "from", from);
00852    iks_insert_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
00853    iks_insert_node(presence, x);
00854 
00855    res = ast_xmpp_client_send(client, presence);
00856 
00857 done:
00858    iks_delete(x);
00859    iks_delete(presence);
00860 
00861    return res;
00862 }
00863 
00864 int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
00865 {
00866    return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
00867 }
00868 
00869 int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
00870 {
00871    return xmpp_client_send_message(client, 1, nickname, address, message);
00872 }
00873 
00874 int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
00875 {
00876    return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
00877 }
00878 
00879 void ast_xmpp_increment_mid(char *mid)
00880 {
00881    int i = 0;
00882 
00883    for (i = strlen(mid) - 1; i >= 0; i--) {
00884       if (mid[i] != 'z') {
00885          mid[i] = mid[i] + 1;
00886          i = 0;
00887       } else {
00888          mid[i] = 'a';
00889       }
00890    }
00891 }
00892 
00893 /*!
00894  * \brief Create an IQ packet
00895  * \param client the configured XMPP client we use to connect to a XMPP server
00896  * \param type the type of IQ packet to create
00897  * \return iks*
00898  */
00899 static iks* xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
00900 {
00901    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00902    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
00903    iks *request;
00904 
00905    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
00906        !(request = iks_new("iq"))) {
00907       return NULL;
00908    }
00909 
00910    if (!ast_strlen_zero(clientcfg->pubsubnode)) {
00911       iks_insert_attrib(request, "to", clientcfg->pubsubnode);
00912    }
00913 
00914    iks_insert_attrib(request, "from", client->jid->full);
00915    iks_insert_attrib(request, "type", type);
00916    ast_xmpp_client_lock(client);
00917    ast_xmpp_increment_mid(client->mid);
00918    iks_insert_attrib(request, "id", client->mid);
00919    ast_xmpp_client_unlock(client);
00920 
00921    return request;
00922 }
00923 
00924 /*!
00925  * \brief Build the skeleton of a publish
00926  * \param client the configured XMPP client we use to connect to a XMPP server
00927  * \param node Name of the node that will be published to
00928  * \param event_type
00929  * \return iks *
00930  */
00931 static iks* xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node,
00932                       const char *event_type)
00933 {
00934    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
00935    iks *request, *pubsub, *publish, *item;
00936 
00937    if (!cfg || !cfg->global || !(request = xmpp_pubsub_iq_create(client, "set"))) {
00938       return NULL;
00939    }
00940 
00941    pubsub = iks_insert(request, "pubsub");
00942    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
00943    publish = iks_insert(pubsub, "publish");
00944    iks_insert_attrib(publish, "node", ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248) ? node : event_type);
00945    item = iks_insert(publish, "item");
00946    iks_insert_attrib(item, "id", node);
00947 
00948    return item;
00949 
00950 }
00951 
00952 static iks* xmpp_pubsub_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
00953 {
00954    iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
00955       *field_deliver_payload, *field_persist_items, *field_access_model,
00956       *field_pubsub_collection;
00957    configure = iks_insert(pubsub, "configure");
00958    x = iks_insert(configure, "x");
00959    iks_insert_attrib(x, "xmlns", "jabber:x:data");
00960    iks_insert_attrib(x, "type", "submit");
00961    field_owner = iks_insert(x, "field");
00962    iks_insert_attrib(field_owner, "var", "FORM_TYPE");
00963    iks_insert_attrib(field_owner, "type", "hidden");
00964    iks_insert_cdata(iks_insert(field_owner, "value"),
00965           "http://jabber.org/protocol/pubsub#owner", 39);
00966    if (node_type) {
00967       field_node_type = iks_insert(x, "field");
00968       iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
00969       iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
00970    }
00971    field_node_config = iks_insert(x, "field");
00972    iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
00973    iks_insert_attrib(field_node_config, "type", "hidden");
00974    iks_insert_cdata(iks_insert(field_node_config, "value"),
00975           "http://jabber.org/protocol/pubsub#node_config", 45);
00976    field_deliver_payload = iks_insert(x, "field");
00977    iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
00978    iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
00979    field_persist_items = iks_insert(x, "field");
00980    iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
00981    iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
00982    field_access_model = iks_insert(x, "field");
00983    iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
00984    iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
00985    if (node_type && !strcasecmp(node_type, "leaf")) {
00986       field_pubsub_collection = iks_insert(x, "field");
00987       iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
00988       iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
00989              strlen(collection_name));
00990    }
00991    return configure;
00992 }
00993 
00994 /*!
00995  * \brief Add Owner affiliations for pubsub node
00996  * \param client the configured XMPP client we use to connect to a XMPP server
00997  * \param node the name of the node to which to add affiliations
00998  * \return void
00999  */
01000 static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
01001 {
01002    iks *modify_affiliates = xmpp_pubsub_iq_create(client, "set");
01003    iks *pubsub, *affiliations, *affiliate;
01004    struct ao2_iterator i;
01005    struct ast_xmpp_buddy *buddy;
01006 
01007    if (!modify_affiliates) {
01008       ast_log(LOG_ERROR, "Could not create IQ for creating affiliations on client '%s'\n", client->name);
01009       return;
01010    }
01011 
01012    pubsub = iks_insert(modify_affiliates, "pubsub");
01013    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
01014    affiliations = iks_insert(pubsub, "affiliations");
01015    iks_insert_attrib(affiliations, "node", node);
01016 
01017    i = ao2_iterator_init(client->buddies, 0);
01018    while ((buddy = ao2_iterator_next(&i))) {
01019       affiliate = iks_insert(affiliations, "affiliation");
01020       iks_insert_attrib(affiliate, "jid", buddy->id);
01021       iks_insert_attrib(affiliate, "affiliation", "owner");
01022       ao2_ref(buddy, -1);
01023    }
01024    ao2_iterator_destroy(&i);
01025 
01026    ast_xmpp_client_send(client, modify_affiliates);
01027    iks_delete(modify_affiliates);
01028 }
01029 
01030 /*!
01031  * \brief Create a pubsub node
01032  * \param client the configured XMPP client we use to connect to a XMPP server
01033  * \param node_type the type of node to create
01034  * \param name the name of the node to create
01035  * \param collection_name
01036  * \return void
01037  */
01038 static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const
01039                 char *name, const char *collection_name)
01040 {
01041    iks *node, *pubsub, *create;
01042 
01043    if (!(node = xmpp_pubsub_iq_create(client, "set"))) {
01044       return;
01045    }
01046 
01047    pubsub = iks_insert(node, "pubsub");
01048    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
01049    create = iks_insert(pubsub, "create");
01050    iks_insert_attrib(create, "node", name);
01051    xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
01052    ast_xmpp_client_send(client, node);
01053    xmpp_pubsub_create_affiliations(client, name);
01054    iks_delete(node);
01055 }
01056 
01057 /*!
01058  * \brief Delete a PubSub node
01059  * \param client the configured XMPP client we use to connect to a XMPP server
01060  * \param node_name the name of the node to delete
01061  * return void
01062  */
01063 static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
01064 {
01065    iks *request, *pubsub, *delete;
01066 
01067    if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
01068       return;
01069    }
01070 
01071    pubsub = iks_insert(request, "pubsub");
01072    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
01073    delete = iks_insert(pubsub, "delete");
01074    iks_insert_attrib(delete, "node", node_name);
01075    ast_xmpp_client_send(client, request);
01076 
01077    iks_delete(delete);
01078    iks_delete(pubsub);
01079    iks_delete(request);
01080 }
01081 
01082 /*!
01083  * \brief Create a PubSub collection node.
01084  * \param client the configured XMPP client we use to connect to a XMPP server
01085  * \param collection_name The name to use for this collection
01086  * \return void.
01087  */
01088 static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
01089 {
01090    xmpp_pubsub_create_node(client, "collection", collection_name, NULL);
01091 }
01092 
01093 
01094 /*!
01095  * \brief Create a PubSub leaf node.
01096  * \param client the configured XMPP client we use to connect to a XMPP server
01097  * \param collection_name
01098  * \param leaf_name The name to use for this collection
01099  * \return void.
01100  */
01101 static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name,
01102                 const char *leaf_name)
01103 {
01104    xmpp_pubsub_create_node(client, "leaf", leaf_name, collection_name);
01105 }
01106 
01107 /*!
01108  * \brief Publish MWI to a PubSub node
01109  * \param client the configured XMPP client we use to connect to a XMPP server
01110  * \param mailbox The Mailbox
01111  * \param context The Context
01112  * \param oldmsgs Old messages
01113  * \param newmsgs New Messages
01114  * \return void
01115  */
01116 static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox,
01117                 const char *context, const char *oldmsgs, const char *newmsgs)
01118 {
01119    char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT], eid_str[20];
01120    iks *mailbox_node, *request;
01121 
01122    snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
01123 
01124    if (!(request = xmpp_pubsub_build_publish_skeleton(client, full_mailbox, "message_waiting"))) {
01125       return;
01126    }
01127 
01128    ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
01129    mailbox_node = iks_insert(request, "mailbox");
01130    iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
01131    iks_insert_attrib(mailbox_node, "eid", eid_str);
01132    iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
01133    iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
01134 
01135    ast_xmpp_client_send(client, iks_root(request));
01136 
01137    iks_delete(request);
01138 }
01139 
01140 /*!
01141  * \brief Publish device state to a PubSub node
01142  * \param client the configured XMPP client we use to connect to a XMPP server
01143  * \param device the name of the device whose state to publish
01144  * \param device_state the state to publish
01145  * \return void
01146  */
01147 static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device,
01148                     const char *device_state)
01149 {
01150    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01151    iks *request, *state;
01152    char eid_str[20];
01153 
01154    if (!cfg || !cfg->global || !(request = xmpp_pubsub_build_publish_skeleton(client, device, "device_state"))) {
01155       return;
01156    }
01157 
01158    if (ast_test_flag(&cfg->global->pubsub, XMPP_PUBSUB_AUTOCREATE)) {
01159       if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
01160          xmpp_pubsub_create_node(client, "leaf", device, "device_state");
01161       } else {
01162          xmpp_pubsub_create_node(client, NULL, device, NULL);
01163       }
01164    }
01165 
01166    ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
01167    state = iks_insert(request, "state");
01168    iks_insert_attrib(state, "xmlns", "http://asterisk.org");
01169    iks_insert_attrib(state, "eid", eid_str);
01170    iks_insert_cdata(state, device_state, strlen(device_state));
01171    ast_xmpp_client_send(client, iks_root(request));
01172    iks_delete(request);
01173 }
01174 
01175 /*!
01176  * \brief Callback function for MWI events
01177  * \param ast_event
01178  * \param data void pointer to ast_client structure
01179  * \return void
01180  */
01181 static void xmpp_pubsub_mwi_cb(const struct ast_event *ast_event, void *data)
01182 {
01183    struct ast_xmpp_client *client = data;
01184    const char *mailbox, *context;
01185    char oldmsgs[10], newmsgs[10];
01186 
01187    if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
01188       /* If the event didn't originate from this server, don't send it back out. */
01189       ast_debug(1, "Returning here\n");
01190       return;
01191    }
01192 
01193    mailbox = ast_event_get_ie_str(ast_event, AST_EVENT_IE_MAILBOX);
01194    context = ast_event_get_ie_str(ast_event, AST_EVENT_IE_CONTEXT);
01195    snprintf(oldmsgs, sizeof(oldmsgs), "%d",
01196        ast_event_get_ie_uint(ast_event, AST_EVENT_IE_OLDMSGS));
01197    snprintf(newmsgs, sizeof(newmsgs), "%d",
01198        ast_event_get_ie_uint(ast_event, AST_EVENT_IE_NEWMSGS));
01199    xmpp_pubsub_publish_mwi(client, mailbox, context, oldmsgs, newmsgs);
01200 }
01201 
01202 /*!
01203  * \brief Callback function for device state events
01204  * \param ast_event
01205  * \param data void pointer to ast_client structure
01206  * \return void
01207  */
01208 static void xmpp_pubsub_devstate_cb(const struct ast_event *ast_event, void *data)
01209 {
01210    struct ast_xmpp_client *client = data;
01211    const char *device, *device_state;
01212 
01213    if (ast_eid_cmp(&ast_eid_default, ast_event_get_ie_raw(ast_event, AST_EVENT_IE_EID))) {
01214       /* If the event didn't originate from this server, don't send it back out. */
01215       ast_debug(1, "Returning here\n");
01216       return;
01217    }
01218 
01219    device = ast_event_get_ie_str(ast_event, AST_EVENT_IE_DEVICE);
01220    device_state = ast_devstate_str(ast_event_get_ie_uint(ast_event, AST_EVENT_IE_STATE));
01221    xmpp_pubsub_publish_device_state(client, device, device_state);
01222 }
01223 
01224 /*!
01225  * \brief Unsubscribe from a PubSub node
01226  * \param client the configured XMPP client we use to connect to a XMPP server
01227  * \param node the name of the node to which to unsubscribe from
01228  * \return void
01229  */
01230 static void xmpp_pubsub_unsubscribe(struct ast_xmpp_client *client, const char *node)
01231 {
01232    iks *request = xmpp_pubsub_iq_create(client, "set");
01233    iks *pubsub, *unsubscribe;
01234 
01235    if (!request) {
01236       ast_log(LOG_ERROR, "Could not create IQ when creating pubsub unsubscription on client '%s'\n", client->name);
01237       return;
01238    }
01239 
01240    pubsub = iks_insert(request, "pubsub");
01241    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
01242    unsubscribe = iks_insert(pubsub, "unsubscribe");
01243    iks_insert_attrib(unsubscribe, "jid", client->jid->partial);
01244    iks_insert_attrib(unsubscribe, "node", node);
01245 
01246    ast_xmpp_client_send(client, request);
01247    iks_delete(request);
01248 }
01249 
01250 /*!
01251  * \brief Subscribe to a PubSub node
01252  * \param client the configured XMPP client we use to connect to a XMPP server
01253  * \param node the name of the node to which to subscribe
01254  * \return void
01255  */
01256 static void xmpp_pubsub_subscribe(struct ast_xmpp_client *client, const char *node)
01257 {
01258    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01259    iks *request = xmpp_pubsub_iq_create(client, "set");
01260    iks *pubsub, *subscribe;
01261 
01262    if (!cfg || !cfg->global || !request) {
01263       ast_log(LOG_ERROR, "Could not create IQ when creating pubsub subscription on client '%s'\n", client->name);
01264       return;
01265    }
01266 
01267    pubsub = iks_insert(request, "pubsub");
01268    iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
01269    subscribe = iks_insert(pubsub, "subscribe");
01270    iks_insert_attrib(subscribe, "jid", client->jid->partial);
01271    iks_insert_attrib(subscribe, "node", node);
01272    if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
01273       iks *options, *x, *sub_options, *sub_type, *sub_depth, *sub_expire;
01274       options = iks_insert(pubsub, "options");
01275       x = iks_insert(options, "x");
01276       iks_insert_attrib(x, "xmlns", "jabber:x:data");
01277       iks_insert_attrib(x, "type", "submit");
01278       sub_options = iks_insert(x, "field");
01279       iks_insert_attrib(sub_options, "var", "FORM_TYPE");
01280       iks_insert_attrib(sub_options, "type", "hidden");
01281       iks_insert_cdata(iks_insert(sub_options, "value"),
01282              "http://jabber.org/protocol/pubsub#subscribe_options", 51);
01283       sub_type = iks_insert(x, "field");
01284       iks_insert_attrib(sub_type, "var", "pubsub#subscription_type");
01285       iks_insert_cdata(iks_insert(sub_type, "value"), "items", 5);
01286       sub_depth = iks_insert(x, "field");
01287       iks_insert_attrib(sub_depth, "var", "pubsub#subscription_depth");
01288       iks_insert_cdata(iks_insert(sub_depth, "value"), "all", 3);
01289       sub_expire = iks_insert(x, "field");
01290       iks_insert_attrib(sub_expire, "var", "pubsub#expire");
01291       iks_insert_cdata(iks_insert(sub_expire, "value"), "presence", 8);
01292    }
01293    ast_xmpp_client_send(client, request);
01294    iks_delete(request);
01295 }
01296 
01297 /*!
01298  * \brief Callback for handling PubSub events
01299  * \param data void pointer to ast_xmpp_client structure
01300  * \param pak A pak
01301  * \return IKS_FILTER_EAT
01302  */
01303 static int xmpp_pubsub_handle_event(void *data, ikspak *pak)
01304 {
01305    char *item_id, *device_state, *context;
01306    int oldmsgs, newmsgs;
01307    iks *item, *item_content;
01308    struct ast_eid pubsub_eid;
01309    struct ast_event *event;
01310    item = iks_find(iks_find(iks_find(pak->x, "event"), "items"), "item");
01311    if (!item) {
01312       ast_log(LOG_ERROR, "Could not parse incoming PubSub event\n");
01313       return IKS_FILTER_EAT;
01314    }
01315    item_id = iks_find_attrib(item, "id");
01316    item_content = iks_child(item);
01317    ast_str_to_eid(&pubsub_eid, iks_find_attrib(item_content, "eid"));
01318    if (!ast_eid_cmp(&ast_eid_default, &pubsub_eid)) {
01319       ast_debug(1, "Returning here, eid of incoming event matches ours!\n");
01320       return IKS_FILTER_EAT;
01321    }
01322    if (!strcasecmp(iks_name(item_content), "state")) {
01323       device_state = iks_find_cdata(item, "state");
01324       if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
01325                    AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_STATE,
01326                    AST_EVENT_IE_PLTYPE_UINT, ast_devstate_val(device_state), AST_EVENT_IE_EID,
01327                    AST_EVENT_IE_PLTYPE_RAW, &pubsub_eid, sizeof(pubsub_eid),
01328                    AST_EVENT_IE_END))) {
01329          return IKS_FILTER_EAT;
01330       }
01331    } else if (!strcasecmp(iks_name(item_content), "mailbox")) {
01332       context = strsep(&item_id, "@");
01333       sscanf(iks_find_cdata(item_content, "OLDMSGS"), "%10d", &oldmsgs);
01334       sscanf(iks_find_cdata(item_content, "NEWMSGS"), "%10d", &newmsgs);
01335       if (!(event = ast_event_new(AST_EVENT_MWI, AST_EVENT_IE_MAILBOX,
01336                    AST_EVENT_IE_PLTYPE_STR, item_id, AST_EVENT_IE_CONTEXT,
01337                    AST_EVENT_IE_PLTYPE_STR, context, AST_EVENT_IE_OLDMSGS,
01338                    AST_EVENT_IE_PLTYPE_UINT, oldmsgs, AST_EVENT_IE_NEWMSGS,
01339                    AST_EVENT_IE_PLTYPE_UINT, newmsgs, AST_EVENT_IE_EID, AST_EVENT_IE_PLTYPE_RAW,
01340                    &pubsub_eid, sizeof(pubsub_eid), AST_EVENT_IE_END))) {
01341          return IKS_FILTER_EAT;
01342       }
01343    } else {
01344       ast_debug(1, "Don't know how to handle PubSub event of type %s\n",
01345            iks_name(item_content));
01346       return IKS_FILTER_EAT;
01347    }
01348    ast_event_queue_and_cache(event);
01349    return IKS_FILTER_EAT;
01350 }
01351 
01352 static int xmpp_pubsub_handle_error(void *data, ikspak *pak)
01353 {
01354    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01355    char *node_name, *error;
01356    int error_num;
01357    iks *orig_request, *orig_pubsub = iks_find(pak->x, "pubsub");
01358    struct ast_xmpp_client *client = data;
01359 
01360    if (!cfg || !cfg->global) {
01361       ast_log(LOG_ERROR, "No global configuration available\n");
01362       return IKS_FILTER_EAT;
01363    }
01364 
01365    if (!orig_pubsub) {
01366       ast_log(LOG_ERROR, "Error isn't a PubSub error, why are we here?\n");
01367       return IKS_FILTER_EAT;
01368    }
01369 
01370    orig_request = iks_child(orig_pubsub);
01371    error = iks_find_attrib(iks_find(pak->x, "error"), "code");
01372    node_name = iks_find_attrib(orig_request, "node");
01373 
01374    if (!sscanf(error, "%30d", &error_num)) {
01375       return IKS_FILTER_EAT;
01376    }
01377 
01378    if (error_num > 399 && error_num < 500 && error_num != 404) {
01379       ast_log(LOG_ERROR,
01380          "Error performing operation on PubSub node %s, %s.\n", node_name, error);
01381       return IKS_FILTER_EAT;
01382    } else if (error_num > 499 && error_num < 600) {
01383       ast_log(LOG_ERROR, "PubSub Server error, %s\n", error);
01384       return IKS_FILTER_EAT;
01385    }
01386 
01387    if (!strcasecmp(iks_name(orig_request), "publish")) {
01388       iks *request;
01389 
01390       if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
01391          if (iks_find(iks_find(orig_request, "item"), "state")) {
01392             xmpp_pubsub_create_leaf(client, "device_state", node_name);
01393          } else if (iks_find(iks_find(orig_request, "item"), "mailbox")) {
01394             xmpp_pubsub_create_leaf(client, "message_waiting", node_name);
01395          }
01396       } else {
01397          xmpp_pubsub_create_node(client, NULL, node_name, NULL);
01398       }
01399 
01400       if ((request = xmpp_pubsub_iq_create(client, "set"))) {
01401          iks_insert_node(request, orig_pubsub);
01402          ast_xmpp_client_send(client, request);
01403          iks_delete(request);
01404       } else {
01405          ast_log(LOG_ERROR, "PubSub publish could not create IQ\n");
01406       }
01407 
01408       return IKS_FILTER_EAT;
01409    } else if (!strcasecmp(iks_name(orig_request), "subscribe")) {
01410       if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
01411          xmpp_pubsub_create_collection(client, node_name);
01412       } else {
01413          xmpp_pubsub_create_node(client, NULL, node_name, NULL);
01414       }
01415    }
01416 
01417    return IKS_FILTER_EAT;
01418 }
01419 
01420 /*!
01421  * \brief Initialize collections for event distribution
01422  * \param client the configured XMPP client we use to connect to a XMPP server
01423  * \return void
01424  */
01425 static void xmpp_init_event_distribution(struct ast_xmpp_client *client)
01426 {
01427    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01428    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01429 
01430    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
01431       return;
01432    }
01433 
01434    xmpp_pubsub_unsubscribe(client, "device_state");
01435    xmpp_pubsub_unsubscribe(client, "message_waiting");
01436 
01437    if (!(client->mwi_sub = ast_event_subscribe(AST_EVENT_MWI, xmpp_pubsub_mwi_cb, "xmpp_pubsub_mwi_subscription",
01438                       client, AST_EVENT_IE_END))) {
01439       return;
01440    }
01441 
01442    if (ast_enable_distributed_devstate()) {
01443       return;
01444    }
01445    
01446 
01447    if (!(client->device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE_CHANGE,
01448                           xmpp_pubsub_devstate_cb, "xmpp_pubsub_devstate_subscription", client, AST_EVENT_IE_END))) {
01449       ast_event_unsubscribe(client->mwi_sub);
01450       client->mwi_sub = NULL;
01451       return;
01452    }
01453 
01454    ast_event_dump_cache(client->device_state_sub);
01455 
01456    xmpp_pubsub_subscribe(client, "device_state");
01457    xmpp_pubsub_subscribe(client, "message_waiting");
01458    iks_filter_add_rule(client->filter, xmpp_pubsub_handle_event, client, IKS_RULE_TYPE,
01459              IKS_PAK_MESSAGE, IKS_RULE_FROM, clientcfg->pubsubnode, IKS_RULE_DONE);
01460    iks_filter_add_rule(client->filter, xmpp_pubsub_handle_error, client, IKS_RULE_TYPE,
01461              IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, IKS_RULE_DONE);
01462 
01463 }
01464 
01465 /*! \brief Internal astobj2 callback function which returns the first resource, which is the highest priority one */
01466 static int xmpp_resource_immediate(void *obj, void *arg, int flags)
01467 {
01468    return CMP_MATCH | CMP_STOP;
01469 }
01470 
01471 /*
01472  * \internal
01473  * \brief Dial plan function status(). puts the status of watched user
01474  * into a channel variable.
01475  * \param chan ast_channel
01476  * \param data
01477  * \retval 0 success
01478  * \retval -1 error
01479  */
01480 static int xmpp_status_exec(struct ast_channel *chan, const char *data)
01481 {
01482    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01483    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01484    struct ast_xmpp_buddy *buddy;
01485    struct ast_xmpp_resource *resource;
01486    char *s = NULL, status[2];
01487    int stat = 7;
01488    static int deprecation_warning = 0;
01489    AST_DECLARE_APP_ARGS(args,
01490               AST_APP_ARG(sender);
01491               AST_APP_ARG(jid);
01492               AST_APP_ARG(variable);
01493       );
01494    AST_DECLARE_APP_ARGS(jid,
01495               AST_APP_ARG(screenname);
01496               AST_APP_ARG(resource);
01497       );
01498 
01499    if (deprecation_warning++ % 10 == 0) {
01500       ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
01501    }
01502 
01503    if (ast_strlen_zero(data)) {
01504       ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<jid>[/<resource>],<varname>\n");
01505       return 0;
01506    }
01507    s = ast_strdupa(data);
01508    AST_STANDARD_APP_ARGS(args, s);
01509 
01510    if (args.argc != 3) {
01511       ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
01512       return -1;
01513    }
01514 
01515    AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
01516    if (jid.argc < 1 || jid.argc > 2) {
01517       ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
01518       return -1;
01519    }
01520 
01521    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
01522       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
01523       return -1;
01524    }
01525 
01526    if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
01527       ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
01528       return -1;
01529    }
01530 
01531    if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
01532       resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
01533    }
01534 
01535    ao2_ref(buddy, -1);
01536 
01537    if (resource) {
01538       stat = resource->status;
01539       ao2_ref(resource, -1);
01540    } else {
01541       ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
01542    }
01543 
01544    snprintf(status, sizeof(status), "%d", stat);
01545    pbx_builtin_setvar_helper(chan, args.variable, status);
01546 
01547    return 0;
01548 }
01549 
01550 /*!
01551  * \internal
01552  * \brief Dial plan funtcion to retrieve the status of a buddy.
01553  * \param channel The associated ast_channel, if there is one
01554  * \param data The account, buddy JID, and optional timeout
01555  * timeout.
01556  * \retval 0 success
01557  * \retval -1 failure
01558  */
01559 static int acf_jabberstatus_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
01560 {
01561    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01562    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01563    struct ast_xmpp_buddy *buddy;
01564    struct ast_xmpp_resource *resource;
01565    int stat = 7;
01566    AST_DECLARE_APP_ARGS(args,
01567               AST_APP_ARG(sender);
01568               AST_APP_ARG(jid);
01569       );
01570    AST_DECLARE_APP_ARGS(jid,
01571               AST_APP_ARG(screenname);
01572               AST_APP_ARG(resource);
01573       );
01574 
01575    if (ast_strlen_zero(data)) {
01576       ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<jid>[/<resource>])\n");
01577       return 0;
01578    }
01579    AST_STANDARD_APP_ARGS(args, data);
01580 
01581    if (args.argc != 2) {
01582       ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments: sender and jid.\n");
01583       return -1;
01584    }
01585 
01586    AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
01587    if (jid.argc < 1 || jid.argc > 2) {
01588       ast_log(LOG_WARNING, "Wrong JID %s, exiting\n", args.jid);
01589       return -1;
01590    }
01591 
01592    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
01593       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
01594       return -1;
01595    }
01596 
01597    if (!(buddy = ao2_find(clientcfg->client->buddies, jid.screenname, OBJ_KEY))) {
01598       ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
01599       return -1;
01600    }
01601 
01602    if (ast_strlen_zero(jid.resource) || !(resource = ao2_find(buddy->resources, jid.resource, OBJ_KEY))) {
01603       resource = ao2_callback(buddy->resources, OBJ_NODATA, xmpp_resource_immediate, NULL);
01604    }
01605 
01606    ao2_ref(buddy, -1);
01607 
01608    if (resource) {
01609       stat = resource->status;
01610       ao2_ref(resource, -1);
01611    } else {
01612       ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
01613    }
01614 
01615    snprintf(buf, buflen, "%d", stat);
01616 
01617    return 0;
01618 }
01619 
01620 static struct ast_custom_function jabberstatus_function = {
01621    .name = "JABBER_STATUS",
01622    .read = acf_jabberstatus_read,
01623 };
01624 
01625 /*!
01626  * \brief Application to join a chat room
01627  * \param chan ast_channel
01628  * \param data  Data is sender|jid|nickname.
01629  * \retval 0 success
01630  * \retval -1 error
01631  */
01632 static int xmpp_join_exec(struct ast_channel *chan, const char *data)
01633 {
01634    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01635    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01636    char *s, nick[XMPP_MAX_RESJIDLEN];
01637    AST_DECLARE_APP_ARGS(args,
01638               AST_APP_ARG(sender);
01639               AST_APP_ARG(jid);
01640               AST_APP_ARG(nick);
01641       );
01642 
01643    if (ast_strlen_zero(data)) {
01644       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
01645       return -1;
01646    }
01647    s = ast_strdupa(data);
01648 
01649    AST_STANDARD_APP_ARGS(args, s);
01650    if (args.argc < 2 || args.argc > 3) {
01651       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajijoin);
01652       return -1;
01653    }
01654 
01655    if (strchr(args.jid, '/')) {
01656       ast_log(LOG_ERROR, "Invalid room name : resource must not be appended\n");
01657       return -1;
01658    }
01659 
01660    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
01661       ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
01662       return -1;
01663    }
01664 
01665    if (ast_strlen_zero(args.nick)) {
01666       if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
01667          snprintf(nick, sizeof(nick), "asterisk");
01668       } else {
01669          snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
01670       }
01671    } else {
01672       snprintf(nick, sizeof(nick), "%s", args.nick);
01673    }
01674 
01675    if (!ast_strlen_zero(args.jid) && strchr(args.jid, '@')) {
01676       ast_xmpp_chatroom_join(clientcfg->client, args.jid, nick);
01677    } else {
01678       ast_log(LOG_ERROR, "Problem with specified jid of '%s'\n", args.jid);
01679    }
01680 
01681    return 0;
01682 }
01683 
01684 /*!
01685  * \brief Application to leave a chat room
01686  * \param chan ast_channel
01687  * \param data  Data is sender|jid|nickname.
01688  * \retval 0 success
01689  * \retval -1 error
01690  */
01691 static int xmpp_leave_exec(struct ast_channel *chan, const char *data)
01692 {
01693    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01694    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01695    char *s, nick[XMPP_MAX_RESJIDLEN];
01696    AST_DECLARE_APP_ARGS(args,
01697               AST_APP_ARG(sender);
01698               AST_APP_ARG(jid);
01699               AST_APP_ARG(nick);
01700       );
01701 
01702    if (ast_strlen_zero(data)) {
01703       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
01704       return -1;
01705    }
01706    s = ast_strdupa(data);
01707 
01708    AST_STANDARD_APP_ARGS(args, s);
01709    if (args.argc < 2 || args.argc > 3) {
01710       ast_log(LOG_ERROR, "%s requires arguments (sender,jid[,nickname])\n", app_ajileave);
01711       return -1;
01712    }
01713 
01714    if (strchr(args.jid, '/')) {
01715       ast_log(LOG_ERROR, "Invalid room name, resource must not be appended\n");
01716       return -1;
01717    }
01718 
01719    if (ast_strlen_zero(args.jid) || !strchr(args.jid, '@')) {
01720       ast_log(LOG_ERROR, "No jabber ID specified\n");
01721       return -1;
01722    }
01723 
01724    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
01725       ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
01726       return -1;
01727    }
01728 
01729    if (ast_strlen_zero(args.nick)) {
01730       if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
01731          snprintf(nick, sizeof(nick), "asterisk");
01732       } else {
01733          snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
01734       }
01735    } else {
01736       snprintf(nick, sizeof(nick), "%s", args.nick);
01737    }
01738 
01739    ast_xmpp_chatroom_leave(clientcfg->client, args.jid, nick);
01740 
01741    return 0;
01742 }
01743 
01744 /*!
01745  * \internal
01746  * \brief Dial plan function to send a message.
01747  * \param chan ast_channel
01748  * \param data  Data is account,jid,message.
01749  * \retval 0 success
01750  * \retval -1 failure
01751  */
01752 static int xmpp_send_exec(struct ast_channel *chan, const char *data)
01753 {
01754    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01755    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01756    char *s;
01757    AST_DECLARE_APP_ARGS(args,
01758               AST_APP_ARG(sender);
01759               AST_APP_ARG(recipient);
01760               AST_APP_ARG(message);
01761       );
01762 
01763    if (ast_strlen_zero(data)) {
01764       ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
01765       return -1;
01766    }
01767    s = ast_strdupa(data);
01768 
01769    AST_STANDARD_APP_ARGS(args, s);
01770 
01771    if ((args.argc < 3) || ast_strlen_zero(args.message) || !strchr(args.recipient, '@')) {
01772       ast_log(LOG_WARNING, "%s requires arguments (account,jid,message)\n", app_ajisend);
01773       return -1;
01774    }
01775 
01776    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
01777       ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
01778       return -1;
01779    }
01780 
01781    ast_xmpp_client_send_message(clientcfg->client, args.recipient, args.message);
01782 
01783    return 0;
01784 }
01785 
01786 /*!
01787  * \brief Application to send a message to a groupchat.
01788  * \param chan ast_channel
01789  * \param data  Data is sender|groupchat|message.
01790  * \retval 0 success
01791  * \retval -1 error
01792  */
01793 static int xmpp_sendgroup_exec(struct ast_channel *chan, const char *data)
01794 {
01795    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01796    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01797    char *s, nick[XMPP_MAX_RESJIDLEN];
01798    AST_DECLARE_APP_ARGS(args,
01799               AST_APP_ARG(sender);
01800               AST_APP_ARG(groupchat);
01801               AST_APP_ARG(message);
01802               AST_APP_ARG(nick);
01803       );
01804 
01805    if (ast_strlen_zero(data)) {
01806       ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
01807       return -1;
01808    }
01809    s = ast_strdupa(data);
01810 
01811    AST_STANDARD_APP_ARGS(args, s);
01812    if ((args.argc < 3) || (args.argc > 4) || ast_strlen_zero(args.message) || !strchr(args.groupchat, '@')) {
01813       ast_log(LOG_ERROR, "%s requires arguments (sender,groupchatid,message[,nickname])\n", app_ajisendgroup);
01814       return -1;
01815    }
01816 
01817    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.sender))) {
01818       ast_log(LOG_ERROR, "Could not find sender connection: '%s'\n", args.sender);
01819       return -1;
01820    }
01821 
01822    if (ast_strlen_zero(args.nick) || args.argc == 3) {
01823       if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
01824          snprintf(nick, sizeof(nick), "asterisk");
01825       } else {
01826          snprintf(nick, sizeof(nick), "%s", clientcfg->client->jid->user);
01827       }
01828    } else {
01829       snprintf(nick, sizeof(nick), "%s", args.nick);
01830    }
01831 
01832    ast_xmpp_chatroom_send(clientcfg->client, nick, args.groupchat, args.message);
01833 
01834    return 0;
01835 }
01836 
01837 /*!
01838  * \internal
01839  * \brief Dial plan function to receive a message.
01840  * \param channel The associated ast_channel, if there is one
01841  * \param data The account, JID, and optional timeout
01842  * timeout.
01843  * \retval 0 success
01844  * \retval -1 failure
01845  */
01846 static int acf_jabberreceive_read(struct ast_channel *chan, const char *name, char *data, char *buf, size_t buflen)
01847 {
01848    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
01849    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
01850    char *aux = NULL, *parse = NULL;
01851    int timeout, jidlen, resourcelen, found = 0;
01852    struct timeval start;
01853    long diff = 0;
01854    struct ast_xmpp_message *message;
01855    AST_DECLARE_APP_ARGS(args,
01856               AST_APP_ARG(account);
01857               AST_APP_ARG(jid);
01858               AST_APP_ARG(timeout);
01859       );
01860    AST_DECLARE_APP_ARGS(jid,
01861               AST_APP_ARG(screenname);
01862               AST_APP_ARG(resource);
01863       );
01864 
01865    if (ast_strlen_zero(data)) {
01866       ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
01867       return -1;
01868    }
01869 
01870    parse = ast_strdupa(data);
01871    AST_STANDARD_APP_ARGS(args, parse);
01872 
01873    if (args.argc < 2 || args.argc > 3) {
01874       ast_log(LOG_WARNING, "%s requires arguments (account,jid[,timeout])\n", name);
01875       return -1;
01876    }
01877 
01878    parse = ast_strdupa(args.jid);
01879    AST_NONSTANDARD_APP_ARGS(jid, parse, '/');
01880    if (jid.argc < 1 || jid.argc > 2 || strlen(args.jid) > XMPP_MAX_JIDLEN) {
01881       ast_log(LOG_WARNING, "Invalid JID : %s\n", parse);
01882       return -1;
01883    }
01884 
01885    if (ast_strlen_zero(args.timeout)) {
01886       timeout = 20;
01887    } else {
01888       sscanf(args.timeout, "%d", &timeout);
01889       if (timeout <= 0) {
01890          ast_log(LOG_WARNING, "Invalid timeout specified: '%s'\n", args.timeout);
01891          return -1;
01892       }
01893    }
01894 
01895    jidlen = strlen(jid.screenname);
01896    resourcelen = ast_strlen_zero(jid.resource) ? 0 : strlen(jid.resource);
01897 
01898    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, args.account))) {
01899       ast_log(LOG_WARNING, "Could not find client %s, exiting\n", args.account);
01900       return -1;
01901    }
01902 
01903    ast_debug(3, "Waiting for an XMPP message from %s\n", args.jid);
01904 
01905    start = ast_tvnow();
01906 
01907    if (ast_autoservice_start(chan) < 0) {
01908       ast_log(LOG_WARNING, "Cannot start autoservice for channel %s\n", ast_channel_name(chan));
01909       return -1;
01910    }
01911 
01912    /* search the messages list, grab the first message that matches with
01913     * the from JID we're expecting, and remove it from the messages list */
01914    while (diff < timeout) {
01915       struct timespec ts = { 0, };
01916       struct timeval wait;
01917       int res = 0;
01918 
01919       wait = ast_tvadd(start, ast_tv(timeout, 0));
01920       ts.tv_sec = wait.tv_sec;
01921       ts.tv_nsec = wait.tv_usec * 1000;
01922 
01923       /* wait up to timeout seconds for an incoming message */
01924       ast_mutex_lock(&messagelock);
01925       if (AST_LIST_EMPTY(&clientcfg->client->messages)) {
01926          res = ast_cond_timedwait(&message_received_condition, &messagelock, &ts);
01927       }
01928       ast_mutex_unlock(&messagelock);
01929       if (res == ETIMEDOUT) {
01930          ast_debug(3, "No message received from %s in %d seconds\n", args.jid, timeout);
01931          break;
01932       }
01933 
01934       AST_LIST_LOCK(&clientcfg->client->messages);
01935       AST_LIST_TRAVERSE_SAFE_BEGIN(&clientcfg->client->messages, message, list) {
01936          if (jid.argc == 1) {
01937             /* no resource provided, compare bare JIDs */
01938             if (strncasecmp(jid.screenname, message->from, jidlen)) {
01939                continue;
01940             }
01941          } else {
01942             /* resource appended, compare bare JIDs and resources */
01943             char *resource = strchr(message->from, '/');
01944             if (!resource || strlen(resource) == 0) {
01945                ast_log(LOG_WARNING, "Remote JID has no resource : %s\n", message->from);
01946                if (strncasecmp(jid.screenname, message->from, jidlen)) {
01947                   continue;
01948                }
01949             } else {
01950                resource ++;
01951                if (strncasecmp(jid.screenname, message->from, jidlen) || strncmp(jid.resource, resource, resourcelen)) {
01952                   continue;
01953                }
01954             }
01955          }
01956          /* check if the message is not too old */
01957          if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
01958             ast_debug(3, "Found old message from %s, deleting it\n", message->from);
01959             AST_LIST_REMOVE_CURRENT(list);
01960             xmpp_message_destroy(message);
01961             continue;
01962          }
01963          found = 1;
01964          aux = ast_strdupa(message->message);
01965          AST_LIST_REMOVE_CURRENT(list);
01966          xmpp_message_destroy(message);
01967          break;
01968       }
01969       AST_LIST_TRAVERSE_SAFE_END;
01970       AST_LIST_UNLOCK(&clientcfg->client->messages);
01971       if (found) {
01972          break;
01973       }
01974 
01975       /* check timeout */
01976       diff = ast_tvdiff_ms(ast_tvnow(), start);
01977    }
01978 
01979    if (ast_autoservice_stop(chan) < 0) {
01980       ast_log(LOG_WARNING, "Cannot stop autoservice for channel %s\n", ast_channel_name(chan));
01981    }
01982 
01983    /* return if we timed out */
01984    if (!found) {
01985       ast_log(LOG_NOTICE, "Timed out : no message received from %s\n", args.jid);
01986       return -1;
01987    }
01988    ast_copy_string(buf, aux, buflen);
01989 
01990    return 0;
01991 }
01992 
01993 static struct ast_custom_function jabberreceive_function = {
01994    .name = "JABBER_RECEIVE",
01995    .read = acf_jabberreceive_read,
01996 };
01997 
01998 /*!
01999  * \internal
02000  * \brief Delete old messages from a given JID
02001  * Messages stored during more than client->message_timeout are deleted
02002  * \param client Asterisk's XMPP client
02003  * \param from the JID we received messages from
02004  * \retval the number of deleted messages
02005  */
02006 static int delete_old_messages(struct ast_xmpp_client *client, char *from)
02007 {
02008    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02009    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02010    int deleted = 0, isold = 0;
02011    struct ast_xmpp_message *message = NULL;
02012 
02013    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
02014       return 0;
02015    }
02016 
02017    AST_LIST_LOCK(&client->messages);
02018    AST_LIST_TRAVERSE_SAFE_BEGIN(&client->messages, message, list) {
02019       if (isold) {
02020          if (!from || !strncasecmp(from, message->from, strlen(from))) {
02021             AST_LIST_REMOVE_CURRENT(list);
02022             xmpp_message_destroy(message);
02023             deleted++;
02024          }
02025       } else if (ast_tvdiff_sec(ast_tvnow(), message->arrived) >= clientcfg->message_timeout) {
02026          isold = 1;
02027          if (!from || !strncasecmp(from, message->from, strlen(from))) {
02028             AST_LIST_REMOVE_CURRENT(list);
02029             xmpp_message_destroy(message);
02030             deleted++;
02031          }
02032       }
02033    }
02034    AST_LIST_TRAVERSE_SAFE_END;
02035    AST_LIST_UNLOCK(&client->messages);
02036 
02037    return deleted;
02038 }
02039 
02040 static int xmpp_send_cb(const struct ast_msg *msg, const char *to, const char *from)
02041 {
02042    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02043    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02044    char *sender, *dest;
02045    int res;
02046 
02047    sender = ast_strdupa(from);
02048    strsep(&sender, ":");
02049    dest = ast_strdupa(to);
02050    strsep(&dest, ":");
02051 
02052    if (ast_strlen_zero(sender)) {
02053       ast_log(LOG_ERROR, "MESSAGE(from) of '%s' invalid for XMPP\n", from);
02054       return -1;
02055    }
02056 
02057    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, sender))) {
02058       ast_log(LOG_WARNING, "Could not finder account to send from as '%s'\n", sender);
02059       return -1;
02060    }
02061 
02062    ast_debug(1, "Sending message to '%s' from '%s'\n", dest, clientcfg->name);
02063 
02064    if ((res = ast_xmpp_client_send_message(clientcfg->client, dest, ast_msg_get_body(msg))) != IKS_OK) {
02065       ast_log(LOG_WARNING, "Failed to send XMPP message (%d).\n", res);
02066    }
02067 
02068    return res == IKS_OK ? 0 : -1;
02069 }
02070 
02071 static const struct ast_msg_tech msg_tech = {
02072    .name = "xmpp",
02073    .msg_send = xmpp_send_cb,
02074 };
02075 
02076 /*! \brief Internal function which changes the XMPP client state */
02077 static void xmpp_client_change_state(struct ast_xmpp_client *client, int state)
02078 {
02079    client->state = state;
02080 }
02081 
02082 /*! \brief Internal function which creates a buddy on a client */
02083 static struct ast_xmpp_buddy *xmpp_client_create_buddy(struct ao2_container *container, const char *id)
02084 {
02085    struct ast_xmpp_buddy *buddy;
02086 
02087    if (!(buddy = ao2_alloc(sizeof(*buddy), xmpp_buddy_destructor))) {
02088       return NULL;
02089    }
02090 
02091    if (!(buddy->resources = ao2_container_alloc(RESOURCE_BUCKETS, xmpp_resource_hash, xmpp_resource_cmp))) {
02092       ao2_ref(buddy, -1);
02093       return NULL;
02094    }
02095 
02096    ast_copy_string(buddy->id, id, sizeof(buddy->id));
02097 
02098    /* Assume we need to subscribe to get their presence until proven otherwise */
02099    buddy->subscribe = 1;
02100 
02101    ao2_link(container, buddy);
02102 
02103    return buddy;
02104 }
02105 
02106 /*! \brief Helper function which unsubscribes a user and removes them from the roster */
02107 static int xmpp_client_unsubscribe_user(struct ast_xmpp_client *client, const char *user)
02108 {
02109    iks *iq, *query = NULL, *item = NULL;
02110 
02111    if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_UNSUBSCRIBE, user,
02112                          "Goodbye. Your status is no longer required.\n"))) {
02113       return -1;
02114    }
02115 
02116    if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item"))) {
02117       ast_log(LOG_WARNING, "Could not allocate memory for roster removal of '%s' from client '%s'\n",
02118          user, client->name);
02119       goto done;
02120    }
02121 
02122    iks_insert_attrib(iq, "from", client->jid->full);
02123    iks_insert_attrib(iq, "type", "set");
02124    iks_insert_attrib(query, "xmlns", "jabber:iq:roster");
02125    iks_insert_node(iq, query);
02126    iks_insert_attrib(item, "jid", user);
02127    iks_insert_attrib(item, "subscription", "remove");
02128    iks_insert_node(query, item);
02129 
02130    if (ast_xmpp_client_send(client, iq)) {
02131       ast_log(LOG_WARNING, "Could not send roster removal request of '%s' from client '%s'\n",
02132          user, client->name);
02133    }
02134 
02135 done:
02136    iks_delete(item);
02137    iks_delete(query);
02138    iks_delete(iq);
02139 
02140    return 0;
02141 }
02142 
02143 /*! \brief Callback function which subscribes to a user if needed */
02144 static int xmpp_client_subscribe_user(void *obj, void *arg, int flags)
02145 {
02146    struct ast_xmpp_buddy *buddy = obj;
02147    struct ast_xmpp_client *client = arg;
02148 
02149    if (!buddy->subscribe) {
02150       return 0;
02151    }
02152 
02153    if (ast_xmpp_client_send(client, iks_make_s10n(IKS_TYPE_SUBSCRIBE, buddy->id,
02154                          "Greetings! I am the Asterisk Open Source PBX and I want to subscribe to your presence\n"))) {
02155       ast_log(LOG_WARNING, "Could not send subscription for '%s' on client '%s'\n",
02156          buddy->id, client->name);
02157    }
02158 
02159    buddy->subscribe = 0;
02160 
02161    return 0;
02162 }
02163 
02164 /*! \brief Hook function called when roster is received from server */
02165 static int xmpp_roster_hook(void *data, ikspak *pak)
02166 {
02167    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02168    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02169    struct ast_xmpp_client *client = data;
02170    iks *item;
02171 
02172    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
02173       return IKS_FILTER_EAT;
02174    }
02175 
02176    for (item = iks_child(pak->query); item; item = iks_next(item)) {
02177       struct ast_xmpp_buddy *buddy;
02178 
02179       if (iks_strcmp(iks_name(item), "item")) {
02180          continue;
02181       }
02182 
02183       if (!(buddy = ao2_find(client->buddies, iks_find_attrib(item, "jid"), OBJ_KEY))) {
02184          if (ast_test_flag(&clientcfg->flags, XMPP_AUTOPRUNE)) {
02185             /* The buddy has not been specified in the configuration file, we no longer
02186              * want them on our buddy list or to receive their presence. */
02187             if (xmpp_client_unsubscribe_user(client, iks_find_attrib(item, "jid"))) {
02188                ast_log(LOG_ERROR, "Could not unsubscribe user '%s' on client '%s'\n",
02189                   iks_find_attrib(item, "jid"), client->name);
02190             }
02191             continue;
02192          }
02193 
02194          if (!(buddy = xmpp_client_create_buddy(client->buddies, iks_find_attrib(item, "jid")))) {
02195             ast_log(LOG_ERROR, "Could not allocate buddy '%s' on client '%s'\n", iks_find_attrib(item, "jid"),
02196                client->name);
02197             continue;
02198          }
02199       }
02200 
02201       /* Determine if we need to subscribe to their presence or not */
02202       if (!iks_strcmp(iks_find_attrib(item, "subscription"), "none") ||
02203           !iks_strcmp(iks_find_attrib(item, "subscription"), "from")) {
02204          buddy->subscribe = 1;
02205       } else {
02206          buddy->subscribe = 0;
02207       }
02208 
02209       ao2_ref(buddy, -1);
02210    }
02211 
02212    /* If autoregister is enabled we need to go through every buddy that we need to subscribe to and do so */
02213    if (ast_test_flag(&clientcfg->flags, XMPP_AUTOREGISTER)) {
02214       ao2_callback(client->buddies, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_subscribe_user, client);
02215    }
02216 
02217    xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
02218 
02219    return IKS_FILTER_EAT;
02220 }
02221 
02222 /*! \brief Internal function which changes the presence status of an XMPP client */
02223 static void xmpp_client_set_presence(struct ast_xmpp_client *client, const char *to, const char *from, int level, const char *desc)
02224 {
02225    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02226    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02227    iks *presence = NULL, *cnode = NULL, *priority = NULL;
02228    char priorityS[10];
02229 
02230    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
02231        !(presence = iks_make_pres(level, desc)) || !(cnode = iks_new("c")) || !(priority = iks_new("priority"))) {
02232       ast_log(LOG_ERROR, "Unable to allocate stanzas for setting presence status for client '%s'\n", client->name);
02233       goto done;
02234    }
02235 
02236    if (!ast_strlen_zero(to)) {
02237       iks_insert_attrib(presence, "to", to);
02238    }
02239 
02240    if (!ast_strlen_zero(from)) {
02241       iks_insert_attrib(presence, "from", from);
02242    }
02243 
02244    snprintf(priorityS, sizeof(priorityS), "%d", clientcfg->priority);
02245    iks_insert_cdata(priority, priorityS, strlen(priorityS));
02246    iks_insert_node(presence, priority);
02247    iks_insert_attrib(cnode, "node", "http://www.asterisk.org/xmpp/client/caps");
02248    iks_insert_attrib(cnode, "ver", "asterisk-xmpp");
02249    iks_insert_attrib(cnode, "ext", "voice-v1 video-v1 camera-v1");
02250    iks_insert_attrib(cnode, "xmlns", "http://jabber.org/protocol/caps");
02251    iks_insert_node(presence, cnode);
02252    ast_xmpp_client_send(client, presence);
02253 
02254 done:
02255    iks_delete(cnode);
02256    iks_delete(presence);
02257    iks_delete(priority);
02258 }
02259 
02260 /*! \brief Hook function called when client receives a service discovery get message */
02261 static int xmpp_client_service_discovery_get_hook(void *data, ikspak *pak)
02262 {
02263    struct ast_xmpp_client *client = data;
02264    iks *iq, *disco = NULL, *ident = NULL, *google = NULL, *jingle = NULL, *ice = NULL, *rtp = NULL, *audio = NULL, *video = NULL, *query = NULL;
02265 
02266    if (!(iq = iks_new("iq")) || !(query = iks_new("query")) || !(ident = iks_new("identity")) || !(disco = iks_new("feature")) ||
02267        !(google = iks_new("feature")) || !(jingle = iks_new("feature")) || !(ice = iks_new("feature")) || !(rtp = iks_new("feature")) ||
02268        !(audio = iks_new("feature")) || !(video = iks_new("feature"))) {
02269       ast_log(LOG_ERROR, "Could not allocate memory for responding to service discovery request from '%s' on client '%s'\n",
02270          pak->from->full, client->name);
02271       goto end;
02272    }
02273 
02274    iks_insert_attrib(iq, "from", client->jid->full);
02275 
02276    if (pak->from) {
02277       iks_insert_attrib(iq, "to", pak->from->full);
02278    }
02279 
02280    iks_insert_attrib(iq, "type", "result");
02281    iks_insert_attrib(iq, "id", pak->id);
02282    iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02283    iks_insert_attrib(ident, "category", "client");
02284    iks_insert_attrib(ident, "type", "pc");
02285    iks_insert_attrib(ident, "name", "asterisk");
02286    iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco#info");
02287 
02288    iks_insert_attrib(google, "var", "http://www.google.com/xmpp/protocol/voice/v1");
02289    iks_insert_attrib(jingle, "var", "urn:xmpp:jingle:1");
02290    iks_insert_attrib(ice, "var", "urn:xmpp:jingle:transports:ice-udp:1");
02291    iks_insert_attrib(rtp, "var", "urn:xmpp:jingle:apps:rtp:1");
02292    iks_insert_attrib(audio, "var", "urn:xmpp:jingle:apps:rtp:audio");
02293    iks_insert_attrib(video, "var", "urn:xmpp:jingle:apps:rtp:video");
02294    iks_insert_node(iq, query);
02295    iks_insert_node(query, ident);
02296    iks_insert_node(query, google);
02297    iks_insert_node(query, disco);
02298    iks_insert_node(query, jingle);
02299    iks_insert_node(query, ice);
02300    iks_insert_node(query, rtp);
02301    iks_insert_node(query, audio);
02302    iks_insert_node(query, video);
02303    ast_xmpp_client_send(client, iq);
02304 
02305 end:
02306    iks_delete(query);
02307    iks_delete(video);
02308    iks_delete(audio);
02309    iks_delete(rtp);
02310    iks_delete(ice);
02311    iks_delete(jingle);
02312    iks_delete(google);
02313    iks_delete(ident);
02314    iks_delete(disco);
02315    iks_delete(iq);
02316 
02317    return IKS_FILTER_EAT;
02318 }
02319 
02320 /*! \brief Hook function called when client receives a service discovery result message */
02321 static int xmpp_client_service_discovery_result_hook(void *data, ikspak *pak)
02322 {
02323    struct ast_xmpp_client *client = data;
02324    struct ast_xmpp_buddy *buddy;
02325    struct ast_xmpp_resource *resource;
02326 
02327    if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
02328       return IKS_FILTER_EAT;
02329    }
02330 
02331    if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY))) {
02332       ao2_ref(buddy, -1);
02333       return IKS_FILTER_EAT;
02334    }
02335 
02336    ao2_lock(resource);
02337 
02338    if (iks_find_with_attrib(pak->query, "feature", "var", "urn:xmpp:jingle:1")) {
02339       resource->caps.jingle = 1;
02340    }
02341 
02342    ao2_unlock(resource);
02343 
02344    ao2_ref(resource, -1);
02345    ao2_ref(buddy, -1);
02346 
02347    return IKS_FILTER_EAT;
02348 }
02349 
02350 /*! \brief Hook function called when client finishes authenticating with the server */
02351 static int xmpp_connect_hook(void *data, ikspak *pak)
02352 {
02353    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02354    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02355    struct ast_xmpp_client *client = data;
02356    iks *roster;
02357 
02358    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
02359       return -1;
02360    }
02361 
02362    client->jid = (iks_find_cdata(pak->query, "jid")) ? iks_id_new(client->stack, iks_find_cdata(pak->query, "jid")) : client->jid;
02363 
02364    if (ast_test_flag(&clientcfg->flags, XMPP_DISTRIBUTE_EVENTS)) {
02365       xmpp_init_event_distribution(client);
02366    }
02367 
02368    if (!(roster = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER))) {
02369       ast_log(LOG_ERROR, "Unable to allocate memory for roster request for client '%s'\n", client->name);
02370       return -1;
02371    }
02372 
02373    iks_filter_add_rule(client->filter, xmpp_client_service_discovery_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
02374    iks_filter_add_rule(client->filter, xmpp_client_service_discovery_result_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
02375 
02376    iks_insert_attrib(roster, "id", "roster");
02377    ast_xmpp_client_send(client, roster);
02378 
02379    iks_filter_remove_hook(client->filter, xmpp_connect_hook);
02380    iks_filter_add_rule(client->filter, xmpp_roster_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "roster", IKS_RULE_DONE);
02381 
02382    xmpp_client_set_presence(client, NULL, client->jid->full, clientcfg->status, clientcfg->statusmsg);
02383    xmpp_client_change_state(client, XMPP_STATE_ROSTER);
02384 
02385    return IKS_FILTER_EAT;
02386 }
02387 
02388 /*! \brief Logging hook function */
02389 static void xmpp_log_hook(void *data, const char *xmpp, size_t size, int incoming)
02390 {
02391    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02392    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02393    struct ast_xmpp_client *client = data;
02394 
02395    if (!ast_strlen_zero(xmpp)) {
02396       manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
02397    }
02398 
02399    if (!debug && (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) || !ast_test_flag(&clientcfg->flags, XMPP_DEBUG))) {
02400       return;
02401    }
02402 
02403    if (!incoming) {
02404       if (strlen(xmpp) == 1) {
02405          if (option_debug > 2  && xmpp[0] == ' ') {
02406             ast_verbose("\n<--- XMPP keep alive from '%s' --->\n", client->name);
02407          }
02408       } else {
02409          ast_verbose("\n<--- XMPP sent to '%s' --->\n%s\n<------------->\n", client->name, xmpp);
02410       }
02411    } else {
02412       ast_verbose("\n<--- XMPP received from '%s' --->\n%s\n<------------->\n", client->name, xmpp);
02413    }
02414 }
02415 
02416 /*! \brief Internal function which sends a raw message */
02417 static int xmpp_client_send_raw_message(struct ast_xmpp_client *client, const char *message)
02418 {
02419    int ret;
02420 #ifdef HAVE_OPENSSL
02421    int len = strlen(message);
02422 
02423    if (xmpp_is_secure(client)) {
02424       ret = SSL_write(client->ssl_session, message, len);
02425       if (ret) {
02426          /* Log the message here, because iksemel's logHook is
02427             unaccessible */
02428          xmpp_log_hook(client, message, len, 0);
02429          return IKS_OK;
02430       }
02431    }
02432 #endif
02433    /* If needed, data will be sent unencrypted, and logHook will
02434       be called inside iks_send_raw */
02435    ret = iks_send_raw(client->parser, message);
02436    if (ret != IKS_OK) {
02437       return ret;
02438    }
02439 
02440    return IKS_OK;
02441 }
02442 
02443 /*! \brief Helper function which sends an XMPP stream header to the server */
02444 static int xmpp_send_stream_header(struct ast_xmpp_client *client, const struct ast_xmpp_client_config *cfg, const char *to)
02445 {
02446    char *namespace = ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client";
02447    char msg[91 + strlen(namespace) + 6 + strlen(to) + 16 + 1];
02448 
02449    snprintf(msg, sizeof(msg), "<?xml version='1.0'?>"
02450        "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
02451        "%s' to='%s' version='1.0'>", namespace, to);
02452 
02453    return xmpp_client_send_raw_message(client, msg);
02454 }
02455 
02456 int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza)
02457 {
02458    return xmpp_client_send_raw_message(client, iks_string(iks_stack(stanza), stanza));
02459 }
02460 
02461 /*! \brief Internal function called when we need to request TLS support */
02462 static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02463 {
02464    /* If the client connection is already secure we can jump straight to authenticating */
02465    if (xmpp_is_secure(client)) {
02466       xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
02467       return 0;
02468    }
02469 
02470 #ifndef HAVE_OPENSSL
02471    ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL is not available.\n", client->name);
02472    return -1;
02473 #else
02474    if (iks_send_raw(client->parser, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>") == IKS_NET_TLSFAIL) {
02475       ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be started.\n", client->name);
02476       return -1;
02477    }
02478 
02479    client->stream_flags |= TRY_SECURE;
02480 
02481    xmpp_client_change_state(client, XMPP_STATE_REQUESTED_TLS);
02482 
02483    return 0;
02484 #endif
02485 }
02486 
02487 /*! \brief Internal function called when we receive a response to our TLS initiation request */
02488 static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02489 {
02490 #ifdef HAVE_OPENSSL
02491    int sock;
02492 #endif
02493 
02494    if (!strcmp(iks_name(node), "success")) {
02495       /* TLS is up and working, we can move on to authenticating now */
02496       xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
02497       return 0;
02498    } else if (!strcmp(iks_name(node), "failure")) {
02499       /* TLS negotiation was a failure, close it on down! */
02500       return -1;
02501    } else if (strcmp(iks_name(node), "proceed")) {
02502       /* Ignore any other responses */
02503       return 0;
02504    }
02505 
02506 #ifndef HAVE_OPENSSL
02507    ast_log(LOG_ERROR, "Somehow we managed to try to start TLS negotiation on client '%s' without OpenSSL support, disconnecting\n", client->name);
02508    return -1;
02509 #else
02510    client->ssl_method = SSLv3_method();
02511    if (!(client->ssl_context = SSL_CTX_new((SSL_METHOD *) client->ssl_method))) {
02512       goto failure;
02513    }
02514 
02515    if (!(client->ssl_session = SSL_new(client->ssl_context))) {
02516       goto failure;
02517    }
02518 
02519    sock = iks_fd(client->parser);
02520    if (!SSL_set_fd(client->ssl_session, sock)) {
02521       goto failure;
02522    }
02523 
02524    if (!SSL_connect(client->ssl_session)) {
02525       goto failure;
02526    }
02527 
02528    client->stream_flags &= (~TRY_SECURE);
02529    client->stream_flags |= SECURE;
02530 
02531    if (xmpp_send_stream_header(client, cfg, client->jid->server) != IKS_OK) {
02532       ast_log(LOG_ERROR, "TLS connection for client '%s' could not be established, failed to send stream header after negotiation\n",
02533          client->name);
02534       return -1;
02535    }
02536 
02537    ast_debug(1, "TLS connection for client '%s' started with server\n", client->name);
02538 
02539    xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATE);
02540 
02541    return 0;
02542 
02543 failure:
02544    ast_log(LOG_ERROR, "TLS connection for client '%s' cannot be established. OpenSSL initialization failed.\n", client->name);
02545    return -1;
02546 #endif
02547 }
02548 
02549 /*! \brief Internal function called when we need to authenticate using non-SASL */
02550 static int xmpp_client_authenticate_digest(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02551 {
02552    iks *iq = NULL, *query = NULL;
02553    char buf[41], sidpass[100];
02554 
02555    if (!(iq = iks_new("iq")) || !(query = iks_insert(iq, "query"))) {
02556       ast_log(LOG_ERROR, "Stanzas could not be allocated for authentication on client '%s'\n", client->name);
02557       iks_delete(iq);
02558       return -1;
02559    }
02560 
02561    iks_insert_attrib(iq, "type", "set");
02562    iks_insert_cdata(iks_insert(query, "username"), client->jid->user, 0);
02563    iks_insert_cdata(iks_insert(query, "resource"), client->jid->resource, 0);
02564 
02565    iks_insert_attrib(query, "xmlns", "jabber:iq:auth");
02566    snprintf(sidpass, sizeof(sidpass), "%s%s", iks_find_attrib(node, "id"), cfg->password);
02567    ast_sha1_hash(buf, sidpass);
02568    iks_insert_cdata(iks_insert(query, "digest"), buf, 0);
02569 
02570    ast_xmpp_client_lock(client);
02571    iks_filter_add_rule(client->filter, xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid, IKS_RULE_DONE);
02572    iks_insert_attrib(iq, "id", client->mid);
02573    ast_xmpp_increment_mid(client->mid);
02574    ast_xmpp_client_unlock(client);
02575 
02576    iks_insert_attrib(iq, "to", client->jid->server);
02577 
02578    ast_xmpp_client_send(client, iq);
02579 
02580    iks_delete(iq);
02581 
02582    xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
02583 
02584    return 0;
02585 }
02586 
02587 /*! \brief Internal function called when we need to authenticate using SASL */
02588 static int xmpp_client_authenticate_sasl(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02589 {
02590    int features, len = strlen(client->jid->user) + strlen(cfg->password) + 3;
02591    iks *auth;
02592    char combined[len];
02593    char base64[(len + 2) * 4 / 3];
02594 
02595    if (strcmp(iks_name(node), "stream:features")) {
02596       /* Ignore anything beside stream features */
02597       return 0;
02598    }
02599 
02600    features = iks_stream_features(node);
02601 
02602    if ((features & IKS_STREAM_SASL_MD5) && !xmpp_is_secure(client)) {
02603       if (iks_start_sasl(client->parser, IKS_SASL_DIGEST_MD5, (char*)client->jid->user, (char*)cfg->password) != IKS_OK) {
02604          ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL DIGEST-MD5 but could not\n", client->name);
02605          return -1;
02606       }
02607 
02608       xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
02609       return 0;
02610    }
02611 
02612    /* Our only other available option is plain so if they don't support it, bail out now */
02613    if (!(features & IKS_STREAM_SASL_PLAIN)) {
02614       ast_log(LOG_ERROR, "Tried to authenticate client '%s' using SASL PLAIN but server does not support it\n", client->name);
02615       return -1;
02616    }
02617 
02618    if (!(auth = iks_new("auth"))) {
02619       ast_log(LOG_ERROR, "Could not allocate memory for SASL PLAIN authentication for client '%s'\n", client->name);
02620       return -1;
02621    }
02622 
02623    iks_insert_attrib(auth, "xmlns", IKS_NS_XMPP_SASL);
02624    iks_insert_attrib(auth, "mechanism", "PLAIN");
02625 
02626    if (strchr(client->jid->user, '/')) {
02627       char *user = ast_strdupa(client->jid->user);
02628 
02629       snprintf(combined, sizeof(combined), "%c%s%c%s", 0, strsep(&user, "/"), 0, cfg->password);
02630    } else {
02631       snprintf(combined, sizeof(combined), "%c%s%c%s", 0, client->jid->user, 0, cfg->password);
02632    }
02633 
02634    ast_base64encode(base64, (const unsigned char *) combined, len - 1, (len + 2) * 4 / 3);
02635    iks_insert_cdata(auth, base64, 0);
02636 
02637    ast_xmpp_client_send(client, auth);
02638 
02639    iks_delete(auth);
02640 
02641    xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
02642 
02643    return 0;
02644 }
02645 
02646 /*! \brief Internal function called when we need to authenticate */
02647 static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02648 {
02649    return ast_test_flag(&cfg->flags, XMPP_USESASL) ? xmpp_client_authenticate_sasl(client, cfg, type, node) : xmpp_client_authenticate_digest(client, cfg, type, node);
02650 }
02651 
02652 /*! \brief Internal function called when we are authenticating */
02653 static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02654 {
02655    int features;
02656 
02657    if (!strcmp(iks_name(node), "success")) {
02658       /* Authentication was a success, yay! */
02659       xmpp_send_stream_header(client, cfg, client->jid->server);
02660 
02661       return 0;
02662    } else if (!strcmp(iks_name(node), "failure")) {
02663       /* Authentication was a bust, disconnect and reconnect later */
02664       return -1;
02665    } else if (strcmp(iks_name(node), "stream:features")) {
02666       /* Ignore any other responses */
02667       return 0;
02668    }
02669 
02670    features = iks_stream_features(node);
02671 
02672    if (features & IKS_STREAM_BIND) {
02673       iks *auth;
02674 
02675       if (!(auth = iks_make_resource_bind(client->jid))) {
02676          ast_log(LOG_ERROR, "Failed to allocate memory for stream bind on client '%s'\n", client->name);
02677          return -1;
02678       }
02679 
02680       ast_xmpp_client_lock(client);
02681       iks_insert_attrib(auth, "id", client->mid);
02682       ast_xmpp_increment_mid(client->mid);
02683       ast_xmpp_client_unlock(client);
02684       ast_xmpp_client_send(client, auth);
02685 
02686       iks_delete(auth);
02687 
02688       iks_filter_add_rule(client->filter, xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
02689    }
02690 
02691    if (features & IKS_STREAM_SESSION) {
02692       iks *auth;
02693 
02694       if (!(auth = iks_make_session())) {
02695          ast_log(LOG_ERROR, "Failed to allocate memory for stream session on client '%s'\n", client->name);
02696          return -1;
02697       }
02698 
02699       iks_insert_attrib(auth, "id", "auth");
02700       ast_xmpp_client_lock(client);
02701       ast_xmpp_increment_mid(client->mid);
02702       ast_xmpp_client_unlock(client);
02703       ast_xmpp_client_send(client, auth);
02704 
02705       iks_delete(auth);
02706 
02707       iks_filter_add_rule(client->filter, xmpp_connect_hook, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
02708    }
02709 
02710    return 0;
02711 }
02712 
02713 /*! \brief Internal function called when we should authenticate as a component */
02714 static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02715 {
02716    char secret[160], shasum[320], message[344];
02717    ikspak *pak = iks_packet(node);
02718 
02719    snprintf(secret, sizeof(secret), "%s%s", pak->id, cfg->password);
02720    ast_sha1_hash(shasum, secret);
02721    snprintf(message, sizeof(message), "<handshake>%s</handshake>", shasum);
02722 
02723    if (xmpp_client_send_raw_message(client, message) != IKS_OK) {
02724       ast_log(LOG_ERROR, "Unable to send handshake for component '%s'\n", client->name);
02725       return -1;
02726    }
02727 
02728    xmpp_client_change_state(client, XMPP_STATE_AUTHENTICATING);
02729 
02730    return 0;
02731 }
02732 
02733 /*! \brief Hook function called when component receives a service discovery get message */
02734 static int xmpp_component_service_discovery_get_hook(void *data, ikspak *pak)
02735 {
02736    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02737    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02738    struct ast_xmpp_client *client = data;
02739    iks *iq = NULL, *query = NULL, *identity = NULL, *disco = NULL, *reg = NULL, *commands = NULL, *gateway = NULL;
02740    iks *version = NULL, *vcard = NULL, *search = NULL, *item = NULL;
02741    char *node;
02742 
02743    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
02744        !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(identity = iks_new("identity")) || !(disco = iks_new("feature")) ||
02745        !(reg = iks_new("feature")) || !(commands = iks_new("feature")) || !(gateway = iks_new("feature")) || !(version = iks_new("feature")) ||
02746        !(vcard = iks_new("feature")) || !(search = iks_new("search")) || !(item = iks_new("item"))) {
02747       ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery get response to '%s' on component '%s'\n",
02748          pak->from->partial, client->name);
02749       goto done;
02750    }
02751 
02752    iks_insert_attrib(iq, "from", clientcfg->user);
02753    iks_insert_attrib(iq, "to", pak->from->full);
02754    iks_insert_attrib(iq, "id", pak->id);
02755    iks_insert_attrib(iq, "type", "result");
02756 
02757    if (!(node = iks_find_attrib(pak->query, "node"))) {
02758       iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02759       iks_insert_attrib(identity, "category", "gateway");
02760       iks_insert_attrib(identity, "type", "pstn");
02761       iks_insert_attrib(identity, "name", "Asterisk The Open Source PBX");
02762       iks_insert_attrib(disco, "var", "http://jabber.org/protocol/disco");
02763       iks_insert_attrib(reg, "var", "jabber:iq:register");
02764       iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
02765       iks_insert_attrib(gateway, "var", "jabber:iq:gateway");
02766       iks_insert_attrib(version, "var", "jabber:iq:version");
02767       iks_insert_attrib(vcard, "var", "vcard-temp");
02768       iks_insert_attrib(search, "var", "jabber:iq:search");
02769 
02770       iks_insert_node(iq, query);
02771       iks_insert_node(query, identity);
02772       iks_insert_node(query, disco);
02773       iks_insert_node(query, reg);
02774       iks_insert_node(query, commands);
02775       iks_insert_node(query, gateway);
02776       iks_insert_node(query, version);
02777       iks_insert_node(query, vcard);
02778       iks_insert_node(query, search);
02779    } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
02780       iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
02781       iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
02782       iks_insert_attrib(item, "node", "confirmaccount");
02783       iks_insert_attrib(item, "name", "Confirm account");
02784       iks_insert_attrib(item, "jid", clientcfg->user);
02785 
02786       iks_insert_node(iq, query);
02787       iks_insert_node(query, item);
02788    } else if (!strcasecmp(node, "confirmaccount")) {
02789       iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
02790       iks_insert_attrib(commands, "var", "http://jabber.org/protocol/commands");
02791 
02792       iks_insert_node(iq, query);
02793       iks_insert_node(query, commands);
02794    } else {
02795       ast_debug(3, "Unsupported service discovery info request received with node '%s' on component '%s'\n",
02796            node, client->name);
02797       goto done;
02798    }
02799 
02800    if (ast_xmpp_client_send(client, iq)) {
02801       ast_log(LOG_WARNING, "Could not send response to service discovery request on component '%s'\n",
02802          client->name);
02803    }
02804 
02805 done:
02806    iks_delete(search);
02807    iks_delete(vcard);
02808    iks_delete(version);
02809    iks_delete(gateway);
02810    iks_delete(commands);
02811    iks_delete(reg);
02812    iks_delete(disco);
02813    iks_delete(identity);
02814    iks_delete(query);
02815    iks_delete(iq);
02816 
02817    return IKS_FILTER_EAT;
02818 }
02819 
02820 /*! \brief Hook function called when the component is queried about registration */
02821 static int xmpp_component_register_get_hook(void *data, ikspak *pak)
02822 {
02823    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02824    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02825    struct ast_xmpp_client *client = data;
02826    iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL, *instructions = NULL;
02827    struct ast_xmpp_buddy *buddy;
02828    char *node;
02829 
02830    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
02831        !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(error = iks_new("error")) || !(notacceptable = iks_new("not-acceptable")) ||
02832        !(instructions = iks_new("instructions"))) {
02833       ast_log(LOG_ERROR, "Failed to allocate stanzas for register get response to '%s' on component '%s'\n",
02834          pak->from->partial, client->name);
02835       goto done;
02836    }
02837 
02838    iks_insert_attrib(iq, "from", clientcfg->user);
02839    iks_insert_attrib(iq, "to", pak->from->full);
02840    iks_insert_attrib(iq, "id", pak->id);
02841    iks_insert_attrib(iq, "type", "result");
02842    iks_insert_attrib(query, "xmlns", "jabber:iq:register");
02843    iks_insert_node(iq, query);
02844 
02845    if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
02846       iks_insert_attrib(error, "code", "406");
02847       iks_insert_attrib(error, "type", "modify");
02848       iks_insert_attrib(notacceptable, "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
02849 
02850       iks_insert_node(iq, error);
02851       iks_insert_node(error, notacceptable);
02852 
02853       ast_log(LOG_ERROR, "Received register attempt from '%s' but buddy is not configured on component '%s'\n",
02854          pak->from->partial, client->name);
02855    } else if (!(node = iks_find_attrib(pak->query, "node"))) {
02856       iks_insert_cdata(instructions, "Welcome to Asterisk - the Open Source PBX.\n", 0);
02857       iks_insert_node(query, instructions);
02858       ao2_ref(buddy, -1);
02859    } else {
02860       ast_log(LOG_WARNING, "Received register get to component '%s' using unsupported node '%s' from '%s'\n",
02861          client->name, node, pak->from->partial);
02862       ao2_ref(buddy, -1);
02863       goto done;
02864    }
02865 
02866    if (ast_xmpp_client_send(client, iq)) {
02867       ast_log(LOG_WARNING, "Could not send response to '%s' for received register get on component '%s'\n",
02868          pak->from->partial, client->name);
02869    }
02870 
02871 done:
02872    iks_delete(instructions);
02873    iks_delete(notacceptable);
02874    iks_delete(error);
02875    iks_delete(query);
02876    iks_delete(iq);
02877 
02878    return IKS_FILTER_EAT;
02879 }
02880 
02881 /*! \brief Hook function called when someone registers to the component */
02882 static int xmpp_component_register_set_hook(void *data, ikspak *pak)
02883 {
02884    struct ast_xmpp_client *client = data;
02885    iks *iq, *presence = NULL, *x = NULL;
02886 
02887    if (!(iq = iks_new("iq")) || !(presence = iks_new("presence")) || !(x = iks_new("x"))) {
02888       ast_log(LOG_ERROR, "Failed to allocate stanzas for register set response to '%s' on component '%s'\n",
02889          pak->from->partial, client->name);
02890       goto done;
02891    }
02892 
02893    iks_insert_attrib(iq, "from", client->jid->full);
02894    iks_insert_attrib(iq, "to", pak->from->full);
02895    iks_insert_attrib(iq, "id", pak->id);
02896    iks_insert_attrib(iq, "type", "result");
02897 
02898    if (ast_xmpp_client_send(client, iq)) {
02899       ast_log(LOG_WARNING, "Could not send response to '%s' for received register set on component '%s'\n",
02900          pak->from->partial, client->name);
02901       goto done;
02902    }
02903 
02904    iks_insert_attrib(presence, "from", client->jid->full);
02905    iks_insert_attrib(presence, "to", pak->from->partial);
02906    ast_xmpp_client_lock(client);
02907    iks_insert_attrib(presence, "id", client->mid);
02908    ast_xmpp_increment_mid(client->mid);
02909    ast_xmpp_client_unlock(client);
02910    iks_insert_attrib(presence, "type", "subscribe");
02911    iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
02912 
02913    iks_insert_node(presence, x);
02914 
02915    if (ast_xmpp_client_send(client, presence)) {
02916       ast_log(LOG_WARNING, "Could not send subscription to '%s' on component '%s'\n",
02917          pak->from->partial, client->name);
02918    }
02919 
02920 done:
02921    iks_delete(x);
02922    iks_delete(presence);
02923    iks_delete(iq);
02924 
02925    return IKS_FILTER_EAT;
02926 }
02927 
02928 /*! \brief Hook function called when we receive a service discovery items request */
02929 static int xmpp_component_service_discovery_items_hook(void *data, ikspak *pak)
02930 {
02931    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
02932    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
02933    struct ast_xmpp_client *client = data;
02934    iks *iq = NULL, *query = NULL, *item = NULL, *feature = NULL;
02935    char *node;
02936 
02937    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
02938        !(iq = iks_new("iq")) || !(query = iks_new("query")) || !(item = iks_new("item")) || !(feature = iks_new("feature"))) {
02939       ast_log(LOG_ERROR, "Failed to allocate stanzas for service discovery items response to '%s' on component '%s'\n",
02940          pak->from->partial, client->name);
02941       goto done;
02942    }
02943 
02944    iks_insert_attrib(iq, "from", clientcfg->user);
02945    iks_insert_attrib(iq, "to", pak->from->full);
02946    iks_insert_attrib(iq, "id", pak->id);
02947    iks_insert_attrib(iq, "type", "result");
02948    iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
02949    iks_insert_node(iq, query);
02950 
02951    if (!(node = iks_find_attrib(pak->query, "node"))) {
02952       iks_insert_attrib(item, "node", "http://jabber.org/protocol/commands");
02953       iks_insert_attrib(item, "name", "Asterisk Commands");
02954       iks_insert_attrib(item, "jid", clientcfg->user);
02955 
02956       iks_insert_node(query, item);
02957    } else if (!strcasecmp(node, "http://jabber.org/protocol/commands")) {
02958       iks_insert_attrib(query, "node", "http://jabber.org/protocol/commands");
02959    } else {
02960       ast_log(LOG_WARNING, "Received service discovery items request to component '%s' using unsupported node '%s' from '%s'\n",
02961          client->name, node, pak->from->partial);
02962       goto done;
02963    }
02964 
02965    if (ast_xmpp_client_send(client, iq)) {
02966       ast_log(LOG_WARNING, "Could not send response to service discovery items request from '%s' on component '%s'\n",
02967          pak->from->partial, client->name);
02968    }
02969 
02970 done:
02971    iks_delete(feature);
02972    iks_delete(item);
02973    iks_delete(query);
02974    iks_delete(iq);
02975 
02976    return IKS_FILTER_EAT;
02977 }
02978 
02979 /*! \brief Internal function called when we authenticated as a component */
02980 static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node)
02981 {
02982    if (strcmp(iks_name(node), "handshake")) {
02983       ast_log(LOG_ERROR, "Failed to authenticate component '%s'\n", client->name);
02984       return -1;
02985    }
02986 
02987    iks_filter_add_rule(client->filter, xmpp_component_service_discovery_items_hook, client, IKS_RULE_NS, "http://jabber.org/protocol/disco#items", IKS_RULE_DONE);
02988 
02989    iks_filter_add_rule(client->filter, xmpp_component_service_discovery_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
02990 
02991    /* This uses the client service discovery result hook on purpose, as the code is common between both */
02992    iks_filter_add_rule(client->filter, xmpp_client_service_discovery_result_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_NS, "http://jabber.org/protocol/disco#info", IKS_RULE_DONE);
02993 
02994    iks_filter_add_rule(client->filter, xmpp_component_register_get_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_GET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
02995    iks_filter_add_rule(client->filter, xmpp_component_register_set_hook, client, IKS_RULE_SUBTYPE, IKS_TYPE_SET, IKS_RULE_NS, "jabber:iq:register", IKS_RULE_DONE);
02996 
02997    xmpp_client_change_state(client, XMPP_STATE_CONNECTED);
02998 
02999    return 0;
03000 }
03001 
03002 /*! \brief Internal function called when a message is received */
03003 static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
03004 {
03005    struct ast_xmpp_message *message;
03006    char *body;
03007    int deleted = 0;
03008 
03009    ast_debug(3, "XMPP client '%s' received a message\n", client->name);
03010 
03011    if (!(body = iks_find_cdata(pak->x, "body"))) {
03012       /* Message contains no body, ignore it. */
03013       return 0;
03014    }
03015 
03016    if (!(message = ast_calloc(1, sizeof(*message)))) {
03017       return -1;
03018    }
03019 
03020    message->arrived = ast_tvnow();
03021 
03022    message->message = ast_strdup(body);
03023 
03024    ast_copy_string(message->id, S_OR(pak->id, ""), sizeof(message->id));
03025    message->from = !ast_strlen_zero(pak->from->full) ? ast_strdup(pak->from->full) : NULL;
03026 
03027    if (ast_test_flag(&cfg->flags, XMPP_SEND_TO_DIALPLAN)) {
03028       struct ast_msg *msg;
03029 
03030       if ((msg = ast_msg_alloc())) {
03031          int res;
03032 
03033          ast_xmpp_client_lock(client);
03034 
03035          res = ast_msg_set_to(msg, "xmpp:%s", cfg->user);
03036          res |= ast_msg_set_from(msg, "xmpp:%s", message->from);
03037          res |= ast_msg_set_body(msg, "%s", message->message);
03038          res |= ast_msg_set_context(msg, "%s", cfg->context);
03039 
03040          ast_xmpp_client_unlock(client);
03041 
03042          if (res) {
03043             ast_msg_destroy(msg);
03044          } else {
03045             ast_msg_queue(msg);
03046          }
03047       }
03048    }
03049 
03050    /* remove old messages received from this JID
03051     * and insert received message */
03052    deleted = delete_old_messages(client, pak->from->partial);
03053    ast_debug(3, "Deleted %d messages for client %s from JID %s\n", deleted, client->name, pak->from->partial);
03054    AST_LIST_LOCK(&client->messages);
03055    AST_LIST_INSERT_HEAD(&client->messages, message, list);
03056    AST_LIST_UNLOCK(&client->messages);
03057 
03058    /* wake up threads waiting for messages */
03059    ast_mutex_lock(&messagelock);
03060    ast_cond_broadcast(&message_received_condition);
03061    ast_mutex_unlock(&messagelock);
03062 
03063    return 0;
03064 }
03065 
03066 /*! \brief Helper function which sends a discovery information request to a user */
03067 static int xmpp_client_send_disco_info_request(struct ast_xmpp_client *client, const char *to, const char *from)
03068 {
03069    iks *iq, *query;
03070    int res;
03071 
03072    if (!(iq = iks_new("iq")) || !(query = iks_new("query"))) {
03073       iks_delete(iq);
03074       return -1;
03075    }
03076 
03077    iks_insert_attrib(iq, "type", "get");
03078    iks_insert_attrib(iq, "to", to);
03079    iks_insert_attrib(iq, "from", from);
03080    ast_xmpp_client_lock(client);
03081    iks_insert_attrib(iq, "id", client->mid);
03082    ast_xmpp_increment_mid(client->mid);
03083    ast_xmpp_client_unlock(client);
03084    iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#info");
03085    iks_insert_node(iq, query);
03086 
03087    res = ast_xmpp_client_send(client, iq);
03088 
03089    iks_delete(query);
03090    iks_delete(iq);
03091 
03092    return res;
03093 }
03094 
03095 /*! \brief Internal function called when a presence message is received */
03096 static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak)
03097 {
03098    struct ast_xmpp_buddy *buddy;
03099    struct ast_xmpp_resource *resource;
03100    char *type = iks_find_attrib(pak->x, "type");
03101    int status = pak->show ? pak->show : STATUS_DISAPPEAR;
03102 
03103    /* If no resource is available this is a general buddy presence update, which we will ignore */
03104    if (!pak->from->resource) {
03105       return 0;
03106    }
03107 
03108    if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY))) {
03109       /* Only output the message if it is not about us */
03110       if (strcmp(client->jid->partial, pak->from->partial)) {
03111          ast_log(LOG_WARNING, "Received presence information about '%s' despite not having them in roster on client '%s'\n",
03112             pak->from->partial, client->name);
03113       }
03114       return 0;
03115    }
03116 
03117    /* If this is a component presence probe request answer immediately with our presence status */
03118    if (ast_test_flag(&cfg->flags, XMPP_COMPONENT) && !ast_strlen_zero(type) && !strcasecmp(type, "probe")) {
03119       xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
03120    }
03121 
03122    ao2_lock(buddy->resources);
03123 
03124    if (!(resource = ao2_find(buddy->resources, pak->from->resource, OBJ_KEY | OBJ_NOLOCK))) {
03125       /* Only create the new resource if it is not going away - in reality this should not happen */
03126       if (status != STATUS_DISAPPEAR) {
03127          if (!(resource = ao2_alloc(sizeof(*resource), xmpp_resource_destructor))) {
03128             ast_log(LOG_ERROR, "Could not allocate resource object for resource '%s' of buddy '%s' on client '%s'\n",
03129                pak->from->resource, buddy->id, client->name);
03130             ao2_unlock(buddy->resources);
03131             ao2_ref(buddy, -1);
03132             return 0;
03133          }
03134 
03135          ast_copy_string(resource->resource, pak->from->resource, sizeof(resource->resource));
03136       }
03137    } else {
03138       /* We unlink the resource in case the priority changes or in case they are going away */
03139       ao2_unlink_flags(buddy->resources, resource, OBJ_NOLOCK);
03140    }
03141 
03142    /* Only update the resource and add it back in if it is not going away */
03143    if (resource && (status != STATUS_DISAPPEAR)) {
03144       char *node, *ver;
03145 
03146       /* Try to get the XMPP spec node, and fall back to Google if not found */
03147       if (!(node = iks_find_attrib(iks_find(pak->x, "c"), "node"))) {
03148          node = iks_find_attrib(iks_find(pak->x, "caps:c"), "node");
03149       }
03150 
03151       if (!(ver = iks_find_attrib(iks_find(pak->x, "c"), "ver"))) {
03152          ver = iks_find_attrib(iks_find(pak->x, "caps:c"), "ver");
03153       }
03154 
03155       if (resource->description) {
03156          ast_free(resource->description);
03157       }
03158 
03159       if ((node && strcmp(resource->caps.node, node)) || (ver && strcmp(resource->caps.version, ver))) {
03160          /* For interoperability reasons, proceed even if the resource fails to provide node or version */
03161          if (node) {
03162             ast_copy_string(resource->caps.node, node, sizeof(resource->caps.node));
03163          }
03164          if (ver) {
03165             ast_copy_string(resource->caps.version, ver, sizeof(resource->caps.version));
03166          }
03167 
03168          /* Google Talk places the capabilities information directly in presence, so see if it is there */
03169          if (iks_find_with_attrib(pak->x, "c", "node", "http://www.google.com/xmpp/client/caps") ||
03170              iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.google.com/xmpp/client/caps") ||
03171              iks_find_with_attrib(pak->x, "c", "node", "http://www.android.com/gtalk/client/caps") ||
03172              iks_find_with_attrib(pak->x, "caps:c", "node", "http://www.android.com/gtalk/client/caps") ||
03173              iks_find_with_attrib(pak->x, "c", "node", "http://mail.google.com/xmpp/client/caps") ||
03174              iks_find_with_attrib(pak->x, "caps:c", "node", "http://mail.google.com/xmpp/client/caps")) {
03175             resource->caps.google = 1;
03176          }
03177 
03178          /* To discover if the buddy supports Jingle we need to query, so do so */
03179          if (xmpp_client_send_disco_info_request(client, pak->from->full, client->jid->full)) {
03180             ast_log(LOG_WARNING, "Could not send discovery information request to resource '%s' of buddy '%s' on client '%s', capabilities may be incomplete\n", resource->resource, buddy->id, client->name);
03181          }
03182       }
03183 
03184       resource->status = status;
03185       resource->description = ast_strdup(iks_find_cdata(pak->x, "status"));
03186       resource->priority = atoi((iks_find_cdata(pak->x, "priority")) ? iks_find_cdata(pak->x, "priority") : "0");
03187 
03188       ao2_link_flags(buddy->resources, resource, OBJ_NOLOCK);
03189 
03190       manager_event(EVENT_FLAG_USER, "JabberStatus",
03191                "Account: %s\r\nJID: %s\r\nResource: %s\r\nStatus: %d\r\nPriority: %d"
03192                "\r\nDescription: %s\r\n",
03193                client->name, pak->from->partial, resource->resource, resource->status,
03194                resource->priority, S_OR(resource->description, ""));
03195 
03196       ao2_ref(resource, -1);
03197    } else {
03198       /* This will get hit by presence coming in for an unknown resource, and also when a resource goes away */
03199       if (resource) {
03200          ao2_ref(resource, -1);
03201       }
03202 
03203       manager_event(EVENT_FLAG_USER, "JabberStatus",
03204                "Account: %s\r\nJID: %s\r\nStatus: %d\r\n",
03205                client->name, pak->from->partial, pak->show ? pak->show : IKS_SHOW_UNAVAILABLE);
03206    }
03207 
03208    ao2_unlock(buddy->resources);
03209 
03210    ao2_ref(buddy, -1);
03211 
03212    return 0;
03213 }
03214 
03215 /*! \brief Internal function called when a subscription message is received */
03216 static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg,iks *node, ikspak *pak)
03217 {
03218    struct ast_xmpp_buddy *buddy;
03219 
03220    switch (pak->subtype) {
03221    case IKS_TYPE_SUBSCRIBE:
03222       if (ast_test_flag(&cfg->flags, XMPP_AUTOREGISTER)) {
03223          iks *presence, *status = NULL;
03224 
03225          if ((presence = iks_new("presence")) && (status = iks_new("status"))) {
03226             iks_insert_attrib(presence, "type", "subscribed");
03227             iks_insert_attrib(presence, "to", pak->from->full);
03228             iks_insert_attrib(presence, "from", client->jid->full);
03229 
03230             if (pak->id) {
03231                iks_insert_attrib(presence, "id", pak->id);
03232             }
03233 
03234             iks_insert_cdata(status, "Asterisk has approved your subscription", 0);
03235             iks_insert_node(presence, status);
03236 
03237             if (ast_xmpp_client_send(client, presence)) {
03238                ast_log(LOG_ERROR, "Could not send subscription acceptance to '%s' from client '%s'\n",
03239                   pak->from->partial, client->name);
03240             }
03241          } else {
03242             ast_log(LOG_ERROR, "Could not allocate presence stanzas for accepting subscription from '%s' to client '%s'\n",
03243                pak->from->partial, client->name);
03244          }
03245 
03246          iks_delete(status);
03247          iks_delete(presence);
03248       }
03249 
03250       if (ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
03251          xmpp_client_set_presence(client, pak->from->full, iks_find_attrib(pak->x, "to"), cfg->status, cfg->statusmsg);
03252       }
03253       /* This purposely flows through so we have the subscriber amongst our buddies */
03254    case IKS_TYPE_SUBSCRIBED:
03255       ao2_lock(client->buddies);
03256 
03257       if (!(buddy = ao2_find(client->buddies, pak->from->partial, OBJ_KEY | OBJ_NOLOCK))) {
03258          buddy = xmpp_client_create_buddy(client->buddies, pak->from->partial);
03259       }
03260 
03261       if (!buddy) {
03262          ast_log(LOG_WARNING, "Could not find or create buddy '%s' on client '%s'\n",
03263             pak->from->partial, client->name);
03264       } else {
03265          ao2_ref(buddy, -1);
03266       }
03267 
03268       ao2_unlock(client->buddies);
03269 
03270       break;
03271    default:
03272       break;
03273    }
03274 
03275    return 0;
03276 }
03277 
03278 /*! \brief Action hook for when things occur */
03279 static int xmpp_action_hook(void *data, int type, iks *node)
03280 {
03281    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03282    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03283    struct ast_xmpp_client *client = data;
03284    ikspak *pak;
03285    int i;
03286 
03287    if (!node) {
03288       ast_log(LOG_ERROR, "xmpp_action_hook was called without a packet\n");
03289       return IKS_HOOK;
03290    }
03291 
03292    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
03293       return IKS_HOOK;
03294    }
03295 
03296    /* If the client is disconnecting ignore everything */
03297    if (client->state == XMPP_STATE_DISCONNECTING) {
03298       return IKS_HOOK;
03299    }
03300 
03301    pak = iks_packet(node);
03302 
03303    /* work around iksemel's impossibility to recognize node names
03304     * containing a colon. Set the namespace of the corresponding
03305     * node accordingly. */
03306    if (iks_has_children(node) && strchr(iks_name(iks_child(node)), ':')) {
03307       char *node_ns = NULL;
03308       char attr[XMPP_MAX_ATTRLEN];
03309       char *node_name = iks_name(iks_child(node));
03310       char *aux = strchr(node_name, ':') + 1;
03311       snprintf(attr, strlen("xmlns:") + (strlen(node_name) - strlen(aux)), "xmlns:%s", node_name);
03312       node_ns = iks_find_attrib(iks_child(node), attr);
03313       if (node_ns) {
03314          pak->ns = node_ns;
03315          pak->query = iks_child(node);
03316       }
03317    }
03318 
03319    /* Process through any state handlers */
03320    for (i = 0; i < ARRAY_LEN(xmpp_state_handlers); i++) {
03321       if ((xmpp_state_handlers[i].state == client->state) && (xmpp_state_handlers[i].component == (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? 1 : 0))) {
03322          if (xmpp_state_handlers[i].handler(client, clientcfg, type, node)) {
03323             /* If the handler wants us to stop now, do so */
03324             return IKS_HOOK;
03325          }
03326          break;
03327       }
03328    }
03329 
03330    /* Process through any PAK handlers */
03331    for (i = 0; i < ARRAY_LEN(xmpp_pak_handlers); i++) {
03332       if (xmpp_pak_handlers[i].type == pak->type) {
03333          if (xmpp_pak_handlers[i].handler(client, clientcfg, node, pak)) {
03334             /* If the handler wants us to stop now, do so */
03335             return IKS_HOOK;
03336          }
03337          break;
03338       }
03339    }
03340 
03341    /* Send the packet through the filter in case any filters want to process it */
03342    iks_filter_packet(client->filter, pak);
03343 
03344    iks_delete(node);
03345 
03346    return IKS_OK;
03347 }
03348 
03349 int ast_xmpp_client_disconnect(struct ast_xmpp_client *client)
03350 {
03351    if ((client->thread != AST_PTHREADT_NULL) && !pthread_equal(pthread_self(), client->thread)) {
03352       client->state = XMPP_STATE_DISCONNECTING;
03353       pthread_join(client->thread, NULL);
03354       client->thread = AST_PTHREADT_NULL;
03355    }
03356 
03357    if (client->mwi_sub) {
03358       ast_event_unsubscribe(client->mwi_sub);
03359       client->mwi_sub = NULL;
03360       xmpp_pubsub_unsubscribe(client, "message_waiting");
03361    }
03362 
03363    if (client->device_state_sub) {
03364       ast_event_unsubscribe(client->device_state_sub);
03365       client->device_state_sub = NULL;
03366       xmpp_pubsub_unsubscribe(client, "device_state");
03367    }
03368 
03369 #ifdef HAVE_OPENSSL
03370    if (client->stream_flags & SECURE) {
03371       SSL_shutdown(client->ssl_session);
03372       SSL_CTX_free(client->ssl_context);
03373       SSL_free(client->ssl_session);
03374    }
03375 
03376    client->stream_flags = 0;
03377 #endif
03378 
03379    if (client->parser) {
03380       iks_disconnect(client->parser);
03381    }
03382 
03383    /* Disconnecting the parser and going back to a disconnected state means any hooks should no longer be present */
03384    if (client->filter) {
03385       iks_filter_delete(client->filter);
03386       client->filter = NULL;
03387    }
03388 
03389    client->state = XMPP_STATE_DISCONNECTED;
03390 
03391    return 0;
03392 }
03393 
03394 /*! \brief Internal function used to reconnect an XMPP client to its server */
03395 static int xmpp_client_reconnect(struct ast_xmpp_client *client)
03396 {
03397    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03398    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03399    int res = IKS_NET_NOCONN;
03400 
03401    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name))) {
03402       return -1;
03403    }
03404 
03405    ast_xmpp_client_disconnect(client);
03406 
03407    client->timeout = 50;
03408    iks_parser_reset(client->parser);
03409 
03410    if (!client->filter && !(client->filter = iks_filter_new())) {
03411       ast_log(LOG_ERROR, "Could not create IKS filter for client connection '%s'\n", client->name);
03412       return -1;
03413    }
03414 
03415    /* If it's a component connect to user otherwise connect to server */
03416    res = iks_connect_via(client->parser, S_OR(clientcfg->server, client->jid->server), clientcfg->port,
03417                ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) ? clientcfg->user : client->jid->server);
03418 
03419    if (res == IKS_NET_NOCONN) {
03420       ast_log(LOG_ERROR, "No XMPP connection available when trying to connect client '%s'\n", client->name);
03421       return -1;
03422    } else if (res == IKS_NET_NODNS) {
03423       ast_log(LOG_ERROR, "No DNS available for XMPP connection when trying to connect client '%s'\n", client->name);
03424       return -1;
03425    }
03426 
03427    /* Depending on the configuration of the client we eiher jump to requesting TLS, or authenticating */
03428    xmpp_client_change_state(client, (ast_test_flag(&clientcfg->flags, XMPP_USETLS) ? XMPP_STATE_REQUEST_TLS : XMPP_STATE_AUTHENTICATE));
03429 
03430    return 0;
03431 }
03432 
03433 /*! \brief Internal function which polls on an XMPP client and receives data */
03434 static int xmpp_io_recv(struct ast_xmpp_client *client, char *buffer, size_t buf_len, int timeout)
03435 {
03436    struct pollfd pfd = { .events = POLLIN };
03437    int len, res;
03438 
03439 #ifdef HAVE_OPENSSL
03440    if (xmpp_is_secure(client)) {
03441       pfd.fd = SSL_get_fd(client->ssl_session);
03442       if (pfd.fd < 0) {
03443          return -1;
03444       }
03445    } else
03446 #endif /* HAVE_OPENSSL */
03447       pfd.fd = iks_fd(client->parser);
03448 
03449    res = ast_poll(&pfd, 1, timeout > 0 ? timeout * 1000 : -1);
03450    if (res > 0) {
03451 #ifdef HAVE_OPENSSL
03452       if (xmpp_is_secure(client)) {
03453          len = SSL_read(client->ssl_session, buffer, buf_len);
03454       } else
03455 #endif /* HAVE_OPENSSL */
03456          len = recv(pfd.fd, buffer, buf_len, 0);
03457 
03458       if (len > 0) {
03459          return len;
03460       } else if (len <= 0) {
03461          return -1;
03462       }
03463    }
03464    return res;
03465 }
03466 
03467 /*! \brief Internal function which receives data from the XMPP client connection */
03468 static int xmpp_client_receive(struct ast_xmpp_client *client, unsigned int timeout)
03469 {
03470    int len, ret, pos = 0, newbufpos = 0;
03471    char buf[NET_IO_BUF_SIZE - 1] = "";
03472    char newbuf[NET_IO_BUF_SIZE - 1] = "";
03473    unsigned char c;
03474 
03475    while (1) {
03476       len = xmpp_io_recv(client, buf, NET_IO_BUF_SIZE - 2, timeout);
03477       if (len < 0) return IKS_NET_RWERR;
03478       if (len == 0) return IKS_NET_EXPIRED;
03479       buf[len] = '\0';
03480 
03481       /* our iksemel parser won't work as expected if we feed
03482          it with XML packets that contain multiple whitespace
03483          characters between tags */
03484       while (pos < len) {
03485          c = buf[pos];
03486          /* if we stumble on the ending tag character,
03487             we skip any whitespace that follows it*/
03488          if (c == '>') {
03489             while (isspace(buf[pos+1])) {
03490                pos++;
03491             }
03492          }
03493          newbuf[newbufpos] = c;
03494          newbufpos++;
03495          pos++;
03496       }
03497       pos = 0;
03498       newbufpos = 0;
03499 
03500       /* Log the message here, because iksemel's logHook is
03501          unaccessible */
03502       xmpp_log_hook(client, buf, len, 1);
03503 
03504       /* let iksemel deal with the string length,
03505          and reset our buffer */
03506       ret = iks_parse(client->parser, newbuf, 0, 0);
03507       memset(newbuf, 0, sizeof(newbuf));
03508 
03509       switch (ret) {
03510       case IKS_NOMEM:
03511          ast_log(LOG_WARNING, "Parsing failure: Out of memory.\n");
03512          break;
03513       case IKS_BADXML:
03514          ast_log(LOG_WARNING, "Parsing failure: Invalid XML.\n");
03515          break;
03516       case IKS_HOOK:
03517          ast_log(LOG_WARNING, "Parsing failure: Hook returned an error.\n");
03518          break;
03519       }
03520       if (ret != IKS_OK) {
03521          return ret;
03522       }
03523       ast_debug(3, "XML parsing successful\n");
03524    }
03525    return IKS_OK;
03526 }
03527 
03528 /*! \brief XMPP client connection thread */
03529 static void *xmpp_client_thread(void *data)
03530 {
03531    struct ast_xmpp_client *client = data;
03532    int res = IKS_NET_RWERR;
03533 
03534    do {
03535       if (client->state == XMPP_STATE_DISCONNECTING) {
03536          break;
03537       }
03538 
03539       if (res == IKS_NET_RWERR || client->timeout == 0) {
03540          ast_debug(3, "Connecting client '%s'\n", client->name);
03541          if ((res = xmpp_client_reconnect(client)) != IKS_OK) {
03542             sleep(4);
03543             res = IKS_NET_RWERR;
03544          }
03545          continue;
03546       }
03547 
03548       res = xmpp_client_receive(client, 1);
03549 
03550       /* Decrease timeout if no data received, and delete
03551        * old messages globally */
03552       if (res == IKS_NET_EXPIRED) {
03553          client->timeout--;
03554       }
03555 
03556       if (res == IKS_HOOK) {
03557          ast_debug(2, "JABBER: Got hook event.\n");
03558       } else if (res == IKS_NET_TLSFAIL) {
03559          ast_log(LOG_ERROR, "JABBER:  Failure in TLS.\n");
03560       } else if (!client->timeout && client->state == XMPP_STATE_CONNECTED) {
03561          RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03562          RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03563 
03564          if (cfg && cfg->clients && (clientcfg = xmpp_config_find(cfg->clients, client->name))) {
03565             res = ast_test_flag(&clientcfg->flags, XMPP_KEEPALIVE) ? xmpp_client_send_raw_message(client, " ") : IKS_OK;
03566          } else {
03567             res = IKS_OK;
03568          }
03569 
03570          if (res == IKS_OK) {
03571             client->timeout = 50;
03572          } else {
03573             ast_log(LOG_WARNING, "JABBER: Network Timeout\n");
03574          }
03575       } else if (res == IKS_NET_RWERR) {
03576          ast_log(LOG_WARNING, "JABBER: socket read error\n");
03577       }
03578 
03579    } while (1);
03580 
03581    return NULL;
03582 }
03583 
03584 static int xmpp_client_config_merge_buddies(void *obj, void *arg, int flags)
03585 {
03586    struct ast_xmpp_buddy *buddy1 = obj, *buddy2;
03587    struct ao2_container *buddies = arg;
03588 
03589    /* If the buddy does not already exist link it into the client buddies container */
03590    if (!(buddy2 = ao2_find(buddies, buddy1->id, OBJ_KEY))) {
03591       ao2_link(buddies, buddy1);
03592    } else {
03593       ao2_ref(buddy2, -1);
03594    }
03595 
03596    /* All buddies are unlinked from the configuration buddies container, always */
03597    return 1;
03598 }
03599 
03600 static int xmpp_client_config_post_apply(void *obj, void *arg, int flags)
03601 {
03602    struct ast_xmpp_client_config *cfg = obj;
03603 
03604    /* Merge buddies as need be */
03605    ao2_callback(cfg->buddies, OBJ_MULTIPLE | OBJ_UNLINK, xmpp_client_config_merge_buddies, cfg->client->buddies);
03606 
03607    if (cfg->client->reconnect) {
03608       /* Disconnect the existing session since our role is changing, or we are starting up */
03609       ast_xmpp_client_disconnect(cfg->client);
03610 
03611       if (!(cfg->client->parser = iks_stream_new(ast_test_flag(&cfg->flags, XMPP_COMPONENT) ? "jabber:component:accept" : "jabber:client", cfg->client,
03612                         xmpp_action_hook))) {
03613          ast_log(LOG_ERROR, "Iksemel stream could not be created for client '%s' - client not active\n", cfg->name);
03614          return -1;
03615       }
03616 
03617       iks_set_log_hook(cfg->client->parser, xmpp_log_hook);
03618 
03619       /* Create a JID based on the given user, if no resource is given use the default */
03620       if (!strchr(cfg->user, '/') && !ast_test_flag(&cfg->flags, XMPP_COMPONENT)) {
03621          char resource[strlen(cfg->user) + strlen("/asterisk-xmpp") + 1];
03622 
03623          snprintf(resource, sizeof(resource), "%s/asterisk-xmpp", cfg->user);
03624          cfg->client->jid = iks_id_new(cfg->client->stack, resource);
03625       } else {
03626          cfg->client->jid = iks_id_new(cfg->client->stack, cfg->user);
03627       }
03628 
03629       if (!cfg->client->jid) {
03630          ast_log(LOG_ERROR, "Jabber identity could not be created for client '%s' - client not active\n", cfg->name);
03631          return -1;
03632       }
03633 
03634       ast_pthread_create_background(&cfg->client->thread, NULL, xmpp_client_thread, cfg->client);
03635 
03636       cfg->client->reconnect = 0;
03637    } else if (cfg->client->state == XMPP_STATE_CONNECTED) {
03638       /* If this client is connected update their presence status since it may have changed */
03639       xmpp_client_set_presence(cfg->client, NULL, cfg->client->jid->full, cfg->status, cfg->statusmsg);
03640 
03641       /* Subscribe to the status of any newly added buddies */
03642       if (ast_test_flag(&cfg->flags, XMPP_AUTOREGISTER)) {
03643          ao2_callback(cfg->client->buddies, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_subscribe_user, cfg->client);
03644       }
03645    }
03646 
03647    return 0;
03648 }
03649 
03650 /*!
03651  * \internal
03652  * \brief  Send a Jabber Message via call from the Manager
03653  * \param s mansession Manager session
03654  * \param m message Message to send
03655  * \return  0
03656  */
03657 static int manager_jabber_send(struct mansession *s, const struct message *m)
03658 {
03659    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03660    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03661    const char *id = astman_get_header(m, "ActionID");
03662    const char *jabber = astman_get_header(m, "Jabber");
03663    const char *screenname = astman_get_header(m, "ScreenName");
03664    const char *message = astman_get_header(m, "Message");
03665 
03666    if (ast_strlen_zero(jabber)) {
03667       astman_send_error(s, m, "No transport specified");
03668       return 0;
03669    }
03670    if (ast_strlen_zero(screenname)) {
03671       astman_send_error(s, m, "No ScreenName specified");
03672       return 0;
03673    }
03674    if (ast_strlen_zero(message)) {
03675       astman_send_error(s, m, "No Message specified");
03676       return 0;
03677    }
03678 
03679    astman_send_ack(s, m, "Attempting to send Jabber Message");
03680 
03681    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, jabber))) {
03682       astman_send_error(s, m, "Could not find Sender");
03683       return 0;
03684    }
03685 
03686    if (strchr(screenname, '@') && !ast_xmpp_client_send_message(clientcfg->client, screenname, message)) {
03687       astman_append(s, "Response: Success\r\n");
03688    } else {
03689       astman_append(s, "Response: Error\r\n");
03690    }
03691 
03692    if (!ast_strlen_zero(id)) {
03693       astman_append(s, "ActionID: %s\r\n", id);
03694    }
03695 
03696    astman_append(s, "\r\n");
03697 
03698    return 0;
03699 }
03700 
03701 /*!
03702  * \brief Build the a node request
03703  * \param client the configured XMPP client we use to connect to a XMPP server
03704  * \param collection name of the collection for request
03705  * \return iks*
03706  */
03707 static iks* xmpp_pubsub_build_node_request(struct ast_xmpp_client *client, const char *collection)
03708 {
03709    iks *request = xmpp_pubsub_iq_create(client, "get"), *query;
03710 
03711    if (!request) {
03712       return NULL;
03713    }
03714 
03715    query = iks_insert(request, "query");
03716    iks_insert_attrib(query, "xmlns", "http://jabber.org/protocol/disco#items");
03717 
03718    if (collection) {
03719       iks_insert_attrib(query, "node", collection);
03720    }
03721 
03722    return request;
03723 }
03724 
03725 /*!
03726  * \brief Receive pubsub item lists
03727  * \param data pointer to ast_xmpp_client structure
03728  * \param pak response from pubsub diso#items query
03729  * \return IKS_FILTER_EAT
03730  */
03731 static int xmpp_pubsub_receive_node_list(void *data, ikspak* pak)
03732 {
03733    struct ast_xmpp_client *client = data;
03734    iks *item = NULL;
03735 
03736    if (iks_has_children(pak->query)) {
03737       item = iks_first_tag(pak->query);
03738       ast_verbose("Connection %s: %s\nNode name: %s\n", client->name, client->jid->partial,
03739              iks_find_attrib(item, "node"));
03740       while ((item = iks_next_tag(item))) {
03741          ast_verbose("Node name: %s\n", iks_find_attrib(item, "node"));
03742       }
03743    }
03744 
03745    if (item) {
03746       iks_delete(item);
03747    }
03748 
03749 
03750    return IKS_FILTER_EAT;
03751 }
03752 
03753 /*!
03754 * \brief Request item list from pubsub
03755 * \param client the configured XMPP client we use to connect to a XMPP server
03756 * \param collection name of the collection for request
03757 * \return void
03758 */
03759 static void xmpp_pubsub_request_nodes(struct ast_xmpp_client *client, const char *collection)
03760 {
03761    iks *request = xmpp_pubsub_build_node_request(client, collection);
03762 
03763    if (!request) {
03764       ast_log(LOG_ERROR, "Could not request pubsub nodes on client '%s' - IQ could not be created\n", client->name);
03765       return;
03766    }
03767 
03768    iks_filter_add_rule(client->filter, xmpp_pubsub_receive_node_list, client, IKS_RULE_TYPE,
03769              IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
03770              IKS_RULE_DONE);
03771    ast_xmpp_client_send(client, request);
03772    iks_delete(request);
03773 
03774 }
03775 
03776 /*
03777  * \brief Method to expose PubSub node list via CLI.
03778  * \param e pointer to ast_cli_entry structure
03779  * \param cmd
03780  * \param a pointer to ast_cli_args structure
03781  * \return char *
03782  */
03783 static char *xmpp_cli_list_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
03784                ast_cli_args *a)
03785 {
03786    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03787    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03788    const char *name = NULL, *collection = NULL;
03789 
03790    switch (cmd) {
03791    case CLI_INIT:
03792       e->command = "xmpp list nodes";
03793       e->usage =
03794          "Usage: xmpp list nodes <connection> [collection]\n"
03795          "       Lists the user's nodes on the respective connection\n"
03796          "       ([connection] as configured in xmpp.conf.)\n";
03797       return NULL;
03798    case CLI_GENERATE:
03799       return NULL;
03800    }
03801 
03802    if (a->argc > 5 || a->argc < 4) {
03803       return CLI_SHOWUSAGE;
03804    } else if (a->argc == 4 || a->argc == 5) {
03805       name = a->argv[3];
03806    }
03807 
03808    if (a->argc == 5) {
03809       collection = a->argv[4];
03810    }
03811 
03812    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
03813       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03814       return CLI_FAILURE;
03815    }
03816 
03817    ast_cli(a->fd, "Listing pubsub nodes.\n");
03818 
03819    xmpp_pubsub_request_nodes(clientcfg->client, collection);
03820 
03821    return CLI_SUCCESS;
03822 }
03823 
03824 /*!
03825  * \brief Delete pubsub item lists
03826  * \param data pointer to ast_xmpp_client structure
03827  * \param pak response from pubsub diso#items query
03828  * \return IKS_FILTER_EAT
03829  */
03830 static int xmpp_pubsub_delete_node_list(void *data, ikspak* pak)
03831 {
03832    struct ast_xmpp_client *client = data;
03833    iks *item = NULL;
03834 
03835    if (iks_has_children(pak->query)) {
03836       item = iks_first_tag(pak->query);
03837       ast_log(LOG_WARNING, "Connection: %s  Node name: %s\n", client->jid->partial,
03838          iks_find_attrib(item, "node"));
03839       while ((item = iks_next_tag(item))) {
03840          xmpp_pubsub_delete_node(client, iks_find_attrib(item, "node"));
03841       }
03842    }
03843 
03844    if (item) {
03845       iks_delete(item);
03846    }
03847 
03848    return IKS_FILTER_EAT;
03849 }
03850 
03851 static void xmpp_pubsub_purge_nodes(struct ast_xmpp_client *client, const char* collection_name)
03852 {
03853    iks *request = xmpp_pubsub_build_node_request(client, collection_name);
03854    ast_xmpp_client_send(client, request);
03855    iks_filter_add_rule(client->filter, xmpp_pubsub_delete_node_list, client, IKS_RULE_TYPE,
03856              IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, client->mid,
03857              IKS_RULE_DONE);
03858    ast_xmpp_client_send(client, request);
03859    iks_delete(request);
03860 }
03861 
03862 /*!
03863  * \brief Method to purge PubSub nodes via CLI.
03864  * \param e pointer to ast_cli_entry structure
03865  * \param cmd
03866  * \param a pointer to ast_cli_args structure
03867  * \return char *
03868  */
03869 static char *xmpp_cli_purge_pubsub_nodes(struct ast_cli_entry *e, int cmd, struct
03870                 ast_cli_args *a)
03871 {
03872    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03873    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03874    const char *name;
03875 
03876    switch (cmd) {
03877    case CLI_INIT:
03878       e->command = "xmpp purge nodes";
03879       e->usage =
03880          "Usage: xmpp purge nodes <connection> <node>\n"
03881          "       Purges nodes on PubSub server\n"
03882          "       as configured in xmpp.conf.\n";
03883          return NULL;
03884    case CLI_GENERATE:
03885       return NULL;
03886    }
03887 
03888    if (a->argc != 5) {
03889       return CLI_SHOWUSAGE;
03890    }
03891    name = a->argv[3];
03892 
03893    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
03894       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03895       return CLI_FAILURE;
03896    }
03897 
03898    if (ast_test_flag(&cfg->global->pubsub, XMPP_XEP0248)) {
03899       xmpp_pubsub_purge_nodes(clientcfg->client, a->argv[4]);
03900    } else {
03901       xmpp_pubsub_delete_node(clientcfg->client, a->argv[4]);
03902    }
03903 
03904    return CLI_SUCCESS;
03905 }
03906 
03907 /*!
03908  * \brief Method to expose PubSub node deletion via CLI.
03909  * \param e pointer to ast_cli_entry structure
03910  * \param cmd
03911  * \param a pointer to ast_cli_args structure
03912  * \return char *
03913  */
03914 static char *xmpp_cli_delete_pubsub_node(struct ast_cli_entry *e, int cmd, struct
03915                ast_cli_args *a)
03916 {
03917    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03918    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03919    const char *name;
03920 
03921    switch (cmd) {
03922    case CLI_INIT:
03923       e->command = "xmpp delete node";
03924       e->usage =
03925          "Usage: xmpp delete node <connection> <node>\n"
03926          "       Deletes a node on PubSub server\n"
03927          "       as configured in xmpp.conf.\n";
03928       return NULL;
03929    case CLI_GENERATE:
03930       return NULL;
03931    }
03932 
03933    if (a->argc != 5) {
03934       return CLI_SHOWUSAGE;
03935    }
03936    name = a->argv[3];
03937 
03938    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
03939       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03940       return CLI_FAILURE;
03941    }
03942 
03943    xmpp_pubsub_delete_node(clientcfg->client, a->argv[4]);
03944 
03945    return CLI_SUCCESS;
03946 }
03947 
03948 /*!
03949  * \brief Method to expose PubSub collection node creation via CLI.
03950  * \return char *.
03951  */
03952 static char *xmpp_cli_create_collection(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03953 {
03954    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03955    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03956    const char *name, *collection_name;
03957 
03958    switch (cmd) {
03959    case CLI_INIT:
03960       e->command = "xmpp create collection";
03961       e->usage =
03962          "Usage: xmpp create collection <connection> <collection>\n"
03963          "       Creates a PubSub collection node using the account\n"
03964          "       as configured in xmpp.conf.\n";
03965       return NULL;
03966    case CLI_GENERATE:
03967       return NULL;
03968    }
03969 
03970    if (a->argc != 5) {
03971       return CLI_SHOWUSAGE;
03972    }
03973    name = a->argv[3];
03974    collection_name = a->argv[4];
03975 
03976    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
03977       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
03978       return CLI_FAILURE;
03979    }
03980 
03981    ast_cli(a->fd, "Creating test PubSub node collection.\n");
03982 
03983    xmpp_pubsub_create_collection(clientcfg->client, collection_name);
03984 
03985    return CLI_SUCCESS;
03986 }
03987 
03988 /*!
03989  * \brief Method to expose PubSub leaf node creation via CLI.
03990  * \return char *.
03991  */
03992 static char *xmpp_cli_create_leafnode(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
03993 {
03994    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
03995    RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
03996    const char *name, *collection_name, *leaf_name;
03997 
03998    switch (cmd) {
03999    case CLI_INIT:
04000       e->command = "xmpp create leaf";
04001       e->usage =
04002          "Usage: xmpp create leaf <connection> <collection> <leaf>\n"
04003          "       Creates a PubSub leaf node using the account\n"
04004          "       as configured in xmpp.conf.\n";
04005       return NULL;
04006    case CLI_GENERATE:
04007       return NULL;
04008    }
04009 
04010    if (a->argc != 6) {
04011       return CLI_SHOWUSAGE;
04012    }
04013    name = a->argv[3];
04014    collection_name = a->argv[4];
04015    leaf_name = a->argv[5];
04016 
04017    if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
04018       ast_cli(a->fd, "Unable to find client '%s'!\n", name);
04019       return CLI_FAILURE;
04020    }
04021 
04022    ast_cli(a->fd, "Creating test PubSub node collection.\n");
04023 
04024    xmpp_pubsub_create_leaf(clientcfg->client, collection_name, leaf_name);
04025 
04026    return CLI_SUCCESS;
04027 }
04028 
04029 /*!
04030  * \internal
04031  * \brief Turn on/off console debugging.
04032  * \return CLI_SUCCESS.
04033  */
04034 static char *xmpp_do_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04035 {
04036    switch (cmd) {
04037    case CLI_INIT:
04038       e->command = "xmpp set debug {on|off}";
04039       e->usage =
04040          "Usage: xmpp set debug {on|off}\n"
04041          "       Enables/disables dumping of XMPP/Jabber packets for debugging purposes.\n";
04042       return NULL;
04043    case CLI_GENERATE:
04044       return NULL;
04045    }
04046 
04047    if (a->argc != e->args) {
04048       return CLI_SHOWUSAGE;
04049    }
04050 
04051    if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
04052       debug = 1;
04053       ast_cli(a->fd, "XMPP Debugging Enabled.\n");
04054       return CLI_SUCCESS;
04055    } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
04056       debug = 0;
04057       ast_cli(a->fd, "XMPP Debugging Disabled.\n");
04058       return CLI_SUCCESS;
04059    }
04060    return CLI_SHOWUSAGE; /* defaults to invalid */
04061 }
04062 
04063 /*!
04064  * \internal
04065  * \brief Show client status.
04066  * \return CLI_SUCCESS.
04067  */
04068 static char *xmpp_show_clients(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04069 {
04070    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
04071    struct ao2_iterator i;
04072    struct ast_xmpp_client_config *clientcfg;
04073 
04074    switch (cmd) {
04075    case CLI_INIT:
04076       e->command = "xmpp show connections";
04077       e->usage =
04078          "Usage: xmpp show connections\n"
04079          "       Shows state of client and component connections\n";
04080       return NULL;
04081    case CLI_GENERATE:
04082       return NULL;
04083    }
04084 
04085    if (!cfg || !cfg->clients) {
04086       return NULL;
04087    }
04088 
04089    ast_cli(a->fd, "Jabber Users and their status:\n");
04090 
04091    i = ao2_iterator_init(cfg->clients, 0);
04092    while ((clientcfg = ao2_iterator_next(&i))) {
04093       char *state;
04094 
04095       switch (clientcfg->client->state) {
04096       case XMPP_STATE_DISCONNECTING:
04097          state = "Disconnecting";
04098          break;
04099       case XMPP_STATE_DISCONNECTED:
04100          state = "Disconnected";
04101          break;
04102       case XMPP_STATE_CONNECTING:
04103          state = "Connecting";
04104          break;
04105       case XMPP_STATE_REQUEST_TLS:
04106          state = "Waiting to request TLS";
04107          break;
04108       case XMPP_STATE_REQUESTED_TLS:
04109          state = "Requested TLS";
04110          break;
04111       case XMPP_STATE_AUTHENTICATE:
04112          state = "Waiting to authenticate";
04113          break;
04114       case XMPP_STATE_AUTHENTICATING:
04115          state = "Authenticating";
04116          break;
04117       case XMPP_STATE_ROSTER:
04118          state = "Retrieving roster";
04119          break;
04120       case XMPP_STATE_CONNECTED:
04121          state = "Connected";
04122          break;
04123       default:
04124          state = "Unknown";
04125       }
04126 
04127       ast_cli(a->fd, "       [%s] %s     - %s\n", clientcfg->name, clientcfg->user, state);
04128 
04129       ao2_ref(clientcfg, -1);
04130    }
04131    ao2_iterator_destroy(&i);
04132 
04133    ast_cli(a->fd, "----\n");
04134    ast_cli(a->fd, "   Number of clients: %d\n", ao2_container_count(cfg->clients));
04135 
04136    return CLI_SUCCESS;
04137 }
04138 
04139 /*!
04140  * \internal
04141  * \brief Show buddy lists
04142  * \return CLI_SUCCESS.
04143  */
04144 static char *xmpp_show_buddies(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
04145 {
04146    RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
04147    struct ao2_iterator i;
04148    struct ast_xmpp_client_config *clientcfg;
04149 
04150    switch (cmd) {
04151    case CLI_INIT:
04152       e->command = "xmpp show buddies";
04153       e->usage =
04154          "Usage: xmpp show buddies\n"
04155          "       Shows buddy lists of our clients\n";
04156       return NULL;
04157    case CLI_GENERATE:
04158       return NULL;
04159    }
04160 
04161    if (!cfg || !cfg->clients) {
04162       return NULL;
04163    }
04164 
04165    ast_cli(a->fd, "XMPP buddy lists\n");
04166 
04167    i = ao2_iterator_init(cfg->clients, 0);
04168    while ((clientcfg = ao2_iterator_next(&i))) {
04169       struct ao2_iterator bud;
04170       struct ast_xmpp_buddy *buddy;
04171 
04172       ast_cli(a->fd, "Client: %s\n", clientcfg->name);
04173 
04174       bud = ao2_iterator_init(clientcfg->client->buddies, 0);
04175       while ((buddy = ao2_iterator_next(&bud))) {
04176          struct ao2_iterator res;
04177          struct ast_xmpp_resource *resource;
04178 
04179          ast_cli(a->fd, "\tBuddy:\t%s\n", buddy->id);
04180 
04181          res = ao2_iterator_init(buddy->resources, 0);
04182          while ((resource = ao2_iterator_next(&res))) {
04183             ast_cli(a->fd, "\t\tResource: %s\n", resource->resource);
04184             ast_cli(a->fd, "\t\t\tnode: %s\n", resource->caps.node);
04185             ast_cli(a->fd, "\t\t\tversion: %s\n", resource->caps.version);
04186             ast_cli(a->fd, "\t\t\tGoogle Talk capable: %s\n", resource->caps.google ? "yes" : "no");
04187             ast_cli(a->fd, "\t\t\tJingle capable: %s\n", resource->caps.jingle ? "yes" : "no");
04188 
04189             ao2_ref(resource, -1);
04190          }
04191          ao2_iterator_destroy(&res);
04192 
04193          ao2_ref(buddy, -1);
04194       }
04195       ao2_iterator_destroy(&bud);
04196 
04197       ao2_ref(clientcfg, -1);
04198    }
04199    ao2_iterator_destroy(&i);
04200 
04201    return CLI_SUCCESS;
04202 }
04203 
04204 static struct ast_cli_entry xmpp_cli[] = {
04205    AST_CLI_DEFINE(xmpp_do_set_debug, "Enable/Disable Jabber debug"),
04206    AST_CLI_DEFINE(xmpp_show_clients, "Show state of clients and components"),
04207    AST_CLI_DEFINE(xmpp_show_buddies, "Show buddy lists of our clients"),
04208    AST_CLI_DEFINE(xmpp_cli_create_collection, "Creates a PubSub node collection."),
04209    AST_CLI_DEFINE(xmpp_cli_list_pubsub_nodes, "Lists PubSub nodes"),
04210    AST_CLI_DEFINE(xmpp_cli_create_leafnode, "Creates a PubSub leaf node"),
04211    AST_CLI_DEFINE(xmpp_cli_delete_pubsub_node, "Deletes a PubSub node"),
04212    AST_CLI_DEFINE(xmpp_cli_purge_pubsub_nodes, "Purges PubSub nodes"),
04213 };
04214 
04215 static int unload_module(void)
04216 {
04217    ast_msg_tech_unregister(&msg_tech);
04218    ast_cli_unregister_multiple(xmpp_cli, ARRAY_LEN(xmpp_cli));
04219    ast_unregister_application(app_ajisend);
04220    ast_unregister_application(app_ajisendgroup);
04221    ast_unregister_application(app_ajistatus);
04222    ast_unregister_application(app_ajijoin);
04223    ast_unregister_application(app_ajileave);
04224    ast_manager_unregister("JabberSend");
04225    ast_custom_function_unregister(&jabberstatus_function);
04226    ast_custom_function_unregister(&jabberreceive_function);
04227    aco_info_destroy(&cfg_info);
04228    ao2_global_obj_release(globals);
04229 
04230    ast_cond_destroy(&message_received_condition);
04231    ast_mutex_destroy(&messagelock);
04232 
04233    return 0;
04234 }
04235 
04236 static int global_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
04237 {
04238    struct ast_xmpp_global_config *global = obj;
04239 
04240    if (!strcasecmp(var->name, "debug")) {
04241       debug = ast_true(var->value);
04242    } else if (!strcasecmp(var->name, "autoprune")) {
04243       ast_set2_flag(&global->general, ast_true(var->value), XMPP_AUTOPRUNE);
04244    } else if (!strcasecmp(var->name, "autoregister")) {
04245       ast_set2_flag(&global->general, ast_true(var->value), XMPP_AUTOREGISTER);
04246    } else if (!strcasecmp(var->name, "auth_policy")) {
04247       ast_set2_flag(&global->general, !strcasecmp(var->value, "accept") ? 1 : 0, XMPP_AUTOACCEPT);
04248    } else if (!strcasecmp(var->name, "collection_nodes")) {
04249       ast_set2_flag(&global->pubsub, ast_true(var->value), XMPP_XEP0248);
04250    } else if (!strcasecmp(var->name, "pubsub_autocreate")) {
04251       ast_set2_flag(&global->pubsub, ast_true(var->value), XMPP_PUBSUB_AUTOCREATE);
04252    } else {
04253       return -1;
04254    }
04255 
04256    return 0;
04257 }
04258 
04259 static int client_bitfield_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
04260 {
04261    struct ast_xmpp_client_config *cfg = obj;
04262 
04263    if (!strcasecmp(var->name, "debug")) {
04264       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_DEBUG);
04265    } else if (!strcasecmp(var->name, "type")) {
04266       ast_set2_flag(&cfg->flags, !strcasecmp(var->value, "component") ? 1 : 0, XMPP_COMPONENT);
04267    } else if (!strcasecmp(var->name, "distribute_events")) {
04268       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_DISTRIBUTE_EVENTS);
04269    } else if (!strcasecmp(var->name, "usetls")) {
04270       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_USETLS);
04271    } else if (!strcasecmp(var->name, "usesasl")) {
04272       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_USESASL);
04273    } else if (!strcasecmp(var->name, "forceoldssl")) {
04274       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_FORCESSL);
04275    } else if (!strcasecmp(var->name, "keepalive")) {
04276       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_KEEPALIVE);
04277    } else if (!strcasecmp(var->name, "autoprune")) {
04278       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_AUTOPRUNE);
04279    } else if (!strcasecmp(var->name, "autoregister")) {
04280       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_AUTOREGISTER);
04281    } else if (!strcasecmp(var->name, "auth_policy")) {
04282       ast_set2_flag(&cfg->flags, !strcasecmp(var->value, "accept") ? 1 : 0, XMPP_AUTOACCEPT);
04283    } else if (!strcasecmp(var->name, "sendtodialplan")) {
04284       ast_set2_flag(&cfg->flags, ast_true(var->value), XMPP_SEND_TO_DIALPLAN);
04285    } else {
04286       return -1;
04287    }
04288 
04289    return 0;
04290 }
04291 
04292 static int client_status_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
04293 {
04294    struct ast_xmpp_client_config *cfg = obj;
04295 
04296    if (!strcasecmp(var->value, "unavailable")) {
04297       cfg->status = IKS_SHOW_UNAVAILABLE;
04298    } else if (!strcasecmp(var->value, "available") || !strcasecmp(var->value, "online")) {
04299       cfg->status = IKS_SHOW_AVAILABLE;
04300    } else if (!strcasecmp(var->value, "chat") || !strcasecmp(var->value, "chatty")) {
04301       cfg->status = IKS_SHOW_CHAT;
04302    } else if (!strcasecmp(var->value, "away")) {
04303       cfg->status = IKS_SHOW_AWAY;
04304    } else if (!strcasecmp(var->value, "xa") || !strcasecmp(var->value, "xaway")) {
04305       cfg->status = IKS_SHOW_XA;
04306    } else if (!strcasecmp(var->value, "dnd")) {
04307       cfg->status = IKS_SHOW_DND;
04308    } else if (!strcasecmp(var->value, "invisible")) {
04309 #ifdef IKS_SHOW_INVISIBLE
04310       cfg->status = IKS_SHOW_INVISIBLE;
04311 #else
04312       cfg->status = IKS_SHOW_DND;
04313 #endif
04314    } else {
04315       return -1;
04316    }
04317 
04318    return 0;
04319 }
04320 
04321 static int client_buddy_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
04322 {
04323    struct ast_xmpp_client_config *cfg = obj;
04324    struct ast_xmpp_buddy *buddy;
04325 
04326    if ((buddy = ao2_find(cfg->buddies, var->value, OBJ_KEY))) {
04327       ao2_ref(buddy, -1);
04328       return -1;
04329    }
04330 
04331    if (!(buddy = xmpp_client_create_buddy(cfg->buddies, var->value))) {
04332       return -1;
04333    }
04334 
04335    ao2_ref(buddy, -1);
04336 
04337    return 0;
04338 }
04339 
04340 /*!
04341  * \brief Load the module
04342  *
04343  * Module loading including tests for configuration or dependencies.
04344  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
04345  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
04346  * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
04347  * configuration file or other non-critical problem return 
04348  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
04349  */
04350 static int load_module(void)
04351 {
04352    if (aco_info_init(&cfg_info)) {
04353       return AST_MODULE_LOAD_DECLINE;
04354    }
04355 
04356    aco_option_register_custom(&cfg_info, "debug", ACO_EXACT, global_options, "no", global_bitfield_handler, 0);
04357    aco_option_register_custom(&cfg_info, "autoprune", ACO_EXACT, global_options, "no", global_bitfield_handler, 0);
04358    aco_option_register_custom(&cfg_info, "autoregister", ACO_EXACT, global_options, "yes", global_bitfield_handler, 0);
04359    aco_option_register_custom(&cfg_info, "collection_nodes", ACO_EXACT, global_options, "no", global_bitfield_handler, 0);
04360    aco_option_register_custom(&cfg_info, "pubsub_autocreate", ACO_EXACT, global_options, "no", global_bitfield_handler, 0);
04361    aco_option_register_custom(&cfg_info, "auth_policy", ACO_EXACT, global_options, "accept", global_bitfield_handler, 0);
04362 
04363    aco_option_register(&cfg_info, "username", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, user));
04364    aco_option_register(&cfg_info, "secret", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, password));
04365    aco_option_register(&cfg_info, "serverhost", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, server));
04366    aco_option_register(&cfg_info, "statusmessage", ACO_EXACT, client_options, "Online and Available", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, statusmsg));
04367    aco_option_register(&cfg_info, "pubsub_node", ACO_EXACT, client_options, NULL, OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, pubsubnode));
04368    aco_option_register(&cfg_info, "context", ACO_EXACT, client_options, "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_xmpp_client_config, context));
04369    aco_option_register(&cfg_info, "priority", ACO_EXACT, client_options, "1", OPT_UINT_T, 0, FLDSET(struct ast_xmpp_client_config, priority));
04370    aco_option_register(&cfg_info, "port", ACO_EXACT, client_options, "5222", OPT_UINT_T, 0, FLDSET(struct ast_xmpp_client_config, port));
04371    aco_option_register(&cfg_info, "timeout", ACO_EXACT, client_options, "5", OPT_UINT_T, 0, FLDSET(struct ast_xmpp_client_config, message_timeout));
04372 
04373    aco_option_register_custom(&cfg_info, "debug", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
04374    aco_option_register_custom(&cfg_info, "type", ACO_EXACT, client_options, "client", client_bitfield_handler, 0);
04375    aco_option_register_custom(&cfg_info, "distribute_events", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
04376    aco_option_register_custom(&cfg_info, "usetls", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
04377    aco_option_register_custom(&cfg_info, "usesasl", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
04378    aco_option_register_custom(&cfg_info, "forceoldssl", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
04379    aco_option_register_custom(&cfg_info, "keepalive", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
04380    aco_option_register_custom(&cfg_info, "autoprune", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
04381    aco_option_register_custom(&cfg_info, "autoregister", ACO_EXACT, client_options, "yes", client_bitfield_handler, 0);
04382    aco_option_register_custom(&cfg_info, "auth_policy", ACO_EXACT, client_options, "accept", client_bitfield_handler, 0);
04383    aco_option_register_custom(&cfg_info, "sendtodialplan", ACO_EXACT, client_options, "no", client_bitfield_handler, 0);
04384    aco_option_register_custom(&cfg_info, "status", ACO_EXACT, client_options, "available", client_status_handler, 0);
04385    aco_option_register_custom(&cfg_info, "buddy", ACO_EXACT, client_options, NULL, client_buddy_handler, 0);
04386 
04387    if (aco_process_config(&cfg_info, 0) == ACO_PROCESS_ERROR) {
04388       aco_info_destroy(&cfg_info);
04389       return AST_MODULE_LOAD_DECLINE;
04390    }
04391 
04392    ast_manager_register_xml("JabberSend", EVENT_FLAG_SYSTEM, manager_jabber_send);
04393 
04394    ast_register_application_xml(app_ajisend, xmpp_send_exec);
04395    ast_register_application_xml(app_ajisendgroup, xmpp_sendgroup_exec);
04396    ast_register_application_xml(app_ajistatus, xmpp_status_exec);
04397    ast_register_application_xml(app_ajijoin, xmpp_join_exec);
04398    ast_register_application_xml(app_ajileave, xmpp_leave_exec);
04399 
04400    ast_cli_register_multiple(xmpp_cli, ARRAY_LEN(xmpp_cli));
04401    ast_custom_function_register(&jabberstatus_function);
04402    ast_custom_function_register(&jabberreceive_function);
04403    ast_msg_tech_register(&msg_tech);
04404 
04405    ast_mutex_init(&messagelock);
04406    ast_cond_init(&message_received_condition, NULL);
04407 
04408    return AST_MODULE_LOAD_SUCCESS;
04409 }
04410 
04411 static int reload(void)
04412 {
04413    if (aco_process_config(&cfg_info, 1) == ACO_PROCESS_ERROR) {
04414       return AST_MODULE_LOAD_DECLINE;
04415    }
04416 
04417    return 0;
04418 }
04419 
04420 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk XMPP Interface",
04421       .load = load_module,
04422       .unload = unload_module,
04423       .reload = reload,
04424       .load_pri = AST_MODPRI_CHANNEL_DEPEND,
04425           );

Generated on Thu Oct 11 06:33:55 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6