Asterisk - The Open Source Telephony Project GIT-master-c753fe4
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
Macros | Functions | Variables
res_ari.c File Reference

HTTP binding for the Stasis API. More...

#include "asterisk.h"
#include "ari/internal.h"
#include "ari/ari_websockets.h"
#include "asterisk/ari.h"
#include "asterisk/astobj2.h"
#include "asterisk/module.h"
#include "asterisk/paths.h"
#include "asterisk/stasis_app.h"
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
Include dependency graph for res_ari.c:

Go to the source code of this file.

Macros

#define ACA_HEADERS   "Access-Control-Allow-Headers"
 
#define ACA_METHODS   "Access-Control-Allow-Methods"
 
#define ACR_HEADERS   "Access-Control-Request-Headers"
 
#define ACR_METHOD   "Access-Control-Request-Method"
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
static void add_allow_header (struct stasis_rest_handlers *handler, struct ast_ari_response *response)
 
int ast_ari_add_handler (struct stasis_rest_handlers *handler)
 
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. More...
 
void ast_ari_get_docs (const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
 
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)
 
enum ast_json_encoding_format ast_ari_json_format (void)
 Configured encoding format for JSON output. More...
 
struct ast_jsonast_ari_oom_json (void)
 The stock message to return when out of memory. More...
 
int ast_ari_remove_handler (struct stasis_rest_handlers *handler)
 
void ast_ari_response_accepted (struct ast_ari_response *response)
 Fill in a Accepted (202) ast_ari_response. More...
 
void ast_ari_response_alloc_failed (struct ast_ari_response *response)
 Fill in response with a 500 message for allocation failures. More...
 
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. More...
 
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. More...
 
void ast_ari_response_no_content (struct ast_ari_response *response)
 Fill in a No Content (204) ast_ari_response. More...
 
void ast_ari_response_ok (struct ast_ari_response *response, struct ast_json *message)
 Fill in an OK (200) ast_ari_response. More...
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static struct ari_conf_userauthenticate_api_key (const char *api_key)
 Authenticate a ?api_key=userid:password More...
 
static struct ari_conf_userauthenticate_user (struct ast_variable *get_params, struct ast_variable *headers)
 Authenticate an HTTP request. More...
 
static struct stasis_rest_handlersget_root_handler (void)
 
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. More...
 
static int is_enabled (void)
 Helper function to check if module is enabled. More...
 
static int load_module (void)
 
static int origin_allowed (const char *origin)
 
static void process_cors_request (struct ast_variable *headers, struct ast_ari_response *response)
 Handle CORS headers for simple requests. More...
 
static int reload_module (void)
 
static void remove_trailing_slash (const char *uri, struct ast_ari_response *response)
 
static struct stasis_rest_handlersroot_handler_create (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "Asterisk RESTful Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .requires = "http,res_stasis,res_http_websocket,res_websocket_client", .load_pri = AST_MODPRI_APP_DEPEND, }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_http_uri http_uri
 
static struct ast_jsonoom_json
 
static struct stasis_rest_handlersroot_handler
 
static ast_mutex_t root_handler_lock
 

Detailed Description

HTTP binding for the Stasis API.

Author
David M. Lee, II dlee@.nosp@m.digi.nosp@m.um.co.nosp@m.m

The API itself is documented using Swagger, a lightweight mechanism for documenting RESTful API's using JSON. This allows us to use swagger-ui to provide executable documentation for the API, generate client bindings in different languages, and generate a lot of the boilerplate code for implementing the RESTful bindings. The API docs live in the rest-api/ directory.

The RESTful bindings are generated from the Swagger API docs using a set of Mustache templates. The code generator is written in Python, and uses the Python implementation pystache. Pystache has no dependencies, and be installed easily using pip. Code generation code lives in rest-api-templates/.

The generated code reduces a lot of boilerplate when it comes to handling HTTP requests. It also helps us have greater consistency in the REST API.

The structure of the generated code is:

The basic flow of an HTTP request is:

Definition in file res_ari.c.

Macro Definition Documentation

◆ ACA_HEADERS

#define ACA_HEADERS   "Access-Control-Allow-Headers"

Definition at line 306 of file res_ari.c.

◆ ACA_METHODS

#define ACA_METHODS   "Access-Control-Allow-Methods"

Definition at line 305 of file res_ari.c.

◆ ACR_HEADERS

#define ACR_HEADERS   "Access-Control-Request-Headers"

Definition at line 304 of file res_ari.c.

◆ ACR_METHOD

#define ACR_METHOD   "Access-Control-Request-Method"

Definition at line 303 of file res_ari.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1235 of file res_ari.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1235 of file res_ari.c.

◆ add_allow_header()

static void add_allow_header ( struct stasis_rest_handlers handler,
struct ast_ari_response response 
)
static

Definition at line 268 of file res_ari.c.

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}
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:58
@ AST_HTTP_MAX_METHOD
Definition: http.h:66
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition: http.c:193
#define NULL
Definition: resample.c:96
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
struct ast_str * headers
Definition: ari.h:105
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

References ast_get_http_method(), AST_HTTP_MAX_METHOD, ast_str_append(), handler(), ast_ari_response::headers, and NULL.

Referenced by ast_ari_invoke(), and handle_options().

◆ ast_ari_add_handler()

int ast_ari_add_handler ( struct stasis_rest_handlers handler)

Add a resource for REST handling.

Parameters
handlerHandler to add.
Return values
0on success.
non-zeroon failure.

Definition at line 132 of file res_ari.c.

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}
ast_mutex_t lock
Definition: app_sla.c:337
#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
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:596
static struct stasis_rest_handlers * root_handler
Definition: res_ari.c:106
static ast_mutex_t root_handler_lock
Definition: res_ari.c:103
Handler for a single RESTful path segment.
Definition: ari.h:69
size_t num_children
Definition: ari.h:93
#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

References ao2_alloc, ao2_cleanup, ao2_ref, handler(), lock, NULL, stasis_rest_handlers::num_children, RAII_VAR, root_handler, root_handler_lock, and SCOPED_MUTEX.

Referenced by load_module(), and setup_invocation_test().

◆ ast_ari_callback()

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 
)
static

Callback for the root URI.

Definition at line 928 of file res_ari.c.

934{
935 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
936 struct ast_ari_response response = { .fd = -1, 0 };
937 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
938 struct ast_variable *var;
939 const char *app_name = NULL;
940 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
941 int debug_app = 0;
943 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
945
946 if (!response_body) {
948 ast_http_error(ser, 500, "Server Error", "Out of memory");
949 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
950 }
951
952 response.headers = ast_str_create(40);
953 if (!response.headers) {
955 ast_http_error(ser, 500, "Server Error", "Out of memory");
956 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
957 }
958
959 process_cors_request(headers, &response);
960
961 /* Process form data from a POST. It could be mixed with query
962 * parameters, which seems a bit odd. But it's allowed, so that's okay
963 * with us.
964 */
965 post_vars = ast_http_get_post_vars(ser, headers);
966 if (!post_vars) {
967 ast_trace(-1, "No post_vars\n");
968 switch (errno) {
969 case EFBIG:
970 ast_ari_response_error(&response, 413,
971 "Request Entity Too Large",
972 "Request body too large");
973 goto request_failed;
974 case ENOMEM:
976 ast_ari_response_error(&response, 500,
977 "Internal Server Error",
978 "Out of memory");
979 goto request_failed;
980 case EIO:
981 ast_ari_response_error(&response, 400,
982 "Bad Request", "Error parsing request body");
983 goto request_failed;
984 }
985
986 /* Look for a JSON request entity only if there were no post_vars.
987 * If there were post_vars, then the request body would already have
988 * been consumed and can not be read again.
989 */
990 ast_trace(-1, "Checking body for vars\n");
991 body = ast_http_get_json(ser, headers);
992 if (!body) {
993 switch (errno) {
994 case EFBIG:
995 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
996 goto request_failed;
997 case ENOMEM:
998 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
999 goto request_failed;
1000 case EIO:
1001 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1002 goto request_failed;
1003 }
1004 }
1005 }
1006 if (get_params == NULL) {
1007 ast_trace(-1, "No get_params, using post_vars if any\n");
1008 get_params = post_vars;
1009 } else if (get_params && post_vars) {
1010 /* Has both post_vars and get_params */
1011 struct ast_variable *last_var = post_vars;
1012 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1013 while (last_var->next) {
1014 last_var = last_var->next;
1015 }
1016 /* The duped get_params will get freed when post_vars gets
1017 * ast_variables_destroyed.
1018 */
1019 last_var->next = ast_variables_dup(get_params);
1020 get_params = post_vars;
1021 }
1022
1023 /* At this point, get_params will contain post_vars (if any) */
1024 app_name = ast_variable_find_in_list(get_params, "app");
1025 if (!app_name) {
1026 struct ast_json *app = ast_json_object_get(body, "app");
1027
1029 }
1030 ast_trace(-1, "app_name: %s\n", app_name);
1031
1032 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1033 * and the global debug flag.
1034 */
1036 if (debug_app) {
1037 struct ast_str *buf = ast_str_create(512);
1039
1040 if (!buf || (body && !str)) {
1042 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1044 ast_free(buf);
1045 goto request_failed;
1046 }
1047
1048 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1050 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1051 for (var = headers; var; var = var->next) {
1052 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1053 }
1054 for (var = get_params; var; var = var->next) {
1055 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1056 }
1057 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1059 ast_free(buf);
1060 }
1061
1064 urih, uri, method, get_params, headers, body, &response);
1067 }
1068
1069 if (response.no_response) {
1070 /* The handler indicates no further response is necessary.
1071 * Probably because it already handled it */
1072 ast_free(response.headers);
1073 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1074 }
1075
1076request_failed:
1077
1078 /* If you explicitly want to have no content, set message to
1079 * ast_json_null().
1080 */
1081 ast_assert(response.message != NULL);
1082 ast_assert(response.response_code > 0);
1083
1084 /* response.message could be NULL, in which case the empty response_body
1085 * is correct
1086 */
1087 if (response.message && !ast_json_is_null(response.message)) {
1088 ast_str_append(&response.headers, 0,
1089 "Content-type: application/json\r\n");
1090 if (ast_json_dump_str_format(response.message, &response_body,
1091 ast_ari_json_format()) != 0) {
1092 /* Error encoding response */
1093 response.response_code = 500;
1094 response.response_text = "Internal Server Error";
1095 ast_str_set(&response_body, 0, "%s", "");
1096 ast_str_set(&response.headers, 0, "%s", "");
1097 }
1098 }
1099
1100 if (debug_app) {
1101 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1103 response.response_text, ast_str_buffer(response.headers),
1104 ast_str_buffer(response_body));
1105 }
1106
1107 ast_http_send(ser, method, response.response_code,
1108 response.response_text, response.headers, response_body,
1109 response.fd != -1 ? response.fd : 0, 0);
1110 /* ast_http_send takes ownership, so we don't have to free them */
1111 response_body = NULL;
1112
1113 ast_json_unref(response.message);
1114 if (response.fd >= 0) {
1115 close(response.fd);
1116 }
1117 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1118 response.response_text);
1119}
static const char app[]
Definition: app_adsiprog.c:56
const char * str
Definition: app_jack.c:150
@ ARI_INVOKE_SOURCE_REST
Definition: ari.h:148
ast_ari_invoke_result
Definition: ari.h:137
@ ARI_INVOKE_RESULT_ERROR_CLOSE
Definition: ari.h:140
#define var
Definition: ast_expr2f.c:605
#define ast_free(a)
Definition: astmm.h:180
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 ast_trace(level,...)
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
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
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
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
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
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
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
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
Definition: json.c:273
int errno
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
const char * app_name(struct ast_app *app)
Definition: pbx_app.c:463
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition: res_ari.c:908
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
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition: res_ari.c:855
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:528
const char * method
Definition: res_pjsip.c:1279
int stasis_app_get_debug_by_name(const char *app_name)
Get debug status of an application.
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
#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
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
Abstract JSON element (object, array, string, int, ...).
Support for dynamic strings.
Definition: strings.h:623
struct ast_sockaddr remote_address
Definition: tcptls.h:153
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
#define ast_assert(a)
Definition: utils.h:739

References app, app_name(), ARI_INVOKE_RESULT_ERROR_CLOSE, ARI_INVOKE_SOURCE_REST, ast_ari_invoke(), ast_ari_json_format(), ast_ari_response_error(), ast_assert, ast_free, ast_get_http_method(), ast_http_error(), ast_http_get_json(), ast_http_get_post_vars(), ast_http_request_close_on_completion(), ast_http_send(), ast_json_dump_str_format(), ast_json_dump_string_format(), ast_json_free(), ast_json_is_null(), ast_json_null(), ast_json_object_get(), ast_json_string_get(), ast_json_unref(), ast_sockaddr_stringify(), ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_set(), ast_trace, ast_variable_find_in_list(), ast_variables_destroy(), ast_variables_dup(), ast_verbose(), buf, errno, ast_ari_response::fd, ast_ari_response::headers, ast_ari_response::message, method, ast_variable::next, ast_ari_response::no_response, NULL, process_cors_request(), RAII_VAR, ast_tcptls_session_instance::remote_address, ast_ari_response::response_code, ast_ari_response::response_text, result, S_OR, SCOPE_CALL_WITH_RESULT, SCOPE_ENTER, SCOPE_EXIT_RTN_VALUE, stasis_app_get_debug_by_name(), str, and var.

◆ ast_ari_get_docs()

void ast_ari_get_docs ( const char *  uri,
const char *  prefix,
struct ast_variable headers,
struct ast_ari_response response 
)

Definition at line 726 of file res_ari.c.

728{
729 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
730 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
731 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
732 struct ast_json *obj = NULL;
733 struct ast_variable *host = NULL;
734 struct ast_json_error error = {};
735 struct stat file_stat;
736
737 ast_debug(3, "%s(%s)\n", __func__, uri);
738
739 absolute_path_builder = ast_str_create(80);
740 if (absolute_path_builder == NULL) {
742 return;
743 }
744
745 /* absolute path to the rest-api directory */
746 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
747 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
748 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
749 if (absolute_api_dirname == NULL) {
750 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
752 response, 500, "Internal Server Error",
753 "Cannot find rest-api directory");
754 return;
755 }
756
757 /* absolute path to the requested file */
758 ast_str_append(&absolute_path_builder, 0, "%s", uri);
759 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
760 if (absolute_filename == NULL) {
761 switch (errno) {
762 case ENAMETOOLONG:
763 case ENOENT:
764 case ENOTDIR:
766 response, 404, "Not Found",
767 "Resource not found");
768 break;
769 case EACCES:
771 response, 403, "Forbidden",
772 "Permission denied");
773 break;
774 default:
776 "Error determining real path for uri '%s': %s\n",
777 uri, strerror(errno));
779 response, 500, "Internal Server Error",
780 "Cannot find file");
781 break;
782 }
783 return;
784 }
785
786 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
787 /* HACKERZ! */
789 "Invalid attempt to access '%s' (not in %s)\n",
790 absolute_filename, absolute_api_dirname);
792 response, 404, "Not Found",
793 "Resource not found");
794 return;
795 }
796
797 if (stat(absolute_filename, &file_stat) == 0) {
798 if (!(file_stat.st_mode & S_IFREG)) {
799 /* Not a file */
801 response, 403, "Forbidden",
802 "Invalid access");
803 return;
804 }
805 } else {
806 /* Does not exist */
808 response, 404, "Not Found",
809 "Resource not found");
810 return;
811 }
812
813 /* Load resource object from file */
814 obj = ast_json_load_new_file(absolute_filename, &error);
815 if (obj == NULL) {
816 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
817 error.source, error.line, error.column, error.text);
819 response, 500, "Internal Server Error",
820 "Yikes! Cannot parse resource");
821 return;
822 }
823
824 /* Update the basePath properly */
825 if (ast_json_object_get(obj, "basePath") != NULL) {
826 for (host = headers; host; host = host->next) {
827 if (strcasecmp(host->name, "Host") == 0) {
828 break;
829 }
830 }
831 if (host != NULL) {
832 if (prefix != NULL && strlen(prefix) > 0) {
834 obj, "basePath",
835 ast_json_stringf("http://%s%s/ari", host->value,prefix));
836 } else {
838 obj, "basePath",
839 ast_json_stringf("http://%s/ari", host->value));
840 }
841 } else {
842 /* Without the host, we don't have the basePath */
843 ast_json_object_del(obj, "basePath");
844 }
845 }
846
847 ast_ari_response_ok(response, obj);
848}
void ast_std_free(void *ptr)
Definition: astmm.c:1734
#define ast_log
Definition: astobj2.c:42
static char prefix[MAX_PREFIX]
Definition: http.c:144
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
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
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
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
const char * ast_config_AST_DATA_DIR
Definition: options.c:159
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
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
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
JSON parsing error information.
Definition: json.h:887
int error(const char *format,...)
Definition: utils/frame.c:999

References ast_ari_response_alloc_failed(), ast_ari_response_error(), ast_ari_response_ok(), ast_begins_with(), ast_config_AST_DATA_DIR, ast_debug, ast_free, ast_json_load_new_file(), ast_json_object_del(), ast_json_object_get(), ast_json_object_set(), ast_json_stringf(), ast_log, ast_std_free(), ast_str_append(), ast_str_buffer(), ast_str_create, errno, error(), LOG_ERROR, ast_variable::name, ast_variable::next, NULL, prefix, RAII_VAR, and ast_variable::value.

Referenced by ast_ari_invoke(), and AST_TEST_DEFINE().

◆ ast_ari_invoke()

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 at line 528 of file res_ari.c.

533{
536 struct stasis_rest_handlers *wildcard_handler = NULL;
537 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
540
541 char *path = ast_strdupa(uri);
542 char *path_segment = NULL;
544 SCOPE_ENTER(3, "Request: %s %s, path:%s\n", ast_get_http_method(method), uri, path);
545
546
547 if (!general) {
548 if (ser && source == ARI_INVOKE_SOURCE_REST) {
550 }
551 ast_ari_response_error(response, 500, "Server Error", "URI handler config missing");
553 response->response_code, response->response_text);
554 }
555
556 user = authenticate_user(get_params, headers);
557
558 if (!user && source == ARI_INVOKE_SOURCE_REST) {
559 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
560 * message is used by an origin server to challenge the
561 * authorization of a user agent. This response MUST include a
562 * WWW-Authenticate header field containing at least one
563 * challenge applicable to the requested resource.
564 */
565 ast_ari_response_error(response, 401, "Unauthorized", "Authentication required");
566
567 /* Section 1.2:
568 * realm = "realm" "=" realm-value
569 * realm-value = quoted-string
570 * Section 2:
571 * challenge = "Basic" realm
572 */
573 ast_str_append(&response->headers, 0,
574 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
575 general->auth_realm);
577 response->response_code, response->response_text);
578 } else if (!ast_fully_booted) {
579 ast_ari_response_error(response, 503, "Service Unavailable", "Asterisk not booted");
581 response->response_code, response->response_text);
582 } else if (user && user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
583 ast_ari_response_error(response, 403, "Forbidden", "Write access denied");
585 response->response_code, response->response_text);
586 } else if (ast_ends_with(uri, "/")) {
587 remove_trailing_slash(uri, response);
589 response->response_code, response->response_text);
590 } else if (ast_begins_with(uri, "api-docs/")) {
591 /* Serving up API docs */
592 if (method != AST_HTTP_GET) {
593 ast_ari_response_error(response, 405, "Method Not Allowed", "Unsupported method");
594 } else {
595 if (urih) {
596 /* Skip the api-docs prefix */
597 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, response);
598 } else {
599 /*
600 * If we were invoked without a urih, we're probably
601 * being called from the websocket so just use the
602 * default prefix. It's filled in by ast_http_uri_link().
603 */
604 ast_ari_get_docs(strchr(uri, '/') + 1, http_uri.prefix, headers, response);
605 }
606 }
608 response->response_code, response->response_text);
609 }
610
611 root = handler = get_root_handler();
612 ast_assert(root != NULL);
613
614 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
615 struct stasis_rest_handlers *found_handler = NULL;
616 int i;
617 SCOPE_ENTER(4, "Finding handler for path segment %s\n", path_segment);
618
620
621 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
622 struct stasis_rest_handlers *child = handler->children[i];
623 SCOPE_ENTER(5, "Checking handler path segment %s\n", child->path_segment);
624
625 if (child->is_wildcard) {
626 /* Record the path variable */
627 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
628 path_var->next = path_vars;
629 path_vars = path_var;
630 wildcard_handler = child;
631 ast_trace(-1, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
632
633 } else if (strcmp(child->path_segment, path_segment) == 0) {
634 found_handler = child;
635 ast_trace(-1, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
636 } else {
637 ast_trace(-1, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
638 }
639 SCOPE_EXIT("Done checking %s\n", child->path_segment);
640 }
641
642 if (!found_handler && wildcard_handler) {
643 ast_trace(-1, " No explicit handler found for %s. Using wildcard %s.\n",
644 path_segment, wildcard_handler->path_segment);
645 found_handler = wildcard_handler;
646 wildcard_handler = NULL;
647 }
648
649 if (found_handler == NULL) {
650 /* resource not found */
652 response, 404, "Not Found",
653 "Resource not found");
654 SCOPE_EXIT_EXPR(break, "Handler not found for %s\n", path_segment);
655 } else {
656 handler = found_handler;
657 }
658 SCOPE_EXIT("Done checking %s\n", path_segment);
659 }
660
661 if (handler == NULL || response->response_code == 404) {
662 /* resource not found */
664 response->response_code, response->response_text, uri);
665 }
666
668 if (method == AST_HTTP_OPTIONS) {
669 handle_options(handler, headers, response);
671 }
672
673 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
674 add_allow_header(handler, response);
676 response, 405, "Method Not Allowed",
677 "Invalid method");
679 response->response_code, response->response_text);
680 }
681
682 if (handler->is_websocket && method == AST_HTTP_GET) {
683 if (source == ARI_INVOKE_SOURCE_WEBSOCKET) {
685 response, 400, "Bad request",
686 "Can't upgrade to websocket from a websocket");
688 response->response_code, response->response_text);
689 }
690 /* WebSocket! */
691 ast_trace(-1, "Handling websocket %s\n", uri);
693 get_params, headers);
694 /* Since the WebSocket code handles the connection, we shouldn't
695 * do anything else; setting no_response */
696 response->no_response = 1;
698 }
699
700 callback = handler->callbacks[method];
701 if (callback == NULL) {
702 add_allow_header(handler, response);
704 response, 405, "Method Not Allowed",
705 "Invalid method");
707 response->response_code, response->response_text);
708 }
709
710 ast_trace(-1, "Running callback: %s\n", uri);
711 callback(ser, get_params, path_vars, headers, body, response);
712 if (response->message == NULL && response->response_code == 0) {
713 /* Really should not happen */
714 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
717 response, 501, "Not Implemented",
718 "Method not implemented");
720 response->response_code, response->response_text);
721 }
722 SCOPE_EXIT_RTN_VALUE(ARI_INVOKE_RESULT_SUCCESS, "Response: %d : %s\n",
723 response->response_code, response->response_text);
724}
@ ARI_INVOKE_SOURCE_WEBSOCKET
Definition: ari.h:149
@ ARI_INVOKE_RESULT_SUCCESS
Definition: ari.h:138
@ 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
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.
char * strsep(char **str, const char *delims)
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
#define SCOPE_EXIT_EXPR(__expr,...)
#define SCOPE_EXIT(...)
@ AST_HTTP_GET
Definition: http.h:60
@ AST_HTTP_OPTIONS
Definition: http.h:65
#define ast_variable_new(name, value, filename)
#define ast_fully_booted
Definition: options.h:118
struct ari_conf_general * ari_conf_get_general(void)
static void remove_trailing_slash(const char *uri, struct ast_ari_response *response)
Definition: res_ari.c:502
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition: res_ari.c:268
static struct ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers)
Authenticate an HTTP request.
Definition: res_ari.c:479
static struct ast_http_uri http_uri
Definition: res_ari.c:117
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
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition: res_ari.c:726
static struct stasis_rest_handlers * get_root_handler(void)
Definition: res_ari.c:191
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
Global configuration options for ARI.
Definition: internal.h:57
Per-user configuration options.
Definition: internal.h:84
const char * prefix
Definition: http.h:106
const char * path_segment
Definition: ari.h:71
structure to hold users read from users.conf
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

References add_allow_header(), ao2_cleanup, ari_conf_get_general(), ari_handle_websocket(), ARI_INVOKE_RESULT_ERROR_CLOSE, ARI_INVOKE_RESULT_ERROR_CONTINUE, ARI_INVOKE_RESULT_SUCCESS, ARI_INVOKE_SOURCE_REST, ARI_INVOKE_SOURCE_WEBSOCKET, ast_ari_get_docs(), ast_ari_response_error(), ast_assert, ast_begins_with(), ast_ends_with(), ast_fully_booted, ast_get_http_method(), AST_HTTP_GET, AST_HTTP_MAX_METHOD, AST_HTTP_OPTIONS, ast_http_request_close_on_completion(), ast_log, ast_str_append(), ast_strdupa, ast_trace, ast_uri_decode(), ast_uri_http_legacy, ast_variable_new, ast_variables_destroy(), authenticate_user(), callback(), get_root_handler(), handle_options(), handler(), ast_ari_response::headers, http_uri, stasis_rest_handlers::is_wildcard, LOG_ERROR, ast_ari_response::message, method, ast_variable::next, ast_ari_response::no_response, NULL, stasis_rest_handlers::path_segment, ast_http_uri::prefix, RAII_VAR, remove_trailing_slash(), ast_ari_response::response_code, ast_ari_response::response_text, SCOPE_ENTER, SCOPE_EXIT, SCOPE_EXIT_EXPR, SCOPE_EXIT_RTN_VALUE, and strsep().

Referenced by ari_websocket_process_request(), ast_ari_callback(), and AST_TEST_DEFINE().

◆ ast_ari_json_format()

enum ast_json_encoding_format ast_ari_json_format ( void  )

Configured encoding format for JSON output.

Returns
JSON output encoding (compact, pretty, etc.)

Definition at line 908 of file res_ari.c.

909{
911 return general ? general->format : AST_JSON_COMPACT;
912}
@ AST_JSON_COMPACT
Definition: json.h:793

References ao2_cleanup, ari_conf_get_general(), AST_JSON_COMPACT, and RAII_VAR.

Referenced by ast_ari_callback(), and session_write().

◆ ast_ari_oom_json()

struct ast_json * ast_ari_oom_json ( void  )

The stock message to return when out of memory.

The refcount is NOT bumped on this object, so ast_json_ref() if you want to keep the reference.

Returns
JSON message specifying an out-of-memory error.

Definition at line 127 of file res_ari.c.

128{
129 return oom_json;
130}
static struct ast_json * oom_json
Definition: res_ari.c:109

References oom_json.

◆ ast_ari_remove_handler()

int ast_ari_remove_handler ( struct stasis_rest_handlers handler)

Remove a resource for REST handling.

Parameters
handlerHandler to add.
Return values
0on success.
non-zeroon failure.

Definition at line 155 of file res_ari.c.

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}
#define ast_mutex_unlock(a)
Definition: lock.h:197
#define ast_mutex_lock(a)
Definition: lock.h:196
struct stasis_rest_handlers * children[]
Definition: ari.h:95

References ao2_alloc, ao2_cleanup, ast_assert, ast_mutex_lock, ast_mutex_unlock, stasis_rest_handlers::children, handler(), NULL, stasis_rest_handlers::num_children, root_handler, and root_handler_lock.

Referenced by tear_down_invocation_test(), and unload_module().

◆ ast_ari_response_accepted()

void ast_ari_response_accepted ( struct ast_ari_response response)

Fill in a Accepted (202) ast_ari_response.

Definition at line 244 of file res_ari.c.

245{
246 response->message = ast_json_null();
247 response->response_code = 202;
248 response->response_text = "Accepted";
249}

References ast_json_null(), ast_ari_response::message, ast_ari_response::response_code, and ast_ari_response::response_text.

Referenced by ast_ari_asterisk_reload_module().

◆ ast_ari_response_alloc_failed()

void ast_ari_response_alloc_failed ( struct ast_ari_response response)

Fill in response with a 500 message for allocation failures.

Parameters
responseResponse to fill in.

Definition at line 251 of file res_ari.c.

252{
253 response->message = ast_json_ref(oom_json);
254 response->response_code = 500;
255 response->response_text = "Internal Server Error";
256}
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67

References ast_json_ref(), ast_ari_response::message, oom_json, ast_ari_response::response_code, and ast_ari_response::response_text.

Referenced by ari_bridges_play_helper(), ari_bridges_play_new(), ari_channels_handle_originate_with_id(), ast_ari_applications_subscribe_cb(), ast_ari_applications_unsubscribe_cb(), ast_ari_asterisk_add_log_cb(), ast_ari_asterisk_get_global_var(), ast_ari_asterisk_get_global_var_cb(), ast_ari_asterisk_get_info(), ast_ari_asterisk_get_info_cb(), ast_ari_asterisk_get_module(), ast_ari_asterisk_list_modules(), ast_ari_asterisk_set_global_var_cb(), ast_ari_asterisk_update_object(), ast_ari_bridges_add_channel(), ast_ari_bridges_add_channel_cb(), ast_ari_bridges_create_cb(), ast_ari_bridges_create_with_id_cb(), ast_ari_bridges_list(), ast_ari_bridges_play_cb(), ast_ari_bridges_play_with_id_cb(), ast_ari_bridges_record(), ast_ari_bridges_record_cb(), ast_ari_bridges_remove_channel_cb(), ast_ari_bridges_start_moh(), ast_ari_bridges_start_moh_cb(), ast_ari_channels_continue_in_dialplan(), ast_ari_channels_continue_in_dialplan_cb(), ast_ari_channels_create(), ast_ari_channels_dial(), ast_ari_channels_dial_cb(), ast_ari_channels_get_channel_var(), ast_ari_channels_get_channel_var_cb(), ast_ari_channels_hangup_cb(), ast_ari_channels_list(), ast_ari_channels_move_cb(), ast_ari_channels_mute_cb(), ast_ari_channels_play_cb(), ast_ari_channels_play_with_id_cb(), ast_ari_channels_record_cb(), ast_ari_channels_redirect_cb(), ast_ari_channels_send_dtmf_cb(), ast_ari_channels_set_channel_var_cb(), ast_ari_channels_snoop_channel_cb(), ast_ari_channels_snoop_channel_with_id_cb(), ast_ari_channels_start_moh_cb(), ast_ari_channels_transfer_progress_cb(), ast_ari_channels_unmute_cb(), ast_ari_device_states_update_cb(), ast_ari_endpoints_get(), ast_ari_endpoints_list(), ast_ari_endpoints_list_by_tech(), ast_ari_events_user_event_cb(), ast_ari_get_docs(), ast_ari_mailboxes_update_cb(), ast_ari_playbacks_control_cb(), ast_ari_recordings_copy_stored_cb(), ast_ari_recordings_list_stored(), ast_ari_sounds_list_cb(), control_list_create(), handle_options(), json_to_ast_variables(), return_sorcery_object(), send_message(), and send_refer().

◆ ast_ari_response_created()

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.

Parameters
responseResponse to fill in.
urlURL to the created resource.
messageJSON response. This reference is stolen, so just ast_json_ref if you need to keep a reference to it.

Definition at line 258 of file res_ari.c.

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}
static char url[512]

References ao2_cleanup, ast_str_append(), get_root_handler(), ast_ari_response::headers, ast_ari_response::message, RAII_VAR, ast_ari_response::response_code, ast_ari_response::response_text, and url.

Referenced by ari_bridges_play_found(), ari_bridges_play_new(), ari_channels_handle_play(), ast_ari_bridges_record(), and ast_ari_channels_record().

◆ ast_ari_response_error()

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.

Parameters
responseResponse to fill in.
response_codeHTTP response code.
response_textText corresponding to the HTTP response code.
message_fmtError message format string.

Definition at line 212 of file res_ari.c.

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}
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

References ast_json_pack(), ast_json_ref(), ast_json_unref(), ast_json_vstringf(), ast_ari_response::message, NULL, RAII_VAR, ast_ari_response::response_code, and ast_ari_response::response_text.

Referenced by ari_bridges_play_helper(), ari_bridges_play_new(), ari_channels_handle_originate_with_id(), ari_channels_handle_play(), ari_channels_handle_snoop_channel(), ast_ari_applications_filter(), ast_ari_applications_filter_cb(), ast_ari_applications_get(), ast_ari_applications_get_cb(), ast_ari_applications_list(), ast_ari_applications_list_cb(), ast_ari_applications_subscribe(), ast_ari_applications_subscribe_cb(), ast_ari_applications_unsubscribe(), ast_ari_applications_unsubscribe_cb(), ast_ari_asterisk_add_log(), ast_ari_asterisk_add_log_cb(), ast_ari_asterisk_delete_log(), ast_ari_asterisk_delete_log_cb(), ast_ari_asterisk_delete_object(), ast_ari_asterisk_delete_object_cb(), ast_ari_asterisk_get_global_var(), ast_ari_asterisk_get_global_var_cb(), ast_ari_asterisk_get_info_cb(), ast_ari_asterisk_get_module(), ast_ari_asterisk_get_module_cb(), ast_ari_asterisk_get_object(), ast_ari_asterisk_get_object_cb(), ast_ari_asterisk_list_log_channels(), ast_ari_asterisk_list_log_channels_cb(), ast_ari_asterisk_list_modules_cb(), ast_ari_asterisk_load_module(), ast_ari_asterisk_load_module_cb(), ast_ari_asterisk_ping_cb(), ast_ari_asterisk_reload_module(), ast_ari_asterisk_reload_module_cb(), ast_ari_asterisk_rotate_log(), ast_ari_asterisk_rotate_log_cb(), ast_ari_asterisk_set_global_var(), ast_ari_asterisk_set_global_var_cb(), ast_ari_asterisk_unload_module(), ast_ari_asterisk_unload_module_cb(), ast_ari_asterisk_update_object(), ast_ari_asterisk_update_object_cb(), ast_ari_bridges_add_channel_cb(), ast_ari_bridges_clear_video_source_cb(), ast_ari_bridges_create(), ast_ari_bridges_create_cb(), ast_ari_bridges_create_with_id(), ast_ari_bridges_create_with_id_cb(), ast_ari_bridges_destroy_cb(), ast_ari_bridges_get(), ast_ari_bridges_get_cb(), ast_ari_bridges_list_cb(), ast_ari_bridges_play_cb(), ast_ari_bridges_play_with_id_cb(), ast_ari_bridges_record(), ast_ari_bridges_record_cb(), ast_ari_bridges_remove_channel(), ast_ari_bridges_remove_channel_cb(), ast_ari_bridges_set_video_source(), ast_ari_bridges_set_video_source_cb(), ast_ari_bridges_start_moh_cb(), ast_ari_bridges_stop_moh(), ast_ari_bridges_stop_moh_cb(), ast_ari_callback(), ast_ari_channels_answer(), ast_ari_channels_answer_cb(), ast_ari_channels_continue_in_dialplan(), ast_ari_channels_continue_in_dialplan_cb(), ast_ari_channels_create(), ast_ari_channels_create_cb(), ast_ari_channels_dial(), ast_ari_channels_dial_cb(), ast_ari_channels_external_media(), ast_ari_channels_external_media_cb(), ast_ari_channels_get(), ast_ari_channels_get_cb(), ast_ari_channels_get_channel_var(), ast_ari_channels_get_channel_var_cb(), ast_ari_channels_hangup(), ast_ari_channels_hangup_cb(), ast_ari_channels_hold_cb(), ast_ari_channels_list_cb(), ast_ari_channels_move(), ast_ari_channels_move_cb(), ast_ari_channels_mute(), ast_ari_channels_mute_cb(), ast_ari_channels_originate_cb(), ast_ari_channels_originate_with_id_cb(), ast_ari_channels_play_cb(), ast_ari_channels_play_with_id_cb(), ast_ari_channels_record(), ast_ari_channels_record_cb(), ast_ari_channels_redirect(), ast_ari_channels_redirect_cb(), ast_ari_channels_ring_cb(), ast_ari_channels_ring_stop_cb(), ast_ari_channels_rtpstatistics(), ast_ari_channels_rtpstatistics_cb(), ast_ari_channels_send_dtmf(), ast_ari_channels_send_dtmf_cb(), ast_ari_channels_set_channel_var(), ast_ari_channels_set_channel_var_cb(), ast_ari_channels_snoop_channel_cb(), ast_ari_channels_snoop_channel_with_id_cb(), ast_ari_channels_start_moh_cb(), ast_ari_channels_start_silence_cb(), ast_ari_channels_stop_moh_cb(), ast_ari_channels_stop_silence_cb(), ast_ari_channels_transfer_progress(), ast_ari_channels_transfer_progress_cb(), ast_ari_channels_unhold_cb(), ast_ari_channels_unmute(), ast_ari_channels_unmute_cb(), ast_ari_device_states_delete(), ast_ari_device_states_delete_cb(), ast_ari_device_states_get(), ast_ari_device_states_get_cb(), ast_ari_device_states_list(), ast_ari_device_states_list_cb(), ast_ari_device_states_update(), ast_ari_device_states_update_cb(), ast_ari_endpoints_get(), ast_ari_endpoints_get_cb(), ast_ari_endpoints_list(), ast_ari_endpoints_list_by_tech(), ast_ari_endpoints_list_by_tech_cb(), ast_ari_endpoints_list_cb(), ast_ari_endpoints_refer_cb(), ast_ari_endpoints_refer_to_endpoint(), ast_ari_endpoints_refer_to_endpoint_cb(), ast_ari_endpoints_send_message_cb(), ast_ari_endpoints_send_message_to_endpoint(), ast_ari_endpoints_send_message_to_endpoint_cb(), ast_ari_events_user_event(), ast_ari_events_user_event_cb(), ast_ari_get_docs(), ast_ari_invoke(), ast_ari_mailboxes_delete(), ast_ari_mailboxes_delete_cb(), ast_ari_mailboxes_get(), ast_ari_mailboxes_get_cb(), ast_ari_mailboxes_list(), ast_ari_mailboxes_list_cb(), ast_ari_mailboxes_update(), ast_ari_mailboxes_update_cb(), ast_ari_playbacks_control(), ast_ari_playbacks_control_cb(), ast_ari_playbacks_get(), ast_ari_playbacks_get_cb(), ast_ari_playbacks_stop(), ast_ari_playbacks_stop_cb(), ast_ari_recordings_cancel_cb(), ast_ari_recordings_copy_stored(), ast_ari_recordings_copy_stored_cb(), ast_ari_recordings_delete_stored(), ast_ari_recordings_delete_stored_cb(), ast_ari_recordings_get_live(), ast_ari_recordings_get_live_cb(), ast_ari_recordings_get_stored(), ast_ari_recordings_get_stored_cb(), ast_ari_recordings_get_stored_file(), ast_ari_recordings_get_stored_file_cb(), ast_ari_recordings_list_stored_cb(), ast_ari_recordings_mute_cb(), ast_ari_recordings_pause_cb(), ast_ari_recordings_stop_cb(), ast_ari_recordings_unmute_cb(), ast_ari_recordings_unpause_cb(), ast_ari_sounds_get(), ast_ari_sounds_get_cb(), ast_ari_sounds_list(), ast_ari_sounds_list_cb(), channel_state_invalid(), check_add_remove_channel(), control_list_create(), control_recording(), external_media_audiosocket_tcp(), find_bridge(), find_channel_control(), find_control(), json_to_ast_variables(), remove_trailing_slash(), send_message(), and send_refer().

◆ ast_ari_response_no_content()

void ast_ari_response_no_content ( struct ast_ari_response response)

Fill in a No Content (204) ast_ari_response.

Definition at line 237 of file res_ari.c.

238{
239 response->message = ast_json_null();
240 response->response_code = 204;
241 response->response_text = "No Content";
242}

References ast_json_null(), ast_ari_response::message, ast_ari_response::response_code, and ast_ari_response::response_text.

Referenced by ast_ari_asterisk_add_log(), ast_ari_asterisk_delete_log(), ast_ari_asterisk_delete_object(), ast_ari_asterisk_load_module(), ast_ari_asterisk_reload_module(), ast_ari_asterisk_rotate_log(), ast_ari_asterisk_set_global_var(), ast_ari_asterisk_unload_module(), ast_ari_bridges_add_channel(), ast_ari_bridges_clear_video_source(), ast_ari_bridges_destroy(), ast_ari_bridges_remove_channel(), ast_ari_bridges_set_video_source(), ast_ari_bridges_start_moh(), ast_ari_bridges_stop_moh(), ast_ari_channels_answer(), ast_ari_channels_continue_in_dialplan(), ast_ari_channels_dial(), ast_ari_channels_hangup(), ast_ari_channels_hold(), ast_ari_channels_move(), ast_ari_channels_mute(), ast_ari_channels_redirect(), ast_ari_channels_ring(), ast_ari_channels_ring_stop(), ast_ari_channels_send_dtmf(), ast_ari_channels_set_channel_var(), ast_ari_channels_start_moh(), ast_ari_channels_start_silence(), ast_ari_channels_stop_moh(), ast_ari_channels_stop_silence(), ast_ari_channels_transfer_progress(), ast_ari_channels_unhold(), ast_ari_channels_unmute(), ast_ari_device_states_delete(), ast_ari_device_states_update(), ast_ari_events_user_event(), ast_ari_mailboxes_delete(), ast_ari_mailboxes_update(), ast_ari_playbacks_control(), ast_ari_playbacks_stop(), ast_ari_recordings_delete_stored(), control_recording(), and handle_options().

◆ ast_ari_response_ok()

void ast_ari_response_ok ( struct ast_ari_response response,
struct ast_json message 
)

Fill in an OK (200) ast_ari_response.

Parameters
responseResponse to fill in.
messageJSON response. This reference is stolen, so just ast_json_ref if you need to keep a reference to it.

Definition at line 229 of file res_ari.c.

231{
232 response->message = message;
233 response->response_code = 200;
234 response->response_text = "OK";
235}

References ast_ari_response::message, ast_ari_response::response_code, and ast_ari_response::response_text.

Referenced by ari_channels_handle_originate_with_id(), ari_channels_handle_snoop_channel(), ast_ari_applications_filter(), ast_ari_applications_get(), ast_ari_applications_list(), ast_ari_applications_subscribe(), ast_ari_applications_unsubscribe(), ast_ari_asterisk_get_global_var(), ast_ari_asterisk_get_info(), ast_ari_asterisk_get_module(), ast_ari_asterisk_list_log_channels(), ast_ari_asterisk_list_modules(), ast_ari_asterisk_ping(), ast_ari_bridges_create(), ast_ari_bridges_create_with_id(), ast_ari_bridges_get(), ast_ari_bridges_list(), ast_ari_channels_create(), ast_ari_channels_get(), ast_ari_channels_get_channel_var(), ast_ari_channels_list(), ast_ari_channels_rtpstatistics(), ast_ari_device_states_get(), ast_ari_device_states_list(), ast_ari_endpoints_get(), ast_ari_endpoints_list(), ast_ari_endpoints_list_by_tech(), ast_ari_get_docs(), ast_ari_mailboxes_get(), ast_ari_mailboxes_list(), ast_ari_playbacks_get(), ast_ari_recordings_copy_stored(), ast_ari_recordings_get_live(), ast_ari_recordings_get_stored(), ast_ari_recordings_get_stored_file(), ast_ari_recordings_list_stored(), ast_ari_sounds_get(), ast_ari_sounds_list(), and return_sorcery_object().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1235 of file res_ari.c.

◆ authenticate_api_key()

static struct ari_conf_user * authenticate_api_key ( const char *  api_key)
static

Authenticate a ?api_key=userid:password

Parameters
api_keyAPI key query parameter
Returns
User object for the authenticated user.
Return values
NULLif authentication failed.

Definition at line 451 of file res_ari.c.

452{
453 RAII_VAR(char *, copy, NULL, ast_free);
454 char *username;
455 char *password;
456
457 password = copy = ast_strdup(api_key);
458 if (!copy) {
459 return NULL;
460 }
461
462 username = strsep(&password, ":");
463 if (!password) {
464 ast_log(LOG_WARNING, "Invalid api_key\n");
465 return NULL;
466 }
467
468 return ari_conf_validate_user(username, password);
469}
static int copy(char *infile, char *outfile)
Utility function to copy a file.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define LOG_WARNING
struct ari_conf_user * ari_conf_validate_user(const char *username, const char *password)
Validated a user's credentials.

References ari_conf_validate_user(), ast_free, ast_log, ast_strdup, copy(), LOG_WARNING, NULL, ari_conf_user::password, RAII_VAR, and strsep().

Referenced by authenticate_user().

◆ authenticate_user()

static struct ari_conf_user * authenticate_user ( struct ast_variable get_params,
struct ast_variable headers 
)
static

Authenticate an HTTP request.

Parameters
get_paramsGET parameters of the request.
headersHTTP headers.
Returns
User object for the authenticated user.
Return values
NULLif authentication failed.

Definition at line 479 of file res_ari.c.

481{
482 RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
483 struct ast_variable *v;
484
485 /* HTTP Basic authentication */
486 http_auth = ast_http_get_auth(headers);
487 if (http_auth) {
488 return ari_conf_validate_user(http_auth->userid,
489 http_auth->password);
490 }
491
492 /* ?api_key authentication */
493 for (v = get_params; v; v = v->next) {
494 if (strcasecmp("api_key", v->name) == 0) {
495 return authenticate_api_key(v->value);
496 }
497 }
498
499 return NULL;
500}
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1611
static struct ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
Definition: res_ari.c:451
HTTP authentication information.
Definition: http.h:125

References ao2_cleanup, ari_conf_validate_user(), ast_http_get_auth(), authenticate_api_key(), ast_variable::name, ast_variable::next, NULL, RAII_VAR, and ast_variable::value.

Referenced by ast_ari_invoke().

◆ get_root_handler()

static struct stasis_rest_handlers * get_root_handler ( void  )
static

Definition at line 191 of file res_ari.c.

192{
195 return root_handler;
196}

References ao2_ref, lock, root_handler, root_handler_lock, and SCOPED_MUTEX.

Referenced by ast_ari_invoke(), and ast_ari_response_created().

◆ handle_options()

static void handle_options ( struct stasis_rest_handlers handler,
struct ast_variable headers,
struct ast_ari_response response 
)
static

Handle OPTIONS request, mainly for CORS preflight requests.

Some browsers will send this prior to non-simple methods (i.e. DELETE). See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.

Definition at line 314 of file res_ari.c.

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}
#define LOG_NOTICE
#define ACA_HEADERS
Definition: res_ari.c:306
static int origin_allowed(const char *origin)
Definition: res_ari.c:283
#define ACA_METHODS
Definition: res_ari.c:305
#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 force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
const ast_string_field value
const ast_string_field name
struct header * next

References ACA_HEADERS, ACA_METHODS, ACR_HEADERS, ACR_METHOD, add_allow_header(), ast_ari_response_alloc_failed(), ast_ari_response_no_content(), ast_free, ast_get_http_method(), AST_HTTP_MAX_METHOD, ast_log, ast_str_append(), ast_str_buffer(), ast_str_create, ast_strlen_zero(), handler(), ast_ari_response::headers, LOG_NOTICE, header::name, header::next, NULL, origin_allowed(), RAII_VAR, and header::value.

Referenced by ast_ari_invoke().

◆ is_enabled()

static int is_enabled ( void  )
static

Helper function to check if module is enabled.

Definition at line 96 of file res_ari.c.

97{
99 return general && general->enabled;
100}

References ao2_cleanup, ari_conf_get_general(), and RAII_VAR.

Referenced by ari_websocket_load_module(), ast_cel_set_config(), load_module(), reload_module(), and unload_module().

◆ load_module()

static int load_module ( void  )
static

Definition at line 1144 of file res_ari.c.

1145{
1147
1148 /* root_handler may have been built during a declined load */
1149 if (!root_handler) {
1151 }
1152 if (!root_handler) {
1154 }
1155
1156 /* oom_json may have been built during a declined load */
1157 if (!oom_json) {
1159 "{s: s}", "error", "Allocation failed");
1160 }
1161 if (!oom_json) {
1162 /* Ironic */
1163 unload_module();
1165 }
1166
1167 /*
1168 * ari_websocket_load_module() needs to know if ARI is enabled
1169 * globally so it needs the "general" config to be loaded but it
1170 * also needs to register a sorcery object observer for
1171 * "outbound_websocket" BEFORE the outbound_websocket configs are loaded.
1172 * outbound_websocket in turn needs the users to be loaded so we'll
1173 * initialize sorcery and load "general" and "user" configs first, then
1174 * load the websocket module, then load the "outbound_websocket" configs
1175 * which will fire the observers.
1176 */
1178 unload_module();
1180 }
1181
1183 unload_module();
1185 }
1186
1187 /*
1188 * Now we can load the outbound_websocket configs which will
1189 * fire the observers.
1190 */
1192
1193 if (ari_cli_register() != 0) {
1194 unload_module();
1196 }
1197
1198 if (is_enabled()) {
1199 ast_debug(3, "ARI enabled\n");
1201 } else {
1202 ast_debug(3, "ARI disabled\n");
1203 }
1204
1206}
int ari_websocket_load_module(int is_enabled)
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
@ ARI_CONF_INIT
Definition: internal.h:146
@ ARI_CONF_LOAD_OWC
Definition: internal.h:150
@ ARI_CONF_LOAD_USER
Definition: internal.h:149
@ ARI_CONF_LOAD_GENERAL
Definition: internal.h:148
#define ast_mutex_init(pmutex)
Definition: lock.h:193
@ 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
int ari_cli_register(void)
Register CLI commands for ARI.
Definition: res/ari/cli.c:699
int ari_conf_load(enum ari_conf_load_flags flags)
(Re)load the ARI configuration
static struct stasis_rest_handlers * root_handler_create(void)
Definition: res_ari.c:198
static int is_enabled(void)
Helper function to check if module is enabled.
Definition: res_ari.c:96
static int unload_module(void)
Definition: res_ari.c:1121

References ari_cli_register(), ARI_CONF_INIT, ari_conf_load(), ARI_CONF_LOAD_GENERAL, ARI_CONF_LOAD_OWC, ARI_CONF_LOAD_USER, ari_websocket_load_module(), ast_debug, ast_http_uri_link(), ast_json_pack(), AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_mutex_init, http_uri, is_enabled(), oom_json, root_handler, root_handler_create(), root_handler_lock, and unload_module().

◆ origin_allowed()

static int origin_allowed ( const char *  origin)
static

Definition at line 283 of file res_ari.c.

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}
size_t current

References ao2_cleanup, ari_conf_get_general(), ast_strdupa, current, RAII_VAR, and strsep().

Referenced by handle_options(), and process_cors_request().

◆ process_cors_request()

static void process_cors_request ( struct ast_variable headers,
struct ast_ari_response response 
)
static

Handle CORS headers for simple requests.

See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.

Definition at line 855 of file res_ari.c.

857{
858 char const *origin = NULL;
859 struct ast_variable *header;
860
861 /* Parse CORS headers */
862 for (header = headers; header != NULL; header = header->next) {
863 if (strcmp("Origin", header->name) == 0) {
864 origin = header->value;
865 }
866 }
867
868 /* CORS 6.1, #1 - "If the Origin header is not present terminate this
869 * set of steps."
870 */
871 if (origin == NULL) {
872 return;
873 }
874
875 /* CORS 6.1, #2 - "If the value of the Origin header is not a
876 * case-sensitive match for any of the values in list of origins, do not
877 * set any additional headers and terminate this set of steps.
878 *
879 * Note: Always matching is acceptable since the list of origins can be
880 * unbounded."
881 */
882 if (!origin_allowed(origin)) {
883 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
884 return;
885 }
886
887 /* CORS 6.1, #3 - "If the resource supports credentials add a single
888 * Access-Control-Allow-Origin header, with the value of the Origin
889 * header as value, and add a single Access-Control-Allow-Credentials
890 * header with the case-sensitive string "true" as value.
891 *
892 * Otherwise, add a single Access-Control-Allow-Origin header, with
893 * either the value of the Origin header or the string "*" as value."
894 */
895 ast_str_append(&response->headers, 0,
896 "Access-Control-Allow-Origin: %s\r\n", origin);
897 ast_str_append(&response->headers, 0,
898 "Access-Control-Allow-Credentials: true\r\n");
899
900 /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
901 * or more Access-Control-Expose-Headers headers, with as values the
902 * header field names given in the list of exposed headers."
903 *
904 * No exposed headers; skipping
905 */
906}

References ast_log, ast_str_append(), ast_ari_response::headers, LOG_NOTICE, header::name, header::next, NULL, origin_allowed(), and header::value.

Referenced by ast_ari_callback().

◆ reload_module()

static int reload_module ( void  )
static

Definition at line 1208 of file res_ari.c.

1209{
1210 char was_enabled = is_enabled();
1211 int is_now_enabled = 0;
1212
1214
1215 is_now_enabled = is_enabled();
1216
1217 if (was_enabled && !is_now_enabled) {
1218 ast_debug(3, "Disabling ARI\n");
1220 } else if (!was_enabled && is_now_enabled) {
1221 ast_debug(3, "Enabling ARI\n");
1223 }
1224
1226}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:721
@ ARI_CONF_LOAD_ALL
Definition: internal.h:151
@ ARI_CONF_RELOAD
Definition: internal.h:147

References ari_conf_load(), ARI_CONF_LOAD_ALL, ARI_CONF_RELOAD, ast_debug, ast_http_uri_link(), ast_http_uri_unlink(), AST_MODULE_LOAD_SUCCESS, http_uri, and is_enabled().

◆ remove_trailing_slash()

static void remove_trailing_slash ( const char *  uri,
struct ast_ari_response response 
)
static

Definition at line 502 of file res_ari.c.

504{
505 char *slashless = ast_strdupa(uri);
506 slashless[strlen(slashless) - 1] = '\0';
507
508 /* While it's tempting to redirect the client to the slashless URL,
509 * that is problematic. A 302 Found is the most appropriate response,
510 * but most clients issue a GET on the location you give them,
511 * regardless of the method of the original request.
512 *
513 * While there are some ways around this, it gets into a lot of client
514 * specific behavior and corner cases in the HTTP standard. There's also
515 * very little practical benefit of redirecting; only GET and HEAD can
516 * be redirected automagically; all other requests "MUST NOT
517 * automatically redirect the request unless it can be confirmed by the
518 * user, since this might change the conditions under which the request
519 * was issued."
520 *
521 * Given all of that, a 404 with a nice message telling them what to do
522 * is probably our best bet.
523 */
524 ast_ari_response_error(response, 404, "Not Found",
525 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
526}

References ast_ari_response_error(), and ast_strdupa.

Referenced by ast_ari_invoke().

◆ root_handler_create()

static struct stasis_rest_handlers * root_handler_create ( void  )
static

Definition at line 198 of file res_ari.c.

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}

References ao2_alloc, ao2_cleanup, ao2_ref, handler(), NULL, and RAII_VAR.

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1121 of file res_ari.c.

1122{
1124
1126
1127 if (is_enabled()) {
1128 ast_debug(3, "Disabling ARI\n");
1130 }
1131
1133
1137
1139 oom_json = NULL;
1140
1141 return 0;
1142}
int ari_websocket_unload_module(void)
#define ast_mutex_destroy(a)
Definition: lock.h:195
void ari_cli_unregister(void)
Unregister CLI commands for ARI.
Definition: res/ari/cli.c:703
void ari_conf_destroy(void)
Destroy the ARI configuration.

References ao2_cleanup, ari_cli_unregister(), ari_conf_destroy(), ari_websocket_unload_module(), ast_debug, ast_http_uri_unlink(), ast_json_unref(), ast_mutex_destroy, http_uri, is_enabled(), NULL, oom_json, root_handler, and root_handler_lock.

Referenced by load_module().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "Asterisk RESTful Interface" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload_module, .requires = "http,res_stasis,res_http_websocket,res_websocket_client", .load_pri = AST_MODPRI_APP_DEPEND, }
static

Definition at line 1235 of file res_ari.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1235 of file res_ari.c.

◆ http_uri

struct ast_http_uri http_uri
static

Definition at line 117 of file res_ari.c.

Referenced by ast_ari_invoke(), load_module(), reload_module(), and unload_module().

◆ oom_json

struct ast_json* oom_json
static

Pre-defined message for allocation failures.

Definition at line 109 of file res_ari.c.

Referenced by ast_ari_oom_json(), ast_ari_response_alloc_failed(), load_module(), and unload_module().

◆ root_handler

struct stasis_rest_handlers* root_handler
static

Handler for root RESTful resource.

Definition at line 106 of file res_ari.c.

Referenced by ast_ari_add_handler(), ast_ari_remove_handler(), get_root_handler(), load_module(), and unload_module().

◆ root_handler_lock

ast_mutex_t root_handler_lock
static