res_xmpp.c

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

Generated on Thu Apr 16 06:27:53 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6