func_curl.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2004 - 2006, Tilghman Lesher
  5. *
  6. * Tilghman Lesher <curl-20050919@the-tilghman.com>
  7. * and Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
  8. *
  9. * app_curl.c is distributed with no restrictions on usage or
  10. * redistribution.
  11. *
  12. * See http://www.asterisk.org for more information about
  13. * the Asterisk project. Please do not directly contact
  14. * any of the maintainers of this project for assistance;
  15. * the project provides a web site, mailing lists and IRC
  16. * channels for your use.
  17. *
  18. */
  19. /*! \file
  20. *
  21. * \brief Curl - Load a URL
  22. *
  23. * \author Tilghman Lesher <curl-20050919@the-tilghman.com>
  24. *
  25. * \note Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
  26. *
  27. * \extref Depends on the CURL library - http://curl.haxx.se/
  28. *
  29. * \ingroup functions
  30. */
  31. /*** MODULEINFO
  32. <depend>curl</depend>
  33. <support_level>core</support_level>
  34. ***/
  35. #include "asterisk.h"
  36. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  37. #include <curl/curl.h>
  38. #include "asterisk/lock.h"
  39. #include "asterisk/file.h"
  40. #include "asterisk/channel.h"
  41. #include "asterisk/pbx.h"
  42. #include "asterisk/cli.h"
  43. #include "asterisk/module.h"
  44. #include "asterisk/app.h"
  45. #include "asterisk/utils.h"
  46. #include "asterisk/threadstorage.h"
  47. /*** DOCUMENTATION
  48. <function name="CURL" language="en_US">
  49. <synopsis>
  50. Retrieve content from a remote web or ftp server
  51. </synopsis>
  52. <syntax>
  53. <parameter name="url" required="true" />
  54. <parameter name="post-data">
  55. <para>If specified, an <literal>HTTP POST</literal> will be
  56. performed with the content of
  57. <replaceable>post-data</replaceable>, instead of an
  58. <literal>HTTP GET</literal> (default).</para>
  59. </parameter>
  60. </syntax>
  61. <description />
  62. <see-also>
  63. <ref type="function">CURLOPT</ref>
  64. </see-also>
  65. </function>
  66. <function name="CURLOPT" language="en_US">
  67. <synopsis>
  68. Sets various options for future invocations of CURL.
  69. </synopsis>
  70. <syntax>
  71. <parameter name="key" required="yes">
  72. <enumlist>
  73. <enum name="cookie">
  74. <para>A cookie to send with the request. Multiple
  75. cookies are supported.</para>
  76. </enum>
  77. <enum name="conntimeout">
  78. <para>Number of seconds to wait for a connection to succeed</para>
  79. </enum>
  80. <enum name="dnstimeout">
  81. <para>Number of seconds to wait for DNS to be resolved</para>
  82. </enum>
  83. <enum name="ftptext">
  84. <para>For FTP URIs, force a text transfer (boolean)</para>
  85. </enum>
  86. <enum name="ftptimeout">
  87. <para>For FTP URIs, number of seconds to wait for a
  88. server response</para>
  89. </enum>
  90. <enum name="header">
  91. <para>Include header information in the result
  92. (boolean)</para>
  93. </enum>
  94. <enum name="httptimeout">
  95. <para>For HTTP(S) URIs, number of seconds to wait for a
  96. server response</para>
  97. </enum>
  98. <enum name="maxredirs">
  99. <para>Maximum number of redirects to follow</para>
  100. </enum>
  101. <enum name="proxy">
  102. <para>Hostname or IP address to use as a proxy server</para>
  103. </enum>
  104. <enum name="proxytype">
  105. <para>Type of <literal>proxy</literal></para>
  106. <enumlist>
  107. <enum name="http" />
  108. <enum name="socks4" />
  109. <enum name="socks5" />
  110. </enumlist>
  111. </enum>
  112. <enum name="proxyport">
  113. <para>Port number of the <literal>proxy</literal></para>
  114. </enum>
  115. <enum name="proxyuserpwd">
  116. <para>A <replaceable>username</replaceable><literal>:</literal><replaceable>password</replaceable>
  117. combination to use for authenticating requests through a
  118. <literal>proxy</literal></para>
  119. </enum>
  120. <enum name="referer">
  121. <para>Referer URL to use for the request</para>
  122. </enum>
  123. <enum name="useragent">
  124. <para>UserAgent string to use for the request</para>
  125. </enum>
  126. <enum name="userpwd">
  127. <para>A <replaceable>username</replaceable><literal>:</literal><replaceable>password</replaceable>
  128. to use for authentication when the server response to
  129. an initial request indicates a 401 status code.</para>
  130. </enum>
  131. <enum name="ssl_verifypeer">
  132. <para>Whether to verify the server certificate against
  133. a list of known root certificate authorities (boolean).</para>
  134. </enum>
  135. <enum name="hashcompat">
  136. <para>Assuming the responses will be in <literal>key1=value1&amp;key2=value2</literal>
  137. format, reformat the response such that it can be used
  138. by the <literal>HASH</literal> function.</para>
  139. </enum>
  140. </enumlist>
  141. </parameter>
  142. </syntax>
  143. <description>
  144. <para>Options may be set globally or per channel. Per-channel
  145. settings will override global settings.</para>
  146. </description>
  147. <see-also>
  148. <ref type="function">CURL</ref>
  149. <ref type="function">HASH</ref>
  150. </see-also>
  151. </function>
  152. ***/
  153. #define CURLVERSION_ATLEAST(a,b,c) \
  154. ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
  155. #define CURLOPT_SPECIAL_HASHCOMPAT -500
  156. static void curlds_free(void *data);
  157. static struct ast_datastore_info curl_info = {
  158. .type = "CURL",
  159. .destroy = curlds_free,
  160. };
  161. struct curl_settings {
  162. AST_LIST_ENTRY(curl_settings) list;
  163. CURLoption key;
  164. void *value;
  165. };
  166. AST_LIST_HEAD_STATIC(global_curl_info, curl_settings);
  167. static void curlds_free(void *data)
  168. {
  169. AST_LIST_HEAD(global_curl_info, curl_settings) *list = data;
  170. struct curl_settings *setting;
  171. if (!list) {
  172. return;
  173. }
  174. while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
  175. free(setting);
  176. }
  177. AST_LIST_HEAD_DESTROY(list);
  178. }
  179. enum optiontype {
  180. OT_BOOLEAN,
  181. OT_INTEGER,
  182. OT_INTEGER_MS,
  183. OT_STRING,
  184. OT_ENUM,
  185. };
  186. static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
  187. {
  188. if (!strcasecmp(name, "header")) {
  189. *key = CURLOPT_HEADER;
  190. *ot = OT_BOOLEAN;
  191. } else if (!strcasecmp(name, "proxy")) {
  192. *key = CURLOPT_PROXY;
  193. *ot = OT_STRING;
  194. } else if (!strcasecmp(name, "proxyport")) {
  195. *key = CURLOPT_PROXYPORT;
  196. *ot = OT_INTEGER;
  197. } else if (!strcasecmp(name, "proxytype")) {
  198. *key = CURLOPT_PROXYTYPE;
  199. *ot = OT_ENUM;
  200. } else if (!strcasecmp(name, "dnstimeout")) {
  201. *key = CURLOPT_DNS_CACHE_TIMEOUT;
  202. *ot = OT_INTEGER;
  203. } else if (!strcasecmp(name, "userpwd")) {
  204. *key = CURLOPT_USERPWD;
  205. *ot = OT_STRING;
  206. } else if (!strcasecmp(name, "proxyuserpwd")) {
  207. *key = CURLOPT_PROXYUSERPWD;
  208. *ot = OT_STRING;
  209. } else if (!strcasecmp(name, "maxredirs")) {
  210. *key = CURLOPT_MAXREDIRS;
  211. *ot = OT_INTEGER;
  212. } else if (!strcasecmp(name, "referer")) {
  213. *key = CURLOPT_REFERER;
  214. *ot = OT_STRING;
  215. } else if (!strcasecmp(name, "useragent")) {
  216. *key = CURLOPT_USERAGENT;
  217. *ot = OT_STRING;
  218. } else if (!strcasecmp(name, "cookie")) {
  219. *key = CURLOPT_COOKIE;
  220. *ot = OT_STRING;
  221. } else if (!strcasecmp(name, "ftptimeout")) {
  222. *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
  223. *ot = OT_INTEGER;
  224. } else if (!strcasecmp(name, "httptimeout")) {
  225. #if CURLVERSION_ATLEAST(7,16,2)
  226. *key = CURLOPT_TIMEOUT_MS;
  227. *ot = OT_INTEGER_MS;
  228. #else
  229. *key = CURLOPT_TIMEOUT;
  230. *ot = OT_INTEGER;
  231. #endif
  232. } else if (!strcasecmp(name, "conntimeout")) {
  233. #if CURLVERSION_ATLEAST(7,16,2)
  234. *key = CURLOPT_CONNECTTIMEOUT_MS;
  235. *ot = OT_INTEGER_MS;
  236. #else
  237. *key = CURLOPT_CONNECTTIMEOUT;
  238. *ot = OT_INTEGER;
  239. #endif
  240. } else if (!strcasecmp(name, "ftptext")) {
  241. *key = CURLOPT_TRANSFERTEXT;
  242. *ot = OT_BOOLEAN;
  243. } else if (!strcasecmp(name, "ssl_verifypeer")) {
  244. *key = CURLOPT_SSL_VERIFYPEER;
  245. *ot = OT_BOOLEAN;
  246. } else if (!strcasecmp(name, "hashcompat")) {
  247. *key = CURLOPT_SPECIAL_HASHCOMPAT;
  248. *ot = OT_BOOLEAN;
  249. } else {
  250. return -1;
  251. }
  252. return 0;
  253. }
  254. static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
  255. {
  256. struct ast_datastore *store;
  257. struct global_curl_info *list;
  258. struct curl_settings *cur, *new = NULL;
  259. CURLoption key;
  260. enum optiontype ot;
  261. if (chan) {
  262. if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
  263. /* Create a new datastore */
  264. if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
  265. ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
  266. return -1;
  267. }
  268. if (!(list = ast_calloc(1, sizeof(*list)))) {
  269. ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
  270. ast_datastore_free(store);
  271. }
  272. store->data = list;
  273. AST_LIST_HEAD_INIT(list);
  274. ast_channel_datastore_add(chan, store);
  275. } else {
  276. list = store->data;
  277. }
  278. } else {
  279. /* Populate the global structure */
  280. list = &global_curl_info;
  281. }
  282. if (!parse_curlopt_key(name, &key, &ot)) {
  283. if (ot == OT_BOOLEAN) {
  284. if ((new = ast_calloc(1, sizeof(*new)))) {
  285. new->value = (void *)((long) ast_true(value));
  286. }
  287. } else if (ot == OT_INTEGER) {
  288. long tmp = atol(value);
  289. if ((new = ast_calloc(1, sizeof(*new)))) {
  290. new->value = (void *)tmp;
  291. }
  292. } else if (ot == OT_INTEGER_MS) {
  293. long tmp = atof(value) * 1000.0;
  294. if ((new = ast_calloc(1, sizeof(*new)))) {
  295. new->value = (void *)tmp;
  296. }
  297. } else if (ot == OT_STRING) {
  298. if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
  299. new->value = (char *)new + sizeof(*new);
  300. strcpy(new->value, value);
  301. }
  302. } else if (ot == OT_ENUM) {
  303. if (key == CURLOPT_PROXYTYPE) {
  304. long ptype =
  305. #if CURLVERSION_ATLEAST(7,10,0)
  306. CURLPROXY_HTTP;
  307. #else
  308. CURLPROXY_SOCKS5;
  309. #endif
  310. if (0) {
  311. #if CURLVERSION_ATLEAST(7,15,2)
  312. } else if (!strcasecmp(value, "socks4")) {
  313. ptype = CURLPROXY_SOCKS4;
  314. #endif
  315. #if CURLVERSION_ATLEAST(7,18,0)
  316. } else if (!strcasecmp(value, "socks4a")) {
  317. ptype = CURLPROXY_SOCKS4A;
  318. #endif
  319. #if CURLVERSION_ATLEAST(7,18,0)
  320. } else if (!strcasecmp(value, "socks5")) {
  321. ptype = CURLPROXY_SOCKS5;
  322. #endif
  323. #if CURLVERSION_ATLEAST(7,18,0)
  324. } else if (!strncasecmp(value, "socks5", 6)) {
  325. ptype = CURLPROXY_SOCKS5_HOSTNAME;
  326. #endif
  327. }
  328. if ((new = ast_calloc(1, sizeof(*new)))) {
  329. new->value = (void *)ptype;
  330. }
  331. } else {
  332. /* Highly unlikely */
  333. goto yuck;
  334. }
  335. }
  336. /* Memory allocation error */
  337. if (!new) {
  338. return -1;
  339. }
  340. new->key = key;
  341. } else {
  342. yuck:
  343. ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
  344. return -1;
  345. }
  346. /* Remove any existing entry */
  347. AST_LIST_LOCK(list);
  348. AST_LIST_TRAVERSE_SAFE_BEGIN(list, cur, list) {
  349. if (cur->key == new->key) {
  350. AST_LIST_REMOVE_CURRENT(list);
  351. free(cur);
  352. break;
  353. }
  354. }
  355. AST_LIST_TRAVERSE_SAFE_END
  356. /* Insert new entry */
  357. ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
  358. AST_LIST_INSERT_TAIL(list, new, list);
  359. AST_LIST_UNLOCK(list);
  360. return 0;
  361. }
  362. static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
  363. {
  364. struct ast_datastore *store;
  365. struct global_curl_info *list[2] = { &global_curl_info, NULL };
  366. struct curl_settings *cur = NULL;
  367. CURLoption key;
  368. enum optiontype ot;
  369. int i;
  370. if (parse_curlopt_key(data, &key, &ot)) {
  371. ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
  372. return -1;
  373. }
  374. if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
  375. list[0] = store->data;
  376. list[1] = &global_curl_info;
  377. }
  378. for (i = 0; i < 2; i++) {
  379. if (!list[i]) {
  380. break;
  381. }
  382. AST_LIST_LOCK(list[i]);
  383. AST_LIST_TRAVERSE(list[i], cur, list) {
  384. if (cur->key == key) {
  385. if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
  386. if (buf) {
  387. snprintf(buf, len, "%ld", (long) cur->value);
  388. } else {
  389. ast_str_set(bufstr, len, "%ld", (long) cur->value);
  390. }
  391. } else if (ot == OT_INTEGER_MS) {
  392. if ((long) cur->value % 1000 == 0) {
  393. if (buf) {
  394. snprintf(buf, len, "%ld", (long)cur->value / 1000);
  395. } else {
  396. ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
  397. }
  398. } else {
  399. if (buf) {
  400. snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
  401. } else {
  402. ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
  403. }
  404. }
  405. } else if (ot == OT_STRING) {
  406. ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
  407. if (buf) {
  408. ast_copy_string(buf, cur->value, len);
  409. } else {
  410. ast_str_set(bufstr, 0, "%s", (char *) cur->value);
  411. }
  412. } else if (key == CURLOPT_PROXYTYPE) {
  413. if (0) {
  414. #if CURLVERSION_ATLEAST(7,15,2)
  415. } else if ((long)cur->value == CURLPROXY_SOCKS4) {
  416. if (buf) {
  417. ast_copy_string(buf, "socks4", len);
  418. } else {
  419. ast_str_set(bufstr, 0, "socks4");
  420. }
  421. #endif
  422. #if CURLVERSION_ATLEAST(7,18,0)
  423. } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
  424. if (buf) {
  425. ast_copy_string(buf, "socks4a", len);
  426. } else {
  427. ast_str_set(bufstr, 0, "socks4a");
  428. }
  429. #endif
  430. } else if ((long)cur->value == CURLPROXY_SOCKS5) {
  431. if (buf) {
  432. ast_copy_string(buf, "socks5", len);
  433. } else {
  434. ast_str_set(bufstr, 0, "socks5");
  435. }
  436. #if CURLVERSION_ATLEAST(7,18,0)
  437. } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
  438. if (buf) {
  439. ast_copy_string(buf, "socks5hostname", len);
  440. } else {
  441. ast_str_set(bufstr, 0, "socks5hostname");
  442. }
  443. #endif
  444. #if CURLVERSION_ATLEAST(7,10,0)
  445. } else if ((long)cur->value == CURLPROXY_HTTP) {
  446. if (buf) {
  447. ast_copy_string(buf, "http", len);
  448. } else {
  449. ast_str_set(bufstr, 0, "http");
  450. }
  451. #endif
  452. } else {
  453. if (buf) {
  454. ast_copy_string(buf, "unknown", len);
  455. } else {
  456. ast_str_set(bufstr, 0, "unknown");
  457. }
  458. }
  459. }
  460. break;
  461. }
  462. }
  463. AST_LIST_UNLOCK(list[i]);
  464. if (cur) {
  465. break;
  466. }
  467. }
  468. return cur ? 0 : -1;
  469. }
  470. static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
  471. {
  472. return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
  473. }
  474. static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
  475. {
  476. return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
  477. }
  478. static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
  479. {
  480. register int realsize = size * nmemb;
  481. struct ast_str **pstr = (struct ast_str **)data;
  482. 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));
  483. ast_str_append_substr(pstr, 0, ptr, realsize);
  484. ast_debug(3, "Now, len=%zu, used=%zu\n", ast_str_size(*pstr), ast_str_strlen(*pstr));
  485. return realsize;
  486. }
  487. static const char * const global_useragent = "asterisk-libcurl-agent/1.0";
  488. static int curl_instance_init(void *data)
  489. {
  490. CURL **curl = data;
  491. if (!(*curl = curl_easy_init()))
  492. return -1;
  493. curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
  494. curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
  495. curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
  496. curl_easy_setopt(*curl, CURLOPT_USERAGENT, global_useragent);
  497. return 0;
  498. }
  499. static void curl_instance_cleanup(void *data)
  500. {
  501. CURL **curl = data;
  502. curl_easy_cleanup(*curl);
  503. ast_free(data);
  504. }
  505. AST_THREADSTORAGE_CUSTOM(curl_instance, curl_instance_init, curl_instance_cleanup);
  506. AST_THREADSTORAGE(thread_escapebuf);
  507. static int acf_curl_helper(struct ast_channel *chan, const char *cmd, char *info, char *buf, struct ast_str **input_str, ssize_t len)
  508. {
  509. struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
  510. struct ast_str *str = ast_str_create(16);
  511. int ret = -1;
  512. AST_DECLARE_APP_ARGS(args,
  513. AST_APP_ARG(url);
  514. AST_APP_ARG(postdata);
  515. );
  516. CURL **curl;
  517. struct curl_settings *cur;
  518. struct ast_datastore *store = NULL;
  519. int hashcompat = 0;
  520. AST_LIST_HEAD(global_curl_info, curl_settings) *list = NULL;
  521. if (buf) {
  522. *buf = '\0';
  523. }
  524. if (!str) {
  525. return -1;
  526. }
  527. if (!escapebuf) {
  528. ast_free(str);
  529. return -1;
  530. }
  531. if (ast_strlen_zero(info)) {
  532. ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
  533. ast_free(str);
  534. return -1;
  535. }
  536. AST_STANDARD_APP_ARGS(args, info);
  537. if (chan) {
  538. ast_autoservice_start(chan);
  539. }
  540. if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
  541. ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
  542. return -1;
  543. }
  544. AST_LIST_LOCK(&global_curl_info);
  545. AST_LIST_TRAVERSE(&global_curl_info, cur, list) {
  546. if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
  547. hashcompat = (cur->value != NULL) ? 1 : 0;
  548. } else {
  549. curl_easy_setopt(*curl, cur->key, cur->value);
  550. }
  551. }
  552. if (chan && (store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
  553. list = store->data;
  554. AST_LIST_LOCK(list);
  555. AST_LIST_TRAVERSE(list, cur, list) {
  556. if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
  557. hashcompat = (cur->value != NULL) ? 1 : 0;
  558. } else {
  559. curl_easy_setopt(*curl, cur->key, cur->value);
  560. }
  561. }
  562. }
  563. curl_easy_setopt(*curl, CURLOPT_URL, args.url);
  564. curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &str);
  565. if (args.postdata) {
  566. curl_easy_setopt(*curl, CURLOPT_POST, 1);
  567. curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args.postdata);
  568. }
  569. curl_easy_perform(*curl);
  570. if (store) {
  571. AST_LIST_UNLOCK(list);
  572. }
  573. AST_LIST_UNLOCK(&global_curl_info);
  574. if (args.postdata) {
  575. curl_easy_setopt(*curl, CURLOPT_POST, 0);
  576. }
  577. if (ast_str_strlen(str)) {
  578. ast_str_trim_blanks(str);
  579. ast_debug(3, "str='%s'\n", ast_str_buffer(str));
  580. if (hashcompat) {
  581. char *remainder = ast_str_buffer(str);
  582. char *piece;
  583. struct ast_str *fields = ast_str_create(ast_str_strlen(str) / 2);
  584. struct ast_str *values = ast_str_create(ast_str_strlen(str) / 2);
  585. int rowcount = 0;
  586. while (fields && values && (piece = strsep(&remainder, "&"))) {
  587. char *name = strsep(&piece, "=");
  588. if (piece) {
  589. ast_uri_decode(piece);
  590. }
  591. ast_uri_decode(name);
  592. ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
  593. ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
  594. rowcount++;
  595. }
  596. pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
  597. if (buf) {
  598. ast_copy_string(buf, ast_str_buffer(values), len);
  599. } else {
  600. ast_str_set(input_str, len, "%s", ast_str_buffer(values));
  601. }
  602. ast_free(fields);
  603. ast_free(values);
  604. } else {
  605. if (buf) {
  606. ast_copy_string(buf, ast_str_buffer(str), len);
  607. } else {
  608. ast_str_set(input_str, len, "%s", ast_str_buffer(str));
  609. }
  610. }
  611. ret = 0;
  612. }
  613. ast_free(str);
  614. if (chan)
  615. ast_autoservice_stop(chan);
  616. return ret;
  617. }
  618. static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, char *buf, size_t len)
  619. {
  620. return acf_curl_helper(chan, cmd, info, buf, NULL, len);
  621. }
  622. static int acf_curl2_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
  623. {
  624. return acf_curl_helper(chan, cmd, info, NULL, buf, len);
  625. }
  626. static struct ast_custom_function acf_curl = {
  627. .name = "CURL",
  628. .synopsis = "Retrieves the contents of a URL",
  629. .syntax = "CURL(url[,post-data])",
  630. .desc =
  631. " url - URL to retrieve\n"
  632. " post-data - Optional data to send as a POST (GET is default action)\n",
  633. .read = acf_curl_exec,
  634. .read2 = acf_curl2_exec,
  635. };
  636. static struct ast_custom_function acf_curlopt = {
  637. .name = "CURLOPT",
  638. .synopsis = "Set options for use with the CURL() function",
  639. .syntax = "CURLOPT(<option>)",
  640. .desc =
  641. " cookie - Send cookie with request [none]\n"
  642. " conntimeout - Number of seconds to wait for connection\n"
  643. " dnstimeout - Number of seconds to wait for DNS response\n"
  644. " ftptext - For FTP, force a text transfer (boolean)\n"
  645. " ftptimeout - For FTP, the server response timeout\n"
  646. " header - Retrieve header information (boolean)\n"
  647. " httptimeout - Number of seconds to wait for HTTP response\n"
  648. " maxredirs - Maximum number of redirects to follow\n"
  649. " proxy - Hostname or IP to use as a proxy\n"
  650. " proxytype - http, socks4, or socks5\n"
  651. " proxyport - port number of the proxy\n"
  652. " proxyuserpwd - A <user>:<pass> to use for authentication\n"
  653. " referer - Referer URL to use for the request\n"
  654. " useragent - UserAgent string to use\n"
  655. " userpwd - A <user>:<pass> to use for authentication\n"
  656. " ssl_verifypeer - Whether to verify the peer certificate (boolean)\n"
  657. " hashcompat - Result data will be compatible for use with HASH()\n"
  658. "",
  659. .read = acf_curlopt_read,
  660. .read2 = acf_curlopt_read2,
  661. .write = acf_curlopt_write,
  662. };
  663. static int unload_module(void)
  664. {
  665. int res;
  666. res = ast_custom_function_unregister(&acf_curl);
  667. res |= ast_custom_function_unregister(&acf_curlopt);
  668. return res;
  669. }
  670. static int load_module(void)
  671. {
  672. int res;
  673. if (!ast_module_check("res_curl.so")) {
  674. if (ast_load_resource("res_curl.so") != AST_MODULE_LOAD_SUCCESS) {
  675. ast_log(LOG_ERROR, "Cannot load res_curl, so func_curl cannot be loaded\n");
  676. return AST_MODULE_LOAD_DECLINE;
  677. }
  678. }
  679. res = ast_custom_function_register(&acf_curl);
  680. res |= ast_custom_function_register(&acf_curlopt);
  681. return res;
  682. }
  683. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Load external URL",
  684. .load = load_module,
  685. .unload = unload_module,
  686. .load_pri = AST_MODPRI_REALTIME_DEPEND2,
  687. );