Asterisk - The Open Source Telephony Project GIT-master-80b953f
Loading...
Searching...
No Matches
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
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 (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}
730
731void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
732 struct ast_ari_response *response)
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}
854
855/*!
856 * \brief Handle CORS headers for simple requests.
857 *
858 * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
859 */
860static void process_cors_request(struct ast_variable *headers,
861 struct ast_ari_response *response)
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}
912
914{
916 return general ? general->format : AST_JSON_COMPACT;
917}
918
919/*!
920 * \internal
921 * \brief ARI HTTP handler.
922 *
923 * This handler takes the HTTP request and turns it into the appropriate
924 * RESTful request (conversion to JSON, routing, etc.)
925 *
926 * \param ser TCP session.
927 * \param urih URI handler.
928 * \param uri URI requested.
929 * \param method HTTP method.
930 * \param get_params HTTP \c GET params.
931 * \param headers HTTP headers.
932 */
934 const struct ast_http_uri *urih,
935 const char *uri,
937 struct ast_variable *get_params,
938 struct ast_variable *headers)
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}
1125
1126static int unload_module(void)
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}
1148
1149static int load_module(void)
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}
1212
1213static int reload_module(void)
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}
1232
1234 .support_level = AST_MODULE_SUPPORT_CORE,
1235 .load = load_module,
1236 .unload = unload_module,
1238 .requires = "http,res_stasis,res_http_websocket,res_websocket_client",
1239 .load_pri = AST_MODPRI_APP_DEPEND,
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
static const char app[]
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, int rdlock)
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 SCOPE_EXIT_EXPR(__expr,...)
#define SCOPE_EXIT(...)
#define ast_trace(level,...)
static char prefix[MAX_PREFIX]
Definition http.c:145
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
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:771
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition http.c:1669
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition http.c:207
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
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition http.c:739
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.
#define ast_variable_new(name, value, filename)
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_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_NOTICE
#define LOG_WARNING
#define ast_verbose(...)
Internal API's for res_ari.
@ ARI_CONF_LOAD_ALL
Definition internal.h:154
@ ARI_CONF_RELOAD
Definition internal.h:150
@ 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
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:127
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:475
void ari_cli_unregister(void)
Unregister CLI commands for ARI.
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
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:933
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:913
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:1213
#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:860
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:1149
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 ast_json * oom_json
Definition res_ari.c:109
static int unload_module(void)
Definition res_ari.c:1126
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:1273
#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
#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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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:58
Per-user configuration options.
Definition internal.h:85
const ast_string_field password
Definition internal.h:90
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 phoneprov_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,...)
#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
#define ast_assert(a)
Definition utils.h:779
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