Skip to content
Snippets Groups Projects
app_confbridge.c 97 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		}
    		/*
    		if (a->pos == 3) {
    			return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
    		}
    		*/
    
    
    	if (a->argc != 4) {
    		return CLI_SHOWUSAGE;
    	}
    
    	ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
    		return CLI_SUCCESS;
    	}
    	ao2_lock(bridge);
    	AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    
    		if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
    
    		ast_cli(a->fd, "Kicking %s from confbridge %s\n", ast_channel_name(participant->chan), bridge->name);
    
    		participant->kicked = 1;
    		ast_bridge_remove(bridge->bridge, participant->chan);
    	}
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -1);
    	return CLI_SUCCESS;
    }
    
    static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	struct ao2_iterator i;
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    	struct conference_bridge_user *participant = NULL;
    
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "confbridge list";
    		e->usage =
    			"Usage: confbridge list [<name>]\n"
    			"       Lists all currently active conference bridges.\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) {
    		ast_cli(a->fd, "Conference Bridge Name           Users  Marked Locked?\n");
    		ast_cli(a->fd, "================================ ====== ====== ========\n");
    		i = ao2_iterator_init(conference_bridges, 0);
    		while ((bridge = ao2_iterator_next(&i))) {
    			ast_cli(a->fd, "%-32s %6i %6i %s\n", bridge->name, bridge->users, bridge->markedusers, (bridge->locked ? "locked" : "unlocked"));
    			ao2_ref(bridge, -1);
    		}
    		ao2_iterator_destroy(&i);
    		return CLI_SUCCESS;
    	}
    
    	if (a->argc == 3) {
    		ast_copy_string(tmp.name, a->argv[2], sizeof(tmp.name));
    		bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    		if (!bridge) {
    			ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
    			return CLI_SUCCESS;
    		}
    
    		ast_cli(a->fd, "Channel                       User Profile     Bridge Profile   Menu             CallerID\n");
    		ast_cli(a->fd, "============================= ================ ================ ================ ================\n");
    
    		ao2_lock(bridge);
    		AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    
    			ast_cli(a->fd, "%-29s ", ast_channel_name(participant->chan));
    
    			ast_cli(a->fd, "%-17s", participant->u_profile.name);
    			ast_cli(a->fd, "%-17s", participant->b_profile.name);
    			ast_cli(a->fd, "%-17s", participant->menu_name);
    
    			ast_cli(a->fd, "%-17s", S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"));
    
    			ast_cli(a->fd, "\n");
    		}
    		ao2_unlock(bridge);
    		ao2_ref(bridge, -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)
    {
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    	int res = 0;
    
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		return -1;
    	}
    	ao2_lock(bridge);
    	bridge->locked = lock;
    
    	ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", bridge->locked ? "locked" : "unlocked", bridge->b_profile.name);
    
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -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, const char *user)
    {
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    	struct conference_bridge_user *participant = NULL;
    	int res = 0;
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		return -1;
    	}
    	ao2_lock(bridge);
    	AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    
    		if (!strncmp(user, ast_channel_name(participant->chan), strlen(user))) {
    
    			break;
    		}
    	}
    	if (participant) {
    		participant->features.mute = mute;
    
    		ast_test_suite_event_notify("CONF_MUTE", "Message: participant %s %s\r\nConference: %s\r\nChannel: %s", ast_channel_name(participant->chan), participant->features.mute ? "muted" : "unmuted", bridge->b_profile.name, ast_channel_name(participant->chan));
    
    	} else {
    		res = -2;;
    	}
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -1);
    
    	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) {
    		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";
    		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 != 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";
    		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 != 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";
    		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";
    		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 conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    
    	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];
    	}
    
    	ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		ast_cli(a->fd, "Conference not found.\n");
    		return CLI_FAILURE;
    	}
    	if (conf_is_recording(bridge)) {
    		ast_cli(a->fd, "Conference is already being recorded.\n");
    		ao2_ref(bridge, -1);
    		return CLI_SUCCESS;
    	}
    	if (!ast_strlen_zero(rec_file)) {
    		ao2_lock(bridge);
    		ast_copy_string(bridge->b_profile.rec_file, rec_file, sizeof(bridge->b_profile.rec_file));
    		ao2_unlock(bridge);
    	}
    	if (conf_start_record(bridge)) {
    		ast_cli(a->fd, "Could not start recording due to internal error.\n");
    		ao2_ref(bridge, -1);
    		return CLI_FAILURE;
    	}
    	ast_cli(a->fd, "Recording started\n");
    	ao2_ref(bridge, -1);
    	return CLI_SUCCESS;
    }
    
    static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "confbridge record stop";
    		e->usage =
    			"Usage: confbridge record stop <conference>\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;
    	}
    
    	ast_copy_string(tmp.name, a->argv[3], sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		ast_cli(a->fd, "Conference not found.\n");
    		return CLI_SUCCESS;
    	}
    	conf_stop_record(bridge);
    	ast_cli(a->fd, "Recording stopped.\n");
    	ao2_ref(bridge, -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 a participant."),
    
    	AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute a participant."),
    
    	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,
    };
    
    
    static int action_confbridgelist(struct mansession *s, const struct message *m)
    {
    	const char *actionid = astman_get_header(m, "ActionID");
    	const char *conference = astman_get_header(m, "Conference");
    	struct conference_bridge_user *participant = NULL;
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    	char id_text[80] = "";
    	int total = 0;
    
    	if (!ast_strlen_zero(actionid)) {
    		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
    	}
    	if (ast_strlen_zero(conference)) {
    		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;
    	}
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    	astman_send_listack(s, m, "Confbridge user list will follow", "start");
    
    	ao2_lock(bridge);
    	AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    		total++;
    		astman_append(s,
    			"Event: ConfbridgeList\r\n"
    			"%s"
    			"Conference: %s\r\n"
    			"CallerIDNum: %s\r\n"
    			"CallerIDName: %s\r\n"
    			"Channel: %s\r\n"
    			"Admin: %s\r\n"
    			"MarkedUser: %s\r\n"
    			"\r\n",
    			id_text,
    			bridge->name,
    
    			S_COR(ast_channel_caller(participant->chan)->id.number.valid, ast_channel_caller(participant->chan)->id.number.str, "<unknown>"),
    			S_COR(ast_channel_caller(participant->chan)->id.name.valid, ast_channel_caller(participant->chan)->id.name.str, "<no name>"),
    
    			ast_channel_name(participant->chan),
    
    			ast_test_flag(&participant->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
    			ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No");
    	}
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -1);
    
    	astman_append(s,
    	"Event: ConfbridgeListComplete\r\n"
    	"EventList: Complete\r\n"
    	"ListItems: %d\r\n"
    	"%s"
    	"\r\n", total, id_text);
    
    	return 0;
    }
    
    static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
    {
    	const char *actionid = astman_get_header(m, "ActionID");
    	struct conference_bridge *bridge = NULL;
    	struct ao2_iterator i;
    	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;
    	}
    
    	astman_send_listack(s, m, "Confbridge conferences will follow", "start");
    
    	/* Traverse the conference list */
    	i = ao2_iterator_init(conference_bridges, 0);
    	while ((bridge = ao2_iterator_next(&i))) {
    		totalitems++;
    
    		ao2_lock(bridge);
    		astman_append(s,
    		"Event: ConfbridgeListRooms\r\n"
    		"%s"
    		"Conference: %s\r\n"
    		"Parties: %d\r\n"
    		"Marked: %d\r\n"
    		"Locked: %s\r\n"
    		"\r\n",
    		id_text,
    		bridge->name,
    		bridge->users,
    		bridge->markedusers,
    		bridge->locked ? "Yes" : "No"); 
    		ao2_unlock(bridge);
    
    		ao2_ref(bridge, -1);
    	}
    	ao2_iterator_destroy(&i);
    
    	/* Send final confirmation */
    	astman_append(s,
    	"Event: ConfbridgeListRoomsComplete\r\n"
    	"EventList: Complete\r\n"
    	"ListItems: %d\r\n"
    	"%s"
    	"\r\n", totalitems, id_text);
    	return 0;
    }
    
    static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
    {
    	const char *conference = astman_get_header(m, "Conference");
    	const char *channel = astman_get_header(m, "Channel");
    	int res = 0;
    
    	if (ast_strlen_zero(conference)) {
    		astman_send_error(s, m, "No Conference name provided.");
    		return 0;
    	}
    	if (ast_strlen_zero(channel)) {
    		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, channel);
    
    	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 = astman_get_header(m, "Conference");
    	int res = 0;
    
    	if (ast_strlen_zero(conference)) {
    		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))) {
    		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 = astman_get_header(m, "Conference");
    	const char *channel = astman_get_header(m, "Channel");
    	struct conference_bridge_user *participant = NULL;
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    	int found = 0;
    
    	if (ast_strlen_zero(conference)) {
    		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;
    	}
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    	ao2_lock(bridge);
    	AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    
    		if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
    
    			participant->kicked = 1;
    			ast_bridge_remove(bridge->bridge, participant->chan);
    			found = 1;
    			break;
    		}
    	}
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -1);
    
    	if (found) {
    		astman_send_ack(s, m, "User kicked");
    	} else {
    		astman_send_error(s, m, "No Channel by that name found in Conference.");
    	}
    	return 0;
    }
    
    static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
    {
    	const char *conference = astman_get_header(m, "Conference");
    	const char *recordfile = astman_get_header(m, "RecordFile");
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    
    	if (ast_strlen_zero(conference)) {
    		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;
    	}
    
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    	if (conf_is_recording(bridge)) {
    		astman_send_error(s, m, "Conference is already being recorded.");
    		ao2_ref(bridge, -1);
    		return 0;
    	}
    
    	if (!ast_strlen_zero(recordfile)) {
    		ao2_lock(bridge);
    		ast_copy_string(bridge->b_profile.rec_file, recordfile, sizeof(bridge->b_profile.rec_file));
    		ao2_unlock(bridge);
    	}
    
    	if (conf_start_record(bridge)) {
    		astman_send_error(s, m, "Internal error starting conference recording.");
    		ao2_ref(bridge, -1);
    		return 0;
    	}
    
    	ao2_ref(bridge, -1);
    	astman_send_ack(s, m, "Conference Recording Started.");
    	return 0;
    }
    static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
    {
    	const char *conference = astman_get_header(m, "Conference");
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    
    	if (ast_strlen_zero(conference)) {
    		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;
    	}
    
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    	if (conf_stop_record(bridge)) {
    		astman_send_error(s, m, "Internal error while stopping recording.");
    		ao2_ref(bridge, -1);
    		return 0;
    	}
    
    	ao2_ref(bridge, -1);
    	astman_send_ack(s, m, "Conference Recording Stopped.");
    	return 0;
    }
    
    
    static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
    {
    	const char *conference = astman_get_header(m, "Conference");
    	const char *channel = astman_get_header(m, "Channel");
    	struct conference_bridge_user *participant = NULL;
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge tmp;
    
    	if (ast_strlen_zero(conference)) {
    		astman_send_error(s, m, "No Conference name provided.");
    		return 0;
    	}
    	if (ast_strlen_zero(channel)) {
    		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;
    	}
    
    	ast_copy_string(tmp.name, conference, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    		astman_send_error(s, m, "No Conference by that name found.");
    		return 0;
    	}
    
    	/* find channel and set as video src. */
    	ao2_lock(bridge);
    	AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    
    		if (!strncmp(channel, ast_channel_name(participant->chan), strlen(channel))) {
    
    			ast_bridge_set_single_src_video_mode(bridge->bridge, participant->chan);
    			break;
    		}
    	}
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -1);
    
    	/* do not access participant after bridge unlock.  We are just
    	 * using this check to see if it was found or not */
    	if (!participant) {
    		astman_send_error(s, m, "No channel by that name found in conference.");
    		return 0;
    	}
    	astman_send_ack(s, m, "Conference single video source set.");
    	return 0;
    }
    
    
    static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
    {
    	char *parse = NULL;
    	struct conference_bridge *bridge = NULL;
    	struct conference_bridge_user *participant = NULL;
    	struct conference_bridge tmp;
    	int count = 0;
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(type);
    		AST_APP_ARG(confno);
    	);
    
    	/* parse all the required arguments and make sure they exist. */
    	if (ast_strlen_zero(data)) {
    		return -1;
    	}
    	parse = ast_strdupa(data);
    	AST_STANDARD_APP_ARGS(args, parse);
    	if (ast_strlen_zero(args.confno) || ast_strlen_zero(args.type)) {
    		return -1;
    	}
    	if (!ao2_container_count(conference_bridges)) {
    
    		snprintf(buf, len, "0");
    		return 0;
    
    	}
    	ast_copy_string(tmp.name, args.confno, sizeof(tmp.name));
    	bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
    	if (!bridge) {
    
    		snprintf(buf, len, "0");
    		return 0;
    
    	}
    
    	/* get the correct count for the type requested */
    	ao2_lock(bridge);
    	if (!strncasecmp(args.type, "parties", 7)) {
    		AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    			count++;
    		}
    	} else if (!strncasecmp(args.type, "admins", 6)) {
    		AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    			if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {
    				count++;
    			}
    		}
    	} else if (!strncasecmp(args.type, "marked", 6)) {
    		AST_LIST_TRAVERSE(&bridge->users_list, participant, list) {
    			if (ast_test_flag(&participant->u_profile, USER_OPT_MARKEDUSER)) {
    				count++;
    			}
    		}
    	} else if (!strncasecmp(args.type, "locked", 6)) {
    		count = bridge->locked;
    	} else {
    
    		ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.  Should be one of: "
    			"parties, admins, marked, or locked.\n", args.type);
    
    	}
    	snprintf(buf, len, "%d", count);
    	ao2_unlock(bridge);
    	ao2_ref(bridge, -1);
    	return 0;
    }
    
    /*! \brief Called when module is being unloaded */
    static int unload_module(void)
    {
    	int res = ast_unregister_application(app);
    
    
    	ast_custom_function_unregister(&confbridge_function);
    
    	ast_custom_function_unregister(&confbridge_info_function);
    
    	ast_cli_unregister_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
    
    
    	/* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
    	ao2_ref(conference_bridges, -1);
    
    
    	conf_destroy_config();
    
    	ast_channel_unregister(&record_tech);
    	record_tech.capabilities = ast_format_cap_destroy(record_tech.capabilities);
    
    	res |= ast_manager_unregister("ConfbridgeList");
    	res |= ast_manager_unregister("ConfbridgeListRooms");
    	res |= ast_manager_unregister("ConfbridgeMute");
    	res |= ast_manager_unregister("ConfbridgeUnmute");
    	res |= ast_manager_unregister("ConfbridgeKick");
    	res |= ast_manager_unregister("ConfbridgeUnlock");
    	res |= ast_manager_unregister("ConfbridgeLock");
    	res |= ast_manager_unregister("ConfbridgeStartRecord");
    	res |= ast_manager_unregister("ConfbridgeStopRecord");
    
    
    	return res;
    }
    
    /*! \brief Called when module is being loaded */
    static int load_module(void)
    {
    
    	int res = 0;
    	if ((ast_custom_function_register(&confbridge_function))) {
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    	if ((ast_custom_function_register(&confbridge_info_function))) {
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    	if (!(record_tech.capabilities = ast_format_cap_alloc())) {
    		return AST_MODULE_LOAD_FAILURE;
    	}
    	ast_format_cap_add_all(record_tech.capabilities);
    	if (ast_channel_register(&record_tech)) {
    		ast_log(LOG_ERROR, "Unable to register ConfBridge recorder.\n");
    		return AST_MODULE_LOAD_FAILURE;
    	}
    
    	/* Create a container to hold the conference bridges */
    	if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
    
    		return AST_MODULE_LOAD_FAILURE;
    
    	}
    	if (ast_register_application_xml(app, confbridge_exec)) {
    		ao2_ref(conference_bridges, -1);
    
    		return AST_MODULE_LOAD_FAILURE;
    
    	res |= ast_cli_register_multiple(cli_confbridge, sizeof(cli_confbridge) / sizeof(struct ast_cli_entry));
    	res |= ast_manager_register_xml("ConfbridgeList", EVENT_FLAG_REPORTING, action_confbridgelist);
    	res |= ast_manager_register_xml("ConfbridgeListRooms", EVENT_FLAG_REPORTING, action_confbridgelistrooms);
    	res |= ast_manager_register_xml("ConfbridgeMute", EVENT_FLAG_CALL, action_confbridgemute);
    	res |= ast_manager_register_xml("ConfbridgeUnmute", EVENT_FLAG_CALL, action_confbridgeunmute);
    	res |= ast_manager_register_xml("ConfbridgeKick", EVENT_FLAG_CALL, action_confbridgekick);
    	res |= ast_manager_register_xml("ConfbridgeUnlock", EVENT_FLAG_CALL, action_confbridgeunlock);
    	res |= ast_manager_register_xml("ConfbridgeLock", EVENT_FLAG_CALL, action_confbridgelock);
    	res |= ast_manager_register_xml("ConfbridgeStartRecord", EVENT_FLAG_CALL, action_confbridgestartrecord);
    	res |= ast_manager_register_xml("ConfbridgeStopRecord", EVENT_FLAG_CALL, action_confbridgestoprecord);
    
    	res |= ast_manager_register_xml("ConfbridgeSetSingleVideoSrc", EVENT_FLAG_CALL, action_confbridgesetsinglevideosrc);
    
    	res |= conf_load_config(0);
    
    
    static int reload(void)
    {
    	return conf_load_config(1);
    
    AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
    	.load = load_module,
    	.unload = unload_module,
    
    	.reload = reload,
    
    	.load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
    );