Index: apps/app_dial.c
===================================================================
--- apps/app_dial.c	(revision 327742)
+++ apps/app_dial.c	(working copy)
@@ -228,6 +228,22 @@
 							<para>If specified, <replaceable>filename</replaceable> specifies the sound prompt to play as
 							a warning when time <replaceable>x</replaceable> is reached. If not set, the time remaining will be announced.</para>
 						</variable>
+						<variable name="LIMIT_RTCC_INTERVAL">
+							<value name="interval"/>
+							<para>Recalculate the timelimit every <replaceable>interval</replaceable> ms.</para>
+						</variable>
+						<variable name="LIMIT_RTCC_APP">
+							<value name="appname"/>
+							<para>Application to use: AppName(AppArgs). The application should set CALL_LIMIT_DIFF variable with the change in call limit duartion (in ms where 0 = no change) or to change the limit and config->nexteventts (obtained via RTCC datastore) accordingly. If no application is given, but interval is present, a Manager Event will be generated.</para>
+						</variable>
+						<variable name="LIMIT_RTCC_DELAY">
+							<value name="interval"/>
+							<para>Delay <replaceable>interval</replaceable> ms before checking the CALL_LIMIT_DIFF value after the application is execututed or the Manager Event is sent.</para>
+						</variable>
+						<variable name="LIMIT_RTCC_END">
+							<value name="appname"/>
+							<para>Application to execute on call end: AppName(AppArgs). This is like running h exten, but is only executed if the call was answered and the structure containing the two channels and the bridge configuration can be used (via RTCC datastore), which is otherwise impossible.</para>
+						</variable>
 					</variablelist>
 				</option>
 				<option name="m">
Index: include/asterisk/features.h
===================================================================
--- include/asterisk/features.h	(revision 327742)
+++ include/asterisk/features.h	(working copy)
@@ -116,6 +116,16 @@
 /*! \brief Determine system call pickup extension */
 const char *ast_pickup_ext(void);
 
+/*! \brief Realtime Call Control datastore */
+static const struct ast_datastore_info ast_rtcc_datastore_info;
+
+/*! \brief Realtime Call Control datastore structure */
+struct ast_bridge_combo {
+	struct ast_channel *ch0;
+	struct ast_channel *ch1;
+	struct ast_bridge_config *config;
+};
+
 /*! \brief Bridge a call, optionally allowing redirection */
 int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
 
Index: include/asterisk/channel.h
===================================================================
--- include/asterisk/channel.h	(revision 327742)
+++ include/asterisk/channel.h	(working copy)
@@ -364,7 +364,7 @@
  * PSTN gateway).
  *
  * \todo Implement settings for transliteration between UTF8 Caller ID names in
- *       to ASCII Caller ID's (DAHDI). �sten �sklund might be transliterated into
+ *       to ASCII Caller ID's (DAHDI). �sten �sklund might be transliterated into
  *       Osten Asklund or Oesten Aasklund depending upon language and person...
  *       We need automatic routines for incoming calls and static settings for
  *       our own accounts.
@@ -960,6 +960,9 @@
 	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;
Index: main/features.c
===================================================================
--- main/features.c	(revision 327742)
+++ main/features.c	(working copy)
@@ -161,6 +161,22 @@
 								<para>File to play as warning if <replaceable>y</replaceable> is
 								defined. The default is to say the time remaining.</para>
 							</variable>
+							<variable name="LIMIT_RTCC_INTERVAL">
+								<value name="interval"/>
+								<para>Recalculate the timelimit every <replaceable>interval</replaceable> ms.</para>
+							</variable>
+							<variable name="LIMIT_RTCC_APP">
+								<value name="appname"/>
+								<para>Application to use: AppName(AppArgs). The application should set CALL_LIMIT_DIFF variable with the change in call limit duartion (in ms where 0 = no change) or to change the limit and config->nexteventts (obtained via RTCC datastore) accordingly. If no application is given, but interval is present, a Manager Event will be generated.</para>
+							</variable>
+							<variable name="LIMIT_RTCC_DELAY">
+								<value name="interval"/>
+								<para>Delay <replaceable>interval</replaceable> ms before checking the CALL_LIMIT_DIFF value after the application is execututed or the Manager Event is sent.</para>
+							</variable>
+							<variable name="LIMIT_RTCC_END">
+								<value name="appname"/>
+								<para>Application to execute on bridge end: AppName(AppArgs). This is like running h exten, but is only executed if the call was answered and the structure containing the two channels and the bridge configuration can be used (via RTCC datastore), which is otherwise impossible.</para>
+							</variable>
 						</variablelist>
 					</option>
 					<option name="S(x)">
@@ -5970,6 +5986,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, ":");
@@ -6036,6 +6053,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 */
@@ -6043,7 +6076,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",
@@ -6053,6 +6086,12 @@
 	} else {
 		ast_verb(4, "Limit Data for this call:\n");
 		ast_verb(4, "timelimit      = %ld ms (%.3lf s)\n", config->timelimit, config->timelimit / 1000.0);
+		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 ms (%.3lf s)\n", config->play_warning, config->play_warning / 1000.0);
 		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");
@@ -6068,7 +6107,231 @@
 	return 0;
 }
 
+static pthread_t bridge_sched_thread = AST_PTHREADT_NULL;
 
+AST_MUTEX_DEFINE_STATIC(bridge_sched_thread_lock);
+
+struct ast_sched_context *bridge_sched;
+
+static void* bridge_sched_monitor(void * data)
+{
+	ast_mutex_t bridge_sched_mutex;
+	ast_cond_t bridge_sched_cond;
+	struct timespec timeout = {0,0};
+	struct timeval next_sched;
+
+	ast_mutex_init(&bridge_sched_mutex);
+	ast_cond_init(&bridge_sched_cond, NULL);
+
+	ast_debug(4, "Bridge scheduler thread starting.\n");
+
+	for(;;)
+	{
+		int nexteventms = ast_sched_wait(bridge_sched);
+		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(&bridge_sched_mutex);
+		ast_cond_timedwait(&bridge_sched_cond, &bridge_sched_mutex, &timeout);
+		ast_mutex_unlock(&bridge_sched_mutex);
+
+		ast_sched_runq(bridge_sched);
+	}
+
+	ast_debug(4, "Bridge scheduler thread stopping.\n");
+
+	ast_mutex_lock(&bridge_sched_thread_lock);
+	ast_mutex_destroy(&bridge_sched_mutex);
+	ast_cond_destroy(&bridge_sched_cond);
+	ast_sched_context_destroy(bridge_sched);
+	bridge_sched_thread = AST_PTHREADT_NULL;
+	ast_mutex_unlock(&bridge_sched_thread_lock);
+
+	return 0;
+}
+
+static int bridge_shed_add(int when, ast_sched_cb callback, const void *data, int variable)
+{
+	int sched_id;
+
+	ast_mutex_lock(&bridge_sched_thread_lock);
+
+	if(!bridge_sched) {
+		bridge_sched = ast_sched_context_create();
+	}
+
+	if ((sched_id = ast_sched_add_variable(bridge_sched, when, callback, data, 1)) && sched_id > 0 && bridge_sched_thread == AST_PTHREADT_NULL && (ast_pthread_create_background(&bridge_sched_thread, NULL, &bridge_sched_monitor, NULL) < 0)) {
+		ast_log(LOG_ERROR, "Failed to start bridge scheduler thread\n");
+		ast_sched_context_destroy(bridge_sched);
+		sched_id = -1;
+	}
+
+	ast_mutex_unlock(&bridge_sched_thread_lock);
+
+	return sched_id;
+}
+
+static void bridge_shed_done(int sched_id)
+{
+	if (sched_id) {
+		ast_mutex_lock(&bridge_sched_thread_lock);
+		if ((ast_sched_when(bridge_sched, sched_id) != -1) && ast_sched_del(bridge_sched, sched_id)) {
+				ast_debug(4, "Schedule %d was not found - probably already exited.\n", sched_id);
+		}
+		ast_mutex_unlock(&bridge_sched_thread_lock);
+	}
+}
+
+static void ast_rtcc_datastore_destroy_cb(void *data)
+{
+	ast_free(data);
+}
+
+static const struct ast_datastore_info ast_rtcc_datastore_info = {
+	.type = "RTCC",
+	.destroy = ast_rtcc_datastore_destroy_cb
+};
+
+static int ast_rtcc_callback(const void *data)
+{
+	struct ast_bridge_combo *rtcc_data = (struct ast_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(&bridge_sched_thread_lock);
+
+	if (!rtcc_data || !rtcc_data->config || !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->caller.id.number.str);
+	ch1_cid_num = ast_strdup(rtcc_data->ch1->caller.id.number.str);
+//	ch1_cid_num = ast_strdup(rtcc_data->ch1->connected.id.number.str);
+
+{
+	struct ast_channel *chan = ast_channel_get_by_name(ch0_name);
+	if (!chan)
+	{
+		/* The channel is gone - run away without reschedule */
+		goto exit;
+	}
+	ast_channel_lock(chan);
+	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 {
+			char *with_data;
+			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(&bridge_sched_thread_lock);
+			res = pbx_exec(rtcc_data->ch0, app, with_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(&bridge_sched_thread_lock);
+	}
+
+	if (delay_ms > 0) {
+		usleep(delay_ms*1000);
+	}
+
+	ast_mutex_lock(&bridge_sched_thread_lock);
+	{
+		long res_ms;
+		struct ast_channel *chan = ast_channel_get_by_name(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;
+		}
+		ast_channel_lock(chan);
+		const char *r = pbx_builtin_getvar_helper(chan,"CALL_LIMIT_DIFF");
+		pbx_builtin_setvar_helper(chan, "CALL_LIMIT_DIFF", "0");
+		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(&bridge_sched_thread_lock);
+	return res;
+}
+
 /*!
  * \brief Bridge channels
  * \param chan
@@ -6086,6 +6349,8 @@
 	struct ast_bridge_config bconfig = { { 0, }, };
 	char *opt_args[OPT_ARG_ARRAY_SIZE];
 	struct timeval calldurationlimit = { 0, };
+	int rtcc_sched_id = 0;
+	struct ast_bridge_combo *rtcc_data = NULL;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(dest_chan);
@@ -6205,8 +6470,70 @@
 	if (ast_test_flag(&opts, OPT_CALLER_PARK))
 		ast_set_flag(&(bconfig.features_caller), AST_FEATURE_PARKCALL);
 
+	if (bconfig.rtcc_check_interval) {
+		struct ast_datastore *rtcc_datastore = NULL;
+		rtcc_data = ast_malloc(sizeof(*rtcc_data));
+		rtcc_data->config = &bconfig;
+		rtcc_data->ch0 = chan;
+		rtcc_data->ch1 = final_dest_chan;
+
+		rtcc_datastore = ast_datastore_alloc(&ast_rtcc_datastore_info, NULL);
+		rtcc_datastore->data = rtcc_data;
+		ast_channel_datastore_add(chan, rtcc_datastore);
+
+		rtcc_sched_id = bridge_shed_add(bconfig.rtcc_check_interval, &ast_rtcc_callback, rtcc_data, 1);
+	}
+
 	ast_bridge_call(chan, final_dest_chan, &bconfig);
 
+	if (bconfig.rtcc_check_interval) {
+		struct ast_datastore *rtcc_datastore = NULL;
+
+		bridge_shed_done(rtcc_sched_id);
+		bconfig.rtcc_check_interval = 0;
+
+		if (!ast_strlen_zero(bconfig.rtcc_end)) {
+			char *s, *app_name = "", *app_data = "";
+
+			s = ast_strdupa(bconfig.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 {
+					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);
+					}
+					if (pbx_exec(chan, theapp, with_data)) {
+						ast_log(LOG_ERROR, "Error executing end application (%s) for call (%s)\n", app_name, chan->uniqueid);
+					}
+				}
+			}
+		}
+		rtcc_datastore = ast_channel_datastore_find(chan, &ast_rtcc_datastore_info, NULL);
+		ast_channel_datastore_remove(chan, rtcc_datastore);
+		ast_datastore_free(rtcc_datastore);
+		ast_free(rtcc_data);
+		rtcc_data = NULL;
+	}
+
 	/* 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) && !ast_test_flag(&opts, OPT_CALLEE_KILL)) {
