app_confbridge: Add ability to get the muted conference state.
[asterisk/asterisk.git] / apps / app_confbridge.c
index cefc133..18e192c 100644 (file)
@@ -43,7 +43,7 @@
 
 #include "asterisk.h"
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+ASTERISK_REGISTER_FILE()
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -118,6 +118,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        <value name="KICKED">The channel was kicked from the conference.</value>
                                        <value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
                                        <value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
+                                       <value name="TIMEOUT">The channel reached its configured timeout.</value>
                                </variable>
                        </variablelist>
                </description>
@@ -129,32 +130,51 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
        </application>
        <function name="CONFBRIDGE" language="en_US">
                <synopsis>
-                       Set a custom dynamic bridge, user, or menu profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
+                       Set a custom dynamic bridge, user, or menu profile on a channel for the
+                       ConfBridge application using the same options available in confbridge.conf.
                </synopsis>
                <syntax>
                        <parameter name="type" required="true">
-                               <para>Type refers to which type of profile the option belongs too.  Type can be <literal>bridge</literal>, <literal>user</literal>, or
-                               <literal>menu</literal>.</para>
+                               <para>To what type of conference profile the option applies.</para>
+                               <enumlist>
+                                       <enum name="bridge"></enum>
+                                       <enum name="menu"></enum>
+                                       <enum name="user"></enum>
+                               </enumlist>
                        </parameter>
                        <parameter name="option" required="true">
-                               <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel, or
-                               <literal>clear</literal> to remove already applied options from the channel.</para>
+                               <para>Option refers to a <filename>confbridge.conf</filename> option
+                               that is being set dynamically on this channel, or <literal>clear</literal>
+                               to remove already applied profile options from the channel.</para>
                        </parameter>
                </syntax>
                <description>
+                       <para>A custom profile uses the default profile type settings defined in
+                       <filename>confbridge.conf</filename> as defaults if the profile template
+                       is not explicitly specified first.</para>
+                       <para>For <literal>bridge</literal> profiles the default template is <literal>default_bridge</literal>.</para>
+                       <para>For <literal>menu</literal> profiles the default template is <literal>default_menu</literal>.</para>
+                       <para>For <literal>user</literal> profiles the default template is <literal>default_user</literal>.</para>
                        <para>---- Example 1 ----</para>
-                       <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
-                       <para>exten => 1,1,Answer() </para>
-                       <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
-                       <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
-                       <para>exten => 1,n,ConfBridge(1) </para>
+                       <para>In this example the custom user profile set on the channel will
+                       automatically be used by the ConfBridge application.</para>
+                       <para>exten => 1,1,Answer()</para>
+                       <para>; In this example the effect of the following line is</para>
+                       <para>; implied:</para>
+                       <para>; same => n,Set(CONFBRIDGE(user,template)=default_user)</para>
+                       <para>same => n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
+                       <para>same => n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
+                       <para>same => n,ConfBridge(1) </para>
                        <para>---- Example 2 ----</para>
-                       <para>This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf.</para>
-                       <para>exten => 1,1,Answer() </para>
-                       <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
-                       <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
-                       <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
-                       <para>exten => 1,n,ConfBridge(1)</para>
+                       <para>This example shows how to use a predefined user profile in
+                       <filename>confbridge.conf</filename> as a template for a dynamic profile.
+                       Here we make an admin/marked user out of the <literal>my_user</literal>
+                       profile that you define in <filename>confbridge.conf</filename>.</para>
+                       <para>exten => 1,1,Answer()</para>
+                       <para>same => n,Set(CONFBRIDGE(user,template)=my_user)</para>
+                       <para>same => n,Set(CONFBRIDGE(user,admin)=yes)</para>
+                       <para>same => n,Set(CONFBRIDGE(user,marked)=yes)</para>
+                       <para>same => n,ConfBridge(1)</para>
                </description>
        </function>
        <function name="CONFBRIDGE_INFO" language="en_US">
@@ -163,14 +183,32 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                </synopsis>
                <syntax>
                        <parameter name="type" required="true">
-                               <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
+                               <para>What conference information is requested.</para>
+                               <enumlist>
+                                       <enum name="admins">
+                                               <para>Get the number of admin users in the conference.</para>
+                                       </enum>
+                                       <enum name="locked">
+                                               <para>Determine if the conference is locked. (0 or 1)</para>
+                                       </enum>
+                                       <enum name="marked">
+                                               <para>Get the number of marked users in the conference.</para>
+                                       </enum>
+                                       <enum name="muted">
+                                               <para>Determine if the conference is muted. (0 or 1)</para>
+                                       </enum>
+                                       <enum name="parties">
+                                               <para>Get the number of users in the conference.</para>
+                                       </enum>
+                               </enumlist>
                        </parameter>
                        <parameter name="conf" required="true">
-                               <para>Conf refers to the name of the conference being referenced.</para>
+                               <para>The name of the conference being referenced.</para>
                        </parameter>
                </syntax>
                <description>
-                       <para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para>
+                       <para>This function returns a non-negative integer for valid conference
+                       names and an empty string for invalid conference names.</para>
                </description>
        </function>
        <manager name="ConfbridgeList" language="en_US">
@@ -1312,6 +1350,13 @@ static struct confbridge_conference *join_conference_bridge(const char *conferen
 
        ao2_lock(conference);
 
+       /* Determine if the new user should join the conference muted. */
+       if (ast_test_flag(&user->u_profile, USER_OPT_STARTMUTED)
+               || (!ast_test_flag(&user->u_profile, USER_OPT_ADMIN) && conference->muted)) {
+               /* Set user level mute request. */
+               user->muted = 1;
+       }
+
        /*
         * Suspend any MOH until the user actually joins the bridge of
         * the conference.  This way any pre-join file playback does not
@@ -1545,6 +1590,13 @@ static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
        return -1;
 }
 
+static int user_timeout(struct ast_bridge_channel *bridge_channel, void *ignore)
+{
+       ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
+       pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "TIMEOUT");
+       return -1;
+}
+
 static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
 {
        char destdir[PATH_MAX];
@@ -1722,12 +1774,6 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
                }
        }
 
-       /* If the caller should be joined already muted, set the flag before we join. */
-       if (ast_test_flag(&user.u_profile, USER_OPT_STARTMUTED)) {
-               /* Set user level mute request. */
-               user.muted = 1;
-       }
-
        /* Look for a conference bridge matching the provided name */
        if (!(conference = join_conference_bridge(args.conf_name, &user))) {
                pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
@@ -1779,6 +1825,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
                ast_autoservice_stop(chan);
        }
 
+       if (user.u_profile.timeout) {
+               ast_bridge_interval_hook(&user.features,
+                       0,
+                       user.u_profile.timeout * 1000,
+                       user_timeout,
+                       NULL,
+                       NULL,
+                       AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+       }
+
        /* See if we need to automatically set this user as a video source or not */
        handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER));
 
@@ -2433,11 +2489,16 @@ static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct
        if (a->argc == 2) {
                struct ao2_iterator iter;
 
-               ast_cli(a->fd, "Conference Bridge Name           Users  Marked Locked?\n");
-               ast_cli(a->fd, "================================ ====== ====== ========\n");
+               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 %s\n", conference->name, conference->activeusers + conference->waitingusers, conference->markedusers, (conference->locked ? "locked" : "unlocked"));
+                       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);
@@ -2927,12 +2988,14 @@ static int action_confbridgelistrooms(struct mansession *s, const struct message
                "Parties: %u\r\n"
                "Marked: %u\r\n"
                "Locked: %s\r\n"
+               "Muted: %s\r\n"
                "\r\n",
                id_text,
                conference->name,
                conference->activeusers + conference->waitingusers,
                conference->markedusers,
-               conference->locked ? "Yes" : "No");
+               AST_YESNO(conference->locked),
+               AST_YESNO(conference->muted));
                ao2_unlock(conference);
 
                ao2_ref(conference, -1);
@@ -3203,30 +3266,31 @@ static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char
 
        /* get the correct count for the type requested */
        ao2_lock(conference);
-       if (!strncasecmp(args.type, "parties", 7)) {
+       if (!strcasecmp(args.type, "parties")) {
                AST_LIST_TRAVERSE(&conference->active_list, user, list) {
                        count++;
                }
                AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
                        count++;
                }
-       } else if (!strncasecmp(args.type, "admins", 6)) {
+       } else if (!strcasecmp(args.type, "admins")) {
                AST_LIST_TRAVERSE(&conference->active_list, user, list) {
                        if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
                                count++;
                        }
                }
-       } else if (!strncasecmp(args.type, "marked", 6)) {
+       } else if (!strcasecmp(args.type, "marked")) {
                AST_LIST_TRAVERSE(&conference->active_list, user, list) {
                        if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
                                count++;
                        }
                }
-       } else if (!strncasecmp(args.type, "locked", 6)) {
+       } else if (!strcasecmp(args.type, "locked")) {
                count = conference->locked;
+       } else if (!strcasecmp(args.type, "muted")) {
+               count = conference->muted;
        } else {
-               ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.  Should be one of: "
-                       "parties, admins, marked, or locked.\n", args.type);
+               ast_log(LOG_ERROR, "Invalid keyword '%s' passed to CONFBRIDGE_INFO.\n", args.type);
        }
        snprintf(buf, len, "%d", count);
        ao2_unlock(conference);