Sat Apr 26 2014 22:01:41

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