Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
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 <[email protected]>
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 resouce, 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  <support_level>core</support_level>
78  ***/
79 
80 /*** DOCUMENTATION
81  <configInfo name="res_ari" language="en_US">
82  <synopsis>HTTP binding for the Stasis API</synopsis>
83  <configFile name="ari.conf">
84  <configObject name="general">
85  <synopsis>General configuration settings</synopsis>
86  <configOption name="enabled">
87  <synopsis>Enable/disable the ARI module</synopsis>
88  <description>
89  <para>This option enables or disables the ARI module.</para>
90  <note>
91  <para>ARI uses Asterisk's HTTP server, which must also be enabled in <filename>http.conf</filename>.</para>
92  </note>
93  </description>
94  <see-also>
95  <ref type="filename">http.conf</ref>
96  <ref type="link">https://wiki.asterisk.org/wiki/display/AST/Asterisk+Builtin+mini-HTTP+Server</ref>
97  </see-also>
98  </configOption>
99  <configOption name="websocket_write_timeout">
100  <synopsis>The timeout (in milliseconds) to set on WebSocket connections.</synopsis>
101  <description>
102  <para>If a websocket connection accepts input slowly, the timeout
103  for writes to it can be increased to keep it from being disconnected.
104  Value is in milliseconds; default is 100 ms.</para>
105  </description>
106  </configOption>
107  <configOption name="pretty">
108  <synopsis>Responses from ARI are formatted to be human readable</synopsis>
109  </configOption>
110  <configOption name="auth_realm">
111  <synopsis>Realm to use for authentication. Defaults to Asterisk REST Interface.</synopsis>
112  </configOption>
113  <configOption name="allowed_origins">
114  <synopsis>Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins.</synopsis>
115  </configOption>
116  <configOption name="channelvars">
117  <synopsis>Comma separated list of channel variables to display in channel json.</synopsis>
118  </configOption>
119  </configObject>
120 
121  <configObject name="user">
122  <synopsis>Per-user configuration settings</synopsis>
123  <configOption name="type">
124  <synopsis>Define this configuration section as a user.</synopsis>
125  <description>
126  <enumlist>
127  <enum name="user"><para>Configure this section as a <replaceable>user</replaceable></para></enum>
128  </enumlist>
129  </description>
130  </configOption>
131  <configOption name="read_only">
132  <synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
133  </configOption>
134  <configOption name="password">
135  <synopsis>Crypted or plaintext password (see password_format)</synopsis>
136  </configOption>
137  <configOption name="password_format">
138  <synopsis>password_format may be set to plain (the default) or crypt. When set to crypt, crypt(3) is used to validate the password. A crypted password can be generated using mkpasswd -m sha-512. When set to plain, the password is in plaintext</synopsis>
139  </configOption>
140  </configObject>
141  </configFile>
142  </configInfo>
143 ***/
144 
145 #include "asterisk.h"
146 
147 #include "ari/internal.h"
148 #include "asterisk/ari.h"
149 #include "asterisk/astobj2.h"
150 #include "asterisk/module.h"
151 #include "asterisk/paths.h"
152 #include "asterisk/stasis_app.h"
153 
154 #include <string.h>
155 #include <sys/stat.h>
156 #include <unistd.h>
157 
158 /*! \brief Helper function to check if module is enabled. */
159 static int is_enabled(void)
160 {
162  return cfg && cfg->general && cfg->general->enabled;
163 }
164 
165 /*! Lock for \ref root_handler */
167 
168 /*! Handler for root RESTful resource. */
170 
171 /*! Pre-defined message for allocation failures. */
172 static struct ast_json *oom_json;
173 
175 {
176  return oom_json;
177 }
178 
180 {
181  RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
182  size_t old_size, new_size;
183 
184  SCOPED_MUTEX(lock, &root_handler_lock);
185 
186  old_size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
187  new_size = old_size + sizeof(handler);
188 
189  new_handler = ao2_alloc(new_size, NULL);
190  if (!new_handler) {
191  return -1;
192  }
193  memcpy(new_handler, root_handler, old_size);
194  new_handler->children[new_handler->num_children++] = handler;
195 
196  ao2_cleanup(root_handler);
197  ao2_ref(new_handler, +1);
198  root_handler = new_handler;
199  return 0;
200 }
201 
203 {
204  struct stasis_rest_handlers *new_handler;
205  size_t size;
206  size_t i;
207  size_t j;
208 
209  ast_assert(root_handler != NULL);
210 
211  ast_mutex_lock(&root_handler_lock);
212  size = sizeof(*new_handler) + root_handler->num_children * sizeof(handler);
213 
214  new_handler = ao2_alloc(size, NULL);
215  if (!new_handler) {
216  ast_mutex_unlock(&root_handler_lock);
217  return -1;
218  }
219 
220  /* Create replacement root_handler less the handler to remove. */
221  memcpy(new_handler, root_handler, sizeof(*new_handler));
222  for (i = 0, j = 0; i < root_handler->num_children; ++i) {
223  if (root_handler->children[i] == handler) {
224  continue;
225  }
226  new_handler->children[j++] = root_handler->children[i];
227  }
228  new_handler->num_children = j;
229 
230  /* Replace the old root_handler with the new. */
231  ao2_cleanup(root_handler);
232  root_handler = new_handler;
233 
234  ast_mutex_unlock(&root_handler_lock);
235  return 0;
236 }
237 
239 {
240  SCOPED_MUTEX(lock, &root_handler_lock);
241  ao2_ref(root_handler, +1);
242  return root_handler;
243 }
244 
246 {
248 
249  handler = ao2_alloc(sizeof(*handler), NULL);
250  if (!handler) {
251  return NULL;
252  }
253  handler->path_segment = "ari";
254 
255  ao2_ref(handler, +1);
256  return handler;
257 }
258 
260  int response_code,
261  const char *response_text,
262  const char *message_fmt, ...)
263 {
265  va_list ap;
266 
267  va_start(ap, message_fmt);
268  message = ast_json_vstringf(message_fmt, ap);
269  va_end(ap);
270  response->message = ast_json_pack("{s: o}",
271  "message", ast_json_ref(message));
272  response->response_code = response_code;
273  response->response_text = response_text;
274 }
275 
276 void ast_ari_response_ok(struct ast_ari_response *response,
277  struct ast_json *message)
278 {
279  response->message = message;
280  response->response_code = 200;
281  response->response_text = "OK";
282 }
283 
285 {
286  response->message = ast_json_null();
287  response->response_code = 204;
288  response->response_text = "No Content";
289 }
290 
292 {
293  response->message = ast_json_null();
294  response->response_code = 202;
295  response->response_text = "Accepted";
296 }
297 
299 {
300  response->message = ast_json_ref(oom_json);
301  response->response_code = 500;
302  response->response_text = "Internal Server Error";
303 }
304 
306  const char *url, struct ast_json *message)
307 {
309  response->message = message;
310  response->response_code = 201;
311  response->response_text = "Created";
312  ast_str_append(&response->headers, 0, "Location: /%s%s\r\n", root->path_segment, url);
313 }
314 
316  struct ast_ari_response *response)
317 {
318  enum ast_http_method m;
319  ast_str_append(&response->headers, 0,
320  "Allow: OPTIONS");
321  for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
322  if (handler->callbacks[m] != NULL) {
323  ast_str_append(&response->headers, 0,
324  ",%s", ast_get_http_method(m));
325  }
326  }
327  ast_str_append(&response->headers, 0, "\r\n");
328 }
329 
330 static int origin_allowed(const char *origin)
331 {
333 
334  char *allowed = ast_strdupa(cfg->general->allowed_origins);
335  char *current;
336 
337  while ((current = strsep(&allowed, ","))) {
338  if (!strcmp(current, "*")) {
339  return 1;
340  }
341 
342  if (!strcmp(current, origin)) {
343  return 1;
344  }
345  }
346 
347  return 0;
348 }
349 
350 #define ACR_METHOD "Access-Control-Request-Method"
351 #define ACR_HEADERS "Access-Control-Request-Headers"
352 #define ACA_METHODS "Access-Control-Allow-Methods"
353 #define ACA_HEADERS "Access-Control-Allow-Headers"
354 
355 /*!
356  * \brief Handle OPTIONS request, mainly for CORS preflight requests.
357  *
358  * Some browsers will send this prior to non-simple methods (i.e. DELETE).
359  * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
360  */
362  struct ast_variable *headers,
363  struct ast_ari_response *response)
364 {
365  struct ast_variable *header;
366  char const *acr_method = NULL;
367  char const *acr_headers = NULL;
368  char const *origin = NULL;
369 
370  RAII_VAR(struct ast_str *, allow, NULL, ast_free);
371  enum ast_http_method m;
372  int allowed = 0;
373 
374  /* Regular OPTIONS response */
375  add_allow_header(handler, response);
376  ast_ari_response_no_content(response);
377 
378  /* Parse CORS headers */
379  for (header = headers; header != NULL; header = header->next) {
380  if (strcmp(ACR_METHOD, header->name) == 0) {
381  acr_method = header->value;
382  } else if (strcmp(ACR_HEADERS, header->name) == 0) {
383  acr_headers = header->value;
384  } else if (strcmp("Origin", header->name) == 0) {
385  origin = header->value;
386  }
387  }
388 
389  /* CORS 6.2, #1 - "If the Origin header is not present terminate this
390  * set of steps."
391  */
392  if (origin == NULL) {
393  return;
394  }
395 
396  /* CORS 6.2, #2 - "If the value of the Origin header is not a
397  * case-sensitive match for any of the values in list of origins do not
398  * set any additional headers and terminate this set of steps.
399  *
400  * Always matching is acceptable since the list of origins can be
401  * unbounded.
402  *
403  * The Origin header can only contain a single origin as the user agent
404  * will not follow redirects."
405  */
406  if (!origin_allowed(origin)) {
407  ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
408  return;
409  }
410 
411  /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
412  * or if parsing failed, do not set any additional headers and terminate
413  * this set of steps."
414  */
415  if (acr_method == NULL) {
416  return;
417  }
418 
419  /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
420  * headers let header field-names be the empty list."
421  */
422  if (acr_headers == NULL) {
423  acr_headers = "";
424  }
425 
426  /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
427  * the values in list of methods do not set any additional headers and
428  * terminate this set of steps."
429  */
430  allow = ast_str_create(20);
431 
432  if (!allow) {
434  return;
435  }
436 
437  /* Go ahead and build the ACA_METHODS header at the same time */
438  for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
439  if (handler->callbacks[m] != NULL) {
440  char const *m_str = ast_get_http_method(m);
441  if (strcmp(m_str, acr_method) == 0) {
442  allowed = 1;
443  }
444  ast_str_append(&allow, 0, ",%s", m_str);
445  }
446  }
447 
448  if (!allowed) {
449  return;
450  }
451 
452  /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
453  * case-insensitive match for any of the values in list of headers do
454  * not set any additional headers and terminate this set of steps.
455  *
456  * Note: Always matching is acceptable since the list of headers can be
457  * unbounded."
458  */
459 
460  /* CORS 6.2 #7 - "If the resource supports credentials add a single
461  * Access-Control-Allow-Origin header, with the value of the Origin
462  * header as value, and add a single Access-Control-Allow-Credentials
463  * header with the case-sensitive string "true" as value."
464  *
465  * Added by process_cors_request() earlier in the request.
466  */
467 
468  /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
469  * header..."
470  */
471 
472  /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
473  * consisting of (a subset of) the list of methods."
474  */
475  ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
476  ACA_METHODS, ast_str_buffer(allow));
477 
478 
479  /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
480  * consisting of (a subset of) the list of headers.
481  *
482  * Since the list of headers can be unbounded simply returning headers
483  * can be enough."
484  */
485  if (!ast_strlen_zero(acr_headers)) {
486  ast_str_append(&response->headers, 0, "%s: %s\r\n",
487  ACA_HEADERS, acr_headers);
488  }
489 }
490 
492  const char *uri, enum ast_http_method method,
493  struct ast_variable *get_params, struct ast_variable *headers,
494  struct ast_json *body, struct ast_ari_response *response)
495 {
496  RAII_VAR(struct stasis_rest_handlers *, root, NULL, ao2_cleanup);
498  struct stasis_rest_handlers *wildcard_handler = NULL;
499  RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
500  char *path = ast_strdupa(uri);
501  char *path_segment;
502  stasis_rest_callback callback;
503 
504  root = handler = get_root_handler();
505  ast_assert(root != NULL);
506 
507  ast_debug(3, "Finding handler for %s\n", path);
508 
509  while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
510  struct stasis_rest_handlers *found_handler = NULL;
511  int i;
512 
513  ast_uri_decode(path_segment, ast_uri_http_legacy);
514  ast_debug(3, " Finding handler for %s\n", path_segment);
515 
516  for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
517  struct stasis_rest_handlers *child = handler->children[i];
518 
519  if (child->is_wildcard) {
520  /* Record the path variable */
521  struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
522  path_var->next = path_vars;
523  path_vars = path_var;
524  wildcard_handler = child;
525  ast_debug(3, " Checking %s %s: Matched wildcard.\n", handler->path_segment, child->path_segment);
526 
527  } else if (strcmp(child->path_segment, path_segment) == 0) {
528  found_handler = child;
529  ast_debug(3, " Checking %s %s: Explicit match with %s\n", handler->path_segment, child->path_segment, path_segment);
530  } else {
531  ast_debug(3, " Checking %s %s: Didn't match %s\n", handler->path_segment, child->path_segment, path_segment);
532  }
533  }
534 
535  if (!found_handler && wildcard_handler) {
536  ast_debug(3, " No explicit handler found for %s. Using wildcard %s.\n",
537  path_segment, wildcard_handler->path_segment);
538  found_handler = wildcard_handler;
539  wildcard_handler = NULL;
540  }
541 
542  if (found_handler == NULL) {
543  /* resource not found */
544  ast_debug(3, " Handler not found for %s\n", path_segment);
546  response, 404, "Not Found",
547  "Resource not found");
548  return;
549  } else {
550  handler = found_handler;
551  }
552  }
553 
554  ast_assert(handler != NULL);
555  if (method == AST_HTTP_OPTIONS) {
556  handle_options(handler, headers, response);
557  return;
558  }
559 
560  if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
561  add_allow_header(handler, response);
563  response, 405, "Method Not Allowed",
564  "Invalid method");
565  return;
566  }
567 
568  if (handler->ws_server && method == AST_HTTP_GET) {
569  /* WebSocket! */
570  ari_handle_websocket(handler->ws_server, ser, uri, method,
571  get_params, headers);
572  /* Since the WebSocket code handles the connection, we shouldn't
573  * do anything else; setting no_response */
574  response->no_response = 1;
575  return;
576  }
577 
578  callback = handler->callbacks[method];
579  if (callback == NULL) {
580  add_allow_header(handler, response);
582  response, 405, "Method Not Allowed",
583  "Invalid method");
584  return;
585  }
586 
587  callback(ser, get_params, path_vars, headers, body, response);
588  if (response->message == NULL && response->response_code == 0) {
589  /* Really should not happen */
590  ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
591  ast_get_http_method(method), uri);
593  response, 501, "Not Implemented",
594  "Method not implemented");
595  }
596 }
597 
598 void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers,
599  struct ast_ari_response *response)
600 {
601  RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
602  RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
603  RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
604  struct ast_json *obj = NULL;
605  struct ast_variable *host = NULL;
606  struct ast_json_error error = {};
607  struct stat file_stat;
608 
609  ast_debug(3, "%s(%s)\n", __func__, uri);
610 
611  absolute_path_builder = ast_str_create(80);
612  if (absolute_path_builder == NULL) {
614  return;
615  }
616 
617  /* absolute path to the rest-api directory */
618  ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
619  ast_str_append(&absolute_path_builder, 0, "/rest-api/");
620  absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
621  if (absolute_api_dirname == NULL) {
622  ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
624  response, 500, "Internal Server Error",
625  "Cannot find rest-api directory");
626  return;
627  }
628 
629  /* absolute path to the requested file */
630  ast_str_append(&absolute_path_builder, 0, "%s", uri);
631  absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
632  if (absolute_filename == NULL) {
633  switch (errno) {
634  case ENAMETOOLONG:
635  case ENOENT:
636  case ENOTDIR:
638  response, 404, "Not Found",
639  "Resource not found");
640  break;
641  case EACCES:
643  response, 403, "Forbidden",
644  "Permission denied");
645  break;
646  default:
648  "Error determining real path for uri '%s': %s\n",
649  uri, strerror(errno));
651  response, 500, "Internal Server Error",
652  "Cannot find file");
653  break;
654  }
655  return;
656  }
657 
658  if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
659  /* HACKERZ! */
661  "Invalid attempt to access '%s' (not in %s)\n",
662  absolute_filename, absolute_api_dirname);
664  response, 404, "Not Found",
665  "Resource not found");
666  return;
667  }
668 
669  if (stat(absolute_filename, &file_stat) == 0) {
670  if (!(file_stat.st_mode & S_IFREG)) {
671  /* Not a file */
673  response, 403, "Forbidden",
674  "Invalid access");
675  return;
676  }
677  } else {
678  /* Does not exist */
680  response, 404, "Not Found",
681  "Resource not found");
682  return;
683  }
684 
685  /* Load resource object from file */
686  obj = ast_json_load_new_file(absolute_filename, &error);
687  if (obj == NULL) {
688  ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
689  error.source, error.line, error.column, error.text);
691  response, 500, "Internal Server Error",
692  "Yikes! Cannot parse resource");
693  return;
694  }
695 
696  /* Update the basePath properly */
697  if (ast_json_object_get(obj, "basePath") != NULL) {
698  for (host = headers; host; host = host->next) {
699  if (strcasecmp(host->name, "Host") == 0) {
700  break;
701  }
702  }
703  if (host != NULL) {
704  if (prefix != NULL && strlen(prefix) > 0) {
706  obj, "basePath",
707  ast_json_stringf("http://%s%s/ari", host->value,prefix));
708  } else {
710  obj, "basePath",
711  ast_json_stringf("http://%s/ari", host->value));
712  }
713  } else {
714  /* Without the host, we don't have the basePath */
715  ast_json_object_del(obj, "basePath");
716  }
717  }
718 
719  ast_ari_response_ok(response, obj);
720 }
721 
722 static void remove_trailing_slash(const char *uri,
723  struct ast_ari_response *response)
724 {
725  char *slashless = ast_strdupa(uri);
726  slashless[strlen(slashless) - 1] = '\0';
727 
728  /* While it's tempting to redirect the client to the slashless URL,
729  * that is problematic. A 302 Found is the most appropriate response,
730  * but most clients issue a GET on the location you give them,
731  * regardless of the method of the original request.
732  *
733  * While there are some ways around this, it gets into a lot of client
734  * specific behavior and corner cases in the HTTP standard. There's also
735  * very little practical benefit of redirecting; only GET and HEAD can
736  * be redirected automagically; all other requests "MUST NOT
737  * automatically redirect the request unless it can be confirmed by the
738  * user, since this might change the conditions under which the request
739  * was issued."
740  *
741  * Given all of that, a 404 with a nice message telling them what to do
742  * is probably our best bet.
743  */
744  ast_ari_response_error(response, 404, "Not Found",
745  "ARI URLs do not end with a slash. Try /ari/%s", slashless);
746 }
747 
748 /*!
749  * \brief Handle CORS headers for simple requests.
750  *
751  * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
752  */
753 static void process_cors_request(struct ast_variable *headers,
754  struct ast_ari_response *response)
755 {
756  char const *origin = NULL;
757  struct ast_variable *header;
758 
759  /* Parse CORS headers */
760  for (header = headers; header != NULL; header = header->next) {
761  if (strcmp("Origin", header->name) == 0) {
762  origin = header->value;
763  }
764  }
765 
766  /* CORS 6.1, #1 - "If the Origin header is not present terminate this
767  * set of steps."
768  */
769  if (origin == NULL) {
770  return;
771  }
772 
773  /* CORS 6.1, #2 - "If the value of the Origin header is not a
774  * case-sensitive match for any of the values in list of origins, do not
775  * set any additional headers and terminate this set of steps.
776  *
777  * Note: Always matching is acceptable since the list of origins can be
778  * unbounded."
779  */
780  if (!origin_allowed(origin)) {
781  ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
782  return;
783  }
784 
785  /* CORS 6.1, #3 - "If the resource supports credentials add a single
786  * Access-Control-Allow-Origin header, with the value of the Origin
787  * header as value, and add a single Access-Control-Allow-Credentials
788  * header with the case-sensitive string "true" as value.
789  *
790  * Otherwise, add a single Access-Control-Allow-Origin header, with
791  * either the value of the Origin header or the string "*" as value."
792  */
793  ast_str_append(&response->headers, 0,
794  "Access-Control-Allow-Origin: %s\r\n", origin);
795  ast_str_append(&response->headers, 0,
796  "Access-Control-Allow-Credentials: true\r\n");
797 
798  /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
799  * or more Access-Control-Expose-Headers headers, with as values the
800  * header field names given in the list of exposed headers."
801  *
802  * No exposed headers; skipping
803  */
804 }
805 
807 {
808  RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
809  cfg = ast_ari_config_get();
810  return cfg->general->format;
811 }
812 
813 /*!
814  * \brief Authenticate a <code>?api_key=userid:password</code>
815  *
816  * \param api_key API key query parameter
817  * \return User object for the authenticated user.
818  * \return \c NULL if authentication failed.
819  */
820 static struct ast_ari_conf_user *authenticate_api_key(const char *api_key)
821 {
822  RAII_VAR(char *, copy, NULL, ast_free);
823  char *username;
824  char *password;
825 
826  password = copy = ast_strdup(api_key);
827  if (!copy) {
828  return NULL;
829  }
830 
831  username = strsep(&password, ":");
832  if (!password) {
833  ast_log(LOG_WARNING, "Invalid api_key\n");
834  return NULL;
835  }
836 
837  return ast_ari_config_validate_user(username, password);
838 }
839 
840 /*!
841  * \brief Authenticate an HTTP request.
842  *
843  * \param get_params GET parameters of the request.
844  * \param header HTTP headers.
845  * \return User object for the authenticated user.
846  * \return \c NULL if authentication failed.
847  */
848 static struct ast_ari_conf_user *authenticate_user(struct ast_variable *get_params,
849  struct ast_variable *headers)
850 {
851  RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
852  struct ast_variable *v;
853 
854  /* HTTP Basic authentication */
855  http_auth = ast_http_get_auth(headers);
856  if (http_auth) {
857  return ast_ari_config_validate_user(http_auth->userid,
858  http_auth->password);
859  }
860 
861  /* ?api_key authentication */
862  for (v = get_params; v; v = v->next) {
863  if (strcasecmp("api_key", v->name) == 0) {
864  return authenticate_api_key(v->value);
865  }
866  }
867 
868  return NULL;
869 }
870 
871 /*!
872  * \internal
873  * \brief ARI HTTP handler.
874  *
875  * This handler takes the HTTP request and turns it into the appropriate
876  * RESTful request (conversion to JSON, routing, etc.)
877  *
878  * \param ser TCP session.
879  * \param urih URI handler.
880  * \param uri URI requested.
881  * \param method HTTP method.
882  * \param get_params HTTP \c GET params.
883  * \param headers HTTP headers.
884  */
886  const struct ast_http_uri *urih,
887  const char *uri,
888  enum ast_http_method method,
889  struct ast_variable *get_params,
890  struct ast_variable *headers)
891 {
893  RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
895  struct ast_ari_response response = { .fd = -1, 0 };
896  RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
897  struct ast_variable *var;
898  const char *app_name = NULL;
899  RAII_VAR(struct ast_json *, body, ast_json_null(), ast_json_unref);
900  int debug_app = 0;
901 
902  if (!response_body) {
904  ast_http_error(ser, 500, "Server Error", "Out of memory");
905  return 0;
906  }
907 
908  response.headers = ast_str_create(40);
909  if (!response.headers) {
911  ast_http_error(ser, 500, "Server Error", "Out of memory");
912  return 0;
913  }
914 
916  if (!conf || !conf->general) {
917  ast_free(response.headers);
919  ast_http_error(ser, 500, "Server Error", "URI handler config missing");
920  return 0;
921  }
922 
923  process_cors_request(headers, &response);
924 
925  /* Process form data from a POST. It could be mixed with query
926  * parameters, which seems a bit odd. But it's allowed, so that's okay
927  * with us.
928  */
929  post_vars = ast_http_get_post_vars(ser, headers);
930  if (!post_vars) {
931  switch (errno) {
932  case EFBIG:
933  ast_ari_response_error(&response, 413,
934  "Request Entity Too Large",
935  "Request body too large");
936  goto request_failed;
937  case ENOMEM:
939  ast_ari_response_error(&response, 500,
940  "Internal Server Error",
941  "Out of memory");
942  goto request_failed;
943  case EIO:
944  ast_ari_response_error(&response, 400,
945  "Bad Request", "Error parsing request body");
946  goto request_failed;
947  }
948 
949  /* Look for a JSON request entity only if there were no post_vars.
950  * If there were post_vars, then the request body would already have
951  * been consumed and can not be read again.
952  */
953  body = ast_http_get_json(ser, headers);
954  if (!body) {
955  switch (errno) {
956  case EFBIG:
957  ast_ari_response_error(&response, 413, "Request Entity Too Large", "Request body too large");
958  goto request_failed;
959  case ENOMEM:
960  ast_ari_response_error(&response, 500, "Internal Server Error", "Error processing request");
961  goto request_failed;
962  case EIO:
963  ast_ari_response_error(&response, 400, "Bad Request", "Error parsing request body");
964  goto request_failed;
965  }
966  }
967  }
968  if (get_params == NULL) {
969  get_params = post_vars;
970  } else if (get_params && post_vars) {
971  /* Has both post_vars and get_params */
972  struct ast_variable *last_var = post_vars;
973  while (last_var->next) {
974  last_var = last_var->next;
975  }
976  /* The duped get_params will get freed when post_vars gets
977  * ast_variables_destroyed.
978  */
979  last_var->next = ast_variables_dup(get_params);
980  get_params = post_vars;
981  }
982 
983  /* At this point, get_params will contain post_vars (if any) */
984  app_name = ast_variable_find_in_list(get_params, "app");
985  if (!app_name) {
986  struct ast_json *app = ast_json_object_get(body, "app");
987 
988  app_name = (app ? ast_json_string_get(app) : NULL);
989  }
990 
991  /* stasis_app_get_debug_by_name returns an "||" of the app's debug flag
992  * and the global debug flag.
993  */
994  debug_app = stasis_app_get_debug_by_name(app_name);
995  if (debug_app) {
996  struct ast_str *buf = ast_str_create(512);
998 
999  if (!buf || (body && !str)) {
1001  ast_ari_response_error(&response, 500, "Server Error", "Out of memory");
1002  ast_json_free(str);
1003  ast_free(buf);
1004  goto request_failed;
1005  }
1006 
1007  ast_str_append(&buf, 0, "<--- ARI request received from: %s --->\n",
1009  for (var = headers; var; var = var->next) {
1010  ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1011  }
1012  for (var = get_params; var; var = var->next) {
1013  ast_str_append(&buf, 0, "%s: %s\n", var->name, var->value);
1014  }
1015  ast_verbose("%sbody:\n%s\n\n", ast_str_buffer(buf), S_OR(str, ""));
1016  ast_json_free(str);
1017  ast_free(buf);
1018  }
1019 
1020  user = authenticate_user(get_params, headers);
1021  if (response.response_code > 0) {
1022  /* POST parameter processing error. Do nothing. */
1023  } else if (!user) {
1024  /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
1025  * message is used by an origin server to challenge the
1026  * authorization of a user agent. This response MUST include a
1027  * WWW-Authenticate header field containing at least one
1028  * challenge applicable to the requested resource.
1029  */
1030  ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required");
1031 
1032  /* Section 1.2:
1033  * realm = "realm" "=" realm-value
1034  * realm-value = quoted-string
1035  * Section 2:
1036  * challenge = "Basic" realm
1037  */
1038  ast_str_append(&response.headers, 0,
1039  "WWW-Authenticate: Basic realm=\"%s\"\r\n",
1040  conf->general->auth_realm);
1041  } else if (!ast_fully_booted) {
1043  ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted");
1044  } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
1045  ast_ari_response_error(&response, 403, "Forbidden", "Write access denied");
1046  } else if (ast_ends_with(uri, "/")) {
1047  remove_trailing_slash(uri, &response);
1048  } else if (ast_begins_with(uri, "api-docs/")) {
1049  /* Serving up API docs */
1050  if (method != AST_HTTP_GET) {
1051  ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method");
1052  } else {
1053  /* Skip the api-docs prefix */
1054  ast_ari_get_docs(strchr(uri, '/') + 1, urih->prefix, headers, &response);
1055  }
1056  } else {
1057  /* Other RESTful resources */
1058  ast_ari_invoke(ser, uri, method, get_params, headers, body,
1059  &response);
1060  }
1061 
1062  if (response.no_response) {
1063  /* The handler indicates no further response is necessary.
1064  * Probably because it already handled it */
1065  ast_free(response.headers);
1066  return 0;
1067  }
1068 
1069 request_failed:
1070 
1071  /* If you explicitly want to have no content, set message to
1072  * ast_json_null().
1073  */
1074  ast_assert(response.message != NULL);
1075  ast_assert(response.response_code > 0);
1076 
1077  /* response.message could be NULL, in which case the empty response_body
1078  * is correct
1079  */
1080  if (response.message && !ast_json_is_null(response.message)) {
1081  ast_str_append(&response.headers, 0,
1082  "Content-type: application/json\r\n");
1083  if (ast_json_dump_str_format(response.message, &response_body,
1084  conf->general->format) != 0) {
1085  /* Error encoding response */
1086  response.response_code = 500;
1087  response.response_text = "Internal Server Error";
1088  ast_str_set(&response_body, 0, "%s", "");
1089  ast_str_set(&response.headers, 0, "%s", "");
1090  }
1091  }
1092 
1093  if (debug_app) {
1094  ast_verbose("<--- Sending ARI response to %s --->\n%d %s\n%s%s\n\n",
1096  response.response_text, ast_str_buffer(response.headers),
1097  ast_str_buffer(response_body));
1098  }
1099 
1100  ast_http_send(ser, method, response.response_code,
1101  response.response_text, response.headers, response_body,
1102  response.fd != -1 ? response.fd : 0, 0);
1103  /* ast_http_send takes ownership, so we don't have to free them */
1104  response_body = NULL;
1105 
1106  ast_json_unref(response.message);
1107  if (response.fd >= 0) {
1108  close(response.fd);
1109  }
1110  return 0;
1111 }
1112 
1113 static struct ast_http_uri http_uri = {
1115  .description = "Asterisk RESTful API",
1116  .uri = "ari",
1117  .has_subtree = 1,
1118  .data = NULL,
1119  .key = __FILE__,
1120  .no_decode_uri = 1,
1121 };
1122 
1123 static int unload_module(void)
1124 {
1126 
1127  if (is_enabled()) {
1128  ast_debug(3, "Disabling ARI\n");
1129  ast_http_uri_unlink(&http_uri);
1130  }
1131 
1133 
1134  ao2_cleanup(root_handler);
1135  root_handler = NULL;
1136  ast_mutex_destroy(&root_handler_lock);
1137 
1138  ast_json_unref(oom_json);
1139  oom_json = NULL;
1140 
1141  return 0;
1142 }
1143 
1144 static int load_module(void)
1145 {
1146  ast_mutex_init(&root_handler_lock);
1147 
1148  /* root_handler may have been built during a declined load */
1149  if (!root_handler) {
1150  root_handler = root_handler_create();
1151  }
1152  if (!root_handler) {
1153  return AST_MODULE_LOAD_DECLINE;
1154  }
1155 
1156  /* oom_json may have been built during a declined load */
1157  if (!oom_json) {
1158  oom_json = ast_json_pack(
1159  "{s: s}", "error", "Allocation failed");
1160  }
1161  if (!oom_json) {
1162  /* Ironic */
1163  unload_module();
1164  return AST_MODULE_LOAD_DECLINE;
1165  }
1166 
1167  if (ast_ari_config_init() != 0) {
1168  unload_module();
1169  return AST_MODULE_LOAD_DECLINE;
1170  }
1171 
1172  if (is_enabled()) {
1173  ast_debug(3, "ARI enabled\n");
1174  ast_http_uri_link(&http_uri);
1175  } else {
1176  ast_debug(3, "ARI disabled\n");
1177  }
1178 
1179  if (ast_ari_cli_register() != 0) {
1180  unload_module();
1181  return AST_MODULE_LOAD_DECLINE;
1182  }
1183 
1184  return AST_MODULE_LOAD_SUCCESS;
1185 }
1186 
1187 static int reload_module(void)
1188 {
1189  char was_enabled = is_enabled();
1190 
1191  if (ast_ari_config_reload() != 0) {
1192  return AST_MODULE_LOAD_DECLINE;
1193  }
1194 
1195  if (was_enabled && !is_enabled()) {
1196  ast_debug(3, "Disabling ARI\n");
1197  ast_http_uri_unlink(&http_uri);
1198  } else if (!was_enabled && is_enabled()) {
1199  ast_debug(3, "Enabling ARI\n");
1200  ast_http_uri_link(&http_uri);
1201  }
1202 
1203  return AST_MODULE_LOAD_SUCCESS;
1204 }
1205 
1207  .support_level = AST_MODULE_SUPPORT_CORE,
1208  .load = load_module,
1209  .unload = unload_module,
1210  .reload = reload_module,
1211  .optional_modules = "res_http_websocket",
1212  .requires = "http,res_stasis",
1213  .load_pri = AST_MODPRI_APP_DEPEND,
1214 );
void ast_uri_decode(char *s, struct ast_flags spec)
Decode URI, URN, URL (overwrite string)
Definition: main/utils.c:616
void ast_ari_config_destroy(void)
Destroy the ARI configuration.
struct ast_variable * next
void ast_std_free(void *ptr)
Definition: astmm.c:1766
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
Asterisk main include file. File version handling, generic pbx functions.
struct ast_str * headers
Definition: ari.h:95
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
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:276
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:648
ast_http_callback callback
Definition: http.h:105
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:202
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
char text[AST_JSON_ERROR_TEXT_LENGTH]
Definition: json.h:837
struct ast_ari_conf * ast_ari_config_get(void)
Get the current ARI configuration.
void ari_handle_websocket(struct ast_websocket_server *ws_server, struct ast_tcptls_session_instance *ser, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
Wrapper for invoking the websocket code for an incoming connection.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix)
Definition: strings.h:112
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:179
#define ACR_HEADERS
Definition: res_ari.c:351
#define LOG_WARNING
Definition: logger.h:274
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition: http.c:673
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
int ast_json_object_del(struct ast_json *object, const char *key)
Delete a field from a JSON object.
Definition: json.c:408
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:298
void ast_json_free(void *p)
Asterisk&#39;s custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
All configuration options for ARI.
Definition: internal.h:54
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition: http.c:705
struct ast_json * ast_json_stringf(const char *format,...)
Create a JSON string, printf style.
Definition: json.c:283
static int reload_module(void)
Definition: res_ari.c:1187
Asterisk RESTful API hooks.
char * username
Definition: internal.h:98
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:1314
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:1091
int ast_ari_config_reload(void)
Reload the ARI configuration.
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)
Definition: res_ari.c:885
static int copy(char *infile, char *outfile)
Utility function to copy a file.
#define ast_assert(a)
Definition: utils.h:695
static struct stasis_rest_handlers * get_root_handler(void)
Definition: res_ari.c:238
#define ast_mutex_lock(a)
Definition: lock.h:187
void ast_verbose(const char *fmt,...)
Definition: extconf.c:2207
static int unload_module(void)
Definition: res_ari.c:1123
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
const char * str
Definition: app_jack.c:147
#define NULL
Definition: resample.c:96
int ast_json_is_null(const struct ast_json *value)
Check if value is JSON null.
Definition: json.c:263
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:456
static struct ast_str * password
Definition: cdr_mysql.c:77
JSON parsing error information.
Definition: json.h:829
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:404
#define ast_strlen_zero(foo)
Definition: strings.h:52
int response_code
Definition: ari.h:98
int stasis_app_get_debug_by_name(const char *app_name)
Get debug status of an application.
All configuration options for statsd client.
Definition: res_statsd.c:95
struct ast_json * ast_json_null(void)
Get the JSON null value.
Definition: json.c:248
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:1065
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:361
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
static char host[256]
Definition: muted.c:77
static void process_cors_request(struct ast_variable *headers, struct ast_ari_response *response)
Handle CORS headers for simple requests.
Definition: res_ari.c:753
#define SCOPED_MUTEX(varname, lock)
scoped lock specialization for mutexes
Definition: lock.h:587
Asterisk file paths, configured in asterisk.conf.
#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:911
#define ast_fully_booted
Definition: options.h:115
ast_mutex_t lock
Definition: app_meetme.c:1091
static struct stasis_rest_handlers * root_handler
Definition: res_ari.c:169
#define ao2_ref(o, delta)
Definition: astobj2.h:464
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
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:830
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
const char * method
Definition: res_pjsip.c:4335
int ast_ari_cli_register(void)
Register CLI commands for ARI.
Definition: res/ari/cli.c:434
#define ast_variable_new(name, value, filename)
struct ast_json * ast_json_vstringf(const char *format, va_list args)
Create a JSON string, vprintf style.
Definition: json.c:293
static struct ast_json * oom_json
Definition: res_ari.c:172
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition: res_ari.c:284
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:463
describes a server instance
Definition: tcptls.h:149
struct ast_websocket_server * ws_server
Definition: ari.h:81
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
#define LOG_ERROR
Definition: logger.h:285
const struct ast_flags ast_uri_http_legacy
Definition: main/utils.c:574
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:499
Per-user configuration options.
Definition: internal.h:96
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:583
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
#define ACR_METHOD
Definition: res_ari.c:350
Internal API&#39;s for res_ari.
int column
Definition: json.h:833
int errno
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:260
int ast_ari_config_init(void)
Initialize the ARI configuration.
const char * app_name(struct ast_app *app)
Definition: pbx_app.c:463
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
#define LOG_NOTICE
Definition: logger.h:263
struct ast_json * ast_ari_oom_json(void)
The stock message to return when out of memory.
Definition: res_ari.c:174
void ast_ari_get_docs(const char *uri, const char *prefix, struct ast_variable *headers, struct ast_ari_response *response)
Definition: res_ari.c:598
size_t num_children
Definition: ari.h:83
const char * response_text
Definition: ari.h:102
#define ast_free(a)
Definition: astmm.h:182
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:259
static int reload(void)
Definition: cdr_mysql.c:741
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
const char * ast_get_http_method(enum ast_http_method method) attribute_pure
Return http method name string.
Definition: http.c:190
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:59
const char * prefix
Definition: http.h:104
static struct ast_ari_conf_user * authenticate_api_key(const char *api_key)
Authenticate a ?api_key=userid:password
Definition: res_ari.c:820
structure to hold users read from users.conf
struct ast_json * message
Definition: ari.h:93
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
static int is_enabled(void)
Helper function to check if module is enabled.
Definition: res_ari.c:159
char source[AST_JSON_ERROR_TEXT_LENGTH]
Definition: json.h:839
const char * path_segment
Definition: ari.h:70
void ast_ari_invoke(struct ast_tcptls_session_instance *ser, 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:491
char * strsep(char **str, const char *delims)
static struct ast_http_uri http_uri
Definition: res_ari.c:1113
HTTP authentication information.
Definition: http.h:123
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
struct stasis_rest_handlers * children[]
Definition: ari.h:85
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:397
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static void remove_trailing_slash(const char *uri, struct ast_ari_response *response)
Definition: res_ari.c:722
void ast_ari_cli_unregister(void)
Unregister CLI commands for ARI.
Definition: res/ari/cli.c:438
static void add_allow_header(struct stasis_rest_handlers *handler, struct ast_ari_response *response)
Definition: res_ari.c:315
static struct ast_ari_conf_user * authenticate_user(struct ast_variable *get_params, struct ast_variable *headers)
Authenticate an HTTP request.
Definition: res_ari.c:848
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
Definition of a URI handler.
Definition: http.h:100
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:1353
struct ast_http_auth * ast_http_get_auth(struct ast_variable *headers)
Get HTTP authentication information from headers.
Definition: http.c:1579
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Definition: strings.h:94
stasis_rest_callback callbacks[AST_HTTP_MAX_METHOD]
Definition: ari.h:79
static ast_mutex_t root_handler_lock
Definition: res_ari.c:166
Abstract JSON element (object, array, string, int, ...).
static int origin_allowed(const char *origin)
Definition: res_ari.c:330
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:305
unsigned int no_response
Definition: ari.h:104
struct ast_ari_conf_user * ast_ari_config_validate_user(const char *username, const char *password)
Validated a user&#39;s credentials.
Stasis Application API. See Stasis Application API for detailed documentation.
int line
Definition: json.h:831
#define ast_mutex_init(pmutex)
Definition: lock.h:184
ast_http_method
HTTP Request methods known by Asterisk.
Definition: http.h:56
#define ast_mutex_destroy(a)
Definition: lock.h:186
#define ACA_METHODS
Definition: res_ari.c:352
static char url[512]
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
static const char app[]
Definition: app_mysql.c:62
static int load_module(void)
Definition: res_ari.c:1144
Asterisk module definitions.
static struct stasis_rest_handlers * root_handler_create(void)
Definition: res_ari.c:245
enum ast_json_encoding_format ast_ari_json_format(void)
Configured encoding format for JSON output.
Definition: res_ari.c:806
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:836
struct ast_variable * ast_variables_dup(struct ast_variable *var)
Duplicate variable list.
Definition: main/config.c:545
Handler for a single RESTful path segment.
Definition: ari.h:68
Structure for mutex and tracking information.
Definition: lock.h:135
#define ACA_HEADERS
Definition: res_ari.c:353
void ast_ari_response_accepted(struct ast_ari_response *response)
Fill in a Accepted (202) ast_ari_response.
Definition: res_ari.c:291
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
#define ast_mutex_unlock(a)
Definition: lock.h:188
ast_json_encoding_format
Encoding format type.
Definition: json.h:745
static char prefix[MAX_PREFIX]
Definition: http.c:141
struct ast_sockaddr remote_address
Definition: tcptls.h:151