Asterisk - The Open Source Telephony Project GIT-master-c7a8271
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
res_ari.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2012 - 2013, Digium, Inc.
5 *
6 * David M. Lee, II <dlee@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief HTTP binding for the Stasis API
22 * \author David M. Lee, II <dlee@digium.com>
23 *
24 * The API itself is documented using <a
25 * href="https://developers.helloreverb.com/swagger/">Swagger</a>, a lightweight
26 * mechanism for documenting RESTful API's using JSON. This allows us to use <a
27 * href="https://github.com/wordnik/swagger-ui">swagger-ui</a> to provide
28 * executable documentation for the API, generate client bindings in different
29 * <a href="https://github.com/asterisk/asterisk_rest_libraries">languages</a>,
30 * and generate a lot of the boilerplate code for implementing the RESTful
31 * bindings. The API docs live in the \c rest-api/ directory.
32 *
33 * The RESTful bindings are generated from the Swagger API docs using a set of
34 * <a href="http://mustache.github.io/mustache.5.html">Mustache</a> templates.
35 * The code generator is written in Python, and uses the Python implementation
36 * <a href="https://github.com/defunkt/pystache">pystache</a>. Pystache has no
37 * dependencies, and be installed easily using \c pip. Code generation code
38 * lives in \c rest-api-templates/.
39 *
40 * The generated code reduces a lot of boilerplate when it comes to handling
41 * HTTP requests. It also helps us have greater consistency in the REST API.
42 *
43 * The structure of the generated code is:
44 *
45 * - res/ari/resource_{resource}.h
46 * - For each operation in the resource, a generated argument structure
47 * (holding the parsed arguments from the request) and function
48 * declarations (to implement in res/ari/resource_{resource}.c)
49 * - res_ari_{resource}.c
50 * - A set of \ref stasis_rest_callback functions, which glue the two
51 * together. They parse out path variables and request parameters to
52 * populate a specific \c *_args which is passed to the specific request
53 * handler (in res/ari/resource_{resource}.c)
54 * - A tree of \ref stasis_rest_handlers for routing requests to its
55 * \ref stasis_rest_callback
56 *
57 * The basic flow of an HTTP request is:
58 *
59 * - ast_ari_callback()
60 * 1. Initial request validation
61 * 2. Routes as either a doc request (ast_ari_get_docs) or API
62 * request (ast_ari_invoke)
63 * - ast_ari_invoke()
64 * 1. Further request validation
65 * 2. Routes the request through the tree of generated
66 * \ref stasis_rest_handlers.
67 * 3. Dispatch to the generated callback
68 * - \c ast_ari_*_cb
69 * 1. Populate \c *_args struct with path and get params
70 * 2. Invoke the request handler
71 * 3. Validates and sends response
72 */
73
74/*** MODULEINFO
75 <depend type="module">res_http_websocket</depend>
76 <depend type="module">res_stasis</depend>
77 <depend type="module">res_websocket_client</depend>
78 <support_level>core</support_level>
79 ***/
80
81#include "asterisk.h"
82
83#include "ari/internal.h"
84#include "ari/ari_websockets.h"
85#include "asterisk/ari.h"
86#include "asterisk/astobj2.h"
87#include "asterisk/module.h"
88#include "asterisk/paths.h"
89#include "asterisk/stasis_app.h"
90
91#include <string.h>
92#include <sys/stat.h>
93#include <unistd.h>
94
95/*! \brief Helper function to check if module is enabled. */
96static int is_enabled(void)
97{
99 return general && general->enabled;
100}
101
102/*! Lock for \ref root_handler */
104
105/*! Handler for root RESTful resource. */
107
108/*! Pre-defined message for allocation failures. */
109static struct ast_json *oom_json;
110
111/*! \brief Callback for the root URI. */
112static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
113 const struct ast_http_uri *urih, const char *uri,
114 enum ast_http_method method, struct ast_variable *get_params,
115 struct ast_variable *headers);
116
117static struct ast_http_uri http_uri = {
119 .description = "Asterisk RESTful API",
120 .uri = "ari",
121 .has_subtree = 1,
122 .data = NULL,
123 .key = __FILE__,
124 .no_decode_uri = 1,
125};
126
128{
129 return oom_json;
130}
131
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}
154
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}
190
192{
195 return root_handler;
196}
197
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}
211
213 int response_code,
214 const char *response_text,
215 const char *message_fmt, ...)
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}
228
230 struct ast_json *message)
231{
232 response->message = message;
233 response->response_code = 200;
234 response->response_text = "OK";
235}
236
238{
239 response->message = ast_json_null();
240 response->response_code = 204;
241 response->response_text = "No Content";
242}
243
245{
246 response->message = ast_json_null();
247 response->response_code = 202;
248 response->response_text = "Accepted";
249}
250
252{
253 response->message = ast_json_ref(oom_json);
254 response->response_code = 500;
255 response->response_text = "Internal Server Error";
256}
257
259 const char *url, struct ast_json *message)
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}
267
269 struct ast_ari_response *response)
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}
282
283static int origin_allowed(const char *origin)
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}
302
303#define ACR_METHOD "Access-Control-Request-Method"
304#define ACR_HEADERS "Access-Control-Request-Headers"
305#define ACA_METHODS "Access-Control-Allow-Methods"
306#define ACA_HEADERS "Access-Control-Allow-Headers"
307
308/*!
309 * \brief Handle OPTIONS request, mainly for CORS preflight requests.
310 *
311 * Some browsers will send this prior to non-simple methods (i.e. DELETE).
312 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
313 */
315 struct ast_variable *headers,
316 struct ast_ari_response *response)
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}
443
444/*!
445 * \brief Authenticate a <code>?api_key=userid:password</code>
446 *
447 * \param api_key API key query parameter
448 * \return User object for the authenticated user.
449 * \retval NULL if authentication failed.
450 */
451static struct ari_conf_user *authenticate_api_key(const char *api_key)
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}
470
471/*!
472 * \brief Authenticate an HTTP request.
473 *
474 * \param get_params GET parameters of the request.
475 * \param headers HTTP headers.
476 * \return User object for the authenticated user.
477 * \retval NULL if authentication failed.
478 */
479static struct ari_conf_user *authenticate_user(struct ast_variable *get_params,
480 struct ast_variable *headers)
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}
501
502static void remove_trailing_slash(const char *uri,
503 struct ast_ari_response *response)
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}
527
529 enum ast_ari_invoke_source source, const struct ast_http_uri *urih,
530 const char *uri, enum ast_http_method method,
531 struct ast_variable *get_params, struct ast_variable *headers,
532 struct ast_json *body, struct ast_ari_response *response)
533{
536 struct stasis_rest_handlers *wildcard_handler = NULL;
537 RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
540
541 char *path = ast_strdupa(uri);
542 char *path_segment = NULL;
544 SCOPE_ENTER(3, "Request: %s %s, path:%s\n", ast_get_http_method(method), uri, path);
545
546
547 if (!general) {
548 if (ser && source == ARI_INVOKE_SOURCE_REST) {
550 }
551 ast_ari_response_error(response, 500, "Server Error", "URI handler config missing");
553 response->response_code, response->response_text);
554 }
555
556 user = authenticate_user(get_params, headers);
557
558 if (!user && source == ARI_INVOKE_SOURCE_REST) {
559 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
560 * message is used by an origin server to challenge the
561 * authorization of a user agent. This response MUST include a
562 * WWW-Authenticate header field containing at least one
563 * challenge applicable to the requested resource.
564 */
565 ast_ari_response_error(response, 401, "Unauthorized", "Authentication required");
566
567 /* Section 1.2:
568 * realm = "realm" "=" realm-value
569 * realm-value = quoted-string
570 * Section 2:
571 * challenge = "Basic" realm
572 */
573 ast_str_append(&response->headers, 0,
574 "WWW-Authenticate: Basic realm=\"%s\"\r\n",
575 general->auth_realm);
577 response->response_code, response->response_text);
578 } else if (!ast_fully_booted) {
579 ast_ari_response_error(response, 503, "Service Unavailable", "Asterisk not booted");
581 response->response_code, response->response_text);
582 } else if (user && user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
583 ast_ari_response_error(response, 403, "Forbidden", "Write access denied");
585 response->response_code, response->response_text);
586 } else if (ast_ends_with(uri, "/")) {
587 remove_trailing_slash(uri, response);
589 response->response_code, response->response_text);
590 } else if (ast_begins_with(uri, "api-docs/")) {
591 /* Serving up API docs */
592 if (method != AST_HTTP_GET) {
593 ast_ari_response_error(response, 405, "Method Not Allowed", "Unsupported method");
594 } else {
595 if (urih) {
596 /* Skip the api-docs prefix */
597 ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, response);
598 } else {
599 /*
600 * If we were invoked without a urih, we're probably
601 * being called from the websocket so just use the
602 * default prefix. It's filled in by ast_http_uri_link().
603 */
604 ast_ari_get_docs(strchr(uri, '/') + 1, http_uri.prefix, headers, response);
605 }
606 }
608 response->response_code, response->response_text);
609 }
610
611 root = handler = get_root_handler();
612 ast_assert(root != NULL);
613
614 while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
615 struct stasis_rest_handlers *found_handler = NULL;
616 int i;
617 SCOPE_ENTER(4, "Finding handler for path segment %s\n", path_segment);
618
620
621 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
622 struct stasis_rest_handlers *child = handler->children[i];
623 SCOPE_ENTER(5, "Checking handler path segment %s\n", child->path_segment);
624
625 if (child->is_wildcard) {
626 /* Record the path variable */
627 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
628 path_var->next = path_vars;
629 path_vars = path_var;
630 wildcard_handler = child;
631 ast_trace(-1, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
632
633 } else if (strcmp(child->path_segment, path_segment) == 0) {
634 found_handler = child;
635 ast_trace(-1, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
636 } else {
637 ast_trace(-1, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
638 }
639 SCOPE_EXIT("Done checking %s\n", child->path_segment);
640 }
641
642 if (!found_handler && wildcard_handler) {
643 ast_trace(-1, " No explicit handler found for %s. Using wildcard %s.\n",
644 path_segment, wildcard_handler->path_segment);
645 found_handler = wildcard_handler;
646 wildcard_handler = NULL;
647 }
648
649 if (found_handler == NULL) {
650 /* resource not found */
652 response, 404, "Not Found",
653 "Resource not found");
654 SCOPE_EXIT_EXPR(break, "Handler not found for %s\n", path_segment);
655 } else {
656 handler = found_handler;
657 }
658 SCOPE_EXIT("Done checking %s\n", path_segment);
659 }
660
661 if (handler == NULL || response->response_code == 404) {
662 /* resource not found */
664 response->response_code, response->response_text, uri);
665 }
666
668 if (method == AST_HTTP_OPTIONS) {
669 handle_options(handler, headers, response);
671 }
672
673 if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
674 add_allow_header(handler, response);
676 response, 405, "Method Not Allowed",
677 "Invalid method");
679 response->response_code, response->response_text);
680 }
681
682 if (handler->is_websocket && method == AST_HTTP_GET) {
683 if (source == ARI_INVOKE_SOURCE_WEBSOCKET) {
685 response, 400, "Bad request",
686 "Can't upgrade to websocket from a websocket");
688 response->response_code, response->response_text);
689 }
690 /* WebSocket! */
691 ast_trace(-1, "Handling websocket %s\n", uri);
693 get_params, headers);
694 /* Since the WebSocket code handles the connection, we shouldn't
695 * do anything else; setting no_response */
696 response->no_response = 1;
698 }
699
700 callback = handler->callbacks[method];
701 if (callback == NULL) {
702 add_allow_header(handler, response);
704 response, 405, "Method Not Allowed",
705 "Invalid method");
707 response->response_code, response->response_text);
708 }
709
710 ast_trace(-1, "Running callback: %s\n", uri);
711 callback(ser, get_params, path_vars, headers, body, response);
712 if (response->message == NULL && response->response_code == 0) {
713 /* Really should not happen */
714 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
717 response, 501, "Not Implemented",
718 "Method not implemented");
720 response->response_code, response->response_text);
721 }
722 SCOPE_EXIT_RTN_VALUE(ARI_INVOKE_RESULT_SUCCESS, "Response: %d : %s\n",
723 response->response_code, response->response_text);
724}
725
726void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
727 struct ast_ari_response *response)
728{
729 RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
730 RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
731 RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
732 struct ast_json *obj = NULL;
733 struct ast_variable *host = NULL;
734 struct ast_json_error error = {};
735 struct stat file_stat;
736
737 ast_debug(3, "%s(%s)\n", __func__, uri);
738
739 absolute_path_builder = ast_str_create(80);
740 if (absolute_path_builder == NULL) {
742 return;
743 }
744
745 /* absolute path to the rest-api directory */
746 ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
747 ast_str_append(&absolute_path_builder, 0, "/rest-api/");
748 absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
749 if (absolute_api_dirname == NULL) {
750 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
752 response, 500, "Internal Server Error",
753 "Cannot find rest-api directory");
754 return;
755 }
756
757 /* absolute path to the requested file */
758 ast_str_append(&absolute_path_builder, 0, "%s", uri);
759 absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
760 if (absolute_filename == NULL) {
761 switch (errno) {
762 case ENAMETOOLONG:
763 case ENOENT:
764 case ENOTDIR:
766 response, 404, "Not Found",
767 "Resource not found");
768 break;
769 case EACCES:
771 response, 403, "Forbidden",
772 "Permission denied");
773 break;
774 default:
776 "Error determining real path for uri '%s': %s\n",
777 uri, strerror(errno));
779 response, 500, "Internal Server Error",
780 "Cannot find file");
781 break;
782 }
783 return;
784 }
785
786 if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
787 /* HACKERZ! */
789 "Invalid attempt to access '%s' (not in %s)\n",
790 absolute_filename, absolute_api_dirname);
792 response, 404, "Not Found",
793 "Resource not found");
794 return;
795 }
796
797 if (stat(absolute_filename, &file_stat) == 0) {
798 if (!(file_stat.st_mode & S_IFREG)) {
799 /* Not a file */
801 response, 403, "Forbidden",
802 "Invalid access");
803 return;
804 }
805 } else {
806 /* Does not exist */
808 response, 404, "Not Found",
809 "Resource not found");
810 return;
811 }
812
813 /* Load resource object from file */
814 obj = ast_json_load_new_file(absolute_filename, &error);
815 if (obj == NULL) {
816 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
817 error.source, error.line, error.column, error.text);
819 response, 500, "Internal Server Error",
820 "Yikes! Cannot parse resource");
821 return;
822 }
823
824 /* Update the basePath properly */
825 if (ast_json_object_get(obj, "basePath") != NULL) {
826 for (host = headers; host; host = host->next) {
827 if (strcasecmp(host->name, "Host") == 0) {
828 break;
829 }
830 }
831 if (host != NULL) {
832 if (prefix != NULL && strlen(prefix) > 0) {
834 obj, "basePath",
835 ast_json_stringf("http://%s%s/ari", host->value,prefix));
836 } else {
838 obj, "basePath",
839 ast_json_stringf("http://%s/ari", host->value));
840 }
841 } else {
842 /* Without the host, we don't have the basePath */
843 ast_json_object_del(obj, "basePath");
844 }
845 }
846
847 ast_ari_response_ok(response, obj);
848}
849
850/*!
851 * \brief Handle CORS headers for simple requests.
852 *
853 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
854 */
855static void process_cors_request(struct ast_variable *headers,
856 struct ast_ari_response *response)
857{
858 char const *origin = NULL;
859 struct ast_variable *header;
860
861 /* Parse CORS headers */
862 for (header = headers; header != NULL; header = header->next) {
863 if (strcmp("Origin", header->name) == 0) {
864 origin = header->value;
865 }
866 }
867
868 /* CORS 6.1, #1 - "If the Origin header is not present terminate this
869 * set of steps."
870 */
871 if (origin == NULL) {
872 return;
873 }
874
875 /* CORS 6.1, #2 - "If the value of the Origin header is not a
876 * case-sensitive match for any of the values in list of origins, do not
877 * set any additional headers and terminate this set of steps.
878 *
879 * Note: Always matching is acceptable since the list of origins can be
880 * unbounded."
881 */
882 if (!origin_allowed(origin)) {
883 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
884 return;
885 }
886
887 /* CORS 6.1, #3 - "If the resource supports credentials add a single
888 * Access-Control-Allow-Origin header, with the value of the Origin
889 * header as value, and add a single Access-Control-Allow-Credentials
890 * header with the case-sensitive string "true" as value.
891 *
892 * Otherwise, add a single Access-Control-Allow-Origin header, with
893 * either the value of the Origin header or the string "*" as value."
894 */
895 ast_str_append(&response->headers, 0,
896 "Access-Control-Allow-Origin: %s\r\n", origin);
897 ast_str_append(&response->headers, 0,
898 "Access-Control-Allow-Credentials: true\r\n");
899
900 /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
901 * or more Access-Control-Expose-Headers headers, with as values the
902 * header field names given in the list of exposed headers."
903 *
904 * No exposed headers; skipping
905 */
906}
907
909{
911 return general ? general->format : AST_JSON_COMPACT;
912}
913
914/*!
915 * \internal
916 * \brief ARI HTTP handler.
917 *
918 * This handler takes the HTTP request and turns it into the appropriate
919 * RESTful request (conversion to JSON, routing, etc.)
920 *
921 * \param ser TCP session.
922 * \param urih URI handler.
923 * \param uri URI requested.
924 * \param method HTTP method.
925 * \param get_params HTTP \c GET params.
926 * \param headers HTTP headers.
927 */
929 const struct ast_http_uri *urih,
930 const char *uri,
932 struct ast_variable *get_params,
933 struct ast_variable *headers)
934{
935 RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
936 struct ast_ari_response response = { .fd = -1, 0 };
937 RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
938 struct ast_variable *var;
939 const char *app_name = NULL;
940 RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
941 int debug_app = 0;
943 SCOPE_ENTER(2, "%s: Request: %s %s\n", ast_sockaddr_stringify(&ser->remote_address),
945
946 if (!response_body) {
948 ast_http_error(ser, 500, "Server Error", "Out of memory");
949 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
950 }
951
952 response.headers = ast_str_create(40);
953 if (!response.headers) {
955 ast_http_error(ser, 500, "Server Error", "Out of memory");
956 SCOPE_EXIT_RTN_VALUE(0, "Out of memory\n");
957 }
958
959 process_cors_request(headers, &response);
960
961 /* Process form data from a POST. It could be mixed with query
962 * parameters, which seems a bit odd. But it's allowed, so that's okay
963 * with us.
964 */
965 post_vars = ast_http_get_post_vars(ser, headers);
966 if (!post_vars) {
967 ast_trace(-1, "No post_vars\n");
968 switch (errno) {
969 case EFBIG:
970 ast_ari_response_error(&response, 413,
971 "Request Entity Too Large",
972 "Request body too large");
973 goto request_failed;
974 case ENOMEM:
976 ast_ari_response_error(&response, 500,
977 "Internal Server Error",
978 "Out of memory");
979 goto request_failed;
980 case EIO:
981 ast_ari_response_error(&response, 400,
982 "Bad Request", "Error parsing request body");
983 goto request_failed;
984 }
985
986 /* Look for a JSON request entity only if there were no post_vars.
987 * If there were post_vars, then the request body would already have
988 * been consumed and can not be read again.
989 */
990 ast_trace(-1, "Checking body for vars\n");
991 body = ast_http_get_json(ser, headers);
992 if (!body) {
993 switch (errno) {
994 case EFBIG:
995 ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
996 goto request_failed;
997 case ENOMEM:
998 ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
999 goto request_failed;
1000 case EIO:
1001 ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
1002 goto request_failed;
1003 }
1004 }
1005 }
1006 if (get_params == NULL) {
1007 ast_trace(-1, "No get_params, using post_vars if any\n");
1008 get_params = post_vars;
1009 } else if (get_params && post_vars) {
1010 /* Has both post_vars and get_params */
1011 struct ast_variable *last_var = post_vars;
1012 ast_trace(-1, "Has get_params and post_vars. Merging\n");
1013 while (last_var->next) {
1014 last_var = last_var->next;
1015 }
1016 /* The duped get_params will get freed when post_vars gets
1017 * ast_variables_destroyed.
1018 */
1019 last_var->next = ast_variables_dup(get_params);
1020 get_params = post_vars;
1021 }
1022
1023 /* At this point, get_params will contain post_vars (if any) */
1024 app_name = ast_variable_find_in_list(get_params, "app");
1025 if (!app_name) {
1026 struct ast_json *app = ast_json_object_get(body, "app");
1027
1029 }
1030 ast_trace(-1, "app_name: %s\n", app_name);
1031
1032 /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
1033 * and the global debug flag.
1034 */
1036 if (debug_app) {
1037 struct ast_str *buf = ast_str_create(512);
1039
1040 if (!buf || (body && !str)) {
1042 ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1044 ast_free(buf);
1045 goto request_failed;
1046 }
1047
1048 ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1050 ast_str_append(&buf, 0, "%s %s\n", ast_get_http_method(method), uri);
1051 for (var = headers; var; var = var->next) {
1052 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1053 }
1054 for (var = get_params; var; var = var->next) {
1055 ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1056 }
1057 ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1059 ast_free(buf);
1060 }
1061
1064 urih, uri, method, get_params, headers, body, &response);
1067 }
1068
1069 if (response.no_response) {
1070 /* The handler indicates no further response is necessary.
1071 * Probably because it already handled it */
1072 ast_free(response.headers);
1073 SCOPE_EXIT_RTN_VALUE(0, "No response needed\n");
1074 }
1075
1076request_failed:
1077
1078 /* If you explicitly want to have no content, set message to
1079 * ast_json_null().
1080 */
1081 ast_assert(response.message != NULL);
1082 ast_assert(response.response_code > 0);
1083
1084 /* response.message could be NULL, in which case the empty response_body
1085 * is correct
1086 */
1087 if (response.message && !ast_json_is_null(response.message)) {
1088 ast_str_append(&response.headers, 0,
1089 "Content-type: application/json\r\n");
1090 if (ast_json_dump_str_format(response.message, &response_body,
1091 ast_ari_json_format()) != 0) {
1092 /* Error encoding response */
1093 response.response_code = 500;
1094 response.response_text = "Internal Server Error";
1095 ast_str_set(&response_body, 0, "%s", "");
1096 ast_str_set(&response.headers, 0, "%s", "");
1097 }
1098 }
1099
1100 if (debug_app) {
1101 ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1103 response.response_text, ast_str_buffer(response.headers),
1104 ast_str_buffer(response_body));
1105 }
1106
1107 ast_http_send(ser, method, response.response_code,
1108 response.response_text, response.headers, response_body,
1109 response.fd != -1 ? response.fd : 0, 0);
1110 /* ast_http_send takes ownership, so we don't have to free them */
1111 response_body = NULL;
1112
1113 ast_json_unref(response.message);
1114 if (response.fd >= 0) {
1115 close(response.fd);
1116 }
1117 SCOPE_EXIT_RTN_VALUE(0, "Done. response: %d : %s\n", response.response_code,
1118 response.response_text);
1119}
1120
1121static int unload_module(void)
1122{
1124
1126
1127 if (is_enabled()) {
1128 ast_debug(3, "Disabling ARI\n");
1130 }
1131
1133
1137
1139 oom_json = NULL;
1140
1141 return 0;
1142}
1143
1144static int load_module(void)
1145{
1147
1148 /* root_handler may have been built during a declined load */
1149 if (!root_handler) {
1151 }
1152 if (!root_handler) {
1154 }
1155
1156 /* oom_json may have been built during a declined load */
1157 if (!oom_json) {
1159 "{s: s}", "error", "Allocation failed");
1160 }
1161 if (!oom_json) {
1162 /* Ironic */
1163 unload_module();
1165 }
1166
1167 /*
1168 * ari_websocket_load_module() needs to know if ARI is enabled
1169 * globally so it needs the "general" config to be loaded but it
1170 * also needs to register a sorcery object observer for
1171 * "outbound_websocket" BEFORE the outbound_websocket configs are loaded.
1172 * outbound_websocket in turn needs the users to be loaded so we'll
1173 * initialize sorcery and load "general" and "user" configs first, then
1174 * load the websocket module, then load the "outbound_websocket" configs
1175 * which will fire the observers.
1176 */
1178 unload_module();
1180 }
1181
1183 unload_module();
1185 }
1186
1187 /*
1188 * Now we can load the outbound_websocket configs which will
1189 * fire the observers.
1190 */
1192
1193 if (ari_cli_register() != 0) {
1194 unload_module();
1196 }
1197
1198 if (is_enabled()) {
1199 ast_debug(3, "ARI enabled\n");
1201 } else {
1202 ast_debug(3, "ARI disabled\n");
1203 }
1204
1206}
1207
1208static int reload_module(void)
1209{
1210 char was_enabled = is_enabled();
1211 int is_now_enabled = 0;
1212
1214
1215 is_now_enabled = is_enabled();
1216
1217 if (was_enabled && !is_now_enabled) {
1218 ast_debug(3, "Disabling ARI\n");
1220 } else if (!was_enabled && is_now_enabled) {
1221 ast_debug(3, "Enabling ARI\n");
1223 }
1224
1226}
1227
1229 .support_level = AST_MODULE_SUPPORT_CORE,
1230 .load = load_module,
1231 .unload = unload_module,
1233 .requires = "http,res_stasis,res_http_websocket,res_websocket_client",
1234 .load_pri = AST_MODPRI_APP_DEPEND,
static const char app[]
Definition: app_adsiprog.c:56
const char * str
Definition: app_jack.c:150
ast_mutex_t lock
Definition: app_sla.c:337
static int copy(char *infile, char *outfile)
Utility function to copy a file.
Asterisk RESTful API hooks.
ast_ari_invoke_source
Definition: ari.h:147
@ ARI_INVOKE_SOURCE_REST
Definition: ari.h:148
@ ARI_INVOKE_SOURCE_WEBSOCKET
Definition: ari.h:149
ast_ari_invoke_result
Definition: ari.h:137
@ ARI_INVOKE_RESULT_SUCCESS
Definition: ari.h:138
@ ARI_INVOKE_RESULT_ERROR_CLOSE
Definition: ari.h:140
@ 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
int ari_websocket_load_module(int is_enabled)
int ari_websocket_unload_module(void)
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.
Internal API's for websockets.
#define var
Definition: ast_expr2f.c:605
char * strsep(char **str, const char *delims)
Asterisk main include file. File version handling, generic pbx functions.
void ast_std_free(void *ptr)
Definition: astmm.c:1734
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#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
static PGresult * result
Definition: cel_pgsql.c:84
size_t current
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2206
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_CALL_WITH_RESULT(level, __var, __funcname,...)
#define SCOPE_ENTER(level,...)
#define SCOPE_EXIT_EXPR(__expr,...)
#define SCOPE_EXIT(...)
#define ast_trace(level,...)
static char prefix[MAX_PREFIX]
Definition: http.c:144
void ast_http_send(struct ast_tcptls_session_instance *ser, enum ast_http_method method, int status_code, const char *status_title, struct ast_str *http_header, struct ast_str *out, int fd, unsigned int static_content)
Generic function for sending HTTP/1.1 response.
Definition: http.c:472
struct ast_variable * ast_http_get_post_vars(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get post variables from client Request Entity-Body, if content type is application/x-www-form-urlenco...
Definition: http.c:1405
struct ast_json * ast_http_get_json(struct ast_tcptls_session_instance *ser, struct ast_variable *headers)
Get JSON from client Request Entity-Body, if content type is application/json.
Definition: http.c:1330
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:58
@ AST_HTTP_GET
Definition: http.h:60
@ AST_HTTP_MAX_METHOD
Definition: http.h:66
@ AST_HTTP_OPTIONS
Definition: http.h:65
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:721
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1611
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition: http.c:193
void ast_http_request_close_on_completion(struct ast_tcptls_session_instance *ser)
Request the HTTP connection be closed after this HTTP request.
Definition: http.c:853
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:664
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:689
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
Definition: main/config.c:1013
#define ast_variable_new(name, value, filename)
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:629
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
Internal API's for res_ari.
@ ARI_CONF_LOAD_ALL
Definition: internal.h:151
@ ARI_CONF_RELOAD
Definition: internal.h:147
@ ARI_CONF_INIT
Definition: internal.h:146
@ ARI_CONF_LOAD_OWC
Definition: internal.h:150
@ ARI_CONF_LOAD_USER
Definition: internal.h:149
@ ARI_CONF_LOAD_GENERAL
Definition: internal.h:148
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
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
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
ast_json_encoding_format
Encoding format type.
Definition: json.h:791
@ AST_JSON_COMPACT
Definition: json.h:793
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
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
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
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
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
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
Definition: json.c:273
#define ast_mutex_init(pmutex)
Definition: lock.h:193
#define ast_mutex_unlock(a)
Definition: lock.h:197
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:596
#define ast_mutex_destroy(a)
Definition: lock.h:195
#define ast_mutex_lock(a)
Definition: lock.h:196
int errno
Asterisk module definitions.
@ AST_MODFLAG_LOAD_ORDER
Definition: module.h:331
@ AST_MODFLAG_GLOBAL_SYMBOLS
Definition: module.h:330
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODPRI_APP_DEPEND
Definition: module.h:342
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ 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
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_fully_booted
Definition: options.h:118
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:159
const char * app_name(struct ast_app *app)
Definition: pbx_app.c:463
void ari_cli_unregister(void)
Unregister CLI commands for ARI.
Definition: res/ari/cli.c:703
int ari_cli_register(void)
Register CLI commands for ARI.
Definition: res/ari/cli.c:699
int ari_conf_load(enum ari_conf_load_flags flags)
(Re)load the ARI configuration
struct ari_conf_general * ari_conf_get_general(void)
struct ari_conf_user * ari_conf_validate_user(const char *username, const char *password)
Validated a user's credentials.
void ari_conf_destroy(void)
Destroy the ARI configuration.
static void remove_trailing_slash(const char *uri, struct ast_ari_response *response)
Definition: res_ari.c:502
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:928
static struct stasis_rest_handlers * root_handler_create(void)
Definition: res_ari.c:198
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition: res_ari.c:908
static struct stasis_rest_handlers * root_handler
Definition: res_ari.c:106
static ast_mutex_t root_handler_lock
Definition: res_ari.c:103
static int is_enabled(void)
Helper function to check if module is enabled.
Definition: res_ari.c:96
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.
Definition: res_ari.c:258
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:155
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 int reload_module(void)
Definition: res_ari.c:1208
#define ACA_HEADERS
Definition: res_ari.c:306
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
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
static int origin_allowed(const char *origin)
Definition: res_ari.c:283
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
#define ACA_METHODS
Definition: res_ari.c:305
void ast_ari_response_accepted(struct ast_ari_response *response)
Fill in a Accepted (202) ast_ari_response.
Definition: res_ari.c:244
struct ast_json * ast_ari_oom_json(void)
The stock message to return when out of memory.
Definition: res_ari.c:127
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition: res_ari.c:855
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
#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 int load_module(void)
Definition: res_ari.c:1144
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition: res_ari.c:726
static struct ast_json * oom_json
Definition: res_ari.c:109
static int unload_module(void)
Definition: res_ari.c:1121
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:132
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
static struct stasis_rest_handlers * get_root_handler(void)
Definition: res_ari.c:191
static struct ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
Definition: res_ari.c:451
static char url[512]
static int reload(void)
const char * method
Definition: res_pjsip.c:1279
#define NULL
Definition: resample.c:96
Stasis Application API. See Stasis Application API for detailed documentation.
int stasis_app_get_debug_by_name(const char *app_name)
Get debug status of an application.
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
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
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
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
#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
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
Global configuration options for ARI.
Definition: internal.h:57
Per-user configuration options.
Definition: internal.h:84
const ast_string_field password
Definition: internal.h:89
struct ast_str * headers
Definition: ari.h:105
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
HTTP authentication information.
Definition: http.h:125
Definition of a URI handler.
Definition: http.h:102
ast_http_callback callback
Definition: http.h:107
const char * prefix
Definition: http.h:106
JSON parsing error information.
Definition: json.h:887
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition: lock.h:142
Support for dynamic strings.
Definition: strings.h:623
describes a server instance
Definition: tcptls.h:151
struct ast_sockaddr remote_address
Definition: tcptls.h:153
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
const ast_string_field value
const ast_string_field name
struct header * next
Handler for a single RESTful path segment.
Definition: ari.h:69
const char * path_segment
Definition: ari.h:71
struct stasis_rest_handlers * children[]
Definition: ari.h:95
size_t num_children
Definition: ari.h:93
structure to hold users read from users.conf
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
int error(const char *format,...)
Definition: utils/frame.c:999
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition: utils.c:762
const struct ast_flags ast_uri_http_legacy
Definition: utils.c:720