CI: Various updates to buildAsterisk.sh
[asterisk/asterisk.git] / main / bridge_roles.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Jonathan Rose <jrose@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 Channel Bridging Roles API
22  *
23  * \author Jonathan Rose <jrose@digium.com>
24  *
25  * \ingroup bridges
26  */
27
28 /*** MODULEINFO
29         <support_level>core</support_level>
30  ***/
31
32 #include "asterisk.h"
33
34 #include <signal.h>
35
36 #include "asterisk/logger.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/datastore.h"
39 #include "asterisk/linkedlists.h"
40 #include "asterisk/bridge.h"
41 #include "asterisk/bridge_roles.h"
42 #include "asterisk/stringfields.h"
43
44 struct bridge_role_option {
45         AST_LIST_ENTRY(bridge_role_option) list;
46         AST_DECLARE_STRING_FIELDS(
47                 AST_STRING_FIELD(option);
48                 AST_STRING_FIELD(value);
49         );
50 };
51
52 struct bridge_role {
53         AST_LIST_ENTRY(bridge_role) list;
54         AST_LIST_HEAD_NOLOCK(, bridge_role_option) options;
55         char role[AST_ROLE_LEN];
56 };
57
58 struct bridge_roles_datastore {
59         AST_LIST_HEAD_NOLOCK(, bridge_role) role_list;
60 };
61
62 /*!
63  * \internal
64  * \brief Destructor function for a bridge role
65  * \since 12.0.0
66  *
67  * \param role bridge_role being destroyed
68  *
69  * \return Nothing
70  */
71 static void bridge_role_destroy(struct bridge_role *role)
72 {
73         struct bridge_role_option *role_option;
74         while ((role_option = AST_LIST_REMOVE_HEAD(&role->options, list))) {
75                 ast_string_field_free_memory(role_option);
76                 ast_free(role_option);
77         }
78         ast_free(role);
79 }
80
81 /*!
82  * \internal
83  * \brief Destructor function for bridge role datastores
84  * \since 12.0.0
85  *
86  * \param data Pointer to the datastore being destroyed
87  *
88  * \return Nothing
89  */
90 static void bridge_role_datastore_destroy(void *data)
91 {
92         struct bridge_roles_datastore *roles_datastore = data;
93         struct bridge_role *role;
94
95         while ((role = AST_LIST_REMOVE_HEAD(&roles_datastore->role_list, list))) {
96                 bridge_role_destroy(role);
97         }
98
99         ast_free(roles_datastore);
100 }
101
102 static const struct ast_datastore_info bridge_role_info = {
103         .type = "bridge roles",
104         .destroy = bridge_role_datastore_destroy,
105 };
106
107 /*!
108  * \internal
109  * \brief Setup a bridge role datastore on a channel
110  * \since 12.0.0
111  *
112  * \param chan Chan the datastore is being setup on
113  *
114  * \retval NULL if failed
115  * \retval pointer to the newly created datastore
116  */
117 static struct bridge_roles_datastore *setup_bridge_roles_datastore(struct ast_channel *chan)
118 {
119         struct ast_datastore *datastore = NULL;
120         struct bridge_roles_datastore *roles_datastore = NULL;
121
122         if (!(datastore = ast_datastore_alloc(&bridge_role_info, NULL))) {
123                 return NULL;
124         }
125
126         if (!(roles_datastore = ast_calloc(1, sizeof(*roles_datastore)))) {
127                 ast_datastore_free(datastore);
128                 return NULL;
129         }
130
131         AST_LIST_HEAD_INIT_NOLOCK(&roles_datastore->role_list);
132
133         datastore->data = roles_datastore;
134         ast_channel_datastore_add(chan, datastore);
135         return roles_datastore;
136 }
137
138 /*!
139  * \internal
140  * \brief Get the bridge_roles_datastore from a channel if it exists. Don't create one if it doesn't.
141  * \since 12.0.0
142  *
143  * \param chan Channel we want the bridge_roles_datastore from
144  *
145  * \retval NULL if we can't find the datastore
146  * \retval pointer to the bridge_roles_datastore
147  */
148 static struct bridge_roles_datastore *fetch_bridge_roles_datastore(struct ast_channel *chan)
149 {
150         struct ast_datastore *datastore = NULL;
151
152         ast_channel_lock(chan);
153         if (!(datastore = ast_channel_datastore_find(chan, &bridge_role_info, NULL))) {
154                 ast_channel_unlock(chan);
155                 return NULL;
156         }
157         ast_channel_unlock(chan);
158
159         return datastore->data;
160 }
161
162 /*!
163  * \internal
164  * \brief Get the bridge_roles_datastore from a channel if it exists. If not, create one.
165  * \since 12.0.0
166  *
167  * \param chan Channel we want the bridge_roles_datastore from
168  *
169  * \retval NULL If we can't find and can't create the datastore
170  * \retval pointer to the bridge_roles_datastore
171  */
172 static struct bridge_roles_datastore *fetch_or_create_bridge_roles_datastore(struct ast_channel *chan)
173 {
174         struct bridge_roles_datastore *roles_datastore;
175
176         ast_channel_lock(chan);
177         roles_datastore = fetch_bridge_roles_datastore(chan);
178         if (!roles_datastore) {
179                 roles_datastore = setup_bridge_roles_datastore(chan);
180         }
181         ast_channel_unlock(chan);
182
183         return roles_datastore;
184 }
185
186 /*!
187  * \internal
188  * \brief Obtain a role from a bridge_roles_datastore if the datastore has it
189  * \since 12.0.0
190  *
191  * \param roles_datastore The bridge_roles_datastore we are looking for the role of
192  * \param role_name Name of the role being sought
193  *
194  * \retval NULL if the datastore does not have the requested role
195  * \retval pointer to the requested role
196  */
197 static struct bridge_role *get_role_from_datastore(struct bridge_roles_datastore *roles_datastore, const char *role_name)
198 {
199         struct bridge_role *role;
200
201         AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
202                 if (!strcmp(role->role, role_name)) {
203                         return role;
204                 }
205         }
206
207         return NULL;
208 }
209
210 /*!
211  * \internal
212  * \brief Obtain a role from a channel structure if the channel's datastore has it
213  * \since 12.0.0
214  *
215  * \param channel The channel we are checking the role of
216  * \param role_name Name of the role sought
217  *
218  * \retval NULL if the channel's datastore does not have the requested role
219  * \retval pointer to the requested role
220  */
221 static struct bridge_role *get_role_from_channel(struct ast_channel *channel, const char *role_name)
222 {
223         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(channel);
224         return roles_datastore ? get_role_from_datastore(roles_datastore, role_name) : NULL;
225 }
226
227 /*!
228  * \internal
229  * \brief Obtain a role option from a bridge role if it exists in the bridge role's option list
230  * \since 12.0.0
231  *
232  * \param role a pointer to the bridge role wea re searching for the option of
233  * \param option Name of the option sought
234  *
235  * \retval NULL if the bridge role doesn't have the requested option
236  * \retval pointer to the requested option
237  */
238 static struct bridge_role_option *get_role_option(struct bridge_role *role, const char *option)
239 {
240         struct bridge_role_option *role_option = NULL;
241         AST_LIST_TRAVERSE(&role->options, role_option, list) {
242                 if (!strcmp(role_option->option, option)) {
243                         return role_option;
244                 }
245         }
246         return NULL;
247 }
248
249 /*!
250  * \internal
251  * \brief Setup a bridge role on an existing bridge role datastore
252  * \since 12.0.0
253  *
254  * \param roles_datastore bridge_roles_datastore receiving the new role
255  * \param role_name Name of the role being received
256  *
257  * \retval 0 on success
258  * \retval -1 on failure
259  */
260 static int setup_bridge_role(struct bridge_roles_datastore *roles_datastore, const char *role_name)
261 {
262         struct bridge_role *role;
263         role = ast_calloc(1, sizeof(*role));
264
265         if (!role) {
266                 return -1;
267         }
268
269         AST_LIST_HEAD_INIT_NOLOCK(&role->options);
270
271         ast_copy_string(role->role, role_name, sizeof(role->role));
272
273         AST_LIST_INSERT_TAIL(&roles_datastore->role_list, role, list);
274         ast_debug(3, "Set role '%s'\n", role_name);
275
276         return 0;
277 }
278
279 /*!
280  * \internal
281  * \brief Setup a bridge role option on an existing bridge role
282  * \since 12.0.0
283  *
284  * \param role The role receiving the option
285  * \param option Name of the option
286  * \param value the option's value
287  *
288  * \retval 0 on success
289  * \retval -1 on failure
290  */
291 static int setup_bridge_role_option(struct bridge_role *role, const char *option, const char *value)
292 {
293         struct bridge_role_option *role_option;
294
295         if (!value) {
296                 value = "";
297         }
298
299         role_option = ast_calloc(1, sizeof(*role_option));
300         if (!role_option) {
301                 return -1;
302         }
303
304         if (ast_string_field_init(role_option, 32)) {
305                 ast_free(role_option);
306                 return -1;
307         }
308
309         ast_string_field_set(role_option, option, option);
310         ast_string_field_set(role_option, value, value);
311
312         AST_LIST_INSERT_TAIL(&role->options, role_option, list);
313
314         return 0;
315 }
316
317 int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
318 {
319         struct bridge_roles_datastore *roles_datastore = fetch_or_create_bridge_roles_datastore(chan);
320
321         if (!roles_datastore) {
322                 ast_log(LOG_WARNING, "Unable to set up bridge role datastore on channel %s\n", ast_channel_name(chan));
323                 return -1;
324         }
325
326         /* Check to make sure we aren't adding a redundant role */
327         if (get_role_from_datastore(roles_datastore, role_name)) {
328                 ast_debug(2, "Bridge role %s is already applied to the channel %s\n", role_name, ast_channel_name(chan));
329                 return 0;
330         }
331
332         /* It wasn't already there, so we can just finish setting it up now. */
333         return setup_bridge_role(roles_datastore, role_name);
334 }
335
336 void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
337 {
338         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
339         struct bridge_role *role;
340
341         if (!roles_datastore) {
342                 /* The roles datastore didn't already exist, so there is no need to remove a role */
343                 ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
344                 return;
345         }
346
347         AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
348                 if (!strcmp(role->role, role_name)) {
349                         ast_debug(2, "Removing bridge role %s from channel %s\n", role_name, ast_channel_name(chan));
350                         AST_LIST_REMOVE_CURRENT(list);
351                         bridge_role_destroy(role);
352                         return;
353                 }
354         }
355         AST_LIST_TRAVERSE_SAFE_END;
356
357         ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
358 }
359
360 void ast_channel_clear_bridge_roles(struct ast_channel *chan)
361 {
362         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
363         struct bridge_role *role;
364
365         if (!roles_datastore) {
366                 /* The roles datastore didn't already exist, so there is no need to remove any roles */
367                 ast_debug(2, "Roles did not exist on channel %s\n", ast_channel_name(chan));
368                 return;
369         }
370
371         AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
372                 ast_debug(2, "Removing bridge role %s from channel %s\n", role->role, ast_channel_name(chan));
373                 AST_LIST_REMOVE_CURRENT(list);
374                 bridge_role_destroy(role);
375         }
376         AST_LIST_TRAVERSE_SAFE_END;
377 }
378
379 int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
380 {
381         struct bridge_role *role = get_role_from_channel(channel, role_name);
382         struct bridge_role_option *role_option;
383
384         if (!role) {
385                 return -1;
386         }
387
388         role_option = get_role_option(role, option);
389
390         if (role_option) {
391                 ast_string_field_set(role_option, value, value);
392                 return 0;
393         }
394
395         return setup_bridge_role_option(role, option, value);
396 }
397
398 int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
399 {
400         return get_role_from_channel(channel, role_name) ? 1 : 0;
401 }
402
403 const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
404 {
405         struct bridge_role *role;
406         struct bridge_role_option *role_option;
407
408         role = get_role_from_channel(channel, role_name);
409         if (!role) {
410                 return NULL;
411         }
412
413         role_option = get_role_option(role, option);
414
415         return role_option ? role_option->value : NULL;
416 }
417
418 int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
419 {
420         if (!bridge_channel->bridge_roles) {
421                 return 0;
422         }
423
424         return get_role_from_datastore(bridge_channel->bridge_roles, role_name) ? 1 : 0;
425 }
426
427 const char *ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option)
428 {
429         struct bridge_role *role;
430         struct bridge_role_option *role_option = NULL;
431
432         if (!bridge_channel->bridge_roles) {
433                 return NULL;
434         }
435
436         role = get_role_from_datastore(bridge_channel->bridge_roles, role_name);
437
438         if (!role) {
439                 return NULL;
440         }
441
442         role_option = get_role_option(role, option);
443
444         return role_option ? role_option->value : NULL;
445 }
446
447 int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel)
448 {
449         struct bridge_roles_datastore *roles_datastore;
450         struct bridge_role *role = NULL;
451         struct bridge_role_option *role_option;
452
453         if (!bridge_channel->chan) {
454                 ast_debug(2, "Attempted to set roles on a bridge channel that has no associated channel. That's a bad idea.\n");
455                 return -1;
456         }
457
458         if (bridge_channel->bridge_roles) {
459                 ast_debug(2, "Attempted to reset roles while roles were already established. Purge existing roles first.\n");
460                 return -1;
461         }
462
463         roles_datastore = fetch_bridge_roles_datastore(bridge_channel->chan);
464         if (!roles_datastore) {
465                 /* No roles to establish. */
466                 return 0;
467         }
468
469         if (!(bridge_channel->bridge_roles = ast_calloc(1, sizeof(*bridge_channel->bridge_roles)))) {
470                 return -1;
471         }
472
473         AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
474                 struct bridge_role *this_role_copy;
475
476                 if (setup_bridge_role(bridge_channel->bridge_roles, role->role)) {
477                         /* We need to abandon the copy because we couldn't setup a role */
478                         ast_bridge_channel_clear_roles(bridge_channel);
479                         return -1;
480                 }
481                 this_role_copy = AST_LIST_LAST(&bridge_channel->bridge_roles->role_list);
482
483                 AST_LIST_TRAVERSE(&role->options, role_option, list) {
484                         if (setup_bridge_role_option(this_role_copy, role_option->option, role_option->value)) {
485                                 /* We need to abandon the copy because we couldn't setup a role option */
486                                 ast_bridge_channel_clear_roles(bridge_channel);
487                                 return -1;
488                         }
489                 }
490         }
491
492         return 0;
493 }
494
495 void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel)
496 {
497         if (bridge_channel->bridge_roles) {
498                 bridge_role_datastore_destroy(bridge_channel->bridge_roles);
499                 bridge_channel->bridge_roles = NULL;
500         }
501 }