[svn-commits] tilghman: branch group/xmlrpc r244497 - /team/group/xmlrpc/funcs/

SVN commits to the Digium repositories svn-commits at lists.digium.com
Tue Feb 2 19:40:57 CST 2010


Author: tilghman
Date: Tue Feb  2 19:40:54 2010
New Revision: 244497

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=244497
Log:
Move function into func_curl, for use of the curl library

Removed:
    team/group/xmlrpc/funcs/func_xmlrpc.c
Modified:
    team/group/xmlrpc/funcs/func_curl.c

Modified: team/group/xmlrpc/funcs/func_curl.c
URL: http://svnview.digium.com/svn/asterisk/team/group/xmlrpc/funcs/func_curl.c?view=diff&rev=244497&r1=244496&r2=244497
==============================================================================
--- team/group/xmlrpc/funcs/func_curl.c (original)
+++ team/group/xmlrpc/funcs/func_curl.c Tue Feb  2 19:40:54 2010
@@ -1,7 +1,7 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C)  2004 - 2006, Tilghman Lesher
+ * Copyright (C)  2004 - 2010, Tilghman Lesher
  *
  * Tilghman Lesher <curl-20050919 at the-tilghman.com>
  * and Brian Wilkins <bwilkins at cfl.rr.com> (Added POST option)
@@ -55,11 +55,25 @@
 
 #define CURLOPT_SPECIAL_HASHCOMPAT -500
 
+/* Constants */
+static const char *xmlrpc_config = "func_xmlrpc.conf";
+static const char * const global_useragent = "asterisk-libcurl-agent/1.0";
+
+/* Prototypes */
 static void curlds_free(void *data);
-
-static struct ast_datastore_info curl_info = {
-	.type = "CURL",
-	.destroy = curlds_free,
+static int curl_instance_init(void *data);
+static void curl_instance_cleanup(void *data);
+static int xmlrpc_instance_init(void *data);
+
+/* Structures */
+struct acf_xmlrpc_query {
+	AST_RWLIST_ENTRY(acf_xmlrpc_query) list;
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(url);
+		AST_STRING_FIELD(method);
+		AST_STRING_FIELD(params);
+	);
+	struct ast_custom_function acf;
 };
 
 struct curl_settings {
@@ -67,21 +81,6 @@
 	CURLoption key;
 	void *value;
 };
-
-AST_LIST_HEAD_STATIC(global_curl_info, curl_settings);
-
-static void curlds_free(void *data)
-{
-	AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
-	struct curl_settings *setting;
-	if (!list) {
-		return;
-	}
-	while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
-		free(setting);
-	}
-	AST_LIST_HEAD_DESTROY(list);
-}
 
 enum optiontype {
 	OT_BOOLEAN,
@@ -90,6 +89,34 @@
 	OT_STRING,
 	OT_ENUM,
 };
+
+/* Static storage */
+AST_LIST_HEAD_STATIC(global_curl_info, curl_settings);
+static AST_RWLIST_HEAD_STATIC(queries, acf_xmlrpc_query);
+
+AST_THREADSTORAGE(query_buf);
+AST_THREADSTORAGE(value_buf);
+AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
+AST_THREADSTORAGE_CUSTOM(xmlrpc_instance, xmlrpc_instance_init, curl_instance_cleanup);
+
+static struct ast_datastore_info curl_info = {
+	.type = "CURL",
+	.destroy = curlds_free,
+};
+
+/* Function definitions */
+static void curlds_free(void *data)
+{
+	AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
+	struct curl_settings *setting;
+	if (!list) {
+		return;
+	}
+	while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
+		free(setting);
+	}
+	AST_LIST_HEAD_DESTROY(list);
+}
 
 static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
 {
@@ -416,8 +443,6 @@
 	return realsize;
 }
 
-static const char * const global_useragent = "asterisk-libcurl-agent/1.0";
-
 static int curl_instance_init(void *data)
 {
 	CURL **curl = data;
@@ -442,7 +467,20 @@
 	ast_free(data);
 }
 
-AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
+static int xmlrpc_instance_init(void *data)
+{
+	CURL **curl = data;
+
+	if (!(*curl = curl_easy_init()))
+		return -1;
+
+	curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
+	curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 5);
+	curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
+	curl_easy_setopt(*curl, CURLOPT_POST, 1);
+
+	return 0;
+}
 
 static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
 {
@@ -611,19 +649,643 @@
 	.write = acf_curlopt_write,
 };
 
+static char *encode_xml(struct ast_str *result, int maxlen, const char *input)
+{
+	const char *c;
+	ast_str_reset(result);
+	for (c = input; c && *c; c++) {
+		const char *amp = strchr(c, '&');
+		const char *lt = strchr(c, '<');
+		if ((amp && lt && amp < lt) || (amp && !lt)) {
+			/* Copy up to the first ampersand */
+			ast_str_append_substr(&result, maxlen, c, amp - c);
+			ast_str_append_substr(&result, maxlen, "&amp;", 5);
+			c = amp;
+		} else if ((amp && lt && lt < amp) || (lt && !amp)) {
+			/* Copy up to the first less-than-sign */
+			ast_str_append_substr(&result, maxlen, c, lt - c);
+			ast_str_append_substr(&result, maxlen, "&lt;", 4);
+			c = lt;
+		} else {
+			ast_str_append_substr(&result, maxlen, c, strlen(c));
+			break;
+		}
+	}
+	return ast_str_buffer(result);
+}
+
+static int acf_xmlrpc_read(struct ast_channel *chan, const char *cmd, char *s, struct ast_str *buf, int len)
+{
+	struct acf_xmlrpc_query *query;
+	char varname[15], rowcount[12] = "-1", *template = "/tmp/xmlrpc-XXXXXX";
+	int res, buflen = 0, bogus_chan = 0;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(field)[100];
+	);
+	struct ast_str *qbuf = ast_str_thread_get(&query_buf, 16);
+	struct ast_str *value = ast_str_thread_get(&value_buf, 16);
+	const char *status = "FAILURE";
+	CURL **curl;
+	struct curl_settings *curl_cur;
+	struct ast_datastore *store = NULL;
+	int hashcompat = 0, fd;
+	FILE *f;
+	AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
+	struct curl_slist *slist = NULL;
+	struct ast_xml_doc *xml;
+	struct ast_xml_node *root, *cur;
+
+	if (!qbuf) {
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	}
+
+	ast_str_reset(colnames);
+
+	AST_RWLIST_RDLOCK(&queries);
+	AST_RWLIST_TRAVERSE(&queries, query, list) {
+		if (!strcmp(query.acf->name, cmd)) {
+			break;
+		}
+	}
+
+	if (!query) {
+		ast_log(LOG_ERROR, "No such function '%s'\n", cmd);
+		AST_RWLIST_UNLOCK(&queries);
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	}
+
+	if (!(curl = ast_threadstorage_get(&xmlrpc_instance, sizeof(*curl)))) {
+		AST_RWLIST_UNLOCK(&queries);
+		ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	}
+
+	if (!(slist = curl_slist_append(slist, "Content-Type: text/xml"))) {
+		AST_RWLIST_UNLOCK(&queries);
+		ast_log(LOG_ERROR, "Cannot set content type\n");
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	}
+	if (!(slist2 = curl_slist_append(slist, "Expect:"))) {
+		AST_RWLIST_UNLOCK(&queries);
+		ast_log(LOG_ERROR, "Cannot clear Expect header\n");
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	} else {
+		slist = slist2;
+	}
+
+	if (!(fd = mkstemp(template))) {
+		AST_RWLIST_UNLOCK(&queries);
+		ast_log(LOG_ERROR, "Cannot create temporary file: %s\n", strerror(errno));
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	}
+
+	if (!(f = fdopen(fd, "w+"))) {
+		AST_RWLIST_UNLOCK(&queries);
+		ast_log(LOG_ERROR, "Cannot create file point for temporary file: %s\n", strerror(errno));
+		close(fd);
+		unlink(template);
+		if (chan) {
+			pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		}
+		return -1;
+	}
+
+	if (!chan) {
+		if (!(chan = ast_dummy_channel_alloc())) {
+			AST_RWLIST_UNLOCK(&queries);
+			fclose(f);
+			unlink(template);
+			return -1;
+		}
+		bogus_chan = 1;
+	}
+
+	if (!bogus_chan) {
+		ast_autoservice_start(chan);
+	}
+
+	AST_LIST_LOCK(&global_curl_info);
+	AST_LIST_TRAVERSE(&global_curl_info, curl_cur, list) {
+		if (curl_cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
+			hashcompat = (curl_cur->value != NULL) ? 1 : 0;
+		} else {
+			curl_easy_setopt(*curl, curl_cur->key, curl_cur->value);
+		}
+	}
+
+	if (!bogus_chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
+		list = store->data;
+		AST_LIST_LOCK(list);
+		AST_LIST_TRAVERSE(list, cur, list) {
+			if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
+				hashcompat = (cur->value != NULL) ? 1 : 0;
+			} else {
+				curl_easy_setopt(*curl, cur->key, cur->value);
+			}
+		}
+	}
+
+	curl_easy_setopt(*curl, CURLOPT_URL, query->url);
+	/* Output XML to our temporary file */
+	curl_easy_setopt(*curl, CURLOPT_FILE, f);
+
+	AST_STANDARD_APP_ARGS(args, s);
+	for (x = 0; x < args.argc; x++) {
+		char *c;
+		snprintf(varname, sizeof(varname), "ARG%d", x + 1);
+		pbx_builtin_pushvar_helper(chan, varname, encode_xml(value, 0, args.field[x]));
+	}
+
+	ast_str_substitute_variables(&qbuf, 0, chan, query->params);
+
+	if (bogus_chan) {
+		chan = ast_channel_release(chan);
+	} else {
+		/* Restore prior values */
+		for (x = 0; x < args.argc; x++) {
+			snprintf(varname, sizeof(varname), "ARG%d", x + 1);
+			pbx_builtin_setvar_helper(chan, varname, NULL);
+		}
+	}
+
+	/* Create final post value */
+	ast_str_set(&value, 0, "<?xml version=\"1.0\"?><methodCall><methodName>%s</methodName><params>%s</params></methodCall>",
+			encode_xml(value, 0, query->method), ast_str_buffer(qbuf));
+	curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, ast_str_buffer(qbuf));
+
+	ast_str_set(&value, 0, "Content-length: %d", ast_str_strlen(qbuf));
+	if (!(slist2 = curl_slist_append(slist, ast_str_buffer(value)))) {
+		ast_log(LOG_WARNING, "Cannot set Content-length\n");
+	} else {
+		slist = slist2;
+	}
+	curl_easy_setopt(*curl, CURLOPT_HTTPHEADER, slist);
+
+	curl_easy_perform(*curl);
+
+	if (store) {
+		AST_LIST_UNLOCK(list);
+	}
+	AST_LIST_UNLOCK(&global_curl_info);
+	AST_RWLIST_UNLOCK(&queries);
+
+	/* Decode XML */
+	if (!(xml = ast_xml_open(template))) {
+		goto end_acf_read:
+	}
+	if (!(root = ast_xml_get_root(xml))) {
+		goto end_acf_read;
+	}
+	if (!(cur = ast_xml_find_element(root, "params", NULL, NULL))) {
+		/* No results */
+		if (!(cur = ast_xml_find_element(root, "fault", NULL, NULL))) {
+			/* TODO Retrieve fault */
+		}
+		goto end_acf_read;
+	}
+
+	/* TODO Retrieve values and encode for output */
+
+end_acf_read:
+	if (xml) {
+		ast_xml_close(xml);
+	}
+	fclose(f);
+	unlink(template);
+
+	if (!bogus_chan) {
+		pbx_builtin_setvar_helper(chan, "XMLRPCSTATUS", status);
+		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(colnames));
+		ast_autoservice_stop(chan);
+	}
+
+	curl_slist_free_all(slist);
+	return 0;
+}
+
+static int init_xmlrpc_query(struct ast_config *cfg, char *catg, struct acf_xmlrpc_query **query)
+{
+	const char *tmp;
+	int i;
+
+	if (!cfg || !catg) {
+		return EINVAL;
+	}
+
+	if (!(*query = ast_calloc(1, sizeof(struct acf_xmlrpc_query)))) {
+		return ENOMEM;
+	}
+
+	if (ast_string_field_init((*query), 128)) {
+		ast_free(*query);
+		*query = NULL;
+		return ENOMEM;
+	}
+
+	if (((tmp = ast_variable_retrieve(cfg, catg, "url")))) {
+		ast_string_field_set((*query), url, tmp);
+	} else {
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return EINVAL;
+	}
+
+	if ((tmp = ast_variable_retrieve(cfg, catg, "method"))) {
+		ast_string_field_set((*query), method, tmp);
+	} else {
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return EINVAL;
+	}
+
+	if ((tmp = ast_variable_retrieve(cfg, catg, "params")))
+		ast_string_field_set((*query), params, tmp);
+	} else {
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return EINVAL;
+	}
+
+	if (ast_string_field_init((*query).acf, 128)) {
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return ENOMEM;
+	}
+
+	if (!(tmp = ast_variable_retrieve(cfg, catg, "prefix")) || ast_strlen_zero(tmp)) {
+		tmp = "XMLRPC";
+		if (ast_string_field_build((*query).acf, name, "%s_%s", tmp, catg)) {
+			ast_string_field_free_memory(*query);
+			ast_free(*query);
+			*query = NULL;
+			return ENOMEM;
+		}
+	}
+
+	if ((tmp = ast_variable_retrieve(cfg, catg, "syntax")) && !ast_strlen_zero(tmp)) {
+		ast_string_field_build((*query).acf, syntax, "%s(%s)", (*query).acf->name, tmp);
+	} else {
+		ast_string_field_build((*query).acf, syntax, "%s(<arg1>[...[,<argN>]])", (*query).acf->name);
+	}
+
+	if (ast_strlen_zero((*query).acf->syntax)) {
+		ast_string_field_free_memory((*query).acf);
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return ENOMEM;
+	}
+
+	if (!(tmp = ast_variable_retrieve(cfg, catg, "synopsis")) || ast_strlen_zero(tmp)) {
+		tmp = "Runs the referenced query with the specified arguments";
+		ast_string_field_set((*query)->acf, synopsis, tmp);
+	}
+
+	if (ast_strlen_zero((*query)->acf->synopsis)) {
+		ast_string_field_free_memory((*query).acf);
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return ENOMEM;
+	}
+
+	ast_string_field_build((*query)->acf, desc,
+				"Runs the following query, as defined in func_xmlrpc.conf, performing\n"
+				"substitution of the arguments into the query as specified by ${ARG1},\n"
+				"${ARG2}, ... ${ARGn}.  When setting the function, the values are provided\n"
+				"either in whole as ${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n"
+				"\nRead: %s\n%s\n",
+				(*query)->method,
+				(*query)->url);
+
+	if (ast_strlen_zero((*query).acf->desc)) {
+		ast_string_field_free_memory((*query).acf);
+		ast_string_field_free_memory(*query);
+		ast_free(*query);
+		*query = NULL;
+		return ENOMEM;
+	}
+
+	(*query).acf->read = acf_xmlrpc_read;
+
+	return 0;
+}
+
+static int free_acf_query(struct acf_xmlrpc_query *query)
+{
+	if (query) {
+		if (query->acf) {
+			ast_string_field_free_memory(query->acf);
+			ast_string_field_free_memory(query);
+		}
+		ast_free(query);
+	}
+	return 0;
+}
+
+static char *cli_xmlrpc_read(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(field)[100];
+	);
+	struct ast_str *query;
+	char *char_args, varname[10];
+	struct acf_xmlrpc_query *query;
+	struct ast_channel *chan;
+	int i;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "xmlrpc read";
+		e->usage =
+			"Usage: xmlrpc read <name> <args> [exec]\n"
+			"       Evaluates the SQL provided in the ODBC function <name>, and\n"
+			"       optionally executes the function.  This function is intended for\n"
+			"       testing purposes.  Remember to quote arguments containing spaces.\n";
+		return NULL;
+	case CLI_GENERATE:
+		if (a->pos == 2) {
+			int wordlen = strlen(a->word), which = 0;
+			/* Complete function name */
+			AST_RWLIST_RDLOCK(&queries);
+			AST_RWLIST_TRAVERSE(&queries, query, list) {
+				if (!strncasecmp(query->acf->name, a->word, wordlen)) {
+					if (++which > a->n) {
+						char *res = ast_strdup(query->acf->name);
+						AST_RWLIST_UNLOCK(&queries);
+						return res;
+					}
+				}
+			}
+			AST_RWLIST_UNLOCK(&queries);
+			return NULL;
+		} else if (a->pos == 4) {
+			return a->n == 0 ? ast_strdup("exec") : NULL;
+		} else {
+			return NULL;
+		}
+	}
+
+	if (a->argc < 4 || a->argc > 5) {
+		return CLI_SHOWUSAGE;
+	}
+
+	query = ast_str_thread_get(&query_buf, 16);
+	if (!query) {
+		return CLI_FAILURE;
+	}
+
+	AST_RWLIST_RDLOCK(&queries);
+	AST_RWLIST_TRAVERSE(&queries, query, list) {
+		if (!strcmp(query->acf->name, a->argv[2])) {
+			break;
+		}
+	}
+
+	if (!query) {
+		ast_cli(a->fd, "No such query '%s'\n", a->argv[2]);
+		AST_RWLIST_UNLOCK(&queries);
+		return CLI_SHOWUSAGE;
+	}
+
+	if (ast_strlen_zero(query->params)) {
+		ast_cli(a->fd, "The function %s has no writequery parameter.\n", a->argv[2]);
+		AST_RWLIST_UNLOCK(&queries);
+		return CLI_SUCCESS;
+	}
+
+	ast_str_make_space(&query, strlen(query->params) * 2 + 300);
+
+	/* Evaluate function */
+	char_args = ast_strdupa(a->argv[3]);
+
+	chan = ast_dummy_channel_alloc();
+
+	AST_STANDARD_APP_ARGS(args, char_args);
+	for (i = 0; i < args.argc; i++) {
+		snprintf(varname, sizeof(varname), "ARG%d", i + 1);
+		pbx_builtin_pushvar_helper(chan, varname, args.field[i]);
+	}
+
+	ast_str_substitute_variables(&query, 0, chan, query->params);
+	chan = ast_channel_release(chan);
+
+	if (a->argc == 5 && !strcmp(a->argv[4], "exec")) {
+		/* Execute the query */
+		struct xmlrpc_obj *obj = NULL;
+		int dsn, executed = 0;
+		SQLHSTMT stmt;
+		int rows = 0, res, x;
+		SQLSMALLINT colcount = 0, collength;
+		SQLLEN indicator;
+		struct ast_str *coldata = ast_str_thread_get(&coldata_buf, 16);
+		char colname[256];
+		SQLULEN maxcol;
+
+		for (dsn = 0; dsn < 5; dsn++) {
+			if (ast_strlen_zero(query->readhandle[dsn])) {
+				continue;
+			}
+			ast_debug(1, "Found handle %s\n", query->readhandle[dsn]);
+			if (!(obj = ast_xmlrpc_request_obj(query->readhandle[dsn], 0))) {
+				continue;
+			}
+
+			ast_debug(1, "Got obj\n");
+			if (!(stmt = ast_xmlrpc_direct_execute(obj, generic_execute, ast_str_buffer(query)))) {
+				ast_xmlrpc_release_obj(obj);
+				obj = NULL;
+				continue;
+			}
+
+			executed = 1;
+
+			res = SQLNumResultCols(stmt, &colcount);
+			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+				ast_cli(a->fd, "SQL Column Count error!\n[%s]\n\n", ast_str_buffer(query));
+				SQLCloseCursor(stmt);
+				SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+				ast_xmlrpc_release_obj(obj);
+				obj = NULL;
+				AST_RWLIST_UNLOCK(&queries);
+				return CLI_SUCCESS;
+			}
+
+			res = SQLFetch(stmt);
+			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+				SQLCloseCursor(stmt);
+				SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+				ast_xmlrpc_release_obj(obj);
+				obj = NULL;
+				if (res == SQL_NO_DATA) {
+					ast_cli(a->fd, "Returned %d rows.  Query executed on handle %d:%s [%s]\n", rows, dsn, query->readhandle[dsn], ast_str_buffer(query));
+					break;
+				} else {
+					ast_cli(a->fd, "Error %d in FETCH [%s]\n", res, ast_str_buffer(query));
+				}
+				AST_RWLIST_UNLOCK(&queries);
+				return CLI_SUCCESS;
+			}
+			for (;;) {
+				for (x = 0; x < colcount; x++) {
+					res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, &maxcol, NULL, NULL);
+					if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) {
+						snprintf(colname, sizeof(colname), "field%d", x);
+					}
+
+					res = ast_xmlrpc_ast_str_SQLGetData(&coldata, maxcol, stmt, x + 1, SQL_CHAR, &indicator);
+					if (indicator == SQL_NULL_DATA) {
+						ast_str_set(&coldata, 0, "(nil)");
+						res = SQL_SUCCESS;
+					}
+
+					if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+						ast_cli(a->fd, "SQL Get Data error %d!\n[%s]\n\n", res, ast_str_buffer(query));
+						SQLCloseCursor(stmt);
+						SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+						ast_xmlrpc_release_obj(obj);
+						obj = NULL;
+						AST_RWLIST_UNLOCK(&queries);
+						return CLI_SUCCESS;
+					}
+
+					ast_cli(a->fd, "%-20.20s  %s\n", colname, ast_str_buffer(coldata));
+				}
+				rows++;
+
+				/* Get next row */
+				res = SQLFetch(stmt);
+				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+					break;
+				}
+				ast_cli(a->fd, "%-20.20s  %s\n", "----------", "----------");
+			}
+			SQLCloseCursor(stmt);
+			SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+			ast_xmlrpc_release_obj(obj);
+			obj = NULL;
+			ast_cli(a->fd, "Returned %d row%s.  Query executed on handle %d [%s]\n", rows, rows == 1 ? "" : "s", dsn, query->readhandle[dsn]);
+			break;
+		}
+		if (obj) {
+			ast_xmlrpc_release_obj(obj);
+			obj = NULL;
+		}
+
+		if (!executed) {
+			ast_cli(a->fd, "Failed to execute query. [%s]\n", ast_str_buffer(query));
+		}
+	} else { /* No execution, just print out the resulting SQL */
+		ast_cli(a->fd, "%s\n", ast_str_buffer(query));
+	}
+	AST_RWLIST_UNLOCK(&queries);
+	return CLI_SUCCESS;
+}
+
+static struct ast_cli_entry cli_func_xmlrpc[] = {
+	AST_CLI_DEFINE(cli_xmlrpc_read, "Test reading a func_xmlrpc function"),
+};
+
+static int reload(void)
+{
+	int res = 0;
+	struct ast_config *cfg;
+	struct acf_xmlrpc_query *oldquery;
+	char *catg;
+	struct ast_flags config_flags = { CONFIG_FLAG_FILEUNCHANGED };
+
+	cfg = ast_config_load(xmlrpc_config, config_flags);
+	if (cfg == CONFIG_STATUS_FILEUNCHANGED || cfg == CONFIG_STATUS_FILEINVALID)
+		return 0;
+
+	AST_RWLIST_WRLOCK(&queries);
+
+	while (!AST_RWLIST_EMPTY(&queries)) {
+		oldquery = AST_RWLIST_REMOVE_HEAD(&queries, list);
+		ast_custom_function_unregister(oldquery->acf);
+		free_acf_query(oldquery);
+	}
+
+	if (!cfg) {
+		ast_log(LOG_WARNING, "Unable to load config for func_xmlrpc: %s\n", xmlrpc_config);
+		goto reload_out;
+	}
+
+	for (catg = ast_category_browse(cfg, NULL);
+	     catg;
+	     catg = ast_category_browse(cfg, catg)) {
+		struct acf_xmlrpc_query *query = NULL;
+
+		if (init_xmlrpc_query(cfg, catg, &query)) {
+			ast_log(LOG_ERROR, "Cannot initialize query %s\n", catg);
+		} else {
+			AST_RWLIST_INSERT_HEAD(&queries, query, list);
+			ast_custom_function_register(query->acf);
+		}
+	}
+
+	ast_config_destroy(cfg);
+reload_out:
+	AST_RWLIST_UNLOCK(&queries);
+	return res;
+}
+
+
 static int unload_module(void)
 {
 	int res;
-
+	struct acf_xmlrpc_query *query;
+
+	AST_RWLIST_WRLOCK(&queries);
+	while (!AST_RWLIST_EMPTY(&queries)) {
+		query = AST_RWLIST_REMOVE_HEAD(&queries, list);
+		ast_custom_function_unregister(query->acf);
+		free_acf_query(query);
+	}
+
+	ast_cli_unregister_multiple(cli_func_xmlrpc, ARRAY_LEN(cli_func_xmlrpc));
 	res = ast_custom_function_unregister(&acf_curl);
 	res |= ast_custom_function_unregister(&acf_curlopt);
 
-	return res;
+	/* Allow any threads waiting for this lock to pass (avoids a race) */
+	AST_RWLIST_UNLOCK(&queries);
+	usleep(1);
+	AST_RWLIST_WRLOCK(&queries);
+
+	AST_RWLIST_UNLOCK(&queries);
+	return 0;
 }
 
 static int load_module(void)
 {
 	int res;
+	struct ast_config *cfg;
+	char *catg;
+	struct ast_flags config_flags = { 0 };
+
+	AST_RWLIST_WRLOCK(&queries);
 
 	if (!ast_module_check("res_curl.so")) {
 		if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
@@ -632,11 +1294,48 @@
 		}
 	}
 
+	cfg = ast_config_load(xmlrpc_config, config_flags);
+	if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
+		ast_log(LOG_NOTICE, "Unable to load config for func_xmlrpc: %s\n", xmlrpc_config);
+		AST_RWLIST_UNLOCK(&queries);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	for (catg = ast_category_browse(cfg, NULL);
+	     catg;
+	     catg = ast_category_browse(cfg, catg)) {
+		struct acf_xmlrpc_query *query = NULL;
+		int err;
+
+		if ((err = init_xmlrpc_query(cfg, catg, &query))) {
+			if (err == ENOMEM) {
+				ast_log(LOG_ERROR, "Out of memory\n");
+			} else if (err == EINVAL) {
+				ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg);
+			} else {
+				ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err);
+			}
+		} else {
+			AST_RWLIST_INSERT_HEAD(&queries, query, list);
+			ast_custom_function_register(query->acf);
+		}
+	}
+
+	ast_config_destroy(cfg);
+
+	AST_RWLIST_UNLOCK(&queries);
+
 	res = ast_custom_function_register(&acf_curl);
 	res |= ast_custom_function_register(&acf_curlopt);
+	ast_cli_register_multiple(cli_func_xmlrpc, ARRAY_LEN(cli_func_xmlrpc));
 
 	return res;
 }
 
-AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Load external URL");
-
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "External URL/XMLRPC lookups",
+		.load = load_module,
+		.unload = unload_module,
+		.reload = reload,
+	       );
+
+




More information about the svn-commits mailing list