Asterisk - The Open Source Telephony Project  21.4.1
resource_endpoints.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 /*! \file
20  *
21  * \brief /api-docs/endpoints.{format} implementation- Endpoint resources
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25 
26 #include "asterisk.h"
27 
28 #include "resource_endpoints.h"
29 
30 #include "asterisk/astobj2.h"
31 #include "asterisk/stasis.h"
32 #include "asterisk/stasis_app.h"
34 #include "asterisk/channel.h"
35 #include "asterisk/message.h"
36 #include "asterisk/refer.h"
37 
38 void ast_ari_endpoints_list(struct ast_variable *headers,
39  struct ast_ari_endpoints_list_args *args,
40  struct ast_ari_response *response)
41 {
42  RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
43  RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
44  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
45  struct ao2_iterator i;
46  void *obj;
47 
49  if (!cache) {
51  response, 500, "Internal Server Error",
52  "Message bus not initialized");
53  return;
54  }
55  ao2_ref(cache, +1);
56 
58  if (!snapshots) {
60  return;
61  }
62 
63  json = ast_json_array_create();
64  if (!json) {
66  return;
67  }
68 
69  i = ao2_iterator_init(snapshots, 0);
70  while ((obj = ao2_iterator_next(&i))) {
71  RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
72  struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
73  struct ast_json *json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
74 
75  if (!json_endpoint || ast_json_array_append(json, json_endpoint)) {
78  return;
79  }
80  }
82 
83  ast_ari_response_ok(response, ast_json_ref(json));
84 }
85 
88  struct ast_ari_response *response)
89 {
90  RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
91  RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
92  RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
93  struct ast_endpoint *tech_endpoint;
94  struct ao2_iterator i;
95  void *obj;
96 
97  tech_endpoint = ast_endpoint_find_by_id(args->tech);
98  if (!tech_endpoint) {
99  ast_ari_response_error(response, 404, "Not Found",
100  "No Endpoints found - invalid tech %s", args->tech);
101  return;
102  }
103  ao2_ref(tech_endpoint, -1);
104 
106  if (!cache) {
108  response, 500, "Internal Server Error",
109  "Message bus not initialized");
110  return;
111  }
112  ao2_ref(cache, +1);
113 
115  if (!snapshots) {
117  return;
118  }
119 
120  json = ast_json_array_create();
121  if (!json) {
123  return;
124  }
125 
126  i = ao2_iterator_init(snapshots, 0);
127  while ((obj = ao2_iterator_next(&i))) {
128  RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
129  struct ast_endpoint_snapshot *snapshot = stasis_message_data(msg);
130  struct ast_json *json_endpoint;
131  int r;
132 
133  if (strcasecmp(args->tech, snapshot->tech) != 0) {
134  continue;
135  }
136 
137  json_endpoint = ast_endpoint_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
138  if (!json_endpoint) {
139  continue;
140  }
141 
143  json, json_endpoint);
144  if (r != 0) {
147  return;
148  }
149  }
151  ast_ari_response_ok(response, ast_json_ref(json));
152 }
153 
154 void ast_ari_endpoints_get(struct ast_variable *headers,
155  struct ast_ari_endpoints_get_args *args,
156  struct ast_ari_response *response)
157 {
158  struct ast_json *json;
159  RAII_VAR(struct ast_endpoint_snapshot *, snapshot, NULL, ao2_cleanup);
160 
161  snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource);
162  if (!snapshot) {
163  ast_ari_response_error(response, 404, "Not Found",
164  "Endpoint not found");
165  return;
166  }
167 
169  if (!json) {
171  return;
172  }
173 
174  ast_ari_response_ok(response, json);
175 }
176 
177 static void send_message(const char *to, const char *from, const char *body, struct ast_variable *variables, struct ast_ari_response *response)
178 {
179  struct ast_variable *current;
180  struct ast_msg *msg;
181  int res = 0;
182 
183  if (ast_strlen_zero(to)) {
184  ast_ari_response_error(response, 400, "Bad Request",
185  "To must be specified");
186  return;
187  }
188 
189  msg = ast_msg_alloc();
190  if (!msg) {
192  return;
193  }
194 
195  res |= ast_msg_set_from(msg, "%s", from);
196  res |= ast_msg_set_to(msg, "%s", to);
197 
198  if (!ast_strlen_zero(body)) {
199  res |= ast_msg_set_body(msg, "%s", body);
200  }
201 
202  for (current = variables; current; current = current->next) {
203  res |= ast_msg_set_var_outbound(msg, current->name, current->value);
204  }
205 
206  if (res) {
208  ast_msg_destroy(msg);
209  return;
210  }
211 
212  if (ast_msg_send(msg, to, from)) {
213  ast_ari_response_error(response, 404, "Not Found",
214  "Endpoint not found");
215  return;
216  }
217 
218  response->message = ast_json_null();
219  response->response_code = 202;
220  response->response_text = "Accepted";
221 }
222 
223 /*!
224  * \internal
225  * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
226  * \since 13.3.0
227  *
228  * \param[out] response HTTP response if error
229  * \param json_variables The JSON blob containing the variable
230  * \param[out] variables An out reference to the variables to populate.
231  *
232  * \retval 0 on success.
233  * \retval -1 on error.
234  */
235 static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
236 {
237  enum ast_json_to_ast_vars_code res;
238 
239  res = ast_json_to_ast_variables(json_variables, variables);
240  switch (res) {
242  return 0;
244  ast_ari_response_error(response, 400, "Bad Request",
245  "Only string values in the 'variables' object allowed");
246  break;
249  break;
250  }
251  ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to Asterisk variables\n");
252 
253  return -1;
254 }
255 
258  struct ast_ari_response *response)
259 {
260  struct ast_variable *variables = NULL;
261 
262  if (args->variables) {
263  struct ast_json *json_variables;
264 
265  ast_ari_endpoints_send_message_parse_body(args->variables, args);
266  json_variables = ast_json_object_get(args->variables, "variables");
267  if (json_variables
268  && json_to_ast_variables(response, json_variables, &variables)) {
269  return;
270  }
271  }
272 
273  send_message(args->to, args->from, args->body, variables, response);
274  ast_variables_destroy(variables);
275 }
276 
279  struct ast_ari_response *response)
280 {
281  struct ast_variable *variables = NULL;
282  struct ast_endpoint_snapshot *snapshot;
283  char msg_to[128];
284  char *tech = ast_strdupa(args->tech);
285 
286  /* Really, we just want to know if this thing exists */
287  snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource);
288  if (!snapshot) {
289  ast_ari_response_error(response, 404, "Not Found",
290  "Endpoint not found");
291  return;
292  }
293  ao2_ref(snapshot, -1);
294 
295  if (args->variables) {
296  struct ast_json *json_variables;
297 
299  json_variables = ast_json_object_get(args->variables, "variables");
300  if (json_variables
301  && json_to_ast_variables(response, json_variables, &variables)) {
302  return;
303  }
304  }
305 
306  snprintf(msg_to, sizeof(msg_to), "%s:%s", ast_str_to_lower(tech), args->resource);
307 
308  send_message(msg_to, args->from, args->body, variables, response);
309  ast_variables_destroy(variables);
310 }
311 
312 static void send_refer(const char *to, const char *from, const char *refer_to, int to_self, struct ast_variable *variables, struct ast_ari_response *response)
313 {
314  struct ast_variable *current;
315  struct ast_refer *refer;
316  int res = 0;
317 
318  if (ast_strlen_zero(to)) {
319  ast_ari_response_error(response, 400, "Bad Request",
320  "To must be specified");
321  return;
322  }
323 
324  refer = ast_refer_alloc();
325  if (!refer) {
327  return;
328  }
329 
330  ast_refer_set_to(refer, "%s", to);
331  ast_refer_set_to_self(refer, to_self);
332 
333  if (!ast_strlen_zero(from)) {
334  ast_refer_set_from(refer, "%s", from);
335  }
336  if (!ast_strlen_zero(refer_to)) {
337  ast_refer_set_refer_to(refer, "%s", refer_to);
338  }
339 
340  for (current = variables; current; current = current->next) {
341  res |= ast_refer_set_var_outbound(refer, current->name, current->value);
342  }
343 
344  if (res) {
346  ast_refer_destroy(refer);
347  return;
348  }
349 
350  if (ast_refer_send(refer)) {
351  ast_ari_response_error(response, 404, "Not Found",
352  "Endpoint not found");
353  return;
354  }
355 
356  response->message = ast_json_null();
357  response->response_code = 202;
358  response->response_text = "Accepted";
359 }
360 
361 static int parse_refer_json(struct ast_json *body,
362  struct ast_ari_response *response,
363  struct ast_variable **variables)
364 {
365  const char *known_variables[] = { "display_name" };
366  const char *value;
367  struct ast_variable *new_var;
368  struct ast_json *json_variable;
369  int err = 0;
370  int i;
371 
372  if (!body) {
373  return 0;
374  }
375 
376  json_variable = ast_json_object_get(body, "variables");
377  if (json_variable) {
378  err = json_to_ast_variables(response, json_variable, variables);
379  if (err) {
380  return err;
381  }
382  }
383 
384  for (i = 0; i < sizeof(known_variables) / sizeof(*known_variables); ++i) {
385  json_variable = ast_json_object_get(body, known_variables[i]);
386  if (json_variable && ast_json_typeof(json_variable) == AST_JSON_STRING) {
387  value = ast_json_string_get(json_variable);
388  new_var = ast_variable_new(known_variables[i], value, "");
389  if (new_var) {
390  ast_variable_list_append(variables, new_var);
391  }
392  }
393  }
394 
395  return err;
396 }
397 
399  struct ast_ari_endpoints_refer_args *args,
400  struct ast_ari_response *response)
401 {
402  struct ast_variable *variables = NULL;
403 
405 
406  if (parse_refer_json(args->variables, response, &variables)) {
407  return;
408  }
409 
410  send_refer(args->to, args->from, args->refer_to, args->to_self, variables, response);
411  ast_variables_destroy(variables);
412 }
413 
416  struct ast_ari_response *response)
417 {
418  struct ast_variable *variables = NULL;
419  struct ast_endpoint_snapshot *snapshot;
420  char to[128];
421  char *tech = ast_strdupa(args->tech);
422 
423  /* Really, we just want to know if this thing exists */
424  snapshot = ast_endpoint_latest_snapshot(args->tech, args->resource);
425  if (!snapshot) {
426  ast_ari_response_error(response, 404, "Not Found",
427  "Endpoint not found");
428  return;
429  }
430  ao2_ref(snapshot, -1);
431 
433 
434  if (parse_refer_json(args->variables, response, &variables)) {
435  return;
436  }
437 
438  snprintf(to, sizeof(to), "%s:%s", ast_str_to_lower(tech), args->resource);
439 
440  send_refer(to, args->from, args->refer_to, args->to_self, variables, response);
441  ast_variables_destroy(variables);
442 }
int ast_ari_endpoints_send_message_parse_body(struct ast_json *body, struct ast_ari_endpoints_send_message_args *args)
Body parsing function for /endpoints/sendMessage.
struct ast_variable * next
Out-of-call refer support.
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
Asterisk main include file. File version handling, generic pbx functions.
int ast_refer_set_var_outbound(struct ast_refer *refer, const char *name, const char *value)
Set a variable on the refer being sent to a refer tech directly.
Definition: refer.c:310
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
A refer.
Definition: refer.c:57
int ast_msg_set_body(struct ast_msg *msg, const char *fmt,...)
Set the 'body' text of a message (in UTF-8)
Definition: main/message.c:490
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_message_type * ast_endpoint_snapshot_type(void)
Message type for ast_endpoint_snapshot.
struct ast_endpoint_snapshot * ast_endpoint_latest_snapshot(const char *tech, const char *resource)
Retrieve the most recent snapshot for the endpoint with the given name.
struct ast_msg * ast_msg_alloc(void)
Allocate a message.
Definition: main/message.c:432
Structure for variables, used for configurations and for channel variables.
void ast_ari_endpoints_refer(struct ast_variable *headers, struct ast_ari_endpoints_refer_args *args, struct ast_ari_response *response)
Refer an endpoint or technology URI to some technology URI or endpoint.
int ast_refer_set_to_self(struct ast_refer *refer, int val)
Set the 'to_self' value of a refer.
Definition: refer.c:191
int ast_ari_endpoints_refer_parse_body(struct ast_json *body, struct ast_ari_endpoints_refer_args *args)
Body parsing function for /endpoints/refer.
void ast_ari_endpoints_get(struct ast_variable *headers, struct ast_ari_endpoints_get_args *args, struct ast_ari_response *response)
Details for an endpoint.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ast_endpoint * ast_endpoint_find_by_id(const char *id)
Finds the endpoint with the given tech[/resource] id.
struct ao2_container * stasis_cache_dump(struct stasis_cache *cache, struct stasis_message_type *type)
Dump cached items to a subscription for the ast_eid_default entity.
Definition: stasis_cache.c:736
Out-of-call text message support.
void ast_ari_endpoints_refer_to_endpoint(struct ast_variable *headers, struct ast_ari_endpoints_refer_to_endpoint_args *args, struct ast_ari_response *response)
Refer an endpoint or technology URI to some technology URI or endpoint.
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_msg_set_to(struct ast_msg *msg, const char *fmt,...)
Set the 'to' URI of a message.
Definition: main/message.c:468
int ast_ari_endpoints_send_message_to_endpoint_parse_body(struct ast_json *body, struct ast_ari_endpoints_send_message_to_endpoint_args *args)
Body parsing function for /endpoints/{tech}/{resource}/sendMessage.
int response_code
Definition: ari.h:99
int ast_msg_send(struct ast_msg *msg, const char *to, const char *from)
Send a msg directly to an endpoint.
int ast_refer_send(struct ast_refer *refer)
Send a refer directly to an endpoint.
Definition: refer.c:411
struct ast_json * ast_json_null(void)
Get the JSON null value.
Definition: json.c:248
Conversion failed because invalid value type supplied.
Definition: json.h:1116
int ast_refer_set_refer_to(struct ast_refer *refer, const char *fmt,...)
Set the 'refer_to' URI of a refer.
Definition: refer.c:180
void ast_ari_endpoints_list(struct ast_variable *headers, struct ast_ari_endpoints_list_args *args, struct ast_ari_response *response)
List all endpoints.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ast_ari_response_ok(struct ast_ari_response *response, struct ast_json *message)
Fill in an OK (200) ast_ari_response.
Definition: res_ari.c:276
int ast_refer_set_to(struct ast_refer *refer, const char *fmt,...)
Set the 'to' URI of a refer.
Definition: refer.c:158
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
static force_inline char * ast_str_to_lower(char *str)
Convert a string to all lower-case.
Definition: strings.h:1321
struct ast_json * ast_json_array_create(void)
Create a empty JSON array.
Definition: json.c:362
A snapshot of an endpoint's state.
void ast_ari_endpoints_list_by_tech(struct ast_variable *headers, struct ast_ari_endpoints_list_by_tech_args *args, struct ast_ari_response *response)
List available endoints for a given endpoint technology.
int ast_json_array_append(struct ast_json *array, struct ast_json *value)
Append to an array.
Definition: json.c:378
Conversion successful.
Definition: json.h:1111
struct stasis_message_sanitizer * stasis_app_get_sanitizer(void)
Get the Stasis message sanitizer for app_stasis applications.
Definition: res_stasis.c:2271
int ast_refer_set_from(struct ast_refer *refer, const char *fmt,...)
Set the 'from' URI of a refer.
Definition: refer.c:169
int ast_ari_endpoints_refer_to_endpoint_parse_body(struct ast_json *body, struct ast_ari_endpoints_refer_to_endpoint_args *args)
Body parsing function for /endpoints/{tech}/{resource}/refer.
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
const ast_string_field tech
enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables)
Convert a ast_json list of key/value pair tuples into a ast_variable list.
Definition: json.c:818
Conversion failed because of allocation failure. (Out Of Memory)
Definition: json.h:1118
const char * response_text
Definition: ari.h:103
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
struct ast_json * message
Definition: ari.h:94
void ast_ari_endpoints_send_message_to_endpoint(struct ast_variable *headers, struct ast_ari_endpoints_send_message_to_endpoint_args *args, struct ast_ari_response *response)
Send a message to some endpoint in a technology.
int ast_msg_set_from(struct ast_msg *msg, const char *fmt,...)
Set the 'from' URI of a message.
Definition: main/message.c:479
void ast_ari_endpoints_send_message(struct ast_variable *headers, struct ast_ari_endpoints_send_message_args *args, struct ast_ari_response *response)
Send a message to some technology URI or endpoint.
Generated file - declares stubs to be implemented in res/ari/resource_endpoints.c.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
int ast_msg_set_var_outbound(struct ast_msg *msg, const char *name, const char *value)
Set a variable on the message being sent to a message tech directly.
Definition: main/message.c:624
struct ast_refer * ast_refer_destroy(struct ast_refer *refer)
Destroy an ast_refer.
Definition: refer.c:152
Abstract JSON element (object, array, string, int, ...).
Stasis Application API. See Stasis Application API for detailed documentation.
Generic container type.
A message.
Definition: main/message.c:247
static struct sorcery_test_caching cache
Global scope caching structure for testing.
Definition: test_sorcery.c:178
struct ast_msg * ast_msg_destroy(struct ast_msg *msg)
Destroy an ast_msg.
Definition: main/message.c:462
#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:941
ast_json_to_ast_vars_code
Definition: json.h:1109
Endpoint abstractions.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct ast_json * ast_endpoint_snapshot_to_json(const struct ast_endpoint_snapshot *snapshot, const struct stasis_message_sanitizer *sanitize)
Build a JSON object from a ast_endpoint_snapshot.
struct ast_refer * ast_refer_alloc(void)
Allocate a refer.
Definition: refer.c:122
struct stasis_cache * ast_endpoint_cache(void)
Backend cache for ast_endpoint_topic_all_cached().