00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "asterisk.h"
00039
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 370183 $")
00041
00042 #include <curl/curl.h>
00043
00044 #include "asterisk/lock.h"
00045 #include "asterisk/file.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/cli.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/app.h"
00051 #include "asterisk/utils.h"
00052 #include "asterisk/threadstorage.h"
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 #define CURLVERSION_ATLEAST(a,b,c) \
00162 ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
00163
00164 #define CURLOPT_SPECIAL_HASHCOMPAT -500
00165
00166 static void curlds_free(void *data);
00167
00168 static const struct ast_datastore_info curl_info = {
00169 .type = "CURL",
00170 .destroy = curlds_free,
00171 };
00172
00173 struct curl_settings {
00174 AST_LIST_ENTRY(curl_settings) list;
00175 CURLoption key;
00176 void *value;
00177 };
00178
00179 AST_LIST_HEAD_STATIC(global_curl_info, curl_settings);
00180
00181 static void curlds_free(void *data)
00182 {
00183 AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
00184 struct curl_settings *setting;
00185 if (!list) {
00186 return;
00187 }
00188 while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
00189 free(setting);
00190 }
00191 AST_LIST_HEAD_DESTROY(list);
00192 }
00193
00194 enum optiontype {
00195 OT_BOOLEAN,
00196 OT_INTEGER,
00197 OT_INTEGER_MS,
00198 OT_STRING,
00199 OT_ENUM,
00200 };
00201
00202 static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
00203 {
00204 if (!strcasecmp(name, "header")) {
00205 *key = CURLOPT_HEADER;
00206 *ot = OT_BOOLEAN;
00207 } else if (!strcasecmp(name, "proxy")) {
00208 *key = CURLOPT_PROXY;
00209 *ot = OT_STRING;
00210 } else if (!strcasecmp(name, "proxyport")) {
00211 *key = CURLOPT_PROXYPORT;
00212 *ot = OT_INTEGER;
00213 } else if (!strcasecmp(name, "proxytype")) {
00214 *key = CURLOPT_PROXYTYPE;
00215 *ot = OT_ENUM;
00216 } else if (!strcasecmp(name, "dnstimeout")) {
00217 *key = CURLOPT_DNS_CACHE_TIMEOUT;
00218 *ot = OT_INTEGER;
00219 } else if (!strcasecmp(name, "userpwd")) {
00220 *key = CURLOPT_USERPWD;
00221 *ot = OT_STRING;
00222 } else if (!strcasecmp(name, "proxyuserpwd")) {
00223 *key = CURLOPT_PROXYUSERPWD;
00224 *ot = OT_STRING;
00225 } else if (!strcasecmp(name, "maxredirs")) {
00226 *key = CURLOPT_MAXREDIRS;
00227 *ot = OT_INTEGER;
00228 } else if (!strcasecmp(name, "referer")) {
00229 *key = CURLOPT_REFERER;
00230 *ot = OT_STRING;
00231 } else if (!strcasecmp(name, "useragent")) {
00232 *key = CURLOPT_USERAGENT;
00233 *ot = OT_STRING;
00234 } else if (!strcasecmp(name, "cookie")) {
00235 *key = CURLOPT_COOKIE;
00236 *ot = OT_STRING;
00237 } else if (!strcasecmp(name, "ftptimeout")) {
00238 *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
00239 *ot = OT_INTEGER;
00240 } else if (!strcasecmp(name, "httptimeout")) {
00241 #if CURLVERSION_ATLEAST(7,16,2)
00242 *key = CURLOPT_TIMEOUT_MS;
00243 *ot = OT_INTEGER_MS;
00244 #else
00245 *key = CURLOPT_TIMEOUT;
00246 *ot = OT_INTEGER;
00247 #endif
00248 } else if (!strcasecmp(name, "conntimeout")) {
00249 #if CURLVERSION_ATLEAST(7,16,2)
00250 *key = CURLOPT_CONNECTTIMEOUT_MS;
00251 *ot = OT_INTEGER_MS;
00252 #else
00253 *key = CURLOPT_CONNECTTIMEOUT;
00254 *ot = OT_INTEGER;
00255 #endif
00256 } else if (!strcasecmp(name, "ftptext")) {
00257 *key = CURLOPT_TRANSFERTEXT;
00258 *ot = OT_BOOLEAN;
00259 } else if (!strcasecmp(name, "ssl_verifypeer")) {
00260 *key = CURLOPT_SSL_VERIFYPEER;
00261 *ot = OT_BOOLEAN;
00262 } else if (!strcasecmp(name, "hashcompat")) {
00263 *key = CURLOPT_SPECIAL_HASHCOMPAT;
00264 *ot = OT_BOOLEAN;
00265 } else {
00266 return -1;
00267 }
00268 return 0;
00269 }
00270
00271 static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
00272 {
00273 struct ast_datastore *store;
00274 struct global_curl_info *list;
00275 struct curl_settings *cur, *new = NULL;
00276 CURLoption key;
00277 enum optiontype ot;
00278
00279 if (chan) {
00280 if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00281
00282 if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
00283 ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
00284 return -1;
00285 }
00286
00287 if (!(list = ast_calloc(1, sizeof(*list)))) {
00288 ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
00289 ast_datastore_free(store);
00290 return -1;
00291 }
00292
00293 store->data = list;
00294 AST_LIST_HEAD_INIT(list);
00295 ast_channel_datastore_add(chan, store);
00296 } else {
00297 list = store->data;
00298 }
00299 } else {
00300
00301 list = &global_curl_info;
00302 }
00303
00304 if (!parse_curlopt_key(name, &key, &ot)) {
00305 if (ot == OT_BOOLEAN) {
00306 if ((new = ast_calloc(1, sizeof(*new)))) {
00307 new->value = (void *)((long) ast_true(value));
00308 }
00309 } else if (ot == OT_INTEGER) {
00310 long tmp = atol(value);
00311 if ((new = ast_calloc(1, sizeof(*new)))) {
00312 new->value = (void *)tmp;
00313 }
00314 } else if (ot == OT_INTEGER_MS) {
00315 long tmp = atof(value) * 1000.0;
00316 if ((new = ast_calloc(1, sizeof(*new)))) {
00317 new->value = (void *)tmp;
00318 }
00319 } else if (ot == OT_STRING) {
00320 if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
00321 new->value = (char *)new + sizeof(*new);
00322 strcpy(new->value, value);
00323 }
00324 } else if (ot == OT_ENUM) {
00325 if (key == CURLOPT_PROXYTYPE) {
00326 long ptype =
00327 #if CURLVERSION_ATLEAST(7,10,0)
00328 CURLPROXY_HTTP;
00329 #else
00330 CURLPROXY_SOCKS5;
00331 #endif
00332 if (0) {
00333 #if CURLVERSION_ATLEAST(7,15,2)
00334 } else if (!strcasecmp(value, "socks4")) {
00335 ptype = CURLPROXY_SOCKS4;
00336 #endif
00337 #if CURLVERSION_ATLEAST(7,18,0)
00338 } else if (!strcasecmp(value, "socks4a")) {
00339 ptype = CURLPROXY_SOCKS4A;
00340 #endif
00341 #if CURLVERSION_ATLEAST(7,18,0)
00342 } else if (!strcasecmp(value, "socks5")) {
00343 ptype = CURLPROXY_SOCKS5;
00344 #endif
00345 #if CURLVERSION_ATLEAST(7,18,0)
00346 } else if (!strncasecmp(value, "socks5", 6)) {
00347 ptype = CURLPROXY_SOCKS5_HOSTNAME;
00348 #endif
00349 }
00350
00351 if ((new = ast_calloc(1, sizeof(*new)))) {
00352 new->value = (void *)ptype;
00353 }
00354 } else {
00355
00356 goto yuck;
00357 }
00358 }
00359
00360
00361 if (!new) {
00362 return -1;
00363 }
00364
00365 new->key = key;
00366 } else {
00367 yuck:
00368 ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
00369 return -1;
00370 }
00371
00372
00373 AST_LIST_LOCK(list);
00374 AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
00375 if (cur->key == new->key) {
00376 AST_LIST_REMOVE_CURRENT(list);
00377 free(cur);
00378 break;
00379 }
00380 }
00381 AST_LIST_TRAVERSE_SAFE_END
00382
00383
00384 ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
00385 AST_LIST_INSERT_TAIL(list, new, list);
00386 AST_LIST_UNLOCK(list);
00387
00388 return 0;
00389 }
00390
00391 static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
00392 {
00393 struct ast_datastore *store;
00394 struct global_curl_info *list[2] = { &global_curl_info, NULL };
00395 struct curl_settings *cur = NULL;
00396 CURLoption key;
00397 enum optiontype ot;
00398 int i;
00399
00400 if (parse_curlopt_key(data, &key, &ot)) {
00401 ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
00402 return -1;
00403 }
00404
00405 if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00406 list[0] = store->data;
00407 list[1] = &global_curl_info;
00408 }
00409
00410 for (i = 0; i < 2; i++) {
00411 if (!list[i]) {
00412 break;
00413 }
00414 AST_LIST_LOCK(list[i]);
00415 AST_LIST_TRAVERSE(list[i], cur, list) {
00416 if (cur->key == key) {
00417 if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
00418 if (buf) {
00419 snprintf(buf, len, "%ld", (long) cur->value);
00420 } else {
00421 ast_str_set(bufstr, len, "%ld", (long) cur->value);
00422 }
00423 } else if (ot == OT_INTEGER_MS) {
00424 if ((long) cur->value % 1000 == 0) {
00425 if (buf) {
00426 snprintf(buf, len, "%ld", (long)cur->value / 1000);
00427 } else {
00428 ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
00429 }
00430 } else {
00431 if (buf) {
00432 snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00433 } else {
00434 ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00435 }
00436 }
00437 } else if (ot == OT_STRING) {
00438 ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
00439 if (buf) {
00440 ast_copy_string(buf, cur->value, len);
00441 } else {
00442 ast_str_set(bufstr, 0, "%s", (char *) cur->value);
00443 }
00444 } else if (key == CURLOPT_PROXYTYPE) {
00445 if (0) {
00446 #if CURLVERSION_ATLEAST(7,15,2)
00447 } else if ((long)cur->value == CURLPROXY_SOCKS4) {
00448 if (buf) {
00449 ast_copy_string(buf, "socks4", len);
00450 } else {
00451 ast_str_set(bufstr, 0, "socks4");
00452 }
00453 #endif
00454 #if CURLVERSION_ATLEAST(7,18,0)
00455 } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
00456 if (buf) {
00457 ast_copy_string(buf, "socks4a", len);
00458 } else {
00459 ast_str_set(bufstr, 0, "socks4a");
00460 }
00461 #endif
00462 } else if ((long)cur->value == CURLPROXY_SOCKS5) {
00463 if (buf) {
00464 ast_copy_string(buf, "socks5", len);
00465 } else {
00466 ast_str_set(bufstr, 0, "socks5");
00467 }
00468 #if CURLVERSION_ATLEAST(7,18,0)
00469 } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
00470 if (buf) {
00471 ast_copy_string(buf, "socks5hostname", len);
00472 } else {
00473 ast_str_set(bufstr, 0, "socks5hostname");
00474 }
00475 #endif
00476 #if CURLVERSION_ATLEAST(7,10,0)
00477 } else if ((long)cur->value == CURLPROXY_HTTP) {
00478 if (buf) {
00479 ast_copy_string(buf, "http", len);
00480 } else {
00481 ast_str_set(bufstr, 0, "http");
00482 }
00483 #endif
00484 } else {
00485 if (buf) {
00486 ast_copy_string(buf, "unknown", len);
00487 } else {
00488 ast_str_set(bufstr, 0, "unknown");
00489 }
00490 }
00491 }
00492 break;
00493 }
00494 }
00495 AST_LIST_UNLOCK(list[i]);
00496 if (cur) {
00497 break;
00498 }
00499 }
00500
00501 return cur ? 0 : -1;
00502 }
00503
00504 static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00505 {
00506 return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
00507 }
00508
00509 static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00510 {
00511 return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
00512 }
00513
00514 static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
00515 {
00516 register int realsize = size * nmemb;
00517 struct ast_str **pstr = (struct ast_str **)data;
00518
00519 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));
00520
00521 ast_str_append_substr(pstr, 0, ptr, realsize);
00522
00523 ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
00524
00525 return realsize;
00526 }
00527
00528 static const char * const global_useragent = "asterisk-libcurl-agent/1.0";
00529
00530 static int curl_instance_init(void *data)
00531 {
00532 CURL **curl = data;
00533
00534 if (!(*curl = curl_easy_init()))
00535 return -1;
00536
00537 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
00538 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
00539 curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
00540 curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
00541
00542 return 0;
00543 }
00544
00545 static void curl_instance_cleanup(void *data)
00546 {
00547 CURL **curl = data;
00548
00549 curl_easy_cleanup(*curl);
00550
00551 ast_free(data);
00552 }
00553
00554 AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
00555 AST_THREADSTORAGE(thread_escapebuf);
00556
00557 static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
00558 {
00559 struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
00560 struct ast_str *str = ast_str_create(16);
00561 int ret = -1;
00562 AST_DECLARE_APP_ARGS(args,
00563 AST_APP_ARG(url);
00564 AST_APP_ARG(postdata);
00565 );
00566 CURL **curl;
00567 struct curl_settings *cur;
00568 struct ast_datastore *store = NULL;
00569 int hashcompat = 0;
00570 AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
00571
00572 if (buf) {
00573 *buf = '\0';
00574 }
00575
00576 if (!str) {
00577 return -1;
00578 }
00579
00580 if (!escapebuf) {
00581 ast_free(str);
00582 return -1;
00583 }
00584
00585 if (ast_strlen_zero(info)) {
00586 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
00587 ast_free(str);
00588 return -1;
00589 }
00590
00591 AST_STANDARD_APP_ARGS(args, info);
00592
00593 if (chan) {
00594 ast_autoservice_start(chan);
00595 }
00596
00597 if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
00598 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
00599 ast_free(str);
00600 return -1;
00601 }
00602
00603 AST_LIST_LOCK(&global_curl_info);
00604 AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
00605 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00606 hashcompat = (cur->value != NULL) ? 1 : 0;
00607 } else {
00608 curl_easy_setopt(*curl, cur->key, cur->value);
00609 }
00610 }
00611
00612 if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00613 list = store->data;
00614 AST_LIST_LOCK(list);
00615 AST_LIST_TRAVERSE(list, cur, list) {
00616 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00617 hashcompat = (cur->value != NULL) ? 1 : 0;
00618 } else {
00619 curl_easy_setopt(*curl, cur->key, cur->value);
00620 }
00621 }
00622 }
00623
00624 curl_easy_setopt(*curl, CURLOPT_URL, args.url);
00625 curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
00626
00627 if (args.postdata) {
00628 curl_easy_setopt(*curl, CURLOPT_POST, 1);
00629 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
00630 }
00631
00632 curl_easy_perform(*curl);
00633
00634 if (store) {
00635 AST_LIST_UNLOCK(list);
00636 }
00637 AST_LIST_UNLOCK(&global_curl_info);
00638
00639 if (args.postdata) {
00640 curl_easy_setopt(*curl, CURLOPT_POST, 0);
00641 }
00642
00643 if (ast_str_strlen(str)) {
00644 ast_str_trim_blanks(str);
00645
00646 ast_debug(3, "str='%s'\n", ast_str_buffer(str));
00647 if (hashcompat) {
00648 char *remainder = ast_str_buffer(str);
00649 char *piece;
00650 struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
00651 struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
00652 int rowcount = 0;
00653 while (fields && values && (piece = strsep(&remainder, "&"))) {
00654 char *name = strsep(&piece, "=");
00655 if (piece) {
00656 ast_uri_decode(piece);
00657 }
00658 ast_uri_decode(name);
00659 ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
00660 ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
00661 rowcount++;
00662 }
00663 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
00664 if (buf) {
00665 ast_copy_string(buf, ast_str_buffer(values), len);
00666 } else {
00667 ast_str_set(input_str, len, "%s", ast_str_buffer(values));
00668 }
00669 ast_free(fields);
00670 ast_free(values);
00671 } else {
00672 if (buf) {
00673 ast_copy_string(buf, ast_str_buffer(str), len);
00674 } else {
00675 ast_str_set(input_str, len, "%s", ast_str_buffer(str));
00676 }
00677 }
00678 ret = 0;
00679 }
00680 ast_free(str);
00681
00682 if (chan)
00683 ast_autoservice_stop(chan);
00684
00685 return ret;
00686 }
00687
00688 static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
00689 {
00690 return acf_curl_helper(chan, cmd, info, buf, NULL, len);
00691 }
00692
00693 static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
00694 {
00695 return acf_curl_helper(chan, cmd, info, NULL, buf, len);
00696 }
00697
00698 static struct ast_custom_function acf_curl = {
00699 .name = "CURL",
00700 .synopsis = "Retrieves the contents of a URL",
00701 .syntax = "CURL(url[,post-data])",
00702 .desc =
00703 " url - URL to retrieve\n"
00704 " post-data - Optional data to send as a POST (GET is default action)\n",
00705 .read = acf_curl_exec,
00706 .read2 = acf_curl2_exec,
00707 };
00708
00709 static struct ast_custom_function acf_curlopt = {
00710 .name = "CURLOPT",
00711 .synopsis = "Set options for use with the CURL() function",
00712 .syntax = "CURLOPT(<option>)",
00713 .desc =
00714 " cookie - Send cookie with request [none]\n"
00715 " conntimeout - Number of seconds to wait for connection\n"
00716 " dnstimeout - Number of seconds to wait for DNS response\n"
00717 " ftptext - For FTP, force a text transfer (boolean)\n"
00718 " ftptimeout - For FTP, the server response timeout\n"
00719 " header - Retrieve header information (boolean)\n"
00720 " httptimeout - Number of seconds to wait for HTTP response\n"
00721 " maxredirs - Maximum number of redirects to follow\n"
00722 " proxy - Hostname or IP to use as a proxy\n"
00723 " proxytype - http, socks4, or socks5\n"
00724 " proxyport - port number of the proxy\n"
00725 " proxyuserpwd - A <user>:<pass> to use for authentication\n"
00726 " referer - Referer URL to use for the request\n"
00727 " useragent - UserAgent string to use\n"
00728 " userpwd - A <user>:<pass> to use for authentication\n"
00729 " ssl_verifypeer - Whether to verify the peer certificate (boolean)\n"
00730 " hashcompat - Result data will be compatible for use with HASH()\n"
00731 "",
00732 .read = acf_curlopt_read,
00733 .read2 = acf_curlopt_read2,
00734 .write = acf_curlopt_write,
00735 };
00736
00737 static int unload_module(void)
00738 {
00739 int res;
00740
00741 res = ast_custom_function_unregister(&acf_curl);
00742 res |= ast_custom_function_unregister(&acf_curlopt);
00743
00744 return res;
00745 }
00746
00747 static int load_module(void)
00748 {
00749 int res;
00750
00751 if (!ast_module_check("res_curl.so")) {
00752 if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00753 ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
00754 return AST_MODULE_LOAD_DECLINE;
00755 }
00756 }
00757
00758 res = ast_custom_function_register(&acf_curl);
00759 res |= ast_custom_function_register(&acf_curlopt);
00760
00761 return res;
00762 }
00763
00764 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Load external URL",
00765 .load = load_module,
00766 .unload = unload_module,
00767 .load_pri = AST_MODPRI_REALTIME_DEPEND2,
00768 );
00769