? atxfer_1204.diff
? atxfer_fixes_gryn1.diff.txt
? atxfer_with_timer.diff
? chanspy.diff
? lame
? m.c
? undo.diff
? utils
? apps/app_chanspy.c
? astman/.depend
? astman/astman
Index: app.c
===================================================================
RCS file: /usr/cvsroot/asterisk/app.c,v
retrieving revision 1.35
diff -u -r1.35 app.c
--- app.c	7 Dec 2004 20:38:43 -0000	1.35
+++ app.c	13 Dec 2004 16:24:17 -0000
@@ -27,6 +27,7 @@
 #include <asterisk/app.h>
 #include <asterisk/dsp.h>
 #include <asterisk/logger.h>
+#include <asterisk/indications.h>
 #include <asterisk/options.h>
 #include <asterisk/utils.h>
 #include <asterisk/lock.h>
@@ -35,6 +36,74 @@
 
 #define MAX_OTHER_FORMATS 10
 
+
+int ast_dtget(struct ast_channel *chan, char *collect, size_t size, int total, int to) {
+	struct tone_zone_sound *ts;
+	struct ast_frame *f;
+	time_t start,now;
+	int x=0;
+	int res=0;
+	int on=0;
+	int myto=0;
+
+	time(&start);
+	ast_set_write_format(chan, ast_best_codec(chan->nativeformats));
+    ast_set_read_format(chan, ast_best_codec(chan->nativeformats));
+	
+	ts = ast_get_indication_tone(chan->zone,"dial");
+    if (ts && ts->data[0]) {
+        res = ast_playtones_start(chan, 0, ts->data, 0);
+		on=1;
+	}
+	else 
+		ast_log(LOG_NOTICE,"Huh....? no dial?\n");
+
+	memset(collect,0,size);
+	for(x=0;x<total&&x<size;) {
+		time(&now);
+		myto = ast_strlen_zero(collect) ? 20 : to;
+		if((now - start) > myto) {
+			ast_playtones_stop(chan);
+			on=0;
+			break;
+		}
+		if(ast_waitfor(chan, 100) > -1) {
+			f = ast_read(chan);
+			if (f) {
+				if (f->frametype == AST_FRAME_DTMF) {
+					if(on) {
+						ast_playtones_stop(chan);
+						on=0;
+					}
+					if(f->subclass == '#') {
+						ast_playtones_stop(chan);
+                        on=0;
+						break;
+					}
+					else if(f->subclass == '*') {
+						memset(collect,0,size);
+						x=0;
+						res = ast_playtones_start(chan, 0, ts->data, 0);
+						on=1;
+					}
+					else {
+						collect[x++] = f->subclass;
+						time(&start);
+					}
+				}
+				ast_frfree(f);
+			}
+			else 
+				break;
+		}
+		else 
+			break;
+	}
+	
+	collect[x++] = '\0';
+	return x;
+}
+
 /* set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
    "ludicrous time" (essentially never times out) */
 int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout)
@@ -1069,4 +1138,17 @@
 	regfree(&regexbuf);
 
 	return count;
+}
+
+int ast_seperate_app_args(char *buf, char delim, char **array, int arraylen)
+{
+    int x = 0;
+    memset(array, 0, arraylen * sizeof(char));
+    for (array[x] = buf ; x < arraylen && array[x]; x++) {
+        if ((array[x+1] = strchr(array[x], delim))) {
+            *array[x+1] = '\0';
+            array[x+1]++;
+        }
+    }
+	return x;
 }
Index: channel.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channel.c,v
retrieving revision 1.150
diff -u -r1.150 channel.c
--- channel.c	9 Dec 2004 19:55:01 -0000	1.150
+++ channel.c	13 Dec 2004 16:24:19 -0000
@@ -2559,7 +2559,7 @@
 	struct timeval start_time,precise_now;
 	long elapsed_ms=0, time_left_ms=0;
 	int playit=0, playitagain=1, first_time=1;
-
+	int dindex = 0;
 	flags = (config->allowdisconnect_out||config->allowredirect_out ? AST_BRIDGE_DTMF_CHANNEL_0 : 0) + (config->allowdisconnect_in||config->allowredirect_in ? AST_BRIDGE_DTMF_CHANNEL_1 : 0);
 
 	*fo = NULL;
@@ -2605,8 +2605,8 @@
 	o0nativeformats = c0->nativeformats;
 	for (/* ever */;;) {
 		/* timestamp */
+		gettimeofday(&precise_now,NULL);
 		if (config->timelimit) {
-			gettimeofday(&precise_now,NULL);
 			elapsed_ms = tvdiff(&precise_now,&start_time);
 			time_left_ms = config->timelimit - elapsed_ms;
 
@@ -2653,7 +2653,7 @@
 			break;
 		}
 		if (c0->pvt->bridge && config->timelimit==0 &&
-			(c0->pvt->bridge == c1->pvt->bridge) && !nativefailed && !c0->monitor && !c1->monitor) {
+			(c0->pvt->bridge == c1->pvt->bridge) && !nativefailed && !c0->monitor && !c1->monitor && !config->block_native) {
 				/* Looks like they share a bridge code */
 			if (option_verbose > 2) 
 				ast_verbose(VERBOSE_PREFIX_3 "Attempting native bridge of %s and %s\n", c0->name, c1->name);
@@ -2712,13 +2712,32 @@
 			ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
 			break;
 		}
+
+		/* 
+		   perform DTMF timing operation when needed.  If this leg of the call has a dtmf timeout return back to 
+		   the caller (probably ast_bridge_call in res_fratures) so it can handle the event.  
+		   We indicate a time's up by setting the dtmf_time_elapsed to -1.
+		*/
+		dindex = who == c0 ? 0 : 1;
+		if(config->dtmf_time_elapsed[dindex] > -1 && (config->dtmf_timer[dindex].tv_sec || config->dtmf_timer[dindex].tv_usec)) {
+			config->dtmf_time_elapsed[dindex] = tvdiff(&precise_now,&config->dtmf_timer[dindex]);
+			if(config->dtmf_time_elapsed[dindex] >= config->dtmf_timeout) {
+				config->dtmf_time_elapsed[dindex] = -1;
+				*rc = who;
+				*fo = f;
+				c0->_bridge = NULL;
+				c1->_bridge = NULL;
+				return 0;
+			}
+		}
+
 		if ((f->frametype == AST_FRAME_VOICE) ||
 			(f->frametype == AST_FRAME_TEXT) ||
 			(f->frametype == AST_FRAME_VIDEO) || 
 			(f->frametype == AST_FRAME_IMAGE) ||
 			(f->frametype == AST_FRAME_DTMF)) {
-			if ((f->frametype == AST_FRAME_DTMF) && 
-				(flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))) {
+			if(((f->frametype == AST_FRAME_DTMF) && 
+				(flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)))) {
 				if ((who == c0)) {
 					if  ((flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
 						*rc = c0;
Index: apps/app_dial.c
===================================================================
RCS file: /usr/cvsroot/asterisk/apps/app_dial.c,v
retrieving revision 1.112
diff -u -r1.112 app_dial.c
--- apps/app_dial.c	12 Dec 2004 04:58:07 -0000	1.112
+++ apps/app_dial.c	13 Dec 2004 16:24:20 -0000
@@ -60,8 +60,11 @@
 "  This application returns -1 if the originating channel hangs up, or if the\n"
 "call is bridged and either of the parties in the bridge terminate the call.\n"
 "The option string may contain zero or more of the following characters:\n"
-"      't' -- allow the called user transfer the calling user by hitting #.\n"
+"      'd' -- Disable DTMF from being passed between the 2 channels.\n"
+"      't' -- allow the called user to transfer the calling user by hitting #.\n"
 "      'T' -- allow the calling user to transfer the call by hitting #.\n"
+"      'x' -- allow the called user to attended transfer the calling user by hitting #.\n"
+"      'X' -- allow the calling user to attended transfer the call by hitting #.\n"
 "      'f' -- Forces callerid to be set as the extension of the line \n"
 "             making/redirecting the outgoing call. For example, some PSTNs\n"
 "             don't allow callerids from other extensions then the ones\n"
@@ -84,6 +87,7 @@
 "      'S(x)' -- hangup the call after x seconds AFTER called party picked up\n"  	
 "      'D([digits])'  -- Send DTMF digit string *after* called party has answered\n"
 "             but before the bridge. (w=500ms sec pause)\n"
+"      'K(<transfer>:<hangup>)' -- Set Keybindings for transfer and hangup. e.g. K(555:123)\n"
 "      'L(x[:y][:z])' -- Limit the call to 'x' ms warning when 'y' ms are left\n"
 "             repeated every 'z' ms) Only 'x' is required, 'y' and 'z' are optional.\n"
 "             The following special variables are optional:\n"
@@ -504,6 +508,9 @@
 	char limitdata[256];
 	char *sdtmfptr;
 	char sdtmfdata[256] = "";
+	char *xferptr=NULL;
+	char *hangupptr=NULL;
+	char keybinddata[256] = "";
 	char *stack,*var;
 	char *mac = NULL, *macroname = NULL;
 	char status[256]="";
@@ -514,7 +521,7 @@
 	int vartype;
 	char *outbound_group = NULL;
 	char *macro_result = NULL, *macro_transfer_dest = NULL;
-	int digit = 0;
+	int digit = 0, mutedtmf = 0;
 	time_t start_time, answer_time, end_time;
 	struct ast_app *app = NULL;
 
@@ -586,6 +593,27 @@
 			else 
 				ast_log(LOG_WARNING, "D( Data lacking trailing ')'\n");
 		}
+
+		/* Key Binding */
+		if ((xferptr = strstr(transfer, "K("))) {
+			strncpy(keybinddata, xferptr + 2, sizeof(keybinddata) - 1);
+			/* Overwrite with X's what was the keybind info */
+			while (*xferptr && (*xferptr != ')')) 
+				*(xferptr++) = 'X';
+			if (*xferptr)
+				*xferptr = 'X';
+			/* Now find the end  */
+			xferptr = strchr(keybinddata, ')');
+			if (xferptr)
+				*xferptr = '\0';
+			else 
+				ast_log(LOG_WARNING, "K( Data lacking trailing ')'\n");
+			xferptr = keybinddata;
+			if((hangupptr = strchr(xferptr,':'))) {
+				*hangupptr = '\0';
+				hangupptr++;
+			}
+		}
 		
 		/* XXX LIMIT SUPPORT */
 		if ((limitptr = strstr(transfer, "L("))) {
@@ -765,20 +793,27 @@
 		memset(tmp, 0, sizeof(struct localuser));
 		if (transfer) {
 			if (strchr(transfer, 't'))
-				tmp->allowredirect_in = 1;
-                        else    tmp->allowredirect_in = 0;
+				tmp->allowredirect_in = AST_REGULAR_TRANSFER;
+			else if (strchr(transfer, 'x'))
+				tmp->allowredirect_in = AST_ATTENDED_TRANSFER;
+			else    tmp->allowredirect_in = 0;
 			if (strchr(transfer, 'T'))
-				tmp->allowredirect_out = 1;
-                        else    tmp->allowredirect_out = 0;
+				tmp->allowredirect_out = AST_REGULAR_TRANSFER;
+			else if (strchr(transfer, 'X'))
+				tmp->allowredirect_out = AST_ATTENDED_TRANSFER;
+			else    tmp->allowredirect_out = 0;
 			if (strchr(transfer, 'r'))
 				tmp->ringbackonly = 1;
-                        else    tmp->ringbackonly = 0;
+			else    tmp->ringbackonly = 0;
+			if (strchr(transfer, 'd'))
+				mutedtmf = 1;
+			else    mutedtmf = 0;
 			if (strchr(transfer, 'm'))
 				tmp->musiconhold = 1;
-                        else    tmp->musiconhold = 0;
+			else    tmp->musiconhold = 0;
 			if (strchr(transfer, 'H'))
 				allowdisconnect_out = tmp->allowdisconnect_out = 1;
-                        else    allowdisconnect_out = tmp->allowdisconnect_out = 0;
+			else    allowdisconnect_out = tmp->allowdisconnect_out = 0;
 			if(strchr(transfer, 'h'))
 				allowdisconnect_in = tmp->allowdisconnect_in = 1;
 			else	allowdisconnect_in = tmp->allowdisconnect_in = 0;
@@ -1122,6 +1157,11 @@
 			config.warning_sound = warning_sound;
 			config.end_sound = end_sound;
 			config.start_sound = start_sound;
+			config.mutedtmf = mutedtmf;
+			if(xferptr) 
+				strncpy(config.xferstr,xferptr,sizeof(config.xferstr));
+			if(hangupptr)
+				strncpy(config.hangupstr,hangupptr,sizeof(config.hangupstr));
 			if (moh) {
 				moh = 0;
 				ast_moh_stop(chan);
Index: include/asterisk/app.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/app.h,v
retrieving revision 1.17
diff -u -r1.17 app.h
--- include/asterisk/app.h	3 Oct 2004 21:18:27 -0000	1.17
+++ include/asterisk/app.h	13 Dec 2004 16:24:20 -0000
@@ -31,6 +31,10 @@
  *  is pressed during playback, it will immediately break out of the message and continue
  *  execution of your code.
  */
+
+/* present a dialtone and collect digits (DISA jr) */
+int ast_dtget(struct ast_channel *chan, char *collect, size_t size, int total, int to);
+
 extern int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout);
 
 /* Full version with audiofd and controlfd.  NOTE: returns '2' on ctrlfd available, not '1' like other full functions */
@@ -81,6 +85,9 @@
 
 //! Get the current channel count of all groups that match the specified pattern and category.
 int ast_app_group_match_get_count(char *groupmatch, char *category);
+
+//! Parse a writable buffer seperated by a delimeter into an array of pointers to strings
+int ast_seperate_app_args(char *buf, char delim, char **array, int arraylen);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }
Index: include/asterisk/channel.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/channel.h,v
retrieving revision 1.62
diff -u -r1.62 channel.h
--- include/asterisk/channel.h	7 Dec 2004 20:38:43 -0000	1.62
+++ include/asterisk/channel.h	13 Dec 2004 16:24:21 -0000
@@ -275,6 +275,9 @@
 	int allowredirect_out;
 	int allowdisconnect_in;
 	int allowdisconnect_out;
+	char hangupstr[10];
+	char xferstr[10];
+	int mutedtmf;
 	long timelimit;
 	long play_warning;
 	long warning_freq;
@@ -282,6 +285,10 @@
 	char *end_sound;
 	char *start_sound;
 	int firstpass;
+	int block_native;
+	struct timeval dtmf_timer[2];
+	int dtmf_time_elapsed[2];
+	int dtmf_timeout;
 };
 
 struct chanmon;
Index: include/asterisk/features.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/features.h,v
retrieving revision 1.3
diff -u -r1.3 features.h
--- include/asterisk/features.h	17 Jul 2004 23:56:12 -0000	1.3
+++ include/asterisk/features.h	13 Dec 2004 16:24:21 -0000
@@ -16,6 +16,8 @@
 
 #ifndef _AST_FEATURES_H
 #define _AST_FEATURES_H
+#define AST_REGULAR_TRANSFER 1
+#define AST_ATTENDED_TRANSFER 2
 
 //! Park a call and read back parked location
 /*! \param chan the channel to actually be parked
Index: res/res_features.c
===================================================================
RCS file: /usr/cvsroot/asterisk/res/res_features.c,v
retrieving revision 1.23
diff -u -r1.23 res_features.c
--- res/res_features.c	9 Dec 2004 22:39:14 -0000	1.23
+++ res/res_features.c	13 Dec 2004 16:24:22 -0000
@@ -20,6 +20,7 @@
 #include <asterisk/module.h>
 #include <asterisk/translate.h>
 #include <asterisk/say.h>
+#include <asterisk/app.h>
 #include <asterisk/channel_pvt.h>
 #include <asterisk/features.h>
 #include <asterisk/musiconhold.h>
@@ -41,7 +42,6 @@
 
 #define DEFAULT_PARK_TIME 45000
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
-
 static char *parkedcall = "ParkedCall";
 
 /* No more than 45 seconds parked before you do something with them */
@@ -97,6 +97,9 @@
 static struct ast_app *monitor_app=NULL;
 static int monitor_ok=1;
 
+static char global_xferstr[AST_MAX_EXTENSION] = "#";
+static char global_hangupstr[AST_MAX_EXTENSION] = "*";
+
 struct parkeduser {
 	struct ast_channel *chan;
 	struct timeval start;
@@ -296,16 +299,61 @@
 	return 0;
 }
 
-int ast_bridge_call(struct ast_channel *chan,struct ast_channel *peer,struct ast_bridge_config *config)
+
+struct ast_bridge_thread_obj {
+	struct ast_bridge_config bconfig;
+	struct ast_channel *chan;
+	struct ast_channel *peer;
+};
+
+static void *ast_bridge_call_thread(void *data) {
+	struct ast_bridge_thread_obj *tobj = data;
+	tobj->chan->appl = "Transferred Call";
+	tobj->chan->data = tobj->peer->name;
+	tobj->peer->appl = "Transferred Call";
+	tobj->peer->data = tobj->chan->name;
+	if (tobj->chan->cdr) {
+		ast_cdr_reset(tobj->chan->cdr,0);
+		ast_cdr_setdestchan(tobj->chan->cdr, tobj->peer->name);
+	}
+	if (tobj->peer->cdr) {
+		ast_cdr_reset(tobj->peer->cdr,0);
+		ast_cdr_setdestchan(tobj->peer->cdr, tobj->chan->name);
+	}
+
+	ast_bridge_call(tobj->peer, tobj->chan, &tobj->bconfig);
+	ast_hangup(tobj->chan);
+	ast_hangup(tobj->peer);
+	tobj->chan = tobj->peer = NULL;
+	free(tobj);
+	tobj=NULL;
+	return NULL;
+}
+
+static void ast_bridge_call_thread_launch(void *data) {
+	pthread_t thread;
+	pthread_attr_t attr;
+	int result;
+	result = pthread_attr_init(&attr);
+	pthread_attr_setschedpolicy(&attr, SCHED_RR);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	result = ast_pthread_create(&thread, &attr,ast_bridge_call_thread, data);
+	result = pthread_attr_destroy(&attr);
+}
+
+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;
+	struct ast_channel *who,*newchan, *xferchan=NULL;
+	struct ast_bridge_thread_obj *tobj;
 	char newext[256], *ptr;
 	int res;
-	int diff;
+	struct ast_bridge_config bconfig;
+	int diff,outstate=0;
+	char xferto[256],dialstr[256];
 	struct ast_option_header *aoh;
 	struct ast_channel *transferer;
 	struct ast_channel *transferee;
@@ -313,6 +361,25 @@
 	char *transferer_real_context;
 	int allowdisconnect_in,allowdisconnect_out,allowredirect_in,allowredirect_out;
 	char *monitor_exec;
+	char *collect = NULL;
+	char chan_collect[25] = "";
+	char peer_collect[25] = "";
+	char *xferstr=config->xferstr,*hangupstr=config->hangupstr;
+	int ccount=0;
+	int max_collect=0;
+	char *cid_num;
+	char *cid_name;
+	int mutedtmf = 0, do_hangup=0 , do_xfer=0, dindex=0;
+	
+
+	if(ast_strlen_zero(xferstr))
+		xferstr = global_xferstr;
+	if(ast_strlen_zero(hangupstr))
+		hangupstr = global_hangupstr;
+
+	if(! ast_strlen_zero(xferstr) || ! ast_strlen_zero(hangupstr)) {
+		max_collect = strlen(xferstr) > strlen(hangupstr) ? strlen(xferstr) : strlen(hangupstr);
+	}
 
 	if (chan && peer) {
 		pbx_builtin_setvar_helper(chan, "BRIDGEPEER", peer->name);
@@ -336,7 +403,8 @@
 	allowredirect_in = config->allowredirect_in;
 	allowredirect_out = config->allowredirect_out;
 	config->firstpass = 1;
-
+	if(!config->dtmf_timeout)
+		config->dtmf_timeout = 1100;
 	/* Answer if need be */
 	if (ast_answer(chan))
 		return -1;
@@ -412,20 +480,259 @@
 					ast_channel_setoption(chan, ntohs(aoh->option), aoh->data, f->datalen - sizeof(struct ast_option_header), 0);
 			}
 		}
-		/* 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 == '*')) {
+
+		/* multi-digit significant DTMF codes hangup=123 transfer=789 etc.
+
+		What we want here is for the caller to be able to dial 123 to hangup the call *BUT* we don't want the
+		callee to hear the dtmf *BUT* if he dials 124 which is no longer correct we must deliver the 124 to
+		the callee at that time *OR* if he dials 12 and waits too long we must also deliver the past due digits.
+		*OR* if he dials 127 we send the 12 and keep the 7 for a bit in case he is going to enter 789.
+
+		This is a but of a pain because this function calls ast_channel_bridge in channel.c
+		who in turn may call a native bridge function so we are not able to execute regularly in order to keep track
+		so we must have a seperate input buffer and timer for each leg of the call.  When dtmf that is potentially
+		part of one of the desired codes is collected we must set the timer on our leg so ast_channel_bridge will be nice
+		enough to send the call back this way and we can act on the results we must also configure the call to make
+		native bridges impossible as the timer would not work since native bridges never return unless there is a dtmf or
+		hangup event. ... *deep breath*
+		*/
+
+		mutedtmf = do_xfer = do_hangup = 0;
+		collect = (who == chan) ? chan_collect : peer_collect;
+		dindex = who == chan ? 0 : 1;		
+
+		if(config->dtmf_time_elapsed[dindex] < 0) {
+			/*The timer has expired so if we have any dtmf buffered, deliver it and unset the timer.*/
+			if(collect[0]) {
+				res = ast_dtmf_stream((who == chan) ? peer : chan, NULL, collect, 250);
+				mutedtmf = 1;
+				ccount = 0;
+				collect[ccount] = '\0';
+			}
+			config->dtmf_time_elapsed[dindex] = 0;
+			config->dtmf_timer[dindex].tv_sec = config->dtmf_timer[dindex].tv_usec = 0;
+			config->block_native = 0;
+		}
+
+
+		if (f && (f->frametype == AST_FRAME_DTMF)) {
+			if(ccount < max_collect) {
+				collect[ccount++] = f->subclass;
+				collect[ccount] = '\0';
+			} else 
+				ccount = 0;
+
+			if (!strncmp(collect, hangupstr, strlen(collect)) || !strncmp(collect, xferstr, strlen(collect))) {
+				/* as long as we are matching keep quiet and reset the timer */
+				mutedtmf = 1;
+				gettimeofday(&config->dtmf_timer[dindex],NULL);
+				config->block_native = 1;
+			} else if (f->subclass == hangupstr[0] || f->subclass == xferstr[0]) {
+				/* we are not matching anymore but the most recent key is the start of a match 
+				   so lets dump the dtmf buffer out to the other channel, restart the timer and continue
+				*/
+				mutedtmf = 1;
+				collect[ccount-1] = '\0';
+				if(collect[0]) {
+					res = ast_dtmf_stream((who == chan) ? peer : chan, NULL, collect, 250);
+				}
+				ccount = 0;
+                collect[ccount++] = f->subclass;
+                collect[ccount] = '\0';
+				gettimeofday(&config->dtmf_timer[dindex],NULL);
+				config->block_native = 1;
+			} else {
+				/* We are flat-out not matching anything we haven't played the digits we have collected
+				   so far so if we have any saved we play it to the other end of the call and turn off the timer*/
+				if (collect[1]) {
+					res = ast_dtmf_stream((who == chan) ? peer : chan, NULL, collect, 250);
+					mutedtmf = 1;
+				}
+				ccount = 0;
+				collect[ccount] = '\0';
+				config->dtmf_time_elapsed[dindex] = 0;
+				config->dtmf_timer[dindex].tv_sec = config->dtmf_timer[dindex].tv_usec = 0;
+				config->block_native = 0;
+			}
+		}
+
+
+		
+		if(f && (f->frametype == AST_FRAME_DTMF) && !ast_strlen_zero(hangupstr) &&
+		   (((who == chan) && allowdisconnect_out) || ((who == peer) && allowdisconnect_in)) &&
+		   (!strncmp(collect,hangupstr,sizeof(collect))))
+			do_hangup = 1;
+		else if ((f->frametype == AST_FRAME_DTMF) && !ast_strlen_zero(xferstr) &&
+			((allowredirect_in == AST_ATTENDED_TRANSFER && who == peer) || (allowredirect_out == AST_ATTENDED_TRANSFER && who == chan)) &&
+			(!strncmp(collect,xferstr,sizeof(collect)))) 
+			do_xfer = AST_ATTENDED_TRANSFER;
+		else if ((f->frametype == AST_FRAME_DTMF) && !ast_strlen_zero(xferstr) &&
+				 ((allowredirect_in == AST_REGULAR_TRANSFER && who == peer) || 
+				  (allowredirect_out == AST_REGULAR_TRANSFER && who == chan)) &&
+				 (ccount >= strlen(xferstr) && !strncmp(collect + (ccount - strlen(xferstr)),xferstr,sizeof(collect))))
+			do_xfer = AST_REGULAR_TRANSFER;
 			
+
+		if(do_xfer || do_hangup) {
+			ccount = 0;
+			collect[ccount] = '\0';
+			mutedtmf = 0;
+		}
+
+
+		if(do_hangup) {
 			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(do_xfer == AST_ATTENDED_TRANSFER) {
+			if(allowredirect_in &&  who == peer) {
+				transferer = peer;
+				transferee = chan;
+			}
+			else {
+				transferer = chan;
+				transferee = peer;
+			}
+			ast_autoservice_start(transferee);
+			ast_moh_start(transferee, NULL);
+			if((ast_dtget(transferer, xferto, sizeof(xferto), 10, 2))) {
+				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(strlen(transferer->macrocontext))
+						transferer_real_context=transferer->macrocontext;
+					else
+						transferer_real_context=transferer->context;
+				}
+				
+				cid_num = transferer->cid.cid_num ? transferer->cid.cid_num: NULL;
+				cid_name = transferer->cid.cid_name ? transferer->cid.cid_name: NULL;
+				if (ast_exists_extension(transferer, transferer_real_context,xferto, 1, cid_num)) {
+					snprintf(dialstr, sizeof(dialstr), "%s@%s/n", xferto, transferer_real_context);
+					if((newchan = ast_request_and_dial("Local", ast_best_codec(transferer->nativeformats), dialstr,30000, &outstate, cid_num, cid_name))) {
+						res = ast_channel_make_compatible(transferer, newchan);
+						if (res < 0) {
+							ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferer->name, newchan->name);
+							ast_hangup(newchan);
+							break;
+						}
+						memset(&bconfig,0,sizeof(struct ast_bridge_config));
+						bconfig.allowdisconnect_in = bconfig.allowdisconnect_out = 1;
+						res = ast_bridge_call(transferer,newchan,&bconfig);
+						if(newchan->_softhangup || newchan->_state != AST_STATE_UP) {
+							ast_hangup(newchan);
+							if (f) {
+                                ast_frfree(f);
+								f = NULL;
+							}
+							if (!ast_streamfile(transferer, "beep", transferer->language)) {
+								if (ast_waitstream(transferer, "") < 0) {
+									ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+								}
+							}
+							ast_moh_stop(transferee);
+							ast_autoservice_stop(transferee);
+							transferer->_softhangup = 0;
+							continue;
+						}
+						
+						res = ast_channel_make_compatible(transferee, newchan);
+						if (res < 0) {
+                            ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", transferee->name, newchan->name);
+                            ast_hangup(newchan);
+							break;
+                        }
+
+
+						ast_moh_stop(transferee);
+
+						if((ast_autoservice_stop(transferee) < 0)
+						   ||(ast_waitfordigit(transferee,100) < 0)
+						   || (ast_waitfordigit(newchan,100) < 0) 
+						   || ast_check_hangup(transferee) 
+						   || ast_check_hangup(newchan)) {
+							ast_hangup(newchan);
+							res = -1;
+							break;
+						}
+						
+
+						xferchan = ast_channel_alloc(0);
+						if (xferchan) {
+							/* Make formats okay */
+							xferchan->readformat = transferee->readformat;
+							xferchan->writeformat = transferee->writeformat;
+							ast_channel_masquerade(xferchan, transferee);
+							/* Setup the extensions and such */
+							snprintf(xferchan->name, sizeof (xferchan->name), "Transfered/%s",transferee->name);
+							ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
+							xferchan->_state = transferee->_state;
+							ast_clear_flag(xferchan,AST_FLAG_ZOMBIE);
+							xferchan->_softhangup = 0;
+							/* Make the masq execute */
+							f = ast_read(xferchan);
+							if (f) {
+								ast_frfree(f);
+								f = NULL;
+							}
+						} else {
+							ast_hangup(newchan);
+							break;
+						}
+						
+						tobj = malloc(sizeof(struct ast_bridge_thread_obj));
+						memset(tobj,0,sizeof(struct ast_bridge_thread_obj));
+						tobj->chan = xferchan;
+						tobj->peer = newchan;
+						tobj->bconfig = *config;
+						ast_moh_stop(transferee);
+						ast_autoservice_stop(transferee);
+						if (!ast_streamfile(newchan, "beep", newchan->language)) {
+							if (ast_waitstream(newchan, "") < 0) {
+								ast_log(LOG_WARNING, "Failed to play courtesy tone!\n");
+							}
+						}
+						
+						ast_bridge_call_thread_launch(tobj);
+						res = -1;
+						break;
+
+					} else {
+						ast_log(LOG_WARNING, "Unable to create channel Local/%s do you have chan_local?\n",dialstr);
+						ast_moh_stop(transferee);
+						ast_autoservice_stop(transferee);
+						res = ast_streamfile(transferer, "errbeep", transferer->language);
+						if (ast_waitstream(transferer, "") < 0) {
+							res = -1;
+							break;
+						}
+						continue;
+					}
+				} else {
+					ast_log(LOG_WARNING, "Extension %s does not exist in context %s\n",xferto,transferer_real_context);
+					ast_moh_stop(transferee);
+					ast_autoservice_stop(transferee);
+					res = ast_streamfile(transferer, "errbeep", transferer->language);
+					if (ast_waitstream(transferer, "") < 0) {
+						res = -1;
+						break;
+					}
+				}
+			}  else {
+					ast_log(LOG_WARNING, "Did not read data.\n");
+				res = ast_streamfile(transferer, "errbeep", transferer->language);
+				if (ast_waitstream(transferer, "") < 0) {
+					res = -1;
+					break;
+				}
+			}
+			ast_moh_stop(transferee);
+			ast_autoservice_stop(transferee);
+		} else if(do_xfer == AST_REGULAR_TRANSFER) {
+
 				if (allowredirect_in &&  who == peer) {
 					transferer = peer;
 					transferee = chan;
@@ -545,18 +852,24 @@
 						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)
+				if (f && (f->frametype == AST_FRAME_DTMF) && ! config->mutedtmf && ! mutedtmf) {
+					if (who == peer)
                         ast_write(chan, f);
-                  else
+					else
                         ast_write(peer, f);
-            }            
+				} 
 #if 1
 				ast_log(LOG_DEBUG, "Read from %s (%d,%d)\n", who->name, f->frametype, f->subclass);
 #endif
 			}
-         if (f)
-               ast_frfree(f);
+		if (f) {
+			ast_frfree(f);
+			f=NULL;
+		}
+	}
+	if (f) {
+		ast_frfree(f);
+		f=NULL;
 	}
 	return res;
 }
@@ -944,6 +1257,10 @@
 				strncpy(parking_ext, var->value, sizeof(parking_ext) - 1);
 			} else if (!strcasecmp(var->name, "context")) {
 				strncpy(parking_con, var->value, sizeof(parking_con) - 1);
+			} else if (!strcasecmp(var->name, "hangupstr")) {
+				strncpy(global_hangupstr, var->value, sizeof(global_hangupstr) - 1);
+			} else if (!strcasecmp(var->name, "xferstr")) {
+				strncpy(global_xferstr, var->value, sizeof(global_xferstr) - 1);
 			} else if (!strcasecmp(var->name, "parkingtime")) {
 				if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
 					ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
