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;


-- 
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

svn-commits mailing list
To UNSUBSCRIBE or update options visit:
   http://lists.digium.com/mailman/listinfo/svn-commits

Reply via email to