Asterisk - The Open Source Telephony Project GIT-master-5467495
Loading...
Searching...
No Matches
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 <depend type="module">res_websocket_client</depend>
78 <support_level>core</support_level>
79 ***/
80
81#include "asterisk.h"
82
83#include "ari/internal.h"
84#include "ari/ari_websockets.h"
85#include "asterisk/ari.h"
86#include "asterisk/astobj2.h"
87#include "asterisk/module.h"
88#include "asterisk/paths.h"
89#include "asterisk/stasis_app.h"
90
91#include <string.h>
92#include <sys/stat.h>
93#include <unistd.h>
94
95/*! \brief Helper function to check if module is enabled. */
96static int is_enabled(void)
97{
99 return general && general->enabled;
100}
101
102/*! Lock for \ref root_handler */
104
105/*! Handler for root RESTful resource. */
107
108/*! Pre-defined message for allocation failures. */
109static struct ast_json *oom_json;
110
111/*! \brief Callback for the root URI. */
112static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
113 const struct ast_http_uri *urih, const char *uri,
114 enum ast_http_method method, struct ast_variable *get_params,
115 struct ast_variable *headers);
116
117static struct ast_http_uri http_uri = {
119 .description = "Asterisk RESTful API",
120 .uri = "ari",
121 .has_subtree = 1,
122 .data = NULL,
123 .key = __FILE__,
124 .no_decode_uri = 1,
125};
126
128{
129 return oom_json;
130}
131
133{
134 RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
135 size_t old_size, new_size;
136
138
139 old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
140 new_size = old_size + sizeof(handler);
141
142 new_handler = ao2_alloc(new_size, NULL);
143 if (!new_handler) {
144 return -1;
145 }
146 memcpy(new_handler, root_handler, old_size);
147 new_handler->children[new_handler->num_children++] = handler;
148
150 ao2_ref(new_handler, +1);
151 root_handler = new_handler;
152 return 0;
153}
154
156{
157 struct stasis_rest_handlers *new_handler;
158 size_t size;
159 size_t i;
160 size_t j;
161
163
165 size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
166
167 new_handler = ao2_alloc(size, NULL);
168 if (!new_handler) {
170 return -1;
171 }
172
173 /* Create replacement root_handler less the handler to remove. */
174 memcpy(new_handler, root_handler, sizeof(*new_handler));
175 for (i = 0, j = 0; i < root_handler->num_children; ++i) {
176 if (root_handler->children[i] == handler) {
177 continue;
178 }
179 new_handler->children[j++] = root_handler->children[i];
180 }
181 new_handler->num_children = j;
182
183 /* Replace the old root_handler with the new. */
185 root_handler = new_handler;
186
188 return 0;
189}
190
197
199{
201
202 handler = ao2_alloc(sizeof(*handler), NULL);
203 if (!handler) {
204 return NULL;
205 }
206 handler->path_segment = "ari";
207
208 ao2_ref(handler, +1);
209 return handler;
210}
211
213 int response_code,
214 const char *response_text,
215 const char *message_fmt, ...)
216{
218 va_list ap;
219
220 va_start(ap, message_fmt);
221 message = ast_json_vstringf(message_fmt, ap);
222 va_end(ap);
223 response->message = ast_json_pack("{s: o}",
224 "message", ast_json_ref(message));
225 response->response_code = response_code;
226 response->response_text = response_text;
227}
228
230 struct ast_json *message)
231{
232 response->message = message;
233 response->response_code = 200;
234 response->response_text = "OK";
235}
236
238{
239 response->message = ast_json_null();
240 response->response_code = 204;
241 response->response_text = "No Content";
242}
243
245{
246 response->message = ast_json_null();
247 response->response_code = 202;
248 response->response_text = "Accepted";
249}
250
252{
253 response->message = ast_json_ref(oom_json);
254 response->response_code = 500;
255 response->response_text = "Internal Server Error";
256}
257
259 const char *url, struct ast_json *message)
260{
262 response->message = message;
263 response->response_code = 201;
264 response->response_text = "Created";
265 ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
266}
267
269 struct ast_ari_response *response)
270{
271 enum ast_http_method m;
272 ast_str_append(&response->headers, 0,
273 "Allow: OPTIONS");
274 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
275 if (handler->callbacks[m] != NULL) {
276 ast_str_append(&response->headers, 0,
277 ",%s", ast_get_http_method(m));
278 }
279 }
280 ast_str_append(&response->headers, 0, "\r\n");
281}
282
283static int origin_allowed(const char *origin)
284{
286
287 char *allowed = ast_strdupa(general ? general->allowed_origins : "");
288 char *current;
289
290 while ((current = strsep(&allowed, ","))) {
291 if (!strcmp(current, "*")) {
292 return 1;
293 }
294
295 if (!strcmp(current, origin)) {
296 return 1;
297 }
298 }
299
300 return 0;
301}
302
303#define ACR_METHOD "Access-Control-Request-Method"
304#define ACR_HEADERS "Access-Control-Request-Headers"
305#define ACA_METHODS "Access-Control-Allow-Methods"
306#define ACA_HEADERS "Access-Control-Allow-Headers"
307
308/*!
309 * \brief Handle OPTIONS request, mainly for CORS preflight requests.
310 *
311 * Some browsers will send this prior to non-simple methods (i.e. DELETE).
312 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
313 */
315 struct ast_variable *headers,
316 struct ast_ari_response *response)
317{
318 struct ast_variable *header;
319 char const *acr_method = NULL;
320 char const *acr_headers = NULL;
321 char const *origin = NULL;
322
323 RAII_VAR(struct ast_str *, allow, NULL, ast_free);
324 enum ast_http_method m;
325 int allowed = 0;
326
327 /* Regular OPTIONS response */
328 add_allow_header(handler, response);
330
331 /* Parse CORS headers */
332 for (header = headers; header != NULL; header = header->next) {
333 if (strcmp(ACR_METHOD, header->name) == 0) {
334 acr_method = header->value;
335 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
336 acr_headers = header->value;
337 } else if (strcmp("Origin", header->name) == 0) {
338 origin = header->value;
339 }
340 }
341
342 /* CORS 6.2, #1 - "If the Origin header is not present terminate this
343 * set of steps."
344 */
345 if (origin == NULL) {
346 return;
347 }
348
349 /* CORS 6.2, #2 - "If the value of the Origin header is not a
350 * case-sensitive match for any of the values in list of origins do not
351 * set any additional headers and terminate this set of steps.
352 *
353 * Always matching is acceptable since the list of origins can be
354 * unbounded.
355 *
356 * The Origin header can only contain a single origin as the user agent
357 * will not follow redirects."
358 */
359 if (!origin_allowed(origin)) {
360 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
361 return;
362 }
363
364 /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
365 * or if parsing failed, do not set any additional headers and terminate
366 * this set of steps."
367 */
368 if (acr_method == NULL) {
369 return;
370 }
371
372 /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
373 * headers let header field-names be the empty list."
374 */
375 if (acr_headers == NULL) {
376 acr_headers = "";
377 }
378
379 /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
380 * the values in list of methods do not set any additional headers and
381 * terminate this set of steps."
382 */
383 allow = ast_str_create(20);
384
385 if (!allow) {
387 return;
388 }
389
390 /* Go ahead and build the ACA_METHODS header at the same time */
391 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
392 if (handler->callbacks[m] != NULL) {
393 char const *m_str = ast_get_http_method(m);
394 if (strcmp(m_str, acr_method) == 0) {
395 allowed = 1;
396 }
397 ast_str_append(&allow, 0, ",%s", m_str);
398 }
399 }
400
401 if (!allowed) {
402 return;
403 }
404
405 /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
406 * case-insensitive match for any of the values in list of headers do
407 * not set any additional headers and terminate this set of steps.
408 *
409 * Note: Always matching is acceptable since the list of headers can be
410 * unbounded."
411 */
412
413 /* CORS 6.2 #7 - "If the resource supports credentials add a single
414 * Access-Control-Allow-Origin header, with the value of the Origin
415 * header as value, and add a single Access-Control-Allow-Credentials
416 * header with the case-sensitive string "true" as value."
417 *
418 * Added by process_cors_request() earlier in the request.
419 */
420
421 /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
422 * header..."
423 */
424
425 /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
426 * consisting of (a subset of) the list of methods."
427 */
428 ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
430
431
432 /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
433 * consisting of (a subset of) the list of headers.
434 *
435 * Since the list of headers can be unbounded simply returning headers
436 * can be enough."
437 */
438 if (!ast_strlen_zero(acr_headers)) {
439 ast_str_append(&response->headers, 0, "%s: %s\r\n",
440 ACA_HEADERS, acr_headers);
441 }
442}
443
444/*
445 * This function is actually copied from http.c. Didn't see a need to make
446 * that function public just for this one use case.
447 */
448static struct ast_http_auth *create_http_auth(const char *userid, const char *password)
449{
450 struct ast_http_auth *auth;
451 size_t userid_len;
452 size_t password_len;
453
454 if (!userid || !password) {
455 ast_log(LOG_ERROR, "Invalid userid/password\n");
456 return NULL;
457 }
458
459 userid_len = strlen(userid) + 1;
460 password_len = strlen(password) + 1;
461
462 /* Allocate enough room to store everything in one memory block */
463 auth = ao2_alloc_options(sizeof(*auth) + userid_len + password_len, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
464 if (!auth) {
465 return NULL;
466 }
467
468 /* Put the userid right after the struct */
469 auth->userid = (char *)(auth + 1);
470 strcpy(auth->userid, userid);
471
472 /* Put the password right after the userid */
473 auth->password = auth->userid + userid_len;
474 strcpy(auth->password, password);
475
476 return auth;
477}
478
479/*!
480 * \brief Parse an api_key parameter from the query parameters into an ast_http_auth structure.
481 *
482 * \param get_params query string parameters
483 * \return ast_http_user object for the api_key.
484 * \retval NULL if api_key wasn't found or is had no password.
485 */
486static struct ast_http_auth *parse_api_keys(struct ast_variable *get_params)
487{
488 struct ast_variable *v;
489
490 for (v = get_params; v; v = v->next) {
491 if (ast_strings_equal(v->name, "api_key")) {
492 if (!ast_strlen_zero(v->value)) {
493 char *password = ast_strdupa(v->value);
494 char *username = strsep(&password, ":");
495 if (!ast_strlen_zero(password)) {
496 return create_http_auth(username, password);
497 }
498 }
499 break;
500 }
501 }
502 return NULL;
503}
504
505#if AST_DEVMODE
506static void test_ari_user_destructor(void *obj)
507{
508 struct ari_conf_user *ari_user = obj;
510}
511#endif
512
513/*!
514 * \brief Authenticate an HTTP request.
515 *
516 * \param get_params GET parameters of the request.
517 * \param headers HTTP headers.
518 * \return User object for the authenticated user.
519 * \retval NULL if authentication failed.
520 */
521static struct ari_conf_user *authenticate_user(struct ast_variable *get_params,
522 struct ast_variable *headers, enum ast_ari_invoke_source source)
523{
524 RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
525 struct ari_conf_user *ari_user = NULL;
526 SCOPE_ENTER(3, "\n");
527
528 /* HTTP Basic authentication */
529 http_auth = ast_http_get_auth(headers);
530 if (!http_auth) {
531 /* ?api_key authentication */
532 http_auth = parse_api_keys(get_params);
533 }
534 if (!http_auth) {
535 SCOPE_EXIT_RTN_VALUE(NULL, "No credentials found in HTTP headers or api_key\n");
536 }
537
538 if (source != ARI_INVOKE_SOURCE_TEST) {
539 ast_trace(-1, "Validating user %s\n", http_auth->userid);
540 ari_user = ari_conf_validate_user(http_auth->userid, http_auth->password);
541 if (!ari_user) {
542 SCOPE_EXIT_RTN_VALUE(NULL, "User %s failed to validate\n", http_auth->userid);
543 }
544 SCOPE_EXIT_RTN_VALUE(ari_user, "User %s validated. Read only: %s\n",
545 http_auth->userid, AST_YESNO(ari_user->read_only));
546 }
547#if AST_DEVMODE
548 /*
549 * ARI_INVOKE_SOURCE_TEST should only be set by the tests in test_ari.c but
550 * as a safety precaution, only check against the test usernames and passwords
551 * if we're in DEVMODE.
552 */
553 else {
554 int readonly = 0;
555
556 if (ast_strings_equal(http_auth->userid, "aritest")) {
557 if (!ast_strings_equal(http_auth->password, "aritestpw")) {
558 SCOPE_EXIT_RTN_VALUE(NULL, "User %s failed to validate\n", http_auth->userid);
559 }
560 } else if (ast_strings_equal(http_auth->userid, "aritestro")) {
561 if (!ast_strings_equal(http_auth->password, "aritestropw")) {
562 SCOPE_EXIT_RTN_VALUE(NULL, "User %s failed to validate\n", http_auth->userid);
563 }
564 readonly = 1;
565 }
566
567 ari_user = ao2_alloc(sizeof(*ari_user), test_ari_user_destructor);
568 if (!ari_user) {
569 SCOPE_EXIT_RTN_VALUE(NULL, "alloc failed\n");
570 }
571 if (ast_string_field_init(ari_user, 256) != 0) {
572 SCOPE_EXIT_RTN_VALUE(NULL, "alloc failed\n");
573 }
574 ast_string_field_set(ari_user, password, http_auth->password);
575 ari_user->read_only = readonly;
576
577 SCOPE_EXIT_RTN_VALUE(ari_user, "User %s validated. Read only: %s\n",
578 http_auth->userid, AST_YESNO(ari_user->read_only));
579 }
580
581#else
582 return NULL;
583#endif
584}
585
586static void remove_trailing_slash(const char *uri,
587 struct ast_ari_response *response)
588{
589 char *slashless = ast_strdupa(uri);
590 slashless[strlen(slashless) - 1] = '\0';
591
592 /* While it's tempting to redirect the client to the slashless URL,
593 * that is problematic. A 302 Found is the most appropriate response,
594 * but most clients issue a GET on the location you give them,
595 * regardless of the method of the original request.
596 *
597 * While there are some ways around this, it gets into a lot of client
598 * specific behavior and corner cases in the HTTP standard. There's also
599 * very little practical benefit of redirecting; only GET and HEAD can
600 * be redirected automagically; all other requests "MUST NOT
601 * automatically redirect the request unless it can be confirmed by the
602 * user, since this might change the conditions under which the request
603 * was issued."
604 *
605 * Given all of that, a 404 with a nice message telling them what to do
606 * is probably our best bet.
607 */
608 ast_ari_response_error(response, 404, "Not Found",
609 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
610}
611
613 enum ast_ari_invoke_source source, const struct ast_http_uri *urih,
614 const char *uri, enum ast_http_method method,
615 struct ast_variable *get_params, struct ast_variable *headers,
616 struct ast_json *body, struct ast_ari_response *response)
617{
620 struct stasis_rest_handlers *wildcard_handler = NULL;
621 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
624
625 char *path = ast_strdupa(uri);
626 char *path_segment = NULL;
628 SCOPE_ENTER(3, "Request: %s %s, path:%s\n", ast_get_http_method(method), uri, path);
629
630
631 if (!general) {
632 if (ser && source == ARI_INVOKE_SOURCE_REST) {
634 }
635 ast_ari_response_error(response, 500, "Server Error", "URI handler config missing");
637 response->response_code, response->response_text);
638 }
639
640 user = authenticate_user(get_params, headers, source);
641 if (!user) {
642 ast_ari_response_error(response, 401, "Unauthorized", "Authentication required");
643 if (source == ARI_INVOKE_SOURCE_REST) {
644 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
645 * message is used by an origin server to challenge the
646 * authorization of a user agent. This response MUST include a
647 * WWW-Authenticate header field containing at least one
648 * challenge applicable to the requested resource.
649 */
650
651 /* Section 1.2:
652 * realm = "realm" "=" realm-value
653 * realm-value = quoted-string
654 * Section 2:
655 * challenge = "Basic" realm
656 */
657 ast_str_append(&response->headers, 0,
658 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
659 general->auth_realm);
660 }
662 response->response_code, response->response_text);
663 }
664
665 if (source == ARI_INVOKE_SOURCE_REST && ser && user->acl && !ast_acl_list_is_empty(user->acl) &&
666 ast_apply_acl(user->acl, &ser->remote_address, "ARI User ACL") == AST_SENSE_DENY) {
667 ast_ari_response_error(response, 403, "Forbidden", "Access denied by ACL");
669 response->response_code, response->response_text);
670 } else if (!ast_fully_booted) {
671 ast_ari_response_error(response, 503, "Service Unavailable", "Asterisk not booted");
673 response->response_code, response->response_text);
674 } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
675 ast_ari_response_error(response, 403, "Forbidden", "Write access denied");
677 response->response_code, response->response_text);
678 } else if (ast_ends_with(uri, "/")) {
679 remove_trailing_slash(uri, response);
681 response->response_code, response->response_text);
682 } else if (ast_begins_with(uri, "api-docs/")) {
683 /* Serving up API docs */
684 if (method != AST_HTTP_GET) {
685 ast_ari_response_error(response, 405, "Method Not Allowed", "Unsupported method");
686 } else {
687 if (urih) {
688 /* Skip the api-docs prefix */
689 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, response);
690 } else {
691 /*
692 * If we were invoked without a urih, we're probably
693 * being called from the websocket so just use the
694 * default prefix. It's filled in by ast_http_uri_link().
695 */
696 ast_ari_get_docs(strchr(uri, '/') + 1, http_uri.prefix, headers, response);
697 }
698 }
700 response->response_code, response->response_text);
701 }
702
703 root = handler = get_root_handler();
704 ast_assert(root != NULL);
705
706 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
707 struct stasis_rest_handlers *found_handler = NULL;
708 int i;
709 SCOPE_ENTER(4, "Finding handler for path segment %s\n", path_segment);
710
712
713 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
714 struct stasis_rest_handlers *child = handler->children[i];
715 SCOPE_ENTER(5, "Checking handler path segment %s\n", child->path_segment);
716
717 if (child->is_wildcard) {
718 /* Record the path variable */
719 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
720 path_var->next = path_vars;
721 path_vars = path_var;
722 wildcard_handler = child;
723 ast_trace(-1, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
724
725 } else if (strcmp(child->path_segment, path_segment) == 0) {
726 found_handler = child;
727 ast_trace(-1, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
728 } else {
729 ast_trace(-1, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
730 }
731 SCOPE_EXIT("Done checking %s\n", child->path_segment);
732 }
733
734 if (!found_handler && wildcard_handler) {
735 ast_trace(-1, " No explicit handler found for %s. Using wildcard %s.\n",
736 path_segment, wildcard_handler->path_segment);
737 found_handler = wildcard_handler;
738 wildcard_handler = NULL;
739 }
740
741 if (found_handler == NULL) {
742 /* resource not found */
744 response, 404, "Not Found",
745 "Resource not found");
746 SCOPE_EXIT_EXPR(break, "Handler not found for %s\n", path_segment);
747 } else {
748 handler = found_handler;
749 }
750 SCOPE_EXIT("Done checking %s\n", path_segment);
751 }
752
753 if (handler == NULL || response->response_code == 404) {
754 /* resource not found */
756 response->response_code, response->response_text, uri);
757 }
758
760 if (method == AST_HTTP_OPTIONS) {
761 handle_options(handler, headers, response);
763 }
764
765 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
766 add_allow_header(handler, response);
768 response, 405, "Method Not Allowed",
769 "Invalid method");
771 response->response_code, response->response_text);
772 }
773
774 if (handler->is_websocket && method == AST_HTTP_GET) {
775 if (source == ARI_INVOKE_SOURCE_WEBSOCKET) {
777 response, 400, "Bad request",
778 "Can't upgrade to websocket from a websocket");
780 response->response_code, response->response_text);
781 }
782 /* WebSocket! */
783 ast_trace(-1, "Handling websocket %s\n", uri);
785 get_params, headers);
786 /* Since the WebSocket code handles the connection, we shouldn't
787 * do anything else; setting no_response */
788 response->no_response = 1;
790 }
791
792 callback = handler->callbacks[method];
793 if (callback == NULL) {
794 add_allow_header(handler, response);
796 response, 405, "Method Not Allowed",
797 "Invalid method");
799 response->response_code, response->response_text);
800 }
801
802 ast_trace(-1, "Running callback: %s\n", uri);
803 callback(ser, get_params, path_vars, headers, body, response);
804 if (response->message == NULL && response->response_code == 0) {
805 /* Really should not happen */
806 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
809 response, 501, "Not Implemented",
810 "Method not implemented");
812 response->response_code, response->response_text);
813 }
814 SCOPE_EXIT_RTN_VALUE(ARI_INVOKE_RESULT_SUCCESS, "Response: %d : %s\n",
815 response->response_code, response->response_text);
816}
817
818void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
819 struct ast_ari_response *response)
820{
821 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
822 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
823 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
824 struct ast_json *obj = NULL;
825 struct ast_variable *host = NULL;
826 struct ast_json_error error = {};
827 struct stat file_stat;
828
829 ast_debug(3, "%s(%s)\n", __func__, uri);
830
831 absolute_path_builder = ast_str_create(80);
832 if (absolute_path_builder == NULL) {
834 return;
835 }
836
837 /* absolute path to the rest-api directory */
838 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
839 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
840 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
841 if (absolute_api_dirname == NULL) {
842 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
844 response, 500, "Internal Server Error",
845 "Cannot find rest-api directory");
846 return;
847 }
848
849 /* absolute path to the requested file */
850 ast_str_append(&absolute_path_builder, 0, "%s", uri);
851 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
852 if (absolute_filename == NULL) {
853 switch (errno) {
854 case ENAMETOOLONG:
855 case ENOENT:
856 case ENOTDIR:
858 response, 404, "Not Found",
859 "Resource not found");
860 break;
861 case EACCES:
863 response, 403, "Forbidden",
864 "Permission denied");
865 break;
866 default:
868 "Error determining real path for uri '%s': %s\n",
869 uri, strerror(errno));
871 response, 500, "Internal Server Error",
872 "Cannot find file");
873 break;
874 }
875 return;
876 }
877
878 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
879 /* HACKERZ! */
881 "Invalid attempt to access '%s' (not in %s)\n",
882 absolute_filename, absolute_api_dirname);
884 response, 404, "Not Found",
885 "Resource not found");
886 return;
887 }
888
889 if (stat(absolute_filename, &file_stat) == 0) {
890 if (!(file_stat.st_mode & S_IFREG)) {
891 /* Not a file */
893 response, 403, "Forbidden",
894 "Invalid access");
895 return;
896 }
897 } else {
898 /* Does not exist */
900 response, 404, "Not Found",
901 "Resource not found");
902 return;
903 }
904
905 /* Load resource object from file */
906 obj = ast_json_load_new_file(absolute_filename, &error);
907 if (obj == NULL) {
908 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
909 error.source, error.line, error.column, error.text);
911 response, 500, "Internal Server Error",
912 "Yikes! Cannot parse resource");
913 return;
914 }
915
916 /* Update the basePath properly */
917 if (ast_json_object_get(obj, "basePath") != NULL) {
918 for (host = headers; host; host = host->next) {
919 if (strcasecmp(host->name, "Host") == 0) {
920 break;
921 }
922 }
923 if (host != NULL) {
924 if (prefix != NULL && strlen(prefix) > 0) {
926 obj, "basePath",
927 ast_json_stringf("http://%s%s/ari", host->value,prefix));
928 } else {
930 obj, "basePath",
931 ast_json_stringf("http://%s/ari", host->value));
932 }
933 } else {
934 /* Without the host, we don't have the basePath */
935 ast_json_object_del(obj, "basePath");
936 }
937 }
938
939 ast_ari_response_ok(response, obj);
940}
941
942/*!
943 * \brief Handle CORS headers for simple requests.
944 *
945 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
946 */
947static void process_cors_request(struct ast_variable *headers,
948 struct ast_ari_response *response)
949{
950 char const *origin = NULL;
951 struct ast_variable *header;
952
953 /* Parse CORS headers */
954 for (header = headers; header != NULL; header = header->next) {
955 if (strcmp("Origin", header->name) == 0) {
956 origin = header->value;
957 }
958 }
959
960 /* CORS 6.1, #1 - "If the Origin header is not present terminate this
961 * set of steps."
962 */
963 if (origin == NULL) {
964 return;
965 }
966
967 /* CORS 6.1, #2 - "If the value of the Origin header is not a
968 * case-sensitive match for any of the values in list of origins, do not
969 * set any additional headers and terminate this set of steps.
970 *
971 * Note: Always matching is acceptable since the list of origins can be
972 * unbounded."
973 */
974 if (!origin_allowed(origin)) {
975 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
976 return;
977 }
978
979 /* CORS 6.1, #3 - "If the resource supports credentials add a single
980 * Access-Control-Allow-Origin header, with the value of the Origin
981 * header as value, and add a single Access-Control-Allow-Credentials
982 * header with the case-sensitive string "true" as value.
983 *
984 * Otherwise, add a single Access-Control-Allow-Origin header, with
985 * either the value of the Origin header or the string "*" as value."
986 */
987 ast_str_append(&response->headers, 0,
988 "Access-Control-Allow-Origin: %s\r\n", origin);
989 ast_str_append(&response->headers, 0,
990 "Access-Control-Allow-Credentials: true\r\n");
991
992 /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
993 * or more Access-Control-Expose-Headers headers, with as values the
994 * header field names given in the list of exposed headers."
995 *
996 * No exposed headers; skipping
997 */
998}
999
1001{
1003 return general ? general->format : AST_JSON_COMPACT;
1004}
1005
1006/*!
1007 * \internal
1008 * \brief ARI HTTP handler.
1009 *
1010 * This handler takes the HTTP request and turns it into the appropriate
1011 * RESTful request (conversion to JSON, routing, etc.)
1012 *
1013 * \param ser TCP session.
1014 * \param urih URI handler.
1015 * \param uri URI requested.
1016 * \param method HTTP method.
1017 * \param get_params HTTP \c GET params.
1018 * \param headers HTTP headers.
1019 */
1021 const struct ast_http_uri *urih,
1022 const char *uri,
1024 struct ast_variable *get_params,
1025 struct ast_variable *headers)
1026{
1027 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
1028 struct ast_ari_response response = { .fd = -1, 0 };
1029 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
1030 struct ast_variable *var;
1031 const char *app_name = NULL;
1032 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
1033 int debug_app = 0;
1035 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
1037
1038 if (!response_body) {
1040 ast_http_error(ser, 500, "Server Error", "Out of memory");
1041 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1042 }
1043
1044 response.headers = ast_str_create(40);
1045 if (!response.headers) {
1047 ast_http_error(ser, 500, "Server Error", "Out of memory");
1048 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1049 }
1050
1051 process_cors_request(headers, &response);
1052
1053 /* Process form data from a POST. It could be mixed with query
1054 * parameters, which seems a bit odd. But it's allowed, so that's okay
1055 * with us.
1056 */
1057 post_vars = ast_http_get_post_vars(ser, headers);
1058 if (!post_vars) {
1059 ast_trace(-1, "No post_vars\n");
1060 switch (errno) {
1061 case EFBIG:
1062 ast_ari_response_error(&response, 413,
1063 "Request Entity Too Large",
1064 "Request body too large");
1065 goto request_failed;
1066 case ENOMEM:
1068 ast_ari_response_error(&response, 500,
1069 "Internal Server Error",
1070 "Out of memory");
1071 goto request_failed;
1072 case EIO:
1073 ast_ari_response_error(&response, 400,
1074 "Bad Request", "Error parsing request body");
1075 goto request_failed;
1076 }
1077
1078 /* Look for a JSON request entity only if there were no post_vars.
1079 * If there were post_vars, then the request body would already have
1080 * been consumed and can not be read again.
1081 */
1082 ast_trace(-1, "Checking body for vars\n");
1083 body = ast_http_get_json(ser, headers);
1084 if (!body) {
1085 switch (errno) {
1086 case EFBIG:
1087 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
1088 goto request_failed;
1089 case ENOMEM:
1090 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
1091 goto request_failed;
1092 case EIO:
1093 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1094 goto request_failed;
1095 }
1096 }
1097 }
1098 if (get_params == NULL) {
1099 ast_trace(-1, "No get_params, using post_vars if any\n");
1100 get_params = post_vars;
1101 } else if (get_params && post_vars) {
1102 /* Has both post_vars and get_params */
1103 struct ast_variable *last_var = post_vars;
1104 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1105 while (last_var->next) {
1106 last_var = last_var->next;
1107 }
1108 /* The duped get_params will get freed when post_vars gets
1109 * ast_variables_destroyed.
1110 */
1111 last_var->next = ast_variables_dup(get_params);
1112 get_params = post_vars;
1113 }
1114
1115 /* At this point, get_params will contain post_vars (if any) */
1116 app_name = ast_variable_find_in_list(get_params, "app");
1117 if (!app_name) {
1118 struct ast_json *app = ast_json_object_get(body, "app");
1119
1121 }
1122 ast_trace(-1, "app_name: %s\n", app_name);
1123
1124 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1125 * and the global debug flag.
1126 */
1128 if (debug_app) {
1129 struct ast_str *buf = ast_str_create(512);
1131
1132 if (!buf || (body && !str)) {
1134 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1136 ast_free(buf);
1137 goto request_failed;
1138 }
1139
1140 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1142 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1143 for (var = headers; var; var = var->next) {
1144 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1145 }
1146 for (var = get_params; var; var = var->next) {
1147 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1148 }
1149 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1151 ast_free(buf);
1152 }
1153
1156 urih, uri, method, get_params, headers, body, &response);
1159 }
1160
1161 if (response.no_response) {
1162 /* The handler indicates no further response is necessary.
1163 * Probably because it already handled it */
1164 ast_free(response.headers);
1165 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1166 }
1167
1168request_failed:
1169
1170 /* If you explicitly want to have no content, set message to
1171 * ast_json_null().
1172 */
1173 ast_assert(response.message != NULL);
1174 ast_assert(response.response_code > 0);
1175
1176 /* response.message could be NULL, in which case the empty response_body
1177 * is correct
1178 */
1179 if (response.message && !ast_json_is_null(response.message)) {
1180 ast_str_append(&response.headers, 0,
1181 "Content-type: application/json\r\n");
1182 if (ast_json_dump_str_format(response.message, &response_body,
1183 ast_ari_json_format()) != 0) {
1184 /* Error encoding response */
1185 response.response_code = 500;
1186 response.response_text = "Internal Server Error";
1187 ast_str_set(&response_body, 0, "%s", "");
1188 ast_str_set(&response.headers, 0, "%s", "");
1189 }
1190 }
1191
1192 if (debug_app) {
1193 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1195 response.response_text, ast_str_buffer(response.headers),
1196 ast_str_buffer(response_body));
1197 }
1198
1199 ast_http_send(ser, method, response.response_code,
1200 response.response_text, response.headers, response_body,
1201 response.fd != -1 ? response.fd : 0, 0);
1202 /* ast_http_send takes ownership, so we don't have to free them */
1203 response_body = NULL;
1204
1205 ast_json_unref(response.message);
1206 if (response.fd >= 0) {
1207 close(response.fd);
1208 }
1209 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1210 response.response_text);
1211}
1212
1213static int unload_module(void)
1214{
1216
1218
1219 if (is_enabled()) {
1220 ast_debug(3, "Disabling ARI\n");
1222 }
1223
1225
1229
1231 oom_json = NULL;
1232
1233 return 0;
1234}
1235
1236static int load_module(void)
1237{
1239
1240 /* root_handler may have been built during a declined load */
1241 if (!root_handler) {
1243 }
1244 if (!root_handler) {
1246 }
1247
1248 /* oom_json may have been built during a declined load */
1249 if (!oom_json) {
1251 "{s: s}", "error", "Allocation failed");
1252 }
1253 if (!oom_json) {
1254 /* Ironic */
1255 unload_module();
1257 }
1258
1259 /*
1260 * ari_websocket_load_module() needs to know if ARI is enabled
1261 * globally so it needs the "general" config to be loaded but it
1262 * also needs to register a sorcery object observer for
1263 * "outbound_websocket" BEFORE the outbound_websocket configs are loaded.
1264 * outbound_websocket in turn needs the users to be loaded so we'll
1265 * initialize sorcery and load "general" and "user" configs first, then
1266 * load the websocket module, then load the "outbound_websocket" configs
1267 * which will fire the observers.
1268 */
1270 unload_module();
1272 }
1273
1275 unload_module();
1277 }
1278
1279 /*
1280 * Now we can load the outbound_websocket configs which will
1281 * fire the observers.
1282 */
1284
1285 if (ari_cli_register() != 0) {
1286 unload_module();
1288 }
1289
1290 if (is_enabled()) {
1291 ast_debug(3, "ARI enabled\n");
1293 } else {
1294 ast_debug(3, "ARI disabled\n");
1295 }
1296
1298}
1299
1300static int reload_module(void)
1301{
1302 char was_enabled = is_enabled();
1303 int is_now_enabled = 0;
1304
1306
1307 is_now_enabled = is_enabled();
1308
1309 if (was_enabled && !is_now_enabled) {
1310 ast_debug(3, "Disabling ARI\n");
1312 } else if (!was_enabled && is_now_enabled) {
1313 ast_debug(3, "Enabling ARI\n");
1315 }
1316
1318}
1319
1321 .support_level = AST_MODULE_SUPPORT_CORE,
1322 .load = load_module,
1323 .unload = unload_module,
1325 .requires = "http,res_stasis,res_http_websocket,res_websocket_client",
1326 .optional_modules = "res_ari_model",
1327 .load_pri = AST_MODPRI_APP_DEPEND,
enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose)
Apply a set of rules to a given IP address.
Definition acl.c:799
@ AST_SENSE_DENY
Definition acl.h:37
int ast_acl_list_is_empty(struct ast_acl_list *acl_list)
Determines if an ACL is empty or if it contains entries.
Definition acl.c:540
static const char app[]
const char * str
Definition app_jack.c:150
ast_mutex_t lock
Definition app_sla.c:337
Asterisk RESTful API hooks.
ast_ari_invoke_source
Definition ari.h:147
@ ARI_INVOKE_SOURCE_REST
Definition ari.h:148
@ ARI_INVOKE_SOURCE_TEST
Definition ari.h:150
@ 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_load_module(int is_enabled)
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.
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_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_log
Definition astobj2.c:42
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#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_options(data_size, destructor_fn, options)
Definition astobj2.h:404
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
static PGresult * result
Definition cel_pgsql.c:84
size_t current
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags, int rdlock)
char buf[BUFSIZE]
Definition eagi_proxy.c:66
#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:145
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:522
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:1463
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:1388
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:779
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition http.c:1677
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition http.c:207
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:911
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:722
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition http.c:747
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.
#define ast_variable_new(name, value, filename)
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define ast_verbose(...)
Internal API's for res_ari.
@ ARI_CONF_LOAD_ALL
Definition internal.h:158
@ ARI_CONF_RELOAD
Definition internal.h:154
@ ARI_CONF_INIT
Definition internal.h:153
@ ARI_CONF_LOAD_OWC
Definition internal.h:157
@ ARI_CONF_LOAD_USER
Definition internal.h:156
@ ARI_CONF_LOAD_GENERAL
Definition internal.h:155
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
@ AST_JSON_COMPACT
Definition json.h:793
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:193
#define ast_mutex_unlock(a)
Definition lock.h:197
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition lock.h:596
#define ast_mutex_destroy(a)
Definition lock.h:195
#define ast_mutex_lock(a)
Definition lock.h:196
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:127
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition options.c:159
const char * app_name(struct ast_app *app)
Definition pbx_app.c:475
void ari_cli_unregister(void)
Unregister CLI commands for ARI.
int ari_cli_register(void)
Register CLI commands for ARI.
int ari_conf_load(enum ari_conf_load_flags flags)
(Re)load the ARI configuration
struct ari_conf_general * ari_conf_get_general(void)
struct ari_conf_user * ari_conf_validate_user(const char *username, const char *password)
Validated a user's credentials.
void ari_conf_destroy(void)
Destroy the ARI configuration.
static void remove_trailing_slash(const char *uri, struct ast_ari_response *response)
Definition res_ari.c:586
static struct ast_http_auth * parse_api_keys(struct ast_variable *get_params)
Parse an api_key parameter from the query parameters into an ast_http_auth structure.
Definition res_ari.c:486
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:1020
static struct stasis_rest_handlers * root_handler_create(void)
Definition res_ari.c:198
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition res_ari.c:1000
static struct ast_http_auth * create_http_auth(const char *userid, const char *password)
Definition res_ari.c:448
static struct stasis_rest_handlers * root_handler
Definition res_ari.c:106
static ast_mutex_t root_handler_lock
Definition res_ari.c:103
static int is_enabled(void)
Helper function to check if module is enabled.
Definition res_ari.c:96
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:258
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition res_ari.c:155
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition res_ari.c:268
static struct ast_http_uri http_uri
Definition res_ari.c:117
static int reload_module(void)
Definition res_ari.c:1300
#define ACA_HEADERS
Definition res_ari.c:306
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:212
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:229
static int origin_allowed(const char *origin)
Definition res_ari.c:283
static struct ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers, enum ast_ari_invoke_source source)
Authenticate an HTTP request.
Definition res_ari.c:521
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:314
#define ACA_METHODS
Definition res_ari.c:305
void ast_ari_response_accepted(struct ast_ari_response *response)
Fill in a Accepted (202) ast_ari_response.
Definition res_ari.c:244
struct ast_json * ast_ari_oom_json(void)
The stock message to return when out of memory.
Definition res_ari.c:127
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition res_ari.c:947
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:251
#define ACR_HEADERS
Definition res_ari.c:304
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition res_ari.c:237
#define ACR_METHOD
Definition res_ari.c:303
static int load_module(void)
Definition res_ari.c:1236
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition res_ari.c:818
static struct ast_json * oom_json
Definition res_ari.c:109
static int unload_module(void)
Definition res_ari.c:1213
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition res_ari.c:132
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:612
static struct stasis_rest_handlers * get_root_handler(void)
Definition res_ari.c:191
static char url[512]
static int reload(void)
const char * method
Definition res_pjsip.c:1277
#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.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
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
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238
#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
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition strings.h:143
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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
Global configuration options for ARI.
Definition internal.h:58
Per-user configuration options.
Definition internal.h:85
const ast_string_field password
Definition internal.h:90
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
char * password
Definition http.h:129
char * userid
Definition http.h:127
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:142
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
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 phoneprov_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,...)
#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:981
#define ast_assert(a)
Definition utils.h:779
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition utils.c:760
const struct ast_flags ast_uri_http_legacy
Definition utils.c:718