[Asterisk-Dev] [PATCH] T flag for Dial() implementation

Daniele Orlandi daniele at orlandi.com
Fri Jun 27 06:35:22 MST 2003


Hello Mark,
Hello Everyone,

I've made a patch (attached) to implement the T flag in Dial() and 
Queue() applications. It includes the following fixes/features:

- Transfers when the transferer is in a macro context should now work
- Music on Hold is activated during the transfer
- New "show parkedcalls" CLI command shows informations on parked calls

There is an outstanding issue that I wasn't able to resolve: I don't
know how to call back the extension who parked a call when the parked
call times out. I'm willing to accept any advice on this issue.

This patch will probably (it had already, actually) raise the question 
on how to escape/disable/whatever DTMF interception so that the tones 
can reach the called party.

A workaround could be to have a prefix that uses a Dial() without flags, 
something like:

exten => _X.,1,Dial(....,..,tTh)
exten => _*69#.,1,Dial(....)

Another solution could be an escaping of the # tone, maybe digiting 
another # or a code to disable DTMF interception... ideas ?

Bye!

-- 
  Daniele Orlandi

-------------- next part --------------
diff -uNr --exclude=.depend --exclude='*.orig' asterisk/apps/app_dial.c asterisk-T-5/apps/app_dial.c
--- asterisk/apps/app_dial.c	2003-06-26 04:06:52.000000000 +0200
+++ asterisk-T-5/apps/app_dial.c	2003-06-26 19:18:27.000000000 +0200
@@ -79,7 +79,8 @@
 struct localuser {
 	struct ast_channel *chan;
 	int stillgoing;
-	int allowredirect;
+	int allowredirect_in;
+	int allowredirect_out;
 	int ringbackonly;
 	int musiconhold;
 	int dataquality;
@@ -105,7 +106,7 @@
 
 #define MAX 256
 
-static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir, int *allowdisconnect)
+static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect)
 {
 	struct localuser *o;
 	int found;
@@ -182,7 +183,8 @@
 					if (option_verbose > 2)
 						ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
 					peer = o->chan;
-					*allowredir = o->allowredirect;
+					*allowredir_in = o->allowredirect_in;
+					*allowredir_out = o->allowredirect_out;
 					*allowdisconnect = o->allowdisconnect;
 				}
 			} else if (o->chan == winner) {
@@ -218,7 +220,8 @@
 								if (option_verbose > 2)
 									ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
 								peer = o->chan;
-								*allowredir = o->allowredirect;
+								*allowredir_in = o->allowredirect_in;
+								*allowredir_out = o->allowredirect_out;
 								*allowdisconnect = o->allowdisconnect;
 							}
 							break;
@@ -326,7 +329,8 @@
 	struct localuser *outgoing=NULL, *tmp;
 	struct ast_channel *peer;
 	int to;
-	int allowredir=0;
+	int allowredir_in=0;
+	int allowredir_out=0;
 	int allowdisconnect=0;
 	int privacy=0;
 	int resetcdr=0;
@@ -447,8 +451,11 @@
 		memset(tmp, 0, sizeof(struct localuser));
 		if (transfer) {
 			if (strchr(transfer, 't'))
-				tmp->allowredirect = 1;
-                        else    tmp->allowredirect = 0;
+				tmp->allowredirect_in = 1;
+                        else    tmp->allowredirect_in = 0;
+			if (strchr(transfer, 'T'))
+				tmp->allowredirect_out = 1;
+                        else    tmp->allowredirect_out = 0;
 			if (strchr(transfer, 'r'))
 				tmp->ringbackonly = 1;
                         else    tmp->ringbackonly = 0;
@@ -583,7 +590,7 @@
 		to = atoi(timeout) * 1000;
 	else
 		to = -1;
-	peer = wait_for_answer(chan, outgoing, &to, &allowredir, &allowdisconnect);
+	peer = wait_for_answer(chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect);
 	if (!peer) {
 		if (to) 
 			/* Musta gotten hung up */
@@ -637,14 +644,16 @@
 			ast_channel_setoption(chan,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
 			ast_channel_setoption(peer,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
 		}
-		res = ast_bridge_call(chan, peer, allowredir, allowdisconnect | clearchannel);
+		res = ast_bridge_call(chan, peer, allowredir_in, allowredir_out, allowdisconnect | clearchannel);
 		if (clearchannel)
 		{
 			int x = 1;
 			ast_channel_setoption(chan,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
 			ast_channel_setoption(peer,AST_OPTION_AUDIO_MODE,&x,sizeof(char),0);
 		}
-		ast_hangup(peer);
+
+		if (res != AST_PBX_NO_HANGUP_PEER)
+			ast_hangup(peer);
 	}	
 out:
 	hanguptree(outgoing, NULL);
diff -uNr --exclude=.depend --exclude='*.orig' asterisk/apps/app_queue.c asterisk-T-5/apps/app_queue.c
--- asterisk/apps/app_queue.c	2003-06-26 04:06:53.000000000 +0200
+++ asterisk-T-5/apps/app_queue.c	2003-06-26 19:18:27.000000000 +0200
@@ -70,7 +70,8 @@
 struct localuser {
 	struct ast_channel *chan;
 	int stillgoing;
-	int allowredirect;
+	int allowredirect_in;
+	int allowredirect_out;
 	int ringbackonly;
 	int musiconhold;
 	int dataquality;
@@ -261,7 +262,7 @@
 
 #define MAX 256
 
-static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir, int *allowdisconnect, char *queue)
+static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect, char *queue)
 {
 	struct localuser *o;
 	int found;
@@ -307,7 +308,8 @@
 					if (option_verbose > 2)
 						ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
 					peer = o->chan;
-					*allowredir = o->allowredirect;
+					*allowredir_in = o->allowredirect_in;
+					*allowredir_out = o->allowredirect_out;
 					*allowdisconnect = o->allowdisconnect;
 				}
 			} else if (o->chan == winner) {
@@ -321,7 +323,8 @@
 								if (option_verbose > 2)
 									ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
 								peer = o->chan;
-								*allowredir = o->allowredirect;
+								*allowredir_in = o->allowredirect_in;
+								*allowredir_out = o->allowredirect_out;
 								*allowdisconnect = o->allowdisconnect;
 							}
 							break;
@@ -419,7 +422,8 @@
 	struct member *cur;
 	struct localuser *outgoing=NULL, *tmp = NULL;
 	int to;
-	int allowredir=0;
+	int allowredir_in=0;
+	int allowredir_out=0;
 	int allowdisconnect=0;
 	char numsubst[AST_MAX_EXTENSION];
 	char restofit[AST_MAX_EXTENSION];
@@ -444,7 +448,9 @@
 		memset(tmp, 0, sizeof(struct localuser));
 		if (options) {
 			if (strchr(options, 't'))
-				tmp->allowredirect = 1;
+				tmp->allowredirect_in = 1;
+			if (strchr(options, 'T'))
+				tmp->allowredirect_out = 1;
 			if (strchr(options, 'r'))
 				tmp->ringbackonly = 1;
 			if (strchr(options, 'm'))
@@ -546,7 +552,7 @@
 		to = -1;
 	ast_pthread_mutex_unlock(&qe->parent->lock);
 	
-	peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir, &allowdisconnect, qe->parent->name);
+	peer = wait_for_answer(qe->chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect, qe->parent->name);
 	if (!peer) {
 		if (to) 
 			/* Musta gotten hung up */
@@ -607,8 +613,11 @@
  			ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
  			ast_channel_sendurl( peer, url );
  		} /* /JDG */
-		bridge = ast_bridge_call(qe->chan, peer, allowredir, allowdisconnect);
-		ast_hangup(peer);
+		bridge = ast_bridge_call(qe->chan, peer, allowredir_in, allowredir_out, allowdisconnect);
+
+		if(bridge != AST_PBX_NO_HANGUP_PEER)
+			ast_hangup(peer);
+
 		if( bridge == 0 ) res=1; /* JDG: bridge successfull, leave app_queue */
 		else res = bridge; /* bridge error, stay in the queue */
 	}	
diff -uNr --exclude=.depend --exclude='*.orig' asterisk/include/asterisk/parking.h asterisk-T-5/include/asterisk/parking.h
--- asterisk/include/asterisk/parking.h	2003-06-26 04:06:53.000000000 +0200
+++ asterisk-T-5/include/asterisk/parking.h	2003-06-26 19:18:27.000000000 +0200
@@ -46,7 +46,7 @@
 
 //! Bridge a call, optionally allowing redirection
 
-extern int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect, int allowdisconnect);
+extern int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect_in, int allowredirect_out, int allowdisconnect);
 
 extern unsigned int ast_get_group(char *s);
 
diff -uNr --exclude=.depend --exclude='*.orig' asterisk/include/asterisk/pbx.h asterisk-T-5/include/asterisk/pbx.h
--- asterisk/include/asterisk/pbx.h	2003-06-25 15:54:34.000000000 +0200
+++ asterisk-T-5/include/asterisk/pbx.h	2003-06-26 19:18:27.000000000 +0200
@@ -28,6 +28,7 @@
 
 //! Special return values from applications to the PBX
 #define AST_PBX_KEEPALIVE	10		/* Destroy the thread, but don't hang up the channel */
+#define AST_PBX_NO_HANGUP_PEER       11
 
 //! Special Priority for an hint
 #define PRIORITY_HINT	-1
diff -uNr --exclude=.depend --exclude='*.orig' asterisk/res/res_parking.c asterisk-T-5/res/res_parking.c
--- asterisk/res/res_parking.c	2003-06-26 04:06:53.000000000 +0200
+++ asterisk-T-5/res/res_parking.c	2003-06-26 19:18:27.000000000 +0200
@@ -24,6 +24,7 @@
 #include <asterisk/parking.h>
 #include <asterisk/musiconhold.h>
 #include <asterisk/config.h>
+#include <asterisk/cli.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
@@ -120,7 +121,11 @@
 				break;
 		}
 		if (x <= parking_stop) {
+			chan->appl = "Parked Call";
+			chan->data = NULL; 
+
 			pu->chan = chan;
+			/* Start music on hold */
 			ast_moh_start(pu->chan, NULL);
 			gettimeofday(&pu->start, NULL);
 			pu->parkingnum = x;
@@ -144,7 +149,6 @@
 				ast_verbose(VERBOSE_PREFIX_2 "Parked %s on %d\n", pu->chan->name, pu->parkingnum);
 			if (peer)
 				ast_say_digits(peer, pu->parkingnum, "", peer->language);
-			/* Start music on hold */
 			return 0;
 		} else {
 			ast_log(LOG_WARNING, "No more parking spaces\n");
@@ -188,7 +192,7 @@
 	return 0;
 }
 
-int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect, int allowdisconnect)
+int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, int allowredirect_in, int allowredirect_out, int allowdisconnect)
 {
 	/* Copy voice back and forth between the two channels.  Give the peer
 	   the ability to transfer calls with '#<extension' syntax. */
@@ -198,13 +202,17 @@
 	char newext[256], *ptr;
 	int res;
 	struct ast_option_header *aoh;
+	struct ast_channel *transferer;
+	struct ast_channel *transferee;
+  char *transferer_real_context;
+
 	/* Answer if need be */
 	if (ast_answer(chan))
 		return -1;
 	peer->appl = "Bridged Call";
 	peer->data = chan->name;
 	for (;;) {
-		res = ast_channel_bridge(chan, peer, (allowdisconnect ? AST_BRIDGE_DTMF_CHANNEL_0 : 0) + (allowredirect ? AST_BRIDGE_DTMF_CHANNEL_1 : 0), &f, &who);
+		res = ast_channel_bridge(chan, peer, (allowdisconnect||allowredirect_out ? AST_BRIDGE_DTMF_CHANNEL_0 : 0) + (allowredirect_in ? AST_BRIDGE_DTMF_CHANNEL_1 : 0), &f, &who);
 		if (res < 0) {
 			ast_log(LOG_WARNING, "Bridge failed on channels %s and %s\n", chan->name, peer->name);
 			return -1;
@@ -245,23 +253,45 @@
 			break;
 
 			}
-		if ((f->frametype == AST_FRAME_DTMF) && (who == peer) && allowredirect &&
-		     (f->subclass == '#')) {
+
+		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;
+				}
+
+				/* Use the non-macro context to transfer the call */
+				if(strlen(transferer->macrocontext))
+					transferer_real_context=transferer->macrocontext;
+				else
+					transferer_real_context=transferer->context;
+
 				/* Start autoservice on chan while we talk
-				   to the peer */
-				ast_autoservice_start(chan);
+				   to the originator */
+				ast_autoservice_start(transferee);
+				ast_moh_start(transferee, NULL);
+
 				memset(newext, 0, sizeof(newext));
 				ptr = newext;
+
 					/* Transfer */
-				if ((res=ast_streamfile(peer, "pbx-transfer", peer->language))) {
-					ast_autoservice_stop(chan);
+				if ((res=ast_streamfile(transferer, "pbx-transfer", transferer->language))) {
+					ast_moh_stop(transferee);
+					ast_autoservice_stop(transferee);
 					break;
 				}
-				if ((res=ast_waitstream(peer, AST_DIGIT_ANY)) < 0) {
-					ast_autoservice_stop(chan);
+				if ((res=ast_waitstream(transferer, AST_DIGIT_ANY)) < 0) {
+					ast_moh_stop(transferee);
+					ast_autoservice_stop(transferee);
 					break;
 				}
-				ast_stopstream(peer);
+				ast_stopstream(transferer);
 				if (res > 0) {
 					/* If they've typed a digit already, handle it */
 					newext[0] = res;
@@ -270,65 +300,77 @@
 				}
 				res = 0;
 				while(strlen(newext) < sizeof(newext) - 1) {
-					res = ast_waitfordigit(peer, 3000);
+					res = ast_waitfordigit(transferer, 3000);
 					if (res < 1) 
 						break;
 					if (res == '#')
 						break;
 					*(ptr++) = res;
-					if (!ast_matchmore_extension(peer, peer->context, newext, 1, peer->callerid)) {
+					if (!ast_matchmore_extension(transferer, transferer_real_context
+								, newext, 1, transferer->callerid)) {
 						break;
 					}
 				}
 
 				if (res < 0) {
-					ast_autoservice_stop(chan);
+					ast_moh_stop(transferee);
+					ast_autoservice_stop(transferee);
 					break;
 				}
 				if (!strcmp(newext, ast_parking_ext())) {
-					if (ast_autoservice_stop(chan))
+					ast_moh_stop(transferee);
+
+					if (ast_autoservice_stop(transferee))
 						res = -1;
-					else if (!ast_park_call(chan, peer, 0, NULL)) {
+					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! */
-						res=AST_PBX_KEEPALIVE;
+
+						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", chan->name);
+						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(chan, peer->context, newext, 1, peer->callerid)) {
-					res=ast_autoservice_stop(chan);
-					if (!chan->pbx) {
+				} else if (ast_exists_extension(transferee, transferer_real_context, newext, 1, transferer->callerid)) {
+					ast_moh_stop(transferee);
+					res=ast_autoservice_stop(transferee);
+					if (!transferee->pbx) {
 						/* Doh!  Use our handy async_goto funcitons */
 						if (option_verbose > 2) 
-							ast_verbose(VERBOSE_PREFIX_3 "Transferring %s to '%s' (context %s) priority 1\n", chan->name, chan->exten, chan->context);
-						if (ast_async_goto(chan, peer->context, newext, 1, 1))
+							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, 1))
 							ast_log(LOG_WARNING, "Async goto fialed :(\n");
 					} else {
-						/* Set the channel's new extension, since it exists, using peer context */
-						strncpy(chan->exten, newext, sizeof(chan->exten)-1);
-						strncpy(chan->context, peer->context, sizeof(chan->context)-1);
-						chan->priority = 0;
+						/* 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, peer->context);
+						ast_verbose(VERBOSE_PREFIX_3 "Unable to find extension '%s' in context '%s'\n", newext, transferer_real_context);
 				}
-				res = ast_streamfile(peer, "pbx-invalid", chan->language);
+				res = ast_streamfile(transferer, "pbx-invalid", transferee->language);
 				if (res) {
-					ast_autoservice_stop(chan);
+					ast_moh_stop(transferee);
+					ast_autoservice_stop(transferee);
 					break;
 				}
-				res = ast_waitstream(peer, AST_DIGIT_ANY);
-				ast_stopstream(peer);
-				res = ast_autoservice_stop(chan);
+				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", chan->name);
+						ast_verbose(VERBOSE_PREFIX_2 "Hungup during autoservice stop on '%s'\n", transferee->name);
 				}
 			} else {
             if (f && (f->frametype == AST_FRAME_DTMF)) {
@@ -393,12 +435,12 @@
 			} else {
 				for (x=0;x<AST_MAX_FDS;x++) {
 					if ((pu->chan->fds[x] > -1) && (FD_ISSET(pu->chan->fds[x], &rfds) || FD_ISSET(pu->chan->fds[x], &efds))) {
-							if (FD_ISSET(pu->chan->fds[x], &efds))
-								pu->chan->exception = 1;
-							pu->chan->fdno = x;
-							/* See if they need servicing */
-							f = ast_read(pu->chan);
-							if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
+						if (FD_ISSET(pu->chan->fds[x], &efds))
+							pu->chan->exception = 1;
+						pu->chan->fdno = x;
+						/* See if they need servicing */
+						f = ast_read(pu->chan);
+						if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
 							/* There's a problem, hang them up*/
 							if (option_verbose > 1) 
 								ast_verbose(VERBOSE_PREFIX_2 "%s got tired of being parked\n", pu->chan->name);
@@ -498,7 +540,7 @@
 		   were the person called. */
 		if (option_verbose > 2) 
 			ast_verbose(VERBOSE_PREFIX_3 "Channel %s connected to parked call %d\n", chan->name, park);
-		res = ast_bridge_call(peer, chan, 1, 0);
+		res = ast_bridge_call(peer, chan, 1, 1, 0);
 		/* Simulate the PBX hanging up */
 		if (res != AST_PBX_KEEPALIVE)
 			ast_hangup(peer);
@@ -520,6 +562,37 @@
 	return res;
 }
 
+static int handle_parkedcalls(int fd, int argc, char *argv[])
+{
+	struct parkeduser *cur;
+
+	ast_cli(fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
+		, "Context", "Extension", "Pri", "Timeout");
+
+	ast_pthread_mutex_lock(&parking_lock);
+
+	cur=parkinglot;
+	while(cur) {
+		ast_cli(fd, "%4d %25s (%-15s %-12s %-4d) %6ds\n"
+			,cur->parkingnum, cur->chan->name, cur->context, cur->exten
+			,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
+
+		cur = cur->next;
+	}
+
+	ast_pthread_mutex_unlock(&parking_lock);
+
+	return RESULT_SUCCESS;
+}
+
+static char showparked_help[] =
+"Usage: show parkedcalls\n"
+"       Lists currently parked calls.\n";
+
+static struct ast_cli_entry showparked =
+{ { "show", "parkedcalls", NULL }, handle_parkedcalls, "Lists parked calls", showparked_help };
+
+
 int load_module(void)
 {
 	int res;
@@ -529,6 +602,9 @@
 	char exten[AST_MAX_EXTENSION];
 	struct ast_config *cfg;
 	struct ast_variable *var;
+
+	ast_cli_register(&showparked);
+
 	cfg = ast_load("parking.conf");
 	if (cfg) {
 		var = ast_variable_browse(cfg, "general");
@@ -641,6 +717,9 @@
 int unload_module(void)
 {
 	STANDARD_HANGUP_LOCALUSERS;
+
+	ast_cli_register(&showparked);
+
 	return ast_unregister_application(parkedcall);
 }
 


More information about the asterisk-dev mailing list