155 #include <sys/stat.h>
162 return cfg && cfg->general && cfg->general->enabled;
182 size_t old_size, new_size;
186 old_size =
sizeof(*new_handler) + root_handler->
num_children *
sizeof(handler);
187 new_size = old_size +
sizeof(handler);
189 new_handler = ao2_alloc(new_size, NULL);
193 memcpy(new_handler, root_handler, old_size);
194 new_handler->children[new_handler->num_children++] = handler;
196 ao2_cleanup(root_handler);
198 root_handler = new_handler;
209 ast_assert(root_handler != NULL);
211 ast_mutex_lock(&root_handler_lock);
212 size =
sizeof(*new_handler) + root_handler->
num_children *
sizeof(handler);
214 new_handler = ao2_alloc(size, NULL);
216 ast_mutex_unlock(&root_handler_lock);
221 memcpy(new_handler, root_handler,
sizeof(*new_handler));
222 for (i = 0, j = 0; i < root_handler->
num_children; ++i) {
223 if (root_handler->
children[i] == handler) {
231 ao2_cleanup(root_handler);
232 root_handler = new_handler;
234 ast_mutex_unlock(&root_handler_lock);
249 handler = ao2_alloc(
sizeof(*handler), NULL);
253 handler->path_segment =
"ari";
261 const char *response_text,
262 const char *message_fmt, ...)
267 va_start(ap, message_fmt);
330 static int origin_allowed(
const char *origin)
334 char *allowed =
ast_strdupa(cfg->general->allowed_origins);
337 while ((current = strsep(&allowed,
","))) {
338 if (!strcmp(current,
"*")) {
342 if (!strcmp(current, origin)) {
350 #define ACR_METHOD "Access-Control-Request-Method"
351 #define ACR_HEADERS "Access-Control-Request-Headers"
352 #define ACA_METHODS "Access-Control-Allow-Methods"
353 #define ACA_HEADERS "Access-Control-Allow-Headers"
366 char const *acr_method = NULL;
367 char const *acr_headers = NULL;
368 char const *origin = NULL;
375 add_allow_header(handler, response);
379 for (header = headers; header != NULL; header = header->
next) {
380 if (strcmp(ACR_METHOD, header->
name) == 0) {
381 acr_method = header->
value;
382 }
else if (strcmp(ACR_HEADERS, header->
name) == 0) {
383 acr_headers = header->
value;
384 }
else if (strcmp(
"Origin", header->
name) == 0) {
385 origin = header->
value;
392 if (origin == NULL) {
406 if (!origin_allowed(origin)) {
407 ast_log(LOG_NOTICE,
"Origin header '%s' does not match an allowed origin.\n", origin);
415 if (acr_method == NULL) {
422 if (acr_headers == NULL) {
441 if (strcmp(m_str, acr_method) == 0) {
485 if (!ast_strlen_zero(acr_headers)) {
487 ACA_HEADERS, acr_headers);
504 root = handler = get_root_handler();
505 ast_assert(root != NULL);
507 ast_debug(3,
"Finding handler for %s\n", path);
509 while ((path_segment = strsep(&path,
"/")) && (strlen(path_segment) > 0)) {
514 ast_debug(3,
" Finding handler for %s\n", path_segment);
516 for (i = 0; found_handler == NULL && i < handler->
num_children; ++i) {
522 path_var->
next = path_vars;
523 path_vars = path_var;
524 wildcard_handler = child;
527 }
else if (strcmp(child->
path_segment, path_segment) == 0) {
528 found_handler = child;
535 if (!found_handler && wildcard_handler) {
536 ast_debug(3,
" No explicit handler found for %s. Using wildcard %s.\n",
538 found_handler = wildcard_handler;
539 wildcard_handler = NULL;
542 if (found_handler == NULL) {
544 ast_debug(3,
" Handler not found for %s\n", path_segment);
546 response, 404,
"Not Found",
547 "Resource not found");
550 handler = found_handler;
554 ast_assert(handler != NULL);
555 if (method == AST_HTTP_OPTIONS) {
560 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
561 add_allow_header(handler, response);
563 response, 405,
"Method Not Allowed",
568 if (handler->
ws_server && method == AST_HTTP_GET) {
571 get_params, headers);
579 if (callback == NULL) {
580 add_allow_header(handler, response);
582 response, 405,
"Method Not Allowed",
587 callback(ser, get_params, path_vars, headers, body, response);
590 ast_log(LOG_ERROR,
"ARI %s %s not implemented\n",
593 response, 501,
"Not Implemented",
594 "Method not implemented");
598 void ast_ari_get_docs(
const char *uri,
const char *prefix,
struct ast_variable *headers,
602 RAII_VAR(
char *, absolute_api_dirname, NULL, ast_std_free);
603 RAII_VAR(
char *, absolute_filename, NULL, ast_std_free);
607 struct stat file_stat;
612 if (absolute_path_builder == NULL) {
618 ast_str_append(&absolute_path_builder, 0,
"%s", ast_config_AST_DATA_DIR);
620 absolute_api_dirname = realpath(
ast_str_buffer(absolute_path_builder), NULL);
621 if (absolute_api_dirname == NULL) {
622 ast_log(LOG_ERROR,
"Error determining real directory for rest-api\n");
624 response, 500,
"Internal Server Error",
625 "Cannot find rest-api directory");
631 absolute_filename = realpath(
ast_str_buffer(absolute_path_builder), NULL);
632 if (absolute_filename == NULL) {
638 response, 404,
"Not Found",
639 "Resource not found");
643 response, 403,
"Forbidden",
644 "Permission denied");
648 "Error determining real path for uri '%s': %s\n",
649 uri, strerror(errno));
651 response, 500,
"Internal Server Error",
661 "Invalid attempt to access '%s' (not in %s)\n",
662 absolute_filename, absolute_api_dirname);
664 response, 404,
"Not Found",
665 "Resource not found");
669 if (stat(absolute_filename, &file_stat) == 0) {
670 if (!(file_stat.st_mode & S_IFREG)) {
673 response, 403,
"Forbidden",
680 response, 404,
"Not Found",
681 "Resource not found");
688 ast_log(LOG_ERROR,
"Error parsing resource file: %s:%d(%d) %s\n",
691 response, 500,
"Internal Server Error",
692 "Yikes! Cannot parse resource");
698 for (host = headers; host; host = host->
next) {
699 if (strcasecmp(host->
name,
"Host") == 0) {
704 if (prefix != NULL && strlen(prefix) > 0) {
722 static void remove_trailing_slash(
const char *uri,
726 slashless[strlen(slashless) - 1] =
'\0';
745 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
756 char const *origin = NULL;
760 for (header = headers; header != NULL; header = header->
next) {
761 if (strcmp(
"Origin", header->
name) == 0) {
762 origin = header->
value;
769 if (origin == NULL) {
780 if (!origin_allowed(origin)) {
781 ast_log(LOG_NOTICE,
"Origin header '%s' does not match an allowed origin.\n", origin);
794 "Access-Control-Allow-Origin: %s\r\n", origin);
796 "Access-Control-Allow-Credentials: true\r\n");
810 return cfg->general->format;
831 username = strsep(&password,
":");
833 ast_log(LOG_WARNING,
"Invalid api_key\n");
858 http_auth->password);
862 for (v = get_params; v; v = v->
next) {
863 if (strcasecmp(
"api_key", v->
name) == 0) {
902 if (!response_body) {
919 ast_http_error(ser, 500,
"Server Error",
"URI handler config missing");
934 "Request Entity Too Large",
935 "Request body too large");
940 "Internal Server Error",
945 "Bad Request",
"Error parsing request body");
968 if (get_params == NULL) {
969 get_params = post_vars;
970 }
else if (get_params && post_vars) {
973 while (last_var->
next) {
974 last_var = last_var->
next;
980 get_params = post_vars;
999 if (!buf || (body && !str)) {
1004 goto request_failed;
1007 ast_str_append(&buf, 0,
"<--- ARI request received from: %s --->\n",
1010 for (var = headers; var; var = var->
next) {
1013 for (var = get_params; var; var = var->
next) {
1040 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
1042 }
else if (!ast_fully_booted) {
1045 }
else if (
user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
1048 remove_trailing_slash(uri, &response);
1051 if (method != AST_HTTP_GET) {
1055 ast_ari_get_docs(strchr(uri,
'/') + 1, urih->prefix, headers, &response);
1059 ast_ari_invoke(ser, uri, method, get_params, headers, body,
1075 ast_assert(response.
message != NULL);
1083 "Content-type: application/json\r\n");
1095 ast_verbose(
"<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1103 response.
fd != -1 ? response.
fd : 0, 0);
1105 response_body = NULL;
1108 if (response.
fd >= 0) {
1115 .callback = ast_ari_callback,
1116 .description =
"Asterisk RESTful API",
1124 static int unload_module(
void)
1135 ao2_cleanup(root_handler);
1136 root_handler = NULL;
1137 ast_mutex_destroy(&root_handler_lock);
1145 static int load_module(
void)
1147 ast_mutex_init(&root_handler_lock);
1150 if (!root_handler) {
1151 root_handler = root_handler_create();
1153 if (!root_handler) {
1160 "{s: s}",
"error",
"Allocation failed");
1188 static int reload_module(
void)
1207 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
"Asterisk RESTful Interface",
1208 .support_level = AST_MODULE_SUPPORT_CORE,
1209 .load = load_module,
1210 .unload = unload_module,
1211 .reload = reload_module,
1212 .optional_modules =
"res_http_websocket",
1213 .requires =
"http,res_stasis",
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
void ast_ari_config_destroy(void)
Destroy the ARI configuration.
struct ast_variable * next
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
void ast_ari_response_ok(struct ast_ari_response *response, struct ast_json *message)
Fill in an OK (200) ast_ari_response.
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.
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
char text[AST_JSON_ERROR_TEXT_LENGTH]
struct ast_ari_conf * ast_ari_config_get(void)
Get the current ARI configuration.
void ari_handle_websocket(struct ast_websocket_server *ws_server, 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.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
Checks whether a string ends with another.
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
int ast_json_object_del(struct ast_json *object, const char *key)
Delete a field from a JSON object.
void ast_ari_response_alloc_failed(struct ast_ari_response *response)
Fill in response with a 500 message for allocation failures.
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
All configuration options for ARI.
Structure for variables, used for configurations and for channel variables.
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
struct ast_json * ast_json_stringf(const char *format,...)
Create a JSON string, printf style.
Asterisk RESTful API hooks.
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.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
int ast_ari_config_reload(void)
Reload the ARI configuration.
static int copy(char *infile, char *outfile)
Utility function to copy a file.
#define ast_strdup(str)
A wrapper for strdup()
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
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.
JSON parsing error information.
int ast_json_object_set(struct ast_json *object, const char *key, struct ast_json *value)
Set a field in a JSON object.
int stasis_app_get_debug_by_name(const char *app_name)
Get debug status of an application.
All configuration options for http media cache.
struct ast_json * ast_json_null(void)
Get the JSON null value.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
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.
struct conf_general_options * general
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Asterisk file paths, configured in asterisk.conf.
#define ast_strdupa(s)
duplicate a string in memory from the stack
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
static struct stasis_rest_handlers * root_handler
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.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
int ast_ari_cli_register(void)
Register CLI commands for ARI.
#define ast_debug(level,...)
Log a DEBUG message.
struct ast_json * ast_json_vstringf(const char *format, va_list args)
Create a JSON string, vprintf style.
static struct ast_json * oom_json
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
char * ast_json_dump_string_format(struct ast_json *root, enum ast_json_encoding_format format)
Encode a JSON value to a string.
describes a server instance
struct ast_websocket_server * ws_server
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.
Per-user configuration options.
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.
Support for dynamic strings.
Internal API's for res_ari.
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
char password[ARI_PASSWORD_LEN]
int ast_ari_config_init(void)
Initialize the ARI configuration.
const char * app_name(struct ast_app *app)
struct ast_json * ast_ari_oom_json(void)
The stock message to return when out of memory.
const char * response_text
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.
Module has failed to load, may be in an inconsistent state.
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
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.
static struct ast_ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
structure to hold users read from users.conf
struct ast_json * message
static int is_enabled(void)
Helper function to check if module is enabled.
char source[AST_JSON_ERROR_TEXT_LENGTH]
const char * path_segment
HTTP authentication information.
struct stasis_rest_handlers * children[]
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
void ast_ari_cli_unregister(void)
Unregister CLI commands for ARI.
static struct ast_ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers)
Authenticate an HTTP request.
Definition of a URI handler.
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...
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
stasis_rest_callback callbacks[AST_HTTP_MAX_METHOD]
static ast_mutex_t root_handler_lock
Abstract JSON element (object, array, string, int, ...).
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.
struct ast_ari_conf_user * ast_ari_config_validate_user(const char *username, const char *password)
Validated a user's credentials.
Stasis Application API. See Stasis Application API for detailed documentation.
ast_http_method
HTTP Request methods known by Asterisk.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Asterisk module definitions.
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Handler for a single RESTful path segment.
Structure for mutex and tracking information.
void ast_ari_response_accepted(struct ast_ari_response *response)
Fill in a Accepted (202) ast_ari_response.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
ast_json_encoding_format
Encoding format type.