Tag: Graphical user interface

Why Asterisk-based GUI software should make better use of regular expressions

 

Important
This is an edited version of a post that originally appeared on a blog called The Michigan Telephone Blog, which was written by a friend before he decided to stop blogging. It is reposted with his permission. Comments dated before the year 2013 were originally posted to his blog.

Having recently discovered that Asterisk supports regular expressions (with some limitations), I got to wondering why the FreePBX-based GUI’s don’t make better use of them.  For example, in FreePBX, Trixbox, Elastix, etc., the Outbound Route pages all support entering patterns that are then directly used in Asterisk dialplans, in a most inefficient manner.  Let me give you an example.  Let’s say you have created an Outbound Route for toll-free calls, and in the “Dial Patterns that will use this Route” section you have entered these patterns:

1800NXXXXXX
1844NXXXXXX
1855NXXXXXX
1866NXXXXXX
1877NXXXXXX
1888NXXXXXX
800NXXXXXX
844NXXXXXX
855NXXXXXX
866NXXXXXX
877NXXXXXX
888NXXXXXX

Let’s further say that you have specified three trunks in the “Trunk Sequence for Matched Routes” section (this is actually conservative given that there are a number of ways you can route toll-free calls for free).  Anyway, this is an actual representative context that would be generated in extensions_additional.conf:

[outrt-5] ; TollFree
include => outrt-5-custom
exten => _1800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1800NXXXXXX,n,Set(_NODEST=)
exten => _1800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1800NXXXXXX,n,Macro(outisbusy,)
exten => _1844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1844NXXXXXX,n,Set(_NODEST=)
exten => _1844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1844NXXXXXX,n,Macro(outisbusy,)
exten => _1855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1855NXXXXXX,n,Set(_NODEST=)
exten => _1855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1855NXXXXXX,n,Macro(outisbusy,)
exten => _1866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1866NXXXXXX,n,Set(_NODEST=)
exten => _1866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1866NXXXXXX,n,Macro(outisbusy,)
exten => _1877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1877NXXXXXX,n,Set(_NODEST=)
exten => _1877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1877NXXXXXX,n,Macro(outisbusy,)
exten => _1888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _1888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _1888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _1888NXXXXXX,n,Set(_NODEST=)
exten => _1888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _1888NXXXXXX,n,Macro(outisbusy,)
exten => _800NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _800NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _800NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _800NXXXXXX,n,Set(_NODEST=)
exten => _800NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _800NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _800NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _800NXXXXXX,n,Macro(outisbusy,)
exten => _844NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _844NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _844NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _844NXXXXXX,n,Set(_NODEST=)
exten => _844NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _844NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _844NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _844NXXXXXX,n,Macro(outisbusy,)
exten => _855NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _855NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _855NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _855NXXXXXX,n,Set(_NODEST=)
exten => _855NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _855NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _855NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _855NXXXXXX,n,Macro(outisbusy,)
exten => _866NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _866NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _866NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _866NXXXXXX,n,Set(_NODEST=)
exten => _866NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _866NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _866NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _866NXXXXXX,n,Macro(outisbusy,)
exten => _877NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _877NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _877NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _877NXXXXXX,n,Set(_NODEST=)
exten => _877NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _877NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _877NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _877NXXXXXX,n,Macro(outisbusy,)
exten => _888NXXXXXX,1,Macro(user-callerid,SKIPTTL,)
exten => _888NXXXXXX,n,Noop(Calling Out Route: TollFree)
exten => _888NXXXXXX,n,Set(MOHCLASS=${IF($[“${MOHCLASS}”=””]?default:${MOHCLASS})})
exten => _888NXXXXXX,n,Set(_NODEST=)
exten => _888NXXXXXX,n,Macro(record-enable,${AMPUSER},OUT,)
exten => _888NXXXXXX,n,Macro(dialout-trunk,7,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,10,${EXTEN},)
exten => _888NXXXXXX,n,Macro(dialout-trunk,8,${EXTEN},)
exten => _888NXXXXXX,n,Macro(outisbusy,)

; end of [outrt-5]

That is 112 lines of dialplan generated by only 12 lines of pattern in the Outbound Route and three trunk selections.  If you have more patterns and/or more trunks, the generated dialplan can grow MUCH larger.  At one point I had a dialplan so large that whenever I did an “orange bar” reload, it took two to three minutes to finish reloading.  Clearly, there is something wrong here.

Now, let’s suppose that instead of, or better yet, in addition to the current Outbound Route pages, we had pages called “Outbound Route – Regular Expression”.  It would eliminate the “Dial Patterns that will use this Route” section, replacing it with a “Regular Expression to select this Route” text box, and it would also have a text box that says “Extensions allowed to use this route (separated by the | character – RegEx patterns may be used).

Let’s say that the contents of the first text box are ^1?8(00|44|55|66|77|88)[2-9][0-9]{6}$ and are stored in variable string1. And let’s say that the contents of the second text box are 10[0-4]X|106[5-8] (specifying that extensions 1000-1049 and 1065-1068 may use this route) and are stored in string 2. Assuming that the part of the dialplan that processes this were in some kind of macro, and the called number is in the variable OUTNUM, you could perhaps do something like this:

exten => s,n,ExecIf($[${REGEX(“${string2}” ${CALLERID(num)})} != 1]?MacroExit())
exten => s,n,ExecIf($[${REGEX(“${string1}” ${OUTNUM})} != 1]?MacroExit())
exten => s,n,Macro(dialout-trunk,7,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,10,${OUTNUM},)
exten => s,n,Macro(dialout-trunk,8,${OUTNUM},)
exten => s,n,Macro(outisbusy,)

The first line bails out of the macro if the Caller ID doesn’t match one of the permitted extensions (this would assume you by default use a regex that matches any extension on your system, for example ^[0-9]{4}$ would match any 4 digit extension number. If you don’t assume this, then you’d need an extra bit of code to skip a null value in string2).

The second line bails if the called number doesn’t match the pattern in string1.

If the called number does match on the regular expression for this route, and the call is from a permitted extension, then the three trunks are tried. Note that now you only need to list each of the three trunks once.

Granted this doesn’t account for every line in the original context, but the other lines could also be transferred into the macro, with a bit of thought. Wouldn’t this be simpler for those who know how to use regular expressions? And, it has the potential to generate MUCH shorter dialplans.

Those who understand the power of regular expressions and who have long or complicated dialplans would appreciate the ability to use regular expressions!  If anyone implements this, don’t be stingy with the maximum string length in the Regular Expression text boxes; I guarantee you that 256 characters is not enough, and neither is 1,024 characters! At a MINIMUM I would say that you need to allow a 4,096 character (4K) regular expression, and while I personally can’t conceive of anyone exceeding that, my bet is that sooner or later someone will.  Remember, you are potentially saving a whole bunch of memory and drive space by allowing regular expressions, so you don’t have to be miserly on the space allotted for the expressions themselves.

Recent Posts

Recent Comments

Archives

Categories

Meta

GiottoPress by Enrique Chavez