summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorJeff Peeler <jpeeler@digium.com>2009-09-24 20:29:51 +0000
committerJeff Peeler <jpeeler@digium.com>2009-09-24 20:29:51 +0000
commitf150b48bc05a2d8f212966763775a04cd727d843 (patch)
treec2f0ba232beac2b342a7293657aace42a4a84c92 /main
parent79b9b75eab30fb3fa22f9c905539ad644517891a (diff)
Add bridge related dial flags to the bridge app
Most of the functionality here is gained simply by setting the feature flag on the bridge config. However, the dial limit functionality has been moved from app_dial to the features code and has been made public so both app_dial and the bridge app can use it. (closes issue #13165) Reported by: tim_ringenbach Patches: app_bridge_options_r138998.diff uploaded by tim ringenbach (license 540), modified by me git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@220344 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/features.c242
1 files changed, 237 insertions, 5 deletions
diff --git a/main/features.c b/main/features.c
index 4af8d1d11..4ec336b02 100644
--- a/main/features.c
+++ b/main/features.c
@@ -71,6 +71,69 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<option name="p">
<para>Play a courtesy tone to <replaceable>channel</replaceable>.</para>
</option>
+ <option name="h">
+ <para>Allow the called party to hang up by sending the
+ <replaceable>*</replaceable> DTMF digit.</para>
+ </option>
+ <option name="H">
+ <para>Allow the calling party to hang up by pressing the
+ <replaceable>*</replaceable> DTMF digit.</para>
+ </option>
+ <option name="k">
+ <para>Allow the called party to enable parking of the call by sending
+ the DTMF sequence defined for call parking in features.conf.</para>
+ </option>
+ <option name="K">
+ <para>Allow the calling party to enable parking of the call by sending
+ the DTMF sequence defined for call parking in features.conf.</para>
+ </option>
+ <option name="L(x[:y][:z])">
+ <para>Limit the call to <replaceable>x</replaceable> ms. Play a warning
+ when <replaceable>y</replaceable> ms are left. Repeat the warning every
+ <replaceable>z</replaceable> ms. The following special variables can be
+ used with this option:</para>
+ <variablelist>
+ <variable name="LIMIT_PLAYAUDIO_CALLER">
+ <para>Play sounds to the caller. yes|no (default yes)</para>
+ </variable>
+ <variable name="LIMIT_PLAYAUDIO_CALLEE">
+ <para>Play sounds to the callee. yes|no</para>
+ </variable>
+ <variable name="LIMIT_TIMEOUT_FILE">
+ <para>File to play when time is up.</para>
+ </variable>
+ <variable name="LIMIT_CONNECT_FILE">
+ <para>File to play when call begins.</para>
+ </variable>
+ <variable name="LIMIT_WARNING_FILE">
+ <para>File to play as warning if <replaceable>y</replaceable> is
+ defined. The default is to say the time remaining.</para>
+ </variable>
+ </variablelist>
+ </option>
+ <option name="S(x)">
+ <para>Hang up the call after <replaceable>x</replaceable> seconds *after* the called party has answered the call.</para>
+ </option>
+ <option name="t">
+ <para>Allow the called party to transfer the calling party by sending the
+ DTMF sequence defined in features.conf.</para>
+ </option>
+ <option name="T">
+ <para>Allow the calling party to transfer the called party by sending the
+ DTMF sequence defined in features.conf.</para>
+ </option>
+ <option name="w">
+ <para>Allow the called party to enable recording of the call by sending
+ the DTMF sequence defined for one-touch recording in features.conf.</para>
+ </option>
+ <option name="W">
+ <para>Allow the calling party to enable recording of the call by sending
+ the DTMF sequence defined for one-touch recording in features.conf.</para>
+ </option>
+ <option name="x">
+ <para>Cause the called party to be hung up after the bridge, instead of being
+ restarted in the dialplan.</para>
+ </option>
</optionlist>
</parameter>
</syntax>
@@ -4722,12 +4785,148 @@ static char *app_bridge = "Bridge";
enum {
BRIDGE_OPT_PLAYTONE = (1 << 0),
+ OPT_CALLEE_HANGUP = (1 << 1),
+ OPT_CALLER_HANGUP = (1 << 2),
+ OPT_DURATION_LIMIT = (1 << 3),
+ OPT_DURATION_STOP = (1 << 4),
+ OPT_CALLEE_TRANSFER = (1 << 5),
+ OPT_CALLER_TRANSFER = (1 << 6),
+ OPT_CALLEE_MONITOR = (1 << 7),
+ OPT_CALLER_MONITOR = (1 << 8),
+ OPT_CALLEE_PARK = (1 << 9),
+ OPT_CALLER_PARK = (1 << 10),
+ OPT_CALLEE_KILL = (1 << 11),
+};
+
+enum {
+ OPT_ARG_DURATION_LIMIT = 0,
+ OPT_ARG_DURATION_STOP,
+ /* note: this entry _MUST_ be the last one in the enum */
+ OPT_ARG_ARRAY_SIZE,
};
AST_APP_OPTIONS(bridge_exec_options, BEGIN_OPTIONS
- AST_APP_OPTION('p', BRIDGE_OPT_PLAYTONE)
+ AST_APP_OPTION('p', BRIDGE_OPT_PLAYTONE),
+ AST_APP_OPTION('h', OPT_CALLEE_HANGUP),
+ AST_APP_OPTION('H', OPT_CALLER_HANGUP),
+ AST_APP_OPTION('k', OPT_CALLEE_PARK),
+ AST_APP_OPTION('K', OPT_CALLER_PARK),
+ AST_APP_OPTION_ARG('L', OPT_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
+ AST_APP_OPTION_ARG('S', OPT_DURATION_STOP, OPT_ARG_DURATION_STOP),
+ AST_APP_OPTION('t', OPT_CALLEE_TRANSFER),
+ AST_APP_OPTION('T', OPT_CALLER_TRANSFER),
+ AST_APP_OPTION('w', OPT_CALLEE_MONITOR),
+ AST_APP_OPTION('W', OPT_CALLER_MONITOR),
+ AST_APP_OPTION('x', OPT_CALLEE_KILL),
END_OPTIONS );
+int ast_bridge_timelimit(struct ast_channel *chan, struct ast_bridge_config *config,
+ char *parse, struct timeval *calldurationlimit)
+{
+ char *stringp = ast_strdupa(parse);
+ char *limit_str, *warning_str, *warnfreq_str;
+ const char *var;
+ int play_to_caller = 0, play_to_callee = 0;
+ int delta;
+
+ limit_str = strsep(&stringp, ":");
+ warning_str = strsep(&stringp, ":");
+ warnfreq_str = strsep(&stringp, ":");
+
+ config->timelimit = atol(limit_str);
+ if (warning_str)
+ config->play_warning = atol(warning_str);
+ if (warnfreq_str)
+ config->warning_freq = atol(warnfreq_str);
+
+ if (!config->timelimit) {
+ ast_log(LOG_WARNING, "Bridge does not accept L(%s), hanging up.\n", limit_str);
+ config->timelimit = config->play_warning = config->warning_freq = 0;
+ config->warning_sound = NULL;
+ return -1; /* error */
+ } else if ( (delta = config->play_warning - config->timelimit) > 0) {
+ int w = config->warning_freq;
+
+ /* If the first warning is requested _after_ the entire call would end,
+ and no warning frequency is requested, then turn off the warning. If
+ a warning frequency is requested, reduce the 'first warning' time by
+ that frequency until it falls within the call's total time limit.
+ Graphically:
+ timelim->| delta |<-playwarning
+ 0__________________|_________________|
+ | w | | | |
+
+ so the number of intervals to cut is 1+(delta-1)/w
+ */
+
+ if (w == 0) {
+ config->play_warning = 0;
+ } else {
+ config->play_warning -= w * ( 1 + (delta-1)/w );
+ if (config->play_warning < 1)
+ config->play_warning = config->warning_freq = 0;
+ }
+ }
+
+ ast_channel_lock(chan);
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLER");
+ play_to_caller = var ? ast_true(var) : 1;
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_PLAYAUDIO_CALLEE");
+ play_to_callee = var ? ast_true(var) : 0;
+
+ if (!play_to_caller && !play_to_callee)
+ play_to_caller = 1;
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_WARNING_FILE");
+ config->warning_sound = !ast_strlen_zero(var) ? ast_strdup(var) : ast_strdup("timeleft");
+
+ /* The code looking at config wants a NULL, not just "", to decide
+ * that the message should not be played, so we replace "" with NULL.
+ * Note, pbx_builtin_getvar_helper _can_ return NULL if the variable is
+ * not found.
+ */
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_TIMEOUT_FILE");
+ config->end_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
+
+ var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
+ config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
+
+ ast_channel_unlock(chan);
+
+ /* undo effect of S(x) in case they are both used */
+ calldurationlimit->tv_sec = 0;
+ calldurationlimit->tv_usec = 0;
+
+ /* more efficient to do it like S(x) does since no advanced opts */
+ if (!config->play_warning && !config->start_sound && !config->end_sound && config->timelimit) {
+ calldurationlimit->tv_sec = config->timelimit / 1000;
+ calldurationlimit->tv_usec = (config->timelimit % 1000) * 1000;
+ ast_verb(3, "Setting call duration limit to %.3lf seconds.\n",
+ calldurationlimit->tv_sec + calldurationlimit->tv_usec / 1000000.0);
+ config->timelimit = play_to_caller = play_to_callee =
+ config->play_warning = config->warning_freq = 0;
+ } else {
+ ast_verb(3, "Limit Data for this call:\n");
+ ast_verb(4, "timelimit = %ld\n", config->timelimit);
+ ast_verb(4, "play_warning = %ld\n", config->play_warning);
+ ast_verb(4, "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
+ ast_verb(4, "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
+ ast_verb(4, "warning_freq = %ld\n", config->warning_freq);
+ ast_verb(4, "start_sound = %s\n", S_OR(config->start_sound, ""));
+ ast_verb(4, "warning_sound = %s\n", config->warning_sound);
+ ast_verb(4, "end_sound = %s\n", S_OR(config->end_sound, ""));
+ }
+ if (play_to_caller)
+ ast_set_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
+ if (play_to_callee)
+ ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
+ return 0;
+}
+
+
/*!
* \brief Bridge channels
* \param chan
@@ -4743,6 +4942,8 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
char *tmp_data = NULL;
struct ast_flags opts = { 0, };
struct ast_bridge_config bconfig = { { 0, }, };
+ char *opt_args[OPT_ARG_ARRAY_SIZE];
+ struct timeval calldurationlimit = { 0, };
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(dest_chan);
@@ -4757,7 +4958,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
tmp_data = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, tmp_data);
if (!ast_strlen_zero(args.options))
- ast_app_parse_options(bridge_exec_options, &opts, NULL, args.options);
+ ast_app_parse_options(bridge_exec_options, &opts, opt_args, args.options);
/* avoid bridge with ourselves */
if (!strncmp(chan->name, args.dest_chan,
@@ -4837,13 +5038,34 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
}
current_dest_chan = ast_channel_unref(current_dest_chan);
+
+ if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
+ if (ast_bridge_timelimit(chan, &bconfig, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
+ goto done;
+ }
+
+ if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER))
+ ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT);
+ if (ast_test_flag(&opts, OPT_CALLER_TRANSFER))
+ ast_set_flag(&(bconfig.features_caller), AST_FEATURE_REDIRECT);
+ if (ast_test_flag(&opts, OPT_CALLEE_HANGUP))
+ ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag(&opts, OPT_CALLER_HANGUP))
+ ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT);
+ if (ast_test_flag(&opts, OPT_CALLEE_MONITOR))
+ ast_set_flag(&(bconfig.features_callee), AST_FEATURE_AUTOMON);
+ if (ast_test_flag(&opts, OPT_CALLER_MONITOR))
+ ast_set_flag(&(bconfig.features_caller), AST_FEATURE_AUTOMON);
+ if (ast_test_flag(&opts, OPT_CALLEE_PARK))
+ ast_set_flag(&(bconfig.features_callee), AST_FEATURE_PARKCALL);
+ if (ast_test_flag(&opts, OPT_CALLER_PARK))
+ ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL);
- /* do the bridge */
ast_bridge_call(chan, final_dest_chan, &bconfig);
/* the bridge has ended, set BRIDGERESULT to SUCCESS. If the other channel has not been hung up, return it to the PBX */
pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS");
- if (!ast_check_hangup(final_dest_chan)) {
+ if (!ast_check_hangup(final_dest_chan) && !ast_test_flag(&opts, OPT_CALLEE_KILL)) {
ast_debug(1, "starting new PBX in %s,%s,%d for chan %s\n",
final_dest_chan->context, final_dest_chan->exten,
final_dest_chan->priority, final_dest_chan->name);
@@ -4854,9 +5076,19 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
} else
ast_debug(1, "SUCCESS continuing PBX on chan %s\n", final_dest_chan->name);
} else {
- ast_debug(1, "hangup chan %s since the other endpoint has hung up\n", final_dest_chan->name);
+ ast_debug(1, "hangup chan %s since the other endpoint has hung up or the x flag was passed\n", final_dest_chan->name);
ast_hangup(final_dest_chan);
}
+done:
+ if (bconfig.warning_sound) {
+ ast_free((char *)bconfig.warning_sound);
+ }
+ if (bconfig.end_sound) {
+ ast_free((char *)bconfig.end_sound);
+ }
+ if (bconfig.start_sound) {
+ ast_free((char *)bconfig.start_sound);
+ }
return 0;
}