[svn-commits] mjordan: branch mjordan/trunk-http-stuff-and-things r426138 - /team/mjordan/t...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Oct 25 20:21:25 CDT 2014


Author: mjordan
Date: Sat Oct 25 20:21:18 2014
New Revision: 426138

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=426138
Log:
Update func_curl to grab remote resources and store them as files

Modified:
    team/mjordan/trunk-http-stuff-and-things/funcs/func_curl.c

Modified: team/mjordan/trunk-http-stuff-and-things/funcs/func_curl.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/funcs/func_curl.c?view=diff&rev=426138&r1=426137&r2=426138
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/funcs/func_curl.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/funcs/func_curl.c Sat Oct 25 20:21:18 2014
@@ -57,15 +57,39 @@
 			Retrieve content from a remote web or ftp server
 		</synopsis>
 		<syntax>
-			<parameter name="url" required="true" />
+			<parameter name="url" required="true">
+				<para>The full URL for the resource to retrieve.</para>
+			</parameter>
 			<parameter name="post-data">
+				<para><emphasis>Read Only</emphasis></para>
 				<para>If specified, an <literal>HTTP POST</literal> will be
 				performed with the content of
 				<replaceable>post-data</replaceable>, instead of an
 				<literal>HTTP GET</literal> (default).</para>
 			</parameter>
 		</syntax>
-		<description />
+		<description>
+			<para>When this function is read, a <literal>HTTP GET</literal>
+			(by default) will be used to retrieve the contents of the provided
+			<replaceable>url</replaceable>. The contents are returned as the
+			result of the function.</para>
+			<example title="Displaying contents of a page" language="text">
+			exten => s,1,Verbose(0, ${CURL(http://localhost:8088/static/astman.css)})
+			</example>
+			<para>When this function is written to, a <literal>HTTP GET</literal>
+			will be used to retrieve the contents of the provided
+			<replaceable>url</replaceable>. The value written to the function
+			specifies the destination file of the cURL'd resource.</para>
+			<example title="Retrieving a file" language="text">
+			exten => s,1,Set(CURL(http://localhost:8088/static/astman.css)=/var/spool/asterisk/tmp/astman.css))
+			</example>
+			<note>
+				<para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
+				is set to <literal>no</literal>, this function can only be written to from the
+				dialplan, and not directly from external protocols. Read operations are
+				unaffected.</para>
+			</note>
+		</description>
 		<see-also>
 			<ref type="function">CURLOPT</ref>
 		</see-also>
@@ -524,16 +548,27 @@
 	return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
 }
 
+/*! \brief Callback data passed to \ref WriteMemoryCallback */
+struct curl_write_callback_data {
+	/*! \brief If a string is being built, the string buffer */
+	struct ast_str *str;
+	/*! \brief The max size of \ref str */
+	ssize_t len;
+	/*! \brief If a file is being retrieved, the file to write to */
+	FILE *out_file;
+};
+
 static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
 {
-	register int realsize = size * nmemb;
-	struct ast_str **pstr = (struct ast_str **)data;
-
-	ast_debug(3, "Called with data=%p, str=%p, realsize=%d, len=%zu, used=%zu\n", data, *pstr, realsize, ast_str_size(*pstr), ast_str_strlen(*pstr));
-
-	ast_str_append_substr(pstr, 0, ptr, realsize);
-
-	ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
+	register int realsize = 0;
+	struct curl_write_callback_data *cb_data = data;
+
+	if (cb_data->str) {
+		realsize = size * nmemb;
+		ast_str_append_substr(&cb_data->str, 0, ptr, realsize);
+	} else if (cb_data->out_file) {
+		realsize = fwrite(ptr, size, nmemb, cb_data->out_file);
+	}
 
 	return realsize;
 }
@@ -567,15 +602,16 @@
 AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
 AST_THREADSTORAGE(thread_escapebuf);
 
-static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
+struct curl_args {
+	const char *url;
+	const char *postdata;
+	struct curl_write_callback_data cb_data;
+};
+
+static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
 {
 	struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
-	struct ast_str *str = ast_str_create(16);
 	int ret = -1;
-	AST_DECLARE_APP_ARGS(args,
-		AST_APP_ARG(url);
-		AST_APP_ARG(postdata);
-	);
 	CURL **curl;
 	struct curl_settings *cur;
 	struct ast_datastore *store = NULL;
@@ -583,35 +619,17 @@
 	AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
 	char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
 
-	if (buf) {
-		*buf = '\0';
-	}
-
-	if (!str) {
-		return -1;
-	}
-
 	if (!escapebuf) {
-		ast_free(str);
-		return -1;
-	}
-
-	if (ast_strlen_zero(info)) {
-		ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
-		ast_free(str);
-		return -1;
-	}
-
-	AST_STANDARD_APP_ARGS(args, info);
+		return -1;
+	}
+
+	if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
+		ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
+		return -1;
+	}
 
 	if (chan) {
 		ast_autoservice_start(chan);
-	}
-
-	if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
-		ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
-		ast_free(str);
-		return -1;
 	}
 
 	AST_LIST_LOCK(&global_curl_info);
@@ -635,12 +653,12 @@
 		}
 	}
 
-	curl_easy_setopt(*curl, CURLOPT_URL, args.url);
-	curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
-
-	if (args.postdata) {
+	curl_easy_setopt(*curl, CURLOPT_URL, args->url);
+	curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &args->cb_data);
+
+	if (args->postdata) {
 		curl_easy_setopt(*curl, CURLOPT_POST, 1);
-		curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
+		curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args->postdata);
 	}
 
 	/* Temporarily assign a buffer for curl to write errors to. */
@@ -648,7 +666,7 @@
 	curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
 
 	if (curl_easy_perform(*curl) != 0) {
-		ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args.url);
+		ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args->url);
 	}
 
 	/* Reset buffer to NULL so curl doesn't try to write to it when the
@@ -662,19 +680,19 @@
 	}
 	AST_LIST_UNLOCK(&global_curl_info);
 
-	if (args.postdata) {
+	if (args->postdata) {
 		curl_easy_setopt(*curl, CURLOPT_POST, 0);
 	}
 
-	if (ast_str_strlen(str)) {
-		ast_str_trim_blanks(str);
-
-		ast_debug(3, "str='%s'\n", ast_str_buffer(str));
+	if (args->cb_data.str && ast_str_strlen(args->cb_data.str)) {
+		ast_str_trim_blanks(args->cb_data.str);
+
+		ast_debug(3, "CURL returned str='%s'\n", ast_str_buffer(args->cb_data.str));
 		if (hashcompat) {
-			char *remainder = ast_str_buffer(str);
+			char *remainder = ast_str_buffer(args->cb_data.str);
 			char *piece;
-			struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
-			struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
+			struct ast_str *fields = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
+			struct ast_str *values = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
 			int rowcount = 0;
 			while (fields && values && (piece = strsep(&remainder, "&"))) {
 				char *name = strsep(&piece, "=");
@@ -688,49 +706,98 @@
 				rowcount++;
 			}
 			pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
-			if (buf) {
-				ast_copy_string(buf, ast_str_buffer(values), len);
-			} else {
-				ast_str_set(input_str, len, "%s", ast_str_buffer(values));
-			}
+			ast_str_set(&args->cb_data.str, 0, "%s", ast_str_buffer(values));
 			ast_free(fields);
 			ast_free(values);
-		} else {
-			if (buf) {
-				ast_copy_string(buf, ast_str_buffer(str), len);
-			} else {
-				ast_str_set(input_str, len, "%s", ast_str_buffer(str));
-			}
 		}
 		ret = 0;
 	}
-	ast_free(str);
-
-	if (chan)
+
+	if (chan) {
 		ast_autoservice_stop(chan);
+	}
 
 	return ret;
 }
 
-static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
-{
-	return acf_curl_helper(chan, cmd, info, buf, NULL, len);
-}
-
-static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
-{
-	return acf_curl_helper(chan, cmd, info, NULL, buf, len);
+static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
+{
+	struct curl_args curl_params = { 0, };
+	int res;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(url);
+		AST_APP_ARG(postdata);
+	);
+
+	AST_STANDARD_APP_ARGS(args, info);
+
+	if (ast_strlen_zero(info)) {
+		ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+		return -1;
+	}
+
+	curl_params.url = args.url;
+	curl_params.postdata = args.postdata;
+	curl_params.cb_data.str = ast_str_create(16);
+	if (!curl_params.cb_data.str) {
+		return -1;
+	}
+
+	res = acf_curl_helper(chan, &curl_params);
+	ast_str_set(buf, len, "%s", ast_str_buffer(curl_params.cb_data.str));
+	ast_free(curl_params.cb_data.str);
+
+	return res;
+}
+
+static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
+{
+	struct curl_args curl_params = { 0, };
+	int res;
+	char *args_value = ast_strdupa(value);
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(file_path);
+		AST_APP_ARG(file_type);
+	);
+
+	AST_STANDARD_APP_ARGS(args, args_value);
+
+	if (ast_strlen_zero(name)) {
+		ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.file_path)) {
+		ast_log(LOG_WARNING, "CURL requires a file to write\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.file_type)) {
+		args.file_type = "wb";
+	}
+
+	curl_params.url = name;
+	curl_params.cb_data.out_file = fopen(args.file_path, args.file_type);
+	if (!curl_params.cb_data.out_file) {
+		ast_log(LOG_WARNING, "Failed to open file %s: %s (%d)\n",
+			args.file_path,
+			strerror(errno),
+			errno);
+		return -1;
+	}
+
+	res = acf_curl_helper(chan, &curl_params);
+
+	fclose(curl_params.cb_data.out_file);
+
+	return res;
 }
 
 static struct ast_custom_function acf_curl = {
 	.name = "CURL",
-	.synopsis = "Retrieves the contents of a URL",
-	.syntax = "CURL(url[,post-data])",
-	.desc =
-	"  url       - URL to retrieve\n"
-	"  post-data - Optional data to send as a POST (GET is default action)\n",
-	.read = acf_curl_exec,
-	.read2 = acf_curl2_exec,
+	.read2 = acf_curl_exec,
+	.write = acf_curl_write,
 };
 
 static struct ast_custom_function acf_curlopt = {
@@ -783,7 +850,7 @@
 		}
 	}
 
-	res = ast_custom_function_register(&acf_curl);
+	res = ast_custom_function_register_escalating(&acf_curl, AST_CFE_WRITE);
 	res |= ast_custom_function_register(&acf_curlopt);
 
 	return res;




More information about the svn-commits mailing list