Index: apps/app_dial.c
===================================================================
--- apps/app_dial.c	(revision 179325)
+++ apps/app_dial.c	(working copy)
@@ -149,6 +149,22 @@
 "           * 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"
+"           * LIMIT_RTCC_INTERVAL      Recalculate the timelimit every x ms.\n"
+"           * LIMIT_RTCC_APP           Application to use: AppName(AppArgs).\n"
+"                          Use as argument <bridge_combo> to pass to your app structure\n"
+"                          containing the two channels and the bridge configuration.\n"
+"                          The application should set CALL_LIMIT variable with the\n"
+"                          new total duration of the call limit (in ms) where 0 = no change\n"
+"                          or to change the limit and config->nexteventts accordingly.\n"
+"                          If no application is given, but interval is present,\n"
+"                          a Manager Event will be generated.\n"
+"           * LIMIT_RTCC_DELAY         Delay (in ms) before checking the CALL_LIMIT_DIFF value\n"
+"                          after the application is execututed or the Manager Event is sent.\n"
+"           * LIMIT_RTCC_END           Application to execute on call end: AppName(AppArgs).\n"
+"                          Use as argument <bridge_combo> to pass to your app structure\n"
+"                          containing the two channels and the bridge configuration.\n"
+"                          This is like running h exten, but allows <bridge_combo> as argument\n"
+"                          And is only executed if the call was answered.\n"
 "    m([class]) - Provide hold music to the calling party until a requested\n"
 "           channel answers. A specific MusicOnHold class can be\n"
 "           specified.\n"
@@ -914,6 +930,7 @@
 	const char *var;
 	int play_to_caller = 0, play_to_callee = 0;
 	int delta;
+	long rtcc_delay = 0;
 
 	limit_str = strsep(&stringp, ":");
 	warning_str = strsep(&stringp, ":");
@@ -981,6 +998,22 @@
 	var = pbx_builtin_getvar_helper(chan, "LIMIT_CONNECT_FILE");
 	config->start_sound = !ast_strlen_zero(var) ? ast_strdup(var) : NULL;
 
+	var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_INTERVAL");
+	if (!ast_strlen_zero(var)) {
+		config->rtcc_check_interval = atol(var);
+	}
+
+	var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_DELAY");
+	if (!ast_strlen_zero(var)) {
+		rtcc_delay = atol(var);
+	}
+
+	var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_APP");
+	config->rtcc_app = S_OR(var, NULL);
+
+	var = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_END");
+	config->rtcc_end = S_OR(var, NULL);
+
 	ast_channel_unlock(chan);
 
 	/* undo effect of S(x) in case they are both used */
@@ -988,7 +1021,7 @@
 	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) {
+	if (!config->rtcc_check_interval && !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",
@@ -998,6 +1031,12 @@
 	} else {
 		ast_verb(3, "Limit Data for this call:\n");
 		ast_verb(4, "timelimit      = %ld\n", config->timelimit);
+		if (config->rtcc_check_interval) {
+			ast_verb(4, "RTCC check each   = %ld ms\n", config->rtcc_check_interval);
+			ast_verb(4, "RTCC app    = %s\n", config->rtcc_app ? config->rtcc_app : "Manager Event");
+			ast_verb(4, "RTCC delay  = %ld\n", rtcc_delay);
+			ast_verb(4, "RTCC end    = %s\n", config->rtcc_end ? config->rtcc_end : "");
+		}
 		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");
@@ -1013,6 +1052,190 @@
 	return 0;
 }
 
+static pthread_t rtcc_thread = AST_PTHREADT_NULL;	/* The thread the real-time call control scheduler will be run on. */
+AST_MUTEX_DEFINE_STATIC(rtcc_threadcontrol_lock);
+
+static struct sched_context* rtcc_sched_con;		/* The real-time call control scheduling context. */
+
+static void* ast_rtcc_monitor(void * data)
+{
+	ast_mutex_t rtcc_mutex;
+	ast_cond_t rtcc_cond;
+	struct timespec timeout = {0,0};
+	struct timeval next_sched;
+
+	ast_mutex_init(&rtcc_mutex);
+	ast_cond_init(&rtcc_cond, NULL);
+
+	ast_debug(4, "Call control monitor thread starting.\n");
+
+	for(;;)
+	{
+		int nexteventms = ast_sched_wait(rtcc_sched_con);
+		if(nexteventms == -1)  {
+			break;
+		}
+
+		next_sched = ast_tvadd(ast_tvnow(), ast_samp2tv(nexteventms, 1000));
+		timeout.tv_sec = next_sched.tv_sec;
+		timeout.tv_nsec = next_sched.tv_usec * 1000;
+
+		ast_mutex_lock(&rtcc_mutex);
+		ast_cond_timedwait(&rtcc_cond, &rtcc_mutex, &timeout);
+		ast_mutex_unlock(&rtcc_mutex);
+
+		ast_sched_runq(rtcc_sched_con);
+	}
+
+	ast_debug(4, "Call control monitor thread stopping.\n");
+
+	ast_mutex_destroy(&rtcc_mutex);
+	ast_cond_destroy(&rtcc_cond);
+
+	ast_mutex_lock(&rtcc_threadcontrol_lock);
+	rtcc_thread = AST_PTHREADT_NULL;
+	ast_mutex_unlock(&rtcc_threadcontrol_lock);
+
+	return 0;
+}
+
+/*! \brief Finds and regulary starts the requested application for RealTime Call Control of a bridged call */
+static int ast_rtcc_callback(const void *data)
+{
+	struct ast_rtcc_bridge_combo *rtcc_data = (struct ast_rtcc_bridge_combo *)data;
+	struct ast_app *app;
+	struct timeval last_run = ast_tvnow();
+	long delay_ms = 0;
+	char *s, *app_name = "", *app_data = "";
+	const char *ch0_name, *ch1_name, *ch0_uniqueid, *ch1_uniqueid, *ch0_cid_num, *ch1_cid_num;
+	int res = 0;
+
+	/* Make sure the call does not get hungup while work and rtcc_data destroyed */
+	ast_mutex_lock(&rtcc_threadcontrol_lock);
+
+	if (!rtcc_data->config->rtcc_check_interval || !rtcc_data->config->timelimit || ((rtcc_data->config->timelimit - rtcc_data->config->play_warning) < ast_tvdiff_ms(ast_tvnow(), rtcc_data->config->start_time))) {
+		/* No more schedules after the warning, hangup or if disabled */
+		goto exit;
+	}
+
+	ast_debug(4, "Call control callback started.\n");
+
+	/* Make a copy so we do not need to access them directly */
+	ch0_name = ast_strdup(rtcc_data->ch0->name);
+	ch1_name = ast_strdup(rtcc_data->ch1->name);
+	ch0_uniqueid = ast_strdup(rtcc_data->ch0->uniqueid);
+	ch1_uniqueid = ast_strdup(rtcc_data->ch1->uniqueid);
+	ch0_cid_num = ast_strdup(rtcc_data->ch0->cid.cid_num);
+	ch1_cid_num = ast_strdup(rtcc_data->ch1->cid.cid_num);
+
+	{
+		struct ast_channel *chan = ast_get_channel_by_name_locked(ch0_name);
+		if (!chan)
+		{
+			/* The channel is gone - run away without reschedule */
+			goto exit;
+		}
+		const char *r = pbx_builtin_getvar_helper(chan,"LIMIT_RTCC_DELAY");
+		ast_channel_unlock(chan);
+		if (!ast_strlen_zero(r)) {
+			delay_ms = atol(r);
+		}
+	}
+
+	if (!ast_strlen_zero(rtcc_data->config->rtcc_app)) {
+		s = ast_strdupa(rtcc_data->config->rtcc_app);
+		app_name = strsep(&s,"(");
+		memset(&app_data, 0, sizeof(app_data));
+		if (!ast_strlen_zero(s)) {
+			app_data = ast_strip(s);
+		}
+
+		if (!ast_strlen_zero(app_data) && (s = strrchr(app_data, ')'))) {
+			app_data[strlen(app_data)-strlen(s)] = '\0';
+		}
+	}
+
+	if (!ast_strlen_zero(app_name) && app_data) {
+		app = pbx_findapp(app_name);
+		if (!app) {
+			ast_log(LOG_WARNING, "Could not find application (%s). Timelimit not checked for call (%s)\n", app_name, ch0_uniqueid);
+		}
+		else if (strcmp(app_data, "<bridge_combo>")) {
+			char with_data[2048];
+			memset(with_data, 0, 2048);
+			ast_channel_lock(rtcc_data->ch0);
+			pbx_substitute_variables_helper(rtcc_data->ch0, app_data, with_data, sizeof(with_data) - 1);
+			ast_channel_unlock(rtcc_data->ch0);
+			if (option_verbose > 10) {
+				ast_verb(4, "Substitute %s TO %s\n", app_data, with_data);
+			}
+			ast_mutex_unlock(&rtcc_threadcontrol_lock);
+			res = pbx_exec(rtcc_data->ch0, app, with_data);
+		}
+		else {
+			ast_mutex_unlock(&rtcc_threadcontrol_lock);
+			res = pbx_exec(rtcc_data->ch0, app, rtcc_data);
+		}
+
+		if (res) {
+			ast_log(LOG_ERROR, "Error executing application (%s). Timelimit not checked for call (%s)\n", app_name, ch0_uniqueid);
+			return 0;
+		}
+	} else {
+		manager_event(EVENT_FLAG_CALL, "RTCC_GetTimeLimitChange", 
+		      "Channel1: %s\r\n"
+		      "Channel2: %s\r\n"
+		      "Uniqueid1: %s\r\n"
+		      "Uniqueid2: %s\r\n"
+		      "CallerID1: %s\r\n"
+		      "CallerID2: %s\r\n",
+		      ch0_name, ch1_name, ch0_uniqueid, ch1_uniqueid, ch0_cid_num, ch1_cid_num);
+		ast_mutex_unlock(&rtcc_threadcontrol_lock);
+	}
+
+	if (delay_ms > 0) {
+		usleep(delay_ms*1000);
+	}
+
+	ast_mutex_lock(&rtcc_threadcontrol_lock);
+	{
+		long res_ms;
+		struct ast_channel *chan = ast_get_channel_by_name_locked(ch0_name);
+
+		/* Make sure the call was not hungup while work and wait */
+		if (!chan)
+		{
+			/* The channel is gone - run away without reschedule */
+			goto exit;
+		}
+		const char *r = pbx_builtin_getvar_helper(chan,"CALL_LIMIT");
+		if (r && (res_ms = atoi(r)) && (res_ms -= rtcc_data->config->timelimit)) {
+			if (chan->whentohangup.tv_sec) {
+				chan->whentohangup.tv_sec += res_ms;
+			}
+			rtcc_data->config->timelimit += res_ms;
+			rtcc_data->config->nexteventts.tv_sec += (res_ms / 1000);
+			if (option_verbose > 10) {
+				ast_verb(4, "RTCC executed for call %s. New limit %ld\n", ch0_uniqueid, rtcc_data->config->timelimit);
+			}
+		}
+		else if (option_verbose > 10) {
+			ast_verb(4, "RTCC executed for call %s. No changes. Current limit %ld\n", ch0_uniqueid, rtcc_data->config->timelimit);
+		}
+		res = ast_tvdiff_ms(ast_tvadd(last_run, ast_samp2tv(rtcc_data->config->rtcc_check_interval, 1000)),ast_tvnow());
+		ast_channel_unlock(chan);
+	}
+
+	if ((res && !rtcc_data->config->rtcc_check_interval) || !rtcc_data->config->timelimit || ((rtcc_data->config->timelimit - rtcc_data->config->play_warning) < ast_tvdiff_ms(ast_tvnow(), rtcc_data->config->start_time))) {
+		/* No more schedules after the warning or if disabled */
+		res = 0;
+	}
+
+exit:
+	ast_mutex_unlock(&rtcc_threadcontrol_lock);
+	return res;
+}
+
 static int do_privacy(struct ast_channel *chan, struct ast_channel *peer,
 	struct ast_flags64 *opts, char **opt_args, struct privacy_args *pa)
 {
@@ -1289,6 +1512,9 @@
 	int cause;
 	char numsubst[256];
 	char cidname[AST_MAX_EXTENSION] = "";
+	struct ast_rtcc_bridge_combo *rtcc_data = NULL;
+	pthread_t rtcc_thread = AST_PTHREADT_NULL;
+	int rtcc_sched_id = 0;
 
 	struct ast_bridge_config config = { { 0, } };
 	struct timeval calldurationlimit = { 0, };
@@ -1955,7 +2181,79 @@
 
 				ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
 			}
+
+			if (config.rtcc_check_interval) {
+				rtcc_data = ast_malloc(sizeof(*rtcc_data));
+				rtcc_data->ch0 = chan;
+				rtcc_data->ch1 = peer;
+				rtcc_data->config = &config;
+
+				ast_mutex_lock(&rtcc_threadcontrol_lock);
+				if(!rtcc_sched_con) {
+					rtcc_sched_con = sched_context_create();
+				}
+				rtcc_sched_id = ast_sched_add_variable(rtcc_sched_con, config.rtcc_check_interval, &ast_rtcc_callback, rtcc_data, 1);
+				if(rtcc_thread == AST_PTHREADT_NULL) {
+					if (ast_pthread_create_background(&rtcc_thread, NULL, &ast_rtcc_monitor, NULL) < 0) {
+						ast_log(LOG_ERROR, "Failed to start new limit RTCC thread for application %s on channel %s\n", config.rtcc_app, chan->name);
+					}
+				}
+				ast_mutex_unlock(&rtcc_threadcontrol_lock);
+			}
+
 			res = ast_bridge_call(chan, peer, &config);
+
+			if (config.rtcc_check_interval) {
+				ast_mutex_lock(&rtcc_threadcontrol_lock);
+				if ( rtcc_sched_id && (ast_sched_when(rtcc_sched_con, rtcc_sched_id) != -1) ) {
+					if (ast_sched_del(rtcc_sched_con, rtcc_sched_id)) {
+						ast_debug(1, "RTCC schedule for application %s on channel %s was not found - probably already exited.\n", config.rtcc_app, chan->name);
+					}
+				}
+				ast_mutex_unlock(&rtcc_threadcontrol_lock);
+
+				if (!ast_strlen_zero(config.rtcc_end)) {
+					char *s, *app_name = "", *app_data = "";
+
+					s = ast_strdupa(config.rtcc_end);
+					app_name = strsep(&s,"(");
+					memset(&app_data, 0, sizeof(app_data));
+					if (!ast_strlen_zero(s)) {
+						app_data = ast_strip(s);
+					}
+
+					if (!ast_strlen_zero(app_data) && (s = strrchr(app_data, ')'))) {
+						app_data[strlen(app_data)-strlen(s)] = '\0';
+					}
+					if (!ast_strlen_zero(app_name) && app_data) {
+						struct ast_app *theapp;
+
+						theapp = pbx_findapp(app_name);
+						if (!theapp) {
+							ast_log(LOG_WARNING, "Could not find end application (%s) for call (%s)\n", app_name, chan->uniqueid);
+						}
+						else if (strcmp(app_data, "<bridge_combo>")) {
+							char with_data[2048];
+							memset(with_data, 0, 2048);
+							ast_channel_lock(chan);
+							pbx_substitute_variables_helper(chan, app_data, with_data, sizeof(with_data) - 1);
+							ast_channel_unlock(chan);
+							if (option_verbose > 10) {
+								ast_verb(4, "Substitute %s TO %s\n", app_data, with_data);
+							}
+							res = pbx_exec(chan, theapp, with_data);
+						}
+						else {
+							res = pbx_exec(chan, theapp, rtcc_data);
+						}
+						if (res) {
+							ast_log(LOG_ERROR, "Error executing end application (%s) for call (%s)\n", app_name, chan->uniqueid);
+							res = 0;
+						}
+					}
+				}
+				ast_free(rtcc_data);
+			}
 		}
 
 		strcpy(peer->context, chan->context);
Index: doc/api-1.6.0-changes.odt
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h	(revision 179325)
+++ include/asterisk/channel.h	(working copy)
@@ -581,14 +581,17 @@
 	struct ast_flags features_callee;
 	struct timeval start_time;
 	struct timeval nexteventts;
+	struct timeval feature_start_time;
 	long feature_timer;
 	long timelimit;
 	long play_warning;
 	long warning_freq;
+	long rtcc_check_interval;
+	const char *rtcc_app;
+	const char *rtcc_end;
 	const char *warning_sound;
 	const char *end_sound;
 	const char *start_sound;
-	int firstpass;
 	unsigned int flags;
 	void (* end_bridge_callback)(void *);   /*!< A callback that is called after a bridge attempt */
 	void *end_bridge_callback_data;         /*!< Data passed to the callback */
@@ -598,6 +601,12 @@
 	void (*end_bridge_callback_data_fixup)(struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator);
 };
 
+struct ast_rtcc_bridge_combo {
+	struct ast_channel *ch0;
+	struct ast_channel *ch1;
+	struct ast_bridge_config *config;
+};
+
 struct chanmon;
 
 struct outgoing_helper {
Index: main/channel.c
===================================================================
--- main/channel.c	(revision 179325)
+++ main/channel.c	(working copy)
@@ -4307,7 +4307,7 @@
 
 static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct ast_channel *c1,
 						 struct ast_bridge_config *config, struct ast_frame **fo,
-						 struct ast_channel **rc, struct timeval bridge_end)
+						 struct ast_channel **rc)
 {
 	/* Copy voice back and forth between the two channels. */
 	struct ast_channel *cs[3];
@@ -4349,13 +4349,17 @@
 			res = AST_BRIDGE_RETRY;
 			break;
 		}
-		if (bridge_end.tv_sec) {
-			to = ast_tvdiff_ms(bridge_end, ast_tvnow());
+		if (config->nexteventts.tv_sec) {
+			to = ast_tvdiff_ms(config->nexteventts, ast_tvnow());
 			if (to <= 0) {
-				if (config->timelimit) {
+				if (config->timelimit && !config->feature_timer && !ast_test_flag(config, AST_FEATURE_WARNING_ACTIVE)) {
 					res = AST_BRIDGE_RETRY;
 					/* generic bridge ending to play warning */
 					ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
+				} else if (config->feature_timer) {
+					/* feature timer expired - make sure we do not play warning */
+					ast_clear_flag(config, AST_FEATURE_WARNING_ACTIVE);
+					res = AST_BRIDGE_RETRY;
 				} else {
 					res = AST_BRIDGE_COMPLETE;
 				}
@@ -4484,13 +4488,13 @@
 {
 	manager_event(EVENT_FLAG_CALL, "Bridge",
 			"Bridgestate: %s\r\n"
-		     "Bridgetype: %s\r\n"
-		      "Channel1: %s\r\n"
-		      "Channel2: %s\r\n"
-		      "Uniqueid1: %s\r\n"
-		      "Uniqueid2: %s\r\n"
-		      "CallerID1: %s\r\n"
-		      "CallerID2: %s\r\n",
+			"Bridgetype: %s\r\n"
+			"Channel1: %s\r\n"
+			"Channel2: %s\r\n"
+			"Uniqueid1: %s\r\n"
+			"Uniqueid2: %s\r\n"
+			"CallerID1: %s\r\n"
+			"CallerID2: %s\r\n",
 			onoff ? "Link" : "Unlink",
 			type == 1 ? "core" : "native",
 			c0->name, c1->name, c0->uniqueid, c1->uniqueid, 
@@ -4505,7 +4509,6 @@
 	struct ast_channel *who = NULL;
 	enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
 	int nativefailed=0;
-	int firstpass;
 	int o0nativeformats;
 	int o1nativeformats;
 	long time_left_ms=0;
@@ -4529,21 +4532,17 @@
 		return -1;
 
 	*fo = NULL;
-	firstpass = config->firstpass;
-	config->firstpass = 0;
 
-	if (ast_tvzero(config->start_time))
+	if (ast_tvzero(config->start_time)) {
 		config->start_time = ast_tvnow();
-	time_left_ms = config->timelimit;
-
-	caller_warning = ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING);
-	callee_warning = ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING);
-
-	if (config->start_sound && firstpass) {
-		if (caller_warning)
-			bridge_playfile(c0, c1, config->start_sound, time_left_ms / 1000);
-		if (callee_warning)
-			bridge_playfile(c1, c0, config->start_sound, time_left_ms / 1000);
+		if (config->start_sound) {
+			if (caller_warning) {
+				bridge_playfile(c0, c1, config->start_sound, config->timelimit / 1000);
+			}
+			if (callee_warning) {
+				bridge_playfile(c1, c0, config->start_sound, config->timelimit / 1000);
+			}
+		}
 	}
 
 	/* Keep track of bridge */
@@ -4555,11 +4554,26 @@
 	o1nativeformats = c1->nativeformats;
 
 	if (config->feature_timer) {
-		config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->feature_timer, 1000));
-	} else if (config->timelimit && firstpass) {
+		config->nexteventts = ast_tvadd(config->feature_start_time, ast_samp2tv(config->feature_timer, 1000));
+	} else if (config->timelimit) {
+		time_left_ms = config->timelimit - ast_tvdiff_ms(ast_tvnow(), config->start_time);
+		caller_warning = ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING);
+		callee_warning = ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING);
 		config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
-		if (caller_warning || callee_warning)
-			config->nexteventts = ast_tvsub(config->nexteventts, ast_samp2tv(config->play_warning, 1000));
+		if ((caller_warning || callee_warning) && config->play_warning) {
+			long next_warn = config->play_warning;
+			if (time_left_ms < config->play_warning) {
+				/* At least one warning was played, which means we are returning after feature */
+				long warns_passed = (config->play_warning - time_left_ms) / config->warning_freq;
+				/* It is 'warns_passed * warning_freq' NOT '(warns_passed + 1) * warning_freq',
+					because nexteventts will be updated once again in the 'if (!to)' block */
+				next_warn = config->play_warning - warns_passed * config->warning_freq;
+			}
+			config->nexteventts = ast_tvsub(config->nexteventts, ast_samp2tv(next_warn, 1000));
+		}
+	} else {
+		config->nexteventts.tv_sec = 0;
+		config->nexteventts.tv_usec = 0;
 	}
 
 	if (!c0->tech->send_digit_begin)
@@ -4607,7 +4621,7 @@
 				res = 0;
 				break;
 			}
-			
+
 			if (!to) {
 				if (time_left_ms >= 5000 && config->warning_sound && config->play_warning && ast_test_flag(config, AST_FEATURE_WARNING_ACTIVE)) {
 					int t = (time_left_ms + 500) / 1000; /* round to nearest second */
@@ -4616,10 +4630,12 @@
 					if (callee_warning)
 						bridge_playfile(c1, c0, config->warning_sound, t);
 				}
-				if (config->warning_freq && (time_left_ms > (config->warning_freq + 5000)))
+
+				if (config->warning_freq && (time_left_ms > (config->warning_freq + 5000))) {
 					config->nexteventts = ast_tvadd(config->nexteventts, ast_samp2tv(config->warning_freq, 1000));
-				else
+				} else {
 					config->nexteventts = ast_tvadd(config->start_time, ast_samp2tv(config->timelimit, 1000));
+				}
 			}
 			ast_clear_flag(config, AST_FEATURE_WARNING_ACTIVE);
 		}
@@ -4680,7 +4696,6 @@
 			ast_set_flag(c0, AST_FLAG_NBRIDGE);
 			ast_set_flag(c1, AST_FLAG_NBRIDGE);
 			if ((res = c0->tech->bridge(c0, c1, config->flags, fo, rc, to)) == AST_BRIDGE_COMPLETE) {
-				/* \todo  XXX here should check that cid_num is not NULL */
 				manager_event(EVENT_FLAG_CALL, "Unlink",
 					      "Channel1: %s\r\n"
 					      "Channel2: %s\r\n"
@@ -4688,7 +4703,7 @@
 					      "Uniqueid2: %s\r\n"
 					      "CallerID1: %s\r\n"
 					      "CallerID2: %s\r\n",
-					      c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
+					      c0->name, c1->name, c0->uniqueid, c1->uniqueid, S_OR(c0->cid.cid_num, "<unknown>"), S_OR(c1->cid.cid_num, "<unknown>"));
 				ast_debug(1, "Returning from native bridge, channels: %s, %s\n", c0->name, c1->name);
 
 				ast_clear_flag(c0, AST_FLAG_NBRIDGE);
@@ -4734,7 +4749,7 @@
 		if (!ast_strlen_zero(pbx_builtin_getvar_helper(c1, "BRIDGEPEER")))
 			pbx_builtin_setvar_helper(c1, "BRIDGEPEER", c0->name);
 
-		res = ast_generic_bridge(c0, c1, config, fo, rc, config->nexteventts);
+		res = ast_generic_bridge(c0, c1, config, fo, rc);
 		if (res != AST_BRIDGE_RETRY) {
 			break;
 		} else if (config->feature_timer) {
@@ -4753,7 +4768,6 @@
 	c0->_bridge = NULL;
 	c1->_bridge = NULL;
 
-	/* \todo  XXX here should check that cid_num is not NULL */
 	manager_event(EVENT_FLAG_CALL, "Unlink",
 		      "Channel1: %s\r\n"
 		      "Channel2: %s\r\n"
@@ -4761,7 +4775,7 @@
 		      "Uniqueid2: %s\r\n"
 		      "CallerID1: %s\r\n"
 		      "CallerID2: %s\r\n",
-		      c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
+		      c0->name, c1->name, c0->uniqueid, c1->uniqueid, S_OR(c0->cid.cid_num, "<unknown>"), S_OR(c1->cid.cid_num, "<unknown>"));
 	ast_debug(1, "Bridge stops bridging channels %s and %s\n", c0->name, c1->name);
 
 	return res;
Index: main/features.c
===================================================================
--- main/features.c	(revision 179325)
+++ main/features.c	(working copy)
@@ -2346,7 +2346,6 @@
 	int hadfeatures=0;
 	int autoloopflag;
 	struct ast_option_header *aoh;
-	struct ast_bridge_config backup_config;
 	struct ast_cdr *bridge_cdr = NULL;
 	struct ast_cdr *orig_peer_cdr = NULL;
 	struct ast_cdr *chan_cdr = pick_unlocked_cdr(chan->cdr); /* the proper chan cdr, if there are forked cdrs */
@@ -2354,10 +2353,6 @@
 	struct ast_cdr *new_chan_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
 	struct ast_cdr *new_peer_cdr = NULL; /* the proper chan cdr, if there are forked cdrs */
 
-	memset(&backup_config, 0, sizeof(backup_config));
-
-	config->start_time = ast_tvnow();
-
 	if (chan && peer) {
 		pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
 		pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
@@ -2393,7 +2388,6 @@
 	}
 
 	set_config_flags(chan, peer, config);
-	config->firstpass = 1;
 
 	/* Answer if need be */
 	if (ast_answer(chan))
@@ -2469,8 +2463,8 @@
 		   feature_timer. Not good!
 		*/
 		if (config->feature_timer && (!f || f->frametype == AST_FRAME_DTMF_END)) {
-			/* Update time limit for next pass */
-			diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
+			/* Update feature timer for next pass */
+			diff = ast_tvdiff_ms(ast_tvnow(), config->feature_start_time);
 			if (res == AST_BRIDGE_RETRY) {
 				/* The feature fully timed out but has not been updated. Skip
 				 * the potential round error from the diff calculation and
@@ -2481,18 +2475,7 @@
 			}
 
 			if (hasfeatures) {
-				/* Running on backup config, meaning a feature might be being
-				   activated, but that's no excuse to keep things going 
-				   indefinitely! */
-				if (backup_config.feature_timer && ((backup_config.feature_timer -= diff) <= 0)) {
-					ast_debug(1, "Timed out, realtime this time!\n");
-					config->feature_timer = 0;
-					who = chan;
-					if (f)
-						ast_frfree(f);
-					f = NULL;
-					res = 0;
-				} else if (config->feature_timer <= 0) {
+				if (config->feature_timer <= 0) {
 					/* Not *really* out of time, just out of time for
 					   digits to come in for features. */
 					ast_debug(1, "Timed out for feature!\n");
@@ -2508,9 +2491,8 @@
 						ast_frfree(f);
 					hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
 					if (!hasfeatures) {
-						/* Restore original (possibly time modified) bridge config */
-						memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
-						memset(&backup_config, 0, sizeof(backup_config));
+						/* No more digits expected - reset the timer */
+						config->feature_timer = 0;
 					}
 					hadfeatures = hasfeatures;
 					/* Continue as we were */
@@ -2534,8 +2516,9 @@
 			}
 		}
 		if (res < 0) {
-			if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer))
+			if (!ast_test_flag(chan, AST_FLAG_ZOMBIE) && !ast_test_flag(peer, AST_FLAG_ZOMBIE) && !ast_check_hangup(chan) && !ast_check_hangup(peer)) {
 				ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
+			}
 			goto before_you_go;
 		}
 		
@@ -2583,14 +2566,14 @@
 				featurecode = peer_featurecode;
 			}
 			/*! append the event to featurecode. we rely on the string being zero-filled, and
-			 * not overflowing it. 
+			 * not overflowing it.
 			 * \todo XXX how do we guarantee the latter ?
 			 */
 			featurecode[strlen(featurecode)] = f->subclass;
 			/* Get rid of the frame before we start doing "stuff" with the channels */
 			ast_frfree(f);
 			f = NULL;
-			config->feature_timer = backup_config.feature_timer;
+			config->feature_timer = 0;
 			res = ast_feature_interpret(chan, peer, config, featurecode, sense);
 			switch(res) {
 			case AST_FEATURE_RETURN_PASSDIGITS:
@@ -2602,30 +2585,21 @@
 			}
 			if (res >= AST_FEATURE_RETURN_PASSDIGITS) {
 				res = 0;
-			} else 
+			} else {
 				break;
+			}
 			hasfeatures = !ast_strlen_zero(chan_featurecode) || !ast_strlen_zero(peer_featurecode);
 			if (hadfeatures && !hasfeatures) {
-				/* Restore backup */
-				memcpy(config, &backup_config, sizeof(struct ast_bridge_config));
-				memset(&backup_config, 0, sizeof(struct ast_bridge_config));
+				/* Feature completed or timed out */
+				config->feature_timer = 0;
 			} else if (hasfeatures) {
-				if (!hadfeatures) {
-					/* Backup configuration */
-					memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
-					/* Setup temporary config options */
-					config->play_warning = 0;
-					ast_clear_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING);
-					ast_clear_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
-					config->warning_freq = 0;
-					config->warning_sound = NULL;
-					config->end_sound = NULL;
-					config->start_sound = NULL;
-					config->firstpass = 0;
+				if (config->timelimit) {
+					/* No warning next time - we are waiting for future */
+					ast_set_flag(config, AST_FEATURE_WARNING_ACTIVE);
 				}
-				config->start_time = ast_tvnow();
+				config->feature_start_time = ast_tvnow();
 				config->feature_timer = featuredigittimeout;
-				ast_debug(1, "Set time limit to %ld\n", config->feature_timer);
+				ast_debug(1, "Set feature timer to %ld\n", config->feature_timer);
 			}
 		}
 		if (f)
