Index: app.c
===================================================================
RCS file: /usr/cvsroot/asterisk/app.c,v
retrieving revision 1.39
diff -u -r1.39 app.c
--- app.c	4 Jan 2005 20:54:36 -0000	1.39
+++ app.c	5 Jan 2005 00:16:38 -0000
@@ -30,10 +30,47 @@
 #include <asterisk/options.h>
 #include <asterisk/utils.h>
 #include <asterisk/lock.h>
+#include <asterisk/indications.h>
 #include "asterisk.h"
 #include "astconf.h"
 
 #define MAX_OTHER_FORMATS 10
+
+
+int ast_app_dtget(struct ast_channel *chan, char *context, char *collect, size_t size, int maxlen, int timeout) 
+{
+	struct tone_zone_sound *ts;
+	int res=0, x=0;
+
+	if(!timeout && chan->pbx)
+		timeout = chan->pbx->dtimeout;
+	else if(!timeout)
+		timeout = 5;
+	
+	ts = ast_get_indication_tone(chan->zone,"dial");
+    if (ts && ts->data[0]) {
+        res = ast_playtones_start(chan, 0, ts->data, 0);
+	}
+	else 
+		ast_log(LOG_NOTICE,"Huh....? no dial?\n");
+	
+	memset(collect, 0, size);
+	for (x=0; strlen(collect) < size; ) {
+		res = ast_waitfordigit(chan, timeout);
+		if(x == 0)
+			ast_playtones_stop(chan);
+		if (res < 1 || res == '#')
+			break;
+		collect[x++] = res;
+		if (!ast_matchmore_extension(chan, context, collect, 1, chan->cid.cid_num)) 
+			break;
+	}
+
+
+	return x;
+}
+
+
 
 /* set timeout to 0 for "standard" timeouts. Set timeout to -1 for 
    "ludicrous time" (essentially never times out) */
Index: include/asterisk/app.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/app.h,v
retrieving revision 1.20
diff -u -r1.20 app.h
--- include/asterisk/app.h	31 Dec 2004 01:11:33 -0000	1.20
+++ include/asterisk/app.h	5 Jan 2005 00:16:38 -0000
@@ -90,6 +90,9 @@
 //! Create an argc argv type structure for app args
 int ast_seperate_app_args(char *buf, char delim, char **array, int arraylen);
 
+//! Present a dialtone and collect a certian length extension
+int ast_app_dtget(struct ast_channel *chan, char *context, char *collect, size_t size, int maxlen, int timeout);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
Index: include/asterisk/channel.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/channel.h,v
retrieving revision 1.66
diff -u -r1.66 channel.h
--- include/asterisk/channel.h	4 Jan 2005 04:01:40 -0000	1.66
+++ include/asterisk/channel.h	5 Jan 2005 00:16:38 -0000
@@ -236,6 +236,8 @@
 #define AST_FEATURE_PLAY_WARNING	(1 << 0)
 #define AST_FEATURE_REDIRECT		(1 << 1)
 #define AST_FEATURE_DISCONNECT		(1 << 2)
+#define AST_FEATURE_ATXFER			(1 << 3)
+#define AST_FEATURE_AUTOMON			(1 << 4)
 
 #define AST_FEATURE_FLAG_NEEDSDTMF		(1 << 0)
 
Index: res/res_features.c
===================================================================
RCS file: /usr/cvsroot/asterisk/res/res_features.c,v
retrieving revision 1.25
diff -u -r1.25 res_features.c
--- res/res_features.c	4 Jan 2005 04:01:40 -0000	1.25
+++ res/res_features.c	5 Jan 2005 00:16:39 -0000
@@ -133,6 +133,50 @@
 	return pickup_ext;
 }
 
+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);
+}
+
+
+
 static int adsi_announce_park(struct ast_channel *chan, int parkingnum)
 {
 	int res;
@@ -311,6 +355,31 @@
 #define FEATURE_SENSE_PEER	(1 << 1)
 #define FEATURE_MAX_LEN		11
 
+static int builtin_automonitor(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, char *code, int sense)
+{
+	char *monitor_exec;
+	const char *default_args = "WAV||m";
+
+	if (option_verbose > 3)
+        ast_verbose(VERBOSE_PREFIX_3 "User hit '%s' to record call.\n", code);
+	if (monitor_ok) {
+		if (!monitor_app) { 
+			if (!(monitor_app = pbx_findapp("Monitor")))
+				monitor_ok=0;
+		}
+		if (!(monitor_exec = pbx_builtin_getvar_helper(chan, "TOUCH_MONITOR")) &&
+			(!(monitor_exec = pbx_builtin_getvar_helper(peer, "TOUCH_MONITOR")))) {
+			monitor_exec = (char *) default_args;
+		}
+
+
+		pbx_exec(peer, monitor_app, monitor_exec, 1);
+		return FEATURE_RETURN_SUCCESS;
+	}
+
+	return -1;
+}
+
 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)
@@ -369,18 +438,8 @@
 		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;
-	}
 
+	res = ast_app_dtget(transferer, transferer_real_context, newext, sizeof(newext), 100, transferdigittimeout) ? 0 : -1;
 	if (res < 0) {
 		ast_moh_stop(transferee);
 		ast_autoservice_stop(transferee);
@@ -447,6 +506,178 @@
 	return FEATURE_RETURN_SUCCESS;
 }
 
+static int builtin_atxfer(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;
+	struct ast_channel *newchan, *xferchan=NULL;
+	int outstate=0;
+	struct ast_bridge_config bconfig;
+	char *transferer_real_context;
+	char xferto[256],dialstr[265];
+	char *cid_num;
+	char *cid_name;
+	int res;
+	struct ast_frame *f = NULL;
+	struct ast_bridge_thread_obj *tobj;
+
+	ast_log(LOG_NOTICE, "XXX Attended 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);
+
+	/* 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;
+	}
+	if((ast_app_dtget(transferer, transferer_real_context, xferto, sizeof(xferto), 100, transferdigittimeout))) {
+		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);
+					return -1;
+				}
+				memset(&bconfig,0,sizeof(struct ast_bridge_config));
+				bconfig.features_caller |= AST_FEATURE_DISCONNECT;
+				bconfig.features_callee |= AST_FEATURE_DISCONNECT;
+				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;
+					return FEATURE_RETURN_SUCCESS;
+				}
+				
+				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);
+					return -1;
+				}
+				
+				
+				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;
+					return -1;
+				}
+
+				if ((xferchan = ast_channel_alloc(0))) {
+					snprintf(xferchan->name, sizeof (xferchan->name), "Transfered/%s",transferee->name);
+					/* Make formats okay */
+					xferchan->readformat = transferee->readformat;
+					xferchan->writeformat = transferee->writeformat;
+					ast_channel_masquerade(xferchan, transferee);
+					ast_explicit_goto(xferchan, transferee->context, transferee->exten, transferee->priority);
+					xferchan->_state = AST_STATE_UP;
+					xferchan->flags = 0;
+					xferchan->_softhangup = 0;
+
+					if((f = ast_read(xferchan))) {
+						ast_frfree(f);
+						f = NULL;
+					}
+					
+				} else {
+					ast_hangup(newchan);
+					return -1;
+				}
+
+				newchan->_state = AST_STATE_UP;
+				newchan->flags = 0;
+				newchan->_softhangup = 0;
+
+				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;
+
+				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);
+				return -1;
+				
+			} 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) {
+					return -1;
+				}
+				return -1;
+			}
+		} 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) {
+				return -1;
+			}
+		}
+	}  else {
+		ast_log(LOG_WARNING, "Did not read data.\n");
+		res = ast_streamfile(transferer, "errbeep", transferer->language);
+		if (ast_waitstream(transferer, "") < 0) {
+			return -1;
+		}
+	}
+	ast_moh_stop(transferee);
+	ast_autoservice_stop(transferee);
+
+
+	return FEATURE_RETURN_SUCCESS;
+}
+
 struct ast_call_feature {
 	int feature_mask;
 	char *fname;
@@ -457,10 +688,13 @@
 	unsigned int flags;
 };
 
+/* add atxfer and automon as undefined so you can only use em if you configure them */
 #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_REDIRECT, "Attended Transfer", "atxfer", "", "", builtin_atxfer, AST_FEATURE_FLAG_NEEDSDTMF },
+	{ AST_FEATURE_AUTOMON, "One Touch Monitor", "automon", "", "", builtin_automonitor, AST_FEATURE_FLAG_NEEDSDTMF },
 	{ AST_FEATURE_DISCONNECT, "Disconnect Call", "disconnect", "*", "*", builtin_disconnect, AST_FEATURE_FLAG_NEEDSDTMF },
 };
 
Index: apps/app_dial.c
===================================================================
RCS file: /usr/cvsroot/asterisk/apps/app_dial.c,v
retrieving revision 1.117
diff -u -r1.117 app_dial.c
--- apps/app_dial.c	4 Jan 2005 04:01:40 -0000	1.117
+++ apps/app_dial.c	5 Jan 2005 00:16:40 -0000
@@ -60,8 +60,10 @@
 "  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"
+"      '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"
+"      'w' -- allow the called user to write the conversation to disk via app_monitor\n"
+"      'W' -- allow the calling user to write the conversation to disk via app_monitor\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"
@@ -474,6 +476,8 @@
 	int to;
 	int allowredir_in=0;
 	int allowredir_out=0;
+	int monitor_in = 0;
+	int monitor_out = 0;
 	int allowdisconnect_in=0;
 	int allowdisconnect_out=0;
 	int hasmacro = 0;
@@ -789,21 +793,25 @@
 		}
 		memset(tmp, 0, sizeof(struct localuser));
 		if (transfer) {
+			if (strchr(transfer, 'w'))
+				monitor_in = 1;
+			if (strchr(transfer, 'W'))
+				monitor_out = 1;
 			if (strchr(transfer, 't'))
 				tmp->allowredirect_in = 1;
-                        else    tmp->allowredirect_in = 0;
+			else    tmp->allowredirect_in = 0;
 			if (strchr(transfer, 'T'))
 				tmp->allowredirect_out = 1;
-                        else    tmp->allowredirect_out = 0;
+			else    tmp->allowredirect_out = 0;
 			if (strchr(transfer, 'r'))
 				tmp->ringbackonly = 1;
-                        else    tmp->ringbackonly = 0;
+			else    tmp->ringbackonly = 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;
@@ -1143,6 +1151,10 @@
 				config.features_callee |= AST_FEATURE_REDIRECT;
 			if (allowredir_out)
 				config.features_caller |= AST_FEATURE_REDIRECT;
+			if (monitor_in)
+				config.features_callee |= AST_FEATURE_AUTOMON;
+			if (monitor_out)
+				config.features_caller |= AST_FEATURE_AUTOMON;
 			if (allowdisconnect_in)
 				config.features_callee |= AST_FEATURE_DISCONNECT;
 			if (allowdisconnect_out)
Index: configs/features.conf.sample
===================================================================
RCS file: /usr/cvsroot/asterisk/configs/features.conf.sample,v
retrieving revision 1.6
diff -u -r1.6 features.conf.sample
--- configs/features.conf.sample	4 Jan 2005 04:01:40 -0000	1.6
+++ configs/features.conf.sample	5 Jan 2005 00:16:40 -0000
@@ -19,3 +19,4 @@
 [featuremap]
 ;blindxfer => #			; Blind transfer
 ;disconnect => *		; Disconnect
+;automon => 0			; One Touch Record
