aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2011-07-06 04:52:35 +0200
committerPatrick McHardy <kaber@trash.net>2011-07-06 04:52:35 +0200
commit916e420bf0c8db7a8cb1f60557cd2807652142cf (patch)
treeece8aaf3f22e6e1cc545a858cad9e05c34713426 /apps
parent9364aaccb699c6d19ac2cbe760c208b34ba7838a (diff)
parent357b97fb29d196a5f336d6a2879278ea135ab08c (diff)
Merge branch 'master' of 192.168.0.100:/repos/git/asterisk
Diffstat (limited to 'apps')
-rw-r--r--apps/app_confbridge.c234
-rw-r--r--apps/app_dial.c4
-rw-r--r--apps/app_meetme.c2
-rw-r--r--apps/app_queue.c377
-rw-r--r--apps/app_stack.c2
-rw-r--r--apps/app_voicemail.c6
-rw-r--r--apps/confbridge/conf_config_parser.c24
-rw-r--r--apps/confbridge/include/confbridge.h4
8 files changed, 516 insertions, 137 deletions
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
index 177ee3717..d046c489f 100644
--- a/apps/app_confbridge.c
+++ b/apps/app_confbridge.c
@@ -77,6 +77,11 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
<para>Enters the user into a specified conference bridge. The user can exit the conference by hangup or DTMF menu option.</para>
</description>
+ <see-also>
+ <ref type="application">ConfBridge</ref>
+ <ref type="function">CONFBRIDGE</ref>
+ <ref type="function">CONFBRIDGE_INFO</ref>
+ </see-also>
</application>
<function name="CONFBRIDGE" language="en_US">
<synopsis>
@@ -233,6 +238,19 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<description>
</description>
</manager>
+ <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
+ <synopsis>
+ Set a conference user as the single video source distributed to all other participants.
+ </synopsis>
+ <syntax>
+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+ <parameter name="Conference" required="true" />
+ <parameter name="Channel" required="true" />
+ </syntax>
+ <description>
+ </description>
+ </manager>
+
***/
/*!
@@ -547,9 +565,9 @@ static void send_leave_event(struct ast_channel *chan, const char *conf_name)
* \param (OPTIONAL) conference_bridge_user Caller
*
* \note if caller is NULL, the announcment will be sent to all participants in the conference.
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*/
-static void announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference_bridge->b_profile.sounds);
const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference_bridge->b_profile.sounds);
@@ -557,14 +575,14 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
if (conference_bridge->users == 1) {
/* Awww we are the only person in the conference bridge */
- return;
+ return 0;
} else if (conference_bridge->users == 2) {
if (conference_bridge_user) {
/* Eep, there is one other person */
if (ast_stream_and_wait(conference_bridge_user->chan,
only_one,
"")) {
- return;
+ return -1;
}
} else {
play_sound_file(conference_bridge, only_one);
@@ -575,15 +593,15 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
if (ast_stream_and_wait(conference_bridge_user->chan,
there_are,
"")) {
- return;
+ return -1;
}
if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
- return;
+ return -1;
}
if (ast_stream_and_wait(conference_bridge_user->chan,
other_in_party,
"")) {
- return;
+ return -1;
}
} else {
play_sound_file(conference_bridge, there_are);
@@ -591,6 +609,7 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
play_sound_file(conference_bridge, other_in_party);
}
}
+ return 0;
}
/*!
@@ -600,15 +619,79 @@ static void announce_user_count(struct conference_bridge *conference_bridge, str
* \param chan Channel to play audio prompt to
* \param file Prompt to play
*
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*
* \note This function assumes that conference_bridge is locked
*/
-static void play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
+static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
{
+ int res;
ao2_unlock(conference_bridge);
- ast_stream_and_wait(chan, file, "");
+ res = ast_stream_and_wait(chan, file, "");
ao2_lock(conference_bridge);
+ return res;
+}
+
+static void handle_video_on_join(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ /* only automatically set video source for marked users */
+ if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
+ return;
+ }
+
+ if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
+ int set = 1;
+ struct conference_bridge_user *tmp_user = NULL;
+ ao2_lock(conference_bridge);
+ /* see if anyone is already the video src */
+ AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
+ if (tmp_user == conference_bridge_user) {
+ continue;
+ }
+ if (ast_bridge_is_video_src(conference_bridge->bridge, tmp_user->chan)) {
+ set = 0;
+ break;
+ }
+ }
+ ao2_unlock(conference_bridge);
+ if (set) {
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, conference_bridge_user->chan);
+ }
+ } else if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
+ /* we joined and are video capable, we override anyone else that may have already been the video feed */
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, conference_bridge_user->chan);
+ }
+}
+
+static void handle_video_on_exit(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ struct conference_bridge_user *tmp_user = NULL;
+
+ /* if this isn't a video source, nothing to update */
+ if (!ast_bridge_is_video_src(conference_bridge->bridge, conference_bridge_user->chan)) {
+ return;
+ }
+
+ ast_bridge_remove_video_src(conference_bridge->bridge, conference_bridge_user->chan);
+
+ /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
+ if (!ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
+ !ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
+ return;
+ }
+
+ /* Make the next avaliable marked user the video src. */
+ ao2_lock(conference_bridge);
+ AST_LIST_TRAVERSE(&conference_bridge->users_list, tmp_user, list) {
+ if (tmp_user == conference_bridge_user) {
+ continue;
+ }
+ if (ast_test_flag(&tmp_user->u_profile, USER_OPT_MARKEDUSER)) {
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, tmp_user->chan);
+ break;
+ }
+ }
+ ao2_unlock(conference_bridge);
}
/*!
@@ -617,16 +700,17 @@ static void play_prompt_to_channel(struct conference_bridge *conference_bridge,
* \param conference_bridge Conference bridge being joined
* \param conference_bridge_user Conference bridge user joining
*
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*/
-static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER)) {
struct conference_bridge_user *other_conference_bridge_user = NULL;
- /* If we are not the first marked user to join just bail out now */
+ /* If we are not the first user to join, then the users are already
+ * in the conference so we do not need to update them. */
if (conference_bridge->markedusers >= 2) {
- return;
+ return 0;
}
/* Iterate through every participant stopping MOH on them if need be */
@@ -661,19 +745,21 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
other_conference_bridge_user->features.mute = 0;
}
}
-
} else {
/* If a marked user already exists in the conference bridge we can just bail out now */
if (conference_bridge->markedusers) {
- return;
+ return 0;
}
/* Be sure we are muted so we can't talk to anybody else waiting */
conference_bridge_user->features.mute = 1;
/* If we have not been quieted play back that they are waiting for the leader */
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET)) {
- play_prompt_to_channel(conference_bridge,
+ if (play_prompt_to_channel(conference_bridge,
conference_bridge_user->chan,
- conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds));
+ conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, conference_bridge_user->b_profile.sounds))) {
+ /* user hungup while the sound was playing */
+ return -1;
+ }
}
/* Start music on hold if needed */
/* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
@@ -684,6 +770,7 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
conference_bridge_user->playing_moh = 1;
}
}
+ return 0;
}
/*!
@@ -692,17 +779,20 @@ static void post_join_marked(struct conference_bridge *conference_bridge, struct
* \param conference_bridge Conference bridge being joined
* \param conference_bridge_user Conference bridge user joining
*
- * \return Returns nothing
+ * \return Returns 0 on success, -1 if the user hung up
*/
-static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
/* Play back audio prompt and start MOH if need be if we are the first participant */
if (conference_bridge->users == 1) {
/* If audio prompts have not been quieted or this prompt quieted play it on out */
if (!ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
- play_prompt_to_channel(conference_bridge,
+ if (play_prompt_to_channel(conference_bridge,
conference_bridge_user->chan,
- conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds));
+ conf_get_sound(CONF_SOUND_ONLY_PERSON, conference_bridge_user->b_profile.sounds))) {
+ /* user hungup while the sound was playing */
+ return -1;
+ }
}
/* If we need to start music on hold on the channel do so now */
/* We need to re-check the number of users in the conference bridge here because another conference bridge
@@ -712,13 +802,16 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->u_profile.moh_class, NULL);
conference_bridge_user->playing_moh = 1;
}
- return;
+ return 0;
}
/* Announce number of users if need be */
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
ao2_unlock(conference_bridge);
- announce_user_count(conference_bridge, conference_bridge_user);
+ if (announce_user_count(conference_bridge, conference_bridge_user)) {
+ ao2_lock(conference_bridge);
+ return -1;
+ }
ao2_lock(conference_bridge);
}
@@ -737,9 +830,13 @@ static void post_join_unmarked(struct conference_bridge *conference_bridge, stru
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
(conference_bridge->users > conference_bridge_user->u_profile.announce_user_count_all_after)) {
ao2_unlock(conference_bridge);
- announce_user_count(conference_bridge, NULL);
+ if (announce_user_count(conference_bridge, NULL)) {
+ ao2_lock(conference_bridge);
+ return -1;
+ }
ao2_lock(conference_bridge);
}
+ return 0;
}
/*!
@@ -772,6 +869,8 @@ static void destroy_conference_bridge(void *obj)
conf_bridge_profile_destroy(&conference_bridge->b_profile);
}
+static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
+
/*!
* \brief Join a conference bridge
*
@@ -840,6 +939,10 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
/* Set the internal mixing interval on the bridge from the bridge profile */
ast_bridge_set_mixing_interval(conference_bridge->bridge, conference_bridge->b_profile.mix_interval);
+ if (ast_test_flag(&conference_bridge->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
+ ast_bridge_set_talker_src_video_mode(conference_bridge->bridge);
+ }
+
/* Setup lock for playback channel */
ast_mutex_init(&conference_bridge->playback_lock);
@@ -876,9 +979,17 @@ static struct conference_bridge *join_conference_bridge(const char *name, struct
/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
if (ast_test_flag(&conference_bridge_user->u_profile, USER_OPT_MARKEDUSER | USER_OPT_WAITMARKED)) {
- post_join_marked(conference_bridge, conference_bridge_user);
+ if (post_join_marked(conference_bridge, conference_bridge_user)) {
+ ao2_unlock(conference_bridge);
+ leave_conference_bridge(conference_bridge, conference_bridge_user);
+ return NULL;
+ }
} else {
- post_join_unmarked(conference_bridge, conference_bridge_user);
+ if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
+ ao2_unlock(conference_bridge);
+ leave_conference_bridge(conference_bridge, conference_bridge_user);
+ return NULL;
+ }
}
/* check to see if recording needs to be started or not */
@@ -1335,14 +1446,16 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
ast_moh_stop(chan);
}
ast_stream_and_wait(chan, join_sound, "");
- if (conference_bridge_user.playing_moh) {
- ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
- }
ast_autoservice_start(chan);
play_sound_file(conference_bridge, join_sound);
ast_autoservice_stop(chan);
+ if (conference_bridge_user.playing_moh) {
+ ast_moh_start(chan, conference_bridge_user.u_profile.moh_class, NULL);
+ }
}
+ handle_video_on_join(conference_bridge, &conference_bridge_user);
+
/* Join our conference bridge for real */
send_join_event(conference_bridge_user.chan, conference_bridge->name);
ast_bridge_join(conference_bridge->bridge,
@@ -1352,6 +1465,9 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
&conference_bridge_user.tech_args);
send_leave_event(conference_bridge_user.chan, conference_bridge->name);
+
+ handle_video_on_exit(conference_bridge, &conference_bridge_user);
+
/* if this user has a intro, play it when leaving */
if (!quiet && !ast_strlen_zero(conference_bridge_user.name_rec_location)) {
ast_autoservice_start(chan);
@@ -1373,9 +1489,6 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
leave_conference_bridge(conference_bridge, &conference_bridge_user);
conference_bridge = NULL;
- /* Can't forget to clean up the features structure, or else we risk a memory leak */
- ast_bridge_features_cleanup(&conference_bridge_user.features);
-
/* If the user was kicked from the conference play back the audio prompt for it */
if (!quiet && conference_bridge_user.kicked) {
res = ast_stream_and_wait(chan,
@@ -1657,6 +1770,11 @@ static int execute_menu_entry(struct conference_bridge *conference_bridge,
break;
case MENU_ACTION_NOOP:
break;
+ case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
+ ao2_lock(conference_bridge);
+ ast_bridge_set_single_src_video_mode(conference_bridge->bridge, bridge_channel->chan);
+ ao2_unlock(conference_bridge);
+ break;
}
}
return res;
@@ -2412,6 +2530,55 @@ static int action_confbridgestoprecord(struct mansession *s, const struct messag
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, participant->chan->name, 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;
@@ -2543,6 +2710,7 @@ static int load_module(void)
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);
conf_load_config(0);
return res;
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 25172c743..d3b8009de 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -65,6 +65,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/aoc.h"
#include "asterisk/ccss.h"
#include "asterisk/indications.h"
+#include "asterisk/framehook.h"
/*** DOCUMENTATION
<application name="Dial" language="en_US">
@@ -631,7 +632,8 @@ END_OPTIONS );
OPT_CALLER_HANGUP | OPT_CALLEE_TRANSFER | OPT_CALLER_TRANSFER | \
OPT_CALLEE_MONITOR | OPT_CALLER_MONITOR | OPT_CALLEE_PARK | \
OPT_CALLER_PARK | OPT_ANNOUNCE | OPT_CALLEE_MACRO | OPT_CALLEE_GOSUB) && \
- !chan->audiohooks && !peer->audiohooks)
+ !chan->audiohooks && !peer->audiohooks && \
+ ast_framehook_list_is_empty(chan->framehooks) && ast_framehook_list_is_empty(peer->framehooks))
/*
* The list of active channels
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index dad293b6e..40f5b0ee4 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -371,7 +371,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>Run admin <replaceable>command</replaceable> for a specific
- <replaceable>channel</replaceable> in any coference.</para>
+ <replaceable>channel</replaceable> in any conference.</para>
</description>
</application>
<application name="SLAStation" language="en_US">
diff --git a/apps/app_queue.c b/apps/app_queue.c
index be929c1dc..9442ce649 100644
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -57,7 +57,7 @@
*/
/*** MODULEINFO
- <use>res_monitor</use>
+ <use type="module">res_monitor</use>
***/
#include "asterisk.h"
@@ -521,11 +521,25 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<enum name="count">
<para>Returns the total number of members for the specified queue.</para>
</enum>
+ <enum name="penalty">
+ <para>Gets or sets queue member penalty.</para>
+ </enum>
+ <enum name="paused">
+ <para>Gets or sets queue member paused status.</para>
+ </enum>
+ <enum name="ignorebusy">
+ <para>Gets or sets queue member ignorebusy.</para>
+ </enum>
</enumlist>
</parameter>
+ <parameter name="interface" required="false" />
</syntax>
<description>
- <para>Returns the number of members currently associated with the specified <replaceable>queuename</replaceable>.</para>
+ <para>Allows access to queue counts [R] and member information [R/W].</para>
+ <para>
+ <replaceable>queuename</replaceable> is required for all operations
+ <replaceable>interface</replaceable> is required for all member operations.
+ </para>
</description>
<see-also>
<ref type="application">Queue</ref>
@@ -658,6 +672,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</syntax>
<description>
<para>Gets or sets queue members penalty.</para>
+ <warning><para>This function has been deprecated in favor of the <literal>QUEUE_MEMBER()</literal> function</para></warning>
</description>
<see-also>
<ref type="application">Queue</ref>
@@ -680,7 +695,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Queues.
</synopsis>
<syntax>
- <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
</description>
@@ -934,6 +948,9 @@ static struct ast_event_sub *device_state_sub;
/*! \brief queues.conf [general] option */
static int update_cdr = 0;
+/*! \brief queues.conf [general] option */
+static int negative_penalty_invalid = 0;
+
enum queue_result {
QUEUE_UNKNOWN = 0,
QUEUE_TIMEOUT = 1,
@@ -1043,6 +1060,7 @@ struct member {
unsigned int dead:1; /*!< Used to detect members deleted in realtime */
unsigned int delme:1; /*!< Flag to delete entry on reload */
char rt_uniqueid[80]; /*!< Unique id of realtime member entry */
+ unsigned int ignorebusy:1; /*!< Flag to ignore member if the status is not available */
};
enum empty_conditions {
@@ -1160,6 +1178,7 @@ struct call_queue {
int timeout; /*!< How long to wait for an answer */
int weight; /*!< Respective weight */
int autopause; /*!< Auto pause queue members if they fail to answer */
+ int autopausedelay; /*!< Delay auto pause for autopausedelay seconds since last call */
int timeoutpriority; /*!< Do we allow a fraction of the timeout to occur for a ring? */
/* Queue strategy things */
@@ -1190,9 +1209,10 @@ static AST_LIST_HEAD_STATIC(rule_lists, rule_list);
static struct ao2_container *queues;
static void update_realtime_members(struct call_queue *q);
+static struct member *interface_exists(struct call_queue *q, const char *interface);
static int set_member_paused(const char *queuename, const char *interface, const char *reason, int paused);
-static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
+static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan);
/*! \brief sets the QUEUESTATUS channel variable */
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
{
@@ -1698,6 +1718,7 @@ static void init_queue(struct call_queue *q)
q->numperiodicannounce = 0;
q->autopause = QUEUE_AUTOPAUSE_OFF;
q->timeoutpriority = TIMEOUT_PRIORITY_APP;
+ q->autopausedelay = 0;
if (!q->members) {
if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
/* linear strategy depends on order, so we have to place all members in a single bucket */
@@ -1760,7 +1781,7 @@ static void clear_queue(struct call_queue *q)
* \retval 0 on success
* \note Call this with the rule_lists locked
*/
-static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
+static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
{
char *timestr, *maxstr, *minstr, *contentdup;
struct penalty_rule *rule = NULL, *rule_iter;
@@ -2003,6 +2024,8 @@ static void queue_set_param(struct call_queue *q, const char *param, const char
q->montype = 1;
} else if (!strcasecmp(param, "autopause")) {
q->autopause = autopause2int(val);
+ } else if (!strcasecmp(param, "autopausedelay")) {
+ q->autopausedelay = atoi(val);
} else if (!strcasecmp(param, "maxlen")) {
q->maxlen = atoi(val);
if (q->maxlen < 0)
@@ -2081,7 +2104,9 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
int penalty = 0;
int paused = 0;
int found = 0;
+ int ignorebusy = 0;
+ const char *config_val;
const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
@@ -2095,8 +2120,11 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
if (penalty_str) {
penalty = atoi(penalty_str);
- if (penalty < 0)
+ if ((penalty < 0) && negative_penalty_invalid) {
+ return;
+ } else if (penalty < 0) {
penalty = 0;
+ }
}
if (paused_str) {
@@ -2105,31 +2133,39 @@ static void rt_handle_member_record(struct call_queue *q, char *interface, struc
paused = 0;
}
- /* Find member by realtime uniqueid and update */
- mem_iter = ao2_iterator_init(q->members, 0);
- while ((m = ao2_iterator_next(&mem_iter))) {
- if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
- m->dead = 0; /* Do not delete this one. */
- ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
- if (paused_str)
- m->paused = paused;
- if (strcasecmp(state_interface, m->state_interface)) {
- ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
- }
- m->penalty = penalty;
- found = 1;
- ao2_ref(m, -1);
- break;
- }
- ao2_ref(m, -1);
- }
+ if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy"))) {
+ ignorebusy = ast_true(config_val);
+ } else {
+ ignorebusy = 1;
+ }
+
+ /* Find member by realtime uniqueid and update */
+ mem_iter = ao2_iterator_init(q->members, 0);
+ while ((m = ao2_iterator_next(&mem_iter))) {
+ if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
+ m->dead = 0; /* Do not delete this one. */
+ ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
+ if (paused_str)
+ m->paused = paused;
+ if (strcasecmp(state_interface, m->state_interface)) {
+ ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
+ }
+ m->penalty = penalty;
+ m->ignorebusy = ignorebusy;
+ found = 1;
+ ao2_ref(m, -1);
+ break;
+ }
+ ao2_ref(m, -1);
+ }
ao2_iterator_destroy(&mem_iter);
- /* Create a new member */
- if (!found) {
+ /* Create a new member */
+ if (!found) {
if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
m->dead = 0;
m->realtime = 1;
+ m->ignorebusy = ignorebusy;
ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
ao2_link(q->members, m);
@@ -2873,16 +2909,24 @@ static int num_available_members(struct call_queue *q)
mem_iter = ao2_iterator_init(q->members, 0);
while ((mem = ao2_iterator_next(&mem_iter))) {
switch (mem->status) {
- case AST_DEVICE_INUSE:
- if (!q->ringinuse)
+ case AST_DEVICE_INVALID:
+ case AST_DEVICE_UNAVAILABLE:
+ break;
+ case AST_DEVICE_INUSE:
+ case AST_DEVICE_BUSY:
+ case AST_DEVICE_RINGING:
+ case AST_DEVICE_RINGINUSE:
+ case AST_DEVICE_ONHOLD:
+ if ((!q->ringinuse) || (!mem->ignorebusy)) {
+ break;
+ }
+ /* else fall through */
+ case AST_DEVICE_NOT_INUSE:
+ case AST_DEVICE_UNKNOWN:
+ if (!mem->paused) {
+ avl++;
+ }
break;
- /* else fall through */
- case AST_DEVICE_NOT_INUSE:
- case AST_DEVICE_UNKNOWN:
- if (!mem->paused) {
- avl++;
- }
- break;
}
ao2_ref(mem, -1);
@@ -3010,38 +3054,54 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
char tech[256];
char *location;
const char *macrocontext, *macroexten;
+ enum ast_device_state newstate;
/* on entry here, we know that tmp->chan == NULL */
- if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
- (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
- ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
- (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
- if (qe->chan->cdr)
+ if (tmp->member->paused) {
+ ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
- (*busies)++;
return 0;
}
- if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
- ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
- if (qe->chan->cdr)
+ if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
+ (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
+ ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
+ (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
+ (*busies)++;
return 0;
}
- if (tmp->member->paused) {
- ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
- if (qe->chan->cdr)
- ast_cdr_busy(qe->chan->cdr);
- tmp->stillgoing = 0;
- return 0;
+ if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
+ if ((tmp->member->status == AST_DEVICE_UNKNOWN) || (tmp->member->status == AST_DEVICE_NOT_INUSE)) {
+ newstate = ast_parse_device_state(tmp->member->interface);
+ if (newstate != tmp->member->status) {
+ ast_log(LOG_ERROR, "Found a channel matching iterface %s while status was %i changed to %i\n",
+ tmp->member->interface, tmp->member->status, newstate);
+ update_status(qe->parent, tmp->member, newstate);
+ }
+ }
+ if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
+ ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
+ if (qe->chan->cdr) {
+ ast_cdr_busy(qe->chan->cdr);
+ }
+ tmp->stillgoing = 0;
+ return 0;
+ }
}
+
if (use_weight && compare_weight(qe->parent,tmp->member)) {
ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
(*busies)++;
return 0;
@@ -3056,8 +3116,9 @@ static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies
/* Request the peer */
tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
if (!tmp->chan) { /* If we can't, just go on to the next call */
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_cdr_busy(qe->chan->cdr);
+ }
tmp->stillgoing = 0;
ao2_lock(qe->parent);
@@ -3396,6 +3457,18 @@ static void rna(int rnatime, struct queue_ent *qe, char *interface, char *member
}
ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
+ if (qe->parent->autopausedelay > 0) {
+ struct member *mem;
+ ao2_lock(qe->parent);
+ if ((mem = interface_exists(qe->parent, interface))) {
+ time_t idletime = time(&idletime)-mem->lastcall;
+ if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
+ ao2_unlock(qe->parent);
+ return;
+ }
+ }
+ ao2_unlock(qe->parent);
+ }
if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
@@ -4707,8 +4780,9 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
else
ast_moh_stop(qe->chan);
/* If appropriate, log that we have a destination channel */
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_cdr_setdestchan(qe->chan->cdr, peer->name);
+ }
/* Make sure channels are compatible */
res = ast_channel_make_compatible(qe->chan, peer);
if (res < 0) {
@@ -4788,10 +4862,11 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
if (mixmonapp) {
ast_debug(1, "Starting MixMonitor as requested.\n");
if (!monitorfilename) {
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
- else
+ } else {
snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
+ }
} else {
const char *m = monitorfilename;
for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
@@ -4858,12 +4933,13 @@ static int try_calling(struct queue_ent *qe, const char *options, char *announce
ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
/* We purposely lock the CDR so that pbx_exec does not update the application data */
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
+ }
pbx_exec(qe->chan, mixmonapp, mixmonargs);
- if (qe->chan->cdr)
+ if (qe->chan->cdr) {
ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
-
+ }
} else {
ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
}
@@ -5180,7 +5256,10 @@ static int remove_from_queue(const char *queuename, const char *interface)
ao2_lock(q);
if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
/* XXX future changes should beware of this assumption!! */
- if (!mem->dynamic) {
+ /*Change Penalty on realtime users*/
+ if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
+ update_realtime_member_field(mem, q->name, "penalty", "-1");
+ } else if (!mem->dynamic) {
ao2_ref(mem, -1);
ao2_unlock(q);
queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
@@ -5355,35 +5434,34 @@ static int set_member_penalty(const char *queuename, const char *interface, int
int foundinterface = 0, foundqueue = 0;
struct call_queue *q;
struct member *mem;
- struct ao2_iterator queue_iter;
+ char rtpenalty[80];
- if (penalty < 0) {
+ if (penalty < 0 && !negative_penalty_invalid) {
ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
return RESULT_FAILURE;
}
- queue_iter = ao2_iterator_init(queues, 0);
- while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
+ if ((q = load_realtime_queue(queuename))) {
+ foundqueue++;
ao2_lock(q);
- if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
- foundqueue++;
- if ((mem = interface_exists(q, interface))) {
- foundinterface++;
+ if ((mem = interface_exists(q, interface))) {
+ foundinterface++;
+ if (!mem->realtime) {
mem->penalty = penalty;
-
- ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
- manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
- "Queue: %s\r\n"
- "Location: %s\r\n"
- "Penalty: %d\r\n",
- q->name, mem->interface, penalty);
- ao2_ref(mem, -1);
+ } else {
+ sprintf(rtpenalty,"%i", penalty);
+ update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
}
+ ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
+ manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
+ "Queue: %s\r\n"
+ "Location: %s\r\n"
+ "Penalty: %d\r\n",
+ q->name, mem->interface, penalty);
+ ao2_ref(mem, -1);
}
ao2_unlock(q);
- queue_t_unref(q, "Done with iterator");
}
- ao2_iterator_destroy(&queue_iter);
if (foundinterface) {
return RESULT_SUCCESS;
@@ -5769,7 +5847,6 @@ static void copy_rules(struct queue_ent *qe, const char *rulename)
struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
if (!new_pr) {
ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
- AST_LIST_UNLOCK(&rule_lists);
break;
}
new_pr->time = pr_iter->time;
@@ -6157,31 +6234,37 @@ static int queue_function_exists(struct ast_channel *chan, const char *cmd, char
return 0;
}
-/*!
+/*!
* \brief Get number either busy / free / ready or total members of a specific queue
- * \retval number of members (busy / free / ready / total)
+ * \brief Get or set member properties penalty / paused / ignorebusy
+ * \retval number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
* \retval -1 on error
*/
-static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+static int queue_function_mem_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
int count = 0;
struct member *m;
struct ao2_iterator mem_iter;
struct call_queue *q;
- char *option;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(option);
+ AST_APP_ARG(interface);
+ );
+ /* Make sure the returned value on error is zero length string. */
+ buf[0] = '\0';
if (ast_strlen_zero(data)) {
ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
return -1;
}
- if ((option = strchr(data, ',')))
- *option++ = '\0';
- else
- option = "logged";
- if ((q = load_realtime_queue(data))) {
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if ((q = load_realtime_queue(args.queuename))) {
ao2_lock(q);
- if (!strcasecmp(option, "logged")) {
+ if (!strcasecmp(args.option, "logged")) {
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
@@ -6191,7 +6274,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else if (!strcasecmp(option, "free")) {
+ } else if (!strcasecmp(args.option, "free")) {
mem_iter = ao2_iterator_init(q->members, 0);
while ((m = ao2_iterator_next(&mem_iter))) {
/* Count the agents who are logged in and presently answering calls */
@@ -6201,7 +6284,7 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else if (!strcasecmp(option, "ready")) {
+ } else if (!strcasecmp(args.option, "ready")) {
time_t now;
time(&now);
mem_iter = ao2_iterator_init(q->members, 0);
@@ -6214,22 +6297,104 @@ static int queue_function_qac(struct ast_channel *chan, const char *cmd, char *d
ao2_ref(m, -1);
}
ao2_iterator_destroy(&mem_iter);
- } else /* must be "count" */
+ } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
count = q->membercount;
+ } else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
+ ((m = interface_exists(q, args.interface)))) {
+ count = m->penalty;
+ } else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
+ ((m = interface_exists(q, args.interface)))) {
+ count = m->paused;
+ } else if (!strcasecmp(args.option, "ignorebusy") && !ast_strlen_zero(args.interface) &&
+ ((m = interface_exists(q, args.interface)))) {
+ count = m->ignorebusy;
+ }
ao2_unlock(q);
queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
- } else
- ast_log(LOG_WARNING, "queue %s was not found\n", data);
+ } else {
+ ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
+ }
snprintf(buf, len, "%d", count);
return 0;
}
-/*!
+/*! \brief Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy. */
+static int queue_function_mem_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+ int memvalue;
+ struct call_queue *q;
+ struct member *m;
+ char rtvalue[80];
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(queuename);
+ AST_APP_ARG(option);
+ AST_APP_ARG(interface);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
+ return -1;
+ }
+
+ AST_STANDARD_APP_ARGS(args, data);
+
+ if (args.argc < 3) {
+ ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
+ return -1;
+ }
+
+ if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
+ ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
+ return -1;
+ }
+
+ memvalue = atoi(value);
+
+ if (!strcasecmp(args.option, "penalty")) {
+ /* if queuename = NULL then penalty will be set for interface in all the queues.*/
+ if (set_member_penalty(args.queuename, args.interface, memvalue)) {
+ ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
+ return -1;
+ }
+ } else if ((q = load_realtime_queue(args.queuename))) {
+ ao2_lock(q);
+ if ((m = interface_exists(q, args.interface))) {
+ sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
+ if (!strcasecmp(args.option, "paused")) {
+ if (m->realtime) {
+ update_realtime_member_field(m, q->name, args.option, rtvalue);
+ } else {
+ m->paused = (memvalue <= 0) ? 0 : 1;
+ }
+ } else if (!strcasecmp(args.option, "ignorebusy")) {
+ if (m->realtime) {
+ update_realtime_member_field(m, q->name, args.option, rtvalue);
+ } else {
+ m->ignorebusy = (memvalue <= 0) ? 0 : 1;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
+ return -1;
+ }
+ } else {
+ ast_log(LOG_ERROR, "Invalid interface or queue\n");
+ return -1;
+ }
+ ao2_unlock(q);
+ } else {
+ ast_log(LOG_ERROR, "Invalid queue\n");
+ return -1;
+ }
+ return 0;
+}
+
+/*!
* \brief Get the total number of members in a specific queue (Deprecated)
- * \retval number of members
- * \retval -1 on error
+ * \retval number of members
+ * \retval -1 on error
*/
static int queue_function_qac_dep(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
{
@@ -6435,7 +6600,8 @@ static struct ast_custom_function queuevar_function = {
static struct ast_custom_function queuemembercount_function = {
.name = "QUEUE_MEMBER",
- .read = queue_function_qac,
+ .read = queue_function_mem_read,
+ .write = queue_function_mem_write,
};
static struct ast_custom_function queuemembercount_dep = {
@@ -6494,6 +6660,7 @@ static int reload_queue_rules(int reload)
while ((rulecat = ast_category_browse(cfg, rulecat))) {
if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
AST_LIST_UNLOCK(&rule_lists);
+ ast_config_destroy(cfg);
return AST_MODULE_LOAD_FAILURE;
} else {
ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
@@ -6517,8 +6684,9 @@ static void queue_set_global_params(struct ast_config *cfg)
{
const char *general_val = NULL;
queue_persistent_members = 0;
- if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
+ if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
queue_persistent_members = ast_true(general_val);
+ }
autofill_default = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
autofill_default = ast_true(general_val);
@@ -6533,6 +6701,9 @@ static void queue_set_global_params(struct ast_config *cfg)
shared_lastcall = 0;
if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
shared_lastcall = ast_true(general_val);
+ negative_penalty_invalid = 0;
+ if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid")))
+ negative_penalty_invalid = ast_true(general_val);
}
/*! \brief reload information pertaining to a single member
@@ -7087,9 +7258,15 @@ static int manager_queues_show(struct mansession *s, const struct message *m)
static int manager_queue_rule_show(struct mansession *s, const struct message *m)
{
const char *rule = astman_get_header(m, "Rule");
+ const char *id = astman_get_header(m, "ActionID");
struct rule_list *rl_iter;
struct penalty_rule *pr_iter;
+ astman_append(s, "Response: Success\r\n");
+ if (!ast_strlen_zero(id)) {
+ astman_append(s, "ActionID: %s\r\n", id);
+ }
+
AST_LIST_LOCK(&rule_lists);
AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
@@ -7103,6 +7280,10 @@ static int manager_queue_rule_show(struct mansession *s, const struct message *m
}
AST_LIST_UNLOCK(&rule_lists);
+ /*
+ * Two blank lines instead of one because the Response and
+ * ActionID headers used to not be present.
+ */
astman_append(s, "\r\n\r\n");
return RESULT_SUCCESS;
diff --git a/apps/app_stack.c b/apps/app_stack.c
index 29c37a20b..10a1af48d 100644
--- a/apps/app_stack.c
+++ b/apps/app_stack.c
@@ -26,7 +26,7 @@
*/
/*** MODULEINFO
- <use>res_agi</use>
+ <use type="module">res_agi</use>
***/
#include "asterisk.h"
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 5e342bda0..971e1879d 100644
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -38,8 +38,8 @@
*/
/*** MODULEINFO
- <use>res_adsi</use>
- <use>res_smdi</use>
+ <use type="module">res_adsi</use>
+ <use type="module">res_smdi</use>
***/
/*** MAKEOPTS
@@ -60,7 +60,7 @@
<depend>imap_tk</depend>
<conflict>ODBC_STORAGE</conflict>
<conflict>FILE_STORAGE</conflict>
- <use>openssl</use>
+ <use type="external">openssl</use>
<defaultenabled>no</defaultenabled>
</member>
</category>
diff --git a/apps/confbridge/conf_config_parser.c b/apps/confbridge/conf_config_parser.c
index 3d1f31326..8864f52bc 100644
--- a/apps/confbridge/conf_config_parser.c
+++ b/apps/confbridge/conf_config_parser.c
@@ -284,6 +284,14 @@ static int set_bridge_option(const char *name, const char *value, struct bridge_
}
} else if (!strcasecmp(name, "record_conference")) {
ast_set2_flag(b_profile, ast_true(value), BRIDGE_OPT_RECORD_CONFERENCE);
+ } else if (!strcasecmp(name, "video_mode")) {
+ if (!strcasecmp(value, "first_marked")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED);
+ } else if (!strcasecmp(value, "last_marked")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED);
+ } else if (!strcasecmp(value, "follow_talker")) {
+ ast_set_flag(b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER);
+ }
} else if (!strcasecmp(name, "max_members")) {
if (sscanf(value, "%30u", &b_profile->max_members) != 1) {
return -1;
@@ -534,6 +542,7 @@ static int add_action_to_menu_entry(struct conf_menu_entry *menu_entry, enum con
case MENU_ACTION_ADMIN_TOGGLE_LOCK:
case MENU_ACTION_ADMIN_KICK_LAST:
case MENU_ACTION_LEAVE:
+ case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
break;
case MENU_ACTION_PLAYBACK:
case MENU_ACTION_PLAYBACK_AND_CONTINUE:
@@ -649,6 +658,8 @@ static int add_menu_entry(struct conf_menu *menu, const char *dtmf, const char *
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_ADMIN_KICK_LAST, NULL);
} else if (!strcasecmp(action, "leave_conference")) {
res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_LEAVE, NULL);
+ } else if (!strcasecmp(action, "set_as_single_video_src")) {
+ res |= add_action_to_menu_entry(menu_entry, MENU_ACTION_SET_SINGLE_VIDEO_SRC, NULL);
} else if (!strncasecmp(action, "dialplan_exec(", 14)) {
ast_copy_string(buf, action, sizeof(buf));
action_args = buf;
@@ -983,6 +994,16 @@ static char *handle_cli_confbridge_show_bridge_profile(struct ast_cli_entry *e,
ast_cli(a->fd,"Max Members: No Limit\n");
}
+ if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_LAST_MARKED) {
+ ast_cli(a->fd, "Video Mode: last_marked\n");
+ } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) {
+ ast_cli(a->fd, "Video Mode: first_marked\n");
+ } else if (b_profile.flags & BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER) {
+ ast_cli(a->fd, "Video Mode: follow_talker\n");
+ } else {
+ ast_cli(a->fd, "Video Mode: no video\n");
+ }
+
ast_cli(a->fd,"sound_join: %s\n", conf_get_sound(CONF_SOUND_JOIN, b_profile.sounds));
ast_cli(a->fd,"sound_leave: %s\n", conf_get_sound(CONF_SOUND_LEAVE, b_profile.sounds));
ast_cli(a->fd,"sound_only_person: %s\n", conf_get_sound(CONF_SOUND_ONLY_PERSON, b_profile.sounds));
@@ -1142,6 +1163,9 @@ static char *handle_cli_confbridge_show_menu(struct ast_cli_entry *e, int cmd, s
case MENU_ACTION_LEAVE:
ast_cli(a->fd, "leave_conference");
break;
+ case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
+ ast_cli(a->fd, "set_as_single_video_src");
+ break;
}
action_num++;
}
diff --git a/apps/confbridge/include/confbridge.h b/apps/confbridge/include/confbridge.h
index de467b5f7..7a2f6bb07 100644
--- a/apps/confbridge/include/confbridge.h
+++ b/apps/confbridge/include/confbridge.h
@@ -61,6 +61,9 @@ enum user_profile_flags {
enum bridge_profile_flags {
BRIDGE_OPT_RECORD_CONFERENCE = (1 << 0), /*!< Set if the conference should be recorded */
+ BRIDGE_OPT_VIDEO_SRC_LAST_MARKED = (1 << 1), /*!< Set if conference should feed video of last marked user to all participants. */
+ BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED = (1 << 2), /*!< Set if conference should feed video of first marked user to all participants. */
+ BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER = (1 << 3), /*!< Set if conference set the video feed to follow the loudest talker. */
};
enum conf_menu_action_id {
@@ -78,6 +81,7 @@ enum conf_menu_action_id {
MENU_ACTION_ADMIN_KICK_LAST,
MENU_ACTION_LEAVE,
MENU_ACTION_NOOP,
+ MENU_ACTION_SET_SINGLE_VIDEO_SRC,
};
/*! The conference menu action contains both