Thu Apr 28 2011 17:16:06

Asterisk developer's documentation


func_curl.c File Reference

Curl - Load a URL. More...

#include "asterisk.h"
#include <curl/curl.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/cli.h"
#include "asterisk/module.h"
#include "asterisk/app.h"
#include "asterisk/utils.h"
#include "asterisk/threadstorage.h"
Include dependency graph for func_curl.c:

Go to the source code of this file.

Data Structures

struct  curl_settings
struct  global_curl_info

Defines

#define CURLOPT_SPECIAL_HASHCOMPAT   -500
#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))))

Enumerations

enum  optiontype {
  OT_BOOLEAN, OT_INTEGER, OT_INTEGER_MS, OT_STRING,
  OT_ENUM
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static int acf_curl_exec (struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
static int acf_curlopt_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int acf_curlopt_write (struct ast_channel *chan, const char *cmd, char *name, const char *value)
 AST_THREADSTORAGE_CUSTOM_SCOPE (curl_instance, curl_instance_init, curl_instance_cleanup, static)
static void curl_instance_cleanup (void *data)
static int curl_instance_init (void *data)
static void curlds_free (void *data)
static int load_module (void)
static int parse_curlopt_key (const char *name, CURLoption *key, enum optiontype *ot)
static int unload_module (void)
static size_t WriteMemoryCallback (void *ptr, size_t size, size_t nmemb, void *data)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Load external URL" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
struct ast_custom_function acf_curl
struct ast_custom_function acf_curlopt
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_datastore_info curl_info
struct global_curl_info global_curl_info
static const char * global_useragent = "asterisk-libcurl-agent/1.0"

Detailed Description

Curl - Load a URL.

Author:
Tilghman Lesher <curl-20050919@the-tilghman.com>
Note:
Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
ExtRef:
Depends on the CURL library - http://curl.haxx.se/

Definition in file func_curl.c.


Define Documentation

#define CURLOPT_SPECIAL_HASHCOMPAT   -500

Definition at line 56 of file func_curl.c.

Referenced by acf_curl_exec(), and parse_curlopt_key().

#define CURLVERSION_ATLEAST (   a,
  b,
 
)    ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))

Definition at line 53 of file func_curl.c.


Enumeration Type Documentation

enum optiontype
Enumerator:
OT_BOOLEAN 
OT_INTEGER 
OT_INTEGER_MS 
OT_STRING 
OT_ENUM 

Definition at line 86 of file func_curl.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 572 of file func_curl.c.

static void __unreg_module ( void  ) [static]

Definition at line 572 of file func_curl.c.

static int acf_curl_exec ( struct ast_channel chan,
const char *  cmd,
char *  info,
char *  buf,
size_t  len 
) [static]

Definition at line 397 of file func_curl.c.

References AST_APP_ARG, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, ast_free, AST_LIST_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_STANDARD_APP_ARGS, ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_strlen(), ast_str_trim_blanks(), ast_strlen_zero(), ast_threadstorage_get(), ast_uri_decode(), CURLOPT_SPECIAL_HASHCOMPAT, ast_datastore::data, curl_settings::key, curl_settings::list, LOG_ERROR, LOG_WARNING, name, pbx_builtin_setvar_helper(), str, strsep(), url, and curl_settings::value.

{
   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;
   int hashcompat = 0;
   AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;

   *buf = '\0';
   
   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);  

   if (chan) {
      ast_autoservice_start(chan);
   }

   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) {
      if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
         hashcompat = (cur->value != NULL) ? 1 : 0;
      } else {
         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) {
         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, 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 (ast_str_strlen(str)) {
      ast_str_trim_blanks(str);

      ast_debug(3, "str='%s'\n", ast_str_buffer(str));
      if (hashcompat) {
         char *remainder = ast_str_buffer(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);
         int rowcount = 0;
         while (fields && values && (piece = strsep(&remainder, "&"))) {
            char *name = strsep(&piece, "=");
            if (!piece) {
               piece = "";
            }
            ast_uri_decode(piece);
            ast_uri_decode(name);
            ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", name);
            ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", piece);
            rowcount++;
         }
         pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
         ast_copy_string(buf, ast_str_buffer(values), len);
         ast_free(fields);
         ast_free(values);
      } else {
         ast_copy_string(buf, ast_str_buffer(str), len);
      }
      ret = 0;
   }
   ast_free(str);

   if (chan)
      ast_autoservice_stop(chan);
   
   return ret;
}
static int acf_curlopt_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 282 of file func_curl.c.

References ast_channel_datastore_find(), ast_copy_string(), ast_debug, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_datastore::data, global_curl_info, curl_settings::key, curl_settings::list, LOG_ERROR, OT_BOOLEAN, OT_INTEGER, OT_INTEGER_MS, OT_STRING, parse_curlopt_key(), and curl_settings::value.

{
   struct ast_datastore *store;
   struct global_curl_info *list[2] = { &global_curl_info, NULL };
   struct curl_settings *cur = NULL;
   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] = &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 int acf_curlopt_write ( struct ast_channel chan,
const char *  cmd,
char *  name,
const char *  value 
) [static]

Definition at line 163 of file func_curl.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_find(), ast_datastore_alloc(), ast_datastore_free(), ast_debug, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), ast_true(), ast_datastore::data, free, global_curl_info, curl_settings::key, curl_settings::list, LOG_ERROR, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, OT_STRING, and parse_curlopt_key().

{
   struct ast_datastore *store;
   struct global_curl_info *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 = &global_curl_info;
   }

   if (!parse_curlopt_key(name, &key, &ot)) {
      if (ot == OT_BOOLEAN) {
         if ((new = ast_calloc(1, sizeof(*new)))) {
            new->value = (void *)((long) 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;
}
AST_THREADSTORAGE_CUSTOM_SCOPE ( curl_instance  ,
curl_instance_init  ,
curl_instance_cleanup  ,
static   
)
static void curl_instance_cleanup ( void *  data) [static]

Definition at line 386 of file func_curl.c.

References ast_free.

{
   CURL **curl = data;

   curl_easy_cleanup(*curl);

   ast_free(data);
}
static int curl_instance_init ( void *  data) [static]

Definition at line 371 of file func_curl.c.

References global_useragent, and WriteMemoryCallback().

{
   CURL **curl = data;

   if (!(*curl = curl_easy_init()))
      return -1;

   curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
   curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
   curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
   curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);

   return 0;
}
static void curlds_free ( void *  data) [static]

Definition at line 73 of file func_curl.c.

References AST_LIST_HEAD, AST_LIST_HEAD_DESTROY, AST_LIST_REMOVE_HEAD, and free.

{
   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 load_module ( void  ) [static]

Definition at line 555 of file func_curl.c.

References acf_curl, acf_curlopt, ast_custom_function_register, ast_load_resource(), ast_log(), ast_module_check(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, and LOG_ERROR.

{
   int res;

   if (!ast_module_check("res_curl.so")) {
      if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
         ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
         return AST_MODULE_LOAD_DECLINE;
      }
   }

   res = ast_custom_function_register(&acf_curl);
   res |= ast_custom_function_register(&acf_curlopt);

   return res;
}
static int parse_curlopt_key ( const char *  name,
CURLoption *  key,
enum optiontype ot 
) [static]

Definition at line 94 of file func_curl.c.

References CURLOPT_SPECIAL_HASHCOMPAT, OT_BOOLEAN, OT_ENUM, OT_INTEGER, OT_INTEGER_MS, and OT_STRING.

Referenced by acf_curlopt_read(), and acf_curlopt_write().

{
   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 if (!strcasecmp(name, "ssl_verifypeer")) {
      *key = CURLOPT_SSL_VERIFYPEER;
      *ot = OT_BOOLEAN;
   } else if (!strcasecmp(name, "hashcompat")) {
      *key = CURLOPT_SPECIAL_HASHCOMPAT;
      *ot = OT_BOOLEAN;
   } else {
      return -1;
   }
   return 0;
}
static int unload_module ( void  ) [static]

Definition at line 545 of file func_curl.c.

References acf_curl, acf_curlopt, and ast_custom_function_unregister().

static size_t WriteMemoryCallback ( void *  ptr,
size_t  size,
size_t  nmemb,
void *  data 
) [static]

Definition at line 355 of file func_curl.c.

References ast_debug, ast_str_append_substr(), ast_str_size(), and ast_str_strlen().

Referenced by curl_instance_init().

{
   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));

   return realsize;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Load external URL" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 572 of file func_curl.c.

Definition at line 508 of file func_curl.c.

Referenced by load_module(), and unload_module().

Definition at line 518 of file func_curl.c.

Referenced by load_module(), and unload_module().

Definition at line 572 of file func_curl.c.

struct ast_datastore_info curl_info [static]
Initial value:
 {
   .type = "CURL",
   .destroy = curlds_free,
}

Definition at line 60 of file func_curl.c.

const char* global_useragent = "asterisk-libcurl-agent/1.0" [static]

Definition at line 369 of file func_curl.c.

Referenced by curl_instance_init().