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