Asterisk - The Open Source Telephony Project GIT-master-77d630f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
func_curl.c
Go to the documentation of this file.
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
20/*! \file
21 *
22 * \brief Curl - Load a URL
23 *
24 * \author Tilghman Lesher <curl-20050919@the-tilghman.com>
25 *
26 * \note Brian Wilkins <bwilkins@cfl.rr.com> (Added POST option)
27 *
28 * \extref Depends on the CURL library - http://curl.haxx.se/
29 *
30 * \ingroup functions
31 */
32
33/*** MODULEINFO
34 <depend>res_curl</depend>
35 <depend>curl</depend>
36 <support_level>core</support_level>
37 ***/
38
39#include "asterisk.h"
40
41#include <curl/curl.h>
42
43#include "asterisk/lock.h"
44#include "asterisk/file.h"
45#include "asterisk/channel.h"
46#include "asterisk/pbx.h"
47#include "asterisk/cli.h"
48#include "asterisk/module.h"
49#include "asterisk/app.h"
50#include "asterisk/utils.h"
52#include "asterisk/test.h"
53
54/*** DOCUMENTATION
55 <function name="CURL" language="en_US">
56 <since>
57 <version>10.0.0</version>
58 </since>
59 <synopsis>
60 Retrieve content from a remote web or ftp server
61 </synopsis>
62 <syntax>
63 <parameter name="url" required="true">
64 <para>The full URL for the resource to retrieve.</para>
65 </parameter>
66 <parameter name="post-data">
67 <para><emphasis>Read Only</emphasis></para>
68 <para>If specified, an <literal>HTTP POST</literal> will be
69 performed with the content of
70 <replaceable>post-data</replaceable>, instead of an
71 <literal>HTTP GET</literal> (default).</para>
72 </parameter>
73 </syntax>
74 <description>
75 <para>When this function is read, a <literal>HTTP GET</literal>
76 (by default) will be used to retrieve the contents of the provided
77 <replaceable>url</replaceable>. The contents are returned as the
78 result of the function.</para>
79 <example title="Displaying contents of a page" language="text">
80 exten => s,1,Verbose(0, ${CURL(http://localhost:8088/static/astman.css)})
81 </example>
82 <para>When this function is written to, a <literal>HTTP GET</literal>
83 will be used to retrieve the contents of the provided
84 <replaceable>url</replaceable>. The value written to the function
85 specifies the destination file of the cURL'd resource.</para>
86 <example title="Retrieving a file" language="text">
87 exten => s,1,Set(CURL(http://localhost:8088/static/astman.css)=/var/spool/asterisk/tmp/astman.css))
88 </example>
89 <note>
90 <para>If <literal>live_dangerously</literal> in <literal>asterisk.conf</literal>
91 is set to <literal>no</literal>, this function can only be written to from the
92 dialplan, and not directly from external protocols. Read operations are
93 unaffected.</para>
94 </note>
95 </description>
96 <see-also>
97 <ref type="function">CURLOPT</ref>
98 </see-also>
99 </function>
100 <function name="CURLOPT" language="en_US">
101 <since>
102 <version>10.0.0</version>
103 </since>
104 <synopsis>
105 Sets various options for future invocations of CURL.
106 </synopsis>
107 <syntax>
108 <parameter name="key" required="yes">
109 <enumlist>
110 <enum name="cookie">
111 <para>A cookie to send with the request. Multiple
112 cookies are supported.</para>
113 </enum>
114 <enum name="conntimeout">
115 <para>Number of seconds to wait for a connection to succeed</para>
116 </enum>
117 <enum name="dnstimeout">
118 <para>Number of seconds to wait for DNS to be resolved</para>
119 </enum>
120 <enum name="followlocation">
121 <para>Whether or not to follow HTTP 3xx redirects (boolean)</para>
122 </enum>
123 <enum name="ftptext">
124 <para>For FTP URIs, force a text transfer (boolean)</para>
125 </enum>
126 <enum name="ftptimeout">
127 <para>For FTP URIs, number of seconds to wait for a
128 server response</para>
129 </enum>
130 <enum name="header">
131 <para>Include header information in the result
132 (boolean)</para>
133 </enum>
134 <enum name="httpauth">
135 <para>Type of authentication method to use. The default is Basic Authentication.</para>
136 <para>Multiple values can be specified to enable multiple authentication methods.
137 To do so, invoke CURLOPT once using comma-separated values.</para>
138 <enumlist>
139 <enum name="basic" />
140 <enum name="digest" />
141 </enumlist>
142 </enum>
143 <enum name="httpheader">
144 <para>Add HTTP header. Multiple calls add multiple headers.
145 Setting of any header will remove the default
146 "Content-Type application/x-www-form-urlencoded"</para>
147 </enum>
148 <enum name="httptimeout">
149 <para>For HTTP(S) URIs, number of seconds to wait for a
150 server response</para>
151 </enum>
152 <enum name="maxredirs">
153 <para>Maximum number of redirects to follow. The default is -1,
154 which allows for unlimited redirects. This only makes sense when
155 followlocation is also set.</para>
156 </enum>
157 <enum name="proxy">
158 <para>Hostname or IP address to use as a proxy server</para>
159 </enum>
160 <enum name="proxytype">
161 <para>Type of <literal>proxy</literal></para>
162 <enumlist>
163 <enum name="http" />
164 <enum name="socks4" />
165 <enum name="socks5" />
166 </enumlist>
167 </enum>
168 <enum name="proxyport">
169 <para>Port number of the <literal>proxy</literal></para>
170 </enum>
171 <enum name="proxyuserpwd">
172 <para>A <replaceable>username</replaceable><literal>:</literal><replaceable>password</replaceable>
173 combination to use for authenticating requests through a
174 <literal>proxy</literal></para>
175 </enum>
176 <enum name="referer">
177 <para>Referer URL to use for the request</para>
178 </enum>
179 <enum name="useragent">
180 <para>UserAgent string to use for the request</para>
181 </enum>
182 <enum name="userpwd">
183 <para>A <replaceable>username</replaceable><literal>:</literal><replaceable>password</replaceable>
184 to use for authentication when the server response to
185 an initial request indicates a 401 status code.</para>
186 </enum>
187 <enum name="ssl_verifypeer">
188 <para>Whether to verify the server certificate against
189 a list of known root certificate authorities (boolean).</para>
190 </enum>
191 <enum name="ssl_verifyhost">
192 <para>Whether to verify the host in the server's TLS certificate.
193 Set to 2 to verify the host, 0 to ignore the host.</para>
194 </enum>
195 <enum name="ssl_cainfo">
196 <para>Path to a file holding one or more certificates to verify
197 the peer's certificate with. Only used when <literal>ssl_verifypeer</literal>
198 is enabled.</para>
199 </enum>
200 <enum name="ssl_capath">
201 <para>Path to a directory holding multiple CA certificates to
202 verify the peer's certificate with. Only used when <literal>ssl_verifypeer</literal>
203 is enabled.</para>
204 </enum>
205 <enum name="ssl_cert">
206 <para>Path to a file containing a client certificate. Default format
207 is PEM, and can be changed with <literal>ssl_certtype</literal>.</para>
208 </enum>
209 <enum name="ssl_certtype">
210 <para>The format of the <literal>ssl_cert</literal> file.</para>
211 <enumlist>
212 <enum name="PEM" />
213 <enum name="DER" />
214 </enumlist>
215 </enum>
216 <enum name="ssl_key">
217 <para>Path to a file containing a client private key. Default format
218 is PEM, and can be changed with <literal>ssl_keytype</literal></para>
219 </enum>
220 <enum name="ssl_keytype">
221 <para>The format of the <literal>ssl_key</literal> file.</para>
222 <enumlist>
223 <enum name="PEM" />
224 <enum name="DER" />
225 <enum name="ENG" />
226 </enumlist>
227 </enum>
228 <enum name="ssl_keypasswd">
229 <para>The passphrase to use the <literal>ssl_key</literal> file.</para>
230 </enum>
231 <enum name="hashcompat">
232 <para>Assuming the responses will be in <literal>key1=value1&amp;key2=value2</literal>
233 format, reformat the response such that it can be used
234 by the <literal>HASH</literal> function.</para>
235 <enumlist>
236 <enum name="yes" />
237 <enum name="no" />
238 <enum name="legacy">
239 <para>Also translate <literal>+</literal> to the
240 space character, in violation of current RFC
241 standards.</para>
242 </enum>
243 </enumlist>
244 </enum>
245 <enum name="failurecodes">
246 <para>A comma separated list of HTTP response codes to be treated as errors</para>
247 </enum>
248 </enumlist>
249 </parameter>
250 </syntax>
251 <description>
252 <para>Options may be set globally or per channel. Per-channel
253 settings will override global settings. Only HTTP headers are added instead of overriding</para>
254 </description>
255 <see-also>
256 <ref type="function">CURL</ref>
257 <ref type="function">HASH</ref>
258 </see-also>
259 </function>
260 ***/
261
262#define CURLVERSION_ATLEAST(a,b,c) \
263 ((LIBCURL_VERSION_MAJOR > (a)) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR > (b))) || ((LIBCURL_VERSION_MAJOR == (a)) && (LIBCURL_VERSION_MINOR == (b)) && (LIBCURL_VERSION_PATCH >= (c))))
264
265#define CURLOPT_SPECIAL_HASHCOMPAT ((CURLoption) -500)
266
267#define CURLOPT_SPECIAL_FAILURE_CODE 999
268
269static void curlds_free(void *data);
270
271static const struct ast_datastore_info curl_info = {
272 .type = "CURL",
273 .destroy = curlds_free,
274};
275
278 CURLoption key;
279 void *value;
280};
281
283
284static void curlds_free(void *data)
285{
287 struct curl_settings *setting;
288 if (!list) {
289 return;
290 }
291 while ((setting = AST_LIST_REMOVE_HEAD(list, list))) {
292 ast_free(setting);
293 }
295 ast_free(list);
296}
297
304};
305
310};
311
312static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
313{
314 if (!strcasecmp(name, "header")) {
315 *key = CURLOPT_HEADER;
316 *ot = OT_BOOLEAN;
317 } else if (!strcasecmp(name, "httpheader")) {
318 *key = CURLOPT_HTTPHEADER;
319 *ot = OT_STRING;
320 } else if (!strcasecmp(name, "httpauth")) {
321 *key = CURLOPT_HTTPAUTH;
322 *ot = OT_ENUM;
323 } else if (!strcasecmp(name, "proxy")) {
324 *key = CURLOPT_PROXY;
325 *ot = OT_STRING;
326 } else if (!strcasecmp(name, "proxyport")) {
327 *key = CURLOPT_PROXYPORT;
328 *ot = OT_INTEGER;
329 } else if (!strcasecmp(name, "proxytype")) {
330 *key = CURLOPT_PROXYTYPE;
331 *ot = OT_ENUM;
332 } else if (!strcasecmp(name, "dnstimeout")) {
333 *key = CURLOPT_DNS_CACHE_TIMEOUT;
334 *ot = OT_INTEGER;
335 } else if (!strcasecmp(name, "userpwd")) {
336 *key = CURLOPT_USERPWD;
337 *ot = OT_STRING;
338 } else if (!strcasecmp(name, "proxyuserpwd")) {
339 *key = CURLOPT_PROXYUSERPWD;
340 *ot = OT_STRING;
341 } else if (!strcasecmp(name, "followlocation")) {
342 *key = CURLOPT_FOLLOWLOCATION;
343 *ot = OT_BOOLEAN;
344 } else if (!strcasecmp(name, "maxredirs")) {
345 *key = CURLOPT_MAXREDIRS;
346 *ot = OT_INTEGER;
347 } else if (!strcasecmp(name, "referer")) {
348 *key = CURLOPT_REFERER;
349 *ot = OT_STRING;
350 } else if (!strcasecmp(name, "useragent")) {
351 *key = CURLOPT_USERAGENT;
352 *ot = OT_STRING;
353 } else if (!strcasecmp(name, "cookie")) {
354 *key = CURLOPT_COOKIE;
355 *ot = OT_STRING;
356 } else if (!strcasecmp(name, "ftptimeout")) {
357 *key = CURLOPT_FTP_RESPONSE_TIMEOUT;
358 *ot = OT_INTEGER;
359 } else if (!strcasecmp(name, "httptimeout")) {
360#if CURLVERSION_ATLEAST(7,16,2)
361 *key = CURLOPT_TIMEOUT_MS;
362 *ot = OT_INTEGER_MS;
363#else
364 *key = CURLOPT_TIMEOUT;
365 *ot = OT_INTEGER;
366#endif
367 } else if (!strcasecmp(name, "conntimeout")) {
368#if CURLVERSION_ATLEAST(7,16,2)
369 *key = CURLOPT_CONNECTTIMEOUT_MS;
370 *ot = OT_INTEGER_MS;
371#else
372 *key = CURLOPT_CONNECTTIMEOUT;
373 *ot = OT_INTEGER;
374#endif
375 } else if (!strcasecmp(name, "ftptext")) {
376 *key = CURLOPT_TRANSFERTEXT;
377 *ot = OT_BOOLEAN;
378 } else if (!strcasecmp(name, "ssl_verifypeer")) {
379 *key = CURLOPT_SSL_VERIFYPEER;
380 *ot = OT_BOOLEAN;
381 } else if (!strcasecmp(name, "ssl_verifyhost")) {
382 *key = CURLOPT_SSL_VERIFYHOST;
383 *ot = OT_INTEGER;
384 } else if (!strcasecmp(name, "ssl_cainfo")) {
385 *key = CURLOPT_CAINFO;
386 *ot = OT_STRING;
387 } else if (!strcasecmp(name, "ssl_capath")) {
388 *key = CURLOPT_CAPATH;
389 *ot = OT_STRING;
390 } else if (!strcasecmp(name, "ssl_cert")) {
391 *key = CURLOPT_SSLCERT;
392 *ot = OT_STRING;
393 } else if (!strcasecmp(name, "ssl_certtype")) {
394 *key = CURLOPT_SSLCERTTYPE;
395 *ot = OT_STRING;
396 } else if (!strcasecmp(name, "ssl_key")) {
397 *key = CURLOPT_SSLKEY;
398 *ot = OT_STRING;
399 } else if (!strcasecmp(name, "ssl_keytype")) {
400 *key = CURLOPT_SSLKEYTYPE;
401 *ot = OT_STRING;
402 } else if (!strcasecmp(name, "ssl_keypasswd")) {
403 *key = CURLOPT_KEYPASSWD;
404 *ot = OT_STRING;
405 } else if (!strcasecmp(name, "hashcompat")) {
407 *ot = OT_ENUM;
408 } else if (!strcasecmp(name, "failurecodes")) {
410 *ot = OT_STRING;
411 } else {
412 return -1;
413 }
414 return 0;
415}
416
417static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
418{
419 struct ast_datastore *store;
420 struct global_curl_info *list;
421 struct curl_settings *cur, *new = NULL;
422 CURLoption key;
423 enum optiontype ot;
424
425 if (chan) {
426 ast_channel_lock(chan);
427 if (!(store = ast_channel_datastore_find(chan, &curl_info, NULL))) {
428 /* Create a new datastore */
429 if (!(store = ast_datastore_alloc(&curl_info, NULL))) {
430 ast_log(LOG_ERROR, "Unable to allocate new datastore. Cannot set any CURL options\n");
431 ast_channel_unlock(chan);
432 return -1;
433 }
434
435 if (!(list = ast_calloc(1, sizeof(*list)))) {
436 ast_log(LOG_ERROR, "Unable to allocate list head. Cannot set any CURL options\n");
437 ast_datastore_free(store);
438 ast_channel_unlock(chan);
439 return -1;
440 }
441
442 store->data = list;
444 ast_channel_datastore_add(chan, store);
445 } else {
446 list = store->data;
447 }
448 ast_channel_unlock(chan);
449 } else {
450 /* Populate the global structure */
452 }
453
454 if (!parse_curlopt_key(name, &key, &ot)) {
455 if (ot == OT_BOOLEAN) {
456 if ((new = ast_calloc(1, sizeof(*new)))) {
457 new->value = (void *)((long) ast_true(value));
458 }
459 } else if (ot == OT_INTEGER) {
460 long tmp = atol(value);
461 if ((new = ast_calloc(1, sizeof(*new)))) {
462 new->value = (void *)tmp;
463 }
464 } else if (ot == OT_INTEGER_MS) {
465 long tmp = atof(value) * 1000.0;
466 if ((new = ast_calloc(1, sizeof(*new)))) {
467 new->value = (void *)tmp;
468 }
469 } else if (ot == OT_STRING) {
470 if ((new = ast_calloc(1, sizeof(*new) + strlen(value) + 1))) {
471 new->value = (char *)new + sizeof(*new);
472 strcpy(new->value, value);
473 }
474 } else if (ot == OT_ENUM) {
475 if (key == CURLOPT_PROXYTYPE) {
476 long ptype =
477#if CURLVERSION_ATLEAST(7,10,0)
478 CURLPROXY_HTTP;
479#else
480 CURLPROXY_SOCKS5;
481#endif
482 if (0) {
483#if CURLVERSION_ATLEAST(7,15,2)
484 } else if (!strcasecmp(value, "socks4")) {
485 ptype = CURLPROXY_SOCKS4;
486#endif
487#if CURLVERSION_ATLEAST(7,18,0)
488 } else if (!strcasecmp(value, "socks4a")) {
489 ptype = CURLPROXY_SOCKS4A;
490#endif
491#if CURLVERSION_ATLEAST(7,18,0)
492 } else if (!strcasecmp(value, "socks5")) {
493 ptype = CURLPROXY_SOCKS5;
494#endif
495#if CURLVERSION_ATLEAST(7,18,0)
496 } else if (!strncasecmp(value, "socks5", 6)) {
497 ptype = CURLPROXY_SOCKS5_HOSTNAME;
498#endif
499 }
500
501 if ((new = ast_calloc(1, sizeof(*new)))) {
502 new->value = (void *)ptype;
503 }
504 } else if (key == CURLOPT_HTTPAUTH) {
505 long authtype = 0;
506 char *authmethod, *authstr = ast_strdupa(value);
507 while ((authmethod = strsep(&authstr, ","))) {
508 if (!strcasecmp(authmethod, "basic")) {
509 authtype |= CURLAUTH_BASIC;
510 } else if (!strcasecmp(authmethod, "digest")) {
511 authtype |= CURLAUTH_DIGEST;
512 } else {
513 ast_log(LOG_WARNING, "Auth method '%s' invalid or not supported\n", authmethod);
514 return -1;
515 }
516 }
517 if (!authmethod) {
518 ast_log(LOG_WARNING, "Auth method '%s' invalid or not supported\n", value);
519 }
520 if ((new = ast_calloc(1, sizeof(*new)))) {
521 new->value = (void *)authtype;
522 }
523 } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
524 if ((new = ast_calloc(1, sizeof(*new)))) {
525 new->value = (void *) (long) (!strcasecmp(value, "legacy") ? HASHCOMPAT_LEGACY : ast_true(value) ? HASHCOMPAT_YES : HASHCOMPAT_NO);
526 }
527 } else {
528 /* Highly unlikely */
529 goto yuck;
530 }
531 }
532
533 /* Memory allocation error */
534 if (!new) {
535 return -1;
536 }
537
538 new->key = key;
539 } else {
540yuck:
541 ast_log(LOG_ERROR, "Unrecognized option: %s\n", name);
542 return -1;
543 }
544
545 /* Remove any existing entry, only http headers are left */
547 if (new->key != CURLOPT_HTTPHEADER) {
549 if (cur->key == new->key) {
551 ast_free(cur);
552 break;
553 }
554 }
556 }
557
558 /* Insert new entry */
559 ast_debug(1, "Inserting entry %p with key %d and value %p\n", new, new->key, new->value);
562
563 return 0;
564}
565
566static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
567{
568 struct ast_datastore *store;
569 struct global_curl_info *list[2] = { &global_curl_info, NULL };
570 struct curl_settings *cur = NULL;
571 CURLoption key;
572 enum optiontype ot;
573 int i;
574
575 if (parse_curlopt_key(data, &key, &ot)) {
576 ast_log(LOG_ERROR, "Unrecognized option: '%s'\n", data);
577 return -1;
578 }
579
580 if (chan) {
581 /* If we have a channel, we want to read the options set there before
582 falling back to the global settings */
583 ast_channel_lock(chan);
585 ast_channel_unlock(chan);
586
587 if (store) {
588 list[0] = store->data;
590 }
591 }
592
593 for (i = 0; i < 2; i++) {
594 if (!list[i]) {
595 break;
596 }
598 AST_LIST_TRAVERSE(list[i], cur, list) {
599 if (cur->key == key) {
600 if (ot == OT_BOOLEAN || ot == OT_INTEGER) {
601 if (buf) {
602 snprintf(buf, len, "%ld", (long) cur->value);
603 } else {
604 ast_str_set(bufstr, len, "%ld", (long) cur->value);
605 }
606 } else if (ot == OT_INTEGER_MS) {
607 if ((long) cur->value % 1000 == 0) {
608 if (buf) {
609 snprintf(buf, len, "%ld", (long)cur->value / 1000);
610 } else {
611 ast_str_set(bufstr, len, "%ld", (long) cur->value / 1000);
612 }
613 } else {
614 if (buf) {
615 snprintf(buf, len, "%.3f", (double) ((long) cur->value) / 1000.0);
616 } else {
617 ast_str_set(bufstr, len, "%.3f", (double) ((long) cur->value) / 1000.0);
618 }
619 }
620 } else if (ot == OT_STRING) {
621 ast_debug(1, "Found entry %p, with key %d and value %p\n", cur, cur->key, cur->value);
622 if (buf) {
624 } else {
625 ast_str_set(bufstr, 0, "%s", (char *) cur->value);
626 }
627 } else if (key == CURLOPT_PROXYTYPE) {
628 const char *strval = "unknown";
629 if (0) {
630#if CURLVERSION_ATLEAST(7,15,2)
631 } else if ((long)cur->value == CURLPROXY_SOCKS4) {
632 strval = "socks4";
633#endif
634#if CURLVERSION_ATLEAST(7,18,0)
635 } else if ((long)cur->value == CURLPROXY_SOCKS4A) {
636 strval = "socks4a";
637#endif
638 } else if ((long)cur->value == CURLPROXY_SOCKS5) {
639 strval = "socks5";
640#if CURLVERSION_ATLEAST(7,18,0)
641 } else if ((long)cur->value == CURLPROXY_SOCKS5_HOSTNAME) {
642 strval = "socks5hostname";
643#endif
644#if CURLVERSION_ATLEAST(7,10,0)
645 } else if ((long)cur->value == CURLPROXY_HTTP) {
646 strval = "http";
647#endif
648 }
649 if (buf) {
650 ast_copy_string(buf, strval, len);
651 } else {
652 ast_str_set(bufstr, 0, "%s", strval);
653 }
654 } else if (key == CURLOPT_SPECIAL_HASHCOMPAT) {
655 const char *strval = "unknown";
656 if ((long) cur->value == HASHCOMPAT_LEGACY) {
657 strval = "legacy";
658 } else if ((long) cur->value == HASHCOMPAT_YES) {
659 strval = "yes";
660 } else if ((long) cur->value == HASHCOMPAT_NO) {
661 strval = "no";
662 }
663 if (buf) {
664 ast_copy_string(buf, strval, len);
665 } else {
666 ast_str_set(bufstr, 0, "%s", strval);
667 }
668 }
669 break;
670 }
671 }
673 if (cur) {
674 break;
675 }
676 }
677
678 return cur ? 0 : -1;
679}
680
681static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
682{
683 return acf_curlopt_helper(chan, cmd, data, buf, NULL, len);
684}
685
686static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
687{
688 return acf_curlopt_helper(chan, cmd, data, NULL, buf, len);
689}
690
691/*! \brief Callback data passed to \ref WriteMemoryCallback */
693 /*! \brief If a string is being built, the string buffer */
694 struct ast_str *str;
695 /*! \brief The max size of \ref str */
696 ssize_t len;
697 /*! \brief If a file is being retrieved, the file to write to */
698 FILE *out_file;
699};
700
701static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
702{
703 register int realsize = 0;
704 struct curl_write_callback_data *cb_data = data;
705
706 if (cb_data->str) {
707 realsize = size * nmemb;
708 ast_str_append_substr(&cb_data->str, 0, ptr, realsize);
709 } else if (cb_data->out_file) {
710 realsize = fwrite(ptr, size, nmemb, cb_data->out_file);
711 }
712
713 return realsize;
714}
715
716static int curl_instance_init(void *data)
717{
718 CURL **curl = data;
719
720 if (!(*curl = curl_easy_init()))
721 return -1;
722
723 curl_easy_setopt(*curl, CURLOPT_NOSIGNAL, 1);
724 curl_easy_setopt(*curl, CURLOPT_TIMEOUT, 180);
725 curl_easy_setopt(*curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
726 curl_easy_setopt(*curl, CURLOPT_USERAGENT, AST_CURL_USER_AGENT);
727
728 return 0;
729}
730
731static void curl_instance_cleanup(void *data)
732{
733 CURL **curl = data;
734
735 curl_easy_cleanup(*curl);
736
737 ast_free(data);
738}
739
742
743/*!
744 * \brief Check for potential HTTP injection risk.
745 *
746 * CVE-2014-8150 brought up the fact that HTTP proxies are subject to injection
747 * attacks. An HTTP URL sent to a proxy contains a carriage-return linefeed combination,
748 * followed by a complete HTTP request. Proxies will handle this as two separate HTTP
749 * requests rather than as a malformed URL.
750 *
751 * libcURL patched this vulnerability in version 7.40.0, but we have no guarantee that
752 * Asterisk systems will be using an up-to-date cURL library. Therefore, we implement
753 * the same fix as libcURL for determining if a URL is vulnerable to an injection attack.
754 *
755 * \param url The URL to check for vulnerability
756 * \retval 0 The URL is not vulnerable
757 * \retval 1 The URL is vulnerable.
758 */
759static int url_is_vulnerable(const char *url)
760{
761 if (strpbrk(url, "\r\n")) {
762 return 1;
763 }
764
765 return 0;
766}
767
768struct curl_args {
769 const char *url;
770 const char *postdata;
772};
773
774static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
775{
776 struct ast_str *escapebuf = ast_str_thread_get(&thread_escapebuf, 16);
777 int ret = 0;
778 long http_code = 0; /* read curl response */
779 size_t i;
780 struct ast_vector_int hasfailurecode = { NULL };
781 char *failurecodestrings,*found;
782 CURL **curl;
783 struct curl_settings *cur;
784 struct curl_slist *headers = NULL;
785 struct ast_datastore *store = NULL;
786 int hashcompat = 0;
788 char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
789
790 if (!escapebuf) {
791 return -1;
792 }
793
794 if (!(curl = ast_threadstorage_get(&curl_instance, sizeof(*curl)))) {
795 ast_log(LOG_ERROR, "Cannot allocate curl structure\n");
796 return -1;
797 }
798
799 if (url_is_vulnerable(args->url)) {
800 ast_log(LOG_ERROR, "URL '%s' is vulnerable to HTTP injection attacks. Aborting CURL() call.\n", args->url);
801 return -1;
802 }
803
804 if (chan) {
806 }
807
808 AST_VECTOR_INIT(&hasfailurecode, 0); /*Initialize vector*/
811 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
812 hashcompat = (long) cur->value;
813 } else if (cur->key == CURLOPT_HTTPHEADER) {
814 headers = curl_slist_append(headers, (char*) cur->value);
815 } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
816 failurecodestrings = (char*) cur->value;
817 while( (found = strsep(&failurecodestrings, ",")) != NULL) {
818 AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
819 }
820 } else {
821 curl_easy_setopt(*curl, cur->key, cur->value);
822 }
823 }
825
826 if (chan) {
827 ast_channel_lock(chan);
829 ast_channel_unlock(chan);
830 if (store) {
831 list = store->data;
832 AST_LIST_LOCK(list);
833 AST_LIST_TRAVERSE(list, cur, list) {
834 if (cur->key == CURLOPT_SPECIAL_HASHCOMPAT) {
835 hashcompat = (long) cur->value;
836 } else if (cur->key == CURLOPT_HTTPHEADER) {
837 headers = curl_slist_append(headers, (char*) cur->value);
838 } else if (cur->key == CURLOPT_SPECIAL_FAILURE_CODE) {
839 failurecodestrings = (char*) cur->value;
840 while( (found = strsep(&failurecodestrings, ",")) != NULL) {
841 AST_VECTOR_APPEND(&hasfailurecode, atoi(found));
842 }
843 } else {
844 curl_easy_setopt(*curl, cur->key, cur->value);
845 }
846 }
847 }
848 }
849
850 curl_easy_setopt(*curl, CURLOPT_URL, args->url);
851 curl_easy_setopt(*curl, CURLOPT_FILE, (void *) &args->cb_data);
852
853 if (args->postdata) {
854 curl_easy_setopt(*curl, CURLOPT_POST, 1);
855 curl_easy_setopt(*curl, CURLOPT_POSTFIELDS, args->postdata);
856 }
857
858 /* Always assign the headers - even when NULL - in case we had
859 * custom headers the last time we used this shared cURL
860 * instance */
861 curl_easy_setopt(*curl, CURLOPT_HTTPHEADER, headers);
862
863 /* Temporarily assign a buffer for curl to write errors to. */
864 curl_errbuf[0] = curl_errbuf[CURL_ERROR_SIZE] = '\0';
865 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, curl_errbuf);
866
867 if (curl_easy_perform(*curl) != 0) {
868 ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, args->url);
869 }
870
871 /* Reset buffer to NULL so curl doesn't try to write to it when the
872 * buffer is deallocated. Documentation is vague about allowing NULL
873 * here, but the source allows it. See: "typecheck: allow NULL to unset
874 * CURLOPT_ERRORBUFFER" (62bcf005f4678a93158358265ba905bace33b834). */
875 curl_easy_setopt(*curl, CURLOPT_ERRORBUFFER, (char*)NULL);
876 curl_easy_getinfo (*curl, CURLINFO_RESPONSE_CODE, &http_code);
877
878 for (i = 0; i < AST_VECTOR_SIZE(&hasfailurecode); ++i) {
879 if (http_code == AST_VECTOR_GET(&hasfailurecode,i)){
880 ast_log(LOG_NOTICE, "%s%sCURL '%s' returned response code (%ld).\n",
881 chan ? ast_channel_name(chan) : "",
882 chan ? ast_channel_name(chan) : ": ",
883 args->url,
884 http_code);
885 ret=-1;
886 break;
887 }
888 }
889 AST_VECTOR_FREE(&hasfailurecode); /* Release the vector*/
890
891 if (store) {
892 AST_LIST_UNLOCK(list);
893 }
894 curl_slist_free_all(headers);
895
896 if (args->postdata) {
897 curl_easy_setopt(*curl, CURLOPT_POST, 0);
898 }
899
900 if (args->cb_data.str && ast_str_strlen(args->cb_data.str)) {
901 ast_str_trim_blanks(args->cb_data.str);
902
903 ast_debug(3, "CURL returned str='%s'\n", ast_str_buffer(args->cb_data.str));
904 if (hashcompat) {
905 char *remainder = ast_str_buffer(args->cb_data.str);
906 char *piece;
907 struct ast_str *fields = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
908 struct ast_str *values = ast_str_create(ast_str_strlen(args->cb_data.str) / 2);
909 int rowcount = 0;
910 while (fields && values && (piece = strsep(&remainder, "&"))) {
911 char *name = strsep(&piece, "=");
913 if (piece) {
914 ast_uri_decode(piece, mode);
915 }
916 ast_uri_decode(name, mode);
917 ast_str_append(&fields, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, name, INT_MAX));
918 ast_str_append(&values, 0, "%s%s", rowcount ? "," : "", ast_str_set_escapecommas(&escapebuf, 0, S_OR(piece, ""), INT_MAX));
919 rowcount++;
920 }
921 pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", ast_str_buffer(fields));
922 ast_str_set(&args->cb_data.str, 0, "%s", ast_str_buffer(values));
923 ast_free(fields);
925 }
926 }
927
928 if (chan) {
930 }
931
932 return ret;
933}
934
935static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
936{
937 struct curl_args curl_params = { 0, };
938 int res;
939
943 );
944
946
947 if (ast_strlen_zero(info)) {
948 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
949 return -1;
950 }
951
952 curl_params.url = args.url;
953 curl_params.postdata = args.postdata;
954 curl_params.cb_data.str = ast_str_create(16);
955 if (!curl_params.cb_data.str) {
956 return -1;
957 }
958
959 res = acf_curl_helper(chan, &curl_params);
960 ast_str_set(buf, len, "%s", ast_str_buffer(curl_params.cb_data.str));
961 ast_free(curl_params.cb_data.str);
962
963 return res;
964}
965
966static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
967{
968 struct curl_args curl_params = { 0, };
969 int res;
970 char *args_value = ast_strdupa(value);
972 AST_APP_ARG(file_path);
973 );
974
975 AST_STANDARD_APP_ARGS(args, args_value);
976
977 if (ast_strlen_zero(name)) {
978 ast_log(LOG_WARNING, "CURL requires an argument (URL)\n");
979 return -1;
980 }
981
982 if (ast_strlen_zero(args.file_path)) {
983 ast_log(LOG_WARNING, "CURL requires a file to write\n");
984 return -1;
985 }
986
987 curl_params.url = name;
988 curl_params.cb_data.out_file = fopen(args.file_path, "w");
989 if (!curl_params.cb_data.out_file) {
990 ast_log(LOG_WARNING, "Failed to open file %s: %s (%d)\n",
991 args.file_path,
992 strerror(errno),
993 errno);
994 return -1;
995 }
996
997 res = acf_curl_helper(chan, &curl_params);
998
999 fclose(curl_params.cb_data.out_file);
1000
1001 return res;
1002}
1003
1005 .name = "CURL",
1006 .read2 = acf_curl_exec,
1007 .write = acf_curl_write,
1008};
1009
1011 .name = "CURLOPT",
1012 .read = acf_curlopt_read,
1013 .read2 = acf_curlopt_read2,
1014 .write = acf_curlopt_write,
1015};
1016
1017#ifdef TEST_FRAMEWORK
1018AST_TEST_DEFINE(vulnerable_url)
1019{
1020 const char *bad_urls [] = {
1021 "http://example.com\r\nDELETE http://example.com/everything",
1022 "http://example.com\rDELETE http://example.com/everything",
1023 "http://example.com\nDELETE http://example.com/everything",
1024 "\r\nhttp://example.com",
1025 "\rhttp://example.com",
1026 "\nhttp://example.com",
1027 "http://example.com\r\n",
1028 "http://example.com\r",
1029 "http://example.com\n",
1030 };
1031 const char *good_urls [] = {
1032 "http://example.com",
1033 "http://example.com/%5Cr%5Cn",
1034 };
1035 int i;
1037
1038 switch (cmd) {
1039 case TEST_INIT:
1040 info->name = "vulnerable_url";
1041 info->category = "/funcs/func_curl/";
1042 info->summary = "cURL vulnerable URL test";
1043 info->description =
1044 "Ensure that any combination of '\\r' or '\\n' in a URL invalidates the URL";
1045 case TEST_EXECUTE:
1046 break;
1047 }
1048
1049 for (i = 0; i < ARRAY_LEN(bad_urls); ++i) {
1050 if (!url_is_vulnerable(bad_urls[i])) {
1051 ast_test_status_update(test, "String '%s' detected as valid when it should be invalid\n", bad_urls[i]);
1052 res = AST_TEST_FAIL;
1053 }
1054 }
1055
1056 for (i = 0; i < ARRAY_LEN(good_urls); ++i) {
1057 if (url_is_vulnerable(good_urls[i])) {
1058 ast_test_status_update(test, "String '%s' detected as invalid when it should be valid\n", good_urls[i]);
1059 res = AST_TEST_FAIL;
1060 }
1061 }
1062
1063 return res;
1064}
1065#endif
1066
1067static int unload_module(void)
1068{
1069 int res;
1070
1073
1074 AST_TEST_UNREGISTER(vulnerable_url);
1075
1076 return res;
1077}
1078
1079static int load_module(void)
1080{
1081 int res;
1082
1085
1086 AST_TEST_REGISTER(vulnerable_url);
1087
1088 return res;
1089}
1090
1092 .support_level = AST_MODULE_SUPPORT_CORE,
1093 .load = load_module,
1094 .unload = unload_module,
1095 .load_pri = AST_MODPRI_REALTIME_DEPEND2,
1096 .requires = "res_curl",
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
#define AST_CURL_USER_AGENT
Definition: asterisk.h:44
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2355
#define ast_channel_lock(chan)
Definition: channel.h:2972
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
#define ast_channel_unlock(chan)
Definition: channel.h:2973
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2369
Standard Command Line Interface.
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Generic File Format Support. Should be included by clients of the file handling routines....
static const char name[]
Definition: format_mp3.c:68
#define CURLOPT_SPECIAL_HASHCOMPAT
Definition: func_curl.c:265
static int acf_curl_helper(struct ast_channel *chan, struct curl_args *args)
Definition: func_curl.c:774
static int acf_curlopt_read2(struct ast_channel *chan, const char *cmd, char *data, struct ast_str **buf, ssize_t len)
Definition: func_curl.c:686
static void curl_instance_cleanup(void *data)
Definition: func_curl.c:731
static int acf_curl_exec(struct ast_channel *chan, const char *cmd, char *info, struct ast_str **buf, ssize_t len)
Definition: func_curl.c:935
static const struct ast_datastore_info curl_info
Definition: func_curl.c:271
static int acf_curlopt_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
Definition: func_curl.c:417
static int curl_instance_init(void *data)
Definition: func_curl.c:716
static int acf_curlopt_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_curl.c:681
#define CURLOPT_SPECIAL_FAILURE_CODE
Definition: func_curl.c:267
static struct ast_threadstorage thread_escapebuf
Definition: func_curl.c:741
static void curlds_free(void *data)
Definition: func_curl.c:284
static int url_is_vulnerable(const char *url)
Check for potential HTTP injection risk.
Definition: func_curl.c:759
optiontype
Definition: func_curl.c:298
@ OT_BOOLEAN
Definition: func_curl.c:299
@ OT_ENUM
Definition: func_curl.c:303
@ OT_STRING
Definition: func_curl.c:302
@ OT_INTEGER
Definition: func_curl.c:300
@ OT_INTEGER_MS
Definition: func_curl.c:301
static int acf_curlopt_helper(struct ast_channel *chan, const char *cmd, char *data, char *buf, struct ast_str **bufstr, ssize_t len)
Definition: func_curl.c:566
static int acf_curl_write(struct ast_channel *chan, const char *cmd, char *name, const char *value)
Definition: func_curl.c:966
static struct ast_custom_function acf_curlopt
Definition: func_curl.c:1010
static struct ast_threadstorage curl_instance
Definition: func_curl.c:740
struct global_curl_info global_curl_info
static int load_module(void)
Definition: func_curl.c:1079
static int unload_module(void)
Definition: func_curl.c:1067
static struct ast_custom_function acf_curl
Definition: func_curl.c:1004
static int parse_curlopt_key(const char *name, CURLoption *key, enum optiontype *ot)
Definition: func_curl.c:312
hashcompat
Definition: func_curl.c:306
@ HASHCOMPAT_NO
Definition: func_curl.c:307
@ HASHCOMPAT_LEGACY
Definition: func_curl.c:309
@ HASHCOMPAT_YES
Definition: func_curl.c:308
static size_t WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
Definition: func_curl.c:701
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_HEAD_DESTROY(head)
Destroys a list head structure.
Definition: linkedlists.h:653
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
Definition: linkedlists.h:626
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_HEAD(name, type)
Defines a structure to be used to hold a list of specified type.
Definition: linkedlists.h:173
Asterisk locking-related definitions:
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_REALTIME_DEPEND2
Definition: module.h:336
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
def info(msg)
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register_escalating(acf, escalation)
Register a custom function which requires escalated privileges.
Definition: pbx.h:1571
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1562
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
@ AST_CFE_WRITE
Definition: pbx.h:1555
static char url[512]
#define NULL
Definition: resample.c:96
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2235
char * ast_str_set_escapecommas(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Set a dynamic string to a non-NULL terminated substring, with escaping of commas.
Definition: strings.h:1069
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
char * ast_str_append_substr(struct ast_str **buf, ssize_t maxlen, const char *src, size_t maxsrc)
Append a non-NULL terminated substring to the end of a dynamic string.
Definition: strings.h:1062
void ast_str_trim_blanks(struct ast_str *buf)
Trims trailing whitespace characters from an ast_str string.
Definition: strings.h:719
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
Structure used to handle boolean flags.
Definition: utils.h:217
Support for dynamic strings.
Definition: strings.h:623
Integer vector definition.
Definition: vector.h:52
const char * postdata
Definition: func_curl.c:770
struct curl_write_callback_data cb_data
Definition: func_curl.c:771
const char * url
Definition: func_curl.c:769
void * value
Definition: func_curl.c:279
struct curl_settings::@171 list
CURLoption key
Definition: func_curl.c:278
Callback data passed to WriteMemoryCallback.
Definition: func_curl.c:692
struct ast_str * str
If a string is being built, the string buffer.
Definition: func_curl.c:694
ssize_t len
The max size of str.
Definition: func_curl.c:696
FILE * out_file
If a file is being retrieved, the file to write to.
Definition: func_curl.c:698
int value
Definition: syslog.c:37
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
ast_test_result_state
Definition: test.h:193
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
const char * args
Definitions to aid in the use of thread local storage.
#define AST_THREADSTORAGE_CUSTOM(a, b, c)
Define a thread storage variable, with custom initialization and cleanup.
void * ast_threadstorage_get(struct ast_threadstorage *ts, size_t init_size)
Retrieve thread storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Utility functions.
const struct ast_flags ast_uri_http
Definition: utils.c:719
#define ARRAY_LEN(a)
Definition: utils.h:703
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition: utils.c:762
const struct ast_flags ast_uri_http_legacy
Definition: utils.c:720
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:620
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:185
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:124
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:267
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:691