ARI: Add ability to raise arbitrary User Events
[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 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <signal.h>
37
38 #include "asterisk/logger.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/datastore.h"
41 #include "asterisk/linkedlists.h"
42 #include "asterisk/bridge.h"
43 #include "asterisk/bridge_roles.h"
44 #include "asterisk/stringfields.h"
45
46 struct bridge_role_option {
47         AST_LIST_ENTRY(bridge_role_option) list;
48         AST_DECLARE_STRING_FIELDS(
49                 AST_STRING_FIELD(option);
50                 AST_STRING_FIELD(value);
51         );
52 };
53
54 struct bridge_role {
55         AST_LIST_ENTRY(bridge_role) list;
56         AST_LIST_HEAD(, bridge_role_option) options;
57         char role[AST_ROLE_LEN];
58 };
59
60 struct bridge_roles_datastore {
61         AST_LIST_HEAD(, bridge_role) role_list;
62 };
63
64 /*!
65  * \internal
66  * \brief Destructor function for a bridge role
67  * \since 12.0.0
68  *
69  * \param role bridge_role being destroyed
70  *
71  * \return Nothing
72  */
73 static void bridge_role_destroy(struct bridge_role *role)
74 {
75         struct bridge_role_option *role_option;
76         while ((role_option = AST_LIST_REMOVE_HEAD(&role->options, list))) {
77                 ast_string_field_free_memory(role_option);
78                 ast_free(role_option);
79         }
80         ast_free(role);
81 }
82
83 /*!
84  * \internal
85  * \brief Destructor function for bridge role datastores
86  * \since 12.0.0
87  *
88  * \param data Pointer to the datastore being destroyed
89  *
90  * \return Nothing
91  */
92 static void bridge_role_datastore_destroy(void *data)
93 {
94         struct bridge_roles_datastore *roles_datastore = data;
95         struct bridge_role *role;
96
97         while ((role = AST_LIST_REMOVE_HEAD(&roles_datastore->role_list, list))) {
98                 bridge_role_destroy(role);
99         }
100
101         ast_free(roles_datastore);
102 }
103
104 static const struct ast_datastore_info bridge_role_info = {
105         .type = "bridge roles",
106         .destroy = bridge_role_datastore_destroy,
107 };
108
109 /*!
110  * \internal
111  * \brief Setup a bridge role datastore on a channel
112  * \since 12.0.0
113  *
114  * \param chan Chan the datastore is being setup on
115  *
116  * \retval NULL if failed
117  * \retval pointer to the newly created datastore
118  */
119 static struct bridge_roles_datastore *setup_bridge_roles_datastore(struct ast_channel *chan)
120 {
121         struct ast_datastore *datastore = NULL;
122         struct bridge_roles_datastore *roles_datastore = NULL;
123
124         if (!(datastore = ast_datastore_alloc(&bridge_role_info, NULL))) {
125                 return NULL;
126         }
127
128         if (!(roles_datastore = ast_calloc(1, sizeof(*roles_datastore)))) {
129                 ast_datastore_free(datastore);
130                 return NULL;
131         }
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_copy_string(role->role, role_name, sizeof(role->role));
270
271         AST_LIST_INSERT_TAIL(&roles_datastore->role_list, role, list);
272         ast_debug(3, "Set role '%s'\n", role_name);
273
274         return 0;
275 }
276
277 /*!
278  * \internal
279  * \brief Setup a bridge role option on an existing bridge role
280  * \since 12.0.0
281  *
282  * \param role The role receiving the option
283  * \param option Name of the option
284  * \param value the option's value
285  *
286  * \retval 0 on success
287  * \retval -1 on failure
288  */
289 static int setup_bridge_role_option(struct bridge_role *role, const char *option, const char *value)
290 {
291         struct bridge_role_option *role_option;
292
293         if (!value) {
294                 value = "";
295         }
296
297         role_option = ast_calloc(1, sizeof(*role_option));
298         if (!role_option) {
299                 return -1;
300         }
301
302         if (ast_string_field_init(role_option, 32)) {
303                 ast_free(role_option);
304                 return -1;
305         }
306
307         ast_string_field_set(role_option, option, option);
308         ast_string_field_set(role_option, value, value);
309
310         AST_LIST_INSERT_TAIL(&role->options, role_option, list);
311
312         return 0;
313 }
314
315 int ast_channel_add_bridge_role(struct ast_channel *chan, const char *role_name)
316 {
317         struct bridge_roles_datastore *roles_datastore = fetch_or_create_bridge_roles_datastore(chan);
318
319         if (!roles_datastore) {
320                 ast_log(LOG_WARNING, "Unable to set up bridge role datastore on channel %s\n", ast_channel_name(chan));
321                 return -1;
322         }
323
324         /* Check to make sure we aren't adding a redundant role */
325         if (get_role_from_datastore(roles_datastore, role_name)) {
326                 ast_debug(2, "Bridge role %s is already applied to the channel %s\n", role_name, ast_channel_name(chan));
327                 return 0;
328         }
329
330         /* It wasn't already there, so we can just finish setting it up now. */
331         return setup_bridge_role(roles_datastore, role_name);
332 }
333
334 void ast_channel_remove_bridge_role(struct ast_channel *chan, const char *role_name)
335 {
336         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
337         struct bridge_role *role;
338
339         if (!roles_datastore) {
340                 /* The roles datastore didn't already exist, so there is no need to remove a role */
341                 ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
342                 return;
343         }
344
345         AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
346                 if (!strcmp(role->role, role_name)) {
347                         ast_debug(2, "Removing bridge role %s from channel %s\n", role_name, ast_channel_name(chan));
348                         AST_LIST_REMOVE_CURRENT(list);
349                         bridge_role_destroy(role);
350                         return;
351                 }
352         }
353         AST_LIST_TRAVERSE_SAFE_END;
354
355         ast_debug(2, "Role %s did not exist on channel %s\n", role_name, ast_channel_name(chan));
356 }
357
358 void ast_channel_clear_bridge_roles(struct ast_channel *chan)
359 {
360         struct bridge_roles_datastore *roles_datastore = fetch_bridge_roles_datastore(chan);
361         struct bridge_role *role;
362
363         if (!roles_datastore) {
364                 /* The roles datastore didn't already exist, so there is no need to remove any roles */
365                 ast_debug(2, "Roles did not exist on channel %s\n", ast_channel_name(chan));
366                 return;
367         }
368
369         AST_LIST_TRAVERSE_SAFE_BEGIN(&roles_datastore->role_list, role, list) {
370                 ast_debug(2, "Removing bridge role %s from channel %s\n", role->role, ast_channel_name(chan));
371                 AST_LIST_REMOVE_CURRENT(list);
372                 bridge_role_destroy(role);
373         }
374         AST_LIST_TRAVERSE_SAFE_END;
375 }
376
377 int ast_channel_set_bridge_role_option(struct ast_channel *channel, const char *role_name, const char *option, const char *value)
378 {
379         struct bridge_role *role = get_role_from_channel(channel, role_name);
380         struct bridge_role_option *role_option;
381
382         if (!role) {
383                 return -1;
384         }
385
386         role_option = get_role_option(role, option);
387
388         if (role_option) {
389                 ast_string_field_set(role_option, value, value);
390                 return 0;
391         }
392
393         return setup_bridge_role_option(role, option, value);
394 }
395
396 int ast_channel_has_role(struct ast_channel *channel, const char *role_name)
397 {
398         return get_role_from_channel(channel, role_name) ? 1 : 0;
399 }
400
401 const char *ast_channel_get_role_option(struct ast_channel *channel, const char *role_name, const char *option)
402 {
403         struct bridge_role *role;
404         struct bridge_role_option *role_option;
405
406         role = get_role_from_channel(channel, role_name);
407         if (!role) {
408                 return NULL;
409         }
410
411         role_option = get_role_option(role, option);
412
413         return role_option ? role_option->value : NULL;
414 }
415
416 int ast_bridge_channel_has_role(struct ast_bridge_channel *bridge_channel, const char *role_name)
417 {
418         if (!bridge_channel->bridge_roles) {
419                 return 0;
420         }
421
422         return get_role_from_datastore(bridge_channel->bridge_roles, role_name) ? 1 : 0;
423 }
424
425 const char *ast_bridge_channel_get_role_option(struct ast_bridge_channel *bridge_channel, const char *role_name, const char *option)
426 {
427         struct bridge_role *role;
428         struct bridge_role_option *role_option = NULL;
429
430         if (!bridge_channel->bridge_roles) {
431                 return NULL;
432         }
433
434         role = get_role_from_datastore(bridge_channel->bridge_roles, role_name);
435
436         if (!role) {
437                 return NULL;
438         }
439
440         role_option = get_role_option(role, option);
441
442         return role_option ? role_option->value : NULL;
443 }
444
445 int ast_bridge_channel_establish_roles(struct ast_bridge_channel *bridge_channel)
446 {
447         struct bridge_roles_datastore *roles_datastore;
448         struct bridge_role *role = NULL;
449         struct bridge_role_option *role_option;
450
451         if (!bridge_channel->chan) {
452                 ast_debug(2, "Attempted to set roles on a bridge channel that has no associated channel. That's a bad idea.\n");
453                 return -1;
454         }
455
456         if (bridge_channel->bridge_roles) {
457                 ast_debug(2, "Attempted to reset roles while roles were already established. Purge existing roles first.\n");
458                 return -1;
459         }
460
461         roles_datastore = fetch_bridge_roles_datastore(bridge_channel->chan);
462         if (!roles_datastore) {
463                 /* No roles to establish. */
464                 return 0;
465         }
466
467         if (!(bridge_channel->bridge_roles = ast_calloc(1, sizeof(*bridge_channel->bridge_roles)))) {
468                 return -1;
469         }
470
471         AST_LIST_TRAVERSE(&roles_datastore->role_list, role, list) {
472                 struct bridge_role *this_role_copy;
473
474                 if (setup_bridge_role(bridge_channel->bridge_roles, role->role)) {
475                         /* We need to abandon the copy because we couldn't setup a role */
476                         ast_bridge_channel_clear_roles(bridge_channel);
477                         return -1;
478                 }
479                 this_role_copy = AST_LIST_LAST(&bridge_channel->bridge_roles->role_list);
480
481                 AST_LIST_TRAVERSE(&role->options, role_option, list) {
482                         if (setup_bridge_role_option(this_role_copy, role_option->option, role_option->value)) {
483                                 /* We need to abandon the copy because we couldn't setup a role option */
484                                 ast_bridge_channel_clear_roles(bridge_channel);
485                                 return -1;
486                         }
487                 }
488         }
489
490         return 0;
491 }
492
493 void ast_bridge_channel_clear_roles(struct ast_bridge_channel *bridge_channel)
494 {
495         if (bridge_channel->bridge_roles) {
496                 bridge_role_datastore_destroy(bridge_channel->bridge_roles);
497                 bridge_channel->bridge_roles = NULL;
498         }
499 }