Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h	(revision 177349)
+++ include/asterisk/channel.h	(working copy)
@@ -581,6 +581,8 @@
 	struct ast_flags features_callee;
 	struct timeval start_time;
 	struct timeval nexteventts;
+	struct timeval feature_start_time;
+	long end_feature_timer;
 	long feature_timer;
 	long timelimit;
 	long play_warning;
Index: main/channel.c
===================================================================
--- main/channel.c	(revision 177349)
+++ main/channel.c	(working copy)
@@ -4333,7 +4333,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];
@@ -4375,8 +4375,8 @@
 			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) {
 					res = AST_BRIDGE_RETRY;
@@ -4530,11 +4530,10 @@
 {
 	struct ast_channel *who = NULL;
 	enum ast_bridge_result res = AST_BRIDGE_COMPLETE;
-	int nativefailed=0;
-	int firstpass;
+	int nativefailed = 0;
 	int o0nativeformats;
 	int o1nativeformats;
-	long time_left_ms=0;
+	long time_left_ms = 0;
 	char caller_warning = 0;
 	char callee_warning = 0;
 
@@ -4555,22 +4554,20 @@
 		return -1;
 
 	*fo = NULL;
-	firstpass = config->firstpass;
-	config->firstpass = 0;
 
-	if (ast_tvzero(config->start_time))
+	/* FIXME this is done in ast_bridge_call. Do we need it here too? */
+	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) {
+	time_left_ms = config->timelimit;
+	if (config->start_sound && config->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);
 	}
+	config->firstpass = 0;
 
 	/* Keep track of bridge */
 	c0->_bridge = c1;
@@ -4581,11 +4578,14 @@
 	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) {
+		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)
+		if ((caller_warning || callee_warning) && config->play_warning) {
 			config->nexteventts = ast_tvsub(config->nexteventts, ast_samp2tv(config->play_warning, 1000));
+		}
 	}
 
 	if (!c0->tech->send_digit_begin)
@@ -4642,10 +4642,11 @@
 					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);
 		}
@@ -4706,7 +4707,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"
@@ -4714,7 +4714,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);
@@ -4760,12 +4760,10 @@
 		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);
-		if (res != AST_BRIDGE_RETRY) {
+		res = ast_generic_bridge(c0, c1, config, fo, rc);
+		if (res != AST_BRIDGE_RETRY || config->feature_timer) {
+			/* no retry or feature timer expired but has not been updated, sending back to ast_bridge_call to do so */
 			break;
-		} else if (config->feature_timer) {
-			/* feature timer expired but has not been updated, sending to ast_bridge_call to do so */
-			break;
 		}
 	}
 
@@ -4779,7 +4777,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"
@@ -4787,7 +4784,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 177349)
+++ main/features.c	(working copy)
@@ -2415,7 +2415,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 */
@@ -2423,10 +2422,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);
@@ -2462,12 +2457,23 @@
 	}
 
 	set_config_flags(chan, peer, config);
-	config->firstpass = 1;
 
 	/* Answer if need be */
-	if (ast_answer(chan))
+	if (ast_answer(chan)) {
 		return -1;
+	}
 
+	/* If it is not the first pass we are starting a feature */
+	if (!config->firstpass) {
+		config->feature_start_time = ast_tvnow();
+	}
+
+	/* Set the call start time if not yet and mark it is the first pass */
+	if (ast_tvzero(config->start_time)) {
+		config->start_time = ast_tvnow();
+		config->firstpass = 1;
+	}
+
 	ast_copy_string(orig_channame,chan->name,sizeof(orig_channame));
 	ast_copy_string(orig_peername,peer->name,sizeof(orig_peername));
 	orig_peer_cdr = peer_cdr;
@@ -2532,7 +2538,7 @@
 
 		if (config->feature_timer) {
 			/* Update time limit for next pass */
-			diff = ast_tvdiff_ms(ast_tvnow(), config->start_time);
+			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
@@ -2542,14 +2548,13 @@
 				config->feature_timer -= diff;
 			}
 
-			config->feature_timer -= diff;
 			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)) {
+				if (config->end_feature_timer && ((config->end_feature_timer -= diff) <= 0)) {
 					ast_debug(1, "Timed out, realtime this time!\n");
-					config->feature_timer = 0;
+					config->feature_timer = config->end_feature_timer = 0;
 					who = chan;
 					if (f)
 						ast_frfree(f);
@@ -2571,9 +2576,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));
+						config->feature_timer = config->end_feature_timer;
+						config->end_feature_timer = 0;
 					}
 					hadfeatures = hasfeatures;
 					/* Continue as we were */
@@ -2653,7 +2657,7 @@
 			/* 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 = config->end_feature_timer;
 			res = ast_feature_interpret(chan, peer, config, featurecode, sense);
 			switch(res) {
 			case AST_FEATURE_RETURN_PASSDIGITS:
@@ -2669,24 +2673,13 @@
 				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));
+				config->feature_timer = config->end_feature_timer;
+				config->end_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;
+					config->end_feature_timer = config->feature_timer;
 				}
-				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);
 			}
