Asterisk - The Open Source Telephony Project GIT-master-80b953f
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_api_key (const char *api_key)
 Authenticate a ?api_key=userid:password
 
static struct ari_conf_userauthenticate_user (struct ast_variable *get_params, struct ast_variable *headers)
 Authenticate an HTTP request.
 
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 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", .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 1240 of file res_ari.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

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

939{
940 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
941 struct ast_ari_response response = { .fd = -1, 0 };
942 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
943 struct ast_variable *var;
944 const char *app_name = NULL;
945 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
946 int debug_app = 0;
948 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
950
951 if (!response_body) {
953 ast_http_error(ser, 500, "Server Error", "Out of memory");
954 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
955 }
956
957 response.headers = ast_str_create(40);
958 if (!response.headers) {
960 ast_http_error(ser, 500, "Server Error", "Out of memory");
961 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
962 }
963
964 process_cors_request(headers, &response);
965
966 /* Process form data from a POST. It could be mixed with query
967 * parameters, which seems a bit odd. But it's allowed, so that's okay
968 * with us.
969 */
970 post_vars = ast_http_get_post_vars(ser, headers);
971 if (!post_vars) {
972 ast_trace(-1, "No post_vars\n");
973 switch (errno) {
974 case EFBIG:
975 ast_ari_response_error(&response, 413,
976 "Request Entity Too Large",
977 "Request body too large");
978 goto request_failed;
979 case ENOMEM:
981 ast_ari_response_error(&response, 500,
982 "Internal Server Error",
983 "Out of memory");
984 goto request_failed;
985 case EIO:
986 ast_ari_response_error(&response, 400,
987 "Bad Request", "Error parsing request body");
988 goto request_failed;
989 }
990
991 /* Look for a JSON request entity only if there were no post_vars.
992 * If there were post_vars, then the request body would already have
993 * been consumed and can not be read again.
994 */
995 ast_trace(-1, "Checking body for vars\n");
996 body = ast_http_get_json(ser, headers);
997 if (!body) {
998 switch (errno) {
999 case EFBIG:
1000 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
1001 goto request_failed;
1002 case ENOMEM:
1003 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
1004 goto request_failed;
1005 case EIO:
1006 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1007 goto request_failed;
1008 }
1009 }
1010 }
1011 if (get_params == NULL) {
1012 ast_trace(-1, "No get_params, using post_vars if any\n");
1013 get_params = post_vars;
1014 } else if (get_params && post_vars) {
1015 /* Has both post_vars and get_params */
1016 struct ast_variable *last_var = post_vars;
1017 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1018 while (last_var->next) {
1019 last_var = last_var->next;
1020 }
1021 /* The duped get_params will get freed when post_vars gets
1022 * ast_variables_destroyed.
1023 */
1024 last_var->next = ast_variables_dup(get_params);
1025 get_params = post_vars;
1026 }
1027
1028 /* At this point, get_params will contain post_vars (if any) */
1029 app_name = ast_variable_find_in_list(get_params, "app");
1030 if (!app_name) {
1031 struct ast_json *app = ast_json_object_get(body, "app");
1032
1034 }
1035 ast_trace(-1, "app_name: %s\n", app_name);
1036
1037 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1038 * and the global debug flag.
1039 */
1041 if (debug_app) {
1042 struct ast_str *buf = ast_str_create(512);
1044
1045 if (!buf || (body && !str)) {
1047 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1049 ast_free(buf);
1050 goto request_failed;
1051 }
1052
1053 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1055 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1056 for (var = headers; var; var = var->next) {
1057 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1058 }
1059 for (var = get_params; var; var = var->next) {
1060 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1061 }
1062 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1064 ast_free(buf);
1065 }
1066
1069 urih, uri, method, get_params, headers, body, &response);
1072 }
1073
1074 if (response.no_response) {
1075 /* The handler indicates no further response is necessary.
1076 * Probably because it already handled it */
1077 ast_free(response.headers);
1078 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1079 }
1080
1081request_failed:
1082
1083 /* If you explicitly want to have no content, set message to
1084 * ast_json_null().
1085 */
1086 ast_assert(response.message != NULL);
1087 ast_assert(response.response_code > 0);
1088
1089 /* response.message could be NULL, in which case the empty response_body
1090 * is correct
1091 */
1092 if (response.message && !ast_json_is_null(response.message)) {
1093 ast_str_append(&response.headers, 0,
1094 "Content-type: application/json\r\n");
1095 if (ast_json_dump_str_format(response.message, &response_body,
1096 ast_ari_json_format()) != 0) {
1097 /* Error encoding response */
1098 response.response_code = 500;
1099 response.response_text = "Internal Server Error";
1100 ast_str_set(&response_body, 0, "%s", "");
1101 ast_str_set(&response.headers, 0, "%s", "");
1102 }
1103 }
1104
1105 if (debug_app) {
1106 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1108 response.response_text, ast_str_buffer(response.headers),
1109 ast_str_buffer(response_body));
1110 }
1111
1112 ast_http_send(ser, method, response.response_code,
1113 response.response_text, response.headers, response_body,
1114 response.fd != -1 ? response.fd : 0, 0);
1115 /* ast_http_send takes ownership, so we don't have to free them */
1116 response_body = NULL;
1117
1118 ast_json_unref(response.message);
1119 if (response.fd >= 0) {
1120 close(response.fd);
1121 }
1122 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1123 response.response_text);
1124}
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:1455
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:1380
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:903
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:714
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:913
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:860
enum ast_ari_invoke_result ast_ari_invoke(struct ast_tcptls_session_instance *ser, enum ast_ari_invoke_source source, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
Definition res_ari.c:528
const char * method
Definition res_pjsip.c:1273
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 731 of file res_ari.c.

733{
734 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
735 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
736 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
737 struct ast_json *obj = NULL;
738 struct ast_variable *host = NULL;
739 struct ast_json_error error = {};
740 struct stat file_stat;
741
742 ast_debug(3, "%s(%s)\n", __func__, uri);
743
744 absolute_path_builder = ast_str_create(80);
745 if (absolute_path_builder == NULL) {
747 return;
748 }
749
750 /* absolute path to the rest-api directory */
751 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
752 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
753 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
754 if (absolute_api_dirname == NULL) {
755 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
757 response, 500, "Internal Server Error",
758 "Cannot find rest-api directory");
759 return;
760 }
761
762 /* absolute path to the requested file */
763 ast_str_append(&absolute_path_builder, 0, "%s", uri);
764 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
765 if (absolute_filename == NULL) {
766 switch (errno) {
767 case ENAMETOOLONG:
768 case ENOENT:
769 case ENOTDIR:
771 response, 404, "Not Found",
772 "Resource not found");
773 break;
774 case EACCES:
776 response, 403, "Forbidden",
777 "Permission denied");
778 break;
779 default:
781 "Error determining real path for uri '%s': %s\n",
782 uri, strerror(errno));
784 response, 500, "Internal Server Error",
785 "Cannot find file");
786 break;
787 }
788 return;
789 }
790
791 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
792 /* HACKERZ! */
794 "Invalid attempt to access '%s' (not in %s)\n",
795 absolute_filename, absolute_api_dirname);
797 response, 404, "Not Found",
798 "Resource not found");
799 return;
800 }
801
802 if (stat(absolute_filename, &file_stat) == 0) {
803 if (!(file_stat.st_mode & S_IFREG)) {
804 /* Not a file */
806 response, 403, "Forbidden",
807 "Invalid access");
808 return;
809 }
810 } else {
811 /* Does not exist */
813 response, 404, "Not Found",
814 "Resource not found");
815 return;
816 }
817
818 /* Load resource object from file */
819 obj = ast_json_load_new_file(absolute_filename, &error);
820 if (obj == NULL) {
821 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
822 error.source, error.line, error.column, error.text);
824 response, 500, "Internal Server Error",
825 "Yikes! Cannot parse resource");
826 return;
827 }
828
829 /* Update the basePath properly */
830 if (ast_json_object_get(obj, "basePath") != NULL) {
831 for (host = headers; host; host = host->next) {
832 if (strcasecmp(host->name, "Host") == 0) {
833 break;
834 }
835 }
836 if (host != NULL) {
837 if (prefix != NULL && strlen(prefix) > 0) {
839 obj, "basePath",
840 ast_json_stringf("http://%s%s/ari", host->value,prefix));
841 } else {
843 obj, "basePath",
844 ast_json_stringf("http://%s/ari", host->value));
845 }
846 } else {
847 /* Without the host, we don't have the basePath */
848 ast_json_object_del(obj, "basePath");
849 }
850 }
851
852 ast_ari_response_ok(response, obj);
853}
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 528 of file res_ari.c.

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

914{
916 return general ? general->format : AST_JSON_COMPACT;
917}
@ 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_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(), json_to_ast_variables(), return_sorcery_object(), send_message(), and send_refer().

◆ ast_ari_response_created()

void ast_ari_response_created ( struct ast_ari_response response,
const char *  url,
struct ast_json message 
)

Fill in a Created (201) ast_ari_response.

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

Definition at line 258 of file res_ari.c.

260{
262 response->message = message;
263 response->response_code = 201;
264 response->response_text = "Created";
265 ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
266}
static char url[512]

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

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

◆ ast_ari_response_error()

void ast_ari_response_error ( struct ast_ari_response response,
int  response_code,
const char *  response_text,
const char *  message_fmt,
  ... 
)

Fill in an error ast_ari_response.

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

Definition at line 212 of file res_ari.c.

216{
218 va_list ap;
219
220 va_start(ap, message_fmt);
221 message = ast_json_vstringf(message_fmt, ap);
222 va_end(ap);
223 response->message = ast_json_pack("{s: o}",
224 "message", ast_json_ref(message));
225 response->response_code = response_code;
226 response->response_text = response_text;
227}
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition json.c:612
struct ast_json * ast_json_vstringf(const char *format, va_list args)
Create a JSON string, vprintf style.
Definition json.c:303

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

Referenced by ari_bridges_play_helper(), ari_bridges_play_new(), ari_channels_handle_originate_with_id(), ari_channels_handle_play(), ari_channels_handle_snoop_channel(), ast_ari_applications_filter(), ast_ari_applications_filter_cb(), ast_ari_applications_get(), ast_ari_applications_get_cb(), ast_ari_applications_list(), ast_ari_applications_list_cb(), ast_ari_applications_subscribe(), ast_ari_applications_subscribe_cb(), ast_ari_applications_unsubscribe(), ast_ari_applications_unsubscribe_cb(), ast_ari_asterisk_add_log(), ast_ari_asterisk_add_log_cb(), ast_ari_asterisk_delete_log(), ast_ari_asterisk_delete_log_cb(), ast_ari_asterisk_delete_object(), ast_ari_asterisk_delete_object_cb(), ast_ari_asterisk_get_global_var(), ast_ari_asterisk_get_global_var_cb(), ast_ari_asterisk_get_info_cb(), ast_ari_asterisk_get_module(), ast_ari_asterisk_get_module_cb(), ast_ari_asterisk_get_object(), ast_ari_asterisk_get_object_cb(), ast_ari_asterisk_list_log_channels(), ast_ari_asterisk_list_log_channels_cb(), ast_ari_asterisk_list_modules_cb(), ast_ari_asterisk_load_module(), ast_ari_asterisk_load_module_cb(), ast_ari_asterisk_ping_cb(), ast_ari_asterisk_reload_module(), ast_ari_asterisk_reload_module_cb(), ast_ari_asterisk_rotate_log(), ast_ari_asterisk_rotate_log_cb(), ast_ari_asterisk_set_global_var(), ast_ari_asterisk_set_global_var_cb(), ast_ari_asterisk_unload_module(), ast_ari_asterisk_unload_module_cb(), ast_ari_asterisk_update_object(), ast_ari_asterisk_update_object_cb(), ast_ari_bridges_add_channel_cb(), ast_ari_bridges_clear_video_source_cb(), ast_ari_bridges_create(), ast_ari_bridges_create_cb(), ast_ari_bridges_create_with_id(), ast_ari_bridges_create_with_id_cb(), ast_ari_bridges_destroy_cb(), ast_ari_bridges_get(), ast_ari_bridges_get_cb(), ast_ari_bridges_list_cb(), ast_ari_bridges_play_cb(), ast_ari_bridges_play_with_id_cb(), ast_ari_bridges_record(), ast_ari_bridges_record_cb(), ast_ari_bridges_remove_channel(), ast_ari_bridges_remove_channel_cb(), ast_ari_bridges_set_video_source(), ast_ari_bridges_set_video_source_cb(), ast_ari_bridges_start_moh_cb(), ast_ari_bridges_stop_moh(), ast_ari_bridges_stop_moh_cb(), ast_ari_callback(), ast_ari_channels_answer(), ast_ari_channels_answer_cb(), ast_ari_channels_continue_in_dialplan(), ast_ari_channels_continue_in_dialplan_cb(), ast_ari_channels_create(), ast_ari_channels_create_cb(), ast_ari_channels_dial(), ast_ari_channels_dial_cb(), ast_ari_channels_external_media(), ast_ari_channels_external_media_cb(), ast_ari_channels_get(), ast_ari_channels_get_cb(), ast_ari_channels_get_channel_var(), ast_ari_channels_get_channel_var_cb(), ast_ari_channels_hangup(), ast_ari_channels_hangup_cb(), ast_ari_channels_hold_cb(), ast_ari_channels_list_cb(), ast_ari_channels_move(), ast_ari_channels_move_cb(), ast_ari_channels_mute(), ast_ari_channels_mute_cb(), ast_ari_channels_originate_cb(), ast_ari_channels_originate_with_id_cb(), ast_ari_channels_play_cb(), ast_ari_channels_play_with_id_cb(), ast_ari_channels_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_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(), find_bridge(), find_channel_control(), find_control(), 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_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_start_moh(), ast_ari_channels_start_silence(), ast_ari_channels_stop_moh(), ast_ari_channels_stop_silence(), ast_ari_channels_transfer_progress(), ast_ari_channels_unhold(), ast_ari_channels_unmute(), ast_ari_device_states_delete(), ast_ari_device_states_update(), ast_ari_events_user_event(), ast_ari_mailboxes_delete(), ast_ari_mailboxes_update(), ast_ari_playbacks_control(), ast_ari_playbacks_stop(), ast_ari_recordings_delete_stored(), control_recording(), and handle_options().

◆ ast_ari_response_ok()

void ast_ari_response_ok ( struct ast_ari_response response,
struct ast_json message 
)

Fill in an OK (200) ast_ari_response.

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

Definition at line 229 of file res_ari.c.

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

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

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

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1240 of file res_ari.c.

◆ authenticate_api_key()

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

Authenticate a ?api_key=userid:password

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

Definition at line 451 of file res_ari.c.

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

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

Referenced by authenticate_user().

◆ authenticate_user()

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

Authenticate an HTTP request.

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

Definition at line 479 of file res_ari.c.

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

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

Referenced by ast_ari_invoke().

◆ get_root_handler()

static struct stasis_rest_handlers * get_root_handler ( void  )
static

Definition at line 191 of file res_ari.c.

192{
195 return root_handler;
196}

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

Referenced by ast_ari_invoke(), and ast_ari_response_created().

◆ handle_options()

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

Handle OPTIONS request, mainly for CORS preflight requests.

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

Definition at line 314 of file res_ari.c.

317{
318 struct ast_variable *header;
319 char const *acr_method = NULL;
320 char const *acr_headers = NULL;
321 char const *origin = NULL;
322
323 RAII_VAR(struct ast_str *, allow, NULL, ast_free);
324 enum ast_http_method m;
325 int allowed = 0;
326
327 /* Regular OPTIONS response */
328 add_allow_header(handler, response);
330
331 /* Parse CORS headers */
332 for (header = headers; header != NULL; header = header->next) {
333 if (strcmp(ACR_METHOD, header->name) == 0) {
334 acr_method = header->value;
335 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
336 acr_headers = header->value;
337 } else if (strcmp("Origin", header->name) == 0) {
338 origin = header->value;
339 }
340 }
341
342 /* CORS 6.2, #1 - "If the Origin header is not present terminate this
343 * set of steps."
344 */
345 if (origin == NULL) {
346 return;
347 }
348
349 /* CORS 6.2, #2 - "If the value of the Origin header is not a
350 * case-sensitive match for any of the values in list of origins do not
351 * set any additional headers and terminate this set of steps.
352 *
353 * Always matching is acceptable since the list of origins can be
354 * unbounded.
355 *
356 * The Origin header can only contain a single origin as the user agent
357 * will not follow redirects."
358 */
359 if (!origin_allowed(origin)) {
360 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
361 return;
362 }
363
364 /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
365 * or if parsing failed, do not set any additional headers and terminate
366 * this set of steps."
367 */
368 if (acr_method == NULL) {
369 return;
370 }
371
372 /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
373 * headers let header field-names be the empty list."
374 */
375 if (acr_headers == NULL) {
376 acr_headers = "";
377 }
378
379 /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
380 * the values in list of methods do not set any additional headers and
381 * terminate this set of steps."
382 */
383 allow = ast_str_create(20);
384
385 if (!allow) {
387 return;
388 }
389
390 /* Go ahead and build the ACA_METHODS header at the same time */
391 for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
392 if (handler->callbacks[m] != NULL) {
393 char const *m_str = ast_get_http_method(m);
394 if (strcmp(m_str, acr_method) == 0) {
395 allowed = 1;
396 }
397 ast_str_append(&allow, 0, ",%s", m_str);
398 }
399 }
400
401 if (!allowed) {
402 return;
403 }
404
405 /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
406 * case-insensitive match for any of the values in list of headers do
407 * not set any additional headers and terminate this set of steps.
408 *
409 * Note: Always matching is acceptable since the list of headers can be
410 * unbounded."
411 */
412
413 /* CORS 6.2 #7 - "If the resource supports credentials add a single
414 * Access-Control-Allow-Origin header, with the value of the Origin
415 * header as value, and add a single Access-Control-Allow-Credentials
416 * header with the case-sensitive string "true" as value."
417 *
418 * Added by process_cors_request() earlier in the request.
419 */
420
421 /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
422 * header..."
423 */
424
425 /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
426 * consisting of (a subset of) the list of methods."
427 */
428 ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
430
431
432 /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
433 * consisting of (a subset of) the list of headers.
434 *
435 * Since the list of headers can be unbounded simply returning headers
436 * can be enough."
437 */
438 if (!ast_strlen_zero(acr_headers)) {
439 ast_str_append(&response->headers, 0, "%s: %s\r\n",
440 ACA_HEADERS, acr_headers);
441 }
442}
#define LOG_NOTICE
#define ACA_HEADERS
Definition res_ari.c:306
static int origin_allowed(const char *origin)
Definition res_ari.c:283
#define ACA_METHODS
Definition res_ari.c:305
#define ACR_HEADERS
Definition res_ari.c:304
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition res_ari.c:237
#define ACR_METHOD
Definition res_ari.c:303
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
const ast_string_field value
const ast_string_field name
struct header * next

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

Referenced by ast_ari_invoke().

◆ is_enabled()

static int is_enabled ( void  )
static

Helper function to check if module is enabled.

Definition at line 96 of file res_ari.c.

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

References ao2_cleanup, ari_conf_get_general(), and RAII_VAR.

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

◆ load_module()

static int load_module ( void  )
static

Definition at line 1149 of file res_ari.c.

1150{
1152
1153 /* root_handler may have been built during a declined load */
1154 if (!root_handler) {
1156 }
1157 if (!root_handler) {
1159 }
1160
1161 /* oom_json may have been built during a declined load */
1162 if (!oom_json) {
1164 "{s: s}", "error", "Allocation failed");
1165 }
1166 if (!oom_json) {
1167 /* Ironic */
1168 unload_module();
1170 }
1171
1172 /*
1173 * ari_websocket_load_module() needs to know if ARI is enabled
1174 * globally so it needs the "general" config to be loaded but it
1175 * also needs to register a sorcery object observer for
1176 * "outbound_websocket" BEFORE the outbound_websocket configs are loaded.
1177 * outbound_websocket in turn needs the users to be loaded so we'll
1178 * initialize sorcery and load "general" and "user" configs first, then
1179 * load the websocket module, then load the "outbound_websocket" configs
1180 * which will fire the observers.
1181 */
1183 unload_module();
1185 }
1186
1188 unload_module();
1190 }
1191
1192 /*
1193 * Now we can load the outbound_websocket configs which will
1194 * fire the observers.
1195 */
1197
1198 if (ari_cli_register() != 0) {
1199 unload_module();
1201 }
1202
1203 if (is_enabled()) {
1204 ast_debug(3, "ARI enabled\n");
1206 } else {
1207 ast_debug(3, "ARI disabled\n");
1208 }
1209
1211}
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:739
@ ARI_CONF_INIT
Definition internal.h:149
@ ARI_CONF_LOAD_OWC
Definition internal.h:153
@ ARI_CONF_LOAD_USER
Definition internal.h:152
@ ARI_CONF_LOAD_GENERAL
Definition internal.h:151
#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:1126

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

◆ origin_allowed()

static int origin_allowed ( const char *  origin)
static

Definition at line 283 of file res_ari.c.

284{
286
287 char *allowed = ast_strdupa(general ? general->allowed_origins : "");
288 char *current;
289
290 while ((current = strsep(&allowed, ","))) {
291 if (!strcmp(current, "*")) {
292 return 1;
293 }
294
295 if (!strcmp(current, origin)) {
296 return 1;
297 }
298 }
299
300 return 0;
301}
size_t current

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

Referenced by handle_options(), and process_cors_request().

◆ process_cors_request()

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

Handle CORS headers for simple requests.

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

Definition at line 860 of file res_ari.c.

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

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

1214{
1215 char was_enabled = is_enabled();
1216 int is_now_enabled = 0;
1217
1219
1220 is_now_enabled = is_enabled();
1221
1222 if (was_enabled && !is_now_enabled) {
1223 ast_debug(3, "Disabling ARI\n");
1225 } else if (!was_enabled && is_now_enabled) {
1226 ast_debug(3, "Enabling ARI\n");
1228 }
1229
1231}
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition http.c:771
@ ARI_CONF_LOAD_ALL
Definition internal.h:154
@ ARI_CONF_RELOAD
Definition internal.h:150

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

◆ remove_trailing_slash()

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

Definition at line 502 of file res_ari.c.

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

References ast_ari_response_error(), and ast_strdupa.

Referenced by ast_ari_invoke().

◆ root_handler_create()

static struct stasis_rest_handlers * root_handler_create ( void  )
static

Definition at line 198 of file res_ari.c.

199{
201
202 handler = ao2_alloc(sizeof(*handler), NULL);
203 if (!handler) {
204 return NULL;
205 }
206 handler->path_segment = "ari";
207
208 ao2_ref(handler, +1);
209 return handler;
210}

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

Referenced by load_module().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 1126 of file res_ari.c.

1127{
1129
1131
1132 if (is_enabled()) {
1133 ast_debug(3, "Disabling ARI\n");
1135 }
1136
1138
1142
1144 oom_json = NULL;
1145
1146 return 0;
1147}
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", .load_pri = AST_MODPRI_APP_DEPEND, }
static

Definition at line 1240 of file res_ari.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1240 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:933

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