[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