[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, "&", 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, "<", 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