Being familiar with Asterisk, you will have a good working understanding of extensions and contexts already. They are, of course, the very heartbeat of Asterisk, and as such they are probably subject to the most change from version to version, as Asterisk evolves to cater for new hardware, software, and more complex working practices. So let's have a quick review of extensions and contexts, pointing out significant changes in versions 1.4 and 1.6, before we proceed to the more advanced techniques and uses.
Within the dialplan, matching can be either direct or partial against a pattern. Normally in a PBX, these patterns are numeric. But with Asterisk, they can also be alphanumeric or even just alpha. For example 2000, DID01234123456
, and Main_number
are all valid extensions. As very few phones contain alphabetic keys, the last two are typically only used for incoming DID channels. For the majority of this chapter, we will stick to numeric patterns.
Let's start to explore pattern matching by looking at an extremely simple dialplan:
[context_1] exten => 123,1,Answer() exten => 123,n,SayDigits(999${CALLERID(num)}) exten => 123,n,Hangup()
In this dialplan, when a user with a context of context_1
dials 123
, they will hear 999 and their caller ID will be read back to them.
Now let's look at a slightly more complex context:
[context_1] exten => _1X.,1,Answer() exten => _1X.,n,SayDigits(${EXTEN}${CALLERID(num)}) exten => _1X.,n,Hangup() exten => 123,1,Answer() exten => 123,n,SayDigits(123${CALLERID(num)}) exten => 123,n,Hangup()
You might expect that 123
would match against the _1X
. extension, as that appears first in the context. However, the way Asterisk orders the dialplan when loading means that exact matches are checked for before pattern matches. Hence if you dial 123, it matches against the 123 pattern first and not the _1X
. pattern. This pattern would only route the call if an exact match did not exist in the context.
Note
It is sensible not to use the pattern _
. as a catch-all pattern, as this will catch the Asterisk special extensions like i, t, h
as well. It is far better to use the _X
pattern.
Once understood, pattern matching is pretty straightforward and does what we expect. However, if you introduce included contexts into the mix, things may work in a way you did not expect and the order needs to be thought through carefully. In particular, it's crucial to understand that Asterisk only checks included contexts after checking for exact matches and pattern matches in the local context. The following example illustrates this:
[context_1] include => context_2 exten => _1X.,1,Answer() exten => _1X.,n,SayDigits(${EXTEN}${CALLERID(num)}) exten => _1X.,n,Hangup() include => context_3 exten => 123,1,Answer() exten => 123,n,SayDigits(123${CALLERID(num)}) exten => 123,n,Hangup()
The above dialplan is sorted internally by Asterisk shown as follows, and you can see that though the included contexts are at the top and in the middle, the local context is read first, then the included contexts are read in the order that they were added. Hence, in this case, a dial string of 122
would be matched by the _1X
. pattern before the included contexts are searched.
'123' => 1. Answer() 2. SayDigits(123${CALLERID(num)}) 3. Hangup() '_1X.' => 1. Answer() 2. SayDigits(${EXTEN}${CALLERID(num)}) 3. Hangup() Include => 'context_2' Include => 'context_3'
Note
If you have a catch-all pattern in your dialplan, consider putting it into a separate context. You can then use the include
directive to append that context to the end of the active context, thus ensuring that all of the other pattern matching is attempted first.
One of the most powerful tools you will use on the Asterisk command line is dialplan show <exten>@<context>
. For example:
dialplan show 122@context_1
This will show you the matching order that Asterisk will use for the given extension in the specified context, and if there are matches in any included contexts, those contexts will be explicitly identified.
Finally, in a context you may have a switch
statement, which includes the dialplan of an external system into the local dialplan. In essence, it's an include
for remote systems. Though typing dialplan show
will always show the switch
statement at the bottom, the defined context on the remote system is searched after the local context on your system and before any local included contexts! So again, you have to be very careful as to what is the context on the remote system as this will be searched before your included contexts.
The syntax of the switch
state is as follows:
switch =>IAX2/user:[key]@server/context
The user
and key
are defined in the called server's iax.conf
file, and the context is, of course, in the server's dialplan.
In our examples so far we could have achieved the desired results very easily without the use of multiple contexts. The simple functionality we have looked at could be carried out in a single, all-encompassing context. In practice, this approach could be applicable for systems with a very limited number of users and trunks, and with very restricted functionality, as there may not be a need to restrict the calling habits of a subset of users.
Use of contexts becomes desirable when we need to offer different options to different users. This is likely to be most applicable in medium and large companies, where you may have "users" ranging from the CEO down to an emergency phone in a lift. However, it can also be the case in smaller companies, where you might want to restrict home workers from making international calls for instance. When you get many different types of users, writing a distinct dialplan for each becomes problematic. The sheer size and complexity of the dialplan will make code management very complicated.
To simplify things, we first need to think about what makes the dialplan for each extension different. Then we need to think about what remains the same for each extension, as this needs to be made to work as well. What we often find is that most of these differences can be stored and called in two main ways:
We will come to variables shortly, but the grouping of extensions into contexts allows us to separate concise and distinct functions from each other. In doing so, we can control very tightly which contexts are used in each scenario, and also implement one "master" copy of each distinct function, aiding maintenance of the code.
To illustrate, let's expand our context a bit and use call barring as an example. We will initially have three levels for this example local, national, and international.
These are defined as follows:
Any number starting with a 1-9 is local
Anything starting with a 00 is international.
Anything else starting with a 0 is national or a mobile number.
This is a simplified example, and uses the UK format of dial prefixes.
We have in this example three contexts local_num, national_num
and international_num
. These would correspond to the levels of access we have decided on for our users. For example, an executive phone would be allowed access to all numbers whereas a phone on the shop floor may only be allowed access to local numbers.
We will create the three contexts shown as follows. All we are doing in our example is reading back 1, 2, or 3 to indicate the pattern that has been matched followed by the number dialed ${EXTEN}
.
[local_num] Exten => _Z.,1,Answer() Exten => _Z.,n,SayDigits(1${EXTEN}) Exten => _Z.,n,Hangup() ; [national_num] Exten => _0Z.,1,Answer() Exten => _0Z.,n,SayDigits(2${EXTEN}) Exten => _0Z.,n,Hangup() ; [international_num] Exten => _00X.,1,Answer() Exten => _00X.,n,SayDigits(3${EXTEN}) Exten => _00X.,n,Hangup()
For each context we could write an ordered list to cover all patterns, but it is much neater to create a master context for each user. For example:
[local] Include => local_num [national] Include => national_num Include => local_num [international] Include => international_num Include => national_num Include => local_num
Therefore, in the previous example, a user with the national
context can dial a normal national number, but not an international number. A user with the international
context has the ability to dial both numbers.
This is a pretty simple example with just three level of access, but the modular nature due to the use of contexts allows us to expand it very quickly and easily. For example, we have a user 1000
(our CEO) and he can dial internationally. We also have 1098
and 1099
, which are users on the shop floor, and can dial reception and the emergency services.
In this example, we give our CEO a context of [supauser],while
the shop floor has a context of [emergencyuser]
.
The [supauser]
context has to be able to dial everything, so it looks like this:
[supauser] include => premium_num ; allows dialing to premium rate numbers include => international_num ; allows international dialing include => national_num ; allows national calls include => mobile_num ; allows calls to mobile phones include => local_num ; allows local rate calls include => free_num ; allows free calls such as 800 or operator services include => internal_num ; allows the calling of extensions include => emergency ; allows calls to the emergency services include => default ; allows access to system features
The shop floor just has the following context:
[emergencyuser] include => emergency ; allows calls to emergency services reception.
As you can see, we can mix and match these contexts to cover many different types of extensions. Although you may be asking, "Will this really save me time?" well, let's look at two examples. Firstly, our supplier reduces the cost of UK 0870 numbers to free in the evenings as has happened in the UK with BT(British Telecom). Secondly, we also want the shop floor phone (1099) to be able to dial extensions and toll free calls, but not change the dialplan for 1098
.
We will deal with the simplest of these extensions (1099) first. All we need to do is change the context associated with this user to a new context called [freeuser]:
[freeuser] include => free_num ; allows calls to free numbers include => internal_num ; allows the calling of extensions include => emergency ; allows calls to the emergency services include => default ; allows access to system features .
This is a fast and easy change, which will have no effect on other shop floor users.
And to the change to 0870 numbers, this once again can be put into effect very simply. The only change is that evening and weekend calls are now free. Therefore, we could put it into a [free]
context. Although, it isn't always free. It is free only at weekends which would not be suitable. Hence, for this we use the GotoIfTime
application, which sets the context, extension, and priority in the channel based on the system time, day, date, and month supplied by the OS.
By adding the following to the free context, users can now dial 0870 numbers at the defined times.
exten => _0870XXXXXXX,1,GotoIfTime(17:59-08:00,mon-fri,*,*?national, ${EXTEN},1) exten => _0870XXXXXXX,1,GotoIfTime(*,sat-sun,*,*?national,${EXTEN},1)
In this case, we have made a change for all users who also have a context allowing both local and free calls (as their context includes the free context).
The GotoIfTime()
application can introduce some powerful functionality into your dialplan if used properly. An example that follows is for a support company where calls are routed to the call centre or staff member on call at a specific time. The customer had centers round the globe and we routed the calls to whichever center was open at that time of day.
[folthesun] ; ;This section sets the constants and variables for numbers and times ;Nine timezones are defined to allow for 4 a day and sat and sun working ;At present there are 6 destinations for NA AU and EMEA ; exten => s,1,set(__tzone1=00:00-07:59) exten => s,n,set(__tzone2=08:00-17:30) exten => s,n,set(__tzone3=17:31-23:59) exten => s,n,set(__tzone4=17:31-23:59) exten => s,n,set(__tzone5=00:00-23:59) exten => s,n,set(__tzone6=00:00-23:59) exten => s,n,set(__tzone7=00:00-23:59) exten => s,n,set(__tzone8=00:00-23:59) exten => s,n,set(__tzone9=00:00-23:59) ; exten => s,n,set(_dest1=01234123456) ;dest1 emea_pager exten => s,n,set(_dest2=001765412345) ;dest2 na_pager exten => s,n,set(_dest3=006165453457) ;dest3 au_pager exten => s,n,set(_dest4=08441231234) ;dest4 uk_no exten => s,n,set(_dest5=001744519651) ;dest5 na_no exten => s,n,set(_dest6=006118954654) ;dest6 au_no ; exten => s,n,set(dialpre=9) ;dialing prefix ; exten => s,n,set(dialcon=international) ;dialing context ; exten => s,n,Goto(ftstimeing,s,1) ; [ftstimeing] ; ;This sections runs though the days of the week and checks the time ;against DOW and time ; exten => s,1,GotoIfTime(${tzone1}|mon|*|*?dest1,1) exten => s,n,GotoIfTime(${tzone2}|mon|*|*?dest4,1) exten => s,n,GotoIfTime(${tzone3}|mon|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone4}|mon|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone1}|tue|*|*?dest1,1) exten => s,n,GotoIfTime(${tzone2}|tue|*|*?dest4,1) exten => s,n,GotoIfTime(${tzone3}|tue|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone4}|tue|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone1}|wed|*|*?dest1,1) exten => s,n,GotoIfTime(${tzone2}|wed|*|*?dest4,1) exten => s,n,GotoIfTime(${tzone3}|wed|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone4}|wed|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone1}|thu|*|*?dest1,1) exten => s,n,GotoIfTime(${tzone2}|thu|*|*?dest4,1) exten => s,n,GotoIfTime(${tzone3}|thu|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone4}|thu|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone1}|fri|*|*?dest1,1) exten => s,n,GotoIfTime(${tzone2}|fri|*|*?dest4,1) exten => s,n,GotoIfTime(${tzone3}|fri|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone4}|fri|*|*?dest5,1) exten => s,n,GotoIfTime(${tzone5}|sat|*|*?dest1,1) exten => s,n,GotoIfTime(${tzone6}|sun|*|*?dest1,1) ; ;Fall through point exten => s,n,Goto(dest1,1) contextGotoIfTime() application, using; ;Dialed using the Local channel so call handling is observered ; exten => dest1,1,Noop(Calling ${dest1}) exten => dest1,n,Dial(Local/${dialpre}${dest1}@${dialcon}) exten => dest1,n,Hangup() exten => dest2,1,Noop(Calling ${dest2}) exten => dest2,n,Dial(Local/${dialpre}${dest2}@${dialcon}) exten => dest2,n,Hangup() exten => dest3,1,Noop(Calling ${dest3}) exten => dest3,n,Dial(Local/${dialpre}${dest3}@${dialcon}) exten => dest3,n,Hangup() exten => dest4,1,Noop(Calling ${dest4}) exten => dest4,n,Dial(Local/${dialpre}${dest4}@${dialcon}) exten => dest4,n,Hangup() exten => dest5,1,Noop(Calling ${dest5}) exten => dest5,n,Dial(Local/${dialpre}${dest5}@${dialcon}) exten => dest5,n,Hangup() exten => dest6,1,Noop(Calling ${dest6}) exten => dest6,n,Dial(Local/${dialpre}${dest6}@${dialcon}) exten => dest6,n,Hangup() exten => i,1,Hangup() exten => t,1,Hangup() exten => h,1,Hangup()
This can be expanded to include public holidays, if required. It can be possible to handle many years' public holidays in one line. For example, between the years 2009 and 2016, the UK's summer public holiday falls on the dates between the 25th and 31st of August and is always a Monday. Therefore, we have something like this:
GotoIfTime(*,Mon,25-31,Aug?dest1,1)
This will catch all UK summer public holidays, and as there are no other Mondays in August clashing with these dates, it's a set-and-forget for many years (just don't forget to change it after 2016!). The same goes for the majority of other public holidays except for Easter.
For these variable dates, we can resort back to the internal database to store the details and then use the GotoIf()
application to check if the date is a holiday.