Newer
Older
ao2_unlock(conference);
play_file(bridge_channel, NULL,
conf_get_sound(CONF_SOUND_ERROR_MENU, conference->b_profile.sounds));
} else if (!last_user->kicked) {
last_user->kicked = 1;
Mark Michelson
committed
pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
ast_bridge_remove(conference->bridge, last_user->chan);
ao2_unlock(conference);
return 0;
}
static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
{
struct ast_pbx_args args;
struct ast_pbx *pbx;
char *exten;
char *context;
int priority;
int res;
memset(&args, 0, sizeof(args));
args.no_hangup_chan = 1;
ast_channel_lock(bridge_channel->chan);
/*save off*/
exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
context = ast_strdupa(ast_channel_context(bridge_channel->chan));
priority = ast_channel_priority(bridge_channel->chan);
pbx = ast_channel_pbx(bridge_channel->chan);
ast_channel_pbx_set(bridge_channel->chan, NULL);
ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
ast_channel_unlock(bridge_channel->chan);
/*execute*/
res = ast_pbx_run_args(bridge_channel->chan, &args);
/*restore*/
ast_channel_lock(bridge_channel->chan);
ast_channel_exten_set(bridge_channel->chan, exten);
ast_channel_context_set(bridge_channel->chan, context);
ast_channel_priority_set(bridge_channel->chan, priority);
ast_channel_pbx_set(bridge_channel->chan, pbx);
ast_channel_unlock(bridge_channel->chan);
return res;
}
static int execute_menu_entry(struct confbridge_conference *conference,
struct confbridge_user *user,
struct ast_bridge_channel *bridge_channel,
struct conf_menu_entry *menu_entry,
struct conf_menu *menu)
{
struct conf_menu_action *menu_action;
int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
int stop_prompts = 0;
int res = 0;
AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
switch (menu_action->id) {
case MENU_ACTION_TOGGLE_MUTE:
res |= action_toggle_mute(conference, user, bridge_channel);
case MENU_ACTION_TOGGLE_BINAURAL:
action_toggle_binaural(conference, user, bridge_channel);
break;
Matthew Jordan
committed
case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
if (!isadmin) {
break;
}
action_toggle_mute_participants(conference, user);
Matthew Jordan
committed
break;
case MENU_ACTION_PARTICIPANT_COUNT:
announce_user_count(conference, user, bridge_channel);
Matthew Jordan
committed
break;
case MENU_ACTION_PLAYBACK:
if (!stop_prompts) {
res |= action_playback(bridge_channel, menu_action->data.playback_file);
ast_test_suite_event_notify("CONF_MENU_PLAYBACK",
"Message: %s\r\nChannel: %s",
menu_action->data.playback_file, ast_channel_name(bridge_channel->chan));
}
break;
case MENU_ACTION_RESET_LISTENING:
ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
break;
case MENU_ACTION_RESET_TALKING:
ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
break;
case MENU_ACTION_INCREASE_LISTENING:
ast_audiohook_volume_adjust(user->chan,
AST_AUDIOHOOK_DIRECTION_WRITE, 1);
break;
case MENU_ACTION_DECREASE_LISTENING:
ast_audiohook_volume_adjust(user->chan,
AST_AUDIOHOOK_DIRECTION_WRITE, -1);
break;
case MENU_ACTION_INCREASE_TALKING:
ast_audiohook_volume_adjust(user->chan,
AST_AUDIOHOOK_DIRECTION_READ, 1);
break;
case MENU_ACTION_DECREASE_TALKING:
ast_audiohook_volume_adjust(user->chan,
AST_AUDIOHOOK_DIRECTION_READ, -1);
break;
case MENU_ACTION_PLAYBACK_AND_CONTINUE:
if (!(stop_prompts)) {
res |= action_playback_and_continue(conference,
user,
bridge_channel,
menu,
menu_action->data.playback_file,
menu_entry->dtmf,
&stop_prompts);
}
break;
case MENU_ACTION_DIALPLAN_EXEC:
res |= action_dialplan_exec(bridge_channel, menu_action);
break;
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
if (!isadmin) {
break;
}
conference->locked = (!conference->locked ? 1 : 0);
res |= play_file(bridge_channel, NULL,
conf_get_sound(
conference->locked ? CONF_SOUND_LOCKED_NOW : CONF_SOUND_UNLOCKED_NOW,
conference->b_profile.sounds)) < 0;
break;
case MENU_ACTION_ADMIN_KICK_LAST:
res |= action_kick_last(conference, bridge_channel, user);
break;
case MENU_ACTION_LEAVE:
Mark Michelson
committed
pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF");
ao2_lock(conference);
ast_bridge_remove(conference->bridge, bridge_channel->chan);
ast_test_suite_event_notify("CONF_MENU_LEAVE",
"Channel: %s",
ast_channel_name(bridge_channel->chan));
ao2_unlock(conference);
break;
case MENU_ACTION_NOOP:
break;
ao2_lock(conference);
if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_SFU)) {
ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan);
}
ao2_unlock(conference);
David Vossel
committed
case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
handle_video_on_exit(conference, bridge_channel->chan);
David Vossel
committed
break;
return res;
}
int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
struct confbridge_user *user,
struct conf_menu_entry *menu_entry,
struct conf_menu *menu)
{
/* See if music on hold is playing */
conf_moh_suspend(user);
/* execute the list of actions associated with this menu entry */
execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu);
/* See if music on hold needs to be started back up again */
conf_moh_unsuspend(user);
async_play_sound_ready(bridge_channel->chan);
static int kick_conference_participant(struct confbridge_conference *conference,
const char *channel)
int match;
struct confbridge_user *user = NULL;
int all = !strcasecmp("all", channel);
int participants = !strcasecmp("participants", channel);
SCOPED_AO2LOCK(bridge_lock, conference);
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
if (user->kicked) {
continue;
}
match = !strcasecmp(channel, ast_channel_name(user->chan));
if (match || all
|| (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
Mark Michelson
committed
pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
ast_bridge_remove(conference->bridge, user->chan);
res = 0;
if (match) {
return res;
}
}
}
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
if (user->kicked) {
continue;
}
match = !strcasecmp(channel, ast_channel_name(user->chan));
if (match || all
|| (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
Mark Michelson
committed
pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
ast_bridge_remove(conference->bridge, user->chan);
res = 0;
if (match) {
return res;
}
}
}
static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
{
int which = 0;
struct confbridge_conference *conference;
char *res = NULL;
int wordlen = strlen(word);
struct ao2_iterator iter;
iter = ao2_iterator_init(conference_bridges, 0);
while ((conference = ao2_iterator_next(&iter))) {
if (!strncasecmp(conference->name, word, wordlen) && ++which > state) {
res = ast_strdup(conference->name);
ao2_ref(conference, -1);
break;
}
ao2_ref(conference, -1);
}
ao2_iterator_destroy(&iter);
return res;
}
static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
{
int which = 0;
RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
struct confbridge_user *user;
char *res = NULL;
int wordlen = strlen(word);
conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
if (!conference) {
return NULL;
}
if (!strncasecmp("all", word, wordlen) && ++which > state) {
return ast_strdup("all");
}
if (!strncasecmp("participants", word, wordlen) && ++which > state) {
return ast_strdup("participants");
}
SCOPED_AO2LOCK(bridge_lock, conference);
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
res = ast_strdup(ast_channel_name(user->chan));
return res;
}
}
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
res = ast_strdup(ast_channel_name(user->chan));
return res;
}
}
}
return NULL;
}
static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct confbridge_conference *conference;
int not_found;
switch (cmd) {
case CLI_INIT:
e->command = "confbridge kick";
e->usage =
"Usage: confbridge kick <conference> <channel>\n"
" Kicks a channel out of the conference bridge.\n"
" (all to kick everyone, participants to kick non-admins).\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
if (a->pos == 3) {
return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
if (!conference) {
ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
return CLI_SUCCESS;
}
not_found = kick_conference_participant(conference, a->argv[3]);
ao2_ref(conference, -1);
if (not_found) {
if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
ast_cli(a->fd, "No participants found!\n");
} else {
ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
}
return CLI_SUCCESS;
}
ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]);
return CLI_SUCCESS;
}
static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
char flag_str[6 + 1];/* Max flags + terminator */
int pos = 0;
/* Build flags column string. */
if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
flag_str[pos++] = 'A';
}
if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
flag_str[pos++] = 'M';
}
if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
flag_str[pos++] = 'W';
}
if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) {
flag_str[pos++] = 'E';
}
if (user->muted) {
flag_str[pos++] = 'm';
}
if (waiting) {
flag_str[pos++] = 'w';
}
flag_str[pos] = '\0';
ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n",
ast_channel_name(user->chan),
flag_str,
user->u_profile.name,
user->conference->b_profile.name,
user->menu_name,
S_COR(ast_channel_caller(user->chan)->id.number.valid,
ast_channel_caller(user->chan)->id.number.str, "<unknown>"));
static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct confbridge_conference *conference;
switch (cmd) {
case CLI_INIT:
e->command = "confbridge list";
e->usage =
"Usage: confbridge list [<name>]\n"
" Lists all currently active conference bridges or a specific conference bridge.\n"
"\n"
" When a conference bridge name is provided, flags may be shown for users. Below\n"
" are the flags and what they represent.\n"
"\n"
" Flags:\n"
" A - The user is an admin\n"
" M - The user is a marked user\n"
" W - The user must wait for a marked user to join\n"
" E - The user will be kicked after the last marked user leaves the conference\n"
" m - The user is muted\n"
" w - The user is waiting for a marked user to join\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
if (a->argc == 2) {
struct ao2_iterator iter;
ast_cli(a->fd, "Conference Bridge Name Users Marked Locked Muted\n");
ast_cli(a->fd, "================================ ====== ====== ====== =====\n");
iter = ao2_iterator_init(conference_bridges, 0);
while ((conference = ao2_iterator_next(&iter))) {
ast_cli(a->fd, "%-32s %6u %6u %-6s %s\n",
conference->name,
conference->activeusers + conference->waitingusers,
conference->markedusers,
AST_CLI_YESNO(conference->locked),
AST_CLI_YESNO(conference->muted));
ao2_ref(conference, -1);
}
ao2_iterator_destroy(&iter);
return CLI_SUCCESS;
}
if (a->argc == 3) {
struct confbridge_user *user;
conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
if (!conference) {
ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
return CLI_SUCCESS;
}
ast_cli(a->fd, "Channel Flags User Profile Bridge Profile Menu CallerID\n");
ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n");
ao2_lock(conference);
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
handle_cli_confbridge_list_item(a, user, 0);
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
handle_cli_confbridge_list_item(a, user, 1);
}
ao2_unlock(conference);
ao2_ref(conference, -1);
return CLI_SUCCESS;
}
return CLI_SHOWUSAGE;
}
/* \internal
* \brief finds a conference by name and locks/unlocks.
*
* \retval 0 success
* \retval -1 conference not found
*/
static int generic_lock_unlock_helper(int lock, const char *conference_name)
struct confbridge_conference *conference;
conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
if (!conference) {
ao2_lock(conference);
conference->locked = lock;
ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name);
ao2_unlock(conference);
ao2_ref(conference, -1);
return res;
}
/* \internal
* \brief finds a conference user by channel name and mutes/unmutes them.
*
* \retval 0 success
* \retval -1 conference not found
* \retval -2 user not found
*/
static int generic_mute_unmute_helper(int mute, const char *conference_name,
const char *chan_name)
RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
struct confbridge_user *user;
int all = !strcasecmp("all", chan_name);
int participants = !strcasecmp("participants", chan_name);
int res = -2;
conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
if (!conference) {
{
SCOPED_AO2LOCK(bridge_lock, conference);
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
strlen(chan_name));
if (match || all
|| (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
generic_mute_unmute_user(conference, user, mute);
res = 0;
if (match) {
return res;
}
}
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
strlen(chan_name));
if (match || all
|| (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
generic_mute_unmute_user(conference, user, mute);
res = 0;
if (match) {
return res;
}
}
}
}
return res;
}
static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
{
int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
if (res == -1) {
ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
return -1;
} else if (res == -2) {
if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]);
} else {
ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
}
return -1;
}
ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
return 0;
}
static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "confbridge mute";
e->usage =
"Usage: confbridge mute <conference> <channel>\n"
" Mute a channel in a conference.\n"
" (all to mute everyone, participants to mute non-admins)\n"
" If the specified channel is a prefix,\n"
" the action will be taken on the first\n"
" matching channel.\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
if (a->pos == 3) {
return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
}
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
cli_mute_unmute_helper(1, a);
return CLI_SUCCESS;
}
static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "confbridge unmute";
e->usage =
"Usage: confbridge unmute <conference> <channel>\n"
" Unmute a channel in a conference.\n"
" (all to unmute everyone, participants to unmute non-admins)\n"
" If the specified channel is a prefix,\n"
" the action will be taken on the first\n"
" matching channel.\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
if (a->pos == 3) {
return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
}
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
cli_mute_unmute_helper(0, a);
return CLI_SUCCESS;
}
static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "confbridge lock";
e->usage =
"Usage: confbridge lock <conference>\n"
" Lock a conference. While locked, no new non-admins\n"
" may join the conference.\n";
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
return NULL;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
if (generic_lock_unlock_helper(1, a->argv[2])) {
ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
} else {
ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
}
return CLI_SUCCESS;
}
static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "confbridge unlock";
e->usage =
"Usage: confbridge unlock <conference>\n"
" Unlock a previously locked conference.\n";
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
return NULL;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
if (generic_lock_unlock_helper(0, a->argv[2])) {
ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
} else {
ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
}
return CLI_SUCCESS;
}
static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
const char *rec_file = NULL;
struct confbridge_conference *conference;
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
switch (cmd) {
case CLI_INIT:
e->command = "confbridge record start";
e->usage =
"Usage: confbridge record start <conference> <file>\n"
" <file> is optional, Otherwise the bridge profile\n"
" record file will be used. If the bridge profile\n"
" has no record file specified, a file will automatically\n"
" be generated in the monitor directory\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 3) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
return NULL;
}
if (a->argc < 4) {
return CLI_SHOWUSAGE;
}
if (a->argc == 5) {
rec_file = a->argv[4];
}
conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
if (!conference) {
ast_cli(a->fd, "Conference not found.\n");
return CLI_FAILURE;
}
ao2_lock(conference);
if (conf_is_recording(conference)) {
ast_cli(a->fd, "Conference is already being recorded.\n");
ao2_unlock(conference);
ao2_ref(conference, -1);
return CLI_SUCCESS;
}
if (!ast_strlen_zero(rec_file)) {
ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
Matthew Jordan
committed
Richard Mudgett
committed
if (conf_start_record(conference)) {
ast_cli(a->fd, "Could not start recording due to internal error.\n");
ao2_unlock(conference);
ao2_ref(conference, -1);
ao2_unlock(conference);
Matthew Jordan
committed
ast_cli(a->fd, "Recording started\n");
ao2_ref(conference, -1);
return CLI_SUCCESS;
}
static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct confbridge_conference *conference;
Matthew Jordan
committed
int ret;
switch (cmd) {
case CLI_INIT:
e->command = "confbridge record stop";
e->usage =
"Usage: confbridge record stop <conference>\n"
" Stop a previously started recording.\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 3) {
return complete_confbridge_name(a->line, a->word, a->pos, a->n);
}
return NULL;
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
if (!conference) {
ast_cli(a->fd, "Conference not found.\n");
return CLI_SUCCESS;
}
ao2_lock(conference);
ret = conf_stop_record(conference);
ao2_unlock(conference);
Matthew Jordan
committed
ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
ao2_ref(conference, -1);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_confbridge[] = {
AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."),
AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."),
AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
};
static struct ast_custom_function confbridge_function = {
.name = "CONFBRIDGE",
.write = func_confbridge_helper,
};
static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
static struct ast_custom_function confbridge_info_function = {
.name = "CONFBRIDGE_INFO",
.read = func_confbridge_info,
};
Richard Mudgett
committed
static int action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
Richard Mudgett
committed
struct ast_channel_snapshot *snapshot;
struct ast_str *snap_str;
snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(user->chan));
if (!snapshot) {
return 0;
}
snap_str = ast_manager_build_channel_state_string(snapshot);
if (!snap_str) {
ao2_ref(snapshot, -1);
return 0;
}
astman_append(s,
"Event: ConfbridgeList\r\n"
"%s"
"Conference: %s\r\n"
"Admin: %s\r\n"
"MarkedUser: %s\r\n"
"WaitMarked: %s\r\n"
"EndMarked: %s\r\n"
"Waiting: %s\r\n"
"Muted: %s\r\n"
"Talking: %s\r\n"
"AnsweredTime: %d\r\n"
Richard Mudgett
committed
"%s"
conference->name,
Richard Mudgett
committed
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ADMIN)),
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)),
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)),
AST_YESNO(ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)),
AST_YESNO(waiting),
AST_YESNO(user->muted),
AST_YESNO(user->talking),
Richard Mudgett
committed
ast_channel_get_up_time(user->chan),
ast_str_buffer(snap_str));
ast_free(snap_str);
ao2_ref(snapshot, -1);
return 1;
static int action_confbridgelist(struct mansession *s, const struct message *m)
{
const char *actionid = astman_get_header(m, "ActionID");
const char *conference_name = astman_get_header(m, "Conference");
struct confbridge_user *user;
struct confbridge_conference *conference;
char id_text[80];
id_text[0] = '\0';
if (!ast_strlen_zero(actionid)) {
snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
}
if (ast_strlen_zero(conference_name)) {
astman_send_error(s, m, "No Conference name provided.");
return 0;
}
if (!ao2_container_count(conference_bridges)) {
astman_send_error(s, m, "No active conferences.");
return 0;
}
conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
if (!conference) {
astman_send_error(s, m, "No Conference by that name found.");
return 0;
}
Richard Mudgett
committed
astman_send_listack(s, m, "Confbridge user list will follow", "start");
ao2_lock(conference);
AST_LIST_TRAVERSE(&conference->active_list, user, list) {
Richard Mudgett
committed
total += action_confbridgelist_item(s, id_text, conference, user, 0);
AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
Richard Mudgett
committed
total += action_confbridgelist_item(s, id_text, conference, user, 1);
ao2_unlock(conference);
ao2_ref(conference, -1);
astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
astman_send_list_complete_end(s);
return 0;
}
static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
{
const char *actionid = astman_get_header(m, "ActionID");
struct confbridge_conference *conference;
struct ao2_iterator iter;
char id_text[512] = "";
int totalitems = 0;
if (!ast_strlen_zero(actionid)) {
snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
}
if (!ao2_container_count(conference_bridges)) {
astman_send_error(s, m, "No active conferences.");
return 0;
}
Richard Mudgett
committed
astman_send_listack(s, m, "Confbridge conferences will follow", "start");
/* Traverse the conference list */
iter = ao2_iterator_init(conference_bridges, 0);
while ((conference = ao2_iterator_next(&iter))) {
ao2_lock(conference);
astman_append(s,
"Event: ConfbridgeListRooms\r\n"
"%s"
"Conference: %s\r\n"
"Parties: %u\r\n"
"Marked: %u\r\n"
"Muted: %s\r\n"
conference->name,
conference->activeusers + conference->waitingusers,
conference->markedusers,
AST_YESNO(conference->locked),
AST_YESNO(conference->muted));
ao2_unlock(conference);
ao2_ref(conference, -1);
ao2_iterator_destroy(&iter);
/* Send final confirmation */
astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
astman_send_list_complete_end(s);
return 0;
}
static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
{
const char *conference_name = astman_get_header(m, "Conference");
const char *channel_name = astman_get_header(m, "Channel");
if (ast_strlen_zero(conference_name)) {
astman_send_error(s, m, "No Conference name provided.");
return 0;
}
if (ast_strlen_zero(channel_name)) {
astman_send_error(s, m, "No channel name provided.");
return 0;
}
if (!ao2_container_count(conference_bridges)) {
astman_send_error(s, m, "No active conferences.");
return 0;
}
res = generic_mute_unmute_helper(mute, conference_name, channel_name);
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
if (res == -1) {
astman_send_error(s, m, "No Conference by that name found.");
return 0;
} else if (res == -2) {
astman_send_error(s, m, "No Channel by that name found in Conference.");
return 0;
}
astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
return 0;
}
static int action_confbridgeunmute(struct mansession *s, const struct message *m)
{
return action_mute_unmute_helper(s, m, 0);
}
static int action_confbridgemute(struct mansession *s, const struct message *m)
{
return action_mute_unmute_helper(s, m, 1);
}
static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
{
const char *conference_name = astman_get_header(m, "Conference");
if (ast_strlen_zero(conference_name)) {
astman_send_error(s, m, "No Conference name provided.");
return 0;
}
if (!ao2_container_count(conference_bridges)) {
astman_send_error(s, m, "No active conferences.");
return 0;
}
if ((res = generic_lock_unlock_helper(lock, conference_name))) {
astman_send_error(s, m, "No Conference by that name found.");
return 0;
}
astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
return 0;
}
static int action_confbridgeunlock(struct mansession *s, const struct message *m)
{
return action_lock_unlock_helper(s, m, 0);
}
static int action_confbridgelock(struct mansession *s, const struct message *m)
{
return action_lock_unlock_helper(s, m, 1);
}
static int action_confbridgekick(struct mansession *s, const struct message *m)
{
const char *conference_name = astman_get_header(m, "Conference");
const char *channel = astman_get_header(m, "Channel");
struct confbridge_conference *conference;
int found;
if (ast_strlen_zero(conference_name)) {
astman_send_error(s, m, "No Conference name provided.");