res_ari: Fix documentation schema error
[asterisk/asterisk.git] / res / res_ari.c
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 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         <support_level>core</support_level>
77  ***/
78
79 /*** DOCUMENTATION
80         <configInfo name="res_ari" language="en_US">
81                 <synopsis>HTTP binding for the Stasis API</synopsis>
82                 <configFile name="ari.conf">
83                         <configObject name="general">
84                                 <synopsis>General configuration settings</synopsis>
85                                 <configOption name="enabled">
86                                         <synopsis>Enable/disable the ARI module</synopsis>
87                                         <description>
88                                                 <para>This option enables or disables the ARI module.</para>
89                                                 <note>
90                                                         <para>ARI uses Asterisk's HTTP server, which must also be enabled in <filename>http.conf</filename>.</para>
91                                                 </note>
92                                         </description>
93                                         <see-also>
94                                                 <ref type="filename">http.conf</ref>
95                                                 <ref type="link">https://wiki.asterisk.org/wiki/display/AST/Asterisk+Builtin+mini-HTTP+Server</ref>
96                                         </see-also>
97                                 </configOption>
98                                 <configOption name="pretty">
99                                         <synopsis>Responses from ARI are formatted to be human readable</synopsis>
100                                 </configOption>
101                                 <configOption name="auth_realm">
102                                         <synopsis>Realm to use for authentication. Defaults to Asterisk REST Interface.</synopsis>
103                                 </configOption>
104                                 <configOption name="allowed_origins">
105                                         <synopsis>Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins.</synopsis>
106                                 </configOption>
107                         </configObject>
108
109                         <configObject name="user">
110                                 <synopsis>Per-user configuration settings</synopsis>
111                                 <configOption name="type">
112                                         <synopsis>Define this configuration section as a user.</synopsis>
113                                         <description>
114                                                 <enumlist>
115                                                         <enum name="user"><para>Configure this section as a <replaceable>user</replaceable></para></enum>
116                                                 </enumlist>
117                                         </description>
118                                 </configOption>
119                                 <configOption name="read_only">
120                                         <synopsis>When set to yes, user is only authorized for read-only requests</synopsis>
121                                 </configOption>
122                                 <configOption name="password">
123                                         <synopsis>Crypted or plaintext password (see password_format)</synopsis>
124                                 </configOption>
125                                 <configOption name="password_format">
126                                         <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>
127                                 </configOption>
128                         </configObject>
129                 </configFile>
130         </configInfo>
131 ***/
132
133 #include "asterisk.h"
134
135 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
136
137 #include "ari/internal.h"
138 #include "asterisk/ari.h"
139 #include "asterisk/astobj2.h"
140 #include "asterisk/module.h"
141 #include "asterisk/paths.h"
142
143 #include <string.h>
144 #include <sys/stat.h>
145 #include <unistd.h>
146
147 /*! \brief Helper function to check if module is enabled. */
148 static int is_enabled(void)
149 {
150         RAII_VAR(struct ast_ari_conf *, cfg, ast_ari_config_get(), ao2_cleanup);
151         return cfg && cfg->general && cfg->general->enabled;
152 }
153
154 /*! Lock for \ref root_handler */
155 static ast_mutex_t root_handler_lock;
156
157 /*! Handler for root RESTful resource. */
158 static struct stasis_rest_handlers *root_handler;
159
160 /*! Pre-defined message for allocation failures. */
161 static struct ast_json *oom_json;
162
163 struct ast_json *ast_ari_oom_json(void)
164 {
165         return oom_json;
166 }
167
168 int ast_ari_add_handler(struct stasis_rest_handlers *handler)
169 {
170         RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
171         size_t old_size, new_size;
172
173         SCOPED_MUTEX(lock, &root_handler_lock);
174
175         old_size = sizeof(*new_handler) +
176                 root_handler->num_children * sizeof(handler);
177         new_size = old_size + sizeof(handler);
178
179         new_handler = ao2_alloc(new_size, NULL);
180         if (!new_handler) {
181                 return -1;
182         }
183         memcpy(new_handler, root_handler, old_size);
184         new_handler->children[new_handler->num_children++] = handler;
185
186         ao2_cleanup(root_handler);
187         ao2_ref(new_handler, +1);
188         root_handler = new_handler;
189         ast_module_ref(ast_module_info->self);
190         return 0;
191 }
192
193 int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
194 {
195         RAII_VAR(struct stasis_rest_handlers *, new_handler, NULL, ao2_cleanup);
196         size_t size, i, j;
197
198         ast_assert(root_handler != NULL);
199
200         ast_mutex_lock(&root_handler_lock);
201         size = sizeof(*new_handler) +
202                 root_handler->num_children * sizeof(handler);
203
204         new_handler = ao2_alloc(size, NULL);
205         if (!new_handler) {
206                 return -1;
207         }
208         memcpy(new_handler, root_handler, sizeof(*new_handler));
209
210         for (i = 0, j = 0; i < root_handler->num_children; ++i) {
211                 if (root_handler->children[i] == handler) {
212                         ast_module_unref(ast_module_info->self);
213                         continue;
214                 }
215                 new_handler->children[j++] = root_handler->children[i];
216         }
217         new_handler->num_children = j;
218
219         ao2_cleanup(root_handler);
220         ao2_ref(new_handler, +1);
221         root_handler = new_handler;
222         ast_mutex_unlock(&root_handler_lock);
223         return 0;
224 }
225
226 static struct stasis_rest_handlers *get_root_handler(void)
227 {
228         SCOPED_MUTEX(lock, &root_handler_lock);
229         ao2_ref(root_handler, +1);
230         return root_handler;
231 }
232
233 static struct stasis_rest_handlers *root_handler_create(void)
234 {
235         RAII_VAR(struct stasis_rest_handlers *, handler, NULL, ao2_cleanup);
236
237         handler = ao2_alloc(sizeof(*handler), NULL);
238         if (!handler) {
239                 return NULL;
240         }
241         handler->path_segment = "ari";
242
243         ao2_ref(handler, +1);
244         return handler;
245 }
246
247 void ast_ari_response_error(struct ast_ari_response *response,
248                                 int response_code,
249                                 const char *response_text,
250                                 const char *message_fmt, ...)
251 {
252         RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
253         va_list ap;
254
255         va_start(ap, message_fmt);
256         message = ast_json_vstringf(message_fmt, ap);
257         response->message = ast_json_pack("{s: o}",
258                                           "message", ast_json_ref(message));
259         response->response_code = response_code;
260         response->response_text = response_text;
261 }
262
263 void ast_ari_response_ok(struct ast_ari_response *response,
264                              struct ast_json *message)
265 {
266         response->message = message;
267         response->response_code = 200;
268         response->response_text = "OK";
269 }
270
271 void ast_ari_response_no_content(struct ast_ari_response *response)
272 {
273         response->message = ast_json_null();
274         response->response_code = 204;
275         response->response_text = "No Content";
276 }
277
278 void ast_ari_response_alloc_failed(struct ast_ari_response *response)
279 {
280         response->message = ast_json_ref(oom_json);
281         response->response_code = 500;
282         response->response_text = "Internal Server Error";
283 }
284
285 void ast_ari_response_created(struct ast_ari_response *response,
286         const char *url, struct ast_json *message)
287 {
288         response->message = message;
289         response->response_code = 201;
290         response->response_text = "Created";
291         ast_str_append(&response->headers, 0, "Location: %s\r\n", url);
292 }
293
294 static void add_allow_header(struct stasis_rest_handlers *handler,
295                              struct ast_ari_response *response)
296 {
297         enum ast_http_method m;
298         ast_str_append(&response->headers, 0,
299                        "Allow: OPTIONS");
300         for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
301                 if (handler->callbacks[m] != NULL) {
302                         ast_str_append(&response->headers, 0,
303                                        ",%s", ast_get_http_method(m));
304                 }
305         }
306         ast_str_append(&response->headers, 0, "\r\n");
307 }
308
309 static int origin_allowed(const char *origin)
310 {
311         RAII_VAR(struct ast_ari_conf *, cfg, ast_ari_config_get(), ao2_cleanup);
312
313         char *allowed = ast_strdupa(cfg->general->allowed_origins);
314         char *current;
315
316         while ((current = strsep(&allowed, ","))) {
317                 if (!strcmp(current, "*")) {
318                         return 1;
319                 }
320
321                 if (!strcmp(current, origin)) {
322                         return 1;
323                 }
324         }
325
326         return 0;
327 }
328
329 #define ACR_METHOD "Access-Control-Request-Method"
330 #define ACR_HEADERS "Access-Control-Request-Headers"
331 #define ACA_METHODS "Access-Control-Allow-Methods"
332 #define ACA_HEADERS "Access-Control-Allow-Headers"
333
334 /*!
335  * \brief Handle OPTIONS request, mainly for CORS preflight requests.
336  *
337  * Some browsers will send this prior to non-simple methods (i.e. DELETE).
338  * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.2.
339  */
340 static void handle_options(struct stasis_rest_handlers *handler,
341                            struct ast_variable *headers,
342                            struct ast_ari_response *response)
343 {
344         struct ast_variable *header;
345         char const *acr_method = NULL;
346         char const *acr_headers = NULL;
347         char const *origin = NULL;
348
349         RAII_VAR(struct ast_str *, allow, NULL, ast_free);
350         enum ast_http_method m;
351         int allowed = 0;
352
353         /* Regular OPTIONS response */
354         add_allow_header(handler, response);
355         ast_ari_response_no_content(response);
356
357         /* Parse CORS headers */
358         for (header = headers; header != NULL; header = header->next) {
359                 if (strcmp(ACR_METHOD, header->name) == 0) {
360                         acr_method = header->value;
361                 } else if (strcmp(ACR_HEADERS, header->name) == 0) {
362                         acr_headers = header->value;
363                 } else if (strcmp("Origin", header->name) == 0) {
364                         origin = header->value;
365                 }
366         }
367
368         /* CORS 6.2, #1 - "If the Origin header is not present terminate this
369          * set of steps."
370          */
371         if (origin == NULL) {
372                 return;
373         }
374
375         /* CORS 6.2, #2 - "If the value of the Origin header is not a
376          * case-sensitive match for any of the values in list of origins do not
377          * set any additional headers and terminate this set of steps.
378          *
379          * Always matching is acceptable since the list of origins can be
380          * unbounded.
381          *
382          * The Origin header can only contain a single origin as the user agent
383          * will not follow redirects."
384          */
385         if (!origin_allowed(origin)) {
386                 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
387                 return;
388         }
389
390         /* CORS 6.2, #3 - "If there is no Access-Control-Request-Method header
391          * or if parsing failed, do not set any additional headers and terminate
392          * this set of steps."
393          */
394         if (acr_method == NULL) {
395                 return;
396         }
397
398         /* CORS 6.2, #4 - "If there are no Access-Control-Request-Headers
399          * headers let header field-names be the empty list."
400          */
401         if (acr_headers == NULL) {
402                 acr_headers = "";
403         }
404
405         /* CORS 6.2, #5 - "If method is not a case-sensitive match for any of
406          * the values in list of methods do not set any additional headers and
407          * terminate this set of steps."
408          */
409         allow = ast_str_create(20);
410
411         if (!allow) {
412                 ast_ari_response_alloc_failed(response);
413                 return;
414         }
415
416         /* Go ahead and build the ACA_METHODS header at the same time */
417         for (m = 0; m < AST_HTTP_MAX_METHOD; ++m) {
418                 if (handler->callbacks[m] != NULL) {
419                         char const *m_str = ast_get_http_method(m);
420                         if (strcmp(m_str, acr_method) == 0) {
421                                 allowed = 1;
422                         }
423                         ast_str_append(&allow, 0, ",%s", m_str);
424                 }
425         }
426
427         if (!allowed) {
428                 return;
429         }
430
431         /* CORS 6.2 #6 - "If any of the header field-names is not a ASCII
432          * case-insensitive match for any of the values in list of headers do
433          * not set any additional headers and terminate this set of steps.
434          *
435          * Note: Always matching is acceptable since the list of headers can be
436          * unbounded."
437          */
438
439         /* CORS 6.2 #7 - "If the resource supports credentials add a single
440          * Access-Control-Allow-Origin header, with the value of the Origin
441          * header as value, and add a single Access-Control-Allow-Credentials
442          * header with the case-sensitive string "true" as value."
443          *
444          * Added by process_cors_request() earlier in the request.
445          */
446
447         /* CORS 6.2 #8 - "Optionally add a single Access-Control-Max-Age
448          * header..."
449          */
450
451         /* CORS 6.2 #9 - "Add one or more Access-Control-Allow-Methods headers
452          * consisting of (a subset of) the list of methods."
453          */
454         ast_str_append(&response->headers, 0, "%s: OPTIONS%s\r\n",
455                        ACA_METHODS, ast_str_buffer(allow));
456
457
458         /* CORS 6.2, #10 - "Add one or more Access-Control-Allow-Headers headers
459          * consisting of (a subset of) the list of headers.
460          *
461          * Since the list of headers can be unbounded simply returning headers
462          * can be enough."
463          */
464         if (!ast_strlen_zero(acr_headers)) {
465                 ast_str_append(&response->headers, 0, "%s: %s\r\n",
466                                ACA_HEADERS, acr_headers);
467         }
468 }
469
470 void ast_ari_invoke(struct ast_tcptls_session_instance *ser,
471         const char *uri, enum ast_http_method method,
472         struct ast_variable *get_params, struct ast_variable *headers,
473         struct ast_ari_response *response)
474 {
475         RAII_VAR(char *, response_text, NULL, ast_free);
476         RAII_VAR(struct stasis_rest_handlers *, root, NULL, ao2_cleanup);
477         struct stasis_rest_handlers *handler;
478         RAII_VAR(struct ast_variable *, path_vars, NULL, ast_variables_destroy);
479         char *path = ast_strdupa(uri);
480         char *path_segment;
481         stasis_rest_callback callback;
482
483         root = handler = get_root_handler();
484         ast_assert(root != NULL);
485
486         while ((path_segment = strsep(&path, "/")) && (strlen(path_segment) > 0)) {
487                 struct stasis_rest_handlers *found_handler = NULL;
488                 int i;
489                 ast_uri_decode(path_segment, ast_uri_http_legacy);
490                 ast_debug(3, "Finding handler for %s\n", path_segment);
491                 for (i = 0; found_handler == NULL && i < handler->num_children; ++i) {
492                         struct stasis_rest_handlers *child = handler->children[i];
493
494                         ast_debug(3, "  Checking %s\n", child->path_segment);
495                         if (child->is_wildcard) {
496                                 /* Record the path variable */
497                                 struct ast_variable *path_var = ast_variable_new(child->path_segment, path_segment, __FILE__);
498                                 path_var->next = path_vars;
499                                 path_vars = path_var;
500                                 found_handler = child;
501                         } else if (strcmp(child->path_segment, path_segment) == 0) {
502                                 found_handler = child;
503                         }
504                 }
505
506                 if (found_handler == NULL) {
507                         /* resource not found */
508                         ast_debug(3, "  Handler not found\n");
509                         ast_ari_response_error(
510                                 response, 404, "Not Found",
511                                 "Resource not found");
512                         return;
513                 } else {
514                         ast_debug(3, "  Got it!\n");
515                         handler = found_handler;
516                 }
517         }
518
519         ast_assert(handler != NULL);
520         if (method == AST_HTTP_OPTIONS) {
521                 handle_options(handler, headers, response);
522                 return;
523         }
524
525         if (method < 0 || method >= AST_HTTP_MAX_METHOD) {
526                 add_allow_header(handler, response);
527                 ast_ari_response_error(
528                         response, 405, "Method Not Allowed",
529                         "Invalid method");
530                 return;
531         }
532
533         if (handler->ws_server && method == AST_HTTP_GET) {
534                 /* WebSocket! */
535                 ari_handle_websocket(handler->ws_server, ser, uri, method,
536                         get_params, headers);
537                 /* Since the WebSocket code handles the connection, we shouldn't
538                  * do anything else; setting no_response */
539                 response->no_response = 1;
540                 return;
541         }
542
543         callback = handler->callbacks[method];
544         if (callback == NULL) {
545                 add_allow_header(handler, response);
546                 ast_ari_response_error(
547                         response, 405, "Method Not Allowed",
548                         "Invalid method");
549                 return;
550         }
551
552         callback(ser, get_params, path_vars, headers, response);
553         if (response->message == NULL && response->response_code == 0) {
554                 /* Really should not happen */
555                 ast_log(LOG_ERROR, "ARI %s %s not implemented\n",
556                         ast_get_http_method(method), uri);
557                 ast_ari_response_error(
558                         response, 501, "Not Implemented",
559                         "Method not implemented");
560         }
561 }
562
563 void ast_ari_get_docs(const char *uri, struct ast_variable *headers,
564                           struct ast_ari_response *response)
565 {
566         RAII_VAR(struct ast_str *, absolute_path_builder, NULL, ast_free);
567         RAII_VAR(char *, absolute_api_dirname, NULL, ast_std_free);
568         RAII_VAR(char *, absolute_filename, NULL, ast_std_free);
569         struct ast_json *obj = NULL;
570         struct ast_variable *host = NULL;
571         struct ast_json_error error = {};
572         struct stat file_stat;
573
574         ast_debug(3, "%s(%s)\n", __func__, uri);
575
576         absolute_path_builder = ast_str_create(80);
577         if (absolute_path_builder == NULL) {
578                 ast_ari_response_alloc_failed(response);
579                 return;
580         }
581
582         /* absolute path to the rest-api directory */
583         ast_str_append(&absolute_path_builder, 0, "%s", ast_config_AST_DATA_DIR);
584         ast_str_append(&absolute_path_builder, 0, "/rest-api/");
585         absolute_api_dirname = realpath(ast_str_buffer(absolute_path_builder), NULL);
586         if (absolute_api_dirname == NULL) {
587                 ast_log(LOG_ERROR, "Error determining real directory for rest-api\n");
588                 ast_ari_response_error(
589                         response, 500, "Internal Server Error",
590                         "Cannot find rest-api directory");
591                 return;
592         }
593
594         /* absolute path to the requested file */
595         ast_str_append(&absolute_path_builder, 0, "%s", uri);
596         absolute_filename = realpath(ast_str_buffer(absolute_path_builder), NULL);
597         if (absolute_filename == NULL) {
598                 switch (errno) {
599                 case ENAMETOOLONG:
600                 case ENOENT:
601                 case ENOTDIR:
602                         ast_ari_response_error(
603                                 response, 404, "Not Found",
604                                 "Resource not found");
605                         break;
606                 case EACCES:
607                         ast_ari_response_error(
608                                 response, 403, "Forbidden",
609                                 "Permission denied");
610                         break;
611                 default:
612                         ast_log(LOG_ERROR,
613                                 "Error determining real path for uri '%s': %s\n",
614                                 uri, strerror(errno));
615                         ast_ari_response_error(
616                                 response, 500, "Internal Server Error",
617                                 "Cannot find file");
618                         break;
619                 }
620                 return;
621         }
622
623         if (!ast_begins_with(absolute_filename, absolute_api_dirname)) {
624                 /* HACKERZ! */
625                 ast_log(LOG_ERROR,
626                         "Invalid attempt to access '%s' (not in %s)\n",
627                         absolute_filename, absolute_api_dirname);
628                 ast_ari_response_error(
629                         response, 404, "Not Found",
630                         "Resource not found");
631                 return;
632         }
633
634         if (stat(absolute_filename, &file_stat) == 0) {
635                 if (!(file_stat.st_mode & S_IFREG)) {
636                         /* Not a file */
637                         ast_ari_response_error(
638                                 response, 403, "Forbidden",
639                                 "Invalid access");
640                         return;
641                 }
642         } else {
643                 /* Does not exist */
644                 ast_ari_response_error(
645                         response, 404, "Not Found",
646                         "Resource not found");
647                 return;
648         }
649
650         /* Load resource object from file */
651         obj = ast_json_load_new_file(absolute_filename, &error);
652         if (obj == NULL) {
653                 ast_log(LOG_ERROR, "Error parsing resource file: %s:%d(%d) %s\n",
654                         error.source, error.line, error.column, error.text);
655                 ast_ari_response_error(
656                         response, 500, "Internal Server Error",
657                         "Yikes! Cannot parse resource");
658                 return;
659         }
660
661         /* Update the basePath properly */
662         if (ast_json_object_get(obj, "basePath") != NULL) {
663                 for (host = headers; host; host = host->next) {
664                         if (strcasecmp(host->name, "Host") == 0) {
665                                 break;
666                         }
667                 }
668                 if (host != NULL) {
669                         ast_json_object_set(
670                                 obj, "basePath",
671                                 ast_json_stringf("http://%s/ari", host->value));
672                 } else {
673                         /* Without the host, we don't have the basePath */
674                         ast_json_object_del(obj, "basePath");
675                 }
676         }
677
678         ast_ari_response_ok(response, obj);
679 }
680
681 static void remove_trailing_slash(const char *uri,
682                                   struct ast_ari_response *response)
683 {
684         char *slashless = ast_strdupa(uri);
685         slashless[strlen(slashless) - 1] = '\0';
686
687         /* While it's tempting to redirect the client to the slashless URL,
688          * that is problematic. A 302 Found is the most appropriate response,
689          * but most clients issue a GET on the location you give them,
690          * regardless of the method of the original request.
691          *
692          * While there are some ways around this, it gets into a lot of client
693          * specific behavior and corner cases in the HTTP standard. There's also
694          * very little practical benefit of redirecting; only GET and HEAD can
695          * be redirected automagically; all other requests "MUST NOT
696          * automatically redirect the request unless it can be confirmed by the
697          * user, since this might change the conditions under which the request
698          * was issued."
699          *
700          * Given all of that, a 404 with a nice message telling them what to do
701          * is probably our best bet.
702          */
703         ast_ari_response_error(response, 404, "Not Found",
704                 "ARI URLs do not end with a slash. Try /ari/%s", slashless);
705 }
706
707 /*!
708  * \brief Handle CORS headers for simple requests.
709  *
710  * See http://www.w3.org/TR/cors/ for the spec. Especially section 6.1.
711  */
712 static void process_cors_request(struct ast_variable *headers,
713                                  struct ast_ari_response *response)
714 {
715         char const *origin = NULL;
716         struct ast_variable *header;
717
718         /* Parse CORS headers */
719         for (header = headers; header != NULL; header = header->next) {
720                 if (strcmp("Origin", header->name) == 0) {
721                         origin = header->value;
722                 }
723         }
724
725         /* CORS 6.1, #1 - "If the Origin header is not present terminate this
726          * set of steps."
727          */
728         if (origin == NULL) {
729                 return;
730         }
731
732         /* CORS 6.1, #2 - "If the value of the Origin header is not a
733          * case-sensitive match for any of the values in list of origins, do not
734          * set any additional headers and terminate this set of steps.
735          *
736          * Note: Always matching is acceptable since the list of origins can be
737          * unbounded."
738          */
739         if (!origin_allowed(origin)) {
740                 ast_log(LOG_NOTICE, "Origin header '%s' does not match an allowed origin.\n", origin);
741                 return;
742         }
743
744         /* CORS 6.1, #3 - "If the resource supports credentials add a single
745          * Access-Control-Allow-Origin header, with the value of the Origin
746          * header as value, and add a single Access-Control-Allow-Credentials
747          * header with the case-sensitive string "true" as value.
748          *
749          * Otherwise, add a single Access-Control-Allow-Origin header, with
750          * either the value of the Origin header or the string "*" as value."
751          */
752         ast_str_append(&response->headers, 0,
753                        "Access-Control-Allow-Origin: %s\r\n", origin);
754         ast_str_append(&response->headers, 0,
755                        "Access-Control-Allow-Credentials: true\r\n");
756
757         /* CORS 6.1, #4 - "If the list of exposed headers is not empty add one
758          * or more Access-Control-Expose-Headers headers, with as values the
759          * header field names given in the list of exposed headers."
760          *
761          * No exposed headers; skipping
762          */
763 }
764
765 enum ast_json_encoding_format ast_ari_json_format(void)
766 {
767         RAII_VAR(struct ast_ari_conf *, cfg, NULL, ao2_cleanup);
768         cfg = ast_ari_config_get();
769         return cfg->general->format;
770 }
771
772 /*!
773  * \brief Authenticate a <code>?api_key=userid:password</code>
774  *
775  * \param api_key API key query parameter
776  * \return User object for the authenticated user.
777  * \return \c NULL if authentication failed.
778  */
779 static struct ast_ari_conf_user *authenticate_api_key(const char *api_key)
780 {
781         RAII_VAR(char *, copy, NULL, ast_free);
782         char *username;
783         char *password;
784
785         password = copy = ast_strdup(api_key);
786         if (!copy) {
787                 return NULL;
788         }
789
790         username = strsep(&password, ":");
791         if (!password) {
792                 ast_log(LOG_WARNING, "Invalid api_key\n");
793                 return NULL;
794         }
795
796         return ast_ari_config_validate_user(username, password);
797 }
798
799 /*!
800  * \brief Authenticate an HTTP request.
801  *
802  * \param get_params GET parameters of the request.
803  * \param header HTTP headers.
804  * \return User object for the authenticated user.
805  * \return \c NULL if authentication failed.
806  */
807 static struct ast_ari_conf_user *authenticate_user(struct ast_variable *get_params,
808         struct ast_variable *headers)
809 {
810         RAII_VAR(struct ast_http_auth *, http_auth, NULL, ao2_cleanup);
811         struct ast_variable *v;
812
813         /* HTTP Basic authentication */
814         http_auth = ast_http_get_auth(headers);
815         if (http_auth) {
816                 return ast_ari_config_validate_user(http_auth->userid,
817                         http_auth->password);
818         }
819
820         /* ?api_key authentication */
821         for (v = get_params; v; v = v->next) {
822                 if (strcasecmp("api_key", v->name) == 0) {
823                         return authenticate_api_key(v->value);
824                 }
825         }
826
827         return NULL;
828 }
829
830 /*!
831  * \internal
832  * \brief ARI HTTP handler.
833  *
834  * This handler takes the HTTP request and turns it into the appropriate
835  * RESTful request (conversion to JSON, routing, etc.)
836  *
837  * \param ser TCP session.
838  * \param urih URI handler.
839  * \param uri URI requested.
840  * \param method HTTP method.
841  * \param get_params HTTP \c GET params.
842  * \param headers HTTP headers.
843  */
844 static int ast_ari_callback(struct ast_tcptls_session_instance *ser,
845                                 const struct ast_http_uri *urih,
846                                 const char *uri,
847                                 enum ast_http_method method,
848                                 struct ast_variable *get_params,
849                                 struct ast_variable *headers)
850 {
851         RAII_VAR(struct ast_ari_conf *, conf, NULL, ao2_cleanup);
852         RAII_VAR(struct ast_str *, response_body, ast_str_create(256), ast_free);
853         RAII_VAR(struct ast_ari_conf_user *, user, NULL, ao2_cleanup);
854         struct ast_ari_response response = {};
855         int ret = 0;
856         RAII_VAR(struct ast_variable *, post_vars, NULL, ast_variables_destroy);
857
858         if (!response_body) {
859                 return -1;
860         }
861
862         response.headers = ast_str_create(40);
863         if (!response.headers) {
864                 return -1;
865         }
866
867         conf = ast_ari_config_get();
868         if (!conf || !conf->general) {
869                 ast_free(response.headers);
870                 return -1;
871         }
872
873         process_cors_request(headers, &response);
874
875         /* Process form data from a POST. It could be mixed with query
876          * parameters, which seems a bit odd. But it's allowed, so that's okay
877          * with us.
878          */
879         post_vars = ast_http_get_post_vars(ser, headers);
880         if (get_params == NULL) {
881                 switch (errno) {
882                 case EFBIG:
883                         ast_ari_response_error(&response, 413,
884                                 "Request Entity Too Large",
885                                 "Request body too large");
886                         break;
887                 case ENOMEM:
888                         ast_ari_response_error(&response, 500,
889                                 "Internal Server Error",
890                                 "Error processing request");
891                         break;
892                 case EIO:
893                         ast_ari_response_error(&response, 400,
894                                 "Bad Request", "Error parsing request body");
895                         break;
896                 }
897                 get_params = post_vars;
898         } else if (get_params && post_vars) {
899                 /* Has both post_vars and get_params */
900                 struct ast_variable *last_var = post_vars;
901                 while (last_var->next) {
902                         last_var = last_var->next;
903                 }
904                 /* The duped get_params will get freed when post_vars gets
905                  * ast_variables_destroyed.
906                  */
907                 last_var->next = ast_variables_dup(get_params);
908                 get_params = post_vars;
909         }
910
911         user = authenticate_user(get_params, headers);
912         if (response.response_code > 0) {
913                 /* POST parameter processing error. Do nothing. */
914         } else if (!user) {
915                 /* Per RFC 2617, section 1.2: The 401 (Unauthorized) response
916                  * message is used by an origin server to challenge the
917                  * authorization of a user agent. This response MUST include a
918                  * WWW-Authenticate header field containing at least one
919                  * challenge applicable to the requested resource.
920                  */
921                 ast_ari_response_error(&response, 401, "Unauthorized", "Authentication required");
922
923                 /* Section 1.2:
924                  *   realm       = "realm" "=" realm-value
925                  *   realm-value = quoted-string
926                  * Section 2:
927                  *   challenge   = "Basic" realm
928                  */
929                 ast_str_append(&response.headers, 0,
930                         "WWW-Authenticate: Basic realm=\"%s\"\r\n",
931                         conf->general->auth_realm);
932         } else if (!ast_fully_booted) {
933                 ast_ari_response_error(&response, 503, "Service Unavailable", "Asterisk not booted");
934         } else if (user->read_only && method != AST_HTTP_GET && method != AST_HTTP_OPTIONS) {
935                 ast_ari_response_error(&response, 403, "Forbidden", "Write access denied");
936         } else if (ast_ends_with(uri, "/")) {
937                 remove_trailing_slash(uri, &response);
938         } else if (ast_begins_with(uri, "api-docs/")) {
939                 /* Serving up API docs */
940                 if (method != AST_HTTP_GET) {
941                         ast_ari_response_error(&response, 405, "Method Not Allowed", "Unsupported method");
942                 } else {
943                         /* Skip the api-docs prefix */
944                         ast_ari_get_docs(strchr(uri, '/') + 1, headers, &response);
945                 }
946         } else {
947                 /* Other RESTful resources */
948                 ast_ari_invoke(ser, uri, method, get_params, headers,
949                         &response);
950         }
951
952         if (response.no_response) {
953                 /* The handler indicates no further response is necessary.
954                  * Probably because it already handled it */
955                 ast_free(response.headers);
956                 return 0;
957         }
958
959         /* If you explicitly want to have no content, set message to
960          * ast_json_null().
961          */
962         ast_assert(response.message != NULL);
963         ast_assert(response.response_code > 0);
964
965         /* response.message could be NULL, in which case the empty response_body
966          * is correct
967          */
968         if (response.message && !ast_json_is_null(response.message)) {
969                 ast_str_append(&response.headers, 0,
970                                "Content-type: application/json\r\n");
971                 if (ast_json_dump_str_format(response.message, &response_body,
972                                 conf->general->format) != 0) {
973                         /* Error encoding response */
974                         response.response_code = 500;
975                         response.response_text = "Internal Server Error";
976                         ast_str_set(&response_body, 0, "%s", "");
977                         ast_str_set(&response.headers, 0, "%s", "");
978                         ret = -1;
979                 }
980         }
981
982         ast_http_send(ser, method, response.response_code,
983                       response.response_text, response.headers, response_body,
984                       0, 0);
985         /* ast_http_send takes ownership, so we don't have to free them */
986         response_body = NULL;
987
988         ast_json_unref(response.message);
989         return ret;
990 }
991
992 static struct ast_http_uri http_uri = {
993         .callback = ast_ari_callback,
994         .description = "Asterisk RESTful API",
995         .uri = "ari",
996
997         .has_subtree = 1,
998         .data = NULL,
999         .key = __FILE__,
1000         .no_decode_uri = 1,
1001 };
1002
1003 static int load_module(void)
1004 {
1005         ast_mutex_init(&root_handler_lock);
1006
1007         /* root_handler may have been built during a declined load */
1008         if (!root_handler) {
1009                 root_handler = root_handler_create();
1010         }
1011         if (!root_handler) {
1012                 return AST_MODULE_LOAD_FAILURE;
1013         }
1014
1015         /* oom_json may have been built during a declined load */
1016         if (!oom_json) {
1017                 oom_json = ast_json_pack(
1018                         "{s: s}", "error", "Allocation failed");
1019         }
1020         if (!oom_json) {
1021                 /* Ironic */
1022                 return AST_MODULE_LOAD_FAILURE;
1023         }
1024
1025         if (ast_ari_config_init() != 0) {
1026                 return AST_MODULE_LOAD_DECLINE;
1027         }
1028
1029         if (is_enabled()) {
1030                 ast_debug(3, "ARI enabled\n");
1031                 ast_http_uri_link(&http_uri);
1032         } else {
1033                 ast_debug(3, "ARI disabled\n");
1034         }
1035
1036         if (ast_ari_cli_register() != 0) {
1037                 return AST_MODULE_LOAD_FAILURE;
1038         }
1039
1040         return AST_MODULE_LOAD_SUCCESS;
1041 }
1042
1043 static int unload_module(void)
1044 {
1045         ast_ari_cli_unregister();
1046
1047         if (is_enabled()) {
1048                 ast_debug(3, "Disabling ARI\n");
1049                 ast_http_uri_unlink(&http_uri);
1050         }
1051
1052         ast_ari_config_destroy();
1053
1054         ao2_cleanup(root_handler);
1055         root_handler = NULL;
1056         ast_mutex_destroy(&root_handler_lock);
1057
1058         ast_json_unref(oom_json);
1059         oom_json = NULL;
1060
1061         return 0;
1062 }
1063
1064 static int reload_module(void)
1065 {
1066         char was_enabled = is_enabled();
1067
1068         if (ast_ari_config_reload() != 0) {
1069                 return AST_MODULE_LOAD_DECLINE;
1070         }
1071
1072         if (was_enabled && !is_enabled()) {
1073                 ast_debug(3, "Disabling ARI\n");
1074                 ast_http_uri_unlink(&http_uri);
1075         } else if (!was_enabled && is_enabled()) {
1076                 ast_debug(3, "Enabling ARI\n");
1077                 ast_http_uri_link(&http_uri);
1078         }
1079
1080         return AST_MODULE_LOAD_SUCCESS;
1081 }
1082
1083 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Asterisk RESTful Interface",
1084         .load = load_module,
1085         .unload = unload_module,
1086         .reload = reload_module,
1087         .nonoptreq = "res_http_websocket",
1088         .load_pri = AST_MODPRI_APP_DEPEND,
1089         );