Asterisk - The Open Source Telephony Project GIT-master-8f1982c
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 ast_ari_conf_userauthenticate_api_key (const char *api_key)
 Authenticate a ?api_key=userid:password More...
 
static struct ast_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", .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 413 of file res_ari.c.

◆ ACA_METHODS

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

Definition at line 412 of file res_ari.c.

◆ ACR_HEADERS

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

Definition at line 411 of file res_ari.c.

◆ ACR_METHOD

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

Definition at line 410 of file res_ari.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1326 of file res_ari.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1326 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 375 of file res_ari.c.

377{
378 enum ast_http_method m;
379 ast_str_append(&response->headers, 0,
380 "Allow: OPTIONS");
381 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
382 if (handler->callbacks[m] != NULL) {
383 ast_str_append(&response->headers, 0,
384 ",%s", ast_get_http_method(m));
385 }
386 }
387 ast_str_append(&response->headers, 0, "\r\n");
388}
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 239 of file res_ari.c.

240{
241 RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
242 size_t old_size, new_size;
243
245
246 old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
247 new_size = old_size + sizeof(handler);
248
249 new_handler = ao2_alloc(new_size, NULL);
250 if (!new_handler) {
251 return -1;
252 }
253 memcpy(new_handler, root_handler, old_size);
254 new_handler->children[new_handler->num_children++] = handler;
255
257 ao2_ref(new_handler, +1);
258 root_handler = new_handler;
259 return 0;
260}
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:213
static ast_mutex_t root_handler_lock
Definition: res_ari.c:210
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 1036 of file res_ari.c.

1042{
1043 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
1044 struct ast_ari_response response = { .fd = -1, 0 };
1045 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
1046 struct ast_variable *var;
1047 const char *app_name = NULL;
1048 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
1049 int debug_app = 0;
1051 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
1053
1054 if (!response_body) {
1056 ast_http_error(ser, 500, "Server Error", "Out of memory");
1057 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1058 }
1059
1060 response.headers = ast_str_create(40);
1061 if (!response.headers) {
1063 ast_http_error(ser, 500, "Server Error", "Out of memory");
1064 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1065 }
1066
1067 process_cors_request(headers, &response);
1068
1069 /* Process form data from a POST. It could be mixed with query
1070 * parameters, which seems a bit odd. But it's allowed, so that's okay
1071 * with us.
1072 */
1073 post_vars = ast_http_get_post_vars(ser, headers);
1074 if (!post_vars) {
1075 ast_trace(-1, "No post_vars\n");
1076 switch (errno) {
1077 case EFBIG:
1078 ast_ari_response_error(&response, 413,
1079 "Request Entity Too Large",
1080 "Request body too large");
1081 goto request_failed;
1082 case ENOMEM:
1084 ast_ari_response_error(&response, 500,
1085 "Internal Server Error",
1086 "Out of memory");
1087 goto request_failed;
1088 case EIO:
1089 ast_ari_response_error(&response, 400,
1090 "Bad Request", "Error parsing request body");
1091 goto request_failed;
1092 }
1093
1094 /* Look for a JSON request entity only if there were no post_vars.
1095 * If there were post_vars, then the request body would already have
1096 * been consumed and can not be read again.
1097 */
1098 ast_trace(-1, "Checking body for vars\n");
1099 body = ast_http_get_json(ser, headers);
1100 if (!body) {
1101 switch (errno) {
1102 case EFBIG:
1103 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
1104 goto request_failed;
1105 case ENOMEM:
1106 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
1107 goto request_failed;
1108 case EIO:
1109 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1110 goto request_failed;
1111 }
1112 }
1113 }
1114 if (get_params == NULL) {
1115 ast_trace(-1, "No get_params, using post_vars if any\n");
1116 get_params = post_vars;
1117 } else if (get_params && post_vars) {
1118 /* Has both post_vars and get_params */
1119 struct ast_variable *last_var = post_vars;
1120 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1121 while (last_var->next) {
1122 last_var = last_var->next;
1123 }
1124 /* The duped get_params will get freed when post_vars gets
1125 * ast_variables_destroyed.
1126 */
1127 last_var->next = ast_variables_dup(get_params);
1128 get_params = post_vars;
1129 }
1130
1131 /* At this point, get_params will contain post_vars (if any) */
1132 app_name = ast_variable_find_in_list(get_params, "app");
1133 if (!app_name) {
1134 struct ast_json *app = ast_json_object_get(body, "app");
1135
1137 }
1138 ast_trace(-1, "app_name: %s\n", app_name);
1139
1140 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1141 * and the global debug flag.
1142 */
1144 if (debug_app) {
1145 struct ast_str *buf = ast_str_create(512);
1147
1148 if (!buf || (body && !str)) {
1150 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1152 ast_free(buf);
1153 goto request_failed;
1154 }
1155
1156 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1158 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1159 for (var = headers; var; var = var->next) {
1160 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1161 }
1162 for (var = get_params; var; var = var->next) {
1163 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1164 }
1165 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1167 ast_free(buf);
1168 }
1169
1172 urih, uri, method, get_params, headers, body, &response);
1175 }
1176
1177 if (response.no_response) {
1178 /* The handler indicates no further response is necessary.
1179 * Probably because it already handled it */
1180 ast_free(response.headers);
1181 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1182 }
1183
1184request_failed:
1185
1186 /* If you explicitly want to have no content, set message to
1187 * ast_json_null().
1188 */
1189 ast_assert(response.message != NULL);
1190 ast_assert(response.response_code > 0);
1191
1192 /* response.message could be NULL, in which case the empty response_body
1193 * is correct
1194 */
1195 if (response.message && !ast_json_is_null(response.message)) {
1196 ast_str_append(&response.headers, 0,
1197 "Content-type: application/json\r\n");
1198 if (ast_json_dump_str_format(response.message, &response_body,
1199 ast_ari_json_format()) != 0) {
1200 /* Error encoding response */
1201 response.response_code = 500;
1202 response.response_text = "Internal Server Error";
1203 ast_str_set(&response_body, 0, "%s", "");
1204 ast_str_set(&response.headers, 0, "%s", "");
1205 }
1206 }
1207
1208 if (debug_app) {
1209 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1211 response.response_text, ast_str_buffer(response.headers),
1212 ast_str_buffer(response_body));
1213 }
1214
1215 ast_http_send(ser, method, response.response_code,
1216 response.response_text, response.headers, response_body,
1217 response.fd != -1 ? response.fd : 0, 0);
1218 /* ast_http_send takes ownership, so we don't have to free them */
1219 response_body = NULL;
1220
1221 ast_json_unref(response.message);
1222 if (response.fd >= 0) {
1223 close(response.fd);
1224 }
1225 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1226 response.response_text);
1227}
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:1015
void ast_ari_response_error(struct ast_ari_response *response, int response_code, const char *response_text, const char *message_fmt,...)
Fill in an error ast_ari_response.
Definition: res_ari.c:319
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition: res_ari.c:962
enum ast_ari_invoke_result ast_ari_invoke(struct ast_tcptls_session_instance *ser, enum ast_ari_invoke_source source, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition: res_ari.c:635
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 833 of file res_ari.c.

835{
836 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
837 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
838 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
839 struct ast_json *obj = NULL;
840 struct ast_variable *host = NULL;
841 struct ast_json_error error = {};
842 struct stat file_stat;
843
844 ast_debug(3, "%s(%s)\n", __func__, uri);
845
846 absolute_path_builder = ast_str_create(80);
847 if (absolute_path_builder == NULL) {
849 return;
850 }
851
852 /* absolute path to the rest-api directory */
853 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
854 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
855 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
856 if (absolute_api_dirname == NULL) {
857 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
859 response, 500, "Internal Server Error",
860 "Cannot find rest-api directory");
861 return;
862 }
863
864 /* absolute path to the requested file */
865 ast_str_append(&absolute_path_builder, 0, "%s", uri);
866 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
867 if (absolute_filename == NULL) {
868 switch (errno) {
869 case ENAMETOOLONG:
870 case ENOENT:
871 case ENOTDIR:
873 response, 404, "Not Found",
874 "Resource not found");
875 break;
876 case EACCES:
878 response, 403, "Forbidden",
879 "Permission denied");
880 break;
881 default:
883 "Error determining real path for uri '%s': %s\n",
884 uri, strerror(errno));
886 response, 500, "Internal Server Error",
887 "Cannot find file");
888 break;
889 }
890 return;
891 }
892
893 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
894 /* HACKERZ! */
896 "Invalid attempt to access '%s' (not in %s)\n",
897 absolute_filename, absolute_api_dirname);
899 response, 404, "Not Found",
900 "Resource not found");
901 return;
902 }
903
904 if (stat(absolute_filename, &file_stat) == 0) {
905 if (!(file_stat.st_mode & S_IFREG)) {
906 /* Not a file */
908 response, 403, "Forbidden",
909 "Invalid access");
910 return;
911 }
912 } else {
913 /* Does not exist */
915 response, 404, "Not Found",
916 "Resource not found");
917 return;
918 }
919
920 /* Load resource object from file */
921 obj = ast_json_load_new_file(absolute_filename, &error);
922 if (obj == NULL) {
923 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
924 error.source, error.line, error.column, error.text);
926 response, 500, "Internal Server Error",
927 "Yikes! Cannot parse resource");
928 return;
929 }
930
931 /* Update the basePath properly */
932 if (ast_json_object_get(obj, "basePath") != NULL) {
933 for (host = headers; host; host = host->next) {
934 if (strcasecmp(host->name, "Host") == 0) {
935 break;
936 }
937 }
938 if (host != NULL) {
939 if (prefix != NULL && strlen(prefix) > 0) {
941 obj, "basePath",
942 ast_json_stringf("http://%s%s/ari", host->value,prefix));
943 } else {
945 obj, "basePath",
946 ast_json_stringf("http://%s/ari", host->value));
947 }
948 } else {
949 /* Without the host, we don't have the basePath */
950 ast_json_object_del(obj, "basePath");
951 }
952 }
953
954 ast_ari_response_ok(response, obj);
955}
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:336
void ast_ari_response_alloc_failed(struct ast_ari_response *response)
Fill in response with a 500 message for allocation failures.
Definition: res_ari.c:358
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 635 of file res_ari.c.

640{
643 struct stasis_rest_handlers *wildcard_handler = NULL;
644 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
647
648 char *path = ast_strdupa(uri);
649 char *path_segment = NULL;
651 SCOPE_ENTER(3, "Request: %s %s, path:%s\n", ast_get_http_method(method), uri, path);
652
653
654 if (!conf || !conf->general) {
655 if (ser && source == ARI_INVOKE_SOURCE_REST) {
657 }
658 ast_ari_response_error(response, 500, "Server Error", "URI handler config missing");
660 response->response_code, response->response_text);
661 }
662
663 user = authenticate_user(get_params, headers);
664
665 if (!user && source == ARI_INVOKE_SOURCE_REST) {
666 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
667 * message is used by an origin server to challenge the
668 * authorization of a user agent. This response MUST include a
669 * WWW-Authenticate header field containing at least one
670 * challenge applicable to the requested resource.
671 */
672 ast_ari_response_error(response, 401, "Unauthorized", "Authentication required");
673
674 /* Section 1.2:
675 * realm = "realm" "=" realm-value
676 * realm-value = quoted-string
677 * Section 2:
678 * challenge = "Basic" realm
679 */
680 ast_str_append(&response->headers, 0,
681 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
682 conf->general->auth_realm);
684 response->response_code, response->response_text);
685 } else if (!ast_fully_booted) {
686 ast_ari_response_error(response, 503, "Service Unavailable", "Asterisk not booted");
688 response->response_code, response->response_text);
689 } else if (user && user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
690 ast_ari_response_error(response, 403, "Forbidden", "Write access denied");
692 response->response_code, response->response_text);
693 } else if (ast_ends_with(uri, "/")) {
694 remove_trailing_slash(uri, response);
696 response->response_code, response->response_text);
697 } else if (ast_begins_with(uri, "api-docs/")) {
698 /* Serving up API docs */
699 if (method != AST_HTTP_GET) {
700 ast_ari_response_error(response, 405, "Method Not Allowed", "Unsupported method");
701 } else {
702 if (urih) {
703 /* Skip the api-docs prefix */
704 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, response);
705 } else {
706 /*
707 * If we were invoked without a urih, we're probably
708 * being called from the websocket so just use the
709 * default prefix. It's filled in by ast_http_uri_link().
710 */
711 ast_ari_get_docs(strchr(uri, '/') + 1, http_uri.prefix, headers, response);
712 }
713 }
715 response->response_code, response->response_text);
716 }
717
718 root = handler = get_root_handler();
719 ast_assert(root != NULL);
720
721 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
722 struct stasis_rest_handlers *found_handler = NULL;
723 int i;
724 SCOPE_ENTER(4, "Finding handler for path segment %s\n", path_segment);
725
727
728 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
729 struct stasis_rest_handlers *child = handler->children[i];
730 SCOPE_ENTER(5, "Checking handler path segment %s\n", child->path_segment);
731
732 if (child->is_wildcard) {
733 /* Record the path variable */
734 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
735 path_var->next = path_vars;
736 path_vars = path_var;
737 wildcard_handler = child;
738 ast_trace(-1, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
739
740 } else if (strcmp(child->path_segment, path_segment) == 0) {
741 found_handler = child;
742 ast_trace(-1, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
743 } else {
744 ast_trace(-1, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
745 }
746 SCOPE_EXIT("Done checking %s\n", child->path_segment);
747 }
748
749 if (!found_handler && wildcard_handler) {
750 ast_trace(-1, " No explicit handler found for %s. Using wildcard %s.\n",
751 path_segment, wildcard_handler->path_segment);
752 found_handler = wildcard_handler;
753 wildcard_handler = NULL;
754 }
755
756 if (found_handler == NULL) {
757 /* resource not found */
759 response, 404, "Not Found",
760 "Resource not found");
761 SCOPE_EXIT_EXPR(break, "Handler not found for %s\n", path_segment);
762 } else {
763 handler = found_handler;
764 }
765 SCOPE_EXIT("Done checking %s\n", path_segment);
766 }
767
768 if (handler == NULL || response->response_code == 404) {
769 /* resource not found */
771 response->response_code, response->response_text, uri);
772 }
773
775 if (method == AST_HTTP_OPTIONS) {
776 handle_options(handler, headers, response);
778 }
779
780 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
781 add_allow_header(handler, response);
783 response, 405, "Method Not Allowed",
784 "Invalid method");
786 response->response_code, response->response_text);
787 }
788
789 if (handler->is_websocket && method == AST_HTTP_GET) {
790 if (source == ARI_INVOKE_SOURCE_WEBSOCKET) {
792 response, 400, "Bad request",
793 "Can't upgrade to websocket from a websocket");
795 response->response_code, response->response_text);
796 }
797 /* WebSocket! */
798 ast_trace(-1, "Handling websocket %s\n", uri);
800 get_params, headers);
801 /* Since the WebSocket code handles the connection, we shouldn't
802 * do anything else; setting no_response */
803 response->no_response = 1;
805 }
806
807 callback = handler->callbacks[method];
808 if (callback == NULL) {
809 add_allow_header(handler, response);
811 response, 405, "Method Not Allowed",
812 "Invalid method");
814 response->response_code, response->response_text);
815 }
816
817 ast_trace(-1, "Running callback: %s\n", uri);
818 callback(ser, get_params, path_vars, headers, body, response);
819 if (response->message == NULL && response->response_code == 0) {
820 /* Really should not happen */
821 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
824 response, 501, "Not Implemented",
825 "Method not implemented");
827 response->response_code, response->response_text);
828 }
829 SCOPE_EXIT_RTN_VALUE(ARI_INVOKE_RESULT_SUCCESS, "Response: %d : %s\n",
830 response->response_code, response->response_text);
831}
@ 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 ast_ari_conf * ast_ari_config_get(void)
Get the current ARI configuration.
static void remove_trailing_slash(const char *uri, struct ast_ari_response *response)
Definition: res_ari.c:609
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition: res_ari.c:375
static struct ast_http_uri http_uri
Definition: res_ari.c:224
static void handle_options(struct stasis_rest_handlers *handler, struct ast_variable *headers, struct ast_ari_response *response)
Handle OPTIONS request, mainly for CORS preflight requests.
Definition: res_ari.c:421
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition: res_ari.c:833
static struct ast_ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers)
Authenticate an HTTP request.
Definition: res_ari.c:586
static struct stasis_rest_handlers * get_root_handler(void)
Definition: res_ari.c:298
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
Per-user configuration options.
Definition: internal.h:96
All configuration options for ARI.
Definition: internal.h:54
const char * prefix
Definition: http.h:106
All configuration options for http media cache.
struct conf_general_options * general
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_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_config_get(), 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(), conf::general, 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 1015 of file res_ari.c.

1016{
1017 RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
1018 cfg = ast_ari_config_get();
1019 return cfg->general->format;
1020}

References ao2_cleanup, ast_ari_config_get(), NULL, and RAII_VAR.

Referenced by ari_ws_session_write(), and ast_ari_callback().

◆ 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 234 of file res_ari.c.

235{
236 return oom_json;
237}
static struct ast_json * oom_json
Definition: res_ari.c:216

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 262 of file res_ari.c.

263{
264 struct stasis_rest_handlers *new_handler;
265 size_t size;
266 size_t i;
267 size_t j;
268
270
272 size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
273
274 new_handler = ao2_alloc(size, NULL);
275 if (!new_handler) {
277 return -1;
278 }
279
280 /* Create replacement root_handler less the handler to remove. */
281 memcpy(new_handler, root_handler, sizeof(*new_handler));
282 for (i = 0, j = 0; i < root_handler->num_children; ++i) {
283 if (root_handler->children[i] == handler) {
284 continue;
285 }
286 new_handler->children[j++] = root_handler->children[i];
287 }
288 new_handler->num_children = j;
289
290 /* Replace the old root_handler with the new. */
292 root_handler = new_handler;
293
295 return 0;
296}
#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 351 of file res_ari.c.

352{
353 response->message = ast_json_null();
354 response->response_code = 202;
355 response->response_text = "Accepted";
356}

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 358 of file res_ari.c.

359{
360 response->message = ast_json_ref(oom_json);
361 response->response_code = 500;
362 response->response_text = "Internal Server Error";
363}
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(), parse_app_args(), 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 365 of file res_ari.c.

367{
369 response->message = message;
370 response->response_code = 201;
371 response->response_text = "Created";
372 ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
373}
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 319 of file res_ari.c.

323{
325 va_list ap;
326
327 va_start(ap, message_fmt);
328 message = ast_json_vstringf(message_fmt, ap);
329 va_end(ap);
330 response->message = ast_json_pack("{s: o}",
331 "message", ast_json_ref(message));
332 response->response_code = response_code;
333 response->response_text = response_text;
334}
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(), parse_app_args(), 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 344 of file res_ari.c.

345{
346 response->message = ast_json_null();
347 response->response_code = 204;
348 response->response_text = "No Content";
349}

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 336 of file res_ari.c.

338{
339 response->message = message;
340 response->response_code = 200;
341 response->response_text = "OK";
342}

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 1326 of file res_ari.c.

◆ authenticate_api_key()

static struct ast_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 558 of file res_ari.c.

559{
560 RAII_VAR(char *, copy, NULL, ast_free);
561 char *username;
562 char *password;
563
564 password = copy = ast_strdup(api_key);
565 if (!copy) {
566 return NULL;
567 }
568
569 username = strsep(&password, ":");
570 if (!password) {
571 ast_log(LOG_WARNING, "Invalid api_key\n");
572 return NULL;
573 }
574
575 return ast_ari_config_validate_user(username, password);
576}
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 ast_ari_conf_user * ast_ari_config_validate_user(const char *username, const char *password)
Validated a user's credentials.

References ast_ari_config_validate_user(), ast_free, ast_log, ast_strdup, copy(), LOG_WARNING, NULL, ast_ari_conf_user::password, RAII_VAR, strsep(), and ast_ari_conf_user::username.

Referenced by authenticate_user().

◆ authenticate_user()

static struct ast_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 586 of file res_ari.c.

588{
589 RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
590 struct ast_variable *v;
591
592 /* HTTP Basic authentication */
593 http_auth = ast_http_get_auth(headers);
594 if (http_auth) {
595 return ast_ari_config_validate_user(http_auth->userid,
596 http_auth->password);
597 }
598
599 /* ?api_key authentication */
600 for (v = get_params; v; v = v->next) {
601 if (strcasecmp("api_key", v->name) == 0) {
602 return authenticate_api_key(v->value);
603 }
604 }
605
606 return NULL;
607}
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1611
static struct ast_ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
Definition: res_ari.c:558
HTTP authentication information.
Definition: http.h:125

References ao2_cleanup, ast_ari_config_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 298 of file res_ari.c.

299{
302 return root_handler;
303}

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 421 of file res_ari.c.

424{
425 struct ast_variable *header;
426 char const *acr_method = NULL;
427 char const *acr_headers = NULL;
428 char const *origin = NULL;
429
430 RAII_VAR(struct ast_str *, allow, NULL, ast_free);
431 enum ast_http_method m;
432 int allowed = 0;
433
434 /* Regular OPTIONS response */
435 add_allow_header(handler, response);
437
438 /* Parse CORS headers */
439 for (header = headers; header != NULL; header = header->next) {
440 if (strcmp(ACR_METHOD, header->name) == 0) {
441 acr_method = header->value;
442 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
443 acr_headers = header->value;
444 } else if (strcmp("Origin", header->name) == 0) {
445 origin = header->value;
446 }
447 }
448
449 /* CORS 6.2, #1 - "If the Origin header is not present terminate this
450 * set of steps."
451 */
452 if (origin == NULL) {
453 return;
454 }
455
456 /* CORS 6.2, #2 - "If the value of the Origin header is not a
457 * case-sensitive match for any of the values in list of origins do not
458 * set any additional headers and terminate this set of steps.
459 *
460 * Always matching is acceptable since the list of origins can be
461 * unbounded.
462 *
463 * The Origin header can only contain a single origin as the user agent
464 * will not follow redirects."
465 */
466 if (!origin_allowed(origin)) {
467 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
468 return;
469 }
470
471 /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
472 * or if parsing failed, do not set any additional headers and terminate
473 * this set of steps."
474 */
475 if (acr_method == NULL) {
476 return;
477 }
478
479 /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
480 * headers let header field-names be the empty list."
481 */
482 if (acr_headers == NULL) {
483 acr_headers = "";
484 }
485
486 /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
487 * the values in list of methods do not set any additional headers and
488 * terminate this set of steps."
489 */
490 allow = ast_str_create(20);
491
492 if (!allow) {
494 return;
495 }
496
497 /* Go ahead and build the ACA_METHODS header at the same time */
498 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
499 if (handler->callbacks[m] != NULL) {
500 char const *m_str = ast_get_http_method(m);
501 if (strcmp(m_str, acr_method) == 0) {
502 allowed = 1;
503 }
504 ast_str_append(&allow, 0, ",%s", m_str);
505 }
506 }
507
508 if (!allowed) {
509 return;
510 }
511
512 /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
513 * case-insensitive match for any of the values in list of headers do
514 * not set any additional headers and terminate this set of steps.
515 *
516 * Note: Always matching is acceptable since the list of headers can be
517 * unbounded."
518 */
519
520 /* CORS 6.2 #7 - "If the resource supports credentials add a single
521 * Access-Control-Allow-Origin header, with the value of the Origin
522 * header as value, and add a single Access-Control-Allow-Credentials
523 * header with the case-sensitive string "true" as value."
524 *
525 * Added by process_cors_request() earlier in the request.
526 */
527
528 /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
529 * header..."
530 */
531
532 /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
533 * consisting of (a subset of) the list of methods."
534 */
535 ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
537
538
539 /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
540 * consisting of (a subset of) the list of headers.
541 *
542 * Since the list of headers can be unbounded simply returning headers
543 * can be enough."
544 */
545 if (!ast_strlen_zero(acr_headers)) {
546 ast_str_append(&response->headers, 0, "%s: %s\r\n",
547 ACA_HEADERS, acr_headers);
548 }
549}
#define LOG_NOTICE
#define ACA_HEADERS
Definition: res_ari.c:413
static int origin_allowed(const char *origin)
Definition: res_ari.c:390
#define ACA_METHODS
Definition: res_ari.c:412
#define ACR_HEADERS
Definition: res_ari.c:411
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition: res_ari.c:344
#define ACR_METHOD
Definition: res_ari.c:410
static 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 203 of file res_ari.c.

204{
206 return cfg && cfg->general && cfg->general->enabled;
207}

References ao2_cleanup, ast_ari_config_get(), and RAII_VAR.

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

◆ load_module()

static int load_module ( void  )
static

Definition at line 1252 of file res_ari.c.

1253{
1255
1256 /* root_handler may have been built during a declined load */
1257 if (!root_handler) {
1259 }
1260 if (!root_handler) {
1262 }
1263
1264 /* oom_json may have been built during a declined load */
1265 if (!oom_json) {
1267 "{s: s}", "error", "Allocation failed");
1268 }
1269 if (!oom_json) {
1270 /* Ironic */
1271 unload_module();
1273 }
1274
1275 if (ast_ari_config_init() != 0) {
1276 unload_module();
1278 }
1279
1281 unload_module();
1283 }
1284
1285 if (is_enabled()) {
1286 ast_debug(3, "ARI enabled\n");
1288 } else {
1289 ast_debug(3, "ARI disabled\n");
1290 }
1291
1292 if (ast_ari_cli_register() != 0) {
1293 unload_module();
1295 }
1296
1298}
int ari_websocket_load_module(void)
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
#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 ast_ari_cli_register(void)
Register CLI commands for ARI.
Definition: res/ari/cli.c:431
int ast_ari_config_init(void)
Initialize the ARI configuration.
static struct stasis_rest_handlers * root_handler_create(void)
Definition: res_ari.c:305
static int is_enabled(void)
Helper function to check if module is enabled.
Definition: res_ari.c:203
static int unload_module(void)
Definition: res_ari.c:1229

References ari_websocket_load_module(), ast_ari_cli_register(), ast_ari_config_init(), 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 390 of file res_ari.c.

391{
393
394 char *allowed = ast_strdupa(cfg->general->allowed_origins);
395 char *current;
396
397 while ((current = strsep(&allowed, ","))) {
398 if (!strcmp(current, "*")) {
399 return 1;
400 }
401
402 if (!strcmp(current, origin)) {
403 return 1;
404 }
405 }
406
407 return 0;
408}
size_t current

References ao2_cleanup, ast_ari_config_get(), 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 962 of file res_ari.c.

964{
965 char const *origin = NULL;
966 struct ast_variable *header;
967
968 /* Parse CORS headers */
969 for (header = headers; header != NULL; header = header->next) {
970 if (strcmp("Origin", header->name) == 0) {
971 origin = header->value;
972 }
973 }
974
975 /* CORS 6.1, #1 - "If the Origin header is not present terminate this
976 * set of steps."
977 */
978 if (origin == NULL) {
979 return;
980 }
981
982 /* CORS 6.1, #2 - "If the value of the Origin header is not a
983 * case-sensitive match for any of the values in list of origins, do not
984 * set any additional headers and terminate this set of steps.
985 *
986 * Note: Always matching is acceptable since the list of origins can be
987 * unbounded."
988 */
989 if (!origin_allowed(origin)) {
990 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
991 return;
992 }
993
994 /* CORS 6.1, #3 - "If the resource supports credentials add a single
995 * Access-Control-Allow-Origin header, with the value of the Origin
996 * header as value, and add a single Access-Control-Allow-Credentials
997 * header with the case-sensitive string "true" as value.
998 *
999 * Otherwise, add a single Access-Control-Allow-Origin header, with
1000 * either the value of the Origin header or the string "*" as value."
1001 */
1002 ast_str_append(&response->headers, 0,
1003 "Access-Control-Allow-Origin: %s\r\n", origin);
1004 ast_str_append(&response->headers, 0,
1005 "Access-Control-Allow-Credentials: true\r\n");
1006
1007 /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
1008 * or more Access-Control-Expose-Headers headers, with as values the
1009 * header field names given in the list of exposed headers."
1010 *
1011 * No exposed headers; skipping
1012 */
1013}

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 1300 of file res_ari.c.

1301{
1302 char was_enabled = is_enabled();
1303
1304 if (ast_ari_config_reload() != 0) {
1306 }
1307
1308 if (was_enabled && !is_enabled()) {
1309 ast_debug(3, "Disabling ARI\n");
1311 } else if (!was_enabled && is_enabled()) {
1312 ast_debug(3, "Enabling ARI\n");
1314 }
1315
1317}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:721
int ast_ari_config_reload(void)
Reload the ARI configuration.

References ast_ari_config_reload(), ast_debug, ast_http_uri_link(), ast_http_uri_unlink(), AST_MODULE_LOAD_DECLINE, 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 609 of file res_ari.c.

611{
612 char *slashless = ast_strdupa(uri);
613 slashless[strlen(slashless) - 1] = '\0';
614
615 /* While it's tempting to redirect the client to the slashless URL,
616 * that is problematic. A 302 Found is the most appropriate response,
617 * but most clients issue a GET on the location you give them,
618 * regardless of the method of the original request.
619 *
620 * While there are some ways around this, it gets into a lot of client
621 * specific behavior and corner cases in the HTTP standard. There's also
622 * very little practical benefit of redirecting; only GET and HEAD can
623 * be redirected automagically; all other requests "MUST NOT
624 * automatically redirect the request unless it can be confirmed by the
625 * user, since this might change the conditions under which the request
626 * was issued."
627 *
628 * Given all of that, a 404 with a nice message telling them what to do
629 * is probably our best bet.
630 */
631 ast_ari_response_error(response, 404, "Not Found",
632 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
633}

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 305 of file res_ari.c.

306{
308
309 handler = ao2_alloc(sizeof(*handler), NULL);
310 if (!handler) {
311 return NULL;
312 }
313 handler->path_segment = "ari";
314
315 ao2_ref(handler, +1);
316 return handler;
317}

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 1229 of file res_ari.c.

1230{
1232
1234
1235 if (is_enabled()) {
1236 ast_debug(3, "Disabling ARI\n");
1238 }
1239
1241
1245
1247 oom_json = NULL;
1248
1249 return 0;
1250}
int ari_websocket_unload_module(void)
#define ast_mutex_destroy(a)
Definition: lock.h:195
void ast_ari_cli_unregister(void)
Unregister CLI commands for ARI.
Definition: res/ari/cli.c:435
void ast_ari_config_destroy(void)
Destroy the ARI configuration.

References ao2_cleanup, ari_websocket_unload_module(), ast_ari_cli_unregister(), ast_ari_config_destroy(), 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", .load_pri = AST_MODPRI_APP_DEPEND, }
static

Definition at line 1326 of file res_ari.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1326 of file res_ari.c.

◆ http_uri

struct ast_http_uri http_uri
static

Definition at line 224 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 216 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 213 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