[Asterisk-Dev] [patch][rfc] Match as you go dialling -- new early-matching wildcard.

David Woodhouse dwmw2 at infradead.org
Mon Mar 28 17:55:51 MST 2005


Mine can't be a particularly uncommon requirement -- I want to route
certain calls (001800NXXXXXX, etc.) over VoIP, while passing everything
else to the local telco. As soon as Asterisk determines that the dialled
number is going to be routed via the ISDN BRI to the local telco, it
should obtain a channel and connect with earlyb3.

The naïve approach is to include something like this in the default
context:

exten => _Z.,1,Dial(${TRUNK}:b${EXTEN})
exten => _0Z.,1,Dial(${TRUNK}:b${EXTEN})
exten => _001[01234569].,1,Dial(${TRUNK}:b${EXTEN})
exten => _0018[123459].,1,Dial(${TRUNK}:b${EXTEN})
exten => _00180Z.,1,Dial(${TRUNK}:b${EXTEN})
exten => _00188[012345679].,1,Dial(${TRUNK}:b${EXTEN})
exten => _00187[012345689].,1,Dial(${TRUNK}:b${EXTEN})
exten => _00186[012345789].,1,Dial(${TRUNK}:b${EXTEN})

This works when I issue the 'dial' command with a full phone number, and
will presumably¹ also work if I use an ISDN phone and dial the full
number before picking up. However, if I just pick up an analogue phone
and start dialling with DTMF, there is a delay of DigitTimeout after the
last digit, before Asterisk actually picks up the external line and
tries to connect. This means that the user misses out on the immediate
feedback which is offered by earlyb3 or analogue lines -- that would be
an unacceptable regression.

Another approach is to do the above but omit the '.' wildcard character.
Then it works fine for analogue phones sending DTMF: as soon as a
non-VoIP number is dialled, we pick up the outgoing channel and further
digits are sent as DTMF while being ignored by Asterisk. but this
obviously works this works _only_ for DTMF dialling -- it fails if
asterisk can see the complete number up-front, when the call is made
from a 'dial' command or an ISDN phone.

Neither of the above are really acceptable -- my users use both forms of
dialling, and both must continue to work, with earlyb3 feedback. I
couldn't see any way to achieve that, so I played with pbx.c a bit until
it worked.

I introduced a new form of wildcard, using the '+' character. This
differs from the '+' wildcard character in two respects: firstly, it
matches zero or more characters; not one or more characters. Secondly,
and more importantly, its behaviour w.r.t match-as-you-dial DTMF
dialling is different. If the only extension which can match the
received digits is one with a '+' wildcard, then it'll match immediately
without waiting for DigitTimeout, just as with a non-wildcard extension
which is unambiguous.

The patch to do this is below. Can anyone point out a way to achieve the
same behaviour without any modification to Asterisk itself? Or a better
modification which would allow me to do the same?

My dialplan now looks like this...

[btoutgoing]
; Outgoing calls via BT by default
exten => _X+,1,Dial(${TRUNK}:b${EXTEN})
[ustollfree]
; US free numbers via FWD
exten => _001800NXXXXXX,1,Macro(stdoutgoing,IAX2/fwdnet-outbound/*${EXTEN:2})
exten => _001888NXXXXXX,1,Macro(stdoutgoing,IAX2/fwdnet-outbound/*${EXTEN:2})
exten => _001877NXXXXXX,1,Macro(stdoutgoing,IAX2/fwdnet-outbound/*${EXTEN:2})
exten => _001866NXXXXXX,1,Macro(stdoutgoing,IAX2/fwdnet-outbound/*${EXTEN:2})
[default]
; What you should get when you pick up an internal phone and get a dialtone
include => ustollfree
include => btoutgoing
exten => i,1,Playback(invalid)          ; "That's not valid, try again"
exten => s,1,WaitExten
exten => *,1,Goto(internal,s,1)
[internal]
exten => _9+,1,GotoIf($[${EXTEN} = 9]?default,s,1:default,${EXTEN:1},1)
exten => _8+,1,Dial(${TRUNK}:b${EXTEN:1})
; ... <standard internal extensions> ...

So this way I get optimal behaviour -- if I pick up an internal phone
and start dialling, I get the instant feedback from the telco which I
expect. And if I dial '*8001800xxxxxxx' from an ISDN phone, I get to
force Asterisk to use the local telco even for routing US toll-free
calls. Etc.

Was there a better way of doing it?

Index: pbx.c
===================================================================
RCS file: /usr/cvsroot/asterisk/pbx.c,v
retrieving revision 1.215
diff -u -r1.215 pbx.c
--- pbx.c	26 Mar 2005 21:01:59 -0000	1.215
+++ pbx.c	29 Mar 2005 00:50:32 -0000
@@ -629,6 +629,9 @@
 		case '.':\
 			/* Must match */\
 			return 1;\
+		case '+':\
+			/* Greedy match */\
+			return 2;\
 		case ' ':\
 		case '-':\
 			/* Ignore these characters */\
@@ -641,6 +644,9 @@
 		data++;\
 		pattern++;\
 	}\
+	/* If we ran off the end of the data and the pattern ends in '+', it's a greedy match */\
+	if (match && !*data && (*pattern == '+'))\
+		return 2;\
 }
 
 int ast_extension_match(const char *pattern, const char *data)
@@ -669,8 +675,9 @@
 		return 1;
 	}
 	EXTENSION_MATCH_CORE(data,pattern,match);
-	/* If there's more or we don't care about more, return non-zero, otlherwise it's a miss */
-	if (!needmore || *pattern) {
+	/* If there's more or we don't care about more, or if it's a possible greedy match, 
+	   return non-zero; otherwise it's a miss */
+	if (!needmore || *pattern || match == 2) {
 		return match;
 	} else
 		return 0;
@@ -750,15 +757,23 @@
 	while(tmp) {
 		/* Match context */
 		if (bypass || !strcmp(tmp->name, context)) {
+			struct ast_exten *greedymatch = NULL;
+
 			if (*status < STATUS_NO_EXTENSION)
 				*status = STATUS_NO_EXTENSION;
-			eroot = tmp->root;
-			while(eroot) {
+			for (eroot = tmp->root; eroot; eroot=eroot->next) {
+				int greedy = 0;
 				/* Match extension */
 				if ((((action != HELPER_MATCHMORE) && ast_extension_match(eroot->exten, exten)) ||
-						((action == HELPER_CANMATCH) && (ast_extension_close(eroot->exten, exten, 0))) ||
-						((action == HELPER_MATCHMORE) && (ast_extension_close(eroot->exten, exten, 1)))) &&
-						(!eroot->matchcid || matchcid(eroot->cidmatch, callerid))) {
+				     ((action == HELPER_CANMATCH) && (ast_extension_close(eroot->exten, exten, 0))) ||
+				     ((action == HELPER_MATCHMORE) && (greedy = ast_extension_close(eroot->exten, exten, 1)))) &&
+				    (!eroot->matchcid || matchcid(eroot->cidmatch, callerid))) {
+
+					if (action == HELPER_MATCHMORE && greedy == 2 && !greedymatch) {
+						/* It matched an extension ending in a '+' wildcard
+						   So ignore it for now, unless there's a better match */
+						greedymatch = eroot;
+					} else {
 						e = eroot;
 						if (*status < STATUS_NO_PRIORITY)
 							*status = STATUS_NO_PRIORITY;
@@ -779,8 +794,14 @@
 							}
 							e = e->peer;
 						}
+					}
 				}
-				eroot = eroot->next;
+			}
+			if (greedymatch) {
+				/* Bizarre logic for HELPER_MATCHMORE. We return zero to break out 
+				   of the loop waiting for more digits, and _then_ match (normally)
+				   the extension we ended up with */
+				return NULL;
 			}
 			/* Check alternative switches */
 			sw = tmp->alts;

  
-- 
dwmw2

¹ 'Presumably' because I can't actually switch a BRI card into NT-mode and 
plug the existing ISDN TA and phones into that until I have some confidence
that it's all going to work that way without regressions.




More information about the asterisk-dev mailing list