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: 360413 $")
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 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 }
00291
00292 store->data = list;
00293 AST_LIST_HEAD_INIT(list);
00294 ast_channel_datastore_add(chan, store);
00295 } else {
00296 list = store->data;
00297 }
00298 } else {
00299
00300 list = &global_curl_info;
00301 }
00302
00303 if (!parse_curlopt_key(name, &key, &ot)) {
00304 if (ot == OT_BOOLEAN) {
00305 if ((new = ast_calloc(1, sizeof(*new)))) {
00306 new->value = (void *)((long) ast_true(value));
00307 }
00308 } else if (ot == OT_INTEGER) {
00309 long tmp = atol(value);
00310 if ((new = ast_calloc(1, sizeof(*new)))) {
00311 new->value = (void *)tmp;
00312 }
00313 } else if (ot == OT_INTEGER_MS) {
00314 long tmp = atof(value) * 1000.0;
00315 if ((new = ast_calloc(1, sizeof(*new)))) {
00316 new->value = (void *)tmp;
00317 }
00318 } else if (ot == OT_STRING) {
00319 if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
00320 new->value = (char *)new + sizeof(*new);
00321 strcpy(new->value, value);
00322 }
00323 } else if (ot == OT_ENUM) {
00324 if (key == CURLOPT_PROXYTYPE) {
00325 long ptype =
00326 #if CURLVERSION_ATLEAST(7,10,0)
00327 CURLPROXY_HTTP;
00328 #else
00329 CURLPROXY_SOCKS5;
00330 #endif
00331 if (0) {
00332 #if CURLVERSION_ATLEAST(7,15,2)
00333 } else if (!strcasecmp(value, "socks4")) {
00334 ptype = CURLPROXY_SOCKS4;
00335 #endif
00336 #if CURLVERSION_ATLEAST(7,18,0)
00337 } else if (!strcasecmp(value, "socks4a")) {
00338 ptype = CURLPROXY_SOCKS4A;
00339 #endif
00340 #if CURLVERSION_ATLEAST(7,18,0)
00341 } else if (!strcasecmp(value, "socks5")) {
00342 ptype = CURLPROXY_SOCKS5;
00343 #endif
00344 #if CURLVERSION_ATLEAST(7,18,0)
00345 } else if (!strncasecmp(value, "socks5", 6)) {
00346 ptype = CURLPROXY_SOCKS5_HOSTNAME;
00347 #endif
00348 }
00349
00350 if ((new = ast_calloc(1, sizeof(*new)))) {
00351 new->value = (void *)ptype;
00352 }
00353 } else {
00354
00355 goto yuck;
00356 }
00357 }
00358
00359
00360 if (!new) {
00361 return -1;
00362 }
00363
00364 new->key = key;
00365 } else {
00366 yuck:
00367 ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
00368 return -1;
00369 }
00370
00371
00372 AST_LIST_LOCK(list);
00373 AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
00374 if (cur->key == new->key) {
00375 AST_LIST_REMOVE_CURRENT(list);
00376 free(cur);
00377 break;
00378 }
00379 }
00380 AST_LIST_TRAVERSE_SAFE_END
00381
00382
00383 ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
00384 AST_LIST_INSERT_TAIL(list, new, list);
00385 AST_LIST_UNLOCK(list);
00386
00387 return 0;
00388 }
00389
00390 static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
00391 {
00392 struct ast_datastore *store;
00393 struct global_curl_info *list[2] = { &global_curl_info, NULL };
00394 struct curl_settings *cur = NULL;
00395 CURLoption key;
00396 enum optiontype ot;
00397 int i;
00398
00399 if (parse_curlopt_key(data, &key, &ot)) {
00400 ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
00401 return -1;
00402 }
00403
00404 if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00405 list[0] = store->data;
00406 list[1] = &global_curl_info;
00407 }
00408
00409 for (i = 0; i < 2; i++) {
00410 if (!list[i]) {
00411 break;
00412 }
00413 AST_LIST_LOCK(list[i]);
00414 AST_LIST_TRAVERSE(list[i], cur, list) {
00415 if (cur->key == key) {
00416 if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
00417 if (buf) {
00418 snprintf(buf, len, "%ld", (long) cur->value);
00419 } else {
00420 ast_str_set(bufstr, len, "%ld", (long) cur->value);
00421 }
00422 } else if (ot == OT_INTEGER_MS) {
00423 if ((long) cur->value % 1000 == 0) {
00424 if (buf) {
00425 snprintf(buf, len, "%ld", (long)cur->value / 1000);
00426 } else {
00427 ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
00428 }
00429 } else {
00430 if (buf) {
00431 snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00432 } else {
00433 ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
00434 }
00435 }
00436 } else if (ot == OT_STRING) {
00437 ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
00438 if (buf) {
00439 ast_copy_string(buf, cur->value, len);
00440 } else {
00441 ast_str_set(bufstr, 0, "%s", (char *) cur->value);
00442 }
00443 } else if (key == CURLOPT_PROXYTYPE) {
00444 if (0) {
00445 #if CURLVERSION_ATLEAST(7,15,2)
00446 } else if ((long)cur->value == CURLPROXY_SOCKS4) {
00447 if (buf) {
00448 ast_copy_string(buf, "socks4", len);
00449 } else {
00450 ast_str_set(bufstr, 0, "socks4");
00451 }
00452 #endif
00453 #if CURLVERSION_ATLEAST(7,18,0)
00454 } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
00455 if (buf) {
00456 ast_copy_string(buf, "socks4a", len);
00457 } else {
00458 ast_str_set(bufstr, 0, "socks4a");
00459 }
00460 #endif
00461 } else if ((long)cur->value == CURLPROXY_SOCKS5) {
00462 if (buf) {
00463 ast_copy_string(buf, "socks5", len);
00464 } else {
00465 ast_str_set(bufstr, 0, "socks5");
00466 }
00467 #if CURLVERSION_ATLEAST(7,18,0)
00468 } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
00469 if (buf) {
00470 ast_copy_string(buf, "socks5hostname", len);
00471 } else {
00472 ast_str_set(bufstr, 0, "socks5hostname");
00473 }
00474 #endif
00475 #if CURLVERSION_ATLEAST(7,10,0)
00476 } else if ((long)cur->value == CURLPROXY_HTTP) {
00477 if (buf) {
00478 ast_copy_string(buf, "http", len);
00479 } else {
00480 ast_str_set(bufstr, 0, "http");
00481 }
00482 #endif
00483 } else {
00484 if (buf) {
00485 ast_copy_string(buf, "unknown", len);
00486 } else {
00487 ast_str_set(bufstr, 0, "unknown");
00488 }
00489 }
00490 }
00491 break;
00492 }
00493 }
00494 AST_LIST_UNLOCK(list[i]);
00495 if (cur) {
00496 break;
00497 }
00498 }
00499
00500 return cur ? 0 : -1;
00501 }
00502
00503 static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00504 {
00505 return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
00506 }
00507
00508 static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
00509 {
00510 return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
00511 }
00512
00513 static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
00514 {
00515 register int realsize = size * nmemb;
00516 struct ast_str **pstr = (struct ast_str **)data;
00517
00518 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));
00519
00520 ast_str_append_substr(pstr, 0, ptr, realsize);
00521
00522 ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
00523
00524 return realsize;
00525 }
00526
00527 static const char * const global_useragent = "asterisk-libcurl-agent/1.0";
00528
00529 static int curl_instance_init(void *data)
00530 {
00531 CURL **curl = data;
00532
00533 if (!(*curl = curl_easy_init()))
00534 return -1;
00535
00536 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
00537 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
00538 curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
00539 curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
00540
00541 return 0;
00542 }
00543
00544 static void curl_instance_cleanup(void *data)
00545 {
00546 CURL **curl = data;
00547
00548 curl_easy_cleanup(*curl);
00549
00550 ast_free(data);
00551 }
00552
00553 AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
00554 AST_THREADSTORAGE(thread_escapebuf);
00555
00556 static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
00557 {
00558 struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
00559 struct ast_str *str = ast_str_create(16);
00560 int ret = -1;
00561 AST_DECLARE_APP_ARGS(args,
00562 AST_APP_ARG(url);
00563 AST_APP_ARG(postdata);
00564 );
00565 CURL **curl;
00566 struct curl_settings *cur;
00567 struct ast_datastore *store = NULL;
00568 int hashcompat = 0;
00569 AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
00570
00571 if (buf) {
00572 *buf = '\0';
00573 }
00574
00575 if (!str) {
00576 return -1;
00577 }
00578
00579 if (!escapebuf) {
00580 ast_free(str);
00581 return -1;
00582 }
00583
00584 if (ast_strlen_zero(info)) {
00585 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
00586 ast_free(str);
00587 return -1;
00588 }
00589
00590 AST_STANDARD_APP_ARGS(args, info);
00591
00592 if (chan) {
00593 ast_autoservice_start(chan);
00594 }
00595
00596 if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
00597 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
00598 ast_free(str);
00599 return -1;
00600 }
00601
00602 AST_LIST_LOCK(&global_curl_info);
00603 AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
00604 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00605 hashcompat = (cur->value != NULL) ? 1 : 0;
00606 } else {
00607 curl_easy_setopt(*curl, cur->key, cur->value);
00608 }
00609 }
00610
00611 if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
00612 list = store->data;
00613 AST_LIST_LOCK(list);
00614 AST_LIST_TRAVERSE(list, cur, list) {
00615 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
00616 hashcompat = (cur->value != NULL) ? 1 : 0;
00617 } else {
00618 curl_easy_setopt(*curl, cur->key, cur->value);
00619 }
00620 }
00621 }
00622
00623 curl_easy_setopt(*curl, CURLOPT_URL, args.url);
00624 curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
00625
00626 if (args.postdata) {
00627 curl_easy_setopt(*curl, CURLOPT_POST, 1);
00628 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
00629 }
00630
00631 curl_easy_perform(*curl);
00632
00633 if (store) {
00634 AST_LIST_UNLOCK(list);
00635 }
00636 AST_LIST_UNLOCK(&global_curl_info);
00637
00638 if (args.postdata) {
00639 curl_easy_setopt(*curl, CURLOPT_POST, 0);
00640 }
00641
00642 if (ast_str_strlen(str)) {
00643 ast_str_trim_blanks(str);
00644
00645 ast_debug(3, "str='%s'\n", ast_str_buffer(str));
00646 if (hashcompat) {
00647 char *remainder = ast_str_buffer(str);
00648 char *piece;
00649 struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
00650 struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
00651 int rowcount = 0;
00652 while (fields && values && (piece = strsep(&remainder, "&"))) {
00653 char *name = strsep(&piece, "=");
00654 if (piece) {
00655 ast_uri_decode(piece);
00656 }
00657 ast_uri_decode(name);
00658 ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
00659 ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
00660 rowcount++;
00661 }
00662 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
00663 if (buf) {
00664 ast_copy_string(buf, ast_str_buffer(values), len);
00665 } else {
00666 ast_str_set(input_str, len, "%s", ast_str_buffer(values));
00667 }
00668 ast_free(fields);
00669 ast_free(values);
00670 } else {
00671 if (buf) {
00672 ast_copy_string(buf, ast_str_buffer(str), len);
00673 } else {
00674 ast_str_set(input_str, len, "%s", ast_str_buffer(str));
00675 }
00676 }
00677 ret = 0;
00678 }
00679 ast_free(str);
00680
00681 if (chan)
00682 ast_autoservice_stop(chan);
00683
00684 return ret;
00685 }
00686
00687 static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
00688 {
00689 return acf_curl_helper(chan, cmd, info, buf, NULL, len);
00690 }
00691
00692 static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
00693 {
00694 return acf_curl_helper(chan, cmd, info, NULL, buf, len);
00695 }
00696
00697 static struct ast_custom_function acf_curl = {
00698 .name = "CURL",
00699 .synopsis = "Retrieves the contents of a URL",
00700 .syntax = "CURL(url[,post-data])",
00701 .desc =
00702 " url - URL to retrieve\n"
00703 " post-data - Optional data to send as a POST (GET is default action)\n",
00704 .read = acf_curl_exec,
00705 .read2 = acf_curl2_exec,
00706 };
00707
00708 static struct ast_custom_function acf_curlopt = {
00709 .name = "CURLOPT",
00710 .synopsis = "Set options for use with the CURL() function",
00711 .syntax = "CURLOPT(<option>)",
00712 .desc =
00713 " cookie - Send cookie with request [none]\n"
00714 " conntimeout - Number of seconds to wait for connection\n"
00715 " dnstimeout - Number of seconds to wait for DNS response\n"
00716 " ftptext - For FTP, force a text transfer (boolean)\n"
00717 " ftptimeout - For FTP, the server response timeout\n"
00718 " header - Retrieve header information (boolean)\n"
00719 " httptimeout - Number of seconds to wait for HTTP response\n"
00720 " maxredirs - Maximum number of redirects to follow\n"
00721 " proxy - Hostname or IP to use as a proxy\n"
00722 " proxytype - http, socks4, or socks5\n"
00723 " proxyport - port number of the proxy\n"
00724 " proxyuserpwd - A <user>:<pass> to use for authentication\n"
00725 " referer - Referer URL to use for the request\n"
00726 " useragent - UserAgent string to use\n"
00727 " userpwd - A <user>:<pass> to use for authentication\n"
00728 " ssl_verifypeer - Whether to verify the peer certificate (boolean)\n"
00729 " hashcompat - Result data will be compatible for use with HASH()\n"
00730 "",
00731 .read = acf_curlopt_read,
00732 .read2 = acf_curlopt_read2,
00733 .write = acf_curlopt_write,
00734 };
00735
00736 static int unload_module(void)
00737 {
00738 int res;
00739
00740 res = ast_custom_function_unregister(&acf_curl);
00741 res |= ast_custom_function_unregister(&acf_curlopt);
00742
00743 return res;
00744 }
00745
00746 static int load_module(void)
00747 {
00748 int res;
00749
00750 if (!ast_module_check("res_curl.so")) {
00751 if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
00752 ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
00753 return AST_MODULE_LOAD_DECLINE;
00754 }
00755 }
00756
00757 res = ast_custom_function_register(&acf_curl);
00758 res |= ast_custom_function_register(&acf_curlopt);
00759
00760 return res;
00761 }
00762
00763 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Load external URL",
00764 .load = load_module,
00765 .unload = unload_module,
00766 .load_pri = AST_MODPRI_REALTIME_DEPEND2,
00767 );
00768