Asterisk - The Open Source Telephony Project  21.4.1
refer.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2023, Commend International
5  *
6  * Maximilian Fridrich <m.fridrich@commend.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 Out-of-call refer support
22  *
23  * \author Maximilian Fridrich <m.fridrich@commend.com>
24  */
25 
26 /*** MODULEINFO
27  <support_level>core</support_level>
28  ***/
29 
30 #include "asterisk.h"
31 
32 #include "asterisk/_private.h"
33 
34 #include "asterisk/module.h"
35 #include "asterisk/datastore.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/manager.h"
38 #include "asterisk/strings.h"
39 #include "asterisk/astobj2.h"
40 #include "asterisk/vector.h"
41 #include "asterisk/app.h"
42 #include "asterisk/taskprocessor.h"
43 #include "asterisk/refer.h"
44 
45 struct refer_data {
46  /* Stored in stuff[] at struct end */
47  char *name;
48  /* Stored separately */
49  char *value;
50  /* Holds name */
51  char stuff[0];
52 };
53 
54 /*!
55  * \brief A refer.
56  */
57 struct ast_refer {
59  /*! Where the refer is going */
61  /*! Where we "say" the refer came from */
63  /*! Where to refer to */
65  /*! An endpoint associated with this refer */
67  /*! The technology of the endpoint associated with this refer */
69  );
70  /* Whether to refer to Asterisk itself, if refer_to is an Asterisk endpoint. */
71  int to_self;
72  /*! Technology/dialplan specific variables associated with the refer */
74 };
75 
76 /*! \brief Lock for \c refer_techs vector */
78 
79 /*! \brief Vector of refer technologies */
80 AST_VECTOR(, const struct ast_refer_tech *) refer_techs;
81 
82 static int refer_data_cmp_fn(void *obj, void *arg, int flags)
83 {
84  const struct refer_data *object_left = obj;
85  const struct refer_data *object_right = arg;
86  const char *right_key = arg;
87  int cmp;
88 
89  switch (flags & OBJ_SEARCH_MASK) {
90  case OBJ_SEARCH_OBJECT:
91  right_key = object_right->name;
92  case OBJ_SEARCH_KEY:
93  cmp = strcasecmp(object_left->name, right_key);
94  break;
96  cmp = strncasecmp(object_left->name, right_key, strlen(right_key));
97  break;
98  default:
99  cmp = 0;
100  break;
101  }
102  if (cmp) {
103  return 0;
104  }
105  return CMP_MATCH;
106 }
107 
108 static void refer_data_destructor(void *obj)
109 {
110  struct refer_data *data = obj;
111  ast_free(data->value);
112 }
113 
114 static void refer_destructor(void *obj)
115 {
116  struct ast_refer *refer = obj;
117 
119  ao2_cleanup(refer->vars);
120 }
121 
123 {
124  struct ast_refer *refer;
125 
126  if (!(refer = ao2_alloc_options(sizeof(*refer), refer_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
127  return NULL;
128  }
129 
130  if (ast_string_field_init(refer, 128)) {
131  ao2_ref(refer, -1);
132  return NULL;
133  }
134 
136  NULL, refer_data_cmp_fn);
137  if (!refer->vars) {
138  ao2_ref(refer, -1);
139  return NULL;
140  }
141  refer->to_self = 0;
142 
143  return refer;
144 }
145 
146 struct ast_refer *ast_refer_ref(struct ast_refer *refer)
147 {
148  ao2_ref(refer, 1);
149  return refer;
150 }
151 
152 struct ast_refer *ast_refer_destroy(struct ast_refer *refer)
153 {
154  ao2_ref(refer, -1);
155  return NULL;
156 }
157 
158 int ast_refer_set_to(struct ast_refer *refer, const char *fmt, ...)
159 {
160  va_list ap;
161 
162  va_start(ap, fmt);
163  ast_string_field_build_va(refer, to, fmt, ap);
164  va_end(ap);
165 
166  return 0;
167 }
168 
169 int ast_refer_set_from(struct ast_refer *refer, const char *fmt, ...)
170 {
171  va_list ap;
172 
173  va_start(ap, fmt);
174  ast_string_field_build_va(refer, from, fmt, ap);
175  va_end(ap);
176 
177  return 0;
178 }
179 
180 int ast_refer_set_refer_to(struct ast_refer *refer, const char *fmt, ...)
181 {
182  va_list ap;
183 
184  va_start(ap, fmt);
185  ast_string_field_build_va(refer, refer_to, fmt, ap);
186  va_end(ap);
187 
188  return 0;
189 }
190 
191 int ast_refer_set_to_self(struct ast_refer *refer, int val)
192 {
193  refer->to_self = val;
194  return 0;
195 }
196 
197 int ast_refer_set_tech(struct ast_refer *refer, const char *fmt, ...)
198 {
199  va_list ap;
200 
201  va_start(ap, fmt);
202  ast_string_field_build_va(refer, tech, fmt, ap);
203  va_end(ap);
204 
205  return 0;
206 }
207 
208 int ast_refer_set_endpoint(struct ast_refer *refer, const char *fmt, ...)
209 {
210  va_list ap;
211 
212  va_start(ap, fmt);
213  ast_string_field_build_va(refer, endpoint, fmt, ap);
214  va_end(ap);
215 
216  return 0;
217 }
218 
219 const char *ast_refer_get_refer_to(const struct ast_refer *refer)
220 {
221  return refer->refer_to;
222 }
223 
224 const char *ast_refer_get_from(const struct ast_refer *refer)
225 {
226  return refer->from;
227 }
228 
229 const char *ast_refer_get_to(const struct ast_refer *refer)
230 {
231  return refer->to;
232 }
233 
234 int ast_refer_get_to_self(const struct ast_refer *refer)
235 {
236  return refer->to_self;
237 }
238 
239 const char *ast_refer_get_tech(const struct ast_refer *refer)
240 {
241  return refer->tech;
242 }
243 
244 const char *ast_refer_get_endpoint(const struct ast_refer *refer)
245 {
246  return refer->endpoint;
247 }
248 
249 static struct refer_data *refer_data_new(const char *name)
250 {
251  struct refer_data *data;
252  int name_len = strlen(name) + 1;
253 
254  if ((data = ao2_alloc_options(name_len + sizeof(*data), refer_data_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK))) {
255  data->name = data->stuff;
256  strcpy(data->name, name);
257  }
258 
259  return data;
260 }
261 
262 static struct refer_data *refer_data_find(struct ao2_container *vars, const char *name)
263 {
264  return ao2_find(vars, name, OBJ_SEARCH_KEY);
265 }
266 
267 char *ast_refer_get_var_and_unlink(struct ast_refer *refer, const char *name)
268 {
269  struct refer_data *data;
270  char *val = NULL;
271 
272  if (!(data = ao2_find(refer->vars, name, OBJ_SEARCH_KEY | OBJ_UNLINK))) {
273  return NULL;
274  }
275 
276  val = ast_strdup(data->value);
277  ao2_ref(data, -1);
278 
279  return val;
280 }
281 
282 static int refer_set_var_full(struct ast_refer *refer, const char *name, const char *value)
283 {
284  struct refer_data *data;
285 
286  if (!(data = refer_data_find(refer->vars, name))) {
287  if (ast_strlen_zero(value)) {
288  return 0;
289  }
290  if (!(data = refer_data_new(name))) {
291  return -1;
292  };
293  data->value = ast_strdup(value);
294 
295  ao2_link(refer->vars, data);
296  } else {
297  if (ast_strlen_zero(value)) {
298  ao2_unlink(refer->vars, data);
299  } else {
300  ast_free(data->value);
301  data->value = ast_strdup(value);
302  }
303  }
304 
305  ao2_ref(data, -1);
306 
307  return 0;
308 }
309 
310 int ast_refer_set_var_outbound(struct ast_refer *refer, const char *name, const char *value)
311 {
312  return refer_set_var_full(refer, name, value);
313 }
314 
315 const char *ast_refer_get_var(struct ast_refer *refer, const char *name)
316 {
317  struct refer_data *data;
318  const char *val = NULL;
319 
320  if (!(data = refer_data_find(refer->vars, name))) {
321  return NULL;
322  }
323 
324  val = data->value;
325  ao2_ref(data, -1);
326 
327  return val;
328 }
329 
331  struct ao2_iterator iter;
332  struct refer_data *current_used;
333 };
334 
336 {
337  struct ast_refer_var_iterator *iter;
338 
339  iter = ast_calloc(1, sizeof(*iter));
340  if (!iter) {
341  return NULL;
342  }
343 
344  iter->iter = ao2_iterator_init(refer->vars, 0);
345 
346  return iter;
347 }
348 
349 int ast_refer_var_iterator_next(struct ast_refer_var_iterator *iter, const char **name, const char **value)
350 {
351  struct refer_data *data;
352 
353  if (!iter) {
354  return 0;
355  }
356 
357  data = ao2_iterator_next(&iter->iter);
358  if (!data) {
359  return 0;
360  }
361 
362  *name = data->name;
363  *value = data->value;
364 
365  iter->current_used = data;
366 
367  return 1;
368 }
369 
371 {
372  ao2_cleanup(iter->current_used);
373  iter->current_used = NULL;
374 }
375 
377 {
378  if (iter) {
379  ao2_iterator_destroy(&iter->iter);
381  ast_free(iter);
382  }
383 }
384 
385 /*!
386  * \internal \brief Find a \c ast_refer_tech by its technology name
387  *
388  * \param tech_name The name of the refer technology
389  *
390  * \note \c refer_techs should be locked via \c refer_techs_lock prior to
391  * calling this function
392  *
393  * \retval NULL if no \ref ast_refer_tech has been registered
394  * \return \ref ast_refer_tech if registered
395  */
396 static const struct ast_refer_tech *refer_find_by_tech_name(const char *tech_name)
397 {
398  const struct ast_refer_tech *current;
399  int i;
400 
401  for (i = 0; i < AST_VECTOR_SIZE(&refer_techs); i++) {
402  current = AST_VECTOR_GET(&refer_techs, i);
403  if (!strcmp(current->name, tech_name)) {
404  return current;
405  }
406  }
407 
408  return NULL;
409 }
410 
411 int ast_refer_send(struct ast_refer *refer)
412 {
413  char *tech_name = NULL;
414  const struct ast_refer_tech *refer_tech;
415  int res = -1;
416 
417  if (ast_strlen_zero(refer->to)) {
418  ao2_ref(refer, -1);
419  return -1;
420  }
421 
422  tech_name = ast_strdupa(refer->to);
423  tech_name = strsep(&tech_name, ":");
424 
425  ast_rwlock_rdlock(&refer_techs_lock);
426  refer_tech = refer_find_by_tech_name(tech_name);
427 
428  if (!refer_tech) {
429  ast_log(LOG_ERROR, "Unknown refer tech: %s\n", tech_name);
430  ast_rwlock_unlock(&refer_techs_lock);
431  ao2_ref(refer, -1);
432  return -1;
433  }
434 
435  ao2_lock(refer);
436  res = refer_tech->refer_send(refer);
437  ao2_unlock(refer);
438 
439  ast_rwlock_unlock(&refer_techs_lock);
440 
441  ao2_ref(refer, -1);
442 
443  return res;
444 }
445 
447 {
448  const struct ast_refer_tech *match;
449 
450  ast_rwlock_wrlock(&refer_techs_lock);
451 
452  match = refer_find_by_tech_name(tech->name);
453  if (match) {
454  ast_log(LOG_ERROR, "Refer technology already registered for '%s'\n",
455  tech->name);
456  ast_rwlock_unlock(&refer_techs_lock);
457  return -1;
458  }
459 
460  if (AST_VECTOR_APPEND(&refer_techs, tech)) {
461  ast_log(LOG_ERROR, "Failed to register refer technology for '%s'\n",
462  tech->name);
463  ast_rwlock_unlock(&refer_techs_lock);
464  return -1;
465  }
466  ast_verb(5, "Refer technology '%s' registered.\n", tech->name);
467 
468  ast_rwlock_unlock(&refer_techs_lock);
469 
470  return 0;
471 }
472 
473 /*!
474  * \brief Comparison callback for \c ast_refer_tech vector removal
475  *
476  * \param vec_elem The element in the vector being compared
477  * \param srch The element being looked up
478  *
479  * \retval non-zero The items are equal
480  * \retval 0 The items are not equal
481  */
482 static int refer_tech_cmp(const struct ast_refer_tech *vec_elem, const struct ast_refer_tech *srch)
483 {
484  if (!vec_elem->name || !srch->name) {
485  return (vec_elem->name == srch->name) ? 1 : 0;
486  }
487  return !strcmp(vec_elem->name, srch->name);
488 }
489 
491 {
492  int match;
493 
494  ast_rwlock_wrlock(&refer_techs_lock);
497  ast_rwlock_unlock(&refer_techs_lock);
498 
499  if (match) {
500  ast_log(LOG_ERROR, "No '%s' refer technology found.\n", tech->name);
501  return -1;
502  }
503 
504  ast_verb(5, "Refer technology '%s' unregistered.\n", tech->name);
505 
506  return 0;
507 }
508 
509 /*!
510  * \internal
511  * \brief Clean up other resources on Asterisk shutdown
512  */
513 static void refer_shutdown(void)
514 {
516  ast_rwlock_destroy(&refer_techs_lock);
517 }
518 
519 /*!
520  * \internal
521  * \brief Initialize stuff during Asterisk startup.
522  *
523  * Cleanup isn't a big deal in this function. If we return non-zero,
524  * Asterisk is going to exit.
525  *
526  * \retval 0 success
527  * \retval non-zero failure
528  */
529 int ast_refer_init(void)
530 {
531  ast_rwlock_init(&refer_techs_lock);
532  if (AST_VECTOR_INIT(&refer_techs, 8)) {
533  return -1;
534  }
535  ast_register_cleanup(refer_shutdown);
536  return 0;
537 }
#define AST_VECTOR_FREE(vec)
Deallocates this vector.
Definition: vector.h:174
int ast_refer_tech_register(const struct ast_refer_tech *tech)
Register a refer technology.
Definition: refer.c:446
Out-of-call refer support.
const char * ast_refer_get_from(const struct ast_refer *refer)
Retrieve the source of this refer.
Definition: refer.c:224
void ast_refer_var_iterator_destroy(struct ast_refer_var_iterator *iter)
Destroy a refer variable iterator.
Definition: refer.c:376
struct ast_refer * ast_refer_destroy(struct ast_refer *refer)
Destroy an ast_refer.
Definition: refer.c:152
Asterisk main include file. File version handling, generic pbx functions.
String manipulation functions.
A refer.
Definition: refer.c:57
#define AST_VECTOR_REMOVE_CMP_UNORDERED(vec, value, cmp, cleanup)
Remove an element from a vector that matches the given comparison.
Definition: vector.h:488
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
int ast_refer_get_to_self(const struct ast_refer *refer)
Retrieve the "to_self" value of this refer.
Definition: refer.c:234
int ast_refer_tech_unregister(const struct ast_refer_tech *tech)
Unregister a refer technology.
Definition: refer.c:490
int ast_refer_send(struct ast_refer *refer)
Send a refer directly to an endpoint.
Definition: refer.c:411
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
#define ast_rwlock_init(rwlock)
wrapper for rwlock with tracking enabled
Definition: lock.h:224
struct ao2_container * vars
Definition: refer.c:73
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
#define AST_VECTOR_APPEND(vec, elem)
Append an element to a vector, growing the vector if needed.
Definition: vector.h:256
struct ast_refer * ast_refer_alloc(void)
Allocate a refer.
Definition: refer.c:122
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
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
int ast_refer_set_tech(struct ast_refer *refer, const char *fmt,...)
Set the technology associated with this refer.
Definition: refer.c:197
A refer technology.
Definition: refer.h:52
int(*const refer_send)(const struct ast_refer *refer)
Send a refer.
Definition: refer.h:73
Asterisk datastore objects.
const ast_string_field from
Definition: refer.c:69
The arg parameter is a partial search key similar to OBJ_SEARCH_KEY.
Definition: astobj2.h:1116
#define AST_VECTOR_INIT(vec, size)
Initialize a vector.
Definition: vector.h:113
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
#define AST_VECTOR_ELEM_CLEANUP_NOOP(elem)
Vector element cleanup that does nothing.
Definition: vector.h:571
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
const char * ast_refer_get_to(const struct ast_refer *refer)
Retrieve the destination of this refer.
Definition: refer.c:229
struct @387 refer_techs
Vector of refer technologies.
#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_refer_get_refer_to(const struct ast_refer *refer)
Get the "refer-to" value of a refer.
Definition: refer.c:219
int ast_refer_var_iterator_next(struct ast_refer_var_iterator *iter, const char **name, const char **value)
Get the next variable name and value.
Definition: refer.c:349
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
#define AST_VECTOR(name, type)
Define a vector structure.
Definition: vector.h:44
Core PBX routines and definitions.
const char * ast_refer_get_endpoint(const struct ast_refer *refer)
Retrieve the endpoint associated with this refer.
Definition: refer.c:244
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
const ast_string_field refer_to
Definition: refer.c:69
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
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_refer_set_endpoint(struct ast_refer *refer, const char *fmt,...)
Set the technology's endpoint associated with this refer.
Definition: refer.c:208
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
const ast_string_field to
Definition: refer.c:69
const char *const name
Name of this refer technology.
Definition: refer.h:61
Prototypes for public functions only of internal interest,.
Vector container support.
An API for managing task processing threads that can be shared across modules.
const char * ast_refer_get_tech(const struct ast_refer *refer)
Retrieve the technology associated with this refer.
Definition: refer.c:239
const ast_string_field tech
Definition: refer.c:69
The arg parameter is an object of the same type.
Definition: astobj2.h:1087
#define AST_VECTOR_GET(vec, idx)
Get an element from a vector.
Definition: vector.h:680
const char * ast_refer_get_var(struct ast_refer *refer, const char *name)
Get the specified variable on the refer.
Definition: refer.c:315
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure for rwlock and tracking information.
Definition: lock.h:157
int ast_refer_set_from(struct ast_refer *refer, const char *fmt,...)
Set the 'from' URI of a refer.
Definition: refer.c:169
struct ast_refer * ast_refer_ref(struct ast_refer *refer)
Bump a refer's ref count.
Definition: refer.c:146
Generic container type.
char * ast_refer_get_var_and_unlink(struct ast_refer *refer, const char *name)
Get the specified variable on the refer and unlink it from the container of variables.
Definition: refer.c:267
Search option field mask.
Definition: astobj2.h:1072
const ast_string_field endpoint
Definition: refer.c:69
int ast_refer_set_to(struct ast_refer *refer, const char *fmt,...)
Set the 'to' URI of a refer.
Definition: refer.c:158
struct ast_refer_var_iterator * ast_refer_var_iterator_init(const struct ast_refer *refer)
Create a new refer variable iterator.
Definition: refer.c:335
static int refer_tech_cmp(const struct ast_refer_tech *vec_elem, const struct ast_refer_tech *srch)
Comparison callback for ast_refer_tech vector removal.
Definition: refer.c:482
Asterisk module definitions.
int ast_refer_init(void)
Definition: refer.c:529
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static ast_rwlock_t refer_techs_lock
Lock for refer_techs vector.
Definition: refer.c:77
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
void ast_refer_var_unref_current(struct ast_refer_var_iterator *iter)
Unref a refer var from inside an iterator loop.
Definition: refer.c:370
#define AST_VECTOR_SIZE(vec)
Get the number of elements in a vector.
Definition: vector.h:609
#define ast_string_field_build_va(x, field, fmt, args)
Set a field to a complex (built) value.
Definition: stringfields.h:591
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532