Asterisk - The Open Source Telephony Project  21.4.1
res_aeap.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2021, Sangoma Technologies Corporation
5  *
6  * Ben Ford <bford@sangoma.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 /*** MODULEINFO
20  <depend>res_http_websocket</depend>
21  <support_level>core</support_level>
22  ***/
23 
24 #include "asterisk.h"
25 
26 #include "asterisk/astobj2.h"
27 #include "asterisk/module.h"
28 #include "asterisk/sorcery.h"
29 #include "asterisk/cli.h"
30 #include "asterisk/format.h"
31 #include "asterisk/format_cap.h"
32 #include "asterisk/res_aeap.h"
33 
34 #include "res_aeap/general.h"
35 
36 /*** DOCUMENTATION
37  <configInfo name="res_aeap" language="en_US">
38  <synopsis>Asterisk External Application Protocol (AEAP) module for Asterisk</synopsis>
39  <configFile name="aeap.conf">
40  <configObject name="client">
41  <synopsis>AEAP client options</synopsis>
42  <configOption name="type">
43  <synopsis>Must be of type 'client'.</synopsis>
44  </configOption>
45  <configOption name="url">
46  <synopsis>The URL of the server to connect to.</synopsis>
47  </configOption>
48  <configOption name="protocol">
49  <synopsis>The application protocol.</synopsis>
50  </configOption>
51  <configOption name="codecs">
52  <synopsis>Optional media codec(s)</synopsis>
53  <description><para>
54  If this is specified, Asterisk will use this for codec related negotiations
55  with the external application. Otherwise, Asterisk will default to using the
56  codecs configured on the endpoint.
57  </para></description>
58  </configOption>
59  </configObject>
60  </configFile>
61  </configInfo>
62  ***/
63 
64 /* Asterisk External Application Protocol sorcery object */
65 static struct ast_sorcery *aeap_sorcery;
66 
68  return aeap_sorcery;
69 }
70 
72 {
73  SORCERY_OBJECT(details);
75  /*! The URL of the server to connect to */
77  /*! The application protocol */
79  );
80  /*! An optional list of codecs that will be used if provided */
82 };
83 
84 static void client_config_destructor(void *obj)
85 {
86  struct ast_aeap_client_config *cfg = obj;
87 
89  ao2_cleanup(cfg->codecs);
90 }
91 
92 static void *client_config_alloc(const char *name)
93 {
94  struct ast_aeap_client_config *cfg;
95 
96  cfg = ast_sorcery_generic_alloc(sizeof(*cfg), client_config_destructor);
97  if (!cfg) {
98  return NULL;
99  }
100 
101  if (ast_string_field_init(cfg, 512)) {
102  ao2_ref(cfg, -1);
103  return NULL;
104  }
105 
107  ao2_ref(cfg, -1);
108  return NULL;
109  }
110 
111  return cfg;
112 }
113 
114 static int client_config_apply(const struct ast_sorcery *sorcery, void *obj)
115 {
116  struct ast_aeap_client_config *cfg = obj;
117 
118  if (ast_strlen_zero(cfg->url)) {
119  ast_log(LOG_ERROR, "AEAP - URL must be present for '%s'\n", ast_sorcery_object_get_id(cfg));
120  return -1;
121  }
122 
123  if (!ast_begins_with(cfg->url, "ws")) {
124  ast_log(LOG_ERROR, "AEAP - URL must be ws or wss for '%s'\n", ast_sorcery_object_get_id(cfg));
125  return -1;
126  }
127 
128  return 0;
129 }
130 
132 {
133  return cfg->codecs;
134 }
135 
137  const char *protocol)
138 {
139  return !strcmp(protocol, cfg->protocol);
140 }
141 
142 struct ao2_container *ast_aeap_client_configs_get(const char *protocol)
143 {
144  struct ao2_container *container;
145  struct ast_variable *var;
146 
147  var = protocol ? ast_variable_new("protocol ==", protocol, "") : NULL;
148 
149  container = ast_sorcery_retrieve_by_fields(aeap_sorcery,
150  AEAP_CONFIG_CLIENT, AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, var);
151 
153 
154  return container;
155 }
156 
157 static struct ast_aeap_client_config *client_config_get(const char *id)
158 {
159  return ast_sorcery_retrieve_by_id(aeap_sorcery, AEAP_CONFIG_CLIENT, id);
160 }
161 
162 static char *aeap_tab_complete_name(const char *word, struct ao2_container *container)
163 {
164  void *obj;
165  struct ao2_iterator it;
166  int wordlen = strlen(word);
167  int ret;
168 
169  it = ao2_iterator_init(container, 0);
170  while ((obj = ao2_iterator_next(&it))) {
171  if (!strncasecmp(word, ast_sorcery_object_get_id(obj), wordlen)) {
173  if (ret) {
174  ao2_ref(obj, -1);
175  break;
176  }
177  }
178  ao2_ref(obj, -1);
179  }
181 
182  ao2_ref(container, -1);
183 
184  return NULL;
185 }
186 
187 static int aeap_cli_show(void *obj, void *arg, int flags)
188 {
189  struct ast_cli_args *a = arg;
190  struct ast_variable *options;
191  struct ast_variable *i;
192 
193  if (!obj) {
194  ast_cli(a->fd, "No AEAP configuration found\n");
195  return 0;
196  }
197 
198  options = ast_variable_list_sort(ast_sorcery_objectset_create(aeap_sorcery, obj));
199  if (!options) {
200  return 0;
201  }
202 
203  ast_cli(a->fd, "%s: %s\n", ast_sorcery_object_get_type(obj),
205 
206  for (i = options; i; i = i->next) {
207  ast_cli(a->fd, "\t%s: %s\n", i->name, i->value);
208  }
209 
210  ast_cli(a->fd, "\n");
211 
212  ast_variables_destroy(options);
213 
214  return 0;
215 }
216 
217 static char *client_config_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
218 {
219  struct ast_aeap_client_config *cfg;
220 
221  switch(cmd) {
222  case CLI_INIT:
223  e->command = "aeap show client";
224  e->usage =
225  "Usage: aeap show client <id>\n"
226  " Show the AEAP settings for a given client\n";
227  return NULL;
228  case CLI_GENERATE:
229  if (a->pos == 3) {
230  return aeap_tab_complete_name(a->word, ast_aeap_client_configs_get(NULL));
231  } else {
232  return NULL;
233  }
234  }
235 
236  if (a->argc != 4) {
237  return CLI_SHOWUSAGE;
238  }
239 
240  cfg = client_config_get(a->argv[3]);
241  aeap_cli_show(cfg, a, 0);
242  ao2_cleanup(cfg);
243 
244  return CLI_SUCCESS;
245 }
246 
247 static char *client_config_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
248 {
249  struct ao2_container *container;
250 
251  switch(cmd) {
252  case CLI_INIT:
253  e->command = "aeap show clients";
254  e->usage =
255  "Usage: aeap show clients\n"
256  " Show all configured AEAP clients\n";
257  return NULL;
258  case CLI_GENERATE:
259  return NULL;
260  }
261 
262  if (a->argc != 3) {
263  return CLI_SHOWUSAGE;
264  }
265 
266  container = ast_aeap_client_configs_get(NULL);
267  if (!container || ao2_container_count(container) == 0) {
268  ast_cli(a->fd, "No AEAP clients found\n");
269  ao2_cleanup(container);
270  return CLI_SUCCESS;
271  }
272 
273  ao2_callback(container, OBJ_NODATA, aeap_cli_show, a);
274  ao2_ref(container, -1);
275 
276  return CLI_SUCCESS;
277 }
278 
279 static struct ast_cli_entry aeap_cli[] = {
280  AST_CLI_DEFINE(client_config_show, "Show AEAP client configuration by id"),
281  AST_CLI_DEFINE(client_config_show_all, "Show all AEAP client configurations"),
282 };
283 
284 static struct ast_aeap *aeap_create(const char *id, const struct ast_aeap_params *params,
285  int connect, int timeout)
286 {
287  struct ast_aeap_client_config *cfg;
288  struct ast_aeap *aeap;
289  const char *url = NULL;
290  const char *protocol = NULL;
291 
292  cfg = client_config_get(id);
293  if (cfg) {
294  url = cfg->url;
295  protocol = cfg->protocol;
296  }
297 
298 #ifdef TEST_FRAMEWORK
299  else if (ast_begins_with(id, "_aeap_test_")) {
300  url = "ws://127.0.0.1:8088/ws";
301  protocol = id;
302  }
303 #endif
304 
305  if (!url && !protocol) {
306  ast_log(LOG_ERROR, "AEAP: unable to get configuration for '%s'\n", id);
307  return NULL;
308  }
309 
310  aeap = connect ? ast_aeap_create_and_connect(url, params, url, protocol, timeout) :
311  ast_aeap_create(url, params);
312 
313  ao2_cleanup(cfg);
314  return aeap;
315 }
316 
317 struct ast_aeap *ast_aeap_create_by_id(const char *id, const struct ast_aeap_params *params)
318 {
319  return aeap_create(id, params, 0, 0);
320 }
321 
323  const struct ast_aeap_params *params, int timeout)
324 {
325  return aeap_create(id, params, 1, timeout);
326 }
327 
329 {
330  struct ast_aeap_client_config *cfg;
331  struct ast_variable *vars;
332 
333  cfg = client_config_get(id);
334  if (!cfg) {
335  ast_log(LOG_WARNING, "AEAP: no client configuration '%s' to get fields\n", id);
336  return NULL;
337  }
338 
339  vars = ast_sorcery_objectset_create(aeap_sorcery, cfg);
340 
341  ao2_ref(cfg, -1);
342  return vars;
343 }
344 
345 static int reload_module(void)
346 {
347  ast_sorcery_reload(aeap_sorcery);
348 
349  return 0;
350 }
351 
352 static int unload_module(void)
353 {
354  ast_sorcery_unref(aeap_sorcery);
355  aeap_sorcery = NULL;
356 
357  ast_cli_unregister_multiple(aeap_cli, ARRAY_LEN(aeap_cli));
358 
359  aeap_general_finalize();
360 
361  return 0;
362 }
363 
364 static int load_module(void)
365 {
366  if (aeap_general_initialize()) {
368  }
369 
370  if (!(aeap_sorcery = ast_sorcery_open()))
371  {
372  ast_log(LOG_ERROR, "AEAP - failed to open sorcery\n");
374  }
375 
376  ast_sorcery_apply_default(aeap_sorcery, AEAP_CONFIG_CLIENT, "config", "aeap.conf,criteria=type=client");
377 
378  if (ast_sorcery_object_register(aeap_sorcery, "client", client_config_alloc,
379  NULL, client_config_apply)) {
380  ast_log(LOG_ERROR, "AEAP - failed to register client sorcery object\n");
382  }
383 
384  ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "type", "", OPT_NOOP_T, 0, 0);
385  ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "url", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_aeap_client_config, url));
386  ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "protocol", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_aeap_client_config, protocol));
387  ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "codecs", "", OPT_CODEC_T, 1, FLDSET(struct ast_aeap_client_config, codecs));
388 
389  ast_sorcery_load(aeap_sorcery);
390 
391  ast_cli_register_multiple(aeap_cli, ARRAY_LEN(aeap_cli));
392 
393  return 0;
394 }
395 
396 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER,
397  "Asterisk External Application Protocol Module for Asterisk",
398  .support_level = AST_MODULE_SUPPORT_CORE,
399  .load = load_module,
400  .unload = unload_module,
401  .reload = reload_module,
402  .load_pri = AST_MODPRI_CHANNEL_DEPEND,
403  .requires = "res_http_websocket",
404 );
const ast_string_field url
Definition: res_aeap.c:79
struct ast_variable * next
Type for default option handler for format capabilities.
Asterisk External Application Protocol API.
Callbacks and other parameters used by an Asterisk external application object.
Definition: res_aeap.h:144
struct ast_variable * ast_variable_list_sort(struct ast_variable *head)
Performs an in-place sort on the variable list by ascending name.
Definition: main/config.c:620
Asterisk main include file. File version handling, generic pbx functions.
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int ast_aeap_client_config_has_protocol(const struct ast_aeap_client_config *cfg, const char *protocol)
Check a given protocol against that in an Asterisk external application configuration.
Definition: res_aeap.c:136
descriptor for a cli entry.
Definition: cli.h:171
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
Structure for variables, used for configurations and for channel variables.
Perform no matching, return all objects.
Definition: sorcery.h:123
Full structure for sorcery.
Definition: sorcery.c:230
Type for a default handler that should do nothing.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
Definition: stringfields.h:341
Return all matching objects.
Definition: sorcery.h:120
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
struct ast_sorcery * ast_aeap_sorcery(void)
Retrieve the AEAP sorcery object.
Definition: res_aeap.c:67
struct ast_aeap * ast_aeap_create(const char *type, const struct ast_aeap_params *params)
Create an Asterisk external application object.
Definition: aeap.c:88
const char * ast_sorcery_object_get_type(const void *object)
Get the type of a sorcery object.
Definition: sorcery.c:2329
Media Format API.
void * ast_sorcery_retrieve_by_id(const struct ast_sorcery *sorcery, const char *type, const char *id)
Retrieve an object using its unique identifier.
Definition: sorcery.c:1853
#define ast_sorcery_unref(sorcery)
Decrease the reference count of a sorcery structure.
Definition: sorcery.h:1500
#define FLDSET(type,...)
Convert a struct and list of fields to an argument list of field offsets.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
static struct ao2_container * codecs
Registered codecs.
Definition: codec.c:48
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_STRING_FIELD(name)
Declare a string field.
Definition: stringfields.h:303
const char * ast_sorcery_object_get_id(const void *object)
Get the unique identifier of a sorcery object.
Definition: sorcery.c:2317
struct ao2_container * container
Definition: res_fax.c:501
struct ast_aeap * ast_aeap_create_by_id(const char *id, const struct ast_aeap_params *params)
Create an Asterisk external application object by sorcery id.
Definition: res_aeap.c:317
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
#define ast_sorcery_object_register(sorcery, type, alloc, transform, apply)
Register an object type.
Definition: sorcery.h:837
const struct ast_aeap_params * params
Definition: aeap.c:49
struct ast_aeap * ast_aeap_create_and_connect(const char *type, const struct ast_aeap_params *params, const char *url, const char *protocol, int timeout)
Create and connect to an Asterisk external application.
Definition: aeap.c:363
struct ast_aeap * ast_aeap_create_and_connect_by_id(const char *id, const struct ast_aeap_params *params, int timeout)
Create and connect to an Asterisk external application by sorcery id.
Definition: res_aeap.c:322
Format Capabilities API.
struct ast_variable * ast_aeap_custom_fields_get(const char *id)
Retrieve a list of custom configuration fields.
Definition: res_aeap.c:328
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
#define ast_sorcery_objectset_create(sorcery, object)
Create an object set (KVP list) for an object.
Definition: sorcery.h:1137
char * command
Definition: cli.h:186
#define STRFLDSET(type,...)
Convert a struct and a list of stringfield fields to an argument list of field offsets.
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
const char * usage
Definition: cli.h:177
void * ast_sorcery_retrieve_by_fields(const struct ast_sorcery *sorcery, const char *type, unsigned int flags, struct ast_variable *fields)
Retrieve an object or multiple objects using specific fields.
Definition: sorcery.c:1897
#define ast_sorcery_object_field_register(sorcery, type, name, default_val, opt_type, flags,...)
Register a field within an object.
Definition: sorcery.h:955
void ast_sorcery_load(const struct ast_sorcery *sorcery)
Inform any wizards to load persistent objects.
Definition: sorcery.c:1377
void ast_sorcery_reload(const struct ast_sorcery *sorcery)
Inform any wizards to reload persistent objects.
Definition: sorcery.c:1408
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Standard Command Line Interface.
#define ast_sorcery_open()
Open a new sorcery structure.
Definition: sorcery.h:406
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition: strings.h:97
Type for default option handler for stringfields.
const ast_string_field protocol
Definition: res_aeap.c:79
Generic container type.
const struct ast_format_cap * ast_aeap_client_config_codecs(const struct ast_aeap_client_config *cfg)
Retrieve codec capabilities from the configuration.
Definition: res_aeap.c:131
void * ast_sorcery_generic_alloc(size_t size, ao2_destructor_fn destructor)
Allocate a generic sorcery capable object.
Definition: sorcery.c:1728
struct ao2_container * ast_aeap_client_configs_get(const char *protocol)
Retrieve a listing of all client configuration objects by protocol.
Definition: res_aeap.c:142
int ast_cli_completion_add(char *value)
Add a result to a request for completion options.
Definition: main/cli.c:2761
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
struct ast_format_cap * codecs
Definition: res_aeap.c:81
Sorcery Data Access Layer API.
Definition: aeap.c:47