Clean up ConfBridge commands to account for wait_marked users
authorMatthew Jordan <mjordan@digium.com>
Tue, 26 Feb 2013 15:52:02 +0000 (15:52 +0000)
committerMatthew Jordan <mjordan@digium.com>
Tue, 26 Feb 2013 15:52:02 +0000 (15:52 +0000)
When ConfBridge was refactored to better handle the concept of marked,
wait_marked, and normal users co-existing in a conference (thereby implementing
a state machine for the conference), the wait_marked users were put into their
own list of conference participants, separate from the active users. This list
is used for wait_marked users when they are waiting in a conference but no
marked user has joined; normal users may have joined at this point however.
There are several AMI/CLI commands that affect conference users that were not
checking the wait_marked users list:
* CLI/AMI commands that mute/unmute a participant. In this case, wait_marked
  users have to remain in their particular state and should not be affected -
  however, the commands would return "Channel not found" as opposed to the
  appropriate error condition.
* CLI/AMI commands that kick a participant. An admin should always be able to
  kick a participant out of the conference.

This patch fixes both sets of commands, and cleans up the CLI commands slightly
by allowing them to complete a participant name (this was supposed to have been
added, but the function call was commented out and wasn't implemented).

Review: https://reviewboard.asterisk.org/r/2346/

(closes issue AST-1114)
Reported by: John Bigelow
Tested by: John Bigelow
........

Merged revisions 382068 from http://svn.asterisk.org/svn/asterisk/branches/11

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@382070 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_confbridge.c

index edae7b1..dfa4a0a 100644 (file)
@@ -2180,6 +2180,29 @@ int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
        return 0;
 }
 
+static int kick_conference_participant(struct conference_bridge *bridge, const char *channel)
+{
+       struct conference_bridge_user *participant = NULL;
+
+       SCOPED_AO2LOCK(bridge_lock, bridge);
+       AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
+               if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
+                       participant->kicked = 1;
+                       ast_bridge_remove(bridge->bridge, participant->chan);
+                       return 0;
+               }
+       }
+       AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
+               if (!strcasecmp(ast_channel_name(participant->chan), channel)) {
+                       participant->kicked = 1;
+                       ast_bridge_remove(bridge->bridge, participant->chan);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
 {
        int which = 0;
@@ -2202,11 +2225,44 @@ static char *complete_confbridge_name(const char *line, const char *word, int po
        return res;
 }
 
+static char *complete_confbridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state)
+{
+       int which = 0;
+       RAII_VAR(struct conference_bridge *, bridge, NULL, ao2_cleanup);
+       struct conference_bridge tmp;
+       struct conference_bridge_user *participant;
+       char *res = NULL;
+       int wordlen = strlen(word);
+
+       ast_copy_string(tmp.name, bridge_name, sizeof(tmp.name));
+       bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
+       if (!bridge) {
+               return NULL;
+       }
+
+       {
+               SCOPED_AO2LOCK(bridge_lock, bridge);
+               AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
+                       if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) {
+                               res = ast_strdup(ast_channel_name(participant->chan));
+                               return res;
+                       }
+               }
+               AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
+                       if (!strncasecmp(ast_channel_name(participant->chan), word, wordlen) && ++which > state) {
+                               res = ast_strdup(ast_channel_name(participant->chan));
+                               return res;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        struct conference_bridge *bridge = NULL;
        struct conference_bridge tmp;
-       struct conference_bridge_user *participant = NULL;
 
        switch (cmd) {
        case CLI_INIT:
@@ -2219,11 +2275,9 @@ static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct
                if (a->pos == 2) {
                        return complete_confbridge_name(a->line, a->word, a->pos, a->n);
                }
-               /*
                if (a->pos == 3) {
-                       return complete_confbridge_channel(a->line, a->word, a->pos, a->n);
+                       return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
                }
-               */
                return NULL;
        }
 
@@ -2237,19 +2291,12 @@ static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct
                ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
                return CLI_SUCCESS;
        }
-       ao2_lock(bridge);
-       AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
-               if (!strncmp(a->argv[3], ast_channel_name(participant->chan), strlen(ast_channel_name(participant->chan)))) {
-                       break;
-               }
-       }
-       if (participant) {
-               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);
+       if (kick_conference_participant(bridge, a->argv[3])) {
+               ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
+               return CLI_SUCCESS;
        }
-       ao2_unlock(bridge);
        ao2_ref(bridge, -1);
+       ast_cli(a->fd, "Participant '%s' kicked out of conference '%s'\n", a->argv[3], a->argv[2]);
        return CLI_SUCCESS;
 }
 
@@ -2437,12 +2484,16 @@ static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct
        case CLI_INIT:
                e->command = "confbridge mute";
                e->usage =
-                       "Usage: confbridge mute <conference> <channel>\n";
+                       "Usage: confbridge mute <conference> <channel>\n"
+                       "       Mute a channel in a conference.\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) {
@@ -2460,12 +2511,16 @@ static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, stru
        case CLI_INIT:
                e->command = "confbridge unmute";
                e->usage =
-                       "Usage: confbridge unmute <conference> <channel>\n";
+                       "Usage: confbridge unmute <conference> <channel>\n"
+                       "       Unmute a channel in a conference.\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) {
@@ -2483,7 +2538,9 @@ static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct
        case CLI_INIT:
                e->command = "confbridge lock";
                e->usage =
-                       "Usage: confbridge lock <conference>\n";
+                       "Usage: confbridge lock <conference>\n"
+                       "       Lock a conference. While locked, no new non-admins\n"
+                       "       may join the conference.\n";
                return NULL;
        case CLI_GENERATE:
                if (a->pos == 2) {
@@ -2508,7 +2565,8 @@ static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, stru
        case CLI_INIT:
                e->command = "confbridge unlock";
                e->usage =
-                       "Usage: confbridge unlock <conference>\n";
+                       "Usage: confbridge unlock <conference>\n"
+                       "       Unlock a previously locked conference.\n";
                return NULL;
        case CLI_GENERATE:
                if (a->pos == 2) {
@@ -2596,7 +2654,8 @@ static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd,
        case CLI_INIT:
                e->command = "confbridge record stop";
                e->usage =
-                       "Usage: confbridge record stop <conference>\n";
+                       "Usage: confbridge record stop <conference>\n"
+                       "       Stop a previously started recording.\n";
                return NULL;
        case CLI_GENERATE:
                if (a->pos == 3) {
@@ -2852,7 +2911,6 @@ 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;
@@ -2865,6 +2923,7 @@ static int action_confbridgekick(struct mansession *s, const struct message *m)
                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) {
@@ -2872,16 +2931,7 @@ static int action_confbridgekick(struct mansession *s, const struct message *m)
                return 0;
        }
 
-       ao2_lock(bridge);
-       AST_LIST_TRAVERSE(&bridge->active_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);
+       found = !kick_conference_participant(bridge, channel);
        ao2_ref(bridge, -1);
 
        if (found) {
@@ -3062,6 +3112,9 @@ static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char
                AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
                        count++;
                }
+               AST_LIST_TRAVERSE(&bridge->waiting_list, participant, list) {
+                       count++;
+               }
        } else if (!strncasecmp(args.type, "admins", 6)) {
                AST_LIST_TRAVERSE(&bridge->active_list, participant, list) {
                        if (ast_test_flag(&participant->u_profile, USER_OPT_ADMIN)) {