[asterisk-commits] tilghman: trunk r141328 - in /trunk: CHANGES funcs/func_curl.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Sep 5 14:12:03 CDT 2008


Author: tilghman
Date: Fri Sep  5 14:12:03 2008
New Revision: 141328

URL: http://svn.digium.com/view/asterisk?view=rev&rev=141328
Log:
Add the CURLOPT dialplan function, which permits setting various options for
use with the CURL dialplan function.
(closes issue #12920)
 Reported by: davevg
 Patches: 
       20080904__bug12920.diff.txt uploaded by Corydon76 (license 14)
 Tested by: Corydon76, davevg

Modified:
    trunk/CHANGES
    trunk/funcs/func_curl.c

Modified: trunk/CHANGES
URL: http://svn.digium.com/view/asterisk/trunk/CHANGES?view=diff&rev=141328&r1=141327&r2=141328
==============================================================================
--- trunk/CHANGES (original)
+++ trunk/CHANGES Fri Sep  5 14:12:03 2008
@@ -2,7 +2,11 @@
 --- Functionality changes from Asterisk 1.6.1 to Asterisk 1.6.2  -------------
 ------------------------------------------------------------------------------
 
-* Nothing, yet!
+Dialplan Functions
+------------------
+ * Added a new dialplan function, CURLOPT, which permits setting various
+   options that may be useful with the CURL dialplan function, such as
+   cookies, proxies, connection timeouts, passwords, etc.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 1.6.0 to Asterisk 1.6.1  -------------

Modified: trunk/funcs/func_curl.c
URL: http://svn.digium.com/view/asterisk/trunk/funcs/func_curl.c?view=diff&rev=141328&r1=141327&r2=141328
==============================================================================
--- trunk/funcs/func_curl.c (original)
+++ trunk/funcs/func_curl.c Fri Sep  5 14:12:03 2008
@@ -50,15 +50,313 @@
 #include "asterisk/utils.h"
 #include "asterisk/threadstorage.h"
 
+#define CURLVERSION_ATLEAST(a,b,c) \
+	((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
+
+static void curlds_free(void *data);
+
+static struct ast_datastore_info curl_info = {
+	.type = "CURL",
+	.destroy = curlds_free,
+};
+
+struct curl_settings {
+	AST_LIST_ENTRY(curl_settings) list;
+	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 *cur;
+	if (!list) {
+		return;
+	}
+	while ((cur = AST_LIST_REMOVE_HEAD(list, list))) {
+		free(cur);
+	}
+	AST_LIST_HEAD_DESTROY(list);
+}
+
+enum optiontype {
+	OT_BOOLEAN,
+	OT_INTEGER,
+	OT_INTEGER_MS,
+	OT_STRING,
+	OT_ENUM,
+};
+
+static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
+{
+	if (!strcasecmp(name, "header")) {
+		*key = CURLOPT_HEADER;
+		*ot = OT_BOOLEAN;
+	} else if (!strcasecmp(name, "proxy")) {
+		*key = CURLOPT_PROXY;
+		*ot = OT_STRING;
+	} else if (!strcasecmp(name, "proxyport")) {
+		*key = CURLOPT_PROXYPORT;
+		*ot = OT_INTEGER;
+	} else if (!strcasecmp(name, "proxytype")) {
+		*key = CURLOPT_PROXYTYPE;
+		*ot = OT_ENUM;
+	} else if (!strcasecmp(name, "dnstimeout")) {
+		*key = CURLOPT_DNS_CACHE_TIMEOUT;
+		*ot = OT_INTEGER;
+	} else if (!strcasecmp(name, "userpwd")) {
+		*key = CURLOPT_USERPWD;
+		*ot = OT_STRING;
+	} else if (!strcasecmp(name, "proxyuserpwd")) {
+		*key = CURLOPT_PROXYUSERPWD;
+		*ot = OT_STRING;
+	} else if (!strcasecmp(name, "maxredirs")) {
+		*key = CURLOPT_MAXREDIRS;
+		*ot = OT_INTEGER;
+	} else if (!strcasecmp(name, "referer")) {
+		*key = CURLOPT_REFERER;
+		*ot = OT_STRING;
+	} else if (!strcasecmp(name, "useragent")) {
+		*key = CURLOPT_USERAGENT;
+		*ot = OT_STRING;
+	} else if (!strcasecmp(name, "cookie")) {
+		*key = CURLOPT_COOKIE;
+		*ot = OT_STRING;
+	} else if (!strcasecmp(name, "ftptimeout")) {
+		*key = CURLOPT_FTP_RESPONSE_TIMEOUT;
+		*ot = OT_INTEGER;
+	} else if (!strcasecmp(name, "httptimeout")) {
+#if CURLVERSION_ATLEAST(7,16,2)
+		*key = CURLOPT_TIMEOUT_MS;
+		*ot = OT_INTEGER_MS;
+#else
+		*key = CURLOPT_TIMEOUT;
+		*ot = OT_INTEGER;
+#endif
+	} else if (!strcasecmp(name, "conntimeout")) {
+#if CURLVERSION_ATLEAST(7,16,2)
+		*key = CURLOPT_CONNECTTIMEOUT_MS;
+		*ot = OT_INTEGER_MS;
+#else
+		*key = CURLOPT_CONNECTTIMEOUT;
+		*ot = OT_INTEGER;
+#endif
+	} else if (!strcasecmp(name, "ftptext")) {
+		*key = CURLOPT_TRANSFERTEXT;
+		*ot = OT_BOOLEAN;
+	} else {
+		return -1;
+	}
+	return 0;
+}
+
+static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
+{
+	struct ast_datastore *store;
+	AST_LIST_HEAD(global_curl_info, curl_settings) *list;
+	struct curl_settings *cur, *new = NULL;
+	CURLoption key;
+	enum optiontype ot;
+
+	if (chan) {
+		if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
+			/* Create a new datastore */
+			if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
+				ast_log(LOG_ERROR, "Unable to allocate new datastore.  Cannot set any CURL options\n");
+				return -1;
+			}
+
+			if (!(list = ast_calloc(1, sizeof(*list)))) {
+				ast_log(LOG_ERROR, "Unable to allocate list head.  Cannot set any CURL options\n");
+				ast_datastore_free(store);
+			}
+
+			store->data = list;
+			AST_LIST_HEAD_INIT(list);
+			ast_channel_datastore_add(chan, store);
+		} else {
+			list = store->data;
+		}
+	} else {
+		/* Populate the global structure */
+		list = (struct global_curl_info *)&global_curl_info;
+	}
+
+	if (!parse_curlopt_key(name, &key, &ot)) {
+		if (ot == OT_BOOLEAN) {
+			if ((new = ast_calloc(1, sizeof(*new)))) {
+				new->value = (void *)ast_true(value);
+			}
+		} else if (ot == OT_INTEGER) {
+			long tmp = atol(value);
+			if ((new = ast_calloc(1, sizeof(*new)))) {
+				new->value = (void *)tmp;
+			}
+		} else if (ot == OT_INTEGER_MS) {
+			long tmp = atof(value) * 1000.0;
+			if ((new = ast_calloc(1, sizeof(*new)))) {
+				new->value = (void *)tmp;
+			}
+		} else if (ot == OT_STRING) {
+			if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
+				new->value = (char *)new + sizeof(*new);
+				strcpy(new->value, value);
+			}
+		} else if (ot == OT_ENUM) {
+			if (key == CURLOPT_PROXYTYPE) {
+				long ptype =
+#if CURLVERSION_ATLEAST(7,10,0)
+					CURLPROXY_HTTP;
+#else
+					CURLPROXY_SOCKS5;
+#endif
+				if (0) {
+#if CURLVERSION_ATLEAST(7,15,2)
+				} else if (!strcasecmp(value, "socks4")) {
+					ptype = CURLPROXY_SOCKS4;
+#endif
+#if CURLVERSION_ATLEAST(7,18,0)
+				} else if (!strcasecmp(value, "socks4a")) {
+					ptype = CURLPROXY_SOCKS4A;
+#endif
+#if CURLVERSION_ATLEAST(7,18,0)
+				} else if (!strcasecmp(value, "socks5")) {
+					ptype = CURLPROXY_SOCKS5;
+#endif
+#if CURLVERSION_ATLEAST(7,18,0)
+				} else if (!strncasecmp(value, "socks5", 6)) {
+					ptype = CURLPROXY_SOCKS5_HOSTNAME;
+#endif
+				}
+
+				if ((new = ast_calloc(1, sizeof(*new)))) {
+					new->value = (void *)ptype;
+				}
+			} else {
+				/* Highly unlikely */
+				goto yuck;
+			}
+		}
+
+		/* Memory allocation error */
+		if (!new) {
+			return -1;
+		}
+
+		new->key = key;
+	} else {
+yuck:
+		ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
+		return -1;
+	}
+
+	/* Remove any existing entry */
+	AST_LIST_LOCK(list);
+	AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
+		if (cur->key == new->key) {
+			AST_LIST_REMOVE_CURRENT(list);
+			free(cur);
+			break;
+		}
+	}
+	AST_LIST_TRAVERSE_SAFE_END
+
+	/* Insert new entry */
+	ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
+	AST_LIST_INSERT_TAIL(list, new, list);
+	AST_LIST_UNLOCK(list);
+
+	return 0;
+}
+
+static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+	struct ast_datastore *store;
+	AST_LIST_HEAD(global_curl_info, curl_settings) *list[2] = { (struct global_curl_info *)&global_curl_info, NULL };
+	struct curl_settings *cur;
+	CURLoption key;
+	enum optiontype ot;
+	int i;
+
+	if (parse_curlopt_key(data, &key, &ot)) {
+		ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
+		return -1;
+	}
+
+	if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
+		list[0] = store->data;
+		list[1] = (struct global_curl_info *)&global_curl_info;
+	}
+
+	for (i = 0; i < 2; i++) {
+		if (!list[i]) {
+			break;
+		}
+		AST_LIST_LOCK(list[i]);
+		AST_LIST_TRAVERSE(list[i], cur, list) {
+			if (cur->key == key) {
+				if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
+					snprintf(buf, len, "%ld", (long)cur->value);
+				} else if (ot == OT_INTEGER_MS) {
+					if ((long)cur->value % 1000 == 0) {
+						snprintf(buf, len, "%ld", (long)cur->value / 1000);
+					} else {
+						snprintf(buf, len, "%.3f", (double)((long)cur->value) / 1000.0);
+					}
+				} else if (ot == OT_STRING) {
+					ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
+					ast_copy_string(buf, cur->value, len);
+				} else if (key == CURLOPT_PROXYTYPE) {
+					if (0) {
+#if CURLVERSION_ATLEAST(7,15,2)
+					} else if ((long)cur->value == CURLPROXY_SOCKS4) {
+						ast_copy_string(buf, "socks4", len);
+#endif
+#if CURLVERSION_ATLEAST(7,18,0)
+					} else if ((long)cur->value == CURLPROXY_SOCKS4A) {
+						ast_copy_string(buf, "socks4a", len);
+#endif
+					} else if ((long)cur->value == CURLPROXY_SOCKS5) {
+						ast_copy_string(buf, "socks5", len);
+#if CURLVERSION_ATLEAST(7,18,0)
+					} else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
+						ast_copy_string(buf, "socks5hostname", len);
+#endif
+#if CURLVERSION_ATLEAST(7,10,0)
+					} else if ((long)cur->value == CURLPROXY_HTTP) {
+						ast_copy_string(buf, "http", len);
+#endif
+					} else {
+						ast_copy_string(buf, "unknown", len);
+					}
+				}
+				break;
+			}
+		}
+		AST_LIST_UNLOCK(list[i]);
+		if (cur) {
+			break;
+		}
+	}
+
+	return cur ? 0 : -1;
+}
+
 static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
 {
 	register int realsize = size * nmemb;
-	struct ast_str **str = (struct ast_str **)data;
-
-	if (ast_str_make_space(str, (*str)->used + realsize + 1) == 0) {
-		memcpy(&(*str)->str[(*str)->used], ptr, realsize);
-		(*str)->used += realsize;
-	}
+	struct ast_str **pstr = (struct ast_str **)data;
+
+	ast_debug(3, "Called with data=%p, str=%p, realsize=%d, len=%d, used=%d\n", data, *pstr, realsize, (*pstr)->len, (*pstr)->used);
+
+	if (ast_str_make_space(pstr, (((*pstr)->used + realsize + 1) / 512 + 1) * 512 + 470) == 0) {
+		memcpy(&((*pstr)->str[(*pstr)->used]), ptr, realsize);
+		(*pstr)->used += realsize;
+	}
+
+	ast_debug(3, "Now, len=%d, used=%d\n", (*pstr)->len, (*pstr)->used);
 
 	return realsize;
 }
@@ -90,29 +388,6 @@
 }
 
 AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
-
-static int curl_internal(struct ast_str **chunk, char *url, char *post)
-{
-	CURL **curl;
-
-	if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl))))
-		return -1;
-
-	curl_easy_setopt(*curl, CURLOPT_URL, url);
-	curl_easy_setopt(*curl, CURLOPT_WRITEDATA, (void *) chunk);
-
-	if (post) {
-		curl_easy_setopt(*curl, CURLOPT_POST, 1);
-		curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, post);
-	}
-
-	curl_easy_perform(*curl);
-
-	if (post)
-		curl_easy_setopt(*curl, CURLOPT_POST, 0);
-
-	return 0;
-}
 
 static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
 {
@@ -121,6 +396,10 @@
 		AST_APP_ARG(url);
 		AST_APP_ARG(postdata);
 	);
+	CURL **curl;
+	struct curl_settings *cur;
+	struct ast_datastore *store = NULL;
+	AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
 
 	*buf = '\0';
 	
@@ -132,20 +411,54 @@
 
 	AST_STANDARD_APP_ARGS(args, info);	
 
-	if (chan)
+	if (chan) {
 		ast_autoservice_start(chan);
-
-	if (!curl_internal(&str, args.url, args.postdata)) {
-		if (str->used) {
-			str->str[str->used] = '\0';
-			if (str->str[str->used - 1] == '\n') {
-				str->str[str->used - 1] = '\0';
-			}
-
-			ast_copy_string(buf, str->str, len);
-		}
-	} else {
+	}
+
+	if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
 		ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+		return -1;
+	}
+
+	AST_LIST_LOCK(&global_curl_info);
+	AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
+		curl_easy_setopt(*curl, cur->key, cur->value);
+	}
+
+	if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
+		list = store->data;
+		AST_LIST_LOCK(list);
+		AST_LIST_TRAVERSE(list, cur, list) {
+			curl_easy_setopt(*curl, cur->key, cur->value);
+		}
+	}
+
+	curl_easy_setopt(*curl, CURLOPT_URL, args.url);
+	curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
+
+	if (args.postdata) {
+		curl_easy_setopt(*curl, CURLOPT_POST, 1);
+		curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
+	}
+
+	curl_easy_perform(*curl);
+
+	if (store) {
+		AST_LIST_UNLOCK(list);
+	}
+	AST_LIST_UNLOCK(&global_curl_info);
+
+	if (args.postdata) {
+		curl_easy_setopt(*curl, CURLOPT_POST, 0);
+	}
+
+	if (str->used) {
+		str->str[str->used] = '\0';
+		if (str->str[str->used - 1] == '\n') {
+			str->str[str->used - 1] = '\0';
+		}
+
+		ast_copy_string(buf, str->str, len);
 	}
 	ast_free(str);
 
@@ -165,11 +478,37 @@
 	.read = acf_curl_exec,
 };
 
+struct ast_custom_function acf_curlopt = {
+	.name = "CURLOPT",
+	.synopsis = "Set options for use with the CURL() function",
+	.syntax = "CURLOPT(<option>)",
+	.desc =
+"  cookie       - Send cookie with request\n"
+"  conntimeout  - Number of seconds to wait for connection\n"
+"  dnstimeout   - Number of seconds to wait for DNS response\n"
+"  ftptext      - For FTP, force a text transfer (boolean)\n"
+"  ftptimeout   - For FTP, the server response timeout\n"
+"  header       - Retrieve header information (boolean)\n"
+"  httptimeout  - Number of seconds to wait for HTTP response\n"
+"  maxredirs    - Maximum number of redirects to follow\n"
+"  proxy        - Hostname or IP to use as a proxy\n"
+"  proxytype    - http, socks4, or socks5\n"
+"  proxyport    - port number of the proxy\n"
+"  proxyuserpwd - A <user>:<pass> to use for authentication\n"
+"  referer      - Referer URL to use for the request\n"
+"  useragent    - UserAgent string to use\n"
+"  userpwd      - A <user>:<pass> to use for authentication\n"
+"",
+	.read = acf_curlopt_read,
+	.write = acf_curlopt_write,
+};
+
 static int unload_module(void)
 {
 	int res;
 
 	res = ast_custom_function_unregister(&acf_curl);
+	res |= ast_custom_function_unregister(&acf_curlopt);
 
 	return res;
 }
@@ -186,6 +525,7 @@
 	}
 
 	res = ast_custom_function_register(&acf_curl);
+	res |= ast_custom_function_register(&acf_curlopt);
 
 	return res;
 }




More information about the asterisk-commits mailing list