[Asterisk-cvs] asterisk/res res_features.c,1.24,1.25

markster at lists.digium.com markster at lists.digium.com
Mon Jan 3 21:56:31 CST 2005


Update of /usr/cvsroot/asterisk/res
In directory mongoose.digium.com:/tmp/cvs-serv7837/res

Modified Files:
	res_features.c 
Log Message:
Make features configurable and easier to implement


Index: res_features.c
===================================================================
RCS file: /usr/cvsroot/asterisk/res/res_features.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -d -r1.24 -r1.25
--- res_features.c	28 Dec 2004 23:49:46 -0000	1.24
+++ res_features.c	4 Jan 2005 04:01:40 -0000	1.25
@@ -19,6 +19,7 @@
 #include <asterisk/options.h>
 #include <asterisk/module.h>
 #include <asterisk/translate.h>
+#include <asterisk/app.h>
 #include <asterisk/say.h>
 #include <asterisk/channel_pvt.h>
 #include <asterisk/features.h>
@@ -41,6 +42,7 @@
 
 #define DEFAULT_PARK_TIME 45000
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
+#define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
 
 static char *parkedcall = "ParkedCall";
 
@@ -67,6 +69,7 @@
 static int adsipark = 0;
 
 static int transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
+static int featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
 
 /* Default courtesy tone played when party joins conference */
 static char courtesytone[256] = "";
@@ -295,24 +298,258 @@
 	return 0;
 }
 
+
+#define FEATURE_RETURN_HANGUP		-1
+#define FEATURE_RETURN_SUCCESSBREAK	 0
+#define FEATURE_RETURN_PBX_KEEPALIVE	AST_PBX_KEEPALIVE
+#define FEATURE_RETURN_NO_HANGUP_PEER	AST_PBX_NO_HANGUP_PEER
+#define FEATURE_RETURN_PASSDIGITS	 21
+#define FEATURE_RETURN_STOREDIGITS	 22
+#define FEATURE_RETURN_SUCCESS	 	 23
+
+#define FEATURE_SENSE_CHAN	(1 << 0)
+#define FEATURE_SENSE_PEER	(1 << 1)
+#define FEATURE_MAX_LEN		11
+
+static int builtin_disconnect(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
+{
+	if (option_verbose > 3)
+		ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to disconnect call.\n", code);
+	return FEATURE_RETURN_HANGUP;
+}
+
+static int builtin_blindtransfer(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
+{
+	struct ast_channel *transferer;
+	struct ast_channel *transferee;
+	char *transferer_real_context;
+	char newext[256], *ptr;
+	int res;
+	int len;
+
+	ast_log(LOG_NOTICE, "XXX Blind Transfer %s, %s (sense=%d) XXX\n", chan->name, peer->name, sense);
+	if (sense == FEATURE_SENSE_PEER) {
+		transferer = peer;
+		transferee = chan;
+	} else {
+		transferer = chan;
+		transferee = peer;
+	}
+	if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
+	   !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
+		/* Use the non-macro context to transfer the call */
+		if (!ast_strlen_zero(transferer->macrocontext))
+			transferer_real_context = transferer->macrocontext;
+		else
+			transferer_real_context = transferer->context;
+	}
+	/* Start autoservice on chan while we talk
+	   to the originator */
+	ast_autoservice_start(transferee);
+	ast_moh_start(transferee, NULL);
+
+	memset(newext, 0, sizeof(newext));
+	ptr = newext;
+
+	/* Transfer */
+	if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
+		ast_moh_stop(transferee);
+		ast_autoservice_stop(transferee);
+		return res;
+	}
+	if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
+		ast_moh_stop(transferee);
+		ast_autoservice_stop(transferee);
+		return res;
+	}
+	ast_stopstream(transferer);
+	if (res > 0) {
+		/* If they've typed a digit already, handle it */
+		newext[0] = res;
+		ptr++;
+		len--;
+	}
+	res = 0;
+	while (strlen(newext) < sizeof(newext) - 1) {
+		res = ast_waitfordigit(transferer, transferdigittimeout);
+		if (res < 1) 
+			break;
+		if (res == '#')
+			break;
+		*(ptr++) = res;
+		if (!ast_matchmore_extension(transferer, transferer_real_context, newext, 1, transferer->cid.cid_num)) 
+			break;
+	}
+
+	if (res < 0) {
+		ast_moh_stop(transferee);
+		ast_autoservice_stop(transferee);
+		return res;
+	}
+	if (!strcmp(newext, ast_parking_ext())) {
+		ast_moh_stop(transferee);
+
+		if (ast_autoservice_stop(transferee))
+			res = -1;
+		else if (!ast_park_call(transferee, transferer, 0, NULL)) {
+			/* We return non-zero, but tell the PBX not to hang the channel when
+			   the thread dies -- We have to be careful now though.  We are responsible for 
+			   hanging up the channel, else it will never be hung up! */
+
+			if (transferer==peer)
+				res=AST_PBX_KEEPALIVE;
+			else
+				res=AST_PBX_NO_HANGUP_PEER;
+			return res;
+		} else {
+			ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
+		}
+		/* XXX Maybe we should have another message here instead of invalid extension XXX */
+	} else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
+		pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
+		pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
+		ast_moh_stop(transferee);
+		res=ast_autoservice_stop(transferee);
+		if (!transferee->pbx) {
+			/* Doh!  Use our handy async_goto functions */
+			if (option_verbose > 2) 
+				ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
+								,transferee->name, newext, transferer_real_context);
+			if (ast_async_goto(transferee, transferer_real_context, newext, 1))
+				ast_log(LOG_WARNING, "Async goto failed :-(\n");
+			res = -1;
+		} else {
+			/* Set the channel's new extension, since it exists, using transferer context */
+			strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
+			strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
+			transferee->priority = 0;
+		}
+		return res;
+	} else {
+		if (option_verbose > 2)	
+			ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
+	}
+	res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
+	if (res) {
+		ast_moh_stop(transferee);
+		ast_autoservice_stop(transferee);
+		return res;
+	}
+	res = ast_waitstream(transferer, AST_DIGIT_ANY);
+	ast_stopstream(transferer);
+	ast_moh_stop(transferee);
+	res = ast_autoservice_stop(transferee);
+	if (res) {
+		if (option_verbose > 1)
+			ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
+		return res;
+	}
+	return FEATURE_RETURN_SUCCESS;
+}
+
+struct ast_call_feature {
+	int feature_mask;
+	char *fname;
+	char *sname;
+	char exten[FEATURE_MAX_LEN];
+	char default_exten[FEATURE_MAX_LEN];
+	int (*operation)(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense);
+	unsigned int flags;
+};
+
+#define FEATURES_COUNT (sizeof(builtin_features) / sizeof(builtin_features[0]))
+struct ast_call_feature builtin_features[] = 
+{
+	{ AST_FEATURE_REDIRECT, "Blind Transfer", "blindxfer", "#", "#", builtin_blindtransfer, AST_FEATURE_FLAG_NEEDSDTMF },
+	{ AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
+};
+
+static void unmap_features(void)
+{
+	int x;
+	for (x=0;x<FEATURES_COUNT;x++)
+		strcpy(builtin_features[x].exten, builtin_features[x].default_exten);
+}
+
+static int remap_feature(const char *name, const char *value)
+{
+	int x;
+	int res = -1;
+	for (x=0;x<FEATURES_COUNT;x++) {
+		if (!strcasecmp(name, builtin_features[x].sname)) {
+			strncpy(builtin_features[x].exten, value, sizeof(builtin_features[x].exten) - 1);
+			if (option_verbose > 1)
+				ast_verbose(VERBOSE_PREFIX_2 "Remapping feature %s (%s) to sequence '%s'\n", builtin_features[x].fname, builtin_features[x].sname, builtin_features[x].exten);
+			res = 0;
+		} else if (!strcmp(value, builtin_features[x].exten)) 
+			ast_log(LOG_WARNING, "Sequence '%s' already mapped to function %s (%s) while assigning to %s\n", value, builtin_features[x].fname, builtin_features[x].sname, name);
+	}
+	return res;
+}
+
+static int ast_feature_interpret(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
+{
+	int x;
+	unsigned int features;
+	int res = FEATURE_RETURN_PASSDIGITS;
+
+	if (sense == FEATURE_SENSE_CHAN)
+		features = config->features_caller;
+	else
+		features = config->features_callee;
+	ast_log(LOG_DEBUG, "Feature interpret: chan=%s, peer=%s, sense=%d, features=%d\n", chan->name, peer->name, sense, features);
+	for (x=0;x<FEATURES_COUNT;x++) {
+		if ((features & builtin_features[x].feature_mask) &&
+		    !ast_strlen_zero(builtin_features[x].exten)) {
+			/* Feature is up for consideration */
+			if (!strcmp(builtin_features[x].exten, code)) {
+				res = builtin_features[x].operation(chan, peer, config, code, sense);
+				break;
+			} else if (!strncmp(builtin_features[x].exten, code, strlen(code))) {
+				if (res == FEATURE_RETURN_PASSDIGITS)
+					res = FEATURE_RETURN_STOREDIGITS;
+			}
+		}
+	}
+	return res;
+}
+
+static void set_config_flags(struct ast_bridge_config *config)
+{
+	int x;
+	config->flags = 0;
+	for (x=0;x<FEATURES_COUNT;x++) {
+		if (config->features_caller & builtin_features[x].feature_mask) {
+			if (builtin_features[x].flags & AST_FEATURE_FLAG_NEEDSDTMF)
+				ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_0);
+		}
+		if (config->features_callee & builtin_features[x].feature_mask) {
+			if (builtin_features[x].flags & AST_FEATURE_FLAG_NEEDSDTMF)
+				ast_set_flag(config, AST_BRIDGE_DTMF_CHANNEL_1);
+		}
+	}
+}
+
 int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
 {
 	/* Copy voice back and forth between the two channels.  Give the peer
 	   the ability to transfer calls with '#<extension' syntax. */
-	int len;
 	struct ast_frame *f;
 	struct ast_channel *who;
-	char newext[256], *ptr;
+	char chan_featurecode[FEATURE_MAX_LEN + 1]="";
+	char peer_featurecode[FEATURE_MAX_LEN + 1]="";
 	int res;
 	int diff;
+	int hasfeatures=0;
+	int hadfeatures=0;
 	struct ast_option_header *aoh;
-	struct ast_channel *transferer;
-	struct ast_channel *transferee;
 	struct timeval start, end;
-	char *transferer_real_context;
+	struct ast_bridge_config backup_config;
 	int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
 	char *monitor_exec;
 
+	memset(&backup_config, 0, sizeof(backup_config));
+
 	if (chan && peer) {
 		pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
 		pbx_builtin_setvar_helper(peer, "BRIDGEPEER", chan->name);
@@ -330,10 +567,11 @@
 			pbx_exec(peer, monitor_app, monitor_exec, 1);
 	}
 	
-	allowdisconnect_in = config->allowdisconnect_in;
-	allowdisconnect_out = config->allowdisconnect_out;
-	allowredirect_in = config->allowredirect_in;
-	allowredirect_out = config->allowredirect_out;
+	allowdisconnect_in = (config->features_callee & AST_FEATURE_DISCONNECT);
+	allowdisconnect_out = (config->features_caller & AST_FEATURE_DISCONNECT);
+	allowredirect_in = (config->features_callee & AST_FEATURE_REDIRECT);
+	allowredirect_out = (config->features_caller & AST_FEATURE_REDIRECT);
+	set_config_flags(config);
 	config->firstpass = 1;
 
 	/* Answer if need be */
@@ -363,14 +601,52 @@
 			diff = (end.tv_sec - start.tv_sec) * 1000;
 			diff += (end.tv_usec - start.tv_usec) / 1000;
 			config->timelimit -= diff;
-			if (config->timelimit <=0) {
-				/* We ran out of time */
-				config->timelimit = 0;
-				who = chan;
-				if (f)
-					ast_frfree(f);
-				f = NULL;
-				res = 0;
+			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.timelimit && ((backup_config.timelimit -= diff) <= 0)) {
+					ast_log(LOG_DEBUG, "Timed out, realtime this time!\n");
+					config->timelimit = 0;
+					who = chan;
+					if (f)
+						ast_frfree(f);
+					f = NULL;
+					res = 0;
+				} else if (config->timelimit <= 0) {
+					/* Not *really* out of time, just out of time for
+					   digits to come in for features. */
+					ast_log(LOG_DEBUG, "Timed out for feature!\n");
+					if (!ast_strlen_zero(peer_featurecode)) {
+						ast_dtmf_stream(chan, peer, peer_featurecode, 0);
+						memset(peer_featurecode, 0, sizeof(peer_featurecode));
+					}
+					if (!ast_strlen_zero(chan_featurecode)) {
+						ast_dtmf_stream(peer, chan, chan_featurecode, 0);
+						memset(chan_featurecode, 0, sizeof(chan_featurecode));
+					}
+					if (f)
+						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));
+					}
+					hadfeatures = hasfeatures;
+					/* Continue as we were */
+					continue;
+				}
+			} else {
+				if (config->timelimit <=0) {
+					/* We ran out of time */
+					config->timelimit = 0;
+					who = chan;
+					if (f)
+						ast_frfree(f);
+					f = NULL;
+					res = 0;
+				}
 			}
 		}
 		if (res < 0) {
@@ -412,150 +688,62 @@
 			}
 		}
 		/* check for '*', if we find it it's time to disconnect */
-		if (f && (f->frametype == AST_FRAME_DTMF) &&
-			(((who == chan) && allowdisconnect_out) || ((who == peer) && allowdisconnect_in)) &&
-			(f->subclass == '*')) {
-			
-			if (option_verbose > 3)
-				ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
-			res = -1;
-			break;
-		}
-
-		if ((f->frametype == AST_FRAME_DTMF) &&
-			((allowredirect_in && who == peer) || (allowredirect_out && who == chan)) &&
-			(f->subclass == '#')) {
-				if (allowredirect_in &&  who == peer) {
-					transferer = peer;
-					transferee = chan;
-				} else {
-					transferer = chan;
-					transferee = peer;
-				}
-				if (!(transferer_real_context=pbx_builtin_getvar_helper(transferee, "TRANSFER_CONTEXT")) &&
-				   !(transferer_real_context=pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT"))) {
-					/* Use the non-macro context to transfer the call */
-					if (!ast_strlen_zero(transferer->macrocontext))
-						transferer_real_context = transferer->macrocontext;
-					else
-						transferer_real_context = transferer->context;
-				}
-				/* Start autoservice on chan while we talk
-				   to the originator */
-				ast_autoservice_start(transferee);
-				ast_moh_start(transferee, NULL);
-
-				memset(newext, 0, sizeof(newext));
-				ptr = newext;
-
-					/* Transfer */
-				if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
-					ast_moh_stop(transferee);
-					ast_autoservice_stop(transferee);
-					break;
-				}
-				if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
-					ast_moh_stop(transferee);
-					ast_autoservice_stop(transferee);
-					break;
-				}
-				ast_stopstream(transferer);
-				if (res > 0) {
-					/* If they've typed a digit already, handle it */
-					newext[0] = res;
-					ptr++;
-					len --;
-				}
+		if (f && (f->frametype == AST_FRAME_DTMF)) {
+			char *featurecode;
+			int sense;
+			struct ast_channel *other;
+			hadfeatures = hasfeatures;
+			/* This cannot overrun because the longest feature is one shorter than our buffer */
+			if (who == chan) {
+				other = peer;
+				sense = FEATURE_SENSE_CHAN;
+				featurecode = chan_featurecode;
+			} else  {
+				other = chan;
+				sense = FEATURE_SENSE_PEER;
+				featurecode = peer_featurecode;
+			}
+			featurecode[strlen(featurecode)] = f->subclass;
+			res = ast_feature_interpret(chan, peer, config, featurecode, sense);
+			switch(res) {
+			case FEATURE_RETURN_PASSDIGITS:
+				ast_dtmf_stream(other, who, featurecode, 0);
+				/* Fall through */
+			case FEATURE_RETURN_SUCCESS:
+				memset(featurecode, 0, sizeof(chan_featurecode));
+				break;
+			}
+			if (res >= FEATURE_RETURN_PASSDIGITS) {
 				res = 0;
-				while (strlen(newext) < sizeof(newext) - 1) {
-					res = ast_waitfordigit(transferer, transferdigittimeout);
-					if (res < 1) 
-						break;
-					if (res == '#')
-						break;
-					*(ptr++) = res;
-					if (!ast_matchmore_extension(transferer, transferer_real_context
-								, newext, 1, transferer->cid.cid_num)) {
-						break;
-					}
-				}
-
-				if (res < 0) {
-					ast_moh_stop(transferee);
-					ast_autoservice_stop(transferee);
-					break;
-				}
-				if (!strcmp(newext, ast_parking_ext())) {
-					ast_moh_stop(transferee);
-
-					if (ast_autoservice_stop(transferee))
-						res = -1;
-					else if (!ast_park_call(transferee, transferer, 0, NULL)) {
-						/* We return non-zero, but tell the PBX not to hang the channel when
-						   the thread dies -- We have to be careful now though.  We are responsible for 
-						   hanging up the channel, else it will never be hung up! */
-
-						if (transferer==peer)
-							res=AST_PBX_KEEPALIVE;
-						else
-							res=AST_PBX_NO_HANGUP_PEER;
-						break;
-					} else {
-						ast_log(LOG_WARNING, "Unable to park call %s\n", transferee->name);
-					}
-					/* XXX Maybe we should have another message here instead of invalid extension XXX */
-				} else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->cid.cid_num)) {
-					pbx_builtin_setvar_helper(peer, "BLINDTRANSFER", chan->name);
-					pbx_builtin_setvar_helper(chan, "BLINDTRANSFER", peer->name);
-					ast_moh_stop(transferee);
-					res=ast_autoservice_stop(transferee);
-					if (!transferee->pbx) {
-						/* Doh!  Use our handy async_goto functions */
-						if (option_verbose > 2) 
-							ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n"
-								,transferee->name, newext, transferer_real_context);
-						if (ast_async_goto(transferee, transferer_real_context, newext, 1))
-							ast_log(LOG_WARNING, "Async goto failed :-(\n");
-						res = -1;
-					} else {
-						/* Set the channel's new extension, since it exists, using transferer context */
-						strncpy(transferee->exten, newext, sizeof(transferee->exten)-1);
-						strncpy(transferee->context, transferer_real_context, sizeof(transferee->context)-1);
-						transferee->priority = 0;
-						ast_frfree(f);
-					}
-					break;
-				} else {
-					if (option_verbose > 2)	
-						ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
-				}
-				res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
-				if (res) {
-					ast_moh_stop(transferee);
-					ast_autoservice_stop(transferee);
-					break;
-				}
-				res = ast_waitstream(transferer, AST_DIGIT_ANY);
-				ast_stopstream(transferer);
-				ast_moh_stop(transferee);
-				res = ast_autoservice_stop(transferee);
-				if (res) {
-					if (option_verbose > 1)
-						ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
-				}
 			} else {
-            			if (f && (f->frametype == AST_FRAME_DTMF)) {
-                  			if (who == peer)
-                        			ast_write(chan, f);
-                  			else
-                        			ast_write(peer, f);
-            			}            
-#if 1
-				ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass);
-#endif
+				ast_frfree(f);
+				break;
 			}
-         if (f)
-               ast_frfree(f);
+			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));
+			} else if (hasfeatures) {
+				if (!hadfeatures) {
+					/* Backup configuration */
+					memcpy(&backup_config, config, sizeof(struct ast_bridge_config));
+					/* Setup temporary config options */
+					config->play_warning = 0;
+					config->features_caller &= ~(AST_FEATURE_PLAY_WARNING);
+					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->timelimit = featuredigittimeout;
+				ast_log(LOG_DEBUG, "Set time limit to %ld\n", config->timelimit);
+			}
+		}
+		if (f)
+			ast_frfree(f);
 	}
 	return res;
 }
@@ -813,10 +1001,8 @@
 			ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
 
 		memset(&config,0,sizeof(struct ast_bridge_config));
-		config.allowredirect_in = 1;
-		config.allowredirect_out = 1;
-		config.allowdisconnect_out = 0;
-		config.allowdisconnect_in = 0;
+		config.features_callee |= AST_FEATURE_REDIRECT;
+		config.features_caller |= AST_FEATURE_REDIRECT;
 		config.timelimit = 0;
 		config.play_warning = 0;
 		config.warning_freq = 0;
@@ -928,6 +1114,9 @@
 	struct ast_config *cfg;
 	struct ast_variable *var;
 
+	transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
+	featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
+
 	ast_cli_register(&showparked);
 
 	cfg = ast_load("features.conf");
@@ -964,6 +1153,11 @@
 					transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
 				} else
 					transferdigittimeout = transferdigittimeout * 1000;
+			} else if (!strcasecmp(var->name, "featuredigittimeout")) {
+				if ((sscanf(var->value, "%d", &featuredigittimeout) != 1) || (transferdigittimeout < 1)) {
+					ast_log(LOG_WARNING, "%s is not a valid featuredigittimeout\n", var->value);
+					featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
+				}
 			} else if (!strcasecmp(var->name, "courtesytone")) {
 				strncpy(courtesytone, var->value, sizeof(courtesytone) - 1);
 			} else if (!strcasecmp(var->name, "pickupexten")) {
@@ -971,6 +1165,13 @@
 			}
 			var = var->next;
 		}
+		unmap_features();
+		var = ast_variable_browse(cfg, "featuremap");
+		while(var) {
+			if (remap_feature(var->name, var->value))
+				ast_log(LOG_NOTICE, "Unknown feature '%s'\n", var->name);
+			var = var->next;
+		}
 		ast_destroy(cfg);
 	}
 	con = ast_context_find(parking_con);




More information about the svn-commits mailing list