Asterisk - The Open Source Telephony Project  GIT-master-a24979a
res_ari_events.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*
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  <depend type="module">res_http_websocket</depend>
39  <support_level>core</support_level>
40  ***/
41 
42 #include "asterisk.h"
43 
44 #include "asterisk/app.h"
45 #include "asterisk/module.h"
46 #include "asterisk/stasis_app.h"
47 #include "ari/resource_events.h"
48 #if defined(AST_DEVMODE)
50 #endif
52 
53 #define MAX_VALS 128
54 
56  struct ast_variable *get_params, struct ast_variable *headers, const char *session_id)
57 {
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) {
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) {
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) {
106  goto fin;
107  }
108 
109  for (j = 0; j < args.app_count; ++j) {
110  args.app[j] = (vals[j]);
111  }
112  } else
113  if (strcmp(i->name, "subscribeAll") == 0) {
114  args.subscribe_all = ast_true(i->value);
115  } else
116  {}
117  }
118 
119  res = ast_ari_websocket_events_event_websocket_attempted(ser, headers, &args, session_id);
120 
121 fin: __attribute__((unused))
122  if (!response) {
123  ast_http_error(ser, 500, "Server Error", "Memory allocation error");
124  res = -1;
125  } else if (response->response_code != 0) {
126  /* Param parsing failure */
127  RAII_VAR(char *, msg, NULL, ast_json_free);
128  if (response->message) {
129  msg = ast_json_dump_string(response->message);
130  } else {
131  ast_log(LOG_ERROR, "Missing response message\n");
132  }
133 
134  if (msg) {
135  ast_http_error(ser, response->response_code, response->response_text, msg);
136  }
137  res = -1;
138  }
139  ast_free(args.app_parse);
140  ast_free(args.app);
141  return res;
142 }
143 
145  struct ast_variable *get_params, struct ast_variable *headers)
146 {
148  RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
149  struct ast_variable *i;
150  RAII_VAR(struct ast_websocket *, s, ws_session, ast_websocket_unref);
152 
154 
155  response = ast_calloc(1, sizeof(*response));
156  if (!response) {
157  ast_log(LOG_ERROR, "Failed to create response.\n");
158  goto fin;
159  }
160 
161 #if defined(AST_DEVMODE)
164 #else
166 #endif
167  if (!session) {
168  ast_log(LOG_ERROR, "Failed to create ARI session\n");
169  goto fin;
170  }
171 
172  for (i = get_params; i; i = i->next) {
173  if (strcmp(i->name, "app") == 0) {
174  /* Parse comma separated list */
175  char *vals[MAX_VALS];
176  size_t j;
177 
178  args.app_parse = ast_strdup(i->value);
179  if (!args.app_parse) {
181  goto fin;
182  }
183 
184  if (strlen(args.app_parse) == 0) {
185  /* ast_app_separate_args can't handle "" */
186  args.app_count = 1;
187  vals[0] = args.app_parse;
188  } else {
189  args.app_count = ast_app_separate_args(
190  args.app_parse, ',', vals,
191  ARRAY_LEN(vals));
192  }
193 
194  if (args.app_count == 0) {
196  goto fin;
197  }
198 
199  if (args.app_count >= MAX_VALS) {
200  ast_ari_response_error(response, 400,
201  "Bad Request",
202  "Too many values for app");
203  goto fin;
204  }
205 
206  args.app = ast_malloc(sizeof(*args.app) * args.app_count);
207  if (!args.app) {
209  goto fin;
210  }
211 
212  for (j = 0; j < args.app_count; ++j) {
213  args.app[j] = (vals[j]);
214  }
215  } else
216  if (strcmp(i->name, "subscribeAll") == 0) {
217  args.subscribe_all = ast_true(i->value);
218  } else
219  {}
220  }
221 
223 
224 fin: __attribute__((unused))
225  if (response && response->response_code != 0) {
226  /* Param parsing failure */
227  RAII_VAR(char *, msg, NULL, ast_json_free);
228  if (response->message) {
229  msg = ast_json_dump_string(response->message);
230  } else {
231  ast_log(LOG_ERROR, "Missing response message\n");
232  }
233  if (msg) {
234  ast_websocket_write(ws_session,
235  AST_WEBSOCKET_OPCODE_TEXT, msg, strlen(msg));
236  }
237  }
238  ast_free(args.app_parse);
239  ast_free(args.app);
240 }
242  struct ast_json *body,
244 {
245  struct ast_json *field;
246  /* Parse query parameters out of it */
247  field = ast_json_object_get(body, "application");
248  if (field) {
249  args->application = ast_json_string_get(field);
250  }
251  field = ast_json_object_get(body, "source");
252  if (field) {
253  /* If they were silly enough to both pass in a query param and a
254  * JSON body, free up the query value.
255  */
256  ast_free(args->source);
257  if (ast_json_typeof(field) == AST_JSON_ARRAY) {
258  /* Multiple param passed as array */
259  size_t i;
260  args->source_count = ast_json_array_size(field);
261  args->source = ast_malloc(sizeof(*args->source) * args->source_count);
262 
263  if (!args->source) {
264  return -1;
265  }
266 
267  for (i = 0; i < args->source_count; ++i) {
268  args->source[i] = ast_json_string_get(ast_json_array_get(field, i));
269  }
270  } else {
271  /* Multiple param passed as single value */
272  args->source_count = 1;
273  args->source = ast_malloc(sizeof(*args->source) * args->source_count);
274  if (!args->source) {
275  return -1;
276  }
277  args->source[0] = ast_json_string_get(field);
278  }
279  }
280  return 0;
281 }
282 
283 /*!
284  * \brief Parameter parsing callback for /events/user/{eventName}.
285  * \param ser TCP/TLS session object
286  * \param get_params GET parameters in the HTTP request.
287  * \param path_vars Path variables extracted from the request.
288  * \param headers HTTP headers.
289  * \param body
290  * \param[out] response Response to the HTTP request.
291  */
293  struct ast_tcptls_session_instance *ser,
294  struct ast_variable *get_params, struct ast_variable *path_vars,
295  struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
296 {
298  struct ast_variable *i;
299 #if defined(AST_DEVMODE)
300  int is_valid;
301  int code;
302 #endif /* AST_DEVMODE */
303 
304  for (i = get_params; i; i = i->next) {
305  if (strcmp(i->name, "application") == 0) {
306  args.application = (i->value);
307  } else
308  if (strcmp(i->name, "source") == 0) {
309  /* Parse comma separated list */
310  char *vals[MAX_VALS];
311  size_t j;
312 
313  args.source_parse = ast_strdup(i->value);
314  if (!args.source_parse) {
316  goto fin;
317  }
318 
319  if (strlen(args.source_parse) == 0) {
320  /* ast_app_separate_args can't handle "" */
321  args.source_count = 1;
322  vals[0] = args.source_parse;
323  } else {
324  args.source_count = ast_app_separate_args(
325  args.source_parse, ',', vals,
326  ARRAY_LEN(vals));
327  }
328 
329  if (args.source_count == 0) {
331  goto fin;
332  }
333 
334  if (args.source_count >= MAX_VALS) {
335  ast_ari_response_error(response, 400,
336  "Bad Request",
337  "Too many values for source");
338  goto fin;
339  }
340 
341  args.source = ast_malloc(sizeof(*args.source) * args.source_count);
342  if (!args.source) {
344  goto fin;
345  }
346 
347  for (j = 0; j < args.source_count; ++j) {
348  args.source[j] = (vals[j]);
349  }
350  } else
351  {}
352  }
353  for (i = path_vars; i; i = i->next) {
354  if (strcmp(i->name, "eventName") == 0) {
355  args.event_name = (i->value);
356  } else
357  {}
358  }
359  args.variables = body;
360  ast_ari_events_user_event(headers, &args, response);
361 #if defined(AST_DEVMODE)
362  code = response->response_code;
363 
364  switch (code) {
365  case 0: /* Implementation is still a stub, or the code wasn't set */
366  is_valid = response->message == NULL;
367  break;
368  case 500: /* Internal Server Error */
369  case 501: /* Not Implemented */
370  case 404: /* Application does not exist. */
371  case 422: /* Event source not found. */
372  case 400: /* Invalid even tsource URI or userevent data. */
373  is_valid = 1;
374  break;
375  default:
376  if (200 <= code && code <= 299) {
377  is_valid = ast_ari_validate_void(
378  response->message);
379  } else {
380  ast_log(LOG_ERROR, "Invalid error response %d for /events/user/{eventName}\n", code);
381  is_valid = 0;
382  }
383  }
384 
385  if (!is_valid) {
386  ast_log(LOG_ERROR, "Response validation failed for /events/user/{eventName}\n");
387  ast_ari_response_error(response, 500,
388  "Internal Server Error", "Response validation failed");
389  }
390 #endif /* AST_DEVMODE */
391 
392 fin: __attribute__((unused))
393  ast_free(args.source_parse);
394  ast_free(args.source);
395  return;
396 }
397 
398 /*! \brief REST handler for /api-docs/events.json */
400  .path_segment = "eventName",
401  .is_wildcard = 1,
402  .callbacks = {
404  },
405  .num_children = 0,
406  .children = { }
407 };
408 /*! \brief REST handler for /api-docs/events.json */
409 static struct stasis_rest_handlers events_user = {
410  .path_segment = "user",
411  .callbacks = {
412  },
413  .num_children = 1,
414  .children = { &events_user_eventName, }
415 };
416 /*! \brief REST handler for /api-docs/events.json */
417 static struct stasis_rest_handlers events = {
418  .path_segment = "events",
419  .callbacks = {
420  },
421  .num_children = 1,
422  .children = { &events_user, }
423 };
424 
425 static int unload_module(void)
426 {
431  return 0;
432 }
433 
434 static int load_module(void)
435 {
436  int res = 0;
437 
438  struct ast_websocket_protocol *protocol;
439 
442  }
443 
445  if (!events.ws_server) {
448  }
449 
450  protocol = ast_websocket_sub_protocol_alloc("ari");
451  if (!protocol) {
452  ao2_ref(events.ws_server, -1);
456  }
460 
461  res |= ast_ari_add_handler(&events);
462  if (res) {
463  unload_module();
465  }
466 
468 }
469 
470 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - WebSocket resource",
471  .support_level = AST_MODULE_SUPPORT_CORE,
472  .load = load_module,
473  .unload = unload_module,
474  .requires = "res_ari,res_ari_model,res_stasis,res_http_websocket",
475 );
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:202
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
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
struct ast_ari_websocket_session * ast_ari_websocket_session_create(struct ast_websocket *ws_session, int(*validator)(struct ast_json *))
Create an ARI WebSocket session.
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:179
ari_validator ast_ari_validate_message_fn(void)
Function pointer to ast_ari_validate_message().
Generated file - Build validators for ARI model objects.
int ast_ari_validate_void(struct ast_json *json)
Validator for native Swagger void.
Definition: res_ari_model.c:91
Asterisk main include file. File version handling, generic pbx functions.
static struct ast_mansession session
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
@ AST_HTTP_POST
Definition: http.h:61
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:651
Support for WebSocket connections within the Asterisk HTTP server and client WebSocket connections to...
struct ast_websocket_protocol * ast_websocket_sub_protocol_alloc(const char *name)
Allocate a websocket sub-protocol instance.
struct ast_websocket_server * ast_websocket_server_create(void)
Creates a ast_websocket_server.
@ AST_WEBSOCKET_OPCODE_TEXT
void ast_websocket_unref(struct ast_websocket *session)
Decrease the reference count for a WebSocket session.
int ast_websocket_server_add_protocol2(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
Add a sub-protocol handler to the given server.
int ast_websocket_write(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
Construct and transmit a WebSocket frame.
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define ast_app_separate_args(a, b, c, d)
#define LOG_ERROR
enum ast_json_type ast_json_typeof(const struct ast_json *value)
Get the type of value.
Definition: json.c:78
struct ast_json * ast_json_array_get(const struct ast_json *array, size_t index)
Get an element from an array.
Definition: json.c:360
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:783
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
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
@ AST_JSON_ARRAY
Definition: json.h:165
size_t ast_json_array_size(const struct ast_json *array)
Get the size of a JSON array.
Definition: json.c:356
Asterisk module definitions.
#define SCOPED_MODULE_USE(module)
Definition: module.h:665
@ AST_MODFLAG_DEFAULT
Definition: module.h:315
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:543
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
int ast_ari_events_user_event_parse_body(struct ast_json *body, struct ast_ari_events_user_event_args *args)
Body parsing function for /events/user/{eventName}.
static void ast_ari_events_event_websocket_ws_established_cb(struct ast_websocket *ws_session, struct ast_variable *get_params, struct ast_variable *headers)
static struct stasis_rest_handlers events
REST handler for /api-docs/events.json.
static struct stasis_rest_handlers events_user_eventName
REST handler for /api-docs/events.json.
#define MAX_VALS
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, const char *session_id)
static struct stasis_rest_handlers events_user
REST handler for /api-docs/events.json.
static void ast_ari_events_user_event_cb(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)
Parameter parsing callback for /events/user/{eventName}.
static int load_module(void)
static int unload_module(void)
#define NULL
Definition: resample.c:96
void ast_ari_websocket_events_event_websocket_established(struct ast_ari_websocket_session *ws_session, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args)
WebSocket connection for events.
void ast_ari_events_user_event(struct ast_variable *headers, struct ast_ari_events_user_event_args *args, struct ast_ari_response *response)
Generate a user event.
int ast_ari_websocket_events_event_websocket_attempted(struct ast_tcptls_session_instance *ser, struct ast_variable *headers, struct ast_ari_events_event_websocket_args *args, const char *session_id)
WebSocket connection for events.
int ast_ari_websocket_events_event_websocket_init(void)
WebSocket connection for events.
void ast_ari_websocket_events_event_websocket_dtor(void)
WebSocket connection for events.
Generated file - declares stubs to be implemented in res/ari/resource_events.c.
Stasis Application API. See Stasis Application API for detailed documentation.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: main/utils.c:2097
struct ast_json * message
Definition: ari.h:94
int response_code
Definition: ari.h:99
Abstract JSON element (object, array, string, int, ...).
struct ast_module * self
Definition: module.h:342
describes a server instance
Definition: tcptls.h:150
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
A websocket protocol implementation.
ast_websocket_callback session_established
Callback called when a new session is established. Mandatory.
ast_websocket_pre_callback session_attempted
Callback called when a new session is attempted. Optional.
Structure definition for session.
Handler for a single RESTful path segment.
Definition: ari.h:69
const char * path_segment
Definition: ari.h:71
struct ast_websocket_server * ws_server
Definition: ari.h:82
const char * args
#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:936
#define ARRAY_LEN(a)
Definition: utils.h:661