Index: main/features.c
===================================================================
--- main/features.c	(revision 133807)
+++ main/features.c	(working copy)
@@ -3722,19 +3722,78 @@ static char *bridge_synopsis = "Bridge t
 static char *bridge_descrip =
 "Usage: Bridge(channel[,options])\n"
 "	Allows the ability to bridge two channels via the dialplan.\n"
-"The current channel is bridged to the specified 'channel'.\n"
+"The current channel (referred to as the caller) is bridged to the specified 'channel'\n"
+"(who is referred to as the called party below).\n"
 "  Options:\n"
 "    p - Play a courtesy tone to 'channel'.\n"
+"    h    - Allow the called party to hang up by sending the '*' DTMF digit.\n"
+"    H    - Allow the calling party to hang up by hitting the '*' DTMF digit.\n"
+"    k    - Allow the called party to enable parking of the call by sending\n"
+"           the DTMF sequence defined for call parking in features.conf.\n"
+"    K    - Allow the calling party to enable parking of the call by sending\n"
+"           the DTMF sequence defined for call parking in features.conf.\n"
+"    L(x[:y][:z]) - Limit the call to 'x' ms. Play a warning when 'y' ms are\n"
+"           left. Repeat the warning every 'z' ms. The following special\n"
+"           variables can be used with this option:\n"
+"           * LIMIT_PLAYAUDIO_CALLER   yes|no (default yes)\n"
+"                                      Play sounds to the caller.\n"
+"           * LIMIT_PLAYAUDIO_CALLEE   yes|no\n"
+"                                      Play sounds to the callee.\n"
+"           * LIMIT_TIMEOUT_FILE       File to play when time is up.\n"
+"           * LIMIT_CONNECT_FILE       File to play when call begins.\n"
+"           * LIMIT_WARNING_FILE       File to play as warning if 'y' is defined.\n"
+"                                      The default is to say the time remaining.\n"
+"    S(x) - Hang up the call after 'x' seconds *after* the called party has\n"
+"           answered the call.\n"  	
+"    t    - Allow the called party to transfer the calling party by sending the\n"
+"           DTMF sequence defined in features.conf.\n"
+"    T    - Allow the calling party to transfer the called party by sending the\n"
+"           DTMF sequence defined in features.conf.\n"
+"    w    - Allow the called party to enable recording of the call by sending\n"
+"           the DTMF sequence defined for one-touch recording in features.conf.\n"
+"    W    - Allow the calling party to enable recording of the call by sending\n"
+"           the DTMF sequence defined for one-touch recording in features.conf.\n"
+"    x    - Cause the called party to be hung up after the bridge, instead of being\n"
+"           restarted in the dialplan.\n"
 "This application sets the following channel variable upon completion:\n"
 " BRIDGERESULT    The result of the bridge attempt as a text string, one of\n"
 "           SUCCESS | FAILURE | LOOP | NONEXISTENT | INCOMPATIBLE\n";
 
 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 );
 
 /*!
@@ -3752,6 +3811,16 @@ static int bridge_exec(struct ast_channe
 	char *tmp_data  = NULL;
 	struct ast_flags opts = { 0, };
 	struct ast_bridge_config bconfig = { { 0, }, };
+	char *opt_args[OPT_ARG_ARRAY_SIZE];
+	unsigned int calldurationlimit = 0;
+	long timelimit = 0;
+	long play_warning = 0;
+	long warning_freq = 0;
+	const char *warning_sound = NULL;
+	const char *end_sound = NULL;
+	const char *start_sound = NULL;
+	int play_to_caller = 0, play_to_callee = 0;
+
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(dest_chan);
@@ -3843,11 +3912,115 @@ static int bridge_exec(struct ast_channe
 	}
 	
 	/* do the bridge */
+	if (ast_test_flag(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
+		char *limit_str, *warning_str, *warnfreq_str;
+		const char *var;
+
+		warnfreq_str = opt_args[OPT_ARG_DURATION_LIMIT];
+		limit_str = strsep(&warnfreq_str, ":");
+		warning_str = strsep(&warnfreq_str, ":");
+
+		timelimit = atol(limit_str);
+		if (warning_str)
+			play_warning = atol(warning_str);
+		if (warnfreq_str)
+			warning_freq = atol(warnfreq_str);
+
+		if (!timelimit) {
+			ast_log(LOG_WARNING, "Bridge does not accept L(%s), defaulting to 360.\n", limit_str);
+			timelimit = 360;
+		}
+		
+		if (play_warning > timelimit) {
+			/* 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.
+			*/
+
+			if (!warning_freq) {
+				play_warning = 0;
+			} else {
+				/* XXX fix this!! */
+				while (play_warning > timelimit)
+					play_warning -= warning_freq;
+				if (play_warning < 1)
+					play_warning = warning_freq = 0;
+			}
+		}
+
+		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");
+		warning_sound = S_OR(var, "timeleft");
+		
+		var = pbx_builtin_getvar_helper(chan,"LIMIT_TIMEOUT_FILE");
+		end_sound = S_OR(var, NULL);	/* XXX not much of a point in doing this! */
+		
+		var = pbx_builtin_getvar_helper(chan,"LIMIT_CONNECT_FILE");
+		start_sound = S_OR(var, NULL);	/* XXX not much of a point in doing this! */
+
+		/* undo effect of S(x) in case they are both used */
+		calldurationlimit = 0;
+		/* more efficient to do it like S(x) does since no advanced opts */
+		if (!play_warning && !start_sound && !end_sound && timelimit) {
+			calldurationlimit = timelimit / 1000;
+			if (option_verbose > 2)
+				ast_verbose(VERBOSE_PREFIX_3 "Setting call duration limit to %d seconds.\n", calldurationlimit);
+			timelimit = play_to_caller = play_to_callee = play_warning = warning_freq = 0;
+		} else if (option_verbose > 2) {
+			ast_verbose(VERBOSE_PREFIX_3 "Limit Data for this call:\n");
+			ast_verbose(VERBOSE_PREFIX_4 "timelimit      = %ld\n", timelimit);
+			ast_verbose(VERBOSE_PREFIX_4 "play_warning   = %ld\n", play_warning);
+			ast_verbose(VERBOSE_PREFIX_4 "play_to_caller = %s\n", play_to_caller ? "yes" : "no");
+			ast_verbose(VERBOSE_PREFIX_4 "play_to_callee = %s\n", play_to_callee ? "yes" : "no");
+			ast_verbose(VERBOSE_PREFIX_4 "warning_freq   = %ld\n", warning_freq);
+			ast_verbose(VERBOSE_PREFIX_4 "start_sound    = %s\n", start_sound);
+			ast_verbose(VERBOSE_PREFIX_4 "warning_sound  = %s\n", warning_sound);
+			ast_verbose(VERBOSE_PREFIX_4 "end_sound      = %s\n", end_sound);
+		}
+	}
+
+	if (play_to_caller)
+		ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PLAY_WARNING);
+	if (play_to_callee)
+		ast_set_flag(&(bconfig.features_callee), AST_FEATURE_PLAY_WARNING);
+	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);
+
+	bconfig.timelimit = timelimit;
+	bconfig.play_warning = play_warning;
+	bconfig.warning_freq = warning_freq;
+	bconfig.warning_sound = warning_sound;
+	bconfig.end_sound = end_sound;
+	bconfig.start_sound = start_sound;
+
 	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);
@@ -3858,7 +4031,7 @@ static int bridge_exec(struct ast_channe
 		} 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);
 	}
 
