Asterisk - The Open Source Telephony Project GIT-master-5467495
Loading...
Searching...
No Matches
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.
 
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.
 
struct ast_jsonast_ari_oom_json (void)
 The stock message to return when out of memory.
 
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.
 
void ast_ari_response_alloc_failed (struct ast_ari_response *response)
 Fill in response with a 500 message for allocation failures.
 
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.
 
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.
 
void ast_ari_response_no_content (struct ast_ari_response *response)
 Fill in a No Content (204) ast_ari_response.
 
void ast_ari_response_ok (struct ast_ari_response *response, struct ast_json *message)
 Fill in an OK (200) ast_ari_response.
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static struct ari_conf_userauthenticate_user (struct ast_variable *get_params, struct ast_variable *headers, enum ast_ari_invoke_source source)
 Authenticate an HTTP request.
 
static struct ast_http_authcreate_http_auth (const char *userid, const char *password)
 
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.
 
static int is_enabled (void)
 Helper function to check if module is enabled.
 
static int load_module (void)
 
static int origin_allowed (const char *origin)
 
static struct ast_http_authparse_api_keys (struct ast_variable *get_params)
 Parse an api_key parameter from the query parameters into an ast_http_auth structure.
 
static void process_cors_request (struct ast_variable *headers, struct ast_ari_response *response)
 Handle CORS headers for simple requests.
 
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 = ASTERISK_GPL_KEY , .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", .optional_modules = "res_ari_model", .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 1328 of file res_ari.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1328 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:207
#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:981

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(), load_module(), load_module(), load_module(), load_module(), load_module(), load_module(), load_module(), load_module(), load_module(), 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 1020 of file res_ari.c.

1026{
1027 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
1028 struct ast_ari_response response = { .fd = -1, 0 };
1029 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
1030 struct ast_variable *var;
1031 const char *app_name = NULL;
1032 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
1033 int debug_app = 0;
1035 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
1037
1038 if (!response_body) {
1040 ast_http_error(ser, 500, "Server Error", "Out of memory");
1041 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1042 }
1043
1044 response.headers = ast_str_create(40);
1045 if (!response.headers) {
1047 ast_http_error(ser, 500, "Server Error", "Out of memory");
1048 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
1049 }
1050
1051 process_cors_request(headers, &response);
1052
1053 /* Process form data from a POST. It could be mixed with query
1054 * parameters, which seems a bit odd. But it's allowed, so that's okay
1055 * with us.
1056 */
1057 post_vars = ast_http_get_post_vars(ser, headers);
1058 if (!post_vars) {
1059 ast_trace(-1, "No post_vars\n");
1060 switch (errno) {
1061 case EFBIG:
1062 ast_ari_response_error(&response, 413,
1063 "Request Entity Too Large",
1064 "Request body too large");
1065 goto request_failed;
1066 case ENOMEM:
1068 ast_ari_response_error(&response, 500,
1069 "Internal Server Error",
1070 "Out of memory");
1071 goto request_failed;
1072 case EIO:
1073 ast_ari_response_error(&response, 400,
1074 "Bad Request", "Error parsing request body");
1075 goto request_failed;
1076 }
1077
1078 /* Look for a JSON request entity only if there were no post_vars.
1079 * If there were post_vars, then the request body would already have
1080 * been consumed and can not be read again.
1081 */
1082 ast_trace(-1, "Checking body for vars\n");
1083 body = ast_http_get_json(ser, headers);
1084 if (!body) {
1085 switch (errno) {
1086 case EFBIG:
1087 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
1088 goto request_failed;
1089 case ENOMEM:
1090 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
1091 goto request_failed;
1092 case EIO:
1093 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1094 goto request_failed;
1095 }
1096 }
1097 }
1098 if (get_params == NULL) {
1099 ast_trace(-1, "No get_params, using post_vars if any\n");
1100 get_params = post_vars;
1101 } else if (get_params && post_vars) {
1102 /* Has both post_vars and get_params */
1103 struct ast_variable *last_var = post_vars;
1104 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1105 while (last_var->next) {
1106 last_var = last_var->next;
1107 }
1108 /* The duped get_params will get freed when post_vars gets
1109 * ast_variables_destroyed.
1110 */
1111 last_var->next = ast_variables_dup(get_params);
1112 get_params = post_vars;
1113 }
1114
1115 /* At this point, get_params will contain post_vars (if any) */
1116 app_name = ast_variable_find_in_list(get_params, "app");
1117 if (!app_name) {
1118 struct ast_json *app = ast_json_object_get(body, "app");
1119
1121 }
1122 ast_trace(-1, "app_name: %s\n", app_name);
1123
1124 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1125 * and the global debug flag.
1126 */
1128 if (debug_app) {
1129 struct ast_str *buf = ast_str_create(512);
1131
1132 if (!buf || (body && !str)) {
1134 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1136 ast_free(buf);
1137 goto request_failed;
1138 }
1139
1140 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1142 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1143 for (var = headers; var; var = var->next) {
1144 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1145 }
1146 for (var = get_params; var; var = var->next) {
1147 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1148 }
1149 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1151 ast_free(buf);
1152 }
1153
1156 urih, uri, method, get_params, headers, body, &response);
1159 }
1160
1161 if (response.no_response) {
1162 /* The handler indicates no further response is necessary.
1163 * Probably because it already handled it */
1164 ast_free(response.headers);
1165 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1166 }
1167
1168request_failed:
1169
1170 /* If you explicitly want to have no content, set message to
1171 * ast_json_null().
1172 */
1173 ast_assert(response.message != NULL);
1174 ast_assert(response.response_code > 0);
1175
1176 /* response.message could be NULL, in which case the empty response_body
1177 * is correct
1178 */
1179 if (response.message && !ast_json_is_null(response.message)) {
1180 ast_str_append(&response.headers, 0,
1181 "Content-type: application/json\r\n");
1182 if (ast_json_dump_str_format(response.message, &response_body,
1183 ast_ari_json_format()) != 0) {
1184 /* Error encoding response */
1185 response.response_code = 500;
1186 response.response_text = "Internal Server Error";
1187 ast_str_set(&response_body, 0, "%s", "");
1188 ast_str_set(&response.headers, 0, "%s", "");
1189 }
1190 }
1191
1192 if (debug_app) {
1193 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1195 response.response_text, ast_str_buffer(response.headers),
1196 ast_str_buffer(response_body));
1197 }
1198
1199 ast_http_send(ser, method, response.response_code,
1200 response.response_text, response.headers, response_body,
1201 response.fd != -1 ? response.fd : 0, 0);
1202 /* ast_http_send takes ownership, so we don't have to free them */
1203 response_body = NULL;
1204
1205 ast_json_unref(response.message);
1206 if (response.fd >= 0) {
1207 close(response.fd);
1208 }
1209 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1210 response.response_text);
1211}
static const char app[]
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
#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:522
struct ast_variable * ast_http_get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlenco...
Definition http.c:1463
struct ast_json * ast_http_get_json(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get JSON from client Request Entity-Body, if content type is application/json.
Definition http.c:1388
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition http.c:911
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition http.c:722
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.
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
#define ast_verbose(...)
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:475
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition res_ari.c:1000
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:947
enum ast_ari_invoke_result ast_ari_invoke(struct ast_tcptls_session_instance *ser, enum ast_ari_invoke_source source, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition res_ari.c:612
const char * method
Definition res_pjsip.c:1277
int stasis_app_get_debug_by_name(const char *app_name)
Get debug status of an application.
#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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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:779

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

820{
821 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
822 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
823 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
824 struct ast_json *obj = NULL;
825 struct ast_variable *host = NULL;
826 struct ast_json_error error = {};
827 struct stat file_stat;
828
829 ast_debug(3, "%s(%s)\n", __func__, uri);
830
831 absolute_path_builder = ast_str_create(80);
832 if (absolute_path_builder == NULL) {
834 return;
835 }
836
837 /* absolute path to the rest-api directory */
838 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
839 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
840 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
841 if (absolute_api_dirname == NULL) {
842 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
844 response, 500, "Internal Server Error",
845 "Cannot find rest-api directory");
846 return;
847 }
848
849 /* absolute path to the requested file */
850 ast_str_append(&absolute_path_builder, 0, "%s", uri);
851 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
852 if (absolute_filename == NULL) {
853 switch (errno) {
854 case ENAMETOOLONG:
855 case ENOENT:
856 case ENOTDIR:
858 response, 404, "Not Found",
859 "Resource not found");
860 break;
861 case EACCES:
863 response, 403, "Forbidden",
864 "Permission denied");
865 break;
866 default:
868 "Error determining real path for uri '%s': %s\n",
869 uri, strerror(errno));
871 response, 500, "Internal Server Error",
872 "Cannot find file");
873 break;
874 }
875 return;
876 }
877
878 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
879 /* HACKERZ! */
881 "Invalid attempt to access '%s' (not in %s)\n",
882 absolute_filename, absolute_api_dirname);
884 response, 404, "Not Found",
885 "Resource not found");
886 return;
887 }
888
889 if (stat(absolute_filename, &file_stat) == 0) {
890 if (!(file_stat.st_mode & S_IFREG)) {
891 /* Not a file */
893 response, 403, "Forbidden",
894 "Invalid access");
895 return;
896 }
897 } else {
898 /* Does not exist */
900 response, 404, "Not Found",
901 "Resource not found");
902 return;
903 }
904
905 /* Load resource object from file */
906 obj = ast_json_load_new_file(absolute_filename, &error);
907 if (obj == NULL) {
908 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
909 error.source, error.line, error.column, error.text);
911 response, 500, "Internal Server Error",
912 "Yikes! Cannot parse resource");
913 return;
914 }
915
916 /* Update the basePath properly */
917 if (ast_json_object_get(obj, "basePath") != NULL) {
918 for (host = headers; host; host = host->next) {
919 if (strcasecmp(host->name, "Host") == 0) {
920 break;
921 }
922 }
923 if (host != NULL) {
924 if (prefix != NULL && strlen(prefix) > 0) {
926 obj, "basePath",
927 ast_json_stringf("http://%s%s/ari", host->value,prefix));
928 } else {
930 obj, "basePath",
931 ast_json_stringf("http://%s/ari", host->value));
932 }
933 } else {
934 /* Without the host, we don't have the basePath */
935 ast_json_object_del(obj, "basePath");
936 }
937 }
938
939 ast_ari_response_ok(response, obj);
940}
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:145
#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,...)

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(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), 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 612 of file res_ari.c.

617{
620 struct stasis_rest_handlers *wildcard_handler = NULL;
621 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
624
625 char *path = ast_strdupa(uri);
626 char *path_segment = NULL;
628 SCOPE_ENTER(3, "Request: %s %s, path:%s\n", ast_get_http_method(method), uri, path);
629
630
631 if (!general) {
632 if (ser && source == ARI_INVOKE_SOURCE_REST) {
634 }
635 ast_ari_response_error(response, 500, "Server Error", "URI handler config missing");
637 response->response_code, response->response_text);
638 }
639
640 user = authenticate_user(get_params, headers, source);
641 if (!user) {
642 ast_ari_response_error(response, 401, "Unauthorized", "Authentication required");
643 if (source == ARI_INVOKE_SOURCE_REST) {
644 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
645 * message is used by an origin server to challenge the
646 * authorization of a user agent. This response MUST include a
647 * WWW-Authenticate header field containing at least one
648 * challenge applicable to the requested resource.
649 */
650
651 /* Section 1.2:
652 * realm = "realm" "=" realm-value
653 * realm-value = quoted-string
654 * Section 2:
655 * challenge = "Basic" realm
656 */
657 ast_str_append(&response->headers, 0,
658 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
659 general->auth_realm);
660 }
662 response->response_code, response->response_text);
663 }
664
665 if (source == ARI_INVOKE_SOURCE_REST && ser && user->acl && !ast_acl_list_is_empty(user->acl) &&
666 ast_apply_acl(user->acl, &ser->remote_address, "ARI User ACL") == AST_SENSE_DENY) {
667 ast_ari_response_error(response, 403, "Forbidden", "Access denied by ACL");
669 response->response_code, response->response_text);
670 } else if (!ast_fully_booted) {
671 ast_ari_response_error(response, 503, "Service Unavailable", "Asterisk not booted");
673 response->response_code, response->response_text);
674 } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
675 ast_ari_response_error(response, 403, "Forbidden", "Write access denied");
677 response->response_code, response->response_text);
678 } else if (ast_ends_with(uri, "/")) {
679 remove_trailing_slash(uri, response);
681 response->response_code, response->response_text);
682 } else if (ast_begins_with(uri, "api-docs/")) {
683 /* Serving up API docs */
684 if (method != AST_HTTP_GET) {
685 ast_ari_response_error(response, 405, "Method Not Allowed", "Unsupported method");
686 } else {
687 if (urih) {
688 /* Skip the api-docs prefix */
689 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, response);
690 } else {
691 /*
692 * If we were invoked without a urih, we're probably
693 * being called from the websocket so just use the
694 * default prefix. It's filled in by ast_http_uri_link().
695 */
696 ast_ari_get_docs(strchr(uri, '/') + 1, http_uri.prefix, headers, response);
697 }
698 }
700 response->response_code, response->response_text);
701 }
702
703 root = handler = get_root_handler();
704 ast_assert(root != NULL);
705
706 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
707 struct stasis_rest_handlers *found_handler = NULL;
708 int i;
709 SCOPE_ENTER(4, "Finding handler for path segment %s\n", path_segment);
710
712
713 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
714 struct stasis_rest_handlers *child = handler->children[i];
715 SCOPE_ENTER(5, "Checking handler path segment %s\n", child->path_segment);
716
717 if (child->is_wildcard) {
718 /* Record the path variable */
719 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
720 path_var->next = path_vars;
721 path_vars = path_var;
722 wildcard_handler = child;
723 ast_trace(-1, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
724
725 } else if (strcmp(child->path_segment, path_segment) == 0) {
726 found_handler = child;
727 ast_trace(-1, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
728 } else {
729 ast_trace(-1, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
730 }
731 SCOPE_EXIT("Done checking %s\n", child->path_segment);
732 }
733
734 if (!found_handler && wildcard_handler) {
735 ast_trace(-1, " No explicit handler found for %s. Using wildcard %s.\n",
736 path_segment, wildcard_handler->path_segment);
737 found_handler = wildcard_handler;
738 wildcard_handler = NULL;
739 }
740
741 if (found_handler == NULL) {
742 /* resource not found */
744 response, 404, "Not Found",
745 "Resource not found");
746 SCOPE_EXIT_EXPR(break, "Handler not found for %s\n", path_segment);
747 } else {
748 handler = found_handler;
749 }
750 SCOPE_EXIT("Done checking %s\n", path_segment);
751 }
752
753 if (handler == NULL || response->response_code == 404) {
754 /* resource not found */
756 response->response_code, response->response_text, uri);
757 }
758
760 if (method == AST_HTTP_OPTIONS) {
761 handle_options(handler, headers, response);
763 }
764
765 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
766 add_allow_header(handler, response);
768 response, 405, "Method Not Allowed",
769 "Invalid method");
771 response->response_code, response->response_text);
772 }
773
774 if (handler->is_websocket && method == AST_HTTP_GET) {
775 if (source == ARI_INVOKE_SOURCE_WEBSOCKET) {
777 response, 400, "Bad request",
778 "Can't upgrade to websocket from a websocket");
780 response->response_code, response->response_text);
781 }
782 /* WebSocket! */
783 ast_trace(-1, "Handling websocket %s\n", uri);
785 get_params, headers);
786 /* Since the WebSocket code handles the connection, we shouldn't
787 * do anything else; setting no_response */
788 response->no_response = 1;
790 }
791
792 callback = handler->callbacks[method];
793 if (callback == NULL) {
794 add_allow_header(handler, response);
796 response, 405, "Method Not Allowed",
797 "Invalid method");
799 response->response_code, response->response_text);
800 }
801
802 ast_trace(-1, "Running callback: %s\n", uri);
803 callback(ser, get_params, path_vars, headers, body, response);
804 if (response->message == NULL && response->response_code == 0) {
805 /* Really should not happen */
806 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
809 response, 501, "Not Implemented",
810 "Method not implemented");
812 response->response_code, response->response_text);
813 }
814 SCOPE_EXIT_RTN_VALUE(ARI_INVOKE_RESULT_SUCCESS, "Response: %d : %s\n",
815 response->response_code, response->response_text);
816}
enum ast_acl_sense ast_apply_acl(struct ast_acl_list *acl_list, const struct ast_sockaddr *addr, const char *purpose)
Apply a set of rules to a given IP address.
Definition acl.c:799
@ AST_SENSE_DENY
Definition acl.h:37
int ast_acl_list_is_empty(struct ast_acl_list *acl_list)
Determines if an ACL is empty or if it contains entries.
Definition acl.c:540
@ 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, int rdlock)
#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:127
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:586
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition res_ari.c:268
static struct ast_http_uri http_uri
Definition res_ari.c:117
static struct ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers, enum ast_ari_invoke_source source)
Authenticate an HTTP request.
Definition res_ari.c:521
static void handle_options(struct stasis_rest_handlers *handler, struct ast_variable *headers, struct ast_ari_response *response)
Handle OPTIONS request, mainly for CORS preflight requests.
Definition res_ari.c:314
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition res_ari.c:818
static struct 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:58
Per-user configuration options.
Definition internal.h:85
const char * prefix
Definition http.h:106
const char * path_segment
Definition ari.h:71
struct stasis_rest_handlers * children[]
Definition ari.h:95
structure to hold users read from phoneprov_users.conf
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition utils.c:760
const struct ast_flags ast_uri_http_legacy
Definition utils.c:718

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_acl_list_is_empty(), ast_apply_acl(), 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_SENSE_DENY, ast_str_append(), ast_strdupa, ast_trace, ast_uri_decode(), ast_uri_http_legacy, ast_variable_new, ast_variables_destroy(), authenticate_user(), callback(), stasis_rest_handlers::children, 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::num_children, stasis_rest_handlers::path_segment, ast_http_uri::prefix, RAII_VAR, ast_tcptls_session_instance::remote_address, 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(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), AST_TEST_DEFINE(), and invoke_delete_common().

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

1001{
1003 return general ? general->format : AST_JSON_COMPACT;
1004}
@ 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

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(), unload_module(), unload_module(), unload_module(), unload_module(), unload_module(), unload_module(), unload_module(), unload_module(), unload_module(), unload_module(), 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(), ast_ari_bridges_create_with_id(), ast_ari_bridges_get_bridge_var(), ast_ari_bridges_get_bridge_var_cb(), ast_ari_bridges_get_bridge_vars(), ast_ari_bridges_get_bridge_vars_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_set_bridge_var_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_get_channel_vars(), ast_ari_channels_get_channel_vars_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_claim_channel_cb(), 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(), json_to_ast_variables(), 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_bridge_var(), ast_ari_bridges_get_bridge_var_cb(), ast_ari_bridges_get_bridge_vars(), ast_ari_bridges_get_bridge_vars_cb(), 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_bridge_var(), ast_ari_bridges_set_bridge_var_cb(), ast_ari_bridges_set_bridge_vars(), ast_ari_bridges_set_bridge_vars_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_get_channel_vars(), ast_ari_channels_get_channel_vars_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_progress_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_set_channel_vars(), ast_ari_channels_set_channel_vars_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_claim_channel(), ast_ari_events_claim_channel_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(), find_bridge(), find_channel_control(), find_control(), json_to_ast_variables(), json_to_ast_variables(), 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_bridge_var(), ast_ari_bridges_set_bridge_vars(), 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_progress(), 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_set_channel_vars(), 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_claim_channel(), 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_get_bridge_var(), ast_ari_bridges_get_bridge_vars(), ast_ari_bridges_list(), ast_ari_channels_create(), ast_ari_channels_get(), ast_ari_channels_get_channel_var(), ast_ari_channels_get_channel_vars(), 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 1328 of file res_ari.c.

◆ authenticate_user()

static struct ari_conf_user * authenticate_user ( struct ast_variable get_params,
struct ast_variable headers,
enum ast_ari_invoke_source  source 
)
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 521 of file res_ari.c.

523{
524 RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
525 struct ari_conf_user *ari_user = NULL;
526 SCOPE_ENTER(3, "\n");
527
528 /* HTTP Basic authentication */
529 http_auth = ast_http_get_auth(headers);
530 if (!http_auth) {
531 /* ?api_key authentication */
532 http_auth = parse_api_keys(get_params);
533 }
534 if (!http_auth) {
535 SCOPE_EXIT_RTN_VALUE(NULL, "No credentials found in HTTP headers or api_key\n");
536 }
537
538 if (source != ARI_INVOKE_SOURCE_TEST) {
539 ast_trace(-1, "Validating user %s\n", http_auth->userid);
540 ari_user = ari_conf_validate_user(http_auth->userid, http_auth->password);
541 if (!ari_user) {
542 SCOPE_EXIT_RTN_VALUE(NULL, "User %s failed to validate\n", http_auth->userid);
543 }
544 SCOPE_EXIT_RTN_VALUE(ari_user, "User %s validated. Read only: %s\n",
545 http_auth->userid, AST_YESNO(ari_user->read_only));
546 }
547#if AST_DEVMODE
548 /*
549 * ARI_INVOKE_SOURCE_TEST should only be set by the tests in test_ari.c but
550 * as a safety precaution, only check against the test usernames and passwords
551 * if we're in DEVMODE.
552 */
553 else {
554 int readonly = 0;
555
556 if (ast_strings_equal(http_auth->userid, "aritest")) {
557 if (!ast_strings_equal(http_auth->password, "aritestpw")) {
558 SCOPE_EXIT_RTN_VALUE(NULL, "User %s failed to validate\n", http_auth->userid);
559 }
560 } else if (ast_strings_equal(http_auth->userid, "aritestro")) {
561 if (!ast_strings_equal(http_auth->password, "aritestropw")) {
562 SCOPE_EXIT_RTN_VALUE(NULL, "User %s failed to validate\n", http_auth->userid);
563 }
564 readonly = 1;
565 }
566
567 ari_user = ao2_alloc(sizeof(*ari_user), test_ari_user_destructor);
568 if (!ari_user) {
569 SCOPE_EXIT_RTN_VALUE(NULL, "alloc failed\n");
570 }
571 if (ast_string_field_init(ari_user, 256) != 0) {
572 SCOPE_EXIT_RTN_VALUE(NULL, "alloc failed\n");
573 }
574 ast_string_field_set(ari_user, password, http_auth->password);
575 ari_user->read_only = readonly;
576
577 SCOPE_EXIT_RTN_VALUE(ari_user, "User %s validated. Read only: %s\n",
578 http_auth->userid, AST_YESNO(ari_user->read_only));
579 }
580
581#else
582 return NULL;
583#endif
584}
@ ARI_INVOKE_SOURCE_TEST
Definition ari.h:150
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition http.c:1677
struct ari_conf_user * ari_conf_validate_user(const char *username, const char *password)
Validated a user's credentials.
static struct ast_http_auth * parse_api_keys(struct ast_variable *get_params)
Parse an api_key parameter from the query parameters into an ast_http_auth structure.
Definition res_ari.c:486
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238
#define AST_YESNO(x)
return Yes or No depending on the argument.
Definition strings.h:143
const ast_string_field password
Definition internal.h:90
HTTP authentication information.
Definition http.h:125

References ao2_alloc, ao2_cleanup, ari_conf_validate_user(), ARI_INVOKE_SOURCE_TEST, ast_http_get_auth(), ast_string_field_init, ast_string_field_set, ast_strings_equal(), ast_trace, AST_YESNO, NULL, parse_api_keys(), ari_conf_user::password, RAII_VAR, ari_conf_user::read_only, SCOPE_ENTER, and SCOPE_EXIT_RTN_VALUE.

Referenced by ast_ari_invoke().

◆ create_http_auth()

static struct ast_http_auth * create_http_auth ( const char *  userid,
const char *  password 
)
static

Definition at line 448 of file res_ari.c.

449{
450 struct ast_http_auth *auth;
451 size_t userid_len;
452 size_t password_len;
453
454 if (!userid || !password) {
455 ast_log(LOG_ERROR, "Invalid userid/password\n");
456 return NULL;
457 }
458
459 userid_len = strlen(userid) + 1;
460 password_len = strlen(password) + 1;
461
462 /* Allocate enough room to store everything in one memory block */
463 auth = ao2_alloc_options(sizeof(*auth) + userid_len + password_len, NULL, AO2_ALLOC_OPT_LOCK_NOLOCK);
464 if (!auth) {
465 return NULL;
466 }
467
468 /* Put the userid right after the struct */
469 auth->userid = (char *)(auth + 1);
470 strcpy(auth->userid, userid);
471
472 /* Put the password right after the userid */
473 auth->password = auth->userid + userid_len;
474 strcpy(auth->password, password);
475
476 return auth;
477}
@ AO2_ALLOC_OPT_LOCK_NOLOCK
Definition astobj2.h:367
#define ao2_alloc_options(data_size, destructor_fn, options)
Definition astobj2.h:404
char * password
Definition http.h:129
char * userid
Definition http.h:127

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_alloc_options, ast_log, LOG_ERROR, NULL, ast_http_auth::password, and ast_http_auth::userid.

Referenced by parse_api_keys().

◆ 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(), reload_module(), and unload_module().

◆ load_module()

static int load_module ( void  )
static

Definition at line 1236 of file res_ari.c.

1237{
1239
1240 /* root_handler may have been built during a declined load */
1241 if (!root_handler) {
1243 }
1244 if (!root_handler) {
1246 }
1247
1248 /* oom_json may have been built during a declined load */
1249 if (!oom_json) {
1251 "{s: s}", "error", "Allocation failed");
1252 }
1253 if (!oom_json) {
1254 /* Ironic */
1255 unload_module();
1257 }
1258
1259 /*
1260 * ari_websocket_load_module() needs to know if ARI is enabled
1261 * globally so it needs the "general" config to be loaded but it
1262 * also needs to register a sorcery object observer for
1263 * "outbound_websocket" BEFORE the outbound_websocket configs are loaded.
1264 * outbound_websocket in turn needs the users to be loaded so we'll
1265 * initialize sorcery and load "general" and "user" configs first, then
1266 * load the websocket module, then load the "outbound_websocket" configs
1267 * which will fire the observers.
1268 */
1270 unload_module();
1272 }
1273
1275 unload_module();
1277 }
1278
1279 /*
1280 * Now we can load the outbound_websocket configs which will
1281 * fire the observers.
1282 */
1284
1285 if (ari_cli_register() != 0) {
1286 unload_module();
1288 }
1289
1290 if (is_enabled()) {
1291 ast_debug(3, "ARI enabled\n");
1293 } else {
1294 ast_debug(3, "ARI disabled\n");
1295 }
1296
1298}
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:747
@ ARI_CONF_INIT
Definition internal.h:153
@ ARI_CONF_LOAD_OWC
Definition internal.h:157
@ ARI_CONF_LOAD_USER
Definition internal.h:156
@ ARI_CONF_LOAD_GENERAL
Definition internal.h:155
#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.
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:1213

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().

◆ parse_api_keys()

static struct ast_http_auth * parse_api_keys ( struct ast_variable get_params)
static

Parse an api_key parameter from the query parameters into an ast_http_auth structure.

Parameters
get_paramsquery string parameters
Returns
ast_http_user object for the api_key.
Return values
NULLif api_key wasn't found or is had no password.

Definition at line 486 of file res_ari.c.

487{
488 struct ast_variable *v;
489
490 for (v = get_params; v; v = v->next) {
491 if (ast_strings_equal(v->name, "api_key")) {
492 if (!ast_strlen_zero(v->value)) {
493 char *password = ast_strdupa(v->value);
494 char *username = strsep(&password, ":");
495 if (!ast_strlen_zero(password)) {
496 return create_http_auth(username, password);
497 }
498 }
499 break;
500 }
501 }
502 return NULL;
503}
static struct ast_http_auth * create_http_auth(const char *userid, const char *password)
Definition res_ari.c:448

References ast_strdupa, ast_strings_equal(), ast_strlen_zero(), create_http_auth(), ast_variable::name, ast_variable::next, NULL, strsep(), and ast_variable::value.

Referenced by authenticate_user().

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

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

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 int is_now_enabled = 0;
1304
1306
1307 is_now_enabled = is_enabled();
1308
1309 if (was_enabled && !is_now_enabled) {
1310 ast_debug(3, "Disabling ARI\n");
1312 } else if (!was_enabled && is_now_enabled) {
1313 ast_debug(3, "Enabling ARI\n");
1315 }
1316
1318}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition http.c:779
@ ARI_CONF_LOAD_ALL
Definition internal.h:158
@ ARI_CONF_RELOAD
Definition internal.h:154

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

588{
589 char *slashless = ast_strdupa(uri);
590 slashless[strlen(slashless) - 1] = '\0';
591
592 /* While it's tempting to redirect the client to the slashless URL,
593 * that is problematic. A 302 Found is the most appropriate response,
594 * but most clients issue a GET on the location you give them,
595 * regardless of the method of the original request.
596 *
597 * While there are some ways around this, it gets into a lot of client
598 * specific behavior and corner cases in the HTTP standard. There's also
599 * very little practical benefit of redirecting; only GET and HEAD can
600 * be redirected automagically; all other requests "MUST NOT
601 * automatically redirect the request unless it can be confirmed by the
602 * user, since this might change the conditions under which the request
603 * was issued."
604 *
605 * Given all of that, a 404 with a nice message telling them what to do
606 * is probably our best bet.
607 */
608 ast_ari_response_error(response, 404, "Not Found",
609 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
610}

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

1214{
1216
1218
1219 if (is_enabled()) {
1220 ast_debug(3, "Disabling ARI\n");
1222 }
1223
1225
1229
1231 oom_json = NULL;
1232
1233 return 0;
1234}
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.
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 = ASTERISK_GPL_KEY , .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", .optional_modules = "res_ari_model", .load_pri = AST_MODPRI_APP_DEPEND, }
static

Definition at line 1328 of file res_ari.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1328 of file res_ari.c.

◆ http_uri

struct ast_http_uri http_uri
static

Definition at line 117 of file res_ari.c.

117 {
118 .callback = ast_ari_callback,
119 .description = "Asterisk RESTful API",
120 .uri = "ari",
121 .has_subtree = 1,
122 .data = NULL,
123 .key = __FILE__,
124 .no_decode_uri = 1,
125};
static int ast_ari_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Callback for the root URI.
Definition res_ari.c:1020

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