res_ari_events.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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. * !!!!! DO NOT EDIT !!!!!
  21. * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  22. * This file is generated by a mustache template. Please see the original
  23. * template in rest-api-templates/res_ari_resource.c.mustache
  24. */
  25. /*! \file
  26. *
  27. * \brief WebSocket resource
  28. *
  29. * \author David M. Lee, II <dlee@digium.com>
  30. */
  31. /*** MODULEINFO
  32. <depend type="module">res_ari</depend>
  33. <depend type="module">res_stasis</depend>
  34. <support_level>core</support_level>
  35. ***/
  36. #include "asterisk.h"
  37. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  38. #include "asterisk/app.h"
  39. #include "asterisk/module.h"
  40. #include "asterisk/stasis_app.h"
  41. #include "ari/resource_events.h"
  42. #if defined(AST_DEVMODE)
  43. #include "ari/ari_model_validators.h"
  44. #endif
  45. #include "asterisk/http_websocket.h"
  46. #define MAX_VALS 128
  47. static void ast_ari_events_event_websocket_ws_cb(struct ast_websocket *ws_session,
  48. struct ast_variable *get_params, struct ast_variable *headers)
  49. {
  50. struct ast_ari_events_event_websocket_args args = {};
  51. RAII_VAR(struct ast_ari_response *, response, NULL, ast_free);
  52. struct ast_variable *i;
  53. RAII_VAR(struct ast_websocket *, s, ws_session, ast_websocket_unref);
  54. RAII_VAR(struct ast_ari_websocket_session *, session, NULL, ao2_cleanup);
  55. response = ast_calloc(1, sizeof(*response));
  56. if (!response) {
  57. ast_log(LOG_ERROR, "Failed to create response.\n");
  58. goto fin;
  59. }
  60. #if defined(AST_DEVMODE)
  61. session = ast_ari_websocket_session_create(ws_session,
  62. ast_ari_validate_message_fn());
  63. #else
  64. session = ast_ari_websocket_session_create(ws_session, NULL);
  65. #endif
  66. if (!session) {
  67. ast_log(LOG_ERROR, "Failed to create ARI session\n");
  68. goto fin;
  69. }
  70. for (i = get_params; i; i = i->next) {
  71. if (strcmp(i->name, "app") == 0) {
  72. /* Parse comma separated list */
  73. char *vals[MAX_VALS];
  74. size_t j;
  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. 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. if (args.app_count == 0) {
  90. ast_ari_response_alloc_failed(response);
  91. goto fin;
  92. }
  93. if (args.app_count >= MAX_VALS) {
  94. ast_ari_response_error(response, 400,
  95. "Bad Request",
  96. "Too many values for app");
  97. goto fin;
  98. }
  99. args.app = ast_malloc(sizeof(*args.app) * args.app_count);
  100. if (!args.app) {
  101. ast_ari_response_alloc_failed(response);
  102. goto fin;
  103. }
  104. for (j = 0; j < args.app_count; ++j) {
  105. args.app[j] = (vals[j]);
  106. }
  107. } else
  108. {}
  109. }
  110. ast_ari_websocket_events_event_websocket(session, headers, &args);
  111. fin: __attribute__((unused))
  112. if (response && response->response_code != 0) {
  113. /* Param parsing failure */
  114. /* TODO - ideally, this would return the error code to the
  115. * HTTP client; but we've already done the WebSocket
  116. * negotiation. Param parsing should happen earlier, but we
  117. * need a way to pass it through the WebSocket code to the
  118. * callback */
  119. RAII_VAR(char *, msg, NULL, ast_json_free);
  120. if (response->message) {
  121. msg = ast_json_dump_string(response->message);
  122. } else {
  123. ast_log(LOG_ERROR, "Missing response message\n");
  124. }
  125. if (msg) {
  126. ast_websocket_write(ws_session,
  127. AST_WEBSOCKET_OPCODE_TEXT, msg, strlen(msg));
  128. }
  129. }
  130. ast_free(args.app_parse);
  131. ast_free(args.app);
  132. }
  133. int ast_ari_events_user_event_parse_body(
  134. struct ast_json *body,
  135. struct ast_ari_events_user_event_args *args)
  136. {
  137. struct ast_json *field;
  138. /* Parse query parameters out of it */
  139. field = ast_json_object_get(body, "application");
  140. if (field) {
  141. args->application = ast_json_string_get(field);
  142. }
  143. field = ast_json_object_get(body, "source");
  144. if (field) {
  145. /* If they were silly enough to both pass in a query param and a
  146. * JSON body, free up the query value.
  147. */
  148. ast_free(args->source);
  149. if (ast_json_typeof(field) == AST_JSON_ARRAY) {
  150. /* Multiple param passed as array */
  151. size_t i;
  152. args->source_count = ast_json_array_size(field);
  153. args->source = ast_malloc(sizeof(*args->source) * args->source_count);
  154. if (!args->source) {
  155. return -1;
  156. }
  157. for (i = 0; i < args->source_count; ++i) {
  158. args->source[i] = ast_json_string_get(ast_json_array_get(field, i));
  159. }
  160. } else {
  161. /* Multiple param passed as single value */
  162. args->source_count = 1;
  163. args->source = ast_malloc(sizeof(*args->source) * args->source_count);
  164. if (!args->source) {
  165. return -1;
  166. }
  167. args->source[0] = ast_json_string_get(field);
  168. }
  169. }
  170. return 0;
  171. }
  172. /*!
  173. * \brief Parameter parsing callback for /events/user/{eventName}.
  174. * \param get_params GET parameters in the HTTP request.
  175. * \param path_vars Path variables extracted from the request.
  176. * \param headers HTTP headers.
  177. * \param[out] response Response to the HTTP request.
  178. */
  179. static void ast_ari_events_user_event_cb(
  180. struct ast_tcptls_session_instance *ser,
  181. struct ast_variable *get_params, struct ast_variable *path_vars,
  182. struct ast_variable *headers, struct ast_ari_response *response)
  183. {
  184. struct ast_ari_events_user_event_args args = {};
  185. struct ast_variable *i;
  186. RAII_VAR(struct ast_json *, body, NULL, ast_json_unref);
  187. #if defined(AST_DEVMODE)
  188. int is_valid;
  189. int code;
  190. #endif /* AST_DEVMODE */
  191. for (i = get_params; i; i = i->next) {
  192. if (strcmp(i->name, "application") == 0) {
  193. args.application = (i->value);
  194. } else
  195. if (strcmp(i->name, "source") == 0) {
  196. /* Parse comma separated list */
  197. char *vals[MAX_VALS];
  198. size_t j;
  199. args.source_parse = ast_strdup(i->value);
  200. if (!args.source_parse) {
  201. ast_ari_response_alloc_failed(response);
  202. goto fin;
  203. }
  204. if (strlen(args.source_parse) == 0) {
  205. /* ast_app_separate_args can't handle "" */
  206. args.source_count = 1;
  207. vals[0] = args.source_parse;
  208. } else {
  209. args.source_count = ast_app_separate_args(
  210. args.source_parse, ',', vals,
  211. ARRAY_LEN(vals));
  212. }
  213. if (args.source_count == 0) {
  214. ast_ari_response_alloc_failed(response);
  215. goto fin;
  216. }
  217. if (args.source_count >= MAX_VALS) {
  218. ast_ari_response_error(response, 400,
  219. "Bad Request",
  220. "Too many values for source");
  221. goto fin;
  222. }
  223. args.source = ast_malloc(sizeof(*args.source) * args.source_count);
  224. if (!args.source) {
  225. ast_ari_response_alloc_failed(response);
  226. goto fin;
  227. }
  228. for (j = 0; j < args.source_count; ++j) {
  229. args.source[j] = (vals[j]);
  230. }
  231. } else
  232. {}
  233. }
  234. for (i = path_vars; i; i = i->next) {
  235. if (strcmp(i->name, "eventName") == 0) {
  236. args.event_name = (i->value);
  237. } else
  238. {}
  239. }
  240. /* Look for a JSON request entity */
  241. body = ast_http_get_json(ser, headers);
  242. if (!body) {
  243. switch (errno) {
  244. case EFBIG:
  245. ast_ari_response_error(response, 413, "Request Entity Too Large", "Request body too large");
  246. goto fin;
  247. case ENOMEM:
  248. ast_ari_response_error(response, 500, "Internal Server Error", "Error processing request");
  249. goto fin;
  250. case EIO:
  251. ast_ari_response_error(response, 400, "Bad Request", "Error parsing request body");
  252. goto fin;
  253. }
  254. }
  255. args.variables = ast_json_ref(body);
  256. ast_ari_events_user_event(headers, &args, response);
  257. #if defined(AST_DEVMODE)
  258. code = response->response_code;
  259. switch (code) {
  260. case 0: /* Implementation is still a stub, or the code wasn't set */
  261. is_valid = response->message == NULL;
  262. break;
  263. case 500: /* Internal Server Error */
  264. case 501: /* Not Implemented */
  265. case 404: /* Application does not exist. */
  266. case 422: /* Event source not found. */
  267. case 400: /* Invalid even tsource URI or userevent data. */
  268. is_valid = 1;
  269. break;
  270. default:
  271. if (200 <= code && code <= 299) {
  272. is_valid = ast_ari_validate_void(
  273. response->message);
  274. } else {
  275. ast_log(LOG_ERROR, "Invalid error response %d for /events/user/{eventName}\n", code);
  276. is_valid = 0;
  277. }
  278. }
  279. if (!is_valid) {
  280. ast_log(LOG_ERROR, "Response validation failed for /events/user/{eventName}\n");
  281. ast_ari_response_error(response, 500,
  282. "Internal Server Error", "Response validation failed");
  283. }
  284. #endif /* AST_DEVMODE */
  285. fin: __attribute__((unused))
  286. ast_free(args.source_parse);
  287. ast_free(args.source);
  288. return;
  289. }
  290. /*! \brief REST handler for /api-docs/events.{format} */
  291. static struct stasis_rest_handlers events_user_eventName = {
  292. .path_segment = "eventName",
  293. .is_wildcard = 1,
  294. .callbacks = {
  295. [AST_HTTP_POST] = ast_ari_events_user_event_cb,
  296. },
  297. .num_children = 0,
  298. .children = { }
  299. };
  300. /*! \brief REST handler for /api-docs/events.{format} */
  301. static struct stasis_rest_handlers events_user = {
  302. .path_segment = "user",
  303. .callbacks = {
  304. },
  305. .num_children = 1,
  306. .children = { &events_user_eventName, }
  307. };
  308. /*! \brief REST handler for /api-docs/events.{format} */
  309. static struct stasis_rest_handlers events = {
  310. .path_segment = "events",
  311. .callbacks = {
  312. },
  313. .num_children = 1,
  314. .children = { &events_user, }
  315. };
  316. static int load_module(void)
  317. {
  318. int res = 0;
  319. events.ws_server = ast_websocket_server_create();
  320. if (!events.ws_server) {
  321. return AST_MODULE_LOAD_FAILURE;
  322. }
  323. res |= ast_websocket_server_add_protocol(events.ws_server,
  324. "ari", ast_ari_events_event_websocket_ws_cb);
  325. stasis_app_ref();
  326. res |= ast_ari_add_handler(&events);
  327. return res;
  328. }
  329. static int unload_module(void)
  330. {
  331. ast_ari_remove_handler(&events);
  332. ao2_cleanup(events.ws_server);
  333. events.ws_server = NULL;
  334. stasis_app_unref();
  335. return 0;
  336. }
  337. AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "RESTful API module - WebSocket resource",
  338. .support_level = AST_MODULE_SUPPORT_CORE,
  339. .load = load_module,
  340. .unload = unload_module,
  341. .nonoptreq = "res_ari,res_stasis",
  342. );