Asterisk - The Open Source Telephony Project GIT-master-2de1a68
res_ari.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2012 - 2013, Digium, Inc.
5 *
6 * David M. Lee, II <dlee@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief HTTP binding for the Stasis API
22 * \author David M. Lee, II <dlee@digium.com>
23 *
24 * The API itself is documented using <a
25 * href="https://developers.helloreverb.com/swagger/">Swagger</a>, a lightweight
26 * mechanism for documenting RESTful API's using JSON. This allows us to use <a
27 * href="https://github.com/wordnik/swagger-ui">swagger-ui</a> to provide
28 * executable documentation for the API, generate client bindings in different
29 * <a href="https://github.com/asterisk/asterisk_rest_libraries">languages</a>,
30 * and generate a lot of the boilerplate code for implementing the RESTful
31 * bindings. The API docs live in the \c rest-api/ directory.
32 *
33 * The RESTful bindings are generated from the Swagger API docs using a set of
34 * <a href="http://mustache.github.io/mustache.5.html">Mustache</a> templates.
35 * The code generator is written in Python, and uses the Python implementation
36 * <a href="https://github.com/defunkt/pystache">pystache</a>. Pystache has no
37 * dependencies, and be installed easily using \c pip. Code generation code
38 * lives in \c rest-api-templates/.
39 *
40 * The generated code reduces a lot of boilerplate when it comes to handling
41 * HTTP requests. It also helps us have greater consistency in the REST API.
42 *
43 * The structure of the generated code is:
44 *
45 * - res/ari/resource_{resource}.h
46 * - For each operation in the resource, a generated argument structure
47 * (holding the parsed arguments from the request) and function
48 * declarations (to implement in res/ari/resource_{resource}.c)
49 * - res_ari_{resource}.c
50 * - A set of \ref stasis_rest_callback functions, which glue the two
51 * together. They parse out path variables and request parameters to
52 * populate a specific \c *_args which is passed to the specific request
53 * handler (in res/ari/resource_{resource}.c)
54 * - A tree of \ref stasis_rest_handlers for routing requests to its
55 * \ref stasis_rest_callback
56 *
57 * The basic flow of an HTTP request is:
58 *
59 * - ast_ari_callback()
60 * 1. Initial request validation
61 * 2. Routes as either a doc request (ast_ari_get_docs) or API
62 * request (ast_ari_invoke)
63 * - ast_ari_invoke()
64 * 1. Further request validation
65 * 2. Routes the request through the tree of generated
66 * \ref stasis_rest_handlers.
67 * 3. Dispatch to the generated callback
68 * - \c ast_ari_*_cb
69 * 1. Populate \c *_args struct with path and get params
70 * 2. Invoke the request handler
71 * 3. Validates and sends response
72 */
73
74/*** MODULEINFO
75 <depend type="module">res_http_websocket</depend>
76 <depend type="module">res_stasis</depend>
77 <support_level>core</support_level>
78 ***/
79
80/*** DOCUMENTATION
81 <configInfo name="res_ari" language="en_US">
82 <synopsis>HTTP binding for the Stasis API</synopsis>
83 <configFile name="ari.conf">
84 <configObject name="general">
85 <synopsis>General configuration settings</synopsis>
86 <configOption name="enabled">
87 <synopsis>Enable/disable the ARI module</synopsis>
88 <description>
89 <para>This option enables or disables the ARI module.</para>
90 <note>
91 <para>ARI uses Asterisk's HTTP server, which must also be enabled in <filename>http.conf</filename>.</para>
92 </note>
93 </description>
94 <see-also>
95 <ref type="filename">http.conf</ref>
96 <ref type="link">https://docs.asterisk.org/Configuration/Core-Configuration/Asterisk-Builtin-mini-HTTP-Server/</ref>
97 </see-also>
98 </configOption>
99 <configOption name="websocket_write_timeout" default="100">
100 <synopsis>The timeout (in milliseconds) to set on WebSocket connections.</synopsis>
101 <description>
102 <para>If a websocket connection accepts input slowly, the timeout
103 for writes to it can be increased to keep it from being disconnected.
104 Value is in milliseconds.</para>
105 </description>
106 </configOption>
107 <configOption name="pretty">
108 <synopsis>Responses from ARI are formatted to be human readable</synopsis>
109 </configOption>
110 <configOption name="auth_realm">
111 <synopsis>Realm to use for authentication. Defaults to Asterisk REST Interface.</synopsis>
112 </configOption>
113 <configOption name="allowed_origins">
114 <synopsis>Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins.</synopsis>
115 </configOption>
116 <configOption name="channelvars">
117 <synopsis>Comma separated list of channel variables to display in channel json.</synopsis>
118 </configOption>
119 </configObject>
120
121 <configObject name="user">
122 <synopsis>Per-user configuration settings</synopsis>
123 <configOption name="type">
124 <synopsis>Define this configuration section as a user.</synopsis>
125 <description>
126 <enumlist>
127 <enum name="user"><para>Configure this section as a <replaceable>user</replaceable></para></enum>
128 </enumlist>
129 </description>
130 </configOption>
131 <configOption name="read_only">
132 <synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
133 </configOption>
134 <configOption name="password">
135 <synopsis>Crypted or plaintext password (see password_format)</synopsis>
136 </configOption>
137 <configOption name="password_format">
138 <synopsis>password_format may be set to plain (the default) or crypt. When set to crypt, crypt(3) is used to validate the password. A crypted password can be generated using mkpasswd -m sha-512. When set to plain, the password is in plaintext</synopsis>
139 </configOption>
140 </configObject>
141 </configFile>
142 </configInfo>
143***/
144
145#include "asterisk.h"
146
147#include "ari/internal.h"
148#include "asterisk/ari.h"
149#include "asterisk/astobj2.h"
150#include "asterisk/module.h"
151#include "asterisk/paths.h"
152#include "asterisk/stasis_app.h"
153
154#include <string.h>
155#include <sys/stat.h>
156#include <unistd.h>
157
158/*! \brief Helper function to check if module is enabled. */
159static int is_enabled(void)
160{
162 return cfg && cfg->general && cfg->general->enabled;
163}
164
165/*! Lock for \ref root_handler */
167
168/*! Handler for root RESTful resource. */
170
171/*! Pre-defined message for allocation failures. */
172static struct ast_json *oom_json;
173
175{
176 return oom_json;
177}
178
180{
181 RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
182 size_t old_size, new_size;
183
185
186 old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
187 new_size = old_size + sizeof(handler);
188
189 new_handler = ao2_alloc(new_size, NULL);
190 if (!new_handler) {
191 return -1;
192 }
193 memcpy(new_handler, root_handler, old_size);
194 new_handler->children[new_handler->num_children++] = handler;
195
197 ao2_ref(new_handler, +1);
198 root_handler = new_handler;
199 return 0;
200}
201
203{
204 struct stasis_rest_handlers *new_handler;
205 size_t size;
206 size_t i;
207 size_t j;
208
210
212 size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
213
214 new_handler = ao2_alloc(size, NULL);
215 if (!new_handler) {
217 return -1;
218 }
219
220 /* Create replacement root_handler less the handler to remove. */
221 memcpy(new_handler, root_handler, sizeof(*new_handler));
222 for (i = 0, j = 0; i < root_handler->num_children; ++i) {
223 if (root_handler->children[i] == handler) {
224 continue;
225 }
226 new_handler->children[j++] = root_handler->children[i];
227 }
228 new_handler->num_children = j;
229
230 /* Replace the old root_handler with the new. */
232 root_handler = new_handler;
233
235 return 0;
236}
237
239{
242 return root_handler;
243}
244
246{
248
249 handler = ao2_alloc(sizeof(*handler), NULL);
250 if (!handler) {
251 return NULL;
252 }
253 handler->path_segment = "ari";
254
255 ao2_ref(handler, +1);
256 return handler;
257}
258
260 int response_code,
261 const char *response_text,
262 const char *message_fmt, ...)
263{
265 va_list ap;
266
267 va_start(ap, message_fmt);
268 message = ast_json_vstringf(message_fmt, ap);
269 va_end(ap);
270 response->message = ast_json_pack("{s: o}",
271 "message", ast_json_ref(message));
272 response->response_code = response_code;
273 response->response_text = response_text;
274}
275
277 struct ast_json *message)
278{
279 response->message = message;
280 response->response_code = 200;
281 response->response_text = "OK";
282}
283
285{
286 response->message = ast_json_null();
287 response->response_code = 204;
288 response->response_text = "No Content";
289}
290
292{
293 response->message = ast_json_null();
294 response->response_code = 202;
295 response->response_text = "Accepted";
296}
297
299{
300 response->message = ast_json_ref(oom_json);
301 response->response_code = 500;
302 response->response_text = "Internal Server Error";
303}
304
306 const char *url, struct ast_json *message)
307{
309 response->message = message;
310 response->response_code = 201;
311 response->response_text = "Created";
312 ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
313}
314
316 struct ast_ari_response *response)
317{
318 enum ast_http_method m;
319 ast_str_append(&response->headers, 0,
320 "Allow: OPTIONS");
321 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
322 if (handler->callbacks[m] != NULL) {
323 ast_str_append(&response->headers, 0,
324 ",%s", ast_get_http_method(m));
325 }
326 }
327 ast_str_append(&response->headers, 0, "\r\n");
328}
329
330static int origin_allowed(const char *origin)
331{
333
334 char *allowed = ast_strdupa(cfg->general->allowed_origins);
335 char *current;
336
337 while ((current = strsep(&allowed, ","))) {
338 if (!strcmp(current, "*")) {
339 return 1;
340 }
341
342 if (!strcmp(current, origin)) {
343 return 1;
344 }
345 }
346
347 return 0;
348}
349
350#define ACR_METHOD "Access-Control-Request-Method"
351#define ACR_HEADERS "Access-Control-Request-Headers"
352#define ACA_METHODS "Access-Control-Allow-Methods"
353#define ACA_HEADERS "Access-Control-Allow-Headers"
354
355/*!
356 * \brief Handle OPTIONS request, mainly for CORS preflight requests.
357 *
358 * Some browsers will send this prior to non-simple methods (i.e. DELETE).
359 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
360 */
362 struct ast_variable *headers,
363 struct ast_ari_response *response)
364{
365 struct ast_variable *header;
366 char const *acr_method = NULL;
367 char const *acr_headers = NULL;
368 char const *origin = NULL;
369
370 RAII_VAR(struct ast_str *, allow, NULL, ast_free);
371 enum ast_http_method m;
372 int allowed = 0;
373
374 /* Regular OPTIONS response */
375 add_allow_header(handler, response);
377
378 /* Parse CORS headers */
379 for (header = headers; header != NULL; header = header->next) {
380 if (strcmp(ACR_METHOD, header->name) == 0) {
381 acr_method = header->value;
382 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
383 acr_headers = header->value;
384 } else if (strcmp("Origin", header->name) == 0) {
385 origin = header->value;
386 }
387 }
388
389 /* CORS 6.2, #1 - "If the Origin header is not present terminate this
390 * set of steps."
391 */
392 if (origin == NULL) {
393 return;
394 }
395
396 /* CORS 6.2, #2 - "If the value of the Origin header is not a
397 * case-sensitive match for any of the values in list of origins do not
398 * set any additional headers and terminate this set of steps.
399 *
400 * Always matching is acceptable since the list of origins can be
401 * unbounded.
402 *
403 * The Origin header can only contain a single origin as the user agent
404 * will not follow redirects."
405 */
406 if (!origin_allowed(origin)) {
407 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
408 return;
409 }
410
411 /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
412 * or if parsing failed, do not set any additional headers and terminate
413 * this set of steps."
414 */
415 if (acr_method == NULL) {
416 return;
417 }
418
419 /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
420 * headers let header field-names be the empty list."
421 */
422 if (acr_headers == NULL) {
423 acr_headers = "";
424 }
425
426 /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
427 * the values in list of methods do not set any additional headers and
428 * terminate this set of steps."
429 */
430 allow = ast_str_create(20);
431
432 if (!allow) {
434 return;
435 }
436
437 /* Go ahead and build the ACA_METHODS header at the same time */
438 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
439 if (handler->callbacks[m] != NULL) {
440 char const *m_str = ast_get_http_method(m);
441 if (strcmp(m_str, acr_method) == 0) {
442 allowed = 1;
443 }
444 ast_str_append(&allow, 0, ",%s", m_str);
445 }
446 }
447
448 if (!allowed) {
449 return;
450 }
451
452 /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
453 * case-insensitive match for any of the values in list of headers do
454 * not set any additional headers and terminate this set of steps.
455 *
456 * Note: Always matching is acceptable since the list of headers can be
457 * unbounded."
458 */
459
460 /* CORS 6.2 #7 - "If the resource supports credentials add a single
461 * Access-Control-Allow-Origin header, with the value of the Origin
462 * header as value, and add a single Access-Control-Allow-Credentials
463 * header with the case-sensitive string "true" as value."
464 *
465 * Added by process_cors_request() earlier in the request.
466 */
467
468 /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
469 * header..."
470 */
471
472 /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
473 * consisting of (a subset of) the list of methods."
474 */
475 ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
477
478
479 /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
480 * consisting of (a subset of) the list of headers.
481 *
482 * Since the list of headers can be unbounded simply returning headers
483 * can be enough."
484 */
485 if (!ast_strlen_zero(acr_headers)) {
486 ast_str_append(&response->headers, 0, "%s: %s\r\n",
487 ACA_HEADERS, acr_headers);
488 }
489}
490
492 const char *uri, enum ast_http_method method,
493 struct ast_variable *get_params, struct ast_variable *headers,
494 struct ast_json *body, struct ast_ari_response *response)
495{
498 struct stasis_rest_handlers *wildcard_handler = NULL;
499 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
500 char *path = ast_strdupa(uri);
501 char *path_segment;
502 stasis_rest_callback callback;
503
504 root = handler = get_root_handler();
505 ast_assert(root != NULL);
506
507 ast_debug(3, "Finding handler for %s\n", path);
508
509 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
510 struct stasis_rest_handlers *found_handler = NULL;
511 int i;
512
514 ast_debug(3, " Finding handler for %s\n", path_segment);
515
516 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
517 struct stasis_rest_handlers *child = handler->children[i];
518
519 if (child->is_wildcard) {
520 /* Record the path variable */
521 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
522 path_var->next = path_vars;
523 path_vars = path_var;
524 wildcard_handler = child;
525 ast_debug(3, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
526
527 } else if (strcmp(child->path_segment, path_segment) == 0) {
528 found_handler = child;
529 ast_debug(3, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
530 } else {
531 ast_debug(3, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
532 }
533 }
534
535 if (!found_handler && wildcard_handler) {
536 ast_debug(3, " No explicit handler found for %s. Using wildcard %s.\n",
537 path_segment, wildcard_handler->path_segment);
538 found_handler = wildcard_handler;
539 wildcard_handler = NULL;
540 }
541
542 if (found_handler == NULL) {
543 /* resource not found */
544 ast_debug(3, " Handler not found for %s\n", path_segment);
546 response, 404, "Not Found",
547 "Resource not found");
548 return;
549 } else {
550 handler = found_handler;
551 }
552 }
553
555 if (method == AST_HTTP_OPTIONS) {
556 handle_options(handler, headers, response);
557 return;
558 }
559
560 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
561 add_allow_header(handler, response);
563 response, 405, "Method Not Allowed",
564 "Invalid method");
565 return;
566 }
567
568 if (handler->ws_server && method == AST_HTTP_GET) {
569 /* WebSocket! */
570 ari_handle_websocket(handler->ws_server, ser, uri, method,
571 get_params, headers);
572 /* Since the WebSocket code handles the connection, we shouldn't
573 * do anything else; setting no_response */
574 response->no_response = 1;
575 return;
576 }
577
578 callback = handler->callbacks[method];
579 if (callback == NULL) {
580 add_allow_header(handler, response);
582 response, 405, "Method Not Allowed",
583 "Invalid method");
584 return;
585 }
586
587 callback(ser, get_params, path_vars, headers, body, response);
588 if (response->message == NULL && response->response_code == 0) {
589 /* Really should not happen */
590 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
593 response, 501, "Not Implemented",
594 "Method not implemented");
595 }
596}
597
598void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
599 struct ast_ari_response *response)
600{
601 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
602 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
603 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
604 struct ast_json *obj = NULL;
605 struct ast_variable *host = NULL;
606 struct ast_json_error error = {};
607 struct stat file_stat;
608
609 ast_debug(3, "%s(%s)\n", __func__, uri);
610
611 absolute_path_builder = ast_str_create(80);
612 if (absolute_path_builder == NULL) {
614 return;
615 }
616
617 /* absolute path to the rest-api directory */
618 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
619 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
620 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
621 if (absolute_api_dirname == NULL) {
622 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
624 response, 500, "Internal Server Error",
625 "Cannot find rest-api directory");
626 return;
627 }
628
629 /* absolute path to the requested file */
630 ast_str_append(&absolute_path_builder, 0, "%s", uri);
631 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
632 if (absolute_filename == NULL) {
633 switch (errno) {
634 case ENAMETOOLONG:
635 case ENOENT:
636 case ENOTDIR:
638 response, 404, "Not Found",
639 "Resource not found");
640 break;
641 case EACCES:
643 response, 403, "Forbidden",
644 "Permission denied");
645 break;
646 default:
648 "Error determining real path for uri '%s': %s\n",
649 uri, strerror(errno));
651 response, 500, "Internal Server Error",
652 "Cannot find file");
653 break;
654 }
655 return;
656 }
657
658 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
659 /* HACKERZ! */
661 "Invalid attempt to access '%s' (not in %s)\n",
662 absolute_filename, absolute_api_dirname);
664 response, 404, "Not Found",
665 "Resource not found");
666 return;
667 }
668
669 if (stat(absolute_filename, &file_stat) == 0) {
670 if (!(file_stat.st_mode & S_IFREG)) {
671 /* Not a file */
673 response, 403, "Forbidden",
674 "Invalid access");
675 return;
676 }
677 } else {
678 /* Does not exist */
680 response, 404, "Not Found",
681 "Resource not found");
682 return;
683 }
684
685 /* Load resource object from file */
686 obj = ast_json_load_new_file(absolute_filename, &error);
687 if (obj == NULL) {
688 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
689 error.source, error.line, error.column, error.text);
691 response, 500, "Internal Server Error",
692 "Yikes! Cannot parse resource");
693 return;
694 }
695
696 /* Update the basePath properly */
697 if (ast_json_object_get(obj, "basePath") != NULL) {
698 for (host = headers; host; host = host->next) {
699 if (strcasecmp(host->name, "Host") == 0) {
700 break;
701 }
702 }
703 if (host != NULL) {
704 if (prefix != NULL && strlen(prefix) > 0) {
706 obj, "basePath",
707 ast_json_stringf("http://%s%s/ari", host->value,prefix));
708 } else {
710 obj, "basePath",
711 ast_json_stringf("http://%s/ari", host->value));
712 }
713 } else {
714 /* Without the host, we don't have the basePath */
715 ast_json_object_del(obj, "basePath");
716 }
717 }
718
719 ast_ari_response_ok(response, obj);
720}
721
722static void remove_trailing_slash(const char *uri,
723 struct ast_ari_response *response)
724{
725 char *slashless = ast_strdupa(uri);
726 slashless[strlen(slashless) - 1] = '\0';
727
728 /* While it's tempting to redirect the client to the slashless URL,
729 * that is problematic. A 302 Found is the most appropriate response,
730 * but most clients issue a GET on the location you give them,
731 * regardless of the method of the original request.
732 *
733 * While there are some ways around this, it gets into a lot of client
734 * specific behavior and corner cases in the HTTP standard. There's also
735 * very little practical benefit of redirecting; only GET and HEAD can
736 * be redirected automagically; all other requests "MUST NOT
737 * automatically redirect the request unless it can be confirmed by the
738 * user, since this might change the conditions under which the request
739 * was issued."
740 *
741 * Given all of that, a 404 with a nice message telling them what to do
742 * is probably our best bet.
743 */
744 ast_ari_response_error(response, 404, "Not Found",
745 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
746}
747
748/*!
749 * \brief Handle CORS headers for simple requests.
750 *
751 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
752 */
753static void process_cors_request(struct ast_variable *headers,
754 struct ast_ari_response *response)
755{
756 char const *origin = NULL;
757 struct ast_variable *header;
758
759 /* Parse CORS headers */
760 for (header = headers; header != NULL; header = header->next) {
761 if (strcmp("Origin", header->name) == 0) {
762 origin = header->value;
763 }
764 }
765
766 /* CORS 6.1, #1 - "If the Origin header is not present terminate this
767 * set of steps."
768 */
769 if (origin == NULL) {
770 return;
771 }
772
773 /* CORS 6.1, #2 - "If the value of the Origin header is not a
774 * case-sensitive match for any of the values in list of origins, do not
775 * set any additional headers and terminate this set of steps.
776 *
777 * Note: Always matching is acceptable since the list of origins can be
778 * unbounded."
779 */
780 if (!origin_allowed(origin)) {
781 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
782 return;
783 }
784
785 /* CORS 6.1, #3 - "If the resource supports credentials add a single
786 * Access-Control-Allow-Origin header, with the value of the Origin
787 * header as value, and add a single Access-Control-Allow-Credentials
788 * header with the case-sensitive string "true" as value.
789 *
790 * Otherwise, add a single Access-Control-Allow-Origin header, with
791 * either the value of the Origin header or the string "*" as value."
792 */
793 ast_str_append(&response->headers, 0,
794 "Access-Control-Allow-Origin: %s\r\n", origin);
795 ast_str_append(&response->headers, 0,
796 "Access-Control-Allow-Credentials: true\r\n");
797
798 /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
799 * or more Access-Control-Expose-Headers headers, with as values the
800 * header field names given in the list of exposed headers."
801 *
802 * No exposed headers; skipping
803 */
804}
805
807{
808 RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
809 cfg = ast_ari_config_get();
810 return cfg->general->format;
811}
812
813/*!
814 * \brief Authenticate a <code>?api_key=userid:password</code>
815 *
816 * \param api_key API key query parameter
817 * \return User object for the authenticated user.
818 * \retval NULL if authentication failed.
819 */
820static struct ast_ari_conf_user *authenticate_api_key(const char *api_key)
821{
822 RAII_VAR(char *, copy, NULL, ast_free);
823 char *username;
824 char *password;
825
826 password = copy = ast_strdup(api_key);
827 if (!copy) {
828 return NULL;
829 }
830
831 username = strsep(&password, ":");
832 if (!password) {
833 ast_log(LOG_WARNING, "Invalid api_key\n");
834 return NULL;
835 }
836
838}
839
840/*!
841 * \brief Authenticate an HTTP request.
842 *
843 * \param get_params GET parameters of the request.
844 * \param headers HTTP headers.
845 * \return User object for the authenticated user.
846 * \retval NULL if authentication failed.
847 */
848static struct ast_ari_conf_user *authenticate_user(struct ast_variable *get_params,
849 struct ast_variable *headers)
850{
851 RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
852 struct ast_variable *v;
853
854 /* HTTP Basic authentication */
855 http_auth = ast_http_get_auth(headers);
856 if (http_auth) {
857 return ast_ari_config_validate_user(http_auth->userid,
858 http_auth->password);
859 }
860
861 /* ?api_key authentication */
862 for (v = get_params; v; v = v->next) {
863 if (strcasecmp("api_key", v->name) == 0) {
864 return authenticate_api_key(v->value);
865 }
866 }
867
868 return NULL;
869}
870
871/*!
872 * \internal
873 * \brief ARI HTTP handler.
874 *
875 * This handler takes the HTTP request and turns it into the appropriate
876 * RESTful request (conversion to JSON, routing, etc.)
877 *
878 * \param ser TCP session.
879 * \param urih URI handler.
880 * \param uri URI requested.
881 * \param method HTTP method.
882 * \param get_params HTTP \c GET params.
883 * \param headers HTTP headers.
884 */
886 const struct ast_http_uri *urih,
887 const char *uri,
889 struct ast_variable *get_params,
890 struct ast_variable *headers)
891{
893 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
895 struct ast_ari_response response = { .fd = -1, 0 };
896 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
897 struct ast_variable *var;
898 const char *app_name = NULL;
899 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
900 int debug_app = 0;
901
902 if (!response_body) {
904 ast_http_error(ser, 500, "Server Error", "Out of memory");
905 return 0;
906 }
907
908 response.headers = ast_str_create(40);
909 if (!response.headers) {
911 ast_http_error(ser, 500, "Server Error", "Out of memory");
912 return 0;
913 }
914
916 if (!conf || !conf->general) {
917 ast_free(response.headers);
919 ast_http_error(ser, 500, "Server Error", "URI handler config missing");
920 return 0;
921 }
922
923 process_cors_request(headers, &response);
924
925 /* Process form data from a POST. It could be mixed with query
926 * parameters, which seems a bit odd. But it's allowed, so that's okay
927 * with us.
928 */
929 post_vars = ast_http_get_post_vars(ser, headers);
930 if (!post_vars) {
931 switch (errno) {
932 case EFBIG:
933 ast_ari_response_error(&response, 413,
934 "Request Entity Too Large",
935 "Request body too large");
936 goto request_failed;
937 case ENOMEM:
939 ast_ari_response_error(&response, 500,
940 "Internal Server Error",
941 "Out of memory");
942 goto request_failed;
943 case EIO:
944 ast_ari_response_error(&response, 400,
945 "Bad Request", "Error parsing request body");
946 goto request_failed;
947 }
948
949 /* Look for a JSON request entity only if there were no post_vars.
950 * If there were post_vars, then the request body would already have
951 * been consumed and can not be read again.
952 */
953 body = ast_http_get_json(ser, headers);
954 if (!body) {
955 switch (errno) {
956 case EFBIG:
957 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
958 goto request_failed;
959 case ENOMEM:
960 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
961 goto request_failed;
962 case EIO:
963 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
964 goto request_failed;
965 }
966 }
967 }
968 if (get_params == NULL) {
969 get_params = post_vars;
970 } else if (get_params && post_vars) {
971 /* Has both post_vars and get_params */
972 struct ast_variable *last_var = post_vars;
973 while (last_var->next) {
974 last_var = last_var->next;
975 }
976 /* The duped get_params will get freed when post_vars gets
977 * ast_variables_destroyed.
978 */
979 last_var->next = ast_variables_dup(get_params);
980 get_params = post_vars;
981 }
982
983 /* At this point, get_params will contain post_vars (if any) */
984 app_name = ast_variable_find_in_list(get_params, "app");
985 if (!app_name) {
986 struct ast_json *app = ast_json_object_get(body, "app");
987
989 }
990
991 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
992 * and the global debug flag.
993 */
995 if (debug_app) {
996 struct ast_str *buf = ast_str_create(512);
998
999 if (!buf || (body && !str)) {
1001 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1003 ast_free(buf);
1004 goto request_failed;
1005 }
1006
1007 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1009 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1010 for (var = headers; var; var = var->next) {
1011 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1012 }
1013 for (var = get_params; var; var = var->next) {
1014 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1015 }
1016 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1018 ast_free(buf);
1019 }
1020
1021 user = authenticate_user(get_params, headers);
1022 if (response.response_code > 0) {
1023 /* POST parameter processing error. Do nothing. */
1024 } else if (!user) {
1025 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
1026 * message is used by an origin server to challenge the
1027 * authorization of a user agent. This response MUST include a
1028 * WWW-Authenticate header field containing at least one
1029 * challenge applicable to the requested resource.
1030 */
1031 ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required");
1032
1033 /* Section 1.2:
1034 * realm = "realm" "=" realm-value
1035 * realm-value = quoted-string
1036 * Section 2:
1037 * challenge = "Basic" realm
1038 */
1039 ast_str_append(&response.headers, 0,
1040 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
1041 conf->general->auth_realm);
1042 } else if (!ast_fully_booted) {
1044 ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted");
1045 } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
1046 ast_ari_response_error(&response, 403, "Forbidden", "Write access denied");
1047 } else if (ast_ends_with(uri, "/")) {
1048 remove_trailing_slash(uri, &response);
1049 } else if (ast_begins_with(uri, "api-docs/")) {
1050 /* Serving up API docs */
1051 if (method != AST_HTTP_GET) {
1052 ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method");
1053 } else {
1054 /* Skip the api-docs prefix */
1055 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, &response);
1056 }
1057 } else {
1058 /* Other RESTful resources */
1059 ast_ari_invoke(ser, uri, method, get_params, headers, body,
1060 &response);
1061 }
1062
1063 if (response.no_response) {
1064 /* The handler indicates no further response is necessary.
1065 * Probably because it already handled it */
1066 ast_free(response.headers);
1067 return 0;
1068 }
1069
1070request_failed:
1071
1072 /* If you explicitly want to have no content, set message to
1073 * ast_json_null().
1074 */
1075 ast_assert(response.message != NULL);
1076 ast_assert(response.response_code > 0);
1077
1078 /* response.message could be NULL, in which case the empty response_body
1079 * is correct
1080 */
1081 if (response.message && !ast_json_is_null(response.message)) {
1082 ast_str_append(&response.headers, 0,
1083 "Content-type: application/json\r\n");
1084 if (ast_json_dump_str_format(response.message, &response_body,
1085 conf->general->format) != 0) {
1086 /* Error encoding response */
1087 response.response_code = 500;
1088 response.response_text = "Internal Server Error";
1089 ast_str_set(&response_body, 0, "%s", "");
1090 ast_str_set(&response.headers, 0, "%s", "");
1091 }
1092 }
1093
1094 if (debug_app) {
1095 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1097 response.response_text, ast_str_buffer(response.headers),
1098 ast_str_buffer(response_body));
1099 }
1100
1101 ast_http_send(ser, method, response.response_code,
1102 response.response_text, response.headers, response_body,
1103 response.fd != -1 ? response.fd : 0, 0);
1104 /* ast_http_send takes ownership, so we don't have to free them */
1105 response_body = NULL;
1106
1107 ast_json_unref(response.message);
1108 if (response.fd >= 0) {
1109 close(response.fd);
1110 }
1111 return 0;
1112}
1113
1114static struct ast_http_uri http_uri = {
1116 .description = "Asterisk RESTful API",
1117 .uri = "ari",
1118 .has_subtree = 1,
1119 .data = NULL,
1120 .key = __FILE__,
1121 .no_decode_uri = 1,
1122};
1123
1124static int unload_module(void)
1125{
1127
1128 if (is_enabled()) {
1129 ast_debug(3, "Disabling ARI\n");
1131 }
1132
1134
1138
1140 oom_json = NULL;
1141
1142 return 0;
1143}
1144
1145static int load_module(void)
1146{
1148
1149 /* root_handler may have been built during a declined load */
1150 if (!root_handler) {
1152 }
1153 if (!root_handler) {
1155 }
1156
1157 /* oom_json may have been built during a declined load */
1158 if (!oom_json) {
1160 "{s: s}", "error", "Allocation failed");
1161 }
1162 if (!oom_json) {
1163 /* Ironic */
1164 unload_module();
1166 }
1167
1168 if (ast_ari_config_init() != 0) {
1169 unload_module();
1171 }
1172
1173 if (is_enabled()) {
1174 ast_debug(3, "ARI enabled\n");
1176 } else {
1177 ast_debug(3, "ARI disabled\n");
1178 }
1179
1180 if (ast_ari_cli_register() != 0) {
1181 unload_module();
1183 }
1184
1186}
1187
1188static int reload_module(void)
1189{
1190 char was_enabled = is_enabled();
1191
1192 if (ast_ari_config_reload() != 0) {
1194 }
1195
1196 if (was_enabled && !is_enabled()) {
1197 ast_debug(3, "Disabling ARI\n");
1199 } else if (!was_enabled && is_enabled()) {
1200 ast_debug(3, "Enabling ARI\n");
1202 }
1203
1205}
1206
1208 .support_level = AST_MODULE_SUPPORT_CORE,
1209 .load = load_module,
1210 .unload = unload_module,
1212 .optional_modules = "res_http_websocket",
1213 .requires = "http,res_stasis",
1214 .load_pri = AST_MODPRI_APP_DEPEND,
static const char app[]
Definition: app_adsiprog.c:56
const char * str
Definition: app_jack.c:147
ast_mutex_t lock
Definition: app_sla.c:331
static int copy(char *infile, char *outfile)
Utility function to copy a file.
Asterisk RESTful API hooks.
void(* stasis_rest_callback)(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Callback type for RESTful method handlers.
Definition: ari.h:60
void ari_handle_websocket(struct ast_websocket_server *ws_server, struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Wrapper for invoking the websocket code for an incoming connection.
#define var
Definition: ast_expr2f.c:605
Asterisk main include file. File version handling, generic pbx functions.
void ast_std_free(void *ptr)
Definition: astmm.c:1734
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2206
static char prefix[MAX_PREFIX]
Definition: http.c:144
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition: http.c:459
struct ast_variable * ast_http_get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlenco...
Definition: http.c:1356
struct ast_json * ast_http_get_json(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get JSON from client Request Entity-Body, if content type is application/json.
Definition: http.c:1317
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:58
@ AST_HTTP_GET
Definition: http.h:60
@ AST_HTTP_MAX_METHOD
Definition: http.h:66
@ AST_HTTP_OPTIONS
Definition: http.h:65
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:708
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1582
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition: http.c:193
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition: http.c:840
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:651
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:676
char * strsep(char **str, const char *delims)
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:919
#define ast_variable_new(name, value, filename)
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:543
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
Internal API's for res_ari.
struct ast_json * ast_json_null(void)
Get the JSON null value.
Definition: json.c:248
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
struct ast_json * ast_json_vstringf(const char *format, va_list args)
Create a JSON string, vprintf style.
Definition: json.c:303
struct ast_json * ast_json_stringf(const char *format,...)
Create a JSON string, printf style.
Definition: json.c:293
int ast_json_object_del(struct ast_json *object, const char *key)
Delete a field from a JSON object.
Definition: json.c:418
ast_json_encoding_format
Encoding format type.
Definition: json.h:791
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
int ast_json_dump_str_format(struct ast_json *root, struct ast_str **dst, enum ast_json_encoding_format format)
Encode a JSON value to an ast_str.
Definition: json.c:520
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
Definition: json.c:414
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
char * ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format)
Encode a JSON value to a string.
Definition: json.c:484
struct ast_json * ast_json_load_new_file(const char *path, struct ast_json_error *error)
Parse file at path into JSON object or array.
Definition: json.c:604
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
Definition: json.c:273
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:589
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
size_t current
Definition: main/cli.c:113
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:317
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:316
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODPRI_APP_DEPEND
Definition: module.h:328
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_fully_booted
Definition: options.h:117
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
const char * app_name(struct ast_app *app)
Definition: pbx_app.c:463
int ast_ari_cli_register(void)
Register CLI commands for ARI.
Definition: res/ari/cli.c:431
void ast_ari_cli_unregister(void)
Unregister CLI commands for ARI.
Definition: res/ari/cli.c:435
struct ast_ari_conf * ast_ari_config_get(void)
Get the current ARI configuration.
int ast_ari_config_reload(void)
Reload the ARI configuration.
int ast_ari_config_init(void)
Initialize the ARI configuration.
struct ast_ari_conf_user * ast_ari_config_validate_user(const char *username, const char *password)
Validated a user's credentials.
void ast_ari_config_destroy(void)
Destroy the ARI configuration.
static void remove_trailing_slash(const char *uri, struct ast_ari_response *response)
Definition: res_ari.c:722
static int ast_ari_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Definition: res_ari.c:885
static struct stasis_rest_handlers * root_handler_create(void)
Definition: res_ari.c:245
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition: res_ari.c:806
static struct stasis_rest_handlers * root_handler
Definition: res_ari.c:169
static ast_mutex_t root_handler_lock
Definition: res_ari.c:166
static int is_enabled(void)
Helper function to check if module is enabled.
Definition: res_ari.c:159
void ast_ari_response_created(struct ast_ari_response *response, const char *url, struct ast_json *message)
Fill in a Created (201) ast_ari_response.
Definition: res_ari.c:305
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:202
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition: res_ari.c:315
static struct ast_ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
Definition: res_ari.c:820
static struct ast_http_uri http_uri
Definition: res_ari.c:1114
static int reload_module(void)
Definition: res_ari.c:1188
#define ACA_HEADERS
Definition: res_ari.c:353
void ast_ari_response_error(struct ast_ari_response *response, int response_code, const char *response_text, const char *message_fmt,...)
Fill in an error ast_ari_response.
Definition: res_ari.c:259
void ast_ari_invoke(struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: res_ari.c:491
void ast_ari_response_ok(struct ast_ari_response *response, struct ast_json *message)
Fill in an OK (200) ast_ari_response.
Definition: res_ari.c:276
static int origin_allowed(const char *origin)
Definition: res_ari.c:330
static void handle_options(struct stasis_rest_handlers *handler, struct ast_variable *headers, struct ast_ari_response *response)
Handle OPTIONS request, mainly for CORS preflight requests.
Definition: res_ari.c:361
#define ACA_METHODS
Definition: res_ari.c:352
void ast_ari_response_accepted(struct ast_ari_response *response)
Fill in a Accepted (202) ast_ari_response.
Definition: res_ari.c:291
struct ast_json * ast_ari_oom_json(void)
The stock message to return when out of memory.
Definition: res_ari.c:174
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition: res_ari.c:753
void ast_ari_response_alloc_failed(struct ast_ari_response *response)
Fill in response with a 500 message for allocation failures.
Definition: res_ari.c:298
#define ACR_HEADERS
Definition: res_ari.c:351
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition: res_ari.c:284
#define ACR_METHOD
Definition: res_ari.c:350
static int load_module(void)
Definition: res_ari.c:1145
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition: res_ari.c:598
static struct ast_json * oom_json
Definition: res_ari.c:172
static int unload_module(void)
Definition: res_ari.c:1124
static struct ast_ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers)
Authenticate an HTTP request.
Definition: res_ari.c:848
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:179
static struct stasis_rest_handlers * get_root_handler(void)
Definition: res_ari.c:238
static char url[512]
static int reload(void)
const char * method
Definition: res_pjsip.c:1279
#define NULL
Definition: resample.c:96
Stasis Application API. See Stasis Application API for detailed documentation.
int stasis_app_get_debug_by_name(const char *app_name)
Get debug status of an application.
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
static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
Checks whether a string ends with another.
Definition: strings.h:116
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#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
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
Per-user configuration options.
Definition: internal.h:96
char password[ARI_PASSWORD_LEN]
Definition: internal.h:100
char * username
Definition: internal.h:98
All configuration options for ARI.
Definition: internal.h:54
struct ast_str * headers
Definition: ari.h:96
struct ast_json * message
Definition: ari.h:94
int response_code
Definition: ari.h:99
const char * response_text
Definition: ari.h:103
unsigned int no_response
Definition: ari.h:105
HTTP authentication information.
Definition: http.h:125
Definition of a URI handler.
Definition: http.h:102
ast_http_callback callback
Definition: http.h:107
const char * prefix
Definition: http.h:106
JSON parsing error information.
Definition: json.h:887
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition: lock.h:135
Support for dynamic strings.
Definition: strings.h:623
describes a server instance
Definition: tcptls.h:150
struct ast_sockaddr remote_address
Definition: tcptls.h:152
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
All configuration options for http media cache.
struct conf_general_options * general
const ast_string_field value
const ast_string_field name
struct header * next
Handler for a single RESTful path segment.
Definition: ari.h:69
const char * path_segment
Definition: ari.h:71
struct stasis_rest_handlers * children[]
Definition: ari.h:86
size_t num_children
Definition: ari.h:84
structure to hold users read from users.conf
static void handler(const char *name, int response_code, struct ast_variable *get_params, struct ast_variable *path_vars, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: test_ari.c:59
int error(const char *format,...)
Definition: utils/frame.c:999
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
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