aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_dial.c109
-rw-r--r--include/asterisk/features.h5
-rw-r--r--main/features.c242
3 files changed, 243 insertions, 113 deletions
diff --git a/apps/app_dial.c b/apps/app_dial.c
index 89151d2be..2e60df162 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -1282,113 +1282,6 @@ static int valid_priv_reply(struct ast_flags64 *opts, int res)
return 0;
}
-static int do_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, "Dial 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;
-}
-
static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
{
@@ -1741,7 +1634,7 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
}
if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
- if (do_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
+ if (ast_bridge_timelimit(chan, &config, opt_args[OPT_ARG_DURATION_LIMIT], &calldurationlimit))
goto done;
}
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index a660b9e8a..7f1564e3d 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -148,4 +148,9 @@ void ast_unlock_call_features(void);
/*! \brief Reload call features from features.conf */
int ast_features_reload(void);
+/* !\brief parse L option and read associated channel variables to set warning, warning frequency, and timelimit
+ \note caller must be aware of freeing memory for warning_sound, end_sound, and start_sound
+*/
+int ast_bridge_timelimit(struct ast_channel *chan, struct ast_bridge_config *config, char *parse, struct timeval *calldurationlimit);
+
#endif /* _AST_FEATURES_H */
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;
}