[svn-commits] file: trunk r369527 - in /trunk: configs/ include/asterisk/ res/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Jul 2 09:06:26 CDT 2012


Author: file
Date: Mon Jul  2 09:06:19 2012
New Revision: 369527

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=369527
Log:
Add a cleaned up drop-in replacement for res_jabber called res_xmpp. This provides the same externally facing functionality but is implemented differently internally.

This is currently not built by default but this will be changed once chan_jingle2 (insert actual name in your head when reading this after it has been merged)
is in the tree.

Review: https://reviewboard.asterisk.org/r/1983/

Added:
    trunk/configs/xmpp.conf.sample
      - copied unchanged from r369516, trunk/configs/jabber.conf.sample
    trunk/include/asterisk/xmpp.h   (with props)
    trunk/res/res_xmpp.c   (with props)
Modified:
    trunk/configs/cli_aliases.conf.sample

Modified: trunk/configs/cli_aliases.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/cli_aliases.conf.sample?view=diff&rev=369527&r1=369526&r2=369527
==============================================================================
--- trunk/configs/cli_aliases.conf.sample (original)
+++ trunk/configs/cli_aliases.conf.sample Mon Jul  2 09:06:19 2012
@@ -186,3 +186,16 @@
 
 [asterisk14](asterisk14_tpl)
 ; add any additional custom commands you want below here.
+
+[asterisk11_tpl](!)
+jabber list nodes=xmpp list nodes
+jabber purge nodes=xmpp purge nodes
+jabber delete node=xmpp delete node
+jabber create collection=xmpp create collection
+jabber create leaf=xmpp create leaf
+jabber set debug=xmpp set debug
+jabber show connections=xmpp show connections
+jabber show buddies=xmpp show buddies
+
+[asterisk11](asterisk11_tpl)
+; add any additional custom commands you want below here.

Added: trunk/include/asterisk/xmpp.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/xmpp.h?view=auto&rev=369527
==============================================================================
--- trunk/include/asterisk/xmpp.h (added)
+++ trunk/include/asterisk/xmpp.h Mon Jul  2 09:06:19 2012
@@ -1,0 +1,262 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2012, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief XMPP Interface
+ * \author Joshua Colp <jcolp at digium.com>
+ * \extref IKSEMEL http://iksemel.jabberstudio.org
+ */
+
+#ifndef _ASTERISK_XMPP_H
+#define _ASTERISK_XMPP_H
+
+#ifdef HAVE_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#define TRY_SECURE 2
+#define SECURE 4
+
+#endif /* HAVE_OPENSSL */
+
+/* file is read by blocks with this size */
+#define NET_IO_BUF_SIZE 4096
+
+/* Return value for timeout connection expiration */
+#define IKS_NET_EXPIRED 12
+
+#include <iksemel.h>
+
+#include "asterisk/utils.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/pbx.h"
+
+/*
+ * As per RFC 3920 - section 3.1, the maximum length for a full Jabber ID
+ * is 3071 bytes.
+ * The ABNF syntax for jid :
+ * jid = [node "@" ] domain [ "/" resource ]
+ * Each allowable portion of a JID (node identifier, domain identifier,
+ * and resource identifier) MUST NOT be more than 1023 bytes in length,
+ * resulting in a maximum total size (including the '@' and '/' separators)
+ * of 3071 bytes.
+ */
+#define XMPP_MAX_JIDLEN 3071
+
+/*! \brief Maximum size of a resource JID */
+#define XMPP_MAX_RESJIDLEN 1023
+
+/*! \brief Maximum size of an attribute */
+#define XMPP_MAX_ATTRLEN   256
+
+/*! \brief Client connection states */
+enum xmpp_state {
+        XMPP_STATE_DISCONNECTING,   /*!< Client is disconnecting */
+        XMPP_STATE_DISCONNECTED,    /*!< Client is disconnected */
+        XMPP_STATE_CONNECTING,      /*!< Client is connecting */
+        XMPP_STATE_REQUEST_TLS,     /*!< Client should request TLS */
+        XMPP_STATE_REQUESTED_TLS,   /*!< Client has requested TLS */
+        XMPP_STATE_AUTHENTICATE,    /*!< Client needs to authenticate */
+        XMPP_STATE_AUTHENTICATING,  /*!< Client is authenticating */
+        XMPP_STATE_ROSTER,          /*!< Client is currently getting the roster */
+        XMPP_STATE_CONNECTED,       /*!< Client is fully connected */
+};
+
+/*! \brief Resource capabilities */
+struct ast_xmpp_capabilities {
+        char node[200];        /*!< Node string from the capabilities stanza in presence notification */
+        char version[50];      /*!< Version string from the capabilities stanza in presence notification */
+        unsigned int jingle:1; /*!< Set if the resource supports Jingle */
+        unsigned int google:1; /*!< Set if the resource supports Google Talk */
+};
+
+/*! \brief XMPP Resource */
+struct ast_xmpp_resource {
+        char resource[XMPP_MAX_RESJIDLEN]; /*!< JID of the resource */
+        int status;                        /*!< Current status of the resource */
+        char *description;                 /*!< Description of the resource */
+        int priority;                      /*!< Priority, used for deciding what resource to use */
+        struct ast_xmpp_capabilities caps; /*!< Capabilities of the resource */
+};
+
+/*! \brief XMPP Message */
+struct ast_xmpp_message {
+        char *from;                            /*!< Who the message is from */
+        char *message;                         /*!< Message contents */
+        char id[25];                           /*!< Identifier for the message */
+        struct timeval arrived;                /*!< When the message arrived */
+        AST_LIST_ENTRY(ast_xmpp_message) list; /*!< Linked list information */
+};
+
+/*! \brief XMPP Buddy */
+struct ast_xmpp_buddy {
+        char id[XMPP_MAX_JIDLEN];        /*!< JID of the buddy */
+        struct ao2_container *resources; /*!< Resources for the buddy */
+        unsigned int subscribe:1;        /*!< Need to subscribe to get their status */
+};
+
+/*! \brief XMPP Client Connection */
+struct ast_xmpp_client {
+        AST_DECLARE_STRING_FIELDS(
+                AST_STRING_FIELD(name); /*!< Name of the client configuration */
+                );
+        char mid[6]; /* Message ID */
+        iksid *jid;
+        iksparser *parser;
+        iksfilter *filter;
+        ikstack *stack;
+#ifdef HAVE_OPENSSL
+        SSL_CTX *ssl_context;
+        SSL *ssl_session;
+        const SSL_METHOD *ssl_method;
+        unsigned int stream_flags;
+#endif /* HAVE_OPENSSL */
+        enum xmpp_state state;
+        struct ao2_container *buddies;
+        AST_LIST_HEAD(, ast_xmpp_message) messages;
+        pthread_t thread;
+	int timeout;
+	unsigned int reconnect:1; /*!< Reconnect this client */
+};
+
+/*!
+ * \brief Find an XMPP client connection using a given name
+ *
+ * \param name Name of the client connection
+ *
+ * \retval non-NULL on success
+ * \retval NULL on failure
+ *
+ * \note This will return the client connection with the reference count incremented by one.
+ */
+struct ast_xmpp_client *ast_xmpp_client_find(const char *name);
+
+/*!
+ * \brief Disconnect an XMPP client connection
+ *
+ * \param client Pointer to the client
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_client_disconnect(struct ast_xmpp_client *client);
+
+/*!
+ * \brief Release XMPP client connection reference
+ *
+ * \param client Pointer to the client
+ */
+void ast_xmpp_client_unref(struct ast_xmpp_client *client);
+
+/*!
+ * \brief Lock an XMPP client connection
+ *
+ * \param client Pointer to the client
+ */
+void ast_xmpp_client_lock(struct ast_xmpp_client *client);
+
+/*!
+ * \brief Unlock an XMPP client connection
+ *
+ * \param client Pointer to the client
+ */
+void ast_xmpp_client_unlock(struct ast_xmpp_client *client);
+
+/*!
+ * \brief Send an XML stanza out using an established XMPP client connection
+ *
+ * \param client Pointer to the client
+ * \param stanza Pointer to the Iksemel stanza
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_client_send(struct ast_xmpp_client *client, iks *stanza);
+
+/*!
+ * \brief Send a message to a given user using an established XMPP client connection
+ *
+ * \param client Pointer to the client
+ * \param user User the message should be sent to
+ * \param message The message to send
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message);
+
+/*!
+ * \brief Invite a user to an XMPP multi-user chatroom
+ *
+ * \param client Pointer to the client
+ * \param user JID of the user
+ * \param room Name of the chatroom
+ * \param message Message to send with the invitation
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message);
+
+/*!
+ * \brief Join an XMPP multi-user chatroom
+ *
+ * \param client Pointer to the client
+ * \param room Name of the chatroom
+ * \param nickname Nickname to use
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname);
+
+/*!
+ * \brief Send a message to an XMPP multi-user chatroom
+ *
+ * \param client Pointer to the client
+ * \param nickname Nickname to use
+ * \param Address Address of the room
+ * \param message Message itself
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message);
+
+/*!
+ * \brief Leave an XMPP multi-user chatroom
+ *
+ * \param client Pointer to the client
+ * \param room Name of the chatroom
+ * \param nickname Nickname being used
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname);
+
+/*!
+ * \brief Helper function which increments the message identifier
+ *
+ * \param mid Pointer to a string containing the message identifier
+ */
+void ast_xmpp_increment_mid(char *mid);
+
+#endif

Propchange: trunk/include/asterisk/xmpp.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: trunk/include/asterisk/xmpp.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: trunk/include/asterisk/xmpp.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: trunk/res/res_xmpp.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_xmpp.c?view=auto&rev=369527
==============================================================================
--- trunk/res/res_xmpp.c (added)
+++ trunk/res/res_xmpp.c Mon Jul  2 09:06:19 2012
@@ -1,0 +1,4320 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2012, Digium, Inc.
+ *
+ * Joshua Colp <jcolp at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief XMPP client and component module.
+ *
+ * \author Joshua Colp <jcolp at digium.com>
+ *
+ * \extref Iksemel http://code.google.com/p/iksemel/
+ *
+ * A refereouce module for interfacting Asterisk directly as a client or component with
+ * an XMPP/Jabber compliant server.
+ *
+ * This module is based upon the original res_jabber as done by Matt O'Gorman.
+ *
+ */
+
+/*** MODULEINFO
+	<defaultenabled>no</defaultenabled>
+	<depend>iksemel</depend>
+	<use type="external">openssl</use>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <ctype.h>
+#include <iksemel.h>
+
+#include "asterisk/xmpp.h"
+#include "asterisk/module.h"
+#include "asterisk/manager.h"
+#include "asterisk/app.h"
+#include "asterisk/message.h"
+#include "asterisk/manager.h"
+#include "asterisk/event.h"
+#include "asterisk/cli.h"
+#include "asterisk/config_options.h"
+
+/*** DOCUMENTATION
+	<application name="JabberSend" language="en_US">
+		<synopsis>
+			Sends an XMPP message to a buddy.
+		</synopsis>
+		<syntax>
+			<parameter name="account" required="true">
+				<para>The local named account to listen on (specified in
+				xmpp.conf)</para>
+			</parameter>
+			<parameter name="jid" required="true">
+				<para>Jabber ID of the buddy to send the message to. It can be a
+				bare JID (username at domain) or a full JID (username at domain/resource).</para>
+			</parameter>
+			<parameter name="message" required="true">
+				<para>The message to send.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Sends the content of <replaceable>message</replaceable> as text message
+			from the given <replaceable>account</replaceable> to the buddy identified by
+			<replaceable>jid</replaceable></para>
+			<para>Example: JabberSend(asterisk,bob at domain.com,Hello world) sends "Hello world"
+			to <replaceable>bob at domain.com</replaceable> as an XMPP message from the account
+			<replaceable>asterisk</replaceable>, configured in xmpp.conf.</para>
+		</description>
+		<see-also>
+			<ref type="function">JABBER_STATUS</ref>
+			<ref type="function">JABBER_RECEIVE</ref>
+		</see-also>
+	</application>
+	<function name="JABBER_RECEIVE" language="en_US">
+		<synopsis>
+			Reads XMPP messages.
+		</synopsis>
+		<syntax>
+			<parameter name="account" required="true">
+				<para>The local named account to listen on (specified in
+				xmpp.conf)</para>
+			</parameter>
+			<parameter name="jid" required="true">
+				<para>Jabber ID of the buddy to receive message from. It can be a
+				bare JID (username at domain) or a full JID (username at domain/resource).</para>
+			</parameter>
+			<parameter name="timeout">
+				<para>In seconds, defaults to <literal>20</literal>.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Receives a text message on the given <replaceable>account</replaceable>
+			from the buddy identified by <replaceable>jid</replaceable> and returns the contents.</para>
+			<para>Example: ${JABBER_RECEIVE(asterisk,bob at domain.com)} returns an XMPP message
+			sent from <replaceable>bob at domain.com</replaceable> (or nothing in case of a time out), to
+			the <replaceable>asterisk</replaceable> XMPP account configured in xmpp.conf.</para>
+		</description>
+		<see-also>
+			<ref type="function">JABBER_STATUS</ref>
+			<ref type="application">JabberSend</ref>
+		</see-also>
+	</function>
+	<function name="JABBER_STATUS" language="en_US">
+		<synopsis>
+			Retrieves a buddy's status.
+		</synopsis>
+		<syntax>
+			<parameter name="account" required="true">
+				<para>The local named account to listen on (specified in
+				xmpp.conf)</para>
+			</parameter>
+			<parameter name="jid" required="true">
+				<para>Jabber ID of the buddy to receive message from. It can be a
+				bare JID (username at domain) or a full JID (username at domain/resource).</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Retrieves the numeric status associated with the buddy identified
+			by <replaceable>jid</replaceable>.
+			If the buddy does not exist in the buddylist, returns 7.</para>
+			<para>Status will be 1-7.</para>
+			<para>1=Online, 2=Chatty, 3=Away, 4=XAway, 5=DND, 6=Offline</para>
+			<para>If not in roster variable will be set to 7.</para>
+			<para>Example: ${JABBER_STATUS(asterisk,bob at domain.com)} returns 1 if
+			<replaceable>bob at domain.com</replaceable> is online. <replaceable>asterisk</replaceable> is
+			the associated XMPP account configured in xmpp.conf.</para>
+		</description>
+		<see-also>
+			<ref type="function">JABBER_RECEIVE</ref>
+			<ref type="application">JabberSend</ref>
+		</see-also>
+	</function>
+	<application name="JabberSendGroup" language="en_US">
+		<synopsis>
+			Send a Jabber Message to a specified chat room
+		</synopsis>
+		<syntax>
+			<parameter name="Jabber" required="true">
+				<para>Client or transport Asterisk uses to connect to Jabber.</para>
+			</parameter>
+			<parameter name="RoomJID" required="true">
+				<para>XMPP/Jabber JID (Name) of chat room.</para>
+			</parameter>
+			<parameter name="Message" required="true">
+				<para>Message to be sent to the chat room.</para>
+			</parameter>
+			<parameter name="Nickname" required="false">
+				<para>The nickname Asterisk uses in the chat room.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Allows user to send a message to a chat room via XMPP.</para>
+			<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>
+		</description>
+	</application>
+	<application name="JabberJoin" language="en_US">
+		<synopsis>
+			Join a chat room
+		</synopsis>
+		<syntax>
+			<parameter name="Jabber" required="true">
+				<para>Client or transport Asterisk uses to connect to Jabber.</para>
+			</parameter>
+			<parameter name="RoomJID" required="true">
+				<para>XMPP/Jabber JID (Name) of chat room.</para>
+			</parameter>
+			<parameter name="Nickname" required="false">
+				<para>The nickname Asterisk will use in the chat room.</para>
+				<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>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Allows Asterisk to join a chat room.</para>
+		</description>
+	</application>
+	<application name="JabberLeave" language="en_US">
+		<synopsis>
+			Leave a chat room
+		</synopsis>
+		<syntax>
+			<parameter name="Jabber" required="true">
+				<para>Client or transport Asterisk uses to connect to Jabber.</para>
+			</parameter>
+			<parameter name="RoomJID" required="true">
+				<para>XMPP/Jabber JID (Name) of chat room.</para>
+			</parameter>
+			<parameter name="Nickname" required="false">
+				<para>The nickname Asterisk uses in the chat room.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Allows Asterisk to leave a chat room.</para>
+		</description>
+	</application>
+	<application name="JabberStatus" language="en_US">
+		<synopsis>
+			Retrieve the status of a jabber list member
+		</synopsis>
+		<syntax>
+			<parameter name="Jabber" required="true">
+				<para>Client or transport Asterisk users to connect to Jabber.</para>
+			</parameter>
+			<parameter name="JID" required="true">
+				<para>XMPP/Jabber JID (Name) of recipient.</para>
+			</parameter>
+			<parameter name="Variable" required="true">
+				<para>Variable to store the status of requested user.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This application is deprecated. Please use the JABBER_STATUS() function instead.</para>
+			<para>Retrieves the numeric status associated with the specified buddy <replaceable>JID</replaceable>.
+			The return value in the <replaceable>Variable</replaceable>will be one of the following.</para>
+			<enumlist>
+				<enum name="1">
+					<para>Online.</para>
+				</enum>
+				<enum name="2">
+					<para>Chatty.</para>
+				</enum>
+				<enum name="3">
+					<para>Away.</para>
+				</enum>
+				<enum name="4">
+					<para>Extended Away.</para>
+				</enum>
+				<enum name="5">
+					<para>Do Not Disturb.</para>
+				</enum>
+				<enum name="6">
+					<para>Offline.</para>
+				</enum>
+				<enum name="7">
+					<para>Not In Roster.</para>
+				</enum>
+			</enumlist>
+		</description>
+	</application>
+	<manager name="JabberSend" language="en_US">
+		<synopsis>
+			Sends a message to a Jabber Client.
+		</synopsis>
+		<syntax>
+			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+			<parameter name="Jabber" required="true">
+				<para>Client or transport Asterisk uses to connect to JABBER.</para>
+			</parameter>
+			<parameter name="JID" required="true">
+				<para>XMPP/Jabber JID (Name) of recipient.</para>
+			</parameter>
+			<parameter name="Message" required="true">
+				<para>Message to be sent to the buddy.</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>Sends a message to a Jabber Client.</para>
+		</description>
+	</manager>
+***/
+
+/*! \brief Supported general configuration flags */
+enum {
+	XMPP_AUTOPRUNE = (1 << 0),
+	XMPP_AUTOREGISTER = (1 << 1),
+	XMPP_AUTOACCEPT = (1 << 2),
+	XMPP_DEBUG = (1 << 3),
+	XMPP_USETLS = (1 << 4),
+	XMPP_USESASL = (1 << 5),
+	XMPP_FORCESSL = (1 << 6),
+	XMPP_KEEPALIVE = (1 << 7),
+	XMPP_COMPONENT = (1 << 8),
+	XMPP_SEND_TO_DIALPLAN = (1 << 9),
+	XMPP_DISTRIBUTE_EVENTS = (1 << 10),
+};
+
+/*! \brief Supported pubsub configuration flags */
+enum {
+	XMPP_XEP0248 = (1 << 0),
+	XMPP_PUBSUB = (1 << 1),
+	XMPP_PUBSUB_AUTOCREATE = (1 << 2),
+};
+
+/*! \brief Number of buckets for client connections */
+#define CLIENT_BUCKETS 53
+
+/*! \brief Number of buckets for buddies (per client) */
+#define BUDDY_BUCKETS 53
+
+/*! \brief Number of buckets for resources (per buddy) */
+#define RESOURCE_BUCKETS 53
+
+/*! \brief Namespace for TLS support */
+#define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
+
+/*! \brief Status for a disappearing buddy */
+#define STATUS_DISAPPEAR 6
+
+/*! \brief Global debug status */
+static int debug;
+
+/*! \brief XMPP Global Configuration */
+struct ast_xmpp_global_config {
+	struct ast_flags general; /*!< General configuration options */
+	struct ast_flags pubsub;  /*!< Pubsub related configuration options */
+};
+
+/*! \brief XMPP Client Configuration */
+struct ast_xmpp_client_config {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);        /*!< Name of the client connection */
+		AST_STRING_FIELD(user);        /*!< Username to use for authentication */
+		AST_STRING_FIELD(password);    /*!< Password to use for authentication */
+		AST_STRING_FIELD(server);      /*!< Server hostname */
+		AST_STRING_FIELD(statusmsg);   /*!< Status message for presence */
+		AST_STRING_FIELD(pubsubnode);  /*!< Pubsub node */
+		AST_STRING_FIELD(context);     /*!< Context for incoming messages */
+		);
+	int port;                       /*!< Port to use when connecting to server */
+	int message_timeout;            /*!< Timeout for messages */
+	int priority;                   /*!< Resource priority */
+	struct ast_flags flags;         /*!< Various options that have been set */
+	enum ikshowtype status;         /*!< Presence status */
+	struct ast_xmpp_client *client; /*!< Pointer to the client */
+	struct ao2_container *buddies;  /*!< Configured buddies */
+};
+
+struct xmpp_config {
+	struct ast_xmpp_global_config *global; /*!< Global configuration options */
+	struct ao2_container *clients;         /*!< Configured clients */
+};
+
+static AO2_GLOBAL_OBJ_STATIC(globals);
+
+static int xmpp_client_request_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+static int xmpp_client_requested_tls(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+static int xmpp_client_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+static int xmpp_client_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+
+static int xmpp_component_authenticate(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+static int xmpp_component_authenticating(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+
+/*! \brief Defined handlers for XMPP client states */
+static const struct xmpp_state_handler {
+	int state;
+	int component;
+	int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, int type, iks *node);
+} xmpp_state_handlers[] = {
+	{ XMPP_STATE_REQUEST_TLS, 0, xmpp_client_request_tls, },
+	{ XMPP_STATE_REQUESTED_TLS, 0, xmpp_client_requested_tls, },
+	{ XMPP_STATE_AUTHENTICATE, 0, xmpp_client_authenticate, },
+	{ XMPP_STATE_AUTHENTICATING, 0, xmpp_client_authenticating, },
+	{ XMPP_STATE_AUTHENTICATE, 1, xmpp_component_authenticate, },
+	{ XMPP_STATE_AUTHENTICATING, 1, xmpp_component_authenticating, },
+};
+
+static int xmpp_pak_message(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
+static int xmpp_pak_presence(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
+static int xmpp_pak_s10n(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
+
+/*! \brief Defined handlers for different PAK types */
+static const struct xmpp_pak_handler {
+	int type;
+	int (*handler)(struct ast_xmpp_client *client, struct ast_xmpp_client_config *cfg, iks *node, ikspak *pak);
+} xmpp_pak_handlers[] = {
+	{ IKS_PAK_MESSAGE, xmpp_pak_message, },
+	{ IKS_PAK_PRESENCE, xmpp_pak_presence, },
+	{ IKS_PAK_S10N, xmpp_pak_s10n, },
+};
+
+static const char *app_ajisend = "JabberSend";
+static const char *app_ajisendgroup = "JabberSendGroup";
+static const char *app_ajistatus = "JabberStatus";
+static const char *app_ajijoin = "JabberJoin";
+static const char *app_ajileave = "JabberLeave";
+
+static struct ast_event_sub *mwi_sub = NULL;
+static struct ast_event_sub *device_state_sub = NULL;
+
+static ast_cond_t message_received_condition;
+static ast_mutex_t messagelock;
+
+static int xmpp_client_config_post_apply(void *obj, void *arg, int flags);
+
+/*! \brief Destructor function for configuration */
+static void ast_xmpp_client_config_destructor(void *obj)
+{
+	struct ast_xmpp_client_config *cfg = obj;
+	ast_string_field_free_memory(cfg);
+	ao2_cleanup(cfg->client);
+	ao2_cleanup(cfg->buddies);
+}
+
+/*! \brief Destroy function for XMPP messages */
+static void xmpp_message_destroy(struct ast_xmpp_message *message)
+{
+	if (message->from) {
+		ast_free(message->from);
+	}
+	if (message->message) {
+		ast_free(message->message);
+	}
+
+	ast_free(message);
+}
+
+/*! \brief Destructor callback function for XMPP client */
+static void xmpp_client_destructor(void *obj)
+{
+	struct ast_xmpp_client *client = obj;
+	struct ast_xmpp_message *message;
+
+	ast_xmpp_client_disconnect(client);
+
+	if (client->stack) {
+		iks_stack_delete(client->stack);
+	}
+
+	ao2_cleanup(client->buddies);
+
+	while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
+		xmpp_message_destroy(message);
+	}
+	AST_LIST_HEAD_DESTROY(&client->messages);
+}
+
+/*! \brief Hashing function for XMPP buddy */
+static int xmpp_buddy_hash(const void *obj, const int flags)
+{
+	const struct ast_xmpp_buddy *buddy = obj;
+	const char *id = obj;
+
+	return ast_str_hash(flags & OBJ_KEY ? id : buddy->id);
+}
+
+/*! \brief Comparator function for XMPP buddy */
+static int xmpp_buddy_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_xmpp_buddy *buddy1 = obj, *buddy2 = arg;
+	const char *id = arg;
+
+	return !strcmp(buddy1->id, flags & OBJ_KEY ? id : buddy2->id) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+/*! \brief Allocator function for ast_xmpp_client */
+static struct ast_xmpp_client *xmpp_client_alloc(const char *name)
+{
+	struct ast_xmpp_client *client;
+
+	if (!(client = ao2_alloc(sizeof(*client), xmpp_client_destructor))) {
+		return NULL;
+	}
+
+	AST_LIST_HEAD_INIT(&client->messages);
+	client->thread = AST_PTHREADT_NULL;
+
+	if (!(client->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
+		ast_log(LOG_ERROR, "Could not initialize buddy container for '%s'\n", name);
+		ao2_ref(client, -1);
+		return NULL;
+	}
+
+	if (ast_string_field_init(client, 512)) {
+		ast_log(LOG_ERROR, "Could not initialize stringfields for '%s'\n", name);
+		ao2_ref(client, -1);
+		return NULL;
+	}
+
+	if (!(client->stack = iks_stack_new(8192, 8192))) {
+		ast_log(LOG_ERROR, "Could not create an Iksemel stack for '%s'\n", name);
+		ao2_ref(client, -1);
+		return NULL;
+	}
+
+	ast_string_field_set(client, name, name);
+
+	client->timeout = 50;
+	client->state = XMPP_STATE_DISCONNECTED;
+	ast_copy_string(client->mid, "aaaaa", sizeof(client->mid));
+
+	return client;
+}
+
+/*! \brief Find function for configuration */
+static void *xmpp_config_find(struct ao2_container *tmp_container, const char *category)
+{
+	return ao2_find(tmp_container, category, OBJ_KEY);
+}
+
+/*! \brief Look up existing client or create a new one */
+static void *xmpp_client_find_or_create(const char *category)
+{
+	RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+	RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
+
+	if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, category))) {
+		return xmpp_client_alloc(category);
+	}
+
+	ao2_ref(clientcfg->client, +1);
+	return clientcfg->client;
+}
+
+/*! \brief Allocator function for configuration */
+static void *ast_xmpp_client_config_alloc(const char *cat)
+{
+	struct ast_xmpp_client_config *cfg;
+
+	if (!(cfg = ao2_alloc(sizeof(*cfg), ast_xmpp_client_config_destructor))) {
+		return NULL;
+	}
+
+	if (ast_string_field_init(cfg, 512)) {
+		ao2_ref(cfg, -1);
+		return NULL;
+	}
+
+	if (!(cfg->client = xmpp_client_find_or_create(cat))) {
+		ao2_ref(cfg, -1);
+		return NULL;
+	}
+
+	if (!(cfg->buddies = ao2_container_alloc(BUDDY_BUCKETS, xmpp_buddy_hash, xmpp_buddy_cmp))) {
+		ao2_ref(cfg, -1);
+		return NULL;
+	}
+
+	ast_string_field_set(cfg, name, cat);
+
+	return cfg;
+}
+
+/*! \brief Destructor for XMPP configuration */
+static void xmpp_config_destructor(void *obj)
+{
+	struct xmpp_config *cfg = obj;
+	ao2_cleanup(cfg->global);
+	ao2_cleanup(cfg->clients);
+}
+
+/*! \brief Hashing function for configuration */
+static int xmpp_config_hash(const void *obj, const int flags)
+{
+	const struct ast_xmpp_client_config *cfg = obj;
+	const char *name = (flags & OBJ_KEY) ? obj : cfg->name;
+	return ast_str_case_hash(name);
+}
+
+/*! \brief Comparator function for configuration */
+static int xmpp_config_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_xmpp_client_config *one = obj, *two = arg;
+	const char *match = (flags & OBJ_KEY) ? arg : two->name;
+	return strcasecmp(one->name, match) ? 0 : (CMP_MATCH | CMP_STOP);
+}
+
+/*! \brief Allocator for XMPP configuration */
+static void *xmpp_config_alloc(void)
+{
+	struct xmpp_config *cfg;
+
+	if (!(cfg = ao2_alloc(sizeof(*cfg), xmpp_config_destructor))) {
+		return NULL;
+	}
+
+	if (!(cfg->global = ao2_alloc(sizeof(*cfg->global), NULL))) {
+		goto error;
+	}
+
+	ast_set_flag(&cfg->global->general, XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE);
+
+	if (!(cfg->clients = ao2_container_alloc(1, xmpp_config_hash, xmpp_config_cmp))) {
+		goto error;
+	}
+
+	return cfg;
+error:
+	ao2_ref(cfg, -1);
+	return NULL;
+}
+
+static int xmpp_config_prelink(void *newitem)
+{
+	struct ast_xmpp_client_config *clientcfg = newitem;
+	RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+	RAII_VAR(struct ast_xmpp_client_config *, oldclientcfg, NULL, ao2_cleanup);
+
+	if (ast_strlen_zero(clientcfg->user)) {
+		ast_log(LOG_ERROR, "No user specified on client '%s'\n", clientcfg->name);
+		return -1;
+	} else if (ast_strlen_zero(clientcfg->password)) {
+		ast_log(LOG_ERROR, "No password specified on client '%s'\n", clientcfg->name);
+		return -1;
+	} else if (ast_strlen_zero(clientcfg->server)) {
+		ast_log(LOG_ERROR, "No server specified on client '%s'\n", clientcfg->name);
+		return -1;
+	}
+
+	/* If this is a new connection force a reconnect */
+	if (!cfg || !cfg->clients || !(oldclientcfg = xmpp_config_find(cfg->clients, clientcfg->name))) {
+		clientcfg->client->reconnect = 1;
+		return 0;
+	}
+
+	/* If any configuration options are changing that would require reconnecting set the bit so we will do so if possible */
+	if (strcmp(clientcfg->user, oldclientcfg->user) ||
+	    strcmp(clientcfg->password, oldclientcfg->password) ||
+	    strcmp(clientcfg->server, oldclientcfg->server) ||
+	    (clientcfg->port != oldclientcfg->port) ||
+	    (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT) != ast_test_flag(&oldclientcfg->flags, XMPP_COMPONENT)) ||
+	    (clientcfg->priority != oldclientcfg->priority)) {
+		clientcfg->client->reconnect = 1;
+	} else {
+		clientcfg->client->reconnect = 0;
+	}
+
+	return 0;
+}
+
+static void xmpp_config_post_apply(void)
+{
+	RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+
+	ao2_callback(cfg->clients, OBJ_NODATA | OBJ_MULTIPLE, xmpp_client_config_post_apply, NULL);
+}
+
+static struct aco_type global_option = {
+	.type = ACO_GLOBAL,
+	.item_offset = offsetof(struct xmpp_config, global),
+	.category_match = ACO_WHITELIST,
+	.category = "^general$",
+};
+
+struct aco_type *global_options[] = ACO_TYPES(&global_option);
+
+static struct aco_type client_option = {
+	.type = ACO_ITEM,
+	.category_match = ACO_BLACKLIST,
+	.category = "^(general)$",
+	.item_alloc = ast_xmpp_client_config_alloc,
+	.item_find = xmpp_config_find,
+	.item_prelink = xmpp_config_prelink,
+	.item_offset = offsetof(struct xmpp_config, clients),
+};
+
+struct aco_type *client_options[] = ACO_TYPES(&client_option);
+
+struct aco_file res_xmpp_conf = {
+	.filename = "xmpp.conf",
+	.alias = "jabber.conf",
+	.types = ACO_TYPES(&global_option, &client_option),
+};
+
+CONFIG_INFO_STANDARD(cfg_info, globals, xmpp_config_alloc,
+		     .files = ACO_FILES(&res_xmpp_conf),
+		     .post_apply_config = xmpp_config_post_apply,
+	);
+
+/*! \brief Destructor callback function for XMPP resource */
+static void xmpp_resource_destructor(void *obj)
+{
+	struct ast_xmpp_resource *resource = obj;
+
+	if (resource->description) {
+		ast_free(resource->description);
+	}
+}
+
+/*! \brief Hashing function for XMPP resource */
+static int xmpp_resource_hash(const void *obj, const int flags)
+{
+	const struct ast_xmpp_resource *resource = obj;
+
+	return flags & OBJ_KEY ? -1 : resource->priority;
+}
+
+/*! \brief Comparator function for XMPP resource */
+static int xmpp_resource_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_xmpp_resource *resource1 = obj, *resource2 = arg;
+	const char *resource = arg;
+
+	return !strcmp(resource1->resource, flags & OBJ_KEY ? resource : resource2->resource) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+/*! \brief Destructor callback function for XMPP buddy */
+static void xmpp_buddy_destructor(void *obj)
+{
+	struct ast_xmpp_buddy *buddy = obj;
+
+	if (buddy->resources) {
+		ao2_ref(buddy->resources, -1);
+	}
+}
+
+/*! \brief Helper function which returns whether an XMPP client connection is secure or not */
+static int xmpp_is_secure(struct ast_xmpp_client *client)
+{
+#ifdef HAVE_OPENSSL
+	return client->stream_flags & SECURE;
+#else
+	return 0;
+#endif
+}
+
+struct ast_xmpp_client *ast_xmpp_client_find(const char *name)
+{
+	RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+	RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
+
+	if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, name))) {
+		return NULL;
+	}
+
+	ao2_ref(clientcfg->client, +1);
+	return clientcfg->client;
+}
+
+void ast_xmpp_client_unref(struct ast_xmpp_client *client)
+{
+	ao2_ref(client, -1);
+}
+
+void ast_xmpp_client_lock(struct ast_xmpp_client *client)
+{
+	ao2_lock(client);
+}
+
+void ast_xmpp_client_unlock(struct ast_xmpp_client *client)
+{
+	ao2_unlock(client);
+}
+
+/*! \brief Internal function used to send a message to a user or chatroom */
+static int xmpp_client_send_message(struct ast_xmpp_client *client, int group, const char *nick, const char *address, const char *message)
+{
+	RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+	RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
+	int res = 0;
+	char from[XMPP_MAX_JIDLEN];
+	iks *message_packet;
+
+	if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
+	    !(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
+		return -1;
+	}
+
+	if (!ast_strlen_zero(nick) && ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
+		snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
+	} else {
+		snprintf(from, sizeof(from), "%s", client->jid->full);
+	}
+
+	iks_insert_attrib(message_packet, "from", from);
+
+	res = ast_xmpp_client_send(client, message_packet);
+
+	iks_delete(message_packet);
+
+	return res;
+}
+
+int ast_xmpp_client_send_message(struct ast_xmpp_client *client, const char *user, const char *message)
+{
+	return xmpp_client_send_message(client, 0, NULL, user, message);
+}
+
+int ast_xmpp_chatroom_invite(struct ast_xmpp_client *client, const char *user, const char *room, const char *message)
+{
+	int res = 0;
+	iks *invite, *body = NULL, *namespace = NULL;
+
+	if (!(invite = iks_new("message")) || !(body = iks_new("body")) || !(namespace = iks_new("x"))) {
+		res = -1;
+		goto done;
+	}
+
+	iks_insert_attrib(invite, "to", user);
+	ast_xmpp_client_lock(client);
+	iks_insert_attrib(invite, "id", client->mid);
+	ast_xmpp_increment_mid(client->mid);
+	ast_xmpp_client_unlock(client);
+	iks_insert_cdata(body, message, 0);
+	iks_insert_node(invite, body);
+	iks_insert_attrib(namespace, "xmlns", "jabber:x:conference");
+	iks_insert_attrib(namespace, "jid", room);
+	iks_insert_node(invite, namespace);
+
+	res = ast_xmpp_client_send(client, invite);
+
+done:
+	iks_delete(namespace);
+	iks_delete(body);
+	iks_delete(invite);
+
+	return res;
+}
+
+static int xmpp_client_set_group_presence(struct ast_xmpp_client *client, const char *room, int level, const char *nick)
+{
+	RAII_VAR(struct xmpp_config *, cfg, ao2_global_obj_ref(globals), ao2_cleanup);
+	RAII_VAR(struct ast_xmpp_client_config *, clientcfg, NULL, ao2_cleanup);
+	int res = 0;
+	iks *presence = NULL, *x = NULL;
+	char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
+
+	if (!cfg || !cfg->clients || !(clientcfg = xmpp_config_find(cfg->clients, client->name)) ||
+	    !(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
+		res = -1;
+		goto done;
+	}
+
+	if (ast_test_flag(&clientcfg->flags, XMPP_COMPONENT)) {
+		snprintf(from, sizeof(from), "%s@%s/%s", nick, client->jid->full, nick);
+		snprintf(roomid, sizeof(roomid), "%s/%s", room, nick);
+	} else {
+		snprintf(from, sizeof(from), "%s", client->jid->full);
+		snprintf(roomid, sizeof(roomid), "%s/%s", room, S_OR(nick, client->jid->user));
+	}
+
+	iks_insert_attrib(presence, "to", roomid);
+	iks_insert_attrib(presence, "from", from);
+	iks_insert_attrib(x, "xmlns", "http://jabber.org/protocol/muc");
+	iks_insert_node(presence, x);
+
+	res = ast_xmpp_client_send(client, presence);
+
+done:
+	iks_delete(x);
+	iks_delete(presence);
+
+	return res;
+}
+
+int ast_xmpp_chatroom_join(struct ast_xmpp_client *client, const char *room, const char *nickname)
+{
+	return xmpp_client_set_group_presence(client, room, IKS_SHOW_AVAILABLE, nickname);
+}
+
+int ast_xmpp_chatroom_send(struct ast_xmpp_client *client, const char *nickname, const char *address, const char *message)
+{
+	return xmpp_client_send_message(client, 1, nickname, address, message);
+}
+
+int ast_xmpp_chatroom_leave(struct ast_xmpp_client *client, const char *room, const char *nickname)
+{

[... 3473 lines stripped ...]



More information about the svn-commits mailing list