Asterisk - The Open Source Telephony Project  18.5.0
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 <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*
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 <[email protected]>
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 {
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) {
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 {
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 {
147  struct ast_ari_events_event_websocket_args args = {};
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 {
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 get_params GET parameters in the HTTP request.
286  * \param path_vars Path variables extracted from the request.
287  * \param headers HTTP headers.
288  * \param[out] response Response to the HTTP request.
289  */
291  struct ast_tcptls_session_instance *ser,
292  struct ast_variable *get_params, struct ast_variable *path_vars,
293  struct ast_variable *headers, struct ast_json *body, struct ast_ari_response *response)
294 {
295  struct ast_ari_events_user_event_args args = {};
296  struct ast_variable *i;
297 #if defined(AST_DEVMODE)
298  int is_valid;
299  int code;
300 #endif /* AST_DEVMODE */
301 
302  for (i = get_params; i; i = i->next) {
303  if (strcmp(i->name, "application") == 0) {
304  args.application = (i->value);
305  } else
306  if (strcmp(i->name, "source") == 0) {
307  /* Parse comma separated list */
308  char *vals[MAX_VALS];
309  size_t j;
310 
311  args.source_parse = ast_strdup(i->value);
312  if (!args.source_parse) {
314  goto fin;
315  }
316 
317  if (strlen(args.source_parse) == 0) {
318  /* ast_app_separate_args can't handle "" */
319  args.source_count = 1;
320  vals[0] = args.source_parse;
321  } else {
323  args.source_parse, ',', vals,
324  ARRAY_LEN(vals));
325  }
326 
327  if (args.source_count == 0) {
329  goto fin;
330  }
331 
332  if (args.source_count >= MAX_VALS) {
333  ast_ari_response_error(response, 400,
334  "Bad Request",
335  "Too many values for source");
336  goto fin;
337  }
338 
339  args.source = ast_malloc(sizeof(*args.source) * args.source_count);
340  if (!args.source) {
342  goto fin;
343  }
344 
345  for (j = 0; j < args.source_count; ++j) {
346  args.source[j] = (vals[j]);
347  }
348  } else
349  {}
350  }
351  for (i = path_vars; i; i = i->next) {
352  if (strcmp(i->name, "eventName") == 0) {
353  args.event_name = (i->value);
354  } else
355  {}
356  }
357  args.variables = body;
358  ast_ari_events_user_event(headers, &args, response);
359 #if defined(AST_DEVMODE)
360  code = response->response_code;
361 
362  switch (code) {
363  case 0: /* Implementation is still a stub, or the code wasn't set */
364  is_valid = response->message == NULL;
365  break;
366  case 500: /* Internal Server Error */
367  case 501: /* Not Implemented */
368  case 404: /* Application does not exist. */
369  case 422: /* Event source not found. */
370  case 400: /* Invalid even tsource URI or userevent data. */
371  is_valid = 1;
372  break;
373  default:
374  if (200 <= code && code <= 299) {
375  is_valid = ast_ari_validate_void(
376  response->message);
377  } else {
378  ast_log(LOG_ERROR, "Invalid error response %d for /events/user/{eventName}\n", code);
379  is_valid = 0;
380  }
381  }
382 
383  if (!is_valid) {
384  ast_log(LOG_ERROR, "Response validation failed for /events/user/{eventName}\n");
385  ast_ari_response_error(response, 500,
386  "Internal Server Error", "Response validation failed");
387  }
388 #endif /* AST_DEVMODE */
389 
390 fin: __attribute__((unused))
391  ast_free(args.source_parse);
392  ast_free(args.source);
393  return;
394 }
395 
396 /*! \brief REST handler for /api-docs/events.json */
398  .path_segment = "eventName",
399  .is_wildcard = 1,
400  .callbacks = {
402  },
403  .num_children = 0,
404  .children = { }
405 };
406 /*! \brief REST handler for /api-docs/events.json */
408  .path_segment = "user",
409  .callbacks = {
410  },
411  .num_children = 1,
412  .children = { &events_user_eventName, }
413 };
414 /*! \brief REST handler for /api-docs/events.json */
415 static struct stasis_rest_handlers events = {
416  .path_segment = "events",
417  .callbacks = {
418  },
419  .num_children = 1,
420  .children = { &events_user, }
421 };
422 
423 static int unload_module(void)
424 {
425  ast_ari_remove_handler(&events);
426  ao2_cleanup(events.ws_server);
427  events.ws_server = NULL;
429  return 0;
430 }
431 
432 static int load_module(void)
433 {
434  int res = 0;
435 
436  struct ast_websocket_protocol *protocol;
437 
440  }
441 
443  if (!events.ws_server) {
446  }
447 
448  protocol = ast_websocket_sub_protocol_alloc("ari");
449  if (!protocol) {
450  ao2_ref(events.ws_server, -1);
451  events.ws_server = NULL;
454  }
457  res |= ast_websocket_server_add_protocol2(events.ws_server, protocol);
458 
459  res |= ast_ari_add_handler(&events);
460  if (res) {
461  unload_module();
463  }
464 
466 }
467 
468 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - WebSocket resource",
469  .support_level = AST_MODULE_SUPPORT_CORE,
470  .load = load_module,
471  .unload = unload_module,
472  .requires = "res_ari,res_ari_model,res_stasis,res_http_websocket",
473 );
struct ast_variable * next
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
#define MAX_VALS
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition: http.c:648
A websocket protocol implementation.
Generated file - declares stubs to be implemented in res/ari/resource_events.c.
static int load_module(void)
ast_websocket_pre_callback session_attempted
Callback called when a new session is attempted. Optional.
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.
void ast_json_free(void *p)
Asterisk&#39;s custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
Structure for variables, used for configurations and for channel variables.
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:763
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)
Generated file - Build validators for ARI model objects.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
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}.
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.
const char * args
#define NULL
Definition: resample.c:96
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
int ast_ari_validate_void(struct ast_json *json)
Validator for native Swagger void.
Definition: res_ari_model.c:91
struct ast_websocket_server *AST_OPTIONAL_API_NAME() ast_websocket_server_create(void)
int response_code
Definition: ari.h:98
#define ast_log
Definition: astobj2.c:42
struct ast_module * self
Definition: module.h:342
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
static int unload_module(void)
static struct ast_mansession session
#define ao2_ref(o, delta)
Definition: astobj2.h:464
static struct stasis_rest_handlers events
REST handler for /api-docs/events.json.
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:273
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
Support for WebSocket connections within the Asterisk HTTP server and client WebSocket connections to...
describes a server instance
Definition: tcptls.h:149
static struct stasis_rest_handlers events_user_eventName
REST handler for /api-docs/events.json.
struct ast_websocket_server * ws_server
Definition: ari.h:81
#define LOG_ERROR
Definition: logger.h:285
int ast_ari_add_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:179
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
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)
int ast_ari_remove_handler(struct stasis_rest_handlers *handler)
Definition: res_ari.c:202
int AST_OPTIONAL_API_NAME() ast_websocket_write(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
Write function for websocket traffic.
#define ast_free(a)
Definition: astmm.h:182
Structure definition for session.
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
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
enum ast_json_type ast_json_typeof(const struct ast_json *value)
Get the type of value.
Definition: json.c:78
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static struct stasis_rest_handlers events_user
REST handler for /api-docs/events.json.
#define SCOPED_MODULE_USE(module)
Definition: module.h:665
int ast_ari_websocket_events_event_websocket_init(void)
WebSocket connection for events.
struct ast_json * message
Definition: ari.h:93
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
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.
const char * path_segment
Definition: ari.h:70
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
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
void ast_ari_websocket_events_event_websocket_dtor(void)
WebSocket connection for events.
size_t ast_json_array_size(const struct ast_json *array)
Get the size of a JSON array.
Definition: json.c:356
Abstract JSON element (object, array, string, int, ...).
ari_validator ast_ari_validate_message_fn(void)
Function pointer to ast_ari_validate_message().
Stasis Application API. See Stasis Application API for detailed documentation.
ast_websocket_callback session_established
void AST_OPTIONAL_API_NAME() ast_websocket_unref(struct ast_websocket *session)
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.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc(const char *name)
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
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
Handler for a single RESTful path segment.
Definition: ari.h:68
#define ast_app_separate_args(a, b, c, d)