From f150b48bc05a2d8f212966763775a04cd727d843 Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Thu, 24 Sep 2009 20:29:51 +0000 Subject: [PATCH] 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 --- apps/app_dial.c | 109 +--------------- include/asterisk/features.h | 5 + main/features.c | 242 +++++++++++++++++++++++++++++++++++- 3 files changed, 243 insertions(+), 113 deletions(-) diff --git a/apps/app_dial.c b/apps/app_dial.c index 89151d2be0..2e60df1622 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 a660b9e8a6..7f1564e3de 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 4af8d1d111..4ec336b02c 100644 --- a/main/features.c +++ b/main/features.c @@ -71,6 +71,69 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + + + + + + + + + + + @@ -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; }