res/ari: Register Stasis application on WebSocket attempt
[asterisk/asterisk.git] / res / res_ari_events.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 /*
20  * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
21  * !!!!!                               DO NOT EDIT                        !!!!!
22  * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
23  * This file is generated by a mustache template. Please see the original
24  * template in rest-api-templates/res_ari_resource.c.mustache
25  */
26
27 /*! \file
28  *
29  * \brief WebSocket resource
30  *
31  * \author David M. Lee, II <dlee@digium.com>
32  */
33
34 /*** MODULEINFO
35         <depend type="module">res_ari</depend>
36         <depend type="module">res_ari_model</depend>
37         <depend type="module">res_stasis</depend>
38         <support_level>core</support_level>
39  ***/
40
41 #include "asterisk.h"
42
43 ASTERISK_REGISTER_FILE()
44
45 #include "asterisk/app.h"
46 #include "asterisk/module.h"
47 #include "asterisk/stasis_app.h"
48 #include "ari/resource_events.h"
49 #if defined(AST_DEVMODE)
50 #include "ari/ari_model_validators.h"
51 #endif
52 #include "asterisk/http_websocket.h"
53
54 #define MAX_VALS 128
55
56 static int ast_ari_events_event_websocket_ws_attempted_cb(struct ast_tcptls_session_instance *ser, struct ast_variable *get_params, struct ast_variable *headers)
57 {
58         struct ast_ari_events_event_websocket_args args = {};
59         int res = 0;
60         RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
61         struct ast_variable *i;
62
63         response = ast_calloc(1, sizeof(*response));
64         if (!response) {
65                 ast_log(LOG_ERROR, "Failed to create response.\n");
66                 goto fin;
67         }
68
69         for (i = get_params; i; i = i->next) {
70                 if (strcmp(i->name, "app") == 0) {
71                         /* Parse comma separated list */
72                         char *vals[MAX_VALS];
73                         size_t j;
74
75                         args.app_parse = ast_strdup(i->value);
76                         if (!args.app_parse) {
77                                 ast_ari_response_alloc_failed(response);
78                                 goto fin;
79                         }
80
81                         if (strlen(args.app_parse) == 0) {
82                                 /* ast_app_separate_args can't handle "" */
83                                 args.app_count = 1;
84                                 vals[0] = args.app_parse;
85                         } else {
86                                 args.app_count = ast_app_separate_args(
87                                         args.app_parse, ',', vals,
88                                         ARRAY_LEN(vals));
89                         }
90
91                         if (args.app_count == 0) {
92                                 ast_ari_response_alloc_failed(response);
93                                 goto fin;
94                         }
95
96                         if (args.app_count >= MAX_VALS) {
97                                 ast_ari_response_error(response, 400,
98                                         "Bad Request",
99                                         "Too many values for app");
100                                 goto fin;
101                         }
102
103                         args.app = ast_malloc(sizeof(*args.app) * args.app_count);
104                         if (!args.app) {
105                                 ast_ari_response_alloc_failed(response);
106                                 goto fin;
107                         }
108
109                         for (j = 0; j < args.app_count; ++j) {
110                                 args.app[j] = (vals[j]);
111                         }
112                 } else
113                 {}
114         }
115
116         res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args);
117
118 fin: __attribute__((unused))
119         if (!response) {
120                 ast_http_error(ser, 500, "Server Error", "Memory allocation error");
121                 res = -1;
122         } else if (response->response_code != 0) {
123                 /* Param parsing failure */
124                 RAII_VAR(char *, msg, NULL, ast_json_free);
125                 if (response->message) {
126                         msg = ast_json_dump_string(response->message);
127                 } else {
128                         ast_log(LOG_ERROR, "Missing response message\n");
129                 }
130
131                 if (msg) {
132                         ast_http_error(ser, response->response_code, response->response_text, msg);
133                 }
134                 res = -1;
135         }
136         ast_free(args.app_parse);
137         ast_free(args.app);
138         return res;
139 }
140
141 static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session,
142         struct ast_variable *get_params, struct ast_variable *headers)
143 {
144         struct ast_ari_events_event_websocket_args args = {};
145         RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
146         struct ast_variable *i;
147         RAII_VAR(struct ast_websocket *, s, ws_session, ast_websocket_unref);
148         RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
149
150         response = ast_calloc(1, sizeof(*response));
151         if (!response) {
152                 ast_log(LOG_ERROR, "Failed to create response.\n");
153                 goto fin;
154         }
155
156 #if defined(AST_DEVMODE)
157         session = ast_ari_websocket_session_create(ws_session,
158                 ast_ari_validate_message_fn());
159 #else
160         session = ast_ari_websocket_session_create(ws_session, NULL);
161 #endif
162         if (!session) {
163                 ast_log(LOG_ERROR, "Failed to create ARI session\n");
164                 goto fin;
165         }
166
167         for (i = get_params; i; i = i->next) {
168                 if (strcmp(i->name, "app") == 0) {
169                         /* Parse comma separated list */
170                         char *vals[MAX_VALS];
171                         size_t j;
172
173                         args.app_parse = ast_strdup(i->value);
174                         if (!args.app_parse) {
175                                 ast_ari_response_alloc_failed(response);
176                                 goto fin;
177                         }
178
179                         if (strlen(args.app_parse) == 0) {
180                                 /* ast_app_separate_args can't handle "" */
181                                 args.app_count = 1;
182                                 vals[0] = args.app_parse;
183                         } else {
184                                 args.app_count = ast_app_separate_args(
185                                         args.app_parse, ',', vals,
186                                         ARRAY_LEN(vals));
187                         }
188
189                         if (args.app_count == 0) {
190                                 ast_ari_response_alloc_failed(response);
191                                 goto fin;
192                         }
193
194                         if (args.app_count >= MAX_VALS) {
195                                 ast_ari_response_error(response, 400,
196                                         "Bad Request",
197                                         "Too many values for app");
198                                 goto fin;
199                         }
200
201                         args.app = ast_malloc(sizeof(*args.app) * args.app_count);
202                         if (!args.app) {
203                                 ast_ari_response_alloc_failed(response);
204                                 goto fin;
205                         }
206
207                         for (j = 0; j < args.app_count; ++j) {
208                                 args.app[j] = (vals[j]);
209                         }
210                 } else
211                 {}
212         }
213
214         ast_ari_websocket_events_event_websocket_established(session, headers, &args);
215
216 fin: __attribute__((unused))
217         if (response && response->response_code != 0) {
218                 /* Param parsing failure */
219                 RAII_VAR(char *, msg, NULL, ast_json_free);
220                 if (response->message) {
221                         msg = ast_json_dump_string(response->message);
222                 } else {
223                         ast_log(LOG_ERROR, "Missing response message\n");
224                 }
225                 if (msg) {
226                         ast_websocket_write(ws_session,
227                                 AST_WEBSOCKET_OPCODE_TEXT, msg, strlen(msg));
228                 }
229         }
230         ast_free(args.app_parse);
231         ast_free(args.app);
232 }
233 int ast_ari_events_user_event_parse_body(
234         struct ast_json *body,
235         struct ast_ari_events_user_event_args *args)
236 {
237         struct ast_json *field;
238         /* Parse query parameters out of it */
239         field = ast_json_object_get(body, "application");
240         if (field) {
241                 args->application = ast_json_string_get(field);
242         }
243         field = ast_json_object_get(body, "source");
244         if (field) {
245                 /* If they were silly enough to both pass in a query param and a
246                  * JSON body, free up the query value.
247                  */
248                 ast_free(args->source);
249                 if (ast_json_typeof(field) == AST_JSON_ARRAY) {
250                         /* Multiple param passed as array */
251                         size_t i;
252                         args->source_count = ast_json_array_size(field);
253                         args->source = ast_malloc(sizeof(*args->source) * args->source_count);
254
255                         if (!args->source) {
256                                 return -1;
257                         }
258
259                         for (i = 0; i < args->source_count; ++i) {
260                                 args->source[i] = ast_json_string_get(ast_json_array_get(field, i));
261                         }
262                 } else {
263                         /* Multiple param passed as single value */
264                         args->source_count = 1;
265                         args->source = ast_malloc(sizeof(*args->source) * args->source_count);
266                         if (!args->source) {
267                                 return -1;
268                         }
269                         args->source[0] = ast_json_string_get(field);
270                 }
271         }
272         return 0;
273 }
274
275 /*!
276  * \brief Parameter parsing callback for /events/user/{eventName}.
277  * \param get_params GET parameters in the HTTP request.
278  * \param path_vars Path variables extracted from the request.
279  * \param headers HTTP headers.
280  * \param[out] response Response to the HTTP request.
281  */
282 static void ast_ari_events_user_event_cb(
283         struct ast_tcptls_session_instance *ser,
284         struct ast_variable *get_params, struct ast_variable *path_vars,
285         struct ast_variable *headers, struct ast_ari_response *response)
286 {
287         struct ast_ari_events_user_event_args args = {};
288         struct ast_variable *i;
289         RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
290 #if defined(AST_DEVMODE)
291         int is_valid;
292         int code;
293 #endif /* AST_DEVMODE */
294
295         for (i = get_params; i; i = i->next) {
296                 if (strcmp(i->name, "application") == 0) {
297                         args.application = (i->value);
298                 } else
299                 if (strcmp(i->name, "source") == 0) {
300                         /* Parse comma separated list */
301                         char *vals[MAX_VALS];
302                         size_t j;
303
304                         args.source_parse = ast_strdup(i->value);
305                         if (!args.source_parse) {
306                                 ast_ari_response_alloc_failed(response);
307                                 goto fin;
308                         }
309
310                         if (strlen(args.source_parse) == 0) {
311                                 /* ast_app_separate_args can't handle "" */
312                                 args.source_count = 1;
313                                 vals[0] = args.source_parse;
314                         } else {
315                                 args.source_count = ast_app_separate_args(
316                                         args.source_parse, ',', vals,
317                                         ARRAY_LEN(vals));
318                         }
319
320                         if (args.source_count == 0) {
321                                 ast_ari_response_alloc_failed(response);
322                                 goto fin;
323                         }
324
325                         if (args.source_count >= MAX_VALS) {
326                                 ast_ari_response_error(response, 400,
327                                         "Bad Request",
328                                         "Too many values for source");
329                                 goto fin;
330                         }
331
332                         args.source = ast_malloc(sizeof(*args.source) * args.source_count);
333                         if (!args.source) {
334                                 ast_ari_response_alloc_failed(response);
335                                 goto fin;
336                         }
337
338                         for (j = 0; j < args.source_count; ++j) {
339                                 args.source[j] = (vals[j]);
340                         }
341                 } else
342                 {}
343         }
344         for (i = path_vars; i; i = i->next) {
345                 if (strcmp(i->name, "eventName") == 0) {
346                         args.event_name = (i->value);
347                 } else
348                 {}
349         }
350         /* Look for a JSON request entity */
351         body = ast_http_get_json(ser, headers);
352         if (!body) {
353                 switch (errno) {
354                 case EFBIG:
355                         ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
356                         goto fin;
357                 case ENOMEM:
358                         ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
359                         goto fin;
360                 case EIO:
361                         ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
362                         goto fin;
363                 }
364         }
365         args.variables = body;
366         ast_ari_events_user_event(headers, &args, response);
367 #if defined(AST_DEVMODE)
368         code = response->response_code;
369
370         switch (code) {
371         case 0: /* Implementation is still a stub, or the code wasn't set */
372                 is_valid = response->message == NULL;
373                 break;
374         case 500: /* Internal Server Error */
375         case 501: /* Not Implemented */
376         case 404: /* Application does not exist. */
377         case 422: /* Event source not found. */
378         case 400: /* Invalid even tsource URI or userevent data. */
379                 is_valid = 1;
380                 break;
381         default:
382                 if (200 <= code && code <= 299) {
383                         is_valid = ast_ari_validate_void(
384                                 response->message);
385                 } else {
386                         ast_log(LOG_ERROR, "Invalid error response %d for /events/user/{eventName}\n", code);
387                         is_valid = 0;
388                 }
389         }
390
391         if (!is_valid) {
392                 ast_log(LOG_ERROR, "Response validation failed for /events/user/{eventName}\n");
393                 ast_ari_response_error(response, 500,
394                         "Internal Server Error", "Response validation failed");
395         }
396 #endif /* AST_DEVMODE */
397
398 fin: __attribute__((unused))
399         ast_free(args.source_parse);
400         ast_free(args.source);
401         return;
402 }
403
404 /*! \brief REST handler for /api-docs/events.{format} */
405 static struct stasis_rest_handlers events_user_eventName = {
406         .path_segment = "eventName",
407         .is_wildcard = 1,
408         .callbacks = {
409                 [AST_HTTP_POST] = ast_ari_events_user_event_cb,
410         },
411         .num_children = 0,
412         .children = {  }
413 };
414 /*! \brief REST handler for /api-docs/events.{format} */
415 static struct stasis_rest_handlers events_user = {
416         .path_segment = "user",
417         .callbacks = {
418         },
419         .num_children = 1,
420         .children = { &events_user_eventName, }
421 };
422 /*! \brief REST handler for /api-docs/events.{format} */
423 static struct stasis_rest_handlers events = {
424         .path_segment = "events",
425         .callbacks = {
426         },
427         .num_children = 1,
428         .children = { &events_user, }
429 };
430
431 static int load_module(void)
432 {
433         int res = 0;
434         struct ast_websocket_protocol *protocol;
435
436         events.ws_server = ast_websocket_server_create();
437         if (!events.ws_server) {
438                 return AST_MODULE_LOAD_FAILURE;
439         }
440
441         protocol = ast_websocket_sub_protocol_alloc("ari");
442         if (!protocol) {
443                 ao2_ref(events.ws_server, -1);
444                 events.ws_server = NULL;
445                 return AST_MODULE_LOAD_FAILURE;
446         }
447         protocol->session_attempted = ast_ari_events_event_websocket_ws_attempted_cb;
448         protocol->session_established = ast_ari_events_event_websocket_ws_established_cb;
449         res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
450         stasis_app_ref();
451         res |= ast_ari_add_handler(&events);
452         return res;
453 }
454
455 static int unload_module(void)
456 {
457         ast_ari_remove_handler(&events);
458         ao2_cleanup(events.ws_server);
459         events.ws_server = NULL;
460         stasis_app_unref();
461         return 0;
462 }
463
464 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - WebSocket resource",
465         .support_level = AST_MODULE_SUPPORT_CORE,
466         .load = load_module,
467         .unload = unload_module,
468         .nonoptreq = "res_ari,res_stasis",
469 );