[asterisk-commits] russell: branch russell/events r111242 - in /team/russell/events: include/ast...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 26 17:03:44 CDT 2008


Author: russell
Date: Wed Mar 26 17:03:44 2008
New Revision: 111242

URL: http://svn.digium.com/view/asterisk?view=rev&rev=111242
Log:
Commit everything from my working copy.  The most significant changes are
adding res_xmlevents, and associated supporting code in the event API.

res_xmlevents is what I started playing with while traveling this week.  It is
a socket API for monitoring internal Asterisk events that supports the following:
 - connecting via TCP and TLS
 - XML encoded interface
 - per-connection dynamic subscription support to allow clients to decide what
   events they receive

The biggest thing left to do is to add user accounts with access control and
authentication.

Added:
    team/russell/events/res/res_xmlevents.c
      - copied, changed from r110870, team/russell/events/res/res_xmlevents.c
Modified:
    team/russell/events/include/asterisk/event.h
    team/russell/events/include/asterisk/event_defs.h
    team/russell/events/include/asterisk/tcptls.h
    team/russell/events/main/devicestate.c
    team/russell/events/main/event.c
    team/russell/events/main/tcptls.c
    team/russell/events/res/res_ais.c

Modified: team/russell/events/include/asterisk/event.h
URL: http://svn.digium.com/view/asterisk/team/russell/events/include/asterisk/event.h?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/include/asterisk/event.h (original)
+++ team/russell/events/include/asterisk/event.h Wed Mar 26 17:03:44 2008
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2007, Digium, Inc.
+ * Copyright (C) 2007 - 2008, Digium, Inc.
  *
  * Russell Bryant <russell at digium.com>
  *
@@ -436,6 +436,10 @@
  */
 const void *ast_event_get_ie_raw(const struct ast_event *event, enum ast_event_ie_type ie_type);
 
+const char *ast_event_get_ie_type_name(enum ast_event_ie_type ie_type);
+
+enum ast_event_ie_pltype ast_event_get_ie_pltype(enum ast_event_ie_type ie_type);
+
 /*!
  * \brief Get the type for an event
  *
@@ -445,6 +449,12 @@
  *         ast_event_type enum
  */
 enum ast_event_type ast_event_get_type(const struct ast_event *event);
+
+const char *ast_event_get_type_name(const struct ast_event *event);
+
+int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type);
+
+int ast_event_str_to_ie_type(const char *str, enum ast_event_ie_type *ie_type);
 
 /*!
  * \brief Get the size of an event

Modified: team/russell/events/include/asterisk/event_defs.h
URL: http://svn.digium.com/view/asterisk/team/russell/events/include/asterisk/event_defs.h?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/include/asterisk/event_defs.h (original)
+++ team/russell/events/include/asterisk/event_defs.h Wed Mar 26 17:03:44 2008
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2007, Digium, Inc.
+ * Copyright (C) 2007 - 2008, Digium, Inc.
  *
  * Russell Bryant <russell at digium.com>
  *
@@ -30,26 +30,26 @@
 enum ast_event_type {
 	/*! Reserved to provide the ability to subscribe to all events.  A specific
 	 *  event should never have a payload of 0. */
-	AST_EVENT_ALL    = 0x00,
+	AST_EVENT_ALL                 = 0x00,
 	/*! This event type is reserved for use by third-party modules to create
 	 *  custom events without having to modify this file. 
 	 *  \note There are no "custom" IE types, because IEs only have to be
 	 *  unique to the event itself, not necessarily across all events. */
-	AST_EVENT_CUSTOM = 0x01,
+	AST_EVENT_CUSTOM              = 0x01,
 	/*! Voicemail message waiting indication */
-	AST_EVENT_MWI          = 0x02,
+	AST_EVENT_MWI                 = 0x02,
 	/*! Someone has subscribed to events */
-	AST_EVENT_SUB          = 0x03,
+	AST_EVENT_SUB                 = 0x03,
 	/*! Someone has unsubscribed from events */
-	AST_EVENT_UNSUB        = 0x04,
+	AST_EVENT_UNSUB               = 0x04,
 	/*! The aggregate state of a device across all servers configured to be
 	 *  a part of a device state cluster has changed. */
-	AST_EVENT_DEVICE_STATE = 0x05,
+	AST_EVENT_DEVICE_STATE        = 0x05,
 	/*! The state of a device has changed on _one_ server.  This should not be used
 	 *  directly, in general.  Use AST_EVENT_DEVICE_STATE instead. */
 	AST_EVENT_DEVICE_STATE_CHANGE = 0x06,
 	/*! Number of event types.  This should be the last event type + 1 */
-	AST_EVENT_TOTAL        = 0x07,
+	AST_EVENT_TOTAL               = 0x07,
 };
 
 /*! \brief Event Information Element types */
@@ -122,6 +122,8 @@
 	 AST_EVENT_IE_EID      = 0x0A,
 };
 
+#define AST_EVENT_IE_MAX AST_EVENT_IE_EID
+
 /*!
  * \brief Payload types for event information elements
  */
@@ -154,4 +156,15 @@
 struct ast_event_sub;
 struct ast_event_iterator;
 
+/*!
+ * \brief supposed to be an opaque type
+ *
+ * This is only here so that it can be declared on the stack.
+ */
+struct ast_event_iterator {
+	uint16_t event_len;
+	const struct ast_event *event;
+	struct ast_event_ie *ie;
+};
+
 #endif /* AST_EVENT_DEFS_H */

Modified: team/russell/events/include/asterisk/tcptls.h
URL: http://svn.digium.com/view/asterisk/team/russell/events/include/asterisk/tcptls.h?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/include/asterisk/tcptls.h (original)
+++ team/russell/events/include/asterisk/tcptls.h Wed Mar 26 17:03:44 2008
@@ -48,6 +48,8 @@
 
 #ifndef _ASTERISK_SERVER_H
 #define _ASTERISK_SERVER_H
+
+#include <fcntl.h>
 
 #include "asterisk/utils.h"
 
@@ -127,6 +129,7 @@
 	int client;
 	struct sockaddr_in requestor;
 	struct server_args *parent;
+	pthread_t worker_thread;
 };
 
 /*! \brief

Modified: team/russell/events/main/devicestate.c
URL: http://svn.digium.com/view/asterisk/team/russell/events/main/devicestate.c?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/main/devicestate.c (original)
+++ team/russell/events/main/devicestate.c Wed Mar 26 17:03:44 2008
@@ -1,9 +1,10 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 1999 - 2007, Digium, Inc.
+ * Copyright (C) 1999 - 2008, Digium, Inc.
  *
  * Mark Spencer <markster at digium.com>
+ * Russell Bryant <russell at digium.com>
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -21,11 +22,16 @@
  * \brief Device state management
  *
  * \author Mark Spencer <markster at digium.com> 
+ * \author Russell Bryant <russell at digium.com>
  *
  *	\arg \ref AstExtState
  */
 
 /*! \page AstExtState Extension and device states in Asterisk
+ *
+ * (Note that these descriptions of device states and extension
+ * states have not been updated to the way things work
+ * in Asterisk 1.6.)
  *
  *	Asterisk has an internal system that reports states
  *	for an extension. By using the dialplan priority -1,
@@ -417,6 +423,8 @@
 {
 	struct ast_event *event;
 
+	ast_debug(1, "device '%s' state '%d'\n", device, state);
+
 	if (!(event = ast_event_new(AST_EVENT_DEVICE_STATE_CHANGE,
 			AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
 			AST_EVENT_IE_STATE, AST_EVENT_IE_PLTYPE_UINT, state,
@@ -697,6 +705,8 @@
 
 	state = ast_devstate_aggregate_result(&agg);
 
+	ast_debug(1, "Aggregate devstate result is %d\n", state);
+
 	event = ast_event_get_cached(AST_EVENT_DEVICE_STATE,
 		AST_EVENT_IE_DEVICE, AST_EVENT_IE_PLTYPE_STR, device,
 		AST_EVENT_IE_END);

Modified: team/russell/events/main/event.c
URL: http://svn.digium.com/view/asterisk/team/russell/events/main/event.c?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/main/event.c (original)
+++ team/russell/events/main/event.c Wed Mar 26 17:03:44 2008
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2007, Digium, Inc.
+ * Copyright (C) 2007 - 2008, Digium, Inc.
  *
  * Russell Bryant <russell at digium.com>
  *
@@ -78,12 +78,6 @@
 	AST_LIST_ENTRY(ast_event_ref) entry;
 };
 
-struct ast_event_iterator {
-	uint16_t event_len;
-	const struct ast_event *event;
-	struct ast_event_ie *ie;
-};
-
 /*! \brief data shared between event dispatching threads */
 static struct {
 	ast_cond_t cond;
@@ -126,6 +120,107 @@
  * for events that express some sort of state.  So, when someone first
  * needs to know this state, it can get the last known state from the cache. */
 static AST_RWLIST_HEAD(ast_event_ref_list, ast_event_ref) ast_event_cache[AST_EVENT_TOTAL];
+
+/*!
+ * The index of each entry _must_ match the event type number!
+ */
+static struct event_name {
+	enum ast_event_type type;
+	const char *name;
+} event_names[] = {
+	{ 0, "" },
+	{ AST_EVENT_CUSTOM,              "Custom" },
+	{ AST_EVENT_MWI,                 "MWI" },
+	{ AST_EVENT_SUB,                 "Subscription" },
+	{ AST_EVENT_UNSUB,               "Unsubscription" },
+	{ AST_EVENT_DEVICE_STATE,        "DeviceState" },
+	{ AST_EVENT_DEVICE_STATE_CHANGE, "DeviceStateChange" },
+};
+
+/*!
+ * The index of each entry _must_ match the event ie number!
+ */
+static struct ie_map {
+	enum ast_event_ie_type ie_type;
+	enum ast_event_ie_pltype ie_pltype;
+	const char *name;
+} ie_maps[] = {
+	{ 0, 0, "" },
+	{ AST_EVENT_IE_NEWMSGS,   AST_EVENT_IE_PLTYPE_UINT, "NewMessages" },
+	{ AST_EVENT_IE_OLDMSGS,   AST_EVENT_IE_PLTYPE_UINT, "OldMessages" },
+	{ AST_EVENT_IE_MAILBOX,   AST_EVENT_IE_PLTYPE_STR,  "Mailbox" },
+	{ AST_EVENT_IE_UNIQUEID,  AST_EVENT_IE_PLTYPE_UINT, "UniqueID" },
+	{ AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, "EventType" },
+	{ AST_EVENT_IE_EXISTS,    AST_EVENT_IE_PLTYPE_UINT, "Exists" },
+	{ AST_EVENT_IE_DEVICE,    AST_EVENT_IE_PLTYPE_STR,  "Device" },
+	{ AST_EVENT_IE_STATE,     AST_EVENT_IE_PLTYPE_UINT, "State" },
+	{ AST_EVENT_IE_CONTEXT,   AST_EVENT_IE_PLTYPE_STR,  "Context" },
+	{ AST_EVENT_IE_EID,       AST_EVENT_IE_PLTYPE_RAW,  "EntityID" },
+};
+
+const char *ast_event_get_type_name(const struct ast_event *event)
+{
+	enum ast_event_type type;
+
+	type = ast_event_get_type(event);
+
+	if (type >= AST_EVENT_TOTAL || type < 0) {
+		ast_log(LOG_ERROR, "Invalid event type - '%d'\n", type);
+		return "";
+	}
+
+	return event_names[type].name;
+}
+
+int ast_event_str_to_event_type(const char *str, enum ast_event_type *event_type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(event_names); i++) {
+		if (strcasecmp(event_names[i].name, str))
+			continue;
+
+		*event_type = event_names[i].type;
+		return 0;
+	}
+
+	return -1;
+}
+
+const char *ast_event_get_ie_type_name(enum ast_event_ie_type ie_type)
+{
+	if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) {
+		ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type);
+		return "";
+	}
+
+	return ie_maps[ie_type].name;
+}
+
+enum ast_event_ie_pltype ast_event_get_ie_pltype(enum ast_event_ie_type ie_type)
+{
+	if (ie_type <= 0 || ie_type > AST_EVENT_IE_MAX) {
+		ast_log(LOG_ERROR, "Invalid IE type - '%d'\n", ie_type);
+		return AST_EVENT_IE_PLTYPE_UNKNOWN;
+	}
+
+	return ie_maps[ie_type].ie_pltype;
+}
+
+int ast_event_str_to_ie_type(const char *str, enum ast_event_ie_type *ie_type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_LEN(ie_maps); i++) {
+		if (strcasecmp(ie_maps[i].name, str))
+			continue;
+
+		*ie_type = ie_maps[i].ie_type;
+		return 0;
+	}
+
+	return -1;
+}
 
 size_t ast_event_get_size(const struct ast_event *event)
 {
@@ -365,7 +460,7 @@
 {
 	struct ast_event_sub *sub;
 
-	if (type >= AST_EVENT_TOTAL) {
+	if (type < 0 || type >= AST_EVENT_TOTAL) {
 		ast_log(LOG_ERROR, "%u is an invalid type!\n", type);
 		return NULL;
 	}
@@ -386,6 +481,9 @@
 {
 	struct ast_event_ie_val *ie_val;
 
+	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX)
+		return -1;
+
 	if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
 		return -1;
 
@@ -403,6 +501,9 @@
 {
 	struct ast_event_ie_val *ie_val;
 
+	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX)
+		return -1;
+
 	if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
 		return -1;
 
@@ -419,6 +520,9 @@
 {
 	struct ast_event_ie_val *ie_val;
 
+	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX)
+		return -1;
+
 	if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
 		return -1;
 
@@ -439,6 +543,9 @@
 	enum ast_event_ie_type ie_type, void *data, size_t raw_datalen)
 {
 	struct ast_event_ie_val *ie_val;
+
+	if (ie_type < 0 || ie_type > AST_EVENT_IE_MAX)
+		return -1;
 
 	if (!(ie_val = ast_calloc(1, sizeof(*ie_val))))
 		return -1;
@@ -547,7 +654,7 @@
 	struct ast_event *event;
 
 	AST_RWLIST_WRLOCK(&ast_event_subs[sub->type]);
-	AST_LIST_REMOVE(&ast_event_subs[sub->type], sub, entry);
+	AST_RWLIST_REMOVE(&ast_event_subs[sub->type], sub, entry);
 	AST_RWLIST_UNLOCK(&ast_event_subs[sub->type]);
 
 	if (ast_event_check_subscriber(AST_EVENT_UNSUB,
@@ -752,7 +859,7 @@
 	struct ast_event *dup_event;
 	uint16_t event_len;
 
-	event_len = ntohs(event->event_len);
+	event_len = ast_event_get_size(event);
 
 	if (!(dup_event = ast_calloc(1, event_len)))
 		return NULL;

Modified: team/russell/events/main/tcptls.c
URL: http://svn.digium.com/view/asterisk/team/russell/events/main/tcptls.c?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/main/tcptls.c (original)
+++ team/russell/events/main/tcptls.c Wed Mar 26 17:03:44 2008
@@ -106,7 +106,6 @@
 	struct sockaddr_in sin;
 	socklen_t sinlen;
 	struct ast_tcptls_session_instance *ser;
-	pthread_t launched;
 	
 	for (;;) {
 		int i, flags;
@@ -137,7 +136,7 @@
 
 		ser->client = 0;
 			
-		if (ast_pthread_create_detached_background(&launched, NULL, ast_make_file_from_fd, ser)) {
+		if (ast_pthread_create_detached_background(&ser->worker_thread, NULL, ast_make_file_from_fd, ser)) {
 			ast_log(LOG_WARNING, "Unable to launch helper thread: %s\n", strerror(errno));
 			close(ser->fd);
 			ast_free(ser);

Modified: team/russell/events/res/res_ais.c
URL: http://svn.digium.com/view/asterisk/team/russell/events/res/res_ais.c?view=diff&rev=111242&r1=111241&r2=111242
==============================================================================
--- team/russell/events/res/res_ais.c (original)
+++ team/russell/events/res/res_ais.c Wed Mar 26 17:03:44 2008
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2007, Digium, Inc.
+ * Copyright (C) 2007 - 2008, Digium, Inc.
  *
  * Russell Bryant <russell at digium.com>
  *

Copied: team/russell/events/res/res_xmlevents.c (from r110870, team/russell/events/res/res_xmlevents.c)
URL: http://svn.digium.com/view/asterisk/team/russell/events/res/res_xmlevents.c?view=diff&rev=111242&p1=team/russell/events/res/res_xmlevents.c&r1=110870&p2=team/russell/events/res/res_xmlevents.c&r2=111242
==============================================================================
--- team/russell/events/res/res_xmlevents.c (original)
+++ team/russell/events/res/res_xmlevents.c Wed Mar 26 17:03:44 2008
@@ -27,9 +27,16 @@
  * \todo subscription support instead of spamming all events
  */
 
+/*** MODULEINFO
+	<depend>xml2</depend>
+ ***/
+
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$");
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
 
 #include "asterisk/logger.h"
 #include "asterisk/module.h"
@@ -57,9 +64,19 @@
 	AST_LIST_ENTRY(pending_event) entry;
 };
 
+static unsigned int unique_sub_id;
+
+struct xml_event_sub {
+	unsigned int sub_id;
+	char *request_id;
+	struct ast_event_sub *sub;
+	unsigned int subscribed:1;
+	AST_LIST_ENTRY(xml_event_sub) entry;
+};
+
 struct xml_events_session {
-	struct ast_event_sub *sub;
 	struct ast_tcptls_session_instance *ser;
+	AST_LIST_HEAD_NOLOCK(, xml_event_sub) event_subs;
 	AST_LIST_HEAD_NOLOCK(, pending_event) pending_events;
 	unsigned int stop:1;
 };
@@ -90,10 +107,32 @@
 	.worker_fn = session_do,	/* thread handling the session */
 };
 
+static struct xml_event_sub *xml_event_sub_destroy(struct xml_event_sub *event_sub)
+{
+	if (event_sub->sub) {
+		if (event_sub->subscribed)
+			ast_event_unsubscribe(event_sub->sub);
+		else
+			ast_event_sub_destroy(event_sub->sub);
+
+		event_sub->sub = NULL;
+	}
+
+	if (event_sub->request_id) {
+		ast_free(event_sub->request_id);
+		event_sub->request_id = NULL;
+	}
+
+	ast_free(event_sub);
+
+	return NULL;
+}
+
 static void xml_events_session_destructor(void *obj)
 {
 	struct xml_events_session *session = obj;
 	struct pending_event *pending_event;
+	struct xml_event_sub *event_sub;
 
 	if (session->ser) {
 		fclose(session->ser->f);
@@ -101,13 +140,12 @@
 		session->ser = NULL;
 	}
 
-	if (session->sub) {
-		ast_event_unsubscribe(session->sub);
-		session->sub = NULL;
-	}
-
 	while ((pending_event = AST_LIST_REMOVE_HEAD(&session->pending_events, entry))) {
 		ast_free(pending_event);
+	}
+
+	while ((event_sub = AST_LIST_REMOVE_HEAD(&session->event_subs, entry))) {
+		xml_event_sub_destroy(event_sub);
 	}
 }
 
@@ -142,21 +180,12 @@
 
 static void begin_event_tag(struct ast_str **buf, const struct ast_event *event)
 {
-	const char *name;
-
-	ast_str_set(buf, 0, "<event type=\"%d\"", ast_event_get_type(event));
-
-	name = ast_event_get_type_name(event);
-	if (!ast_strlen_zero(name)) {
-		ast_str_append(buf, 0, " name=\"%s\"", name);
-	}
-
-	ast_str_append(buf, 0, ">\n");
+	ast_str_set(buf, 0, "<event name=\"%s\">\n", ast_event_get_type_name(event));
 }
 
 static void end_event_tag(struct ast_str **buf)
 {
-	ast_str_append(buf, 0, "</event>\n");
+	ast_str_append(buf, 0, "</event>\n\n");
 }
 
 static int handle_raw_ie(struct ast_str **buf, enum ast_event_ie_type ie_type, struct ast_event_iterator *iter)
@@ -183,16 +212,10 @@
 {
 	enum ast_event_ie_type ie_type;
 	enum ast_event_ie_pltype ie_pltype;
-	const char *name;
 
 	ie_type = ast_event_iterator_get_ie_type(iter);
 
-	ast_str_append(buf, 0, "   <ie type=\"%d\"", ie_type);
-
-	name = ast_event_get_ie_type_name(ie_type);
-	if (!ast_strlen_zero(name)) {
-		ast_str_append(buf, 0, " name=\"%s\"", name);
-	}
+	ast_str_append(buf, 0, "   <ie name=\"%s\"", ast_event_get_ie_type_name(ie_type));
 
 	ie_pltype = ast_event_get_ie_pltype(ie_type);
 	switch (ie_pltype) {
@@ -263,10 +286,212 @@
 	return 0;
 }
 
+static void finalize_parsing(xmlParserCtxtPtr ctxt)
+{
+	int errors;
+	char buf[1] = "";
+
+	/* Indicate that parsing is complete */
+	if ((errors = xmlParseChunk(ctxt, buf, 0, 1))) {
+		ast_debug(1, "Error(s) occurred when finalizing parsing: %d\n", errors);
+		return;
+	}
+}
+
+static int append_raw_ie_to_sub(struct ast_event_sub *sub, int type, xmlNode *ie_node)
+{
+	enum ast_event_ie_type ie_type = type;
+
+	switch (ie_type) {
+	case AST_EVENT_IE_EID:
+	{
+		struct ast_eid eid;
+
+		ast_str_to_eid(&eid, (const char *) ie_node->content);
+
+		return ast_event_sub_append_ie_raw(sub, type, &eid, sizeof(eid)) ? -1 : 0;
+	}
+	default:
+		return -1;
+	}
+}
+
+static int handle_subscription_ie(struct xml_event_sub *event_sub, xmlNode *ie_node)
+{
+	const char *attr_str;
+	enum ast_event_ie_type type;
+	enum ast_event_ie_pltype ie_pltype;
+	int res;
+
+	attr_str = (const char *) xmlGetProp(ie_node, (const xmlChar *) "name");
+	if (ast_strlen_zero(attr_str)) {
+		ast_log(LOG_ERROR, "Subscription received with no type.\n");
+		return -1;
+	}
+
+	if (ast_event_str_to_ie_type(attr_str, &type)) {
+		ast_log(LOG_ERROR, "Invalid IE type - '%s'\n", attr_str);
+		return -1;
+	}
+
+	if (ast_strlen_zero((const char *) ie_node->content)) {
+		return ast_event_sub_append_ie_exists(event_sub->sub, type) ? -1 : 0;
+	}
+
+	ie_pltype = ast_event_get_ie_pltype(type);
+	switch (ie_pltype) {
+	case AST_EVENT_IE_PLTYPE_UINT:
+	{
+		uint32_t payload;
+
+		if (sscanf((const char *) ie_node->content, "%u", &payload) != 1) {
+			ast_log(LOG_ERROR, "Invalid payload for IE\n");
+			return -1;
+		}
+
+		res = ast_event_sub_append_ie_uint(event_sub->sub, type, payload);
+	}
+	case AST_EVENT_IE_PLTYPE_STR:
+		res = ast_event_sub_append_ie_str(event_sub->sub, type, (const char *) ie_node->content);
+		break;
+	case AST_EVENT_IE_PLTYPE_RAW:
+		res = append_raw_ie_to_sub(event_sub->sub, type, ie_node);
+		break;
+	case AST_EVENT_IE_PLTYPE_UNKNOWN:
+		ast_log(LOG_WARNING, "Payload type unknown for IE of type '%d'\n", type);
+		/* fall through */
+	case AST_EVENT_IE_PLTYPE_EXISTS:
+		res = ast_event_sub_append_ie_exists(event_sub->sub, type);
+		break;
+	}
+
+	return res;
+}
+
+static void handle_unsubscribe(struct xml_events_session *session, xmlNode *root_node)
+{
+	const char *attr_str;
+	unsigned int id;
+	struct xml_event_sub *event_sub;
+
+	attr_str = (const char *) xmlGetProp(root_node, (const xmlChar *) "id");
+	if (ast_strlen_zero(attr_str)) {
+		ast_log(LOG_ERROR, "Subscription received with no type.\n");
+		return;
+	}
+
+	if (sscanf(attr_str, "%u", &id) != 1) {
+		ast_log(LOG_WARNING, "Subscription id not valid - '%d'\n", id);
+		return;
+	}
+
+	ao2_lock(session);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(&session->event_subs, event_sub, entry) {
+		if (event_sub->sub_id != id)
+			continue;
+
+		AST_LIST_REMOVE_CURRENT(entry);
+		event_sub = xml_event_sub_destroy(event_sub);
+		break;
+	}
+	AST_LIST_TRAVERSE_SAFE_END
+	ao2_unlock(session);
+}
+
+/*! \todo XXX Send failure responses, too */
+static void handle_subscribe(struct xml_events_session *session, xmlNode *root_node)
+{
+	xmlNode *cur_node;
+	const char *attr_str;
+	enum ast_event_type type;
+	struct xml_event_sub *event_sub;
+
+	attr_str = (const char *) xmlGetProp(root_node, (const xmlChar *) "name");
+	if (ast_strlen_zero(attr_str)) {
+		ast_log(LOG_ERROR, "Subscription received with no name.\n");
+		return;
+	}
+
+	if (ast_event_str_to_event_type(attr_str, &type)) {
+		ast_log(LOG_ERROR, "Invalid event type - '%s'\n", attr_str);
+		return;
+	}
+
+	if (!(event_sub = ast_calloc(1, sizeof(*event_sub)))) {
+		return;
+	}
+
+	event_sub->sub_id = ast_atomic_fetchadd_int((int *) &unique_sub_id, +1);
+
+	attr_str = (const char *) xmlGetProp(root_node, (const xmlChar *) "request_id");
+	if (!ast_strlen_zero(attr_str)) {
+		if (!(event_sub->request_id = ast_strdup(attr_str))) {
+			event_sub = xml_event_sub_destroy(event_sub);
+			return;
+		}
+	}
+
+	if (!(event_sub->sub = ast_event_subscribe_new(type, event_cb, session))) {
+		event_sub = xml_event_sub_destroy(event_sub);
+		return;
+	}
+
+	for (cur_node = root_node->children; cur_node; cur_node = cur_node->next) {
+		if (cur_node->type != XML_ELEMENT_NODE) {
+			ast_debug(1, "Came across something that wasn't a node, type '%d'\n", cur_node->type);
+			continue;
+		}
+
+		if (!strcasecmp((const char *) cur_node->name, "ie")) {
+			if (handle_subscription_ie(event_sub, cur_node)) {
+				event_sub = xml_event_sub_destroy(event_sub);
+				return;
+			}
+		} else {
+			ast_log(LOG_WARNING, "Came across unexpected node called '%s'\n", (const char *) cur_node->name);
+		}
+	}
+
+	if (ast_strlen_zero(event_sub->request_id)) {
+		fprintf(session->ser->f, "<subscription id=\"%u\"/>\n\n", event_sub->sub_id);
+	} else {
+		fprintf(session->ser->f, "<subscription id=\"%u\" request_id=\"%s\"/>\n\n",
+			event_sub->sub_id, event_sub->request_id);
+	}
+	fflush(session->ser->f);
+
+	ao2_lock(session);
+	ast_event_sub_activate(event_sub->sub);
+	event_sub->subscribed = 1;
+	AST_LIST_INSERT_TAIL(&session->event_subs, event_sub, entry);
+	ao2_unlock(session);
+}
+
+static void process_xml_request(struct xml_events_session *session, xmlDocPtr doc)
+{
+	xmlNode *cur_node;
+
+	for (cur_node = xmlDocGetRootElement(doc); cur_node; cur_node = cur_node->next) {
+		if (cur_node->type != XML_ELEMENT_NODE) {
+			ast_debug(1, "Came across something that wasn't a node, type '%d'\n", cur_node->type);
+			continue;
+		}
+
+		ast_debug(1, "node type: Element, name: %s\n", cur_node->name);
+
+		if (!strcasecmp((const char *) cur_node->name, "subscribe")) {
+			handle_subscribe(session, cur_node);
+		} else if (!strcasecmp((const char *) cur_node->name, "unsubscribe")) {
+			handle_unsubscribe(session, cur_node);
+		} else {
+			ast_log(LOG_WARNING, "Came across unexpected node called '%s'\n", (const char *) cur_node->name);
+		}
+	}
+}
+
 static void *session_do(void *data)
 {
 	struct xml_events_session *session;
-	int flags;
 
 	if (!(session = ao2_alloc(sizeof(*session), xml_events_session_destructor))) {
 		return NULL;
@@ -274,19 +499,11 @@
 
 	session->ser = data;
 
-	flags = fcntl(session->ser->fd, F_GETFL);
-	flags |= O_NONBLOCK;
-	fcntl(session->ser->fd, F_SETFL, flags);
-
-	/* XXX temporary hack.  Eventually add subscription support. */
-	session->sub = ast_event_subscribe(AST_EVENT_ALL, event_cb, session, AST_EVENT_IE_END);
-	if (!session->sub) {
-		session = unref_session(session);
-		return NULL;
-	}
-
 	while (!session->stop) {	
 		int res;
+		char buf[2048];
+		xmlParserCtxtPtr ctxt = NULL;
+		xmlDocPtr doc = NULL;
 
 		res = handle_events(session);
 		if (res < 0) {
@@ -307,20 +524,92 @@
 			continue;
 		}
 
-		res = fread(buf, 1, sizeof(buf), session->ser->f);
-		if (res < 1) {
-			ast_debug(1, "fread() returned < 1, breaking out of session loop.\n");
+		for (;;) {
+			int errors;
+			size_t len;
+
+			buf[0] = '\0';
+
+			fgets(buf, sizeof(buf), session->ser->f);
+			if (feof(session->ser->f)) {
+				break;
+			}
+
+			len = strlen(buf);
+
+			if (len && buf[len - 1] == '\n') {
+				buf[--len] = '\0';
+				if (len && buf[len - 1] == '\r') {
+					buf[--len] = '\0';
+				}
+			}
+
+			if (ast_strlen_zero(buf)) {
+				/* End of request */
+				ast_debug(1, "End of XML request\n");
+
+				if (!ctxt) {
+					ast_debug(1, "XML request ended, but there is no XML parsing context to work with ...\n");
+					break;
+				}
+			
+				ast_debug(1, "Finalizing parsing of XML request.\n");
+
+				finalize_parsing(ctxt);
+
+				if (!ctxt->wellFormed) {
+					ast_log(LOG_WARNING, "XML Request not properly formatted\n");
+					break;
+				}
+
+				ast_debug(1, "Processing received XML request.\n");
+
+				doc = ctxt->myDoc;
+				process_xml_request(session, doc);
+
+				break;
+			}
+			
+			ast_debug(1, "Got line: %s\n", buf);
+
+			if (!ctxt) {
+				if (!(ctxt = xmlCreatePushParserCtxt(NULL, NULL, buf, len, 
+					ast_inet_ntoa(session->ser->requestor.sin_addr)))) {
+					ast_log(LOG_WARNING, "Failed to create the XML parser context\n");
+					break;
+				}
+				ast_debug(1, "Created XML parser context\n");
+				continue;
+			}
+
+			if ((errors = xmlParseChunk(ctxt, buf, len, 0))) {
+				ast_debug(1, "Error(s) occurred when finalizing parsing: %d\n", errors);
+				break;
+			}
+		}
+
+		if (ctxt) {
+			ast_debug(1, "Destroying XML parser context.\n");
+			xmlFreeParserCtxt(ctxt);
+			ctxt = NULL;
+		}
+
+		if (doc) {
+			ast_debug(1, "Destroying XML document.\n");
+			xmlFreeDoc(doc);
+			doc = NULL;
+		}
+
+		if (feof(session->ser->f)) {
+			ast_debug(1, "Session EOF, tearing down\n");
 			break;
 		}
-
-		/* XXX Eventually support subscriptions ... */
 	}
 
 	session = unref_session(session);
 
 	return NULL;
 }
-
 
 static void store_port(const char *val, struct sockaddr_in *sin)
 {
@@ -440,6 +729,8 @@
 
 static int load_module(void)
 {
+    LIBXML_TEST_VERSION;
+
 	if (!(xml_events_sessions = ao2_container_alloc(NUM_SESSIONS_BUCKETS, 
 		xml_events_session_hash, xml_events_session_cmp))) {
 		return AST_MODULE_LOAD_DECLINE;




More information about the asterisk-commits mailing list