[asterisk-commits] file: branch file/res_xmpp r367836 - in /team/file/res_xmpp: include/asterisk...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue May 29 07:31:43 CDT 2012


Author: file
Date: Tue May 29 07:31:38 2012
New Revision: 367836

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=367836
Log:
Add res_xmpp module.

Added:
    team/file/res_xmpp/include/asterisk/xmpp.h   (with props)
    team/file/res_xmpp/res/res_xmpp.c   (with props)

Added: team/file/res_xmpp/include/asterisk/xmpp.h
URL: http://svnview.digium.com/svn/asterisk/team/file/res_xmpp/include/asterisk/xmpp.h?view=auto&rev=367836
==============================================================================
--- team/file/res_xmpp/include/asterisk/xmpp.h (added)
+++ team/file/res_xmpp/include/asterisk/xmpp.h Tue May 29 07:31:38 2012
@@ -1,0 +1,259 @@
+/*
+ * 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 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 */
+		);
+	char mid[6]; /* Message ID */
+	char context[AST_MAX_CONTEXT]; /*!< Context for incoming messages */
+	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;
+	int port;
+	int timeout;
+	int message_timeout;
+	unsigned int authorized:1;
+	struct ast_flags flags;
+	struct ao2_container *buddies;
+	AST_LIST_HEAD(, ast_xmpp_message) messages;
+	pthread_t thread;
+	int priority;
+	enum ikshowtype status;
+};
+
+/*!
+ * \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 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: team/file/res_xmpp/include/asterisk/xmpp.h
------------------------------------------------------------------------------
    svn:eol-style = native

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

Propchange: team/file/res_xmpp/include/asterisk/xmpp.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/file/res_xmpp/res/res_xmpp.c
URL: http://svnview.digium.com/svn/asterisk/team/file/res_xmpp/res/res_xmpp.c?view=auto&rev=367836
==============================================================================
--- team/file/res_xmpp/res/res_xmpp.c (added)
+++ team/file/res_xmpp/res/res_xmpp.c Tue May 29 07:31:38 2012
@@ -1,0 +1,4006 @@
+/*
+ * 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
+	<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"
+
+/*** 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
+				jabber.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 jabber.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
+				jabber.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 jabber.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
+				jabber.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 jabber.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 Name of the original backwards compatible configuration file */
+#define XMPP_OLD_CONFIG "jabber.conf"
+
+/*! \brief Name of the current preferred configuration file */
+#define XMPP_CONFIG "xmpp.conf"
+
+/*! \brief Namespace for TLS support */
+#define XMPP_TLS_NS "urn:ietf:params:xml:ns:xmpp-tls"
+
+/*! \brief Container for configured XMPP client connections */
+static struct ao2_container *clients;
+
+/*! \brief Enabled general configuration flags */
+static struct ast_flags generalflags = { XMPP_AUTOREGISTER | XMPP_AUTOACCEPT | XMPP_USETLS | XMPP_USESASL | XMPP_KEEPALIVE };
+
+/*! \brief Enabled pubsub configuration flags */
+static struct ast_flags pubsubflags = { 0 };
+
+static int xmpp_client_request_tls(struct ast_xmpp_client *client, int type, iks *node);
+static int xmpp_client_requested_tls(struct ast_xmpp_client *client, int type, iks *node);
+static int xmpp_client_authenticate(struct ast_xmpp_client *client, int type, iks *node);
+static int xmpp_client_authenticating(struct ast_xmpp_client *client, int type, iks *node);
+
+static int xmpp_component_authenticate(struct ast_xmpp_client *client, int type, iks *node);
+static int xmpp_component_authenticating(struct ast_xmpp_client *client, 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, 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, iks *node, ikspak *pak);
+static int xmpp_pak_presence(struct ast_xmpp_client *client, iks *node, ikspak *pak);
+static int xmpp_pak_s10n(struct ast_xmpp_client *client, 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, 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;
+
+/*! \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 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 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 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->filter) {
+		iks_filter_delete(client->filter);
+	}
+
+	if (client->parser) {
+		iks_parser_delete(client->parser);
+	}
+
+	if (client->stack) {
+		iks_stack_delete(client->stack);
+	}
+
+	if (client->buddies) {
+		ao2_ref(client->buddies, -1);
+	}
+
+	while ((message = AST_LIST_REMOVE_HEAD(&client->messages, list))) {
+		xmpp_message_destroy(message);
+	}
+	AST_LIST_HEAD_DESTROY(&client->messages);
+}
+
+/*! \brief Hashing function for XMPP client */
+static int xmpp_client_hash(const void *obj, const int flags)
+{
+	const struct ast_xmpp_client *client = obj;
+	const char *name = obj;
+
+	return ast_str_hash(flags & OBJ_KEY ? name : client->name);
+}
+
+/*! \brief Comparator function for XMPP client */
+static int xmpp_client_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_xmpp_client *client1 = obj, *client2 = arg;
+	const char *name = arg;
+
+	return !strcmp(client1->name, flags & OBJ_KEY ? name : client2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+/*! \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)
+{
+	return ao2_find(clients, name, OBJ_KEY);
+}
+
+void ast_xmpp_client_unref(struct ast_xmpp_client *client)
+{
+	ao2_ref(client, -1);
+}
+
+/*! \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)
+{
+	int res = 0;
+	char from[XMPP_MAX_JIDLEN];
+	iks *message_packet;
+
+	if (!(message_packet = iks_make_msg(group ? IKS_TYPE_GROUPCHAT : IKS_TYPE_CHAT, address, message))) {
+		return -1;
+	}
+
+	if (!ast_strlen_zero(nick) && ast_test_flag(&client->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);
+	iks_insert_attrib(invite, "id", client->mid);
+	ast_xmpp_increment_mid(client->mid);
+	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)
+{
+	int res = 0;
+	iks *presence, *x = NULL;
+	char from[XMPP_MAX_JIDLEN], roomid[XMPP_MAX_JIDLEN];
+
+	if (!(presence = iks_make_pres(level, NULL)) || !(x = iks_new("x"))) {
+		res = -1;
+		goto done;
+	}
+
+	if (ast_test_flag(&client->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)
+{
+	return xmpp_client_set_group_presence(client, room, IKS_SHOW_UNAVAILABLE, nickname);
+}
+
+void ast_xmpp_increment_mid(char *mid)
+{
+	int i = 0;
+
+	for (i = strlen(mid) - 1; i >= 0; i--) {
+		if (mid[i] != 'z') {
+			mid[i] = mid[i] + 1;
+			i = 0;
+		} else {
+			mid[i] = 'a';
+		}
+	}
+}
+
+/*!
+ * \brief Create an IQ packet
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param type the type of IQ packet to create
+ * \return iks*
+ */
+static iks* xmpp_pubsub_iq_create(struct ast_xmpp_client *client, const char *type)
+{
+	iks *request;
+
+	if (!(request = iks_new("iq"))) {
+		return NULL;
+	}
+
+
+	iks_insert_attrib(request, "to", client->pubsubnode);
+	iks_insert_attrib(request, "from", client->jid->full);
+	iks_insert_attrib(request, "type", type);
+	ast_xmpp_increment_mid(client->mid);
+	iks_insert_attrib(request, "id", client->mid);
+
+	return request;
+}
+
+/*!
+ * \brief Build the skeleton of a publish
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param node Name of the node that will be published to
+ * \param event_type
+ * \return iks *
+ */
+static iks* xmpp_pubsub_build_publish_skeleton(struct ast_xmpp_client *client, const char *node,
+					       const char *event_type)
+{
+	iks *request, *pubsub, *publish, *item;
+
+	if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
+		return NULL;
+	}
+
+	pubsub = iks_insert(request, "pubsub");
+	iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
+	publish = iks_insert(pubsub, "publish");
+	iks_insert_attrib(publish, "node", ast_test_flag(&pubsubflags, XMPP_XEP0248) ? node : event_type);
+	item = iks_insert(publish, "item");
+	iks_insert_attrib(item, "id", node);
+
+	return item;
+
+}
+
+static iks* xmpp_pubsub_build_node_config(iks *pubsub, const char *node_type, const char *collection_name)
+{
+	iks *configure, *x, *field_owner, *field_node_type, *field_node_config,
+		*field_deliver_payload, *field_persist_items, *field_access_model,
+		*field_pubsub_collection;
+	configure = iks_insert(pubsub, "configure");
+	x = iks_insert(configure, "x");
+	iks_insert_attrib(x, "xmlns", "jabber:x:data");
+	iks_insert_attrib(x, "type", "submit");
+	field_owner = iks_insert(x, "field");
+	iks_insert_attrib(field_owner, "var", "FORM_TYPE");
+	iks_insert_attrib(field_owner, "type", "hidden");
+	iks_insert_cdata(iks_insert(field_owner, "value"),
+			 "http://jabber.org/protocol/pubsub#owner", 39);
+	if (node_type) {
+		field_node_type = iks_insert(x, "field");
+		iks_insert_attrib(field_node_type, "var", "pubsub#node_type");
+		iks_insert_cdata(iks_insert(field_node_type, "value"), node_type, strlen(node_type));
+	}
+	field_node_config = iks_insert(x, "field");
+	iks_insert_attrib(field_node_config, "var", "FORM_TYPE");
+	iks_insert_attrib(field_node_config, "type", "hidden");
+	iks_insert_cdata(iks_insert(field_node_config, "value"),
+			 "http://jabber.org/protocol/pubsub#node_config", 45);
+	field_deliver_payload = iks_insert(x, "field");
+	iks_insert_attrib(field_deliver_payload, "var", "pubsub#deliver_payloads");
+	iks_insert_cdata(iks_insert(field_deliver_payload, "value"), "1", 1);
+	field_persist_items = iks_insert(x, "field");
+	iks_insert_attrib(field_persist_items, "var", "pubsub#persist_items");
+	iks_insert_cdata(iks_insert(field_persist_items, "value"), "1", 1);
+	field_access_model = iks_insert(x, "field");
+	iks_insert_attrib(field_access_model, "var", "pubsub#access_model");
+	iks_insert_cdata(iks_insert(field_access_model, "value"), "whitelist", 9);
+	if (node_type && !strcasecmp(node_type, "leaf")) {
+		field_pubsub_collection = iks_insert(x, "field");
+		iks_insert_attrib(field_pubsub_collection, "var", "pubsub#collection");
+		iks_insert_cdata(iks_insert(field_pubsub_collection, "value"), collection_name,
+				 strlen(collection_name));
+	}
+	return configure;
+}
+
+/*!
+ * \brief Add Owner affiliations for pubsub node
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param node the name of the node to which to add affiliations
+ * \return void
+ */
+static void xmpp_pubsub_create_affiliations(struct ast_xmpp_client *client, const char *node)
+{
+	iks *modify_affiliates = xmpp_pubsub_iq_create(client, "set");
+	iks *pubsub, *affiliations, *affiliate;
+	struct ao2_iterator i;
+	struct ast_xmpp_buddy *buddy;
+
+	pubsub = iks_insert(modify_affiliates, "pubsub");
+	iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
+	affiliations = iks_insert(pubsub, "affiliations");
+	iks_insert_attrib(affiliations, "node", node);
+
+	i = ao2_iterator_init(client->buddies, 0);
+	while ((buddy = ao2_iterator_next(&i))) {
+		affiliate = iks_insert(affiliations, "affiliation");
+		iks_insert_attrib(affiliate, "jid", buddy->id);
+		iks_insert_attrib(affiliate, "affiliation", "owner");
+		ao2_ref(buddy, -1);
+	}
+	ao2_iterator_destroy(&i);
+
+	ast_xmpp_client_send(client, modify_affiliates);
+	iks_delete(modify_affiliates);
+}
+
+/*!
+ * \brief Create a pubsub node
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param node_type the type of node to create
+ * \param name the name of the node to create
+ * \return void
+ */
+static void xmpp_pubsub_create_node(struct ast_xmpp_client *client, const char *node_type, const
+				    char *name, const char *collection_name)
+{
+	iks *node, *pubsub, *create;
+
+	if (!(node = xmpp_pubsub_iq_create(client, "set"))) {
+		return;
+	}
+
+	pubsub = iks_insert(node, "pubsub");
+	iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub");
+	create = iks_insert(pubsub, "create");
+	iks_insert_attrib(create, "node", name);
+	xmpp_pubsub_build_node_config(pubsub, node_type, collection_name);
+	ast_xmpp_client_send(client, node);
+	xmpp_pubsub_create_affiliations(client, name);
+	iks_delete(node);
+}
+
+/*!
+ * \brief Delete a PubSub node
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param node_name the name of the node to delete
+ * return void
+ */
+static void xmpp_pubsub_delete_node(struct ast_xmpp_client *client, const char *node_name)
+{
+	iks *request, *pubsub, *delete;
+
+	if (!(request = xmpp_pubsub_iq_create(client, "set"))) {
+		return;
+	}
+
+	pubsub = iks_insert(request, "pubsub");
+	iks_insert_attrib(pubsub, "xmlns", "http://jabber.org/protocol/pubsub#owner");
+	delete = iks_insert(pubsub, "delete");
+	iks_insert_attrib(delete, "node", node_name);
+	ast_xmpp_client_send(client, request);
+
+	iks_delete(delete);
+	iks_delete(pubsub);
+	iks_delete(request);
+}
+
+/*!
+ * \brief Create a PubSub collection node.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param collection_name The name to use for this collection
+ * \return void.
+ */
+static void xmpp_pubsub_create_collection(struct ast_xmpp_client *client, const char *collection_name)
+{
+	xmpp_pubsub_create_node(client, "collection", collection_name, NULL);
+}
+
+
+/*!
+ * \brief Create a PubSub leaf node.
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param leaf_name The name to use for this collection
+ * \return void.
+ */
+static void xmpp_pubsub_create_leaf(struct ast_xmpp_client *client, const char *collection_name,
+				    const char *leaf_name)
+{
+	xmpp_pubsub_create_node(client, "leaf", leaf_name, collection_name);
+}
+
+/*!
+ * \brief Publish MWI to a PubSub node
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param device the name of the device whose state to publish
+ * \param device_state the state to publish
+ * \return void
+ */
+static void xmpp_pubsub_publish_mwi(struct ast_xmpp_client *client, const char *mailbox,
+				    const char *context, const char *oldmsgs, const char *newmsgs)
+{
+	char full_mailbox[AST_MAX_EXTENSION+AST_MAX_CONTEXT], eid_str[20];
+	iks *mailbox_node, *request;
+
+	snprintf(full_mailbox, sizeof(full_mailbox), "%s@%s", mailbox, context);
+
+	if (!(request = xmpp_pubsub_build_publish_skeleton(client, full_mailbox, "message_waiting"))) {
+		return;
+	}
+
+	ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
+	mailbox_node = iks_insert(request, "mailbox");
+	iks_insert_attrib(mailbox_node, "xmlns", "http://asterisk.org");
+	iks_insert_attrib(mailbox_node, "eid", eid_str);
+	iks_insert_cdata(iks_insert(mailbox_node, "NEWMSGS"), newmsgs, strlen(newmsgs));
+	iks_insert_cdata(iks_insert(mailbox_node, "OLDMSGS"), oldmsgs, strlen(oldmsgs));
+
+	ast_xmpp_client_send(client, iks_root(request));
+
+	iks_delete(request);
+}
+
+/*!
+ * \brief Publish device state to a PubSub node
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param device the name of the device whose state to publish
+ * \param device_state the state to publish
+ * \return void
+ */
+static void xmpp_pubsub_publish_device_state(struct ast_xmpp_client *client, const char *device,
+					     const char *device_state)
+{
+	iks *request, *state;
+	char eid_str[20];
+
+	if (!(request = xmpp_pubsub_build_publish_skeleton(client, device, "device_state"))) {
+		return;
+	}
+
+	if (ast_test_flag(&pubsubflags, XMPP_PUBSUB_AUTOCREATE)) {
+		if (ast_test_flag(&pubsubflags, XMPP_XEP0248)) {
+			xmpp_pubsub_create_node(client, "leaf", device, "device_state");
+		} else {
+			xmpp_pubsub_create_node(client, NULL, device, NULL);
+		}
+	}
+
+	ast_eid_to_str(eid_str, sizeof(eid_str), &ast_eid_default);
+	state = iks_insert(request, "state");
+	iks_insert_attrib(state, "xmlns", "http://asterisk.org");
+	iks_insert_attrib(state, "eid", eid_str);
+	iks_insert_cdata(state, device_state, strlen(device_state));
+	ast_xmpp_client_send(client, iks_root(request));
+	iks_delete(request);
+}
+
+/*!
+ * \brief Callback function for MWI events
+ * \param ast_event
+ * \param data void pointer to ast_client structure
+ * \return void
+ */

[... 3121 lines stripped ...]



More information about the asterisk-commits mailing list