stasis_cache_pattern: Remove silly usage of RAII_VAR.
[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(, bridge_role_option) options;
55         char role[AST_ROLE_LEN];
56 };
57
58 struct bridge_roles_datastore {
59         AST_LIST_HEAD(, 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         datastore->data = roles_datastore;
132         ast_channel_datastore_add(chan, datastore);
133         return roles_datastore;
134 }
135
136 /*!
137  * \internal
138  * \brief Get the bridge_roles_datastore from a channel if it exists. Don't create one if it doesn't.
139  * \since 12.0.0
140  *
141  * \param chan Channel we want the bridge_roles_datastore from
142  *
143  * \retval NULL if we can't find the datastore
144  * \retval pointer to the bridge_roles_datastore
145  */
146 static struct bridge_roles_datastore *fetch_bridge_roles_datastore(struct ast_channel *chan)
147 {
148         struct ast_datastore *datastore = NULL;
149
150         ast_channel_lock(chan);
151         if (!(datastore = ast_channel_datastore_find(chan, &bridge_role_info, NULL))) {
152                 ast_channel_unlock(chan);
153                 return NULL;
154         }
155         ast_channel_unlock(chan);
156
157         return datastore->data;
158 }
159
160 /*!
161  * \internal
162  * \brief Get the bridge_roles_datastore from a channel if it exists. If not, create one.
163  * \since 12.0.0
164  *
165  * \param chan Channel we want the bridge_roles_datastore from
166  *
167  * \retval NULL If we can't find and can't create the datastore
168  * \retval pointer to the bridge_roles_datastore
169  */
170 static struct bridge_roles_datastore *fetch_or_create_bridge_roles_datastore(struct ast_channel *chan)
171 {
172         struct bridge_roles_datastore *roles_datastore;
173
174         ast_channel_lock(chan);
175         roles_datastore = fetch_bridge_roles_datastore(chan);
176         if (!roles_datastore) {
177                 roles_datastore = setup_bridge_roles_datastore(chan);
178         }
179         ast_channel_unlock(chan);
180
181         return roles_datastore;
182 }
183
184 /*!
185  * \internal
186  * \brief Obtain a role from a bridge_roles_datastore if the datastore has it
187  * \since 12.0.0
188  *
189  * \param roles_datastore The bridge_roles_datastore we are looking for the role of
190  * \param role_name Name of the role being sought
191  *
192  * \retval NULL if the datastore does not have the requested role
193  * \retval pointer to the requested role
194  */
195 static struct bridge_role *get_role_from_datastore(struct bridge_roles_datastore *roles_datastore, const char *role_name)
196 {
197         struct bridge_role *role;
198
199         AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
200                 if (!strcmp(role->role, role_name)) {
201                         return role;
202                 }
203         }
204
205         return NULL;
206 }
207
208 /*!
209  * \internal
210  * \brief Obtain a role from a channel structure if the channel's datastore has it
211  * \since 12.0.0
212  *
213  * \param channel The channel we are checking the role of
214  * \param role_name Name of the role sought
215  *
216  * \retval NULL if the channel's datastore does not have the requested role
217  * \retval pointer to the requested role
218  */
219 static struct bridge_role *get_role_from_channel(struct ast_channel *channel, const char *role_name)
220 {
221         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(channel);
222         return roles_datastore ? get_role_from_datastore(roles_datastore, role_name) : NULL;
223 }
224
225 /*!
226  * \internal
227  * \brief Obtain a role option from a bridge role if it exists in the bridge role's option list
228  * \since 12.0.0
229  *
230  * \param role a pointer to the bridge role wea re searching for the option of
231  * \param option Name of the option sought
232  *
233  * \retval NULL if the bridge role doesn't have the requested option
234  * \retval pointer to the requested option
235  */
236 static struct bridge_role_option *get_role_option(struct bridge_role *role, const char *option)
237 {
238         struct bridge_role_option *role_option = NULL;
239         AST_LIST_TRAVERSE(&role->options, role_option, list) {
240                 if (!strcmp(role_option->option, option)) {
241                         return role_option;
242                 }
243         }
244         return NULL;
245 }
246
247 /*!
248  * \internal
249  * \brief Setup a bridge role on an existing bridge role datastore
250  * \since 12.0.0
251  *
252  * \param roles_datastore bridge_roles_datastore receiving the new role
253  * \param role_name Name of the role being received
254  *
255  * \retval 0 on success
256  * \retval -1 on failure
257  */
258 static int setup_bridge_role(struct bridge_roles_datastore *roles_datastore, const char *role_name)
259 {
260         struct bridge_role *role;
261         role = ast_calloc(1, sizeof(*role));
262
263         if (!role) {
264                 return -1;
265         }
266
267         ast_copy_string(role->role, role_name, sizeof(role->role));
268
269         AST_LIST_INSERT_TAIL(&roles_datastore->role_list, role, list);
270         ast_debug(3, "Set role '%s'\n", role_name);
271
272         return 0;
273 }
274
275 /*!
276  * \internal
277  * \brief Setup a bridge role option on an existing bridge role
278  * \since 12.0.0
279  *
280  * \param role The role receiving the option
281  * \param option Name of the option
282  * \param value the option's value
283  *
284  * \retval 0 on success
285  * \retval -1 on failure
286  */
287 static int setup_bridge_role_option(struct bridge_role *role, const char *option, const char *value)
288 {
289         struct bridge_role_option *role_option;
290
291         if (!value) {
292                 value = "";
293         }
294
295         role_option = ast_calloc(1, sizeof(*role_option));
296         if (!role_option) {
297                 return -1;
298         }
299
300         if (ast_string_field_init(role_option, 32)) {
301                 ast_free(role_option);
302                 return -1;
303         }
304
305         ast_string_field_set(role_option, option, option);
306         ast_string_field_set(role_option, value, value);
307
308         AST_LIST_INSERT_TAIL(&role->options, role_option, list);
309
310         return 0;
311 }
312
313 int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
314 {
315         struct bridge_roles_datastore *roles_datastore = fetch_or_create_bridge_roles_datastore(chan);
316
317         if (!roles_datastore) {
318                 ast_log(LOG_WARNING, "Unable to set up bridge role datastore on channel %s\n", ast_channel_name(chan));
319                 return -1;
320         }
321
322         /* Check to make sure we aren't adding a redundant role */
323         if (get_role_from_datastore(roles_datastore, role_name)) {
324                 ast_debug(2, "Bridge role %s is already applied to the channel %s\n", role_name, ast_channel_name(chan));
325                 return 0;
326         }
327
328         /* It wasn't already there, so we can just finish setting it up now. */
329         return setup_bridge_role(roles_datastore, role_name);
330 }
331
332 void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
333 {
334         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
335         struct bridge_role *role;
336
337         if (!roles_datastore) {
338                 /* The roles datastore didn't already exist, so there is no need to remove a role */
339                 ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
340                 return;
341         }
342
343         AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
344                 if (!strcmp(role->role, role_name)) {
345                         ast_debug(2, "Removing bridge role %s from channel %s\n", role_name, ast_channel_name(chan));
346                         AST_LIST_REMOVE_CURRENT(list);
347                         bridge_role_destroy(role);
348                         return;
349                 }
350         }
351         AST_LIST_TRAVERSE_SAFE_END;
352
353         ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
354 }
355
356 void ast_channel_clear_bridge_roles(struct ast_channel *chan)
357 {
358         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
359         struct bridge_role *role;
360
361         if (!roles_datastore) {
362                 /* The roles datastore didn't already exist, so there is no need to remove any roles */
363                 ast_debug(2, "Roles did not exist on channel %s\n", ast_channel_name(chan));
364                 return;
365         }
366
367         AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
368                 ast_debug(2, "Removing bridge role %s from channel %s\n", role->role, ast_channel_name(chan));
369                 AST_LIST_REMOVE_CURRENT(list);
370                 bridge_role_destroy(role);
371         }
372         AST_LIST_TRAVERSE_SAFE_END;
373 }
374
375 int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
376 {
377         struct bridge_role *role = get_role_from_channel(channel, role_name);
378         struct bridge_role_option *role_option;
379
380         if (!role) {
381                 return -1;
382         }
383
384         role_option = get_role_option(role, option);
385
386         if (role_option) {
387                 ast_string_field_set(role_option, value, value);
388                 return 0;
389         }
390
391         return setup_bridge_role_option(role, option, value);
392 }
393
394 int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
395 {
396         return get_role_from_channel(channel, role_name) ? 1 : 0;
397 }
398
399 const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
400 {
401         struct bridge_role *role;
402         struct bridge_role_option *role_option;
403
404         role = get_role_from_channel(channel, role_name);
405         if (!role) {
406                 return NULL;
407         }
408
409         role_option = get_role_option(role, option);
410
411         return role_option ? role_option->value : NULL;
412 }
413
414 int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
415 {
416         if (!bridge_channel->bridge_roles) {
417                 return 0;
418         }
419
420         return get_role_from_datastore(bridge_channel->bridge_roles, role_name) ? 1 : 0;
421 }
422
423 const char *ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option)
424 {
425         struct bridge_role *role;
426         struct bridge_role_option *role_option = NULL;
427
428         if (!bridge_channel->bridge_roles) {
429                 return NULL;
430         }
431
432         role = get_role_from_datastore(bridge_channel->bridge_roles, role_name);
433
434         if (!role) {
435                 return NULL;
436         }
437
438         role_option = get_role_option(role, option);
439
440         return role_option ? role_option->value : NULL;
441 }
442
443 int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel)
444 {
445         struct bridge_roles_datastore *roles_datastore;
446         struct bridge_role *role = NULL;
447         struct bridge_role_option *role_option;
448
449         if (!bridge_channel->chan) {
450                 ast_debug(2, "Attempted to set roles on a bridge channel that has no associated channel. That's a bad idea.\n");
451                 return -1;
452         }
453
454         if (bridge_channel->bridge_roles) {
455                 ast_debug(2, "Attempted to reset roles while roles were already established. Purge existing roles first.\n");
456                 return -1;
457         }
458
459         roles_datastore = fetch_bridge_roles_datastore(bridge_channel->chan);
460         if (!roles_datastore) {
461                 /* No roles to establish. */
462                 return 0;
463         }
464
465         if (!(bridge_channel->bridge_roles = ast_calloc(1, sizeof(*bridge_channel->bridge_roles)))) {
466                 return -1;
467         }
468
469         AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
470                 struct bridge_role *this_role_copy;
471
472                 if (setup_bridge_role(bridge_channel->bridge_roles, role->role)) {
473                         /* We need to abandon the copy because we couldn't setup a role */
474                         ast_bridge_channel_clear_roles(bridge_channel);
475                         return -1;
476                 }
477                 this_role_copy = AST_LIST_LAST(&bridge_channel->bridge_roles->role_list);
478
479                 AST_LIST_TRAVERSE(&role->options, role_option, list) {
480                         if (setup_bridge_role_option(this_role_copy, role_option->option, role_option->value)) {
481                                 /* We need to abandon the copy because we couldn't setup a role option */
482                                 ast_bridge_channel_clear_roles(bridge_channel);
483                                 return -1;
484                         }
485                 }
486         }
487
488         return 0;
489 }
490
491 void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel)
492 {
493         if (bridge_channel->bridge_roles) {
494                 bridge_role_datastore_destroy(bridge_channel->bridge_roles);
495                 bridge_channel->bridge_roles = NULL;
496         }
497 }