[asterisk-commits] bweschke: branch bweschke/originate_w_jabber r95255 - in /team/bweschke/origi...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Dec 28 13:36:42 CST 2007


Author: bweschke
Date: Fri Dec 28 13:36:42 2007
New Revision: 95255

URL: http://svn.digium.com/view/asterisk?view=rev&rev=95255
Log:
 Backport the trunk version of res_jabber to 1.4 codebase because it seems to actually be more stable then what's in 1.4 from testing that davevg has done. It compiles. Now let's see if it works.


Modified:
    team/bweschke/originate_w_jabber/include/asterisk/jabber.h
    team/bweschke/originate_w_jabber/res/res_jabber.c

Modified: team/bweschke/originate_w_jabber/include/asterisk/jabber.h
URL: http://svn.digium.com/view/asterisk/team/bweschke/originate_w_jabber/include/asterisk/jabber.h?view=diff&rev=95255&r1=95254&r2=95255
==============================================================================
--- team/bweschke/originate_w_jabber/include/asterisk/jabber.h (original)
+++ team/bweschke/originate_w_jabber/include/asterisk/jabber.h Fri Dec 28 13:36:42 2007
@@ -16,8 +16,45 @@
  * at the top of the source tree.
  */
 
+/*! \file
+ * \brief AJI - The Asterisk Jabber Interface
+ * \arg \ref AJI_intro
+ * \ref res_jabber.c
+ * \author Matt O'Gorman <mogorman at digium.com>
+ * \extref IKSEMEL http://iksemel.jabberstudio.org
+ *
+ * \page AJI_intro AJI - The Asterisk Jabber Interface
+ * 
+ * The Asterisk Jabber Interface, AJI, publishes an API for
+ * modules to use jabber communication. res_jabber.c implements
+ * a Jabber client and a component that can connect as a service
+ * to Jabber servers.
+ *
+ * \section External dependencies
+ * AJI use the IKSEMEL library found at http://iksemel.jabberstudio.org/
+ *
+ * \section Files
+ * - res_jabber.c
+ * - jabber.h
+ * - chan_gtalk.c
+ *
+ */
+
 #ifndef _ASTERISK_JABBER_H
 #define _ASTERISK_JABBER_H
+
+#ifdef HAVE_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#define TRY_SECURE 2
+#define SECURE 4
+/* 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
+
+#endif /* HAVE_OPENSSL */
 
 #include <iksemel.h>
 #include "asterisk/astobj.h"
@@ -86,7 +123,9 @@
 
 struct aji_buddy {
 	ASTOBJ_COMPONENTS_FULL(struct aji_buddy, AJI_MAX_JIDLEN, 1);
+	char channel[160];
 	struct aji_resource *resources;
+	enum aji_btype btype;
 	unsigned int flags;
 };
 
@@ -104,12 +143,19 @@
 	char user[AJI_MAX_JIDLEN];
 	char serverhost[AJI_MAX_RESJIDLEN];
 	char statusmessage[256];
+	char name_space[256];
 	char sid[10]; /* Session ID */
 	char mid[6]; /* Message ID */
 	iksid *jid;
 	iksparser *p;
 	iksfilter *f;
 	ikstack *stack;
+#ifdef HAVE_OPENSSL
+	SSL_CTX *ssl_context;
+	SSL *ssl_session;
+	SSL_METHOD *ssl_method;
+	unsigned int stream_flags;
+#endif /* HAVE_OPENSSL */
 	enum aji_state state;
 	int port;
 	int debug;
@@ -127,18 +173,27 @@
 	AST_LIST_HEAD(messages,aji_message) messages;
 	void *jingle;
 	pthread_t thread;
+	int priority;
+	enum ikshowtype status;
 };
 
 struct aji_client_container{
 	ASTOBJ_CONTAINER_COMPONENTS(struct aji_client);
 };
 
-int ast_aji_send(struct aji_client *client, const char *address, const char *message);
+/* !Send XML stanza over the established XMPP connection */
+int ast_aji_send(struct aji_client *client, iks *x);
+/*! Send jabber chat message from connected client to jabber URI */
+int ast_aji_send_chat(struct aji_client *client, const char *address, const char *message);
+/*! Disconnect jabber client */
 int ast_aji_disconnect(struct aji_client *client);
 int ast_aji_check_roster(void);
 void ast_aji_increment_mid(char *mid);
+/*! Open Chat session */
 int ast_aji_create_chat(struct aji_client *client,char *room, char *server, char *topic);
+/*! Invite to opened Chat session */
 int ast_aji_invite_chat(struct aji_client *client, char *user, char *room, char *message);
+/*! Join existing Chat session */
 int ast_aji_join_chat(struct aji_client *client,char *room);
 struct aji_client *ast_aji_get_client(const char *name);
 struct aji_client_container *ast_aji_get_clients(void);

Modified: team/bweschke/originate_w_jabber/res/res_jabber.c
URL: http://svn.digium.com/view/asterisk/team/bweschke/originate_w_jabber/res/res_jabber.c?view=diff&rev=95255&r1=95254&r2=95255
==============================================================================
--- team/bweschke/originate_w_jabber/res/res_jabber.c (original)
+++ team/bweschke/originate_w_jabber/res/res_jabber.c Fri Dec 28 13:36:42 2007
@@ -20,17 +20,16 @@
  * \brief A resource for interfacing asterisk directly as a client
  * or a component to a jabber compliant server.
  *
+ * \extref Iksemel http://iksemel.jabberstudio.org/
+ *
  * \todo If you unload this module, chan_gtalk/jingle will be dead. How do we handle that?
- * \todo If you have TLS, you can't unload this module. See bug #9738. This needs to be fixed,
- *       but the bug is in the unmantained Iksemel library
  *
  */
 
 /*** MODULEINFO
 	<depend>iksemel</depend>
-	<use>gnutls</use>
+	<use>openssl</use>
  ***/
-
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -45,8 +44,6 @@
 #include "asterisk/config.h"
 #include "asterisk/callerid.h"
 #include "asterisk/lock.h"
-#include "asterisk/logger.h"
-#include "asterisk/options.h"
 #include "asterisk/cli.h"
 #include "asterisk/app.h"
 #include "asterisk/pbx.h"
@@ -57,6 +54,8 @@
 #include "asterisk/astobj.h"
 #include "asterisk/astdb.h"
 #include "asterisk/manager.h"
+#include "asterisk/logger.h"
+#include "asterisk/options.h"
 
 #define JABBER_CONFIG "jabber.conf"
 
@@ -74,15 +73,22 @@
 static void aji_client_destroy(struct aji_client *obj);
 static int aji_send_exec(struct ast_channel *chan, void *data);
 static int aji_status_exec(struct ast_channel *chan, void *data);
+static int aji_is_secure(struct aji_client *client);
+static int aji_start_tls(struct aji_client *client);
+static int aji_tls_handshake(struct aji_client *client);
+static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout);
+static int aji_recv(struct aji_client *client, int timeout);
+static int aji_send_header(struct aji_client *client, const char *to);
+static int aji_send_raw(struct aji_client *client, const char *xmlstr);
 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming);
+static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass);
 static int aji_act_hook(void *data, int type, iks *node);
 static void aji_handle_iq(struct aji_client *client, iks *node);
 static void aji_handle_message(struct aji_client *client, ikspak *pak);
 static void aji_handle_presence(struct aji_client *client, ikspak *pak);
 static void aji_handle_subscribe(struct aji_client *client, ikspak *pak);
 static void *aji_recv_loop(void *data);
-static int aji_component_initialize(struct aji_client *client);
-static int aji_client_initialize(struct aji_client *client);
+static int aji_initialize(struct aji_client *client);
 static int aji_client_connect(void *data, ikspak *pak);
 static void aji_set_presence(struct aji_client *client, char *to, char *from, int level, char *desc);
 static int aji_do_debug(int fd, int argc, char *argv[]);
@@ -93,7 +99,7 @@
 static int aji_create_client(char *label, struct ast_variable *var, int debug);
 static int aji_create_buddy(char *label, struct aji_client *client);
 static int aji_reload(void);
-static int aji_load_config(void);
+static int aji_load_config(int reload);
 static void aji_pruneregister(struct aji_client *client);
 static int aji_filter_roster(void *data, ikspak *pak);
 static int aji_get_roster(struct aji_client *client);
@@ -111,43 +117,47 @@
 static int aji_register_transport2(void *data, ikspak *pak);
 */
 
-static char debug_usage[] = 
-"Usage: jabber debug\n" 
+/* TODO: Add this guy back in
+	AST_CLI_DEFINE(aji_show_buddies, "Show buddy lists of our clients"),
+*/
+
+static char debug_usage[] =
+"Usage: jabber debug\n"
 "       Enables dumping of Jabber packets for debugging purposes.\n";
 
-static char no_debug_usage[] = 
-"Usage: jabber debug off\n" 
+static char no_debug_usage[] =
+"Usage: jabber debug off\n"
 "       Disables dumping of Jabber packets for debugging purposes.\n";
 
-static char reload_usage[] = 
-"Usage: jabber reload\n" 
+static char reload_usage[] =
+"Usage: jabber reload\n"
 "       Enables reloading of Jabber module.\n";
 
-static char test_usage[] = 
-"Usage: jabber test [client]\n" 
+static char test_usage[] =
+"Usage: jabber test [client]\n"
 "       Sends test message for debugging purposes.  A specific client\n"
 "       as configured in jabber.conf can be optionally specified.\n";
 
 static struct ast_cli_entry aji_cli[] = {
-	{ { "jabber", "debug", NULL},
-	aji_do_debug, "Enable Jabber debugging",
-	debug_usage },
-
-	{ { "jabber", "reload", NULL},
-	aji_do_reload, "Reload Jabber configuration",
-	reload_usage },
-
-	{ { "jabber", "show", "connected", NULL},
-	aji_show_clients, "Show state of clients and components",
-	debug_usage },
-
-	{ { "jabber", "debug", "off", NULL},
-	aji_no_debug, "Disable Jabber debug",
-	no_debug_usage },
-
-	{ { "jabber", "test", NULL},
-	aji_test, "Shows roster, but is generally used for mog's debugging.",
-	test_usage },
+        { { "jabber", "debug", NULL},
+        aji_do_debug, "Enable Jabber debugging",
+        debug_usage },
+
+        { { "jabber", "reload", NULL},
+        aji_do_reload, "Reload Jabber configuration",
+        reload_usage },
+
+        { { "jabber", "show", "connected", NULL},
+        aji_show_clients, "Show state of clients and components",
+        debug_usage },
+
+        { { "jabber", "debug", "off", NULL},
+        aji_no_debug, "Disable Jabber debug",
+        no_debug_usage },
+
+        { { "jabber", "test", NULL},
+        aji_test, "Shows roster, but is generally used for mog's debugging.",
+        test_usage },
 };
 
 static char *app_ajisend = "JabberSend";
@@ -177,11 +187,10 @@
 
 /*! \brief Global flags, initialized to default values */
 static struct ast_flags globalflags = { AJI_AUTOPRUNE | AJI_AUTOREGISTER };
-static int tls_initialized = FALSE;
 
 /*!
  * \brief Deletes the aji_client data structure.
- * \param obj is the structure we will delete.
+ * \param obj aji_client The structure we will delete.
  * \return void.
  */
 static void aji_client_destroy(struct aji_client *obj)
@@ -195,17 +204,17 @@
 	AST_LIST_LOCK(&obj->messages);
 	while ((tmp = AST_LIST_REMOVE_HEAD(&obj->messages, list))) {
 		if (tmp->from)
-			free(tmp->from);
+			ast_free(tmp->from);
 		if (tmp->message)
-			free(tmp->message);
+			ast_free(tmp->message);
 	}
 	AST_LIST_HEAD_DESTROY(&obj->messages);
-	free(obj);
+	ast_free(obj);
 }
 
 /*!
  * \brief Deletes the aji_buddy data structure.
- * \param obj is the structure we will delete.
+ * \param obj aji_buddy The structure we will delete.
  * \return void.
  */
 static void aji_buddy_destroy(struct aji_buddy *obj)
@@ -214,11 +223,11 @@
 
 	while ((tmp = obj->resources)) {
 		obj->resources = obj->resources->next;
-		free(tmp->description);
-		free(tmp);
-	}
-
-	free(obj);
+		ast_free(tmp->description);
+		ast_free(tmp);
+	}
+
+	ast_free(obj);
 }
 
 /*!
@@ -227,7 +236,7 @@
  * our list
  * \param version the version attribute in the caps element we'll look for or 
  * add to our list
- * \param pak the XML stanza we're processing
+ * \param pak struct The XML stanza we're processing
  * \return a pointer to the added or found aji_version structure
  */ 
 static struct aji_version *aji_find_version(char *node, char *version, ikspak *pak)
@@ -252,7 +261,7 @@
 			/* Specified version not found. Let's add it to 
 			   this node in our capabilities list */
 			if(!res) {
-				res = (struct aji_version *)malloc(sizeof(struct aji_version));
+				res = ast_malloc(sizeof(*res));
 				if(!res) {
 					ast_log(LOG_ERROR, "Out of memory!\n");
 					return NULL;
@@ -269,12 +278,12 @@
 	}
 	/* Specified node not found. Let's add it our capabilities list */
 	if(!list) {
-		list = (struct aji_capabilities *)malloc(sizeof(struct aji_capabilities));
+		list = ast_malloc(sizeof(*list));
 		if(!list) {
 			ast_log(LOG_ERROR, "Out of memory!\n");
 			return NULL;
 		}
-		res = (struct aji_version *)malloc(sizeof(struct aji_version));
+		res = ast_malloc(sizeof(*res));
 		if(!res) {
 			ast_log(LOG_ERROR, "Out of memory!\n");
 			ast_free(list);
@@ -291,7 +300,12 @@
 	}
 	return res;
 }
-
+/*!
+ * \brief Find the aji_resource we want
+ * \param buddy aji_buddy A buddy
+ * \param name 
+ * \return aji_resource object
+*/
 static struct aji_resource *aji_find_resource(struct aji_buddy *buddy, char *name)
 {
 	struct aji_resource *res = NULL;
@@ -307,6 +321,11 @@
 	return res;
 }
 
+/*!
+ * \brief Jabber GTalk function
+ * \param node iks
+ * \return 1 on success, 0 on failure.
+*/
 static int gtalk_yuck(iks *node)
 {
 	if (iks_find_with_attrib(node, "c", "node", "http://www.google.com/xmpp/client/caps"))
@@ -316,7 +335,7 @@
 
 /*!
  * \brief Detects the highest bit in a number.
- * \param Number you want to have evaluated.
+ * \param number  Number you want to have evaluated.
  * \return the highest power of 2 that can go into the number.
  */
 static int aji_highest_bit(int number)
@@ -331,6 +350,13 @@
 	return (1 << x);
 }
 
+/*!
+ * \brief Setup the authentication struct
+ * \param id iksid 
+ * \param pass password
+ * \param sid
+ * \return x iks
+*/
 static iks *jabber_make_auth(iksid * id, const char *pass, const char *sid)
 {
 	iks *x, *y;
@@ -355,127 +381,478 @@
 /*!
  * \brief Dial plan function status(). puts the status of watched user 
    into a channel variable.
- * \param channel, and username,watched user, status var
- * \return 0.
+ * \param chan ast_channel
+ * \param data
+ * \return 0 on success, -1 on error
  */
 static int aji_status_exec(struct ast_channel *chan, void *data)
 {
 	struct aji_client *client = NULL;
 	struct aji_buddy *buddy = NULL;
 	struct aji_resource *r = NULL;
-	char *s = NULL, *sender = NULL, *jid = NULL, *screenname = NULL, *resource = NULL, *variable = NULL;
+	char *s = NULL;
 	int stat = 7;
 	char status[2];
+	static int deprecation_warning = 0;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(sender);
+		AST_APP_ARG(jid);
+		AST_APP_ARG(variable);
+	);
+	AST_DECLARE_APP_ARGS(jid,
+		AST_APP_ARG(screenname);
+		AST_APP_ARG(resource);
+	);
+
+	if (deprecation_warning++ % 10 == 0)
+		ast_log(LOG_WARNING, "JabberStatus is deprecated.  Please use the JABBER_STATUS dialplan function in the future.\n");
 
 	if (!data) {
-		ast_log(LOG_ERROR, "This application requires arguments.\n");
+		ast_log(LOG_ERROR, "Usage: JabberStatus(<sender>,<screenname>[/<resource>],<varname>\n");
 		return 0;
 	}
 	s = ast_strdupa(data);
-	if (s) {
-		sender = strsep(&s, "|");
-		if (sender && (sender[0] != '\0')) {
-			jid = strsep(&s, "|");
-			if (jid && (jid[0] != '\0')) {
-				variable = s;
-			} else {
-				ast_log(LOG_ERROR, "Bad arguments\n");
-				return -1;
-			}
-		}
-	}
-
-	if(!strchr(jid, '/')) {
-		resource = NULL;
-	} else {
-		screenname = strsep(&jid, "/");
-		resource = jid;
-	}
-	client = ast_aji_get_client(sender);
-	if (!client) {
-		ast_log(LOG_WARNING, "Could not find sender connection: %s\n", sender);
+	AST_STANDARD_APP_ARGS(args, s);
+
+	if (args.argc != 3) {
+		ast_log(LOG_ERROR, "JabberStatus() requires 3 arguments.\n");
 		return -1;
 	}
-	if(!&client->buddies) {
-		ast_log(LOG_WARNING, "No buddies for connection : %s\n", sender);
+
+	AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
+
+	if (!(client = ast_aji_get_client(args.sender))) {
+		ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
 		return -1;
 	}
-	buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, resource ? screenname: jid);
+	buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
 	if (!buddy) {
-		ast_log(LOG_WARNING, "Could not find buddy in list : %s\n", resource ? screenname : jid);
+		ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
 		return -1;
 	}
-	r = aji_find_resource(buddy, resource);
-	if(!r && buddy->resources) 
+	r = aji_find_resource(buddy, jid.resource);
+	if (!r && buddy->resources) 
 		r = buddy->resources;
-	if(!r)
-		ast_log(LOG_NOTICE, "Resource %s of buddy %s not found \n", resource, screenname);
+	if (!r)
+		ast_log(LOG_NOTICE, "Resource '%s' of buddy '%s' was not found\n", jid.resource, jid.screenname);
 	else
 		stat = r->status;
-	sprintf(status, "%d", stat);
-	pbx_builtin_setvar_helper(chan, variable, status);
+	snprintf(status, sizeof(status), "%d", stat);
+	pbx_builtin_setvar_helper(chan, args.variable, status);
 	return 0;
 }
 
+static int acf_jabberstatus_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t buflen)
+{
+	struct aji_client *client = NULL;
+	struct aji_buddy *buddy = NULL;
+	struct aji_resource *r = NULL;
+	int stat = 7;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(sender);
+		AST_APP_ARG(jid);
+	);
+	AST_DECLARE_APP_ARGS(jid,
+		AST_APP_ARG(screenname);
+		AST_APP_ARG(resource);
+	);
+
+	if (!data) {
+		ast_log(LOG_ERROR, "Usage: JABBER_STATUS(<sender>,<screenname>[/<resource>])\n");
+		return 0;
+	}
+	AST_STANDARD_APP_ARGS(args, data);
+
+	if (args.argc != 2) {
+		ast_log(LOG_ERROR, "JABBER_STATUS requires 2 arguments.\n");
+		return -1;
+	}
+
+	AST_NONSTANDARD_APP_ARGS(jid, args.jid, '/');
+
+	if (!(client = ast_aji_get_client(args.sender))) {
+		ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
+		return -1;
+	}
+	buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, jid.screenname);
+	if (!buddy) {
+		ast_log(LOG_WARNING, "Could not find buddy in list: '%s'\n", jid.screenname);
+		return -1;
+	}
+	r = aji_find_resource(buddy, jid.resource);
+	if (!r && buddy->resources) 
+		r = buddy->resources;
+	if (!r)
+		ast_log(LOG_NOTICE, "Resource %s of buddy %s was not found.\n", jid.resource, jid.screenname);
+	else
+		stat = r->status;
+	snprintf(buf, buflen, "%d", stat);
+	return 0;
+}
+
+static struct ast_custom_function jabberstatus_function = {
+	.name = "JABBER_STATUS",
+	.synopsis = "Retrieve buddy status",
+	.syntax = "JABBER_STATUS(<sender>,<buddy>[/<resource>])",
+	.read = acf_jabberstatus_read,
+	.desc =
+"Retrieves the numeric status associated with the specified buddy.  If the\n"
+"buddy does not exist in the buddylist, returns 7.\n",
+};
+
 /*!
  * \brief Dial plan function to send a message.
- * \param channel, and data, data is sender, reciever, message.
- * \return 0.
+ * \param chan ast_channel
+ * \param data  Data is sender|reciever|message.
+ * \return 0 on success,-1 on error.
  */
 static int aji_send_exec(struct ast_channel *chan, void *data)
 {
 	struct aji_client *client = NULL;
-
-	char *s = NULL, *sender = NULL, *recipient = NULL, *message = NULL;
+	char *s;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(sender);
+		AST_APP_ARG(recipient);
+		AST_APP_ARG(message);
+	);
 
 	if (!data) {
-		ast_log(LOG_ERROR, "This application requires arguments.\n");
+		ast_log(LOG_ERROR, "Usage:  JabberSend(<sender>,<recipient>,<message>)\n");
 		return 0;
 	}
 	s = ast_strdupa(data);
-	if (s) {
-		sender = strsep(&s, "|");
-		if (sender && (sender[0] != '\0')) {
-			recipient = strsep(&s, "|");
-			if (recipient && (recipient[0] != '\0')) {
-				message = s;
-			} else {
-				ast_log(LOG_ERROR, "Bad arguments: %s\n", (char *) data);
-				return -1;
-			}
-		}
-	}
-	if (!(client = ast_aji_get_client(sender))) {
-		ast_log(LOG_WARNING, "Could not find sender connection: %s\n", sender);
+
+	AST_STANDARD_APP_ARGS(args, s);
+	if (args.argc < 3) {
+		ast_log(LOG_ERROR, "JabberSend requires 3 arguments: '%s'\n", (char *) data);
 		return -1;
 	}
-	if (strchr(recipient, '@') && message)
-		ast_aji_send(client, recipient, message);
+
+	if (!(client = ast_aji_get_client(args.sender))) {
+		ast_log(LOG_WARNING, "Could not find sender connection: '%s'\n", args.sender);
+		return -1;
+	}
+	if (strchr(args.recipient, '@') && !ast_strlen_zero(args.message))
+		ast_aji_send_chat(client, args.recipient, args.message);
 	return 0;
 }
 
+/*! 
+ * \brief Tests whether the connection is secured or not
+ * \return 0 if the connection is not secured
+ */
+static int aji_is_secure(struct aji_client *client)
+{
+#ifdef HAVE_OPENSSL
+	return client->stream_flags & SECURE;
+#else
+	return 0;
+#endif
+}
+
+
+/*!
+ * \brief Starts the TLS procedure
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return IKS_OK on success, an error code if sending failed, IKS_NET_TLSFAIL
+ * if OpenSSL is not installed
+ */
+static int aji_start_tls(struct aji_client *client)
+{
+	int ret;
+#ifndef HAVE_OPENSSL
+	return IKS_NET_TLSFAIL;
+#endif	
+	/* This is sent not encrypted */
+	ret = iks_send_raw(client->p, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
+	if (ret)
+		return ret;
+	client->stream_flags |= TRY_SECURE;
+
+	return IKS_OK;
+}
+
+/*! 
+ * \brief TLS handshake, OpenSSL initialization
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \return IKS_OK on success, IKS_NET_TLSFAIL on failure 
+ */
+static int aji_tls_handshake(struct aji_client *client)
+{
+	int ret;
+	int sock;
+
+#ifndef HAVE_OPENSSL
+	return IKS_NET_TLSFAIL;
+#endif
+	
+	ast_log(LOG_DEBUG, "Starting TLS handshake\n"); 
+
+	/* Load encryption, hashing algorithms and error strings */
+	SSL_library_init();
+	SSL_load_error_strings();
+
+	/* Choose an SSL/TLS protocol version, create SSL_CTX */
+	client->ssl_method = SSLv3_method();
+	client->ssl_context = SSL_CTX_new(client->ssl_method);                
+	if (!client->ssl_context)
+		return IKS_NET_TLSFAIL;
+
+	/* Create new SSL session */
+	client->ssl_session = SSL_new(client->ssl_context);
+	if (!client->ssl_session)
+		return IKS_NET_TLSFAIL;
+
+	/* Enforce TLS on our XMPP connection */
+	sock = iks_fd(client->p);
+	ret = SSL_set_fd(client->ssl_session, sock);
+	if (!ret)
+		return IKS_NET_TLSFAIL;
+
+	/* Perform SSL handshake */
+	ret = SSL_connect(client->ssl_session);
+	if (!ret)
+		return IKS_NET_TLSFAIL;
+
+	client->stream_flags &= (~TRY_SECURE);
+	client->stream_flags |= SECURE;
+
+	/* Sent over the established TLS connection */
+	ret = aji_send_header(client, client->jid->server);
+	if (ret != IKS_OK)
+		return IKS_NET_TLSFAIL;
+
+	ast_log(LOG_DEBUG, "TLS started with server\n"); 
+
+	return IKS_OK;
+}
+
+/*! 
+ * \brief Secured or unsecured IO socket receiving function
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param buffer the reception buffer
+ * \param buf_len the size of the buffer
+ * \param timeout the select timer
+ * \return the number of read bytes on success, 0 on timeout expiration, 
+ * -1 on  error
+ */
+static int aji_io_recv(struct aji_client *client, char *buffer, size_t buf_len, int timeout)
+{
+	int sock;
+	fd_set fds;
+	struct timeval tv, *tvptr = NULL;
+	int len, res;
+
+#ifdef HAVE_OPENSSL
+	if (aji_is_secure(client)) {
+		sock = SSL_get_fd(client->ssl_session);
+		if (sock < 0)
+			return -1;		
+	} else
+#endif /* HAVE_OPENSSL */
+		sock = iks_fd(client->p);	
+
+	memset(&tv, 0, sizeof(struct timeval));
+	FD_ZERO(&fds);
+	FD_SET(sock, &fds);
+	tv.tv_sec = timeout;
+
+	/* NULL value for tvptr makes ast_select wait indefinitely */
+	tvptr = (timeout != -1) ? &tv : NULL;
+
+	/* ast_select emulates linux behaviour in terms of timeout handling */
+	res = ast_select(sock + 1, &fds, NULL, NULL, tvptr);
+	if (res > 0) {
+#ifdef HAVE_OPENSSL
+		if (aji_is_secure(client)) {
+			len = SSL_read(client->ssl_session, buffer, buf_len);
+		} else
+#endif /* HAVE_OPENSSL */
+			len = recv(sock, buffer, buf_len, 0);
+
+		if (len > 0) {
+			return len;
+		} else if (len <= 0) {
+			return -1;
+		}
+	}
+	return res;
+}
+
+/*! 
+ * \brief Tries to receive data from the Jabber server
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param timeout the timeout value
+ * This function receives (encrypted or unencrypted) data from the XMPP server,
+ * and passes it to the parser.
+ * \return IKS_OK on success, IKS_NET_RWERR on IO error, IKS_NET_NOCONN, if no
+ * connection available, IKS_NET_EXPIRED on timeout expiration
+ */
+static int aji_recv (struct aji_client *client, int timeout)
+{
+	int len, ret;
+	char buf[NET_IO_BUF_SIZE -1];
+
+	memset(buf, 0, sizeof(buf));
+
+	while (1) {
+		len = aji_io_recv(client, buf, NET_IO_BUF_SIZE - 1, timeout);
+		if (len < 0) return IKS_NET_RWERR;
+		if (len == 0) return IKS_NET_EXPIRED;
+		buf[len] = '\0';
+		
+		/* Log the message here, because iksemel's logHook is 
+		   unaccessible */
+		aji_log_hook(client, buf, len, 1);
+		
+		ret = iks_parse(client->p, buf, len, 0);
+		if (ret != IKS_OK) {
+			return ret;
+		}
+	}
+	return IKS_OK;
+}
+
+/*! 
+ * \brief Sends XMPP header to the server
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param to the target XMPP server
+ * \return IKS_OK on success, any other value on failure
+ */
+static int aji_send_header(struct aji_client *client, const char *to)
+{
+	char *msg;
+	int len, err;
+
+	len = 91 + strlen(client->name_space) + 6 + strlen(to) + 16 + 1;
+	msg = iks_malloc(len);
+	if (!msg)
+		return IKS_NOMEM;
+	sprintf(msg, "<?xml version='1.0'?>"
+		"<stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='"
+		"%s' to='%s' version='1.0'>", client->name_space, to);
+	err = aji_send_raw(client, msg);
+	iks_free(msg);
+	if (err != IKS_OK)
+		return err;
+
+	return IKS_OK;
+}
+
+/*! 
+ * \brief Wraps raw sending
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param x the XMPP packet to send
+ * \return IKS_OK on success, any other value on failure
+ */
+int ast_aji_send(struct aji_client *client, iks *x)
+{
+	return aji_send_raw(client, iks_string(iks_stack(x), x));
+}
+
+/*! 
+ * \brief Sends an XML string over an XMPP connection
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param xmlstr the XML string to send
+ * The XML data is sent whether the connection is secured or not. In the 
+ * latter case, we just call iks_send_raw().
+ * \return IKS_OK on success, any other value on failure
+ */
+static int aji_send_raw(struct aji_client *client, const char *xmlstr)
+{
+	int ret;
+#ifdef HAVE_OPENSSL
+	int len = strlen(xmlstr);
+
+	if (aji_is_secure(client)) {
+		ret = SSL_write(client->ssl_session, xmlstr, len);
+		if (ret) {
+			/* Log the message here, because iksemel's logHook is 
+			   unaccessible */
+			aji_log_hook(client, xmlstr, len, 0);
+			return IKS_OK;
+		}
+	}
+#endif
+	/* If needed, data will be sent unencrypted, and logHook will 
+	   be called inside iks_send_raw */
+	ret = iks_send_raw(client->p, xmlstr);
+	if (ret != IKS_OK)
+		return ret;	
+
+	return IKS_OK;
+}
+
 /*!
  * \brief the debug loop.
- * \param aji_client structure, xml data as string, size of string, direction of packet, 1 for inbound 0 for outbound.
+ * \param data void
+ * \param xmpp xml data as string
+ * \param size size of string
+ * \param is_incoming direction of packet 1 for inbound 0 for outbound.
  */
 static void aji_log_hook(void *data, const char *xmpp, size_t size, int is_incoming)
 {
 	struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
-	manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
+
+	if (!ast_strlen_zero(xmpp))
+		manager_event(EVENT_FLAG_USER, "JabberEvent", "Account: %s\r\nPacket: %s\r\n", client->name, xmpp);
 
 	if (client->debug) {
 		if (is_incoming)
-			ast_verbose("\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
+			ast_verbose(VERBOSE_PREFIX_3 "\nJABBER: %s INCOMING: %s\n", client->name, xmpp);
 		else {
 			if( strlen(xmpp) == 1) {
 				if(option_debug > 2  && xmpp[0] == ' ')
-				ast_verbose("\nJABBER: Keep alive packet\n");
+				ast_verbose(VERBOSE_PREFIX_3 "\nJABBER: Keep alive packet\n");
 			} else
-				ast_verbose("\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
+				ast_verbose(VERBOSE_PREFIX_3 "\nJABBER: %s OUTGOING: %s\n", client->name, xmpp);
 		}
 
 	}
 	ASTOBJ_UNREF(client, aji_client_destroy);
+}
+
+/*!
+ * \brief A wrapper function for iks_start_sasl
+ * \param client the configured XMPP client we use to connect to a XMPP server
+ * \param type the SASL authentication type. Supported types are PLAIN and MD5
+ * \param username
+ * \param pass password.
+ *
+ * If SASL authentication type is MD5, we simply call iks_start_sasl().
+ * If type is PLAIN, we compute the authentication string by ourselves, 
+ * because it looks like Google's jabber server does not accept the value 
+ * computed with iks_start_sasl().
+ * \return IKS_OK on success, IKSNET_NOTSUPP on failure.
+ */
+static int aji_start_sasl(struct aji_client *client, enum ikssasltype type, char *username, char *pass)
+{
+	iks *x = NULL;
+	int len;
+	char *s;
+	char *base64;
+
+	if (type == IKS_STREAM_SASL_MD5)
+		return iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, username, pass);
+
+	x = iks_new("auth"); 
+	if (!x) {
+		ast_log(LOG_ERROR, "Out of memory.\n");
+		return IKS_NET_NOTSUPP;
+	}
+
+	iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
+	len = strlen(username) + strlen(pass) + 3;
+	s = alloca(len);
+	base64 = alloca((len + 2) * 4 / 3);
+	iks_insert_attrib(x, "mechanism", "PLAIN");
+	snprintf(s, len, "%c%s%c%s", 0, username, 0, pass);
+	ast_base64encode(base64, (const unsigned char *) s, len, (len + 2) * 4 / 3);
+	iks_insert_cdata(x, base64, 0);
+	ast_aji_send(client, x);
+	iks_delete(x);
+
+	return IKS_OK;
 }
 
 /*!
@@ -490,6 +867,7 @@
 	struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
 	ikspak *pak = NULL;
 	iks *auth = NULL;
+	int features = 0;
 
 	if(!node) {
 		ast_log(LOG_ERROR, "aji_act_hook was called with out a packet\n"); /* most likely cause type is IKS_NODE_ERROR lost connection */
@@ -507,12 +885,13 @@
 	if (!client->component) { /*client */
 		switch (type) {
 		case IKS_NODE_START:
-			if (client->usetls && !iks_is_secure(client->p)) {
-				if (iks_has_tls()) {
-					iks_start_tls(client->p);
-					tls_initialized = TRUE;
-				} else
-					ast_log(LOG_ERROR, "gnuTLS not installed. You need to recompile the Iksemel library with gnuTLS support\n");
+			if (client->usetls && !aji_is_secure(client)) {
+				if (aji_start_tls(client) == IKS_NET_TLSFAIL) {
+					ast_log(LOG_ERROR, "OpenSSL not installed. You need to install OpenSSL on this system\n");
+					ASTOBJ_UNREF(client, aji_client_destroy);
+					return IKS_HOOK;		
+				}
+
 				break;
 			}
 			if (!client->usesasl) {
@@ -522,7 +901,7 @@
 					iks_insert_attrib(auth, "id", client->mid);
 					iks_insert_attrib(auth, "to", client->jid->server);
 					ast_aji_increment_mid(client->mid);
-					iks_send(client->p, auth);
+					ast_aji_send(client, auth);
 					iks_delete(auth);
 				} else
 					ast_log(LOG_ERROR, "Out of memory.\n");
@@ -530,82 +909,65 @@
 			break;
 
 		case IKS_NODE_NORMAL:
-			{
-				int features = 0;
-				if (!strcmp("stream:features", iks_name(node))) {
-					features = iks_stream_features(node);
-					if (client->usesasl) {
-						if (client->usetls && !iks_is_secure(client->p))
-							break;
-						if (client->authorized) {
-							if (features & IKS_STREAM_BIND) {
-								iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
-								auth = iks_make_resource_bind(client->jid);
-								if (auth) {
-									iks_insert_attrib(auth, "id", client->mid);
-									ast_aji_increment_mid(client->mid);
-									iks_send(client->p, auth);
-									iks_delete(auth);
-								} else {
-									ast_log(LOG_ERROR, "Out of memory.\n");
-									break;
-								}
-							}
-							if (features & IKS_STREAM_SESSION) {
-								iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
-								auth = iks_make_session();
-								if (auth) {
-									iks_insert_attrib(auth, "id", "auth");
-									ast_aji_increment_mid(client->mid);
-									iks_send(client->p, auth);
-									iks_delete(auth);
-								} else {
-									ast_log(LOG_ERROR, "Out of memory.\n");
-								}
-							}
-						} else {
-							if (!client->jid->user) {
-								ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
+			if (client->stream_flags & TRY_SECURE) {
+				if (!strcmp("proceed", iks_name(node))) {
+					return aji_tls_handshake(client);
+				}
+			}
+
+			if (!strcmp("stream:features", iks_name(node))) {
+				features = iks_stream_features(node);
+				if (client->usesasl) {
+					if (client->usetls && !aji_is_secure(client))
+						break;
+					if (client->authorized) {
+						if (features & IKS_STREAM_BIND) {
+							iks_filter_add_rule(client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_DONE);
+							auth = iks_make_resource_bind(client->jid);
+							if (auth) {
+								iks_insert_attrib(auth, "id", client->mid);
+								ast_aji_increment_mid(client->mid);
+								ast_aji_send(client, auth);
+								iks_delete(auth);
+							} else {
+								ast_log(LOG_ERROR, "Out of memory.\n");
 								break;
 							}
-							features = aji_highest_bit(features);
-							if (features == IKS_STREAM_SASL_MD5)
-								iks_start_sasl(client->p, IKS_SASL_DIGEST_MD5, client->jid->user, client->password);
-							else {
-								if (features == IKS_STREAM_SASL_PLAIN) {
-									iks *x = NULL;
-									x = iks_new("auth");
-									if (x) {
-										int len = strlen(client->jid->user) + strlen(client->password) + 3;
-										/* XXX Check return values XXX */
-										char *s = ast_malloc(80 + len);
-										char *base64 = ast_malloc(80 + len * 2);
-										iks_insert_attrib(x, "xmlns", IKS_NS_XMPP_SASL);
-										iks_insert_attrib(x, "mechanism", "PLAIN");
-										sprintf(s, "%c%s%c%s", 0, client->jid->user, 0, client->password);
-										ast_base64encode(base64, (const unsigned char *) s, len, len * 2);
-										iks_insert_cdata(x, base64, 0);
-										iks_send(client->p, x);
-										iks_delete(x);
-										if (base64)
-											free(base64);
-										if (s)
-											free(s);
-									} else {
-										ast_log(LOG_ERROR, "Out of memory.\n");
-									}
-								}
+						}
+						if (features & IKS_STREAM_SESSION) {
+							iks_filter_add_rule (client->f, aji_client_connect, client, IKS_RULE_TYPE, IKS_PAK_IQ, IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, IKS_RULE_ID, "auth", IKS_RULE_DONE);
+							auth = iks_make_session();
+							if (auth) {
+								iks_insert_attrib(auth, "id", "auth");
+								ast_aji_increment_mid(client->mid);
+								ast_aji_send(client, auth);
+								iks_delete(auth);
+							} else {
+								ast_log(LOG_ERROR, "Out of memory.\n");
 							}
 						}
+					} else {
+						int ret;
+						if (!client->jid->user) {
+							ast_log(LOG_ERROR, "Malformed Jabber ID : %s (domain missing?)\n", client->jid->full);
+							break;
+						}
+						features = aji_highest_bit(features);
+						ret = aji_start_sasl(client, features, client->jid->user, client->password);
+						if (ret != IKS_OK) {
+							ASTOBJ_UNREF(client, aji_client_destroy);
+							return IKS_HOOK;
+						}
+						break;
 					}
-				} else if (!strcmp("failure", iks_name(node))) {
-					ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
-				} else if (!strcmp("success", iks_name(node))) {
-					client->authorized = 1;
-					iks_send_header(client->p, client->jid->server);
 				}
-				break;
+			} else if (!strcmp("failure", iks_name(node))) {
+				ast_log(LOG_ERROR, "JABBER: encryption failure. possible bad password.\n");
+			} else if (!strcmp("success", iks_name(node))) {
+				client->authorized = 1;
+				aji_send_header(client, client->jid->server);
 			}
+			break;
 		case IKS_NODE_ERROR: 
 				ast_log(LOG_ERROR, "JABBER: Node Error\n");
 				ASTOBJ_UNREF(client, aji_client_destroy);
@@ -628,15 +990,15 @@
 				handshake = NULL;
 				asprintf(&handshake, "<handshake>%s</handshake>", shasum);
 				if (handshake) {
-					iks_send_raw(client->p, handshake);
-					free(handshake);
+					aji_send_raw(client, handshake);
+					ast_free(handshake);
 					handshake = NULL;
 				}
 				client->state = AJI_CONNECTING;
-				if(iks_recv(client->p,1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
+				if(aji_recv(client, 1) == 2) /*XXX proper result for iksemel library on iks_recv of <handshake/> XXX*/
 					client->state = AJI_CONNECTED;
 				else
-					ast_log(LOG_WARNING,"Jabber didn't seem to handshake, failed to authenicate.\n");
+					ast_log(LOG_WARNING, "Jabber didn't seem to handshake, failed to authenticate.\n");
 				break;
 			}
 			break;
@@ -658,32 +1020,26 @@
 
 	switch (pak->type) {
 	case IKS_PAK_NONE:
-		if (option_debug)
-			ast_log(LOG_DEBUG, "JABBER: I Don't know what to do with you NONE\n");
+		ast_log(LOG_DEBUG, "JABBER: I don't know what to do with paktype NONE.\n");
 		break;
 	case IKS_PAK_MESSAGE:
 		aji_handle_message(client, pak);
-		if (option_debug)
-			ast_log(LOG_DEBUG, "JABBER: I Don't know what to do with you MESSAGE\n");
+		ast_log(LOG_DEBUG, "JABBER: Handling paktype MESSAGE.\n");
 		break;
 	case IKS_PAK_PRESENCE:
 		aji_handle_presence(client, pak);
-		if (option_debug)
-			ast_log(LOG_DEBUG, "JABBER: I Do know how to handle presence!!\n");
+		ast_log(LOG_DEBUG, "JABBER: Handling paktype PRESENCE\n");
 		break;
 	case IKS_PAK_S10N:
 		aji_handle_subscribe(client, pak);
-		if (option_debug)
-			ast_log(LOG_DEBUG, "JABBER: I Dont know S10N subscribe!!\n");
+		ast_log(LOG_DEBUG, "JABBER: Handling paktype S10N\n");
 		break;
 	case IKS_PAK_IQ:
-		if (option_debug)
-			ast_log(LOG_DEBUG, "JABBER: I Dont have an IQ!!!\n");
+		ast_log(LOG_DEBUG, "JABBER: Handling paktype IQ\n");
 		aji_handle_iq(client, node);
 		break;
 	default:
-		if (option_debug)
-			ast_log(LOG_DEBUG, "JABBER: I Dont know %i\n", pak->type);
+		ast_log(LOG_DEBUG, "JABBER: I don't know anything about paktype '%d'\n", pak->type);
 		break;
 	}
 	
@@ -695,7 +1051,12 @@
 	ASTOBJ_UNREF(client, aji_client_destroy);
 	return IKS_OK;
 }
-
+/*!
+ * \brief Uknown
+ * \param data void
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT.
+*/
 static int aji_register_approve_handler(void *data, ikspak *pak)
 {
 	struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
@@ -710,7 +1071,7 @@
 			iks_insert_attrib(iq, "to", pak->from->full);
 			iks_insert_attrib(iq, "id", pak->id);
 			iks_insert_attrib(iq, "type", "result");
-			iks_send(client->p, iq);
+			ast_aji_send(client, iq);
 
 			iks_insert_attrib(presence, "from", client->jid->full);
 			iks_insert_attrib(presence, "to", pak->from->partial);
@@ -719,7 +1080,7 @@
 			iks_insert_attrib(presence, "type", "subscribe");
 			iks_insert_attrib(x, "xmlns", "vcard-temp:x:update");
 			iks_insert_node(presence, x);
-			iks_send(client->p, presence); 
+			ast_aji_send(client, presence); 
 		}
 	} else {
 		ast_log(LOG_ERROR, "Out of memory.\n");
@@ -734,7 +1095,12 @@
 	ASTOBJ_UNREF(client, aji_client_destroy);
 	return IKS_FILTER_EAT;
 }
-
+/*!
+ * \brief register query
+ * \param data incoming aji_client request
+ * \param pak ikspak
+ * \return IKS_FILTER_EAT.
+*/
 static int aji_register_query_handler(void *data, ikspak *pak)
 {
 	struct aji_client *client = ASTOBJ_REF((struct aji_client *) data);
@@ -746,7 +1112,8 @@
 	buddy = ASTOBJ_CONTAINER_FIND(&client->buddies, pak->from->partial);
 	if (!buddy) {
 		iks *iq = NULL, *query = NULL, *error = NULL, *notacceptable = NULL;
-		ast_verbose("Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
+
+		ast_verbose(VERBOSE_PREFIX_3 "Someone.... %s tried to register but they aren't allowed\n", pak->from->partial);
 		iq = iks_new("iq");
 		query = iks_new("query");
 		error = iks_new("error");
@@ -763,7 +1130,7 @@
 			iks_insert_node(iq, query);
 			iks_insert_node(iq, error);

[... 1426 lines stripped ...]



More information about the asterisk-commits mailing list