Asterisk - The Open Source Telephony Project GIT-master-754dea3
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
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 <since>
86 <version>12.0.0</version>
87 </since>
88 <synopsis>General configuration settings</synopsis>
89 <configOption name="enabled">
90 <since>
91 <version>12.0.0</version>
92 </since>
93 <synopsis>Enable/disable the ARI module</synopsis>
94 <description>
95 <para>This option enables or disables the ARI module.</para>
96 <note>
97 <para>ARI uses Asterisk's HTTP server, which must also be enabled in <filename>http.conf</filename>.</para>
98 </note>
99 </description>
100 <see-also>
101 <ref type="filename">http.conf</ref>
102 <ref type="link">https://docs.asterisk.org/Configuration/Core-Configuration/Asterisk-Builtin-mini-HTTP-Server/</ref>
103 </see-also>
104 </configOption>
105 <configOption name="websocket_write_timeout" default="100">
106 <since>
107 <version>11.11.0</version>
108 <version>12.4.0</version>
109 </since>
110 <synopsis>The timeout (in milliseconds) to set on WebSocket connections.</synopsis>
111 <description>
112 <para>If a websocket connection accepts input slowly, the timeout
113 for writes to it can be increased to keep it from being disconnected.
114 Value is in milliseconds.</para>
115 </description>
116 </configOption>
117 <configOption name="pretty">
118 <since>
119 <version>12.0.0</version>
120 </since>
121 <synopsis>Responses from ARI are formatted to be human readable</synopsis>
122 </configOption>
123 <configOption name="auth_realm">
124 <since>
125 <version>12.0.0</version>
126 </since>
127 <synopsis>Realm to use for authentication. Defaults to Asterisk REST Interface.</synopsis>
128 </configOption>
129 <configOption name="allowed_origins">
130 <since>
131 <version>12.0.0</version>
132 </since>
133 <synopsis>Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins.</synopsis>
134 </configOption>
135 <configOption name="channelvars">
136 <since>
137 <version>14.2.0</version>
138 </since>
139 <synopsis>Comma separated list of channel variables to display in channel json.</synopsis>
140 </configOption>
141 </configObject>
142
143 <configObject name="user">
144 <since>
145 <version>12.0.0</version>
146 </since>
147 <synopsis>Per-user configuration settings</synopsis>
148 <configOption name="type">
149 <since>
150 <version>13.30.0</version>
151 <version>16.7.0</version>
152 <version>17.1.0</version>
153 </since>
154 <synopsis>Define this configuration section as a user.</synopsis>
155 <description>
156 <enumlist>
157 <enum name="user"><para>Configure this section as a <replaceable>user</replaceable></para></enum>
158 </enumlist>
159 </description>
160 </configOption>
161 <configOption name="read_only">
162 <since>
163 <version>13.30.0</version>
164 <version>16.7.0</version>
165 <version>17.1.0</version>
166 </since>
167 <synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
168 </configOption>
169 <configOption name="password">
170 <since>
171 <version>13.30.0</version>
172 <version>16.7.0</version>
173 <version>17.1.0</version>
174 </since>
175 <synopsis>Crypted or plaintext password (see password_format)</synopsis>
176 </configOption>
177 <configOption name="password_format">
178 <since>
179 <version>12.0.0</version>
180 </since>
181 <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>
182 </configOption>
183 </configObject>
184 </configFile>
185 </configInfo>
186***/
187
188#include "asterisk.h"
189
190#include "ari/internal.h"
191#include "ari/ari_websockets.h"
192#include "asterisk/ari.h"
193#include "asterisk/astobj2.h"
194#include "asterisk/module.h"
195#include "asterisk/paths.h"
196#include "asterisk/stasis_app.h"
197
198#include <string.h>
199#include <sys/stat.h>
200#include <unistd.h>
201
202/*! \brief Helper function to check if module is enabled. */
203static int is_enabled(void)
204{
206 return cfg && cfg->general && cfg->general->enabled;
207}
208
209/*! Lock for \ref root_handler */
211
212/*! Handler for root RESTful resource. */
214
215/*! Pre-defined message for allocation failures. */
216static struct ast_json *oom_json;
217
218/*! \brief Callback for the root URI. */
219static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
220 const struct ast_http_uri *urih, const char *uri,
221 enum ast_http_method method, struct ast_variable *get_params,
222 struct ast_variable *headers);
223
224static struct ast_http_uri http_uri = {
226 .description = "Asterisk RESTful API",
227 .uri = "ari",
228 .has_subtree = 1,
229 .data = NULL,
230 .key = __FILE__,
231 .no_decode_uri = 1,
232};
233
235{
236 return oom_json;
237}
238
240{
241 RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
242 size_t old_size, new_size;
243
245
246 old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
247 new_size = old_size + sizeof(handler);
248
249 new_handler = ao2_alloc(new_size, NULL);
250 if (!new_handler) {
251 return -1;
252 }
253 memcpy(new_handler, root_handler, old_size);
254 new_handler->children[new_handler->num_children++] = handler;
255
257 ao2_ref(new_handler, +1);
258 root_handler = new_handler;
259 return 0;
260}
261
263{
264 struct stasis_rest_handlers *new_handler;
265 size_t size;
266 size_t i;
267 size_t j;
268
270
272 size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
273
274 new_handler = ao2_alloc(size, NULL);
275 if (!new_handler) {
277 return -1;
278 }
279
280 /* Create replacement root_handler less the handler to remove. */
281 memcpy(new_handler, root_handler, sizeof(*new_handler));
282 for (i = 0, j = 0; i < root_handler->num_children; ++i) {
283 if (root_handler->children[i] == handler) {
284 continue;
285 }
286 new_handler->children[j++] = root_handler->children[i];
287 }
288 new_handler->num_children = j;
289
290 /* Replace the old root_handler with the new. */
292 root_handler = new_handler;
293
295 return 0;
296}
297
299{
302 return root_handler;
303}
304
306{
308
309 handler = ao2_alloc(sizeof(*handler), NULL);
310 if (!handler) {
311 return NULL;
312 }
313 handler->path_segment = "ari";
314
315 ao2_ref(handler, +1);
316 return handler;
317}
318
320 int response_code,
321 const char *response_text,
322 const char *message_fmt, ...)
323{
325 va_list ap;
326
327 va_start(ap, message_fmt);
328 message = ast_json_vstringf(message_fmt, ap);
329 va_end(ap);
330 response->message = ast_json_pack("{s: o}",
331 "message", ast_json_ref(message));
332 response->response_code = response_code;
333 response->response_text = response_text;
334}
335
337 struct ast_json *message)
338{
339 response->message = message;
340 response->response_code = 200;
341 response->response_text = "OK";
342}
343
345{
346 response->message = ast_json_null();
347 response->response_code = 204;
348 response->response_text = "No Content";
349}
350
352{
353 response->message = ast_json_null();
354 response->response_code = 202;
355 response->response_text = "Accepted";
356}
357
359{
360 response->message = ast_json_ref(oom_json);
361 response->response_code = 500;
362 response->response_text = "Internal Server Error";
363}
364
366 const char *url, struct ast_json *message)
367{
369 response->message = message;
370 response->response_code = 201;
371 response->response_text = "Created";
372 ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
373}
374
376 struct ast_ari_response *response)
377{
378 enum ast_http_method m;
379 ast_str_append(&response->headers, 0,
380 "Allow: OPTIONS");
381 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
382 if (handler->callbacks[m] != NULL) {
383 ast_str_append(&response->headers, 0,
384 ",%s", ast_get_http_method(m));
385 }
386 }
387 ast_str_append(&response->headers, 0, "\r\n");
388}
389
390static int origin_allowed(const char *origin)
391{
393
394 char *allowed = ast_strdupa(cfg->general->allowed_origins);
395 char *current;
396
397 while ((current = strsep(&allowed, ","))) {
398 if (!strcmp(current, "*")) {
399 return 1;
400 }
401
402 if (!strcmp(current, origin)) {
403 return 1;
404 }
405 }
406
407 return 0;
408}
409
410#define ACR_METHOD "Access-Control-Request-Method"
411#define ACR_HEADERS "Access-Control-Request-Headers"
412#define ACA_METHODS "Access-Control-Allow-Methods"
413#define ACA_HEADERS "Access-Control-Allow-Headers"
414
415/*!
416 * \brief Handle OPTIONS request, mainly for CORS preflight requests.
417 *
418 * Some browsers will send this prior to non-simple methods (i.e. DELETE).
419 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
420 */
422 struct ast_variable *headers,
423 struct ast_ari_response *response)
424{
425 struct ast_variable *header;
426 char const *acr_method = NULL;
427 char const *acr_headers = NULL;
428 char const *origin = NULL;
429
430 RAII_VAR(struct ast_str *, allow, NULL, ast_free);
431 enum ast_http_method m;
432 int allowed = 0;
433
434 /* Regular OPTIONS response */
435 add_allow_header(handler, response);
437
438 /* Parse CORS headers */
439 for (header = headers; header != NULL; header = header->next) {
440 if (strcmp(ACR_METHOD, header->name) == 0) {
441 acr_method = header->value;
442 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
443 acr_headers = header->value;
444 } else if (strcmp("Origin", header->name) == 0) {
445 origin = header->value;
446 }
447 }
448
449 /* CORS 6.2, #1 - "If the Origin header is not present terminate this
450 * set of steps."
451 */
452 if (origin == NULL) {
453 return;
454 }
455
456 /* CORS 6.2, #2 - "If the value of the Origin header is not a
457 * case-sensitive match for any of the values in list of origins do not
458 * set any additional headers and terminate this set of steps.
459 *
460 * Always matching is acceptable since the list of origins can be
461 * unbounded.
462 *
463 * The Origin header can only contain a single origin as the user agent
464 * will not follow redirects."
465 */
466 if (!origin_allowed(origin)) {
467 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
468 return;
469 }
470
471 /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
472 * or if parsing failed, do not set any additional headers and terminate
473 * this set of steps."
474 */
475 if (acr_method == NULL) {
476 return;
477 }
478
479 /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
480 * headers let header field-names be the empty list."
481 */
482 if (acr_headers == NULL) {
483 acr_headers = "";
484 }
485
486 /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
487 * the values in list of methods do not set any additional headers and
488 * terminate this set of steps."
489 */
490 allow = ast_str_create(20);
491
492 if (!allow) {
494 return;
495 }
496
497 /* Go ahead and build the ACA_METHODS header at the same time */
498 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
499 if (handler->callbacks[m] != NULL) {
500 char const *m_str = ast_get_http_method(m);
501 if (strcmp(m_str, acr_method) == 0) {
502 allowed = 1;
503 }
504 ast_str_append(&allow, 0, ",%s", m_str);
505 }
506 }
507
508 if (!allowed) {
509 return;
510 }
511
512 /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
513 * case-insensitive match for any of the values in list of headers do
514 * not set any additional headers and terminate this set of steps.
515 *
516 * Note: Always matching is acceptable since the list of headers can be
517 * unbounded."
518 */
519
520 /* CORS 6.2 #7 - "If the resource supports credentials add a single
521 * Access-Control-Allow-Origin header, with the value of the Origin
522 * header as value, and add a single Access-Control-Allow-Credentials
523 * header with the case-sensitive string "true" as value."
524 *
525 * Added by process_cors_request() earlier in the request.
526 */
527
528 /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
529 * header..."
530 */
531
532 /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
533 * consisting of (a subset of) the list of methods."
534 */
535 ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
537
538
539 /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
540 * consisting of (a subset of) the list of headers.
541 *
542 * Since the list of headers can be unbounded simply returning headers
543 * can be enough."
544 */
545 if (!ast_strlen_zero(acr_headers)) {
546 ast_str_append(&response->headers, 0, "%s: %s\r\n",
547 ACA_HEADERS, acr_headers);
548 }
549}
550
551/*!
552 * \brief Authenticate a <code>?api_key=userid:password</code>
553 *
554 * \param api_key API key query parameter
555 * \return User object for the authenticated user.
556 * \retval NULL if authentication failed.
557 */
558static struct ast_ari_conf_user *authenticate_api_key(const char *api_key)
559{
560 RAII_VAR(char *, copy, NULL, ast_free);
561 char *username;
562 char *password;
563
564 password = copy = ast_strdup(api_key);
565 if (!copy) {
566 return NULL;
567 }
568
569 username = strsep(&password, ":");
570 if (!password) {
571 ast_log(LOG_WARNING, "Invalid api_key\n");
572 return NULL;
573 }
574
576}
577
578/*!
579 * \brief Authenticate an HTTP request.
580 *
581 * \param get_params GET parameters of the request.
582 * \param headers HTTP headers.
583 * \return User object for the authenticated user.
584 * \retval NULL if authentication failed.
585 */
586static struct ast_ari_conf_user *authenticate_user(struct ast_variable *get_params,
587 struct ast_variable *headers)
588{
589 RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
590 struct ast_variable *v;
591
592 /* HTTP Basic authentication */
593 http_auth = ast_http_get_auth(headers);
594 if (http_auth) {
595 return ast_ari_config_validate_user(http_auth->userid,
596 http_auth->password);
597 }
598
599 /* ?api_key authentication */
600 for (v = get_params; v; v = v->next) {
601 if (strcasecmp("api_key", v->name) == 0) {
602 return authenticate_api_key(v->value);
603 }
604 }
605
606 return NULL;
607}
608
609static void remove_trailing_slash(const char *uri,
610 struct ast_ari_response *response)
611{
612 char *slashless = ast_strdupa(uri);
613 slashless[strlen(slashless) - 1] = '\0';
614
615 /* While it's tempting to redirect the client to the slashless URL,
616 * that is problematic. A 302 Found is the most appropriate response,
617 * but most clients issue a GET on the location you give them,
618 * regardless of the method of the original request.
619 *
620 * While there are some ways around this, it gets into a lot of client
621 * specific behavior and corner cases in the HTTP standard. There's also
622 * very little practical benefit of redirecting; only GET and HEAD can
623 * be redirected automagically; all other requests "MUST NOT
624 * automatically redirect the request unless it can be confirmed by the
625 * user, since this might change the conditions under which the request
626 * was issued."
627 *
628 * Given all of that, a 404 with a nice message telling them what to do
629 * is probably our best bet.
630 */
631 ast_ari_response_error(response, 404, "Not Found",
632 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
633}
634
636 enum ast_ari_invoke_source source, const struct ast_http_uri *urih,
637 const char *uri, enum ast_http_method method,
638 struct ast_variable *get_params, struct ast_variable *headers,
639 struct ast_json *body, struct ast_ari_response *response)
640{
643 struct stasis_rest_handlers *wildcard_handler = NULL;
644 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
647
648 char *path = ast_strdupa(uri);
649 char *path_segment = NULL;
650 stasis_rest_callback callback;
651 SCOPE_ENTER(3, "Request: %s %s, path:%s\n", ast_get_http_method(method), uri, path);
652
653
654 if (!conf || !conf->general) {
655 if (ser && source == ARI_INVOKE_SOURCE_REST) {
657 }
658 ast_ari_response_error(response, 500, "Server Error", "URI handler config missing");
660 response->response_code, response->response_text);
661 }
662
663 user = authenticate_user(get_params, headers);
664
665 if (!user && source == ARI_INVOKE_SOURCE_REST) {
666 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
667 * message is used by an origin server to challenge the
668 * authorization of a user agent. This response MUST include a
669 * WWW-Authenticate header field containing at least one
670 * challenge applicable to the requested resource.
671 */
672 ast_ari_response_error(response, 401, "Unauthorized", "Authentication required");
673
674 /* Section 1.2:
675 * realm = "realm" "=" realm-value
676 * realm-value = quoted-string
677 * Section 2:
678 * challenge = "Basic" realm
679 */
680 ast_str_append(&response->headers, 0,
681 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
682 conf->general->auth_realm);
684 response->response_code, response->response_text);
685 } else if (!ast_fully_booted) {
686 ast_ari_response_error(response, 503, "Service Unavailable", "Asterisk not booted");
688 response->response_code, response->response_text);
689 } else if (user && user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
690 ast_ari_response_error(response, 403, "Forbidden", "Write access denied");
692 response->response_code, response->response_text);
693 } else if (ast_ends_with(uri, "/")) {
694 remove_trailing_slash(uri, response);
696 response->response_code, response->response_text);
697 } else if (ast_begins_with(uri, "api-docs/")) {
698 /* Serving up API docs */
699 if (method != AST_HTTP_GET) {
700 ast_ari_response_error(response, 405, "Method Not Allowed", "Unsupported method");
701 } else {
702 if (urih) {
703 /* Skip the api-docs prefix */
704 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, response);
705 } else {
706 /*
707 * If we were invoked without a urih, we're probably
708 * being called from the websocket so just use the
709 * default prefix. It's filled in by ast_http_uri_link().
710 */
711 ast_ari_get_docs(strchr(uri, '/') + 1, http_uri.prefix, headers, response);
712 }
713 }
715 response->response_code, response->response_text);
716 }
717
718 root = handler = get_root_handler();
719 ast_assert(root != NULL);
720
721 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
722 struct stasis_rest_handlers *found_handler = NULL;
723 int i;
724 SCOPE_ENTER(4, "Finding handler for path segment %s\n", path_segment);
725
727
728 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
729 struct stasis_rest_handlers *child = handler->children[i];
730 SCOPE_ENTER(5, "Checking handler path segment %s\n", child->path_segment);
731
732 if (child->is_wildcard) {
733 /* Record the path variable */
734 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
735 path_var->next = path_vars;
736 path_vars = path_var;
737 wildcard_handler = child;
738 ast_trace(-1, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
739
740 } else if (strcmp(child->path_segment, path_segment) == 0) {
741 found_handler = child;
742 ast_trace(-1, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
743 } else {
744 ast_trace(-1, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
745 }
746 SCOPE_EXIT("Done checking %s\n", child->path_segment);
747 }
748
749 if (!found_handler && wildcard_handler) {
750 ast_trace(-1, " No explicit handler found for %s. Using wildcard %s.\n",
751 path_segment, wildcard_handler->path_segment);
752 found_handler = wildcard_handler;
753 wildcard_handler = NULL;
754 }
755
756 if (found_handler == NULL) {
757 /* resource not found */
759 response, 404, "Not Found",
760 "Resource not found");
761 SCOPE_EXIT_EXPR(break, "Handler not found for %s\n", path_segment);
762 } else {
763 handler = found_handler;
764 }
765 SCOPE_EXIT("Done checking %s\n", path_segment);
766 }
767
768 if (handler == NULL || response->response_code == 404) {
769 /* resource not found */
771 response->response_code, response->response_text, uri);
772 }
773
775 if (method == AST_HTTP_OPTIONS) {
776 handle_options(handler, headers, response);
778 }
779
780 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
781 add_allow_header(handler, response);
783 response, 405, "Method Not Allowed",
784 "Invalid method");
786 response->response_code, response->response_text);
787 }
788
789 if (handler->is_websocket && method == AST_HTTP_GET) {
790 if (source == ARI_INVOKE_SOURCE_WEBSOCKET) {
792 response, 400, "Bad request",
793 "Can't upgrade to websocket from a websocket");
795 response->response_code, response->response_text);
796 }
797 /* WebSocket! */
798 ast_trace(-1, "Handling websocket %s\n", uri);
800 get_params, headers);
801 /* Since the WebSocket code handles the connection, we shouldn't
802 * do anything else; setting no_response */
803 response->no_response = 1;
805 }
806
807 callback = handler->callbacks[method];
808 if (callback == NULL) {
809 add_allow_header(handler, response);
811 response, 405, "Method Not Allowed",
812 "Invalid method");
814 response->response_code, response->response_text);
815 }
816
817 ast_trace(-1, "Running callback: %s\n", uri);
818 callback(ser, get_params, path_vars, headers, body, response);
819 if (response->message == NULL && response->response_code == 0) {
820 /* Really should not happen */
821 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
824 response, 501, "Not Implemented",
825 "Method not implemented");
827 response->response_code, response->response_text);
828 }
829 SCOPE_EXIT_RTN_VALUE(ARI_INVOKE_RESULT_SUCCESS, "Response: %d : %s\n",
830 response->response_code, response->response_text);
831}
832
833void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
834 struct ast_ari_response *response)
835{
836 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
837 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
838 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
839 struct ast_json *obj = NULL;
840 struct ast_variable *host = NULL;
841 struct ast_json_error error = {};
842 struct stat file_stat;
843
844 ast_debug(3, "%s(%s)\n", __func__, uri);
845
846 absolute_path_builder = ast_str_create(80);
847 if (absolute_path_builder == NULL) {
849 return;
850 }
851
852 /* absolute path to the rest-api directory */
853 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
854 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
855 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
856 if (absolute_api_dirname == NULL) {
857 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
859 response, 500, "Internal Server Error",
860 "Cannot find rest-api directory");
861 return;
862 }
863
864 /* absolute path to the requested file */
865 ast_str_append(&absolute_path_builder, 0, "%s", uri);
866 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
867 if (absolute_filename == NULL) {
868 switch (errno) {
869 case ENAMETOOLONG:
870 case ENOENT:
871 case ENOTDIR:
873 response, 404, "Not Found",
874 "Resource not found");
875 break;
876 case EACCES:
878 response, 403, "Forbidden",
879 "Permission denied");
880 break;
881 default:
883 "Error determining real path for uri '%s': %s\n",
884 uri, strerror(errno));
886 response, 500, "Internal Server Error",
887 "Cannot find file");
888 break;
889 }
890 return;
891 }
892
893 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
894 /* HACKERZ! */
896 "Invalid attempt to access '%s' (not in %s)\n",
897 absolute_filename, absolute_api_dirname);
899 response, 404, "Not Found",
900 "Resource not found");
901 return;
902 }
903
904 if (stat(absolute_filename, &file_stat) == 0) {
905 if (!(file_stat.st_mode & S_IFREG)) {
906 /* Not a file */
908 response, 403, "Forbidden",
909 "Invalid access");
910 return;
911 }
912 } else {
913 /* Does not exist */
915 response, 404, "Not Found",
916 "Resource not found");
917 return;
918 }
919
920 /* Load resource object from file */
921 obj = ast_json_load_new_file(absolute_filename, &error);
922 if (obj == NULL) {
923 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
924 error.source, error.line, error.column, error.text);
926 response, 500, "Internal Server Error",
927 "Yikes! Cannot parse resource");
928 return;
929 }
930
931 /* Update the basePath properly */
932 if (ast_json_object_get(obj, "basePath") != NULL) {
933 for (host = headers; host; host = host->next) {
934 if (strcasecmp(host->name, "Host") == 0) {
935 break;
936 }
937 }
938 if (host != NULL) {
939 if (prefix != NULL && strlen(prefix) > 0) {
941 obj, "basePath",
942 ast_json_stringf("http://%s%s/ari", host->value,prefix));
943 } else {
945 obj, "basePath",
946 ast_json_stringf("http://%s/ari", host->value));
947 }
948 } else {
949 /* Without the host, we don't have the basePath */
950 ast_json_object_del(obj, "basePath");
951 }
952 }
953
954 ast_ari_response_ok(response, obj);
955}
956
957/*!
958 * \brief Handle CORS headers for simple requests.
959 *
960 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
961 */
962static void process_cors_request(struct ast_variable *headers,
963 struct ast_ari_response *response)
964{
965 char const *origin = NULL;
966 struct ast_variable *header;
967
968 /* Parse CORS headers */
969 for (header = headers; header != NULL; header = header->next) {
970 if (strcmp("Origin", header->name) == 0) {
971 origin = header->value;
972 }
973 }
974
975 /* CORS 6.1, #1 - "If the Origin header is not present terminate this
976 * set of steps."
977 */
978 if (origin == NULL) {
979 return;
980 }
981
982 /* CORS 6.1, #2 - "If the value of the Origin header is not a
983 * case-sensitive match for any of the values in list of origins, do not
984 * set any additional headers and terminate this set of steps.
985 *
986 * Note: Always matching is acceptable since the list of origins can be
987 * unbounded."
988 */
989 if (!origin_allowed(origin)) {
990 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
991 return;
992 }
993
994 /* CORS 6.1, #3 - "If the resource supports credentials add a single
995 * Access-Control-Allow-Origin header, with the value of the Origin
996 * header as value, and add a single Access-Control-Allow-Credentials
997 * header with the case-sensitive string "true" as value.
998 *
999 * Otherwise, add a single Access-Control-Allow-Origin header, with
1000 * either the value of the Origin header or the string "*" as value."
1001 */
1002 ast_str_append(&response->headers, 0,
1003 "Access-Control-Allow-Origin: %s\r\n", origin);
1004 ast_str_append(&response->headers, 0,
1005 "Access-Control-Allow-Credentials: true\r\n");
1006
1007 /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
1008 * or more Access-Control-Expose-Headers headers, with as values the
1009 * header field names given in the list of exposed headers."
1010 *
1011 * No exposed headers; skipping
1012 */
1013}
1014
1016{
1017 RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
1018 cfg = ast_ari_config_get();
1019 return cfg->general->format;
1020}
1021
1022/*!
1023 * \internal
1024 * \brief ARI HTTP handler.
1025 *
1026 * This handler takes the HTTP request and turns it into the appropriate
1027 * RESTful request (conversion to JSON, routing, etc.)
1028 *
1029 * \param ser TCP session.
1030 * \param urih URI handler.
1031 * \param uri URI requested.
1032 * \param method HTTP method.
1033 * \param get_params HTTP \c GET params.
1034 * \param headers HTTP headers.
1035 */
1037 const struct ast_http_uri *urih,
1038 const char *uri,
1040 struct ast_variable *get_params,
1041 struct ast_variable *headers)
1042{
1043 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
1044 struct ast_ari_response response = { .fd = -1, 0 };
1045 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
1046 struct ast_variable *var;
1047 const char *app_name = NULL;
1048 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
1049 int debug_app = 0;
1051 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
1053
1054 if (!response_body) {
1056 ast_http_error(ser, 500, "Server Error", "Out of memory");
1057 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1058 }
1059
1060 response.headers = ast_str_create(40);
1061 if (!response.headers) {
1063 ast_http_error(ser, 500, "Server Error", "Out of memory");
1064 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1065 }
1066
1067 process_cors_request(headers, &response);
1068
1069 /* Process form data from a POST. It could be mixed with query
1070 * parameters, which seems a bit odd. But it's allowed, so that's okay
1071 * with us.
1072 */
1073 post_vars = ast_http_get_post_vars(ser, headers);
1074 if (!post_vars) {
1075 ast_trace(-1, "No post_vars\n");
1076 switch (errno) {
1077 case EFBIG:
1078 ast_ari_response_error(&response, 413,
1079 "Request Entity Too Large",
1080 "Request body too large");
1081 goto request_failed;
1082 case ENOMEM:
1084 ast_ari_response_error(&response, 500,
1085 "Internal Server Error",
1086 "Out of memory");
1087 goto request_failed;
1088 case EIO:
1089 ast_ari_response_error(&response, 400,
1090 "Bad Request", "Error parsing request body");
1091 goto request_failed;
1092 }
1093
1094 /* Look for a JSON request entity only if there were no post_vars.
1095 * If there were post_vars, then the request body would already have
1096 * been consumed and can not be read again.
1097 */
1098 ast_trace(-1, "Checking body for vars\n");
1099 body = ast_http_get_json(ser, headers);
1100 if (!body) {
1101 switch (errno) {
1102 case EFBIG:
1103 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
1104 goto request_failed;
1105 case ENOMEM:
1106 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
1107 goto request_failed;
1108 case EIO:
1109 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1110 goto request_failed;
1111 }
1112 }
1113 }
1114 if (get_params == NULL) {
1115 ast_trace(-1, "No get_params, using post_vars if any\n");
1116 get_params = post_vars;
1117 } else if (get_params && post_vars) {
1118 /* Has both post_vars and get_params */
1119 struct ast_variable *last_var = post_vars;
1120 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1121 while (last_var->next) {
1122 last_var = last_var->next;
1123 }
1124 /* The duped get_params will get freed when post_vars gets
1125 * ast_variables_destroyed.
1126 */
1127 last_var->next = ast_variables_dup(get_params);
1128 get_params = post_vars;
1129 }
1130
1131 /* At this point, get_params will contain post_vars (if any) */
1132 app_name = ast_variable_find_in_list(get_params, "app");
1133 if (!app_name) {
1134 struct ast_json *app = ast_json_object_get(body, "app");
1135
1137 }
1138 ast_trace(-1, "app_name: %s\n", app_name);
1139
1140 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1141 * and the global debug flag.
1142 */
1144 if (debug_app) {
1145 struct ast_str *buf = ast_str_create(512);
1147
1148 if (!buf || (body && !str)) {
1150 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1152 ast_free(buf);
1153 goto request_failed;
1154 }
1155
1156 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1158 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1159 for (var = headers; var; var = var->next) {
1160 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1161 }
1162 for (var = get_params; var; var = var->next) {
1163 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1164 }
1165 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1167 ast_free(buf);
1168 }
1169
1172 urih, uri, method, get_params, headers, body, &response);
1175 }
1176
1177 if (response.no_response) {
1178 /* The handler indicates no further response is necessary.
1179 * Probably because it already handled it */
1180 ast_free(response.headers);
1181 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1182 }
1183
1184request_failed:
1185
1186 /* If you explicitly want to have no content, set message to
1187 * ast_json_null().
1188 */
1189 ast_assert(response.message != NULL);
1190 ast_assert(response.response_code > 0);
1191
1192 /* response.message could be NULL, in which case the empty response_body
1193 * is correct
1194 */
1195 if (response.message && !ast_json_is_null(response.message)) {
1196 ast_str_append(&response.headers, 0,
1197 "Content-type: application/json\r\n");
1198 if (ast_json_dump_str_format(response.message, &response_body,
1199 ast_ari_json_format()) != 0) {
1200 /* Error encoding response */
1201 response.response_code = 500;
1202 response.response_text = "Internal Server Error";
1203 ast_str_set(&response_body, 0, "%s", "");
1204 ast_str_set(&response.headers, 0, "%s", "");
1205 }
1206 }
1207
1208 if (debug_app) {
1209 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1211 response.response_text, ast_str_buffer(response.headers),
1212 ast_str_buffer(response_body));
1213 }
1214
1215 ast_http_send(ser, method, response.response_code,
1216 response.response_text, response.headers, response_body,
1217 response.fd != -1 ? response.fd : 0, 0);
1218 /* ast_http_send takes ownership, so we don't have to free them */
1219 response_body = NULL;
1220
1221 ast_json_unref(response.message);
1222 if (response.fd >= 0) {
1223 close(response.fd);
1224 }
1225 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1226 response.response_text);
1227}
1228
1229static int unload_module(void)
1230{
1232
1234
1235 if (is_enabled()) {
1236 ast_debug(3, "Disabling ARI\n");
1238 }
1239
1241
1245
1247 oom_json = NULL;
1248
1249 return 0;
1250}
1251
1252static int load_module(void)
1253{
1255
1256 /* root_handler may have been built during a declined load */
1257 if (!root_handler) {
1259 }
1260 if (!root_handler) {
1262 }
1263
1264 /* oom_json may have been built during a declined load */
1265 if (!oom_json) {
1267 "{s: s}", "error", "Allocation failed");
1268 }
1269 if (!oom_json) {
1270 /* Ironic */
1271 unload_module();
1273 }
1274
1275 if (ast_ari_config_init() != 0) {
1276 unload_module();
1278 }
1279
1281 unload_module();
1283 }
1284
1285 if (is_enabled()) {
1286 ast_debug(3, "ARI enabled\n");
1288 } else {
1289 ast_debug(3, "ARI disabled\n");
1290 }
1291
1292 if (ast_ari_cli_register() != 0) {
1293 unload_module();
1295 }
1296
1298}
1299
1300static int reload_module(void)
1301{
1302 char was_enabled = is_enabled();
1303
1304 if (ast_ari_config_reload() != 0) {
1306 }
1307
1308 if (was_enabled && !is_enabled()) {
1309 ast_debug(3, "Disabling ARI\n");
1311 } else if (!was_enabled && is_enabled()) {
1312 ast_debug(3, "Enabling ARI\n");
1314 }
1315
1317}
1318
1320 .support_level = AST_MODULE_SUPPORT_CORE,
1321 .load = load_module,
1322 .unload = unload_module,
1324 .requires = "http,res_stasis,res_http_websocket",
1325 .load_pri = AST_MODPRI_APP_DEPEND,
static const char app[]
Definition: app_adsiprog.c:56
const char * str
Definition: app_jack.c:150
ast_mutex_t lock
Definition: app_sla.c:337
static int copy(char *infile, char *outfile)
Utility function to copy a file.
Asterisk RESTful API hooks.
ast_ari_invoke_source
Definition: ari.h:147
@ ARI_INVOKE_SOURCE_REST
Definition: ari.h:148
@ ARI_INVOKE_SOURCE_WEBSOCKET
Definition: ari.h:149
ast_ari_invoke_result
Definition: ari.h:137
@ ARI_INVOKE_RESULT_SUCCESS
Definition: ari.h:138
@ ARI_INVOKE_RESULT_ERROR_CLOSE
Definition: ari.h:140
@ ARI_INVOKE_RESULT_ERROR_CONTINUE
Definition: ari.h:139
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
int ari_websocket_unload_module(void)
void ari_handle_websocket(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.
int ari_websocket_load_module(void)
Internal API's for websockets.
#define var
Definition: ast_expr2f.c:605
char * strsep(char **str, const char *delims)
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
static PGresult * result
Definition: cel_pgsql.c:84
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2206
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_CALL_WITH_RESULT(level, __var, __funcname,...)
#define SCOPE_ENTER(level,...)
#define SCOPE_EXIT_EXPR(__expr,...)
#define SCOPE_EXIT(...)
#define ast_trace(level,...)
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:472
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:1405
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:1330
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:721
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1611
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:853
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:664
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
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:1013
#define ast_variable_new(name, value, filename)
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:629
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:190
#define ast_mutex_unlock(a)
Definition: lock.h:194
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:593
#define ast_mutex_destroy(a)
Definition: lock.h:192
#define ast_mutex_lock(a)
Definition: lock.h:193
size_t current
Definition: main/cli.c:113
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_APP_DEPEND
Definition: module.h:342
@ 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:609
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)
Callback for the root URI.
Definition: res_ari.c:1036
static struct stasis_rest_handlers * root_handler_create(void)
Definition: res_ari.c:305
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition: res_ari.c:1015
static struct stasis_rest_handlers * root_handler
Definition: res_ari.c:213
static ast_mutex_t root_handler_lock
Definition: res_ari.c:210
static int is_enabled(void)
Helper function to check if module is enabled.
Definition: res_ari.c:203
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:365
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:262
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition: res_ari.c:375
static struct ast_ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
Definition: res_ari.c:558
static struct ast_http_uri http_uri
Definition: res_ari.c:224
static int reload_module(void)
Definition: res_ari.c:1300
#define ACA_HEADERS
Definition: res_ari.c:413
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:319
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:336
static int origin_allowed(const char *origin)
Definition: res_ari.c:390
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:421
#define ACA_METHODS
Definition: res_ari.c:412
void ast_ari_response_accepted(struct ast_ari_response *response)
Fill in a Accepted (202) ast_ari_response.
Definition: res_ari.c:351
struct ast_json * ast_ari_oom_json(void)
The stock message to return when out of memory.
Definition: res_ari.c:234
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition: res_ari.c:962
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:358
#define ACR_HEADERS
Definition: res_ari.c:411
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition: res_ari.c:344
#define ACR_METHOD
Definition: res_ari.c:410
static int load_module(void)
Definition: res_ari.c:1252
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition: res_ari.c:833
static struct ast_json * oom_json
Definition: res_ari.c:216
static int unload_module(void)
Definition: res_ari.c:1229
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:586
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:239
enum ast_ari_invoke_result ast_ari_invoke(struct ast_tcptls_session_instance *ser, enum ast_ari_invoke_source source, const struct ast_http_uri *urih, 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:635
static struct stasis_rest_handlers * get_root_handler(void)
Definition: res_ari.c:298
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:105
struct ast_json * message
Definition: ari.h:103
int response_code
Definition: ari.h:108
const char * response_text
Definition: ari.h:112
unsigned int no_response
Definition: ari.h:114
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:139
Support for dynamic strings.
Definition: strings.h:623
describes a server instance
Definition: tcptls.h:151
struct ast_sockaddr remote_address
Definition: tcptls.h:153
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:95
size_t num_children
Definition: ari.h:93
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