Chapter 18. External Services
Correct me if I’m wrong—the gizmo is connected to the flingflang connected to the watzis, watzis connected to the doo-dad connected to the ding dong.
Asterisk is pretty nifty all by itself, but one of the most powerful, industry-changing, revolutionary aspects of Asterisk is the sheer number of wonderful ways it may be connected to external applications and services. This is truly unprecedented in the world of telecom. In this chapter we’ll explore some popular services and applications that you can integrate with your Asterisk system. Here are some of the external connections we’ve decided to cover (Asterisk has more, but our editor is waiting for us to finish this edition, which is already the largest Asterisk book yet):
If you use LDAP in your network (such as with Active Directory), we’ll show you how to load your SIP users from your LDAP services.
For the person on the go with a dynamically changing calendar, we’ll sample some ideas on how you can integrate Asterisk with your calendaring server (allowing for automatic call redirection based on your current status).
If you’re a fan of instant messaging, there is a section on how to communicate with Asterisk via the XMPP (Jabber) protocol.
Skype fans? Asterisk has a channel for that. We’ll show you how to get it going.
If you want to tie your voicemail into your IMAP server, we’ll take you through the basics.
Want to teach your phone system to read? We’ll cover the basics of text-to-speech.
There are many more external services that Asterisk can connect to, but these are the ones we feel will give you the best sense of what it takes to integrate an external service with Asterisk.
Calendar Integration
Asterisk can be integrated with several different kinds of calendar formats, such as iCal, CalDAV, MS Exchange (Exchange 2003), and MS Exchange Web Services (Exchange 2007 and later). Integrating Asterisk with your calendar gives you the ability to manipulate call routing based on your current calendar information. For example, if you’re not going to be in your office for the afternoon it may make sense for people ringing your desk phone to be routed directly to your voicemail.
Another advantage to calendar integration is the ability to originate calls based on calendar information. For example, if you set up a meeting on your conference server, you can arrange to have a reminder call five minutes before the meeting starts, which then places you into the conference room. We think this type of flexibility and integration is pretty nifty and quite useful.
Compiling Calendaring Support into Asterisk
As there are several modules for calendaring support (allowing us to provide support for different backends, such as MS Exchange, CalDAV, iCal, etc.), you’ll need to install the dependencies for the backends you want to support. This modularized setup has the advantage that you only need to install dependencies for the modules you need; also other backends can easily be integrated with the primary calendaring backend in the future.
Because of the different
dependencies of each module, we need to check menuselect for what needs to be installed for
each of the calendaring modules we wish to support. All modules require
the neon development library,
available from http://www.webdav.org/neon/.
res_calendar_ews (Exchange Web
Services) requires version 0.29 or later, which means some distributions
will require you to compile the neon
library from source instead of using the precompiled package available
from the distribution.
While the configuration for all the calendaring modules is similar, we’ll be discussing CalDAV integration specifically since it is widely supported by a range of calendar software and servers.[155]
CentOS dependencies
Since all the modules require the neon library, we’ll install that first. Be
sure to append .x86_64 to the end of the package
name if installing on a 64-bit machine:
$sudo yum install neon-devel
Note
If you are planning on
compiling the res_calendar_ews
module, you will need to have a version of neon greater than or equal to 0.29.
Currently CentOS is shipping with 0.25, so you will have to
compile the neon library and
link to it from the configure
script. This can be done via ./configure
--with-neon29=<path to
neon>.
The next step is to install the libical-devel dependency. Unfortunately,
this module is not shipped with CentOS and requires a third-party
repository (see Third-Party Repositories). In this case,
we need to install libical-devel
from the EPEL (Extra Packages for Enterprise Linux) repository:
$sudo yum --enablerepo=epel install libical-devel
After
installing our dependencies, we can run the configure script in our Asterisk source
directory and enable both the res_calendar and res_calendar_caldav modules from within the
Resource Modules section of menuselect.
Ubuntu dependencies
Because all the modules require the neon development library, we’re going to
install that first. On Ubuntu, you will typically be given several
different versions (e.g., on 10.04 we have the option of
libneon 2.5, 2.6, and 2.7). We’re going to
install the latest version available to us:
$sudo apt-get install libneon27-dev
Note
If you are planning on
compiling the res_calendar_ews
module, you will need to have neon 0.29 or greater. Currently Ubuntu
is shipping with 0.27, so you will have to compile the neon library and link to it from the
configure script. This can be
done via ./configure --with-neon29=<path
to neon>.
With libneon installed, we can now
install the libical-dev package
and its dependencies with apt-get:
$sudo apt-get install libical-dev
After
installing our dependencies, we can run the configure script in our Asterisk source
directory and enable both the res_calendar and res_calendar_caldav modules from within the
Resource Modules section of menuselect.
Configuring Calendar Support for Asterisk
In this section we’re going to discuss how to connect your Asterisk system to a Google calendar. We’re using calendars from Google for the simple reason that they don’t require any other configuration (such as setting up a calendaring server), which gets us up and running far quicker. Of course, once you’re comfortable with configuring calendaring support in Asterisk, you can connect it to any calendaring server you desire.
The first step is to make sure you
have a Gmail (http://www.gmail.com) account with
Google, which will get you access to a calendaring server. Once you’ve
logged into your Gmail account, there should be a link to your calendar
in the upper-left corner. Click on the Calendar
link and insert a couple of items for the next hour or two. When we
configure our calendar.conf file
we’ll be instructing Asterisk to check for new events every 15 minutes,
and pulling in 60 minutes’ worth of data.
Tip
Be sure to verify the time on your server. If the time is not in sync with the rest of the world—e.g., if is not updated via the Network Time Protocol (NTP)—your events may not show, or may show at the wrong times. This tip is the result of running into this very issue while testing and documenting. : )
The next step is to configure our
calendar.conf file for polling our
calendar server.
Tip
The calendar.conf.sample file has several
examples for calendaring servers, such as those supplied by Microsoft
Exchange–, iCal-, and CalDAV-based calendar servers.
The following configuration will connect to the Google calendaring server and poll for new events every 15 minutes, retrieving 60 minutes’ worth of data. Feel free to change these settings as necessary, but be aware that pulling more data (especially if you have multiple calendars for people in your company) will utilize more memory:
$cat >> calendar.conf [myGoogleCal] type=caldav url=https://www.google.com/calendar/dav/Ctrl+D<Gmail Email Address>/events/ user=<Gmail Email Address>secret=<Gmail Password>refresh=15 timeframe=60
With your calendar.conf file configured, let’s load the
calendaring modules into Asterisk.
First we’ll load the res_calendar.so module into memory, then
we’ll follow it up by doing a module
reload, which will load the sister modules (such as res_calendar_caldav.so) correctly.[156]
$asterisk -r*CLI>module load res_calendar.so*CLI>module reload
After loading the modules we can check to make sure our calendar has connected to the server and been loaded into memory correctly, by executing calendar show calendars:
*CLI>calendar show calendarsCalendar Type Status -------- ---- ------ myGoogleCal caldav busy
Our status is currently set to
busy (which doesn’t have any bearing
on our dialplan at the moment, but simply means we have an event that
has marked us as busy in the calendar), and we can see the currently
loaded events for our time range by running calendar show calendar
<myGoogleCal> from the Asterisk console:
*CLI>calendar show calendar myGoogleCalName : myGoogleCal Notify channel : Notify context : Notify extension : Notify applicatio : Notify appdata : Refresh time : 15 Timeframe : 60 Autoreminder : 0 Events ------ Summary : Awesome Call With Russell Description : Organizer : Location : Cartegories : Priority : 0 UID : hlfhcpi0j360j8fteop49cvk68@google.com Start : 2010-09-28 08:30:00 AM -0400 End : 2010-09-28 09:00:00 AM -0400 Alarm : 2010-09-28 04:20:00 AM -0400
The first field in the top section is the Name of our calendar. Following that are several Notify fields, which are used to dial a destination upon the start of a meeting, that we’ll discuss in more detail shortly. The Refresh time and Timeframe fields are the values we configured for how often to check for new events and how long of a range we should look at for data, respectively. The Autoreminder field controls how long prior to an event we should execute the Notify options.
Tip
If you have not configured any of
the Notify options but have an alarm set to
remind you in the calendar, you may get a WARNING message such as:
WARNING[5196]: res_calendar.c:648 do_notify: Channel should be in form Tech/Dest (was '')
The reason for the warning is that an alarm was set for notification about the start of the meeting, but Asterisk was unable to generate a call due to values not being configured to place the call. This warning message can be safely ignored if you don’t plan on placing calls for event notifications.
The rest of the screen output is a listing of events available within our Timeframe, along with information about the events. The next steps are to look at some dialplan examples of what you can do now that you have your calendaring information in Asterisk, and to configure dialing notifications to remind you about upcoming meetings.
Triggering Calendar Reminders to Your Phone
In this section we’ll discuss how to configure the
calendar.conf file to execute some
simple dialplan that will call your phone prior to a calendar event.
While the dialplan we’ll provide might not be ready for production, it
certainly gives you a good taste of the possibilities that exist for
triggering calls based on calendar state.
Triggering a wakeup call
In our first example, we’re going to call a device and
play back a reminder notice for a particular calendar event. It might
be useful to get this type of reminder if you’re likely to be napping
at your desk when your weekly Monday meeting rolls around. To set up a
wakeup call reminder, we simply need to add the following lines to our
calendar configuration in calendar.conf:
channel=SIP/0000FFFF0001 app=Playback appdata=this-is-yr-wakeup-call
Tip
In your calendar, you need to make sure the event you’re adding has an alarm or reminder associated with it. Otherwise, Asterisk won’t try to generate a call.
After making this change, reload
the res_calendar.so module from
the Asterisk console:
*CLI>module reload res_calendar.so
When the event rolls around, Asterisk
will generate a call to you and play back the sound file this-is-yr-wakeup-call. The output on the
console would look like this:
-- Dialing SIP/0000FFFF0001 for notification on calendar myGoogleCal
== Using SIP RTP CoS mark 5
-- Called 0000FFFF0001
-- SIP/0000FFFF0001-00000001 is ringing
-- SIP/0000FFFF0001-00000001 connected line has changed, passing it to
Calendar/myGoogleCal-5fd3c52
-- SIP/0000FFFF0001-00000001 answered Calendar/myGoogleCal-5fd3c52
-- <SIP/0000FFFF0001-00000001> Playing 'this-is-yr-wakeup-call.ulaw'
(language 'en')Tip
If you modify the calendar
event so it’s just a couple of minutes in the future, you can
trigger the events quickly by unloading and then loading the
res_calendar_caldav.so module
from the Asterisk console. By doing that, you’ll trigger Asterisk
to generate the call immediately.
Remember that our refresh rate is set to 15 minutes and we’re gathering 60 minutes’ worth of events. You might have to adjust these numbers if you wish to test this out on your development server.
Scheduling calls between two participants
In this example we’re going to show how you can use a
combination of some simple dialplan and the CALENDAR_EVENT() dialplan function
to generate a call between two participants based on the
information in the location field. We’re going to fill in the location
field with 0000FFFF0002, which is
the SIP device we wish to call after answering our reminder.
Note
We haven’t specified SIP/0000FFFF0002 directly in the calendar
event because we want to be a bit more secure with what we accept.
Because we’re going to filter out anything but alphanumeric
characters, we won’t be able to accept a forward slash as the
separator between the technology and the location (e.g., SIP/0000FFFF0001). We could certainly
allow this, but then we would run the risk of people making
expensive outbound calls, especially if a user opens his calendar
publicly or is compromised. With the method we’re going to employ,
we simply limit our risk.
Add the following dialplan to
your extensions.conf file:
[AutomatedMeetingSetup]
exten => start,1,Verbose(2,Triggering meeting setup for two participants)
same => n,Set(DeviceToDial=${FILTER(0-9A-Za-z,${CALENDAR_EVENT(location)})})
same => n,Dial(SIP/${DeviceToDial},30)
same => n,Hangup()When the event time arrives, our device will receive a call, and when that call is answered another call will be placed to the endpoint with which we wish to have our meeting. The console output looks like the following:
This is where our calendar triggers a call to our device -- Dialing SIP/0000FFFF0001 for notification on calendar myGoogleCal == Using SIP RTP CoS mark 5 -- Called 0000FFFF0001 -- SIP/0000FFFF0001-00000004 is ringing And now we have answered the call from Asterisk triggered by an event -- SIP/0000FFFF0001-00000004 connected line has changed, passing it to Calendar/myGoogleCal-347ec99 -- SIP/0000FFFF0001-00000004 answered Calendar/myGoogleCal-347ec99 Upon answer, we trigger some dialplan that looks up the endpoint to call -- Executing [start@AutomatedMeetingSetup:1] Verbose("SIP/0000FFFF0001-00000004", "2,Triggering meeting setup for two participants") in new stack == Triggering meeting setup for two participants This is where we used CALENDAR_EVENT(location) to get the remote device -- Executing [start@AutomatedMeetingSetup:2] Set("SIP/0000FFFF0001-00000004", "DeviceToDial=0000FFFF0002") in new stack And now we're dialing that endpoint -- Executing [start@AutomatedMeetingSetup:3] Dial("SIP/0000FFFF0001-00000004", "SIP/0000FFFF0002,30") in new stack == Using SIP RTP CoS mark 5 -- Called 0000FFFF0002 -- SIP/0000FFFF0002-00000005 is ringing The other end answered the call, and Asterisk bridged us together -- SIP/0000FFFF0002-00000005 answered SIP/0000FFFF0001-00000004 -- Locally bridging SIP/0000FFFF0001-00000004 and SIP/0000FFFF0002-00000005
Of course, the dialplan could be expanded to prompt the initial
caller to acknowledge being ready for the meeting prior to calling the
other party. Likewise, we could add some dialplan that plays a prompt
to the other caller that lets her know that she has scheduled a
meeting and that if she presses 1
she will be connected with the other party immediately. We could even
have created a dialplan that would allow the original party to record
a message to be played back to the other caller.
Just for fun, we’ll show you an example of the functionality we just described. Feel free to modify it to your heart’s content:
[AutomatedMeetingSetup]
exten => start,1,Verbose(2,Triggering meeting setup for two participants)
; *** This line should not have any line breaks
same => n,Read(CheckMeetingAcceptance,to-confirm-wakeup&press-1&otherwise
&press-2,,1)
same => n,GotoIf($["${CheckMeetingAcceptance}" != "1"]?hangup,1)
same => n,Playback(silence/1&pls-rcrd-name-at-tone&and-prs-pound-whn-finished)
; We set a random number and assign it to the end of the recording
; so that we have a unique filename in case this is used by multiple
; people at the same time.
;
; We also prefix it with a double underscore because the channel
; variable also needs to be available to the channel we're going to call
;
same => n,Set(__RandomNumber=${RAND()})
same => n,Record(/tmp/meeting-invite-${RandomNumber}.ulaw)
same => n,Set(DeviceToDial=${FILTER(0-9A-Za-z,${CALENDAR_EVENT(location)})})
same => n,Dial(SIP/${DeviceToDial},30,M(CheckConfirm))
same => n,Hangup()
exten => hangup,1,Verbose(2,Call was rejected)
same => n,Playback(vm-goodbye)
same => n,Hangup()
[macro-CheckConfirm]
exten => s,1,Verbose(2,Allowing called party to accept or reject)
same => n,Playback(/tmp/meeting-invite-${RandomNumber})
; *** This line should not have any line breaks
same => n,Read(CheckMeetingAcceptance,to-confirm-wakeup&press-1&otherwise
&press-2,,1)
same => n,GotoIf($["${CheckMeetingAcceptance}" != "1"]?hangup,1)
exten => hangup,1,Verbose(2,Call was rejected by called party)
same => n,Playback(vm-goodbye)
same => n,Hangup()We hope you’ll be able to use this simple dialplan example as a jumping-off point. With a little creativity and some dialplan skills, the possibilities are endless!
Calling meeting participants and placing them into a conference
To expand upon the functionality in the previous section, we’re going to delve into the logic problem of how you might be able to place multiple participants into a meeting. Our goal is to use our calendar to call us when the meeting is scheduled to start, and then, when we answer, to place calls to all the other members of the conference. As the other participants answer their phones, they will be placed into a virtual conference room, where they will wait for the meeting organizer to join. After all participants have been dialed and answered (or perhaps not answered), the organizer will be placed into the call, at which point the meeting will start.
This type of functionality increases the likelihood that the meeting will start on time, and it means the meeting organizer doesn’t have to continually perform roll call as new participants continue to join after the call is supposed to start (which invariably happens, with people’s schedules typically being fairly busy).
The dialplan we’re going to show
you isn’t necessarily a polished, production-ready installation (for
example, the data returned from the calendar comes from the
description field, only deals with device names, and assumes the
technology is SIP). However, we’ve done the hard work for you by
developing the Local channel usage, along with the M() flag (macro) usage with Dial(). With some testing and tweaks this
code could certainly be developed more fully for your particular
installation, but we’ve kept it general to allow for it to be usable
for more people in more situations. The example dialplan looks like
this:
[AutomatedMeetingSetup]
exten => start,1,Verbose(2,Calling multiple people and placing into a conference)
; Get information from calendar and save that information. Prefix
; CalLocation with an underscore so it is available to the Local
; channel (variable inheritance).
;
same => n,Set(CalDescription=${CALENDAR_EVENT(description)})
same => n,Set(_CalLocation=${CALENDAR_EVENT(location)})
same => n,Set(X=1)
; Our separator is a caret (^), so the description should be in the
; format of: 0000FFFF0001^0000FFFF0002^etc...
;
same => n,Set(EndPoint=${CUT(CalDescription,^,${X})})
; This loop is used to build the ${ToDial} variable, which contains
; a list of Local channels to be dialed, thereby triggering the multiple
; Originate() actions simultaneously instead of linearly
;
same => n,While($[${EXISTS(${EndPoint})}])
; This statement must be on a single line
same => n,Set(ToDial=${IF($[${ISNULL(${ToDial})}]?
:${ToDial}&)}Local/${EndPoint}@MeetingOriginator)
same => n,Set(X=$[${X} + 1])
same => n,Set(EndPoint=${CUT(CalDescription,^,${X})})
same => n,EndWhile()
; If no values are passed back, then don't bother dialing
same => n,GotoIf($[${ISNULL(${ToDial})}]?hangup)
same => n,Dial(${ToDial})
; After our Dial() statement returns, we should be placed into
; the conference room. We are marked, so the conference can start
; (which is indicated by the 'A' flag to MeetMe).
;
same => n,MeetMe(${CalLocation},dA)
same => n(hangup),Hangup()
[MeetingOriginator]
exten => _[A-Za-z0-9].,1,NoOp()
same => n,Set(Peer=${FILTER(A-Za-z0-9,${EXTEN})})
; Originate calls to a peer as passed to us from the Local channel. Upon
; answer, the called party should execute the dialplan located at the
; _meetme-XXXX extension, where XXXX is the conference room number.
;
same => n,Originate(SIP/${Peer},exten,MeetingOriginator,meetme-${CalLocation},1)
same => n,Hangup()
; Join the meeting; using the 'w' flag, which means 'wait for marked
; user to join before starting'
;
exten => _meetme-XXXX,1,Verbose(2,Joining a meeting)
same => n,Answer()
same => n,MeetMe(${EXTEN:7},dw)
same => n,Hangup()Controlling Calls Based on Calendar Information
Sometimes it is useful to redirect calls automatically—for
example, when you’re in a meeting, or on vacation. In this section we’ll
be making use of the CALENDAR_BUSY()
dialplan function, which allows us to check the current status of our
calendar to determine if we’re busy or not. A simple example of this
would be to send all calls to voicemail using the busy message whenever
an event that marks us as busy has been scheduled.
The following dialplan shows a simple example where we check our calendar for busy status prior to sending a call to a device. Notice that a lot of the information in this example is static, and additional effort would be required to make it dynamic and suitable for production:
exten => 3000,1,Verbose(2,Simple calendar busy check example)
same => n,Set(CurrentExten=${EXTEN})
same => n,Set(CalendarBusy=${CALENDAR_BUSY(myGoogleCal)})
same => n,GotoIf($["${CalendarBusy}" = "1"]?voicemail,1)
same => n,Dial(SIP/0000FFFF0002,30)
same => n,Goto(voicemail,1)
exten => voicemail,1,Verbose(2,Caller sent to voicemail)
; *** This line should not have any line breaks
same => n,GotoIf($["${DIALSTATUS}" = "BUSY" |
"${CalendarBusy}" = "1"]?busy:unavail)
same => n(busy),VoiceMail(${CurrentExten}@shifteight,b)
same => n,Hangup()
same => n(unavail),VoiceMail(${CurrentExten}@shifteight,u)
same => n,Hangup()And here is a slightly more elaborate
section of dialplan that utilizes a few of the tools we’ve learned
throughout the book, including DB_EXISTS(), GotoIf(), and the IF() function:
exten => _3XXX,1,Verbose(2,Simple calendar busy check example)
same => n,Set(CurrentExten=${EXTEN})
same => n,GotoIf($[${DB_EXISTS(extension/${CurrentExten}/device)}]?:no_device,1)
same => n,Set(CurrentDevice=${DB_RESULT})
same => n,GotoIf($[${DB_EXISTS(extension/${CurrentExten}/calendar)}]?:no_calendar)
same => n,Set(CalendarBusy=${CALENDAR_BUSY(${DB_RESULT})})
same => n,GotoIf($[${CalendarBusy}]?voicemail,1)
same => n(no_calendar),Verbose(2,No calendar was found for this user)
same => n,Dial(SIP/${CurrentDevice},30)
same => n,Goto(voicemail,1)
exten => voicemail,1,Verbose(2,Sending caller to voicemail)
; *** This line should not have any line breaks
same => n,GotoIf($[${DB_EXISTS(extension/${CurrentExten}/voicemail_context)}]
?:no_voicemail)
same => n,Set(VoiceMailContext=${DB_RESULT})
; *** This line should not have any line breaks
same => n,Set(VoiceMailStatus=${IF($["${DIALSTATUS}" = "BUSY" |
0${CalendarBusy}]?b:u)})
same => n,VoiceMail(${CurrentExten}@${VoiceMailContext},${VoiceMailStatus})
same => n,Hangup()
same => n(no_voicemail),Playback(number-not-answering)
same => n,Hangup()
exten => no_device,1,Verbose(2,No device found in the DB)
same => n,Playback(invalid)
same => n,Hangup()Writing Call Information to a Calendar
Using the CALENDAR_WRITE() function opens some other
possibilities in terms of calendar integration. From the Asterisk
dialplan, we can insert information into a calendar, which can be
consumed by other devices and applications. Our next example is a
calendar that tracks call logs. For anyone who may be on the phone a
fair amount who needs to track time for clients, writing all calls to a
calendar for a visual reference can be useful when verifying things at
the end of the day.
We’re going to utilize the Google
web calendar again for this example, but we’re going to create a new,
separate calendar just for tracking calls. In order to write to the
calendar, we’ll need to set up our calendar.conf file a little bit differently, by using the CalDAV
calendar format. First, though, we need to create our new
calendar.
On the left side of the Google calendar interface will be a link labeled Add. Clicking on this will open a new window where you can create the calendar. Go ahead and do that now. We’ve called ours “Phone Calls.”
Now we need to enable CalDAV calendar syncing for our calendar. Information about how to do this is located at http://www.google.com/support/mobile/bin/answer.py?answer=151674. This page notes that only your primary calendar will be synced to the device, but we want to make sure our calls are logged to a separate calendar so we can easily hide them (and so our smartphone doesn’t synchronize the phone’s calls either, which may cause confusion). There are two links near the bottom of the page: one for regular Google calendar users, and the other for Google Apps users. Select the appropriate link and open it. You will then be presented with a page that contains your calendars. Select the Phone Calls calendar and then select Save.
Next up is configuring our
calendar.conf file for Asterisk.
One of the parameters we need is the link to the CalDAV calendar. There
is a Calendar ID value that we need that will identify our calendar
specifically. To find the calendar ID, click the down arrow beside the
calendar name on the lefthand side of the calendar page and select
Calendar Settings. Near the bottom of the calendar
settings will be two rows that contain the icons for sharing the
calendar (XML, ICAL, HTML). Beside the first set of icons inside the
Calendar Address box will be the calendar ID. It
will look like this:
(Calendar ID: 2hfb6p5974gds924j61cmg4gfd@group.calendar.google.com)
If you’re setting this up via
Google Apps, the calendar ID will be prefixed with your domain name and
an underscore (e.g., shifteight.org_). Make a note of this string,
as we’re going to use it next.
Open up the calendar.conf file and add a new calendar
section. In our case we’ve called it [phone_call_calendar]. You’ll recognize the
formatting of the calendar from earlier, so we won’t go through all the
settings here. The key setting to note is the url parameter. The format of this parameter
is:
https://www.google.com/calendar/dav/<calendar_id>/events/We
need to replace the <calendar_id> with
the calendar ID we recently made a note of. The full configuration then
ends up looking like so:
[phone_call_calendar]
type=caldav
; The URL must be on a single line
url=https://www.google.com/calendar/dav/
shifteight.org_2hfb6p5974gds924j61cmg4gfd@group.calendar.google.com/events/
user = leif@shifteight.org
secret = my_secret_password
refresh=15
timeframe=120
Now that we have our calendar configured, we need to load it
into memory, which can be done by reloading the res_calendar.so module:
*CLI>module reload res_calendar.so
Verify that the calendar has been loaded into memory successfully with the calendar show command:
*CLI>calendar show calendarsCalendar Type Status -------- ---- ------ phone_call_calendar caldav free
With our calendar successfully
loaded into memory, we can write some dialplan around our Dial() command to save our call information to
the calendar with the CALENDAR_WRITE() function:
[LocalSets]
exten => _NXXNXXXXXX,1,Verbose(2,Outbound calls)
same => n,Set(CalendarStart=${EPOCH}) ; Used by CALENDAR_WRITE()
same => n,Set(X=${EXTEN}) ; Used by CALENDAR_WRITE()
same => n,Dial(SIP/ITSP/${EXTEN},30)
same => n,NoOp(Handle standard voicemail stuff here)
same => n,Hangup()
exten => h,1,Verbose(2,Call cleanup)
; Everything that follows must be on a single line
same => n,Set(CALENDAR_WRITE(phone_call_calendar,summary,description,start,end)=
OUTBOUND: ${X},Phone call to ${X} lasted for ${CDR(billsec)} seconds.,
${CalendarStart},${EPOCH})In our dialplan we’ve created
a simple scenario where we place an outbound call through our Internet
Telephony Service Provider (ITSP), but prior to placing the call, we
save the epoch[157] to a channel variable (so we can use it later when we
write our calendar entry at the end of the call). After our call, we
write our calendar entry to the phone_call_calendar with the CALENDAR_WRITE() dialplan function within the
built-in h extension. There are
several options we can pass to the calendar, such as the summary,
description, and start and end times. All of this information is then
saved to the calendar.
We’ve also used the CDR() dialplan function in our description to show the number of seconds the
answered portion of the call lasted for, so we can get a more accurate
assessment of whether a call was answered and, if so, how long the
answered portion lasted for. We could also be clever and only write to
the calendar if ${CDR(billsec)} was
greater than 0 by wrapping the Set()
application in an ExecIf(); e.g.,
same => n,ExecIf($[${CDR(billsec)} >
0]?Set(CALENDAR_WRITE...)).
Many possibilities exist for the
CALENDAR_WRITE() function; this is
just one that we’ve implemented and enjoy.
Conclusion
In this section we’ve learned how
to integrate Asterisk with an external calendar server such as that
provided by Google, but the concepts for attaching to other calendaring
servers remain the same. We explored how to set up a meeting between two
participants, and how to set up a multiparty conference using
information obtained from the description field in the calendar. We also
looked at how to control calls using the CALENDAR_BUSY() function, to redirect calls to voicemail when our current event
describes us as busy. By implementing this type of functionality in
Asterisk, you can see the power we have to control call flow using
external services such as those supplied by a calendar server.
And we didn’t even get to dive
into every use of the calendar implementation—there exist other calendar
functions, such as CALENDAR_QUERY(), which allows you to pull back a list of events within a
given time period for a particular calendar, and CALENDAR_QUERY_RESULT(), which allows you to access the specifics of those
calendar events. Additionally, you could create functionality that
writes events into your calendar with the CALENDAR_WRITE() function: for example, you
may wish to develop some dialplan that allows you to set aside blocks of
times in your calendar from your phone when you’re on the road without
access to your laptop. Many possibilities exist, and all it takes is a
little creativity.
VoiceMail IMAP Integration
“Unified messaging” has been a buzzword in the telecommunications industry for ages. It’s all about integrating services together so users can access the same types of data in multiple locations, using different methods. One of the most touted applications is the integration of email and voicemail. Asterisk has been doing this for years, but many of the larger companies are still trying to get this right. Asterisk has had the ability to send users voicemails via email, using the Mail Transport Agent (MTA) in your Linux distro (this always used to be sendmail, but Postfix has become increasingly popular as an MTA). Voicemail to email is one of the oldest features in Asterisk, and it normally works without any configuration at all.[158]
Internet Message Access Protocol
(IMAP) integration has existed in Asterisk (and been steadily evolving)
since version 1.4. IMAP voicemail integration means your users can access
their voicemails via a folder within their email accounts, which gives
them the ability to listen to, forward, and mark voicemail messages with
the same flexibility that the Asterisk VoiceMail() dialplan application gives. Asterisk
will be aware of the statuses of those messages when the users next log in
via the phone system.
As the number of administrators integrating Asterisk with their IMAP servers has increased, the number of bugs filed and fixed has first increased and then decreased, to the point where IMAP integration can be considered stable enough for production use. In this section we’ll discuss how to compile in IMAP voicemail support and connect your voicemail system to your IMAP server.
Compiling IMAP VoiceMail Support into Asterisk
To get IMAP voicemail support into Asterisk, we need to compile the University of Washington’s IMAP library. The UW IMAP toolkit will give us the functionality in Asterisk to connect to our IMAP server. Before compiling the software, though, we need to install some dependencies.
The dependencies for building the IMAP library include the tools required to build Asterisk, but the way we’re building it also requires the development libraries for OpenSSL and Pluggable Authentication Modules (PAM). We’ve included instructions for both CentOS and Ubuntu.
CentOS dependencies
Installing both the OpenSSL and PAM development libraries on CentOS can be done with the following command:
$sudo yum install openssl-devel pam-devel
Note
Remember to add .x86_64 to the name of each package if installing on a 64-bit machine.
Ubuntu dependencies
Installing both the OpenSSL and PAM development libraries on Ubuntu can be done with the following command:
$sudo apt-get install libssl-dev libpam0g-dev
Note
If you try to install
libpam-dev on Ubuntu, it will
warn you that libpam-dev is a
virtual package and that you should explicitly install one of the
packages in the list it presents you with (which in our case
contained only a single package). If libpam0g-dev is not the correct package
on your version of Ubuntu, try installing the virtual package.
This should give you a list of valid packages for the PAM
development library.
Compiling the IMAP library
Now that we have our dependencies satisfied, we can compile the IMAP library that Asterisk will use to connect to our IMAP server.
The first thing to do is change
to the thirdparty directory
located under the asterisk-complete directory. If you have
not already created this directory, do so now:
$cd ~/src/asterisk-complete$mkdir thirdparty$cd thirdparty
Next up is downloading the IMAP toolkit and compiling it. The next steps will get the latest version of the IMAP toolkit from the University of Washington’s server (more information about the toolkit is available at http://www.washington.edu/imap/):
$wget ftp://ftp.cac.washington.edu/mail/imap.tar.Z$tar zxvf imap.tar.Z$cd imap-2007e
Note
The directory name imap-2007e may change as new versions
of the toolkit become available.
There are a few options we need to pass to the make command when building the IMAP library, and the values you should pass will depend on what platform you’re building on (32-bit vs. 64-bit), if you need OpenSSL support, and whether you need IPv6 support or just IPv4. Table 18.1, “IMAP library compile time options” shows some of the various options you could pass on different platforms.
Table 18.1. IMAP library compile time options
| Option | Description |
|---|---|
EXTRACFLAGS="-fPIC" | Required when building on 64-bit platforms. |
EXTRACFLAGS="-I/usr/include/openssl" | Used for building in OpenSSL support. |
IP6=4 | Many platforms that support IPv6 prefer that method of connection, which may not be desirable for all servers. If you would like to force IPv4 as the preferred connection method, set this option. |
If you look in the Makefile shipped with the IMAP library, you
will find a list of platforms for which the library can be compiled.
In our case, we’ll be compiling for either CentOS or Ubuntu with PAM
support. If you’re compiling on other systems, take a look in the
Makefile for the three-letter code that tells the
library how to compile for your platform.
To compile for a 64-bit platform with OpenSSL support and a preference for connecting via IPv4:
$make lnp EXTRACFLAGS="-fPIC -I/usr/include/openssl" IP6=4
To compile for a 32-bit platform with OpenSSL support and a preference for connecting via IPv4:
$make lnp EXTRACFLAGS="-I/usr/include/openssl" IP6=4
If
you don’t wish to compile with OpenSSL support, simply remove the
-I/usr/include/openssl from the
EXTRACFLAGS option. If you would
prefer connecting by IPv6 by default, simply don’t specify the
IP6=4 option.
Note
When installing IMAP support,
we have always compiled the c-client library from source. However,
it may be available as a package for your distribution. For example,
Ubuntu has a libc-client-dev
package available. It may work and save you some trouble, but we
have not tested it.
Compiling Asterisk
After compiling the IMAP library,
we need to recompile the app_voicemail.so module with IMAP support.
The first step is to run the configure script and pass it the
--with-imap option to tell it where the IMAP
library exists:
$cd ~/src/asterisk-complete/asterisk/1.8.0$./configure--with-imap=~/src/asterisk-complete/thirdparty/imap-2007e/
Once the configure script has finished executing, we need to enable IMAP voicemail support in menuselect:
$ make menuselectFrom
the menuselect interface, go to
Voicemail Build Options. Within that menu, you
should have the option to select IMAP_STORAGE.
Tip
If you don’t have the ability
to select that option, check to make sure your IMAP library was
built successfully (i.e., that you have all the required
dependencies installed and that it didn’t error out when building)
and that you correctly specified the path to the IMAP library when
running the configure script. You
can also verify that the IMAP library was found correctly by looking
in the config.log file (located
in your Asterisk build directory) for IMAP.
After selecting IMAP_STORAGE, save and exit from menuselect and run make
install, which will recompile the app_voicemail.so module and install it to
the appropriate location. The next step is to configure the voicemail.conf file located in /etc/asterisk/.
Configuring Asterisk
Now that we’ve compiled IMAP support into Asterisk, we need to enable it by connecting to an IMAP-enabled server. There are many IMAP servers that you could use, including those supplied with Microsoft servers, Dovecot, and Cyrus on Unix, or a web-based IMAP server such as that supplied by Google’s Gmail.[159] Our instructions are going to show how to connect Asterisk to a Gmail account with IMAP enabled, as it requires the least amount of effort to get up and running with IMAP voicemail, but these instructions can easily be adapted for use with any existing IMAP server.
Enabling IMAP on your Gmail account
Enabling IMAP support on your Gmail account is straightforward (see Figure 18.1, “Enabling Gmail IMAP”). Once logged into your account, select Settings from the upper-right corner. Then select Forwarding and POP/IMAP support from the task bar under the Settings header. In the IMAP Access section, enable IMAP support by selecting Enable IMAP. After enabling it, click the Save Changes button at the bottom of the screen.
Configuring voicemail.conf for IMAP
To enable our voicemail system to connect to an IMAP
system, we need to make sure IMAP support has been built into the
app_voicemail.so module per the
instructions in the section called “Compiling Asterisk”.
With IMAP support compiled into Asterisk, we just need to instruct
the voicemail module how to connect to our IMAP server.
We’re going to demonstrate how
to connect to an IMAP-enabled Gmail account and use that to store
and retrieve our voicemail messages. If you haven’t already, read
the section before proceeding. The final step is
configuring voicemail.conf to
connect to the server.
In
voicemail.conf, add the following lines to the
[general] section. Be sure you
only specify a single format (we
recommend wav49) for voicemail
recordings, and remove any references to ODBC voicemail storage if
you’ve enabled that previously:
[general] format=wav49 ; format to store files imapserver=imap.gmail.com ; IMAP server location imapport=993 ; port IMAP server listens to imapflags=ssl ; flags required for connecting expungeonhangup=yes ; delete messages on hangup pollmailboxes=yes ; used for message waiting indication pollfreq=30 ; how often to check for message changes
Before
we configure our user for connecting to the Gmail IMAP server, let’s
discuss the options we’ve just set in the [general] section. These are the basic
options that will get us started; we’ll do some more customization
shortly, but let’s see what we’ve done so far.
First, the format=wav49 option has declared that
we’re going to save our files as GSM with a WAV header, which can be
played on most desktops (including Microsoft Windows) while
retaining a small file size.
Next, we’ve configured Asterisk
to connect to an imapserver
located at imap.gmail.com on
imapport 993. We’ve also set imapflags to ssl, as Gmail
requires a secure connection. Without the ssl IMAP flag being set, the server will
reject our connection attempts (which is why it was important that
we compiled our IMAP library with OpenSSL support). Another option
that may be required on private IMAP servers such as Dovecot is to
specify novalidate-cert for
imapflags when an SSL connection
is necessary, but the certificate is not generated by a certificate
authority.
Next, we’ve set expungeonhangup=yes, which causes messages marked for
deletion to be removed from the server upon hangup from the VoiceMail() application. Without this
option, messages are simply marked as read and left on the server
until they have been removed via an email application or web
interface.
In order to get message waiting
indication (MWI) updates correctly, we need to enable pollmailboxes=yes, which causes Asterisk to check with the
server for any changes to the status of a message. For example, when
someone leaves us a voicemail and we listen to it by opening the
message via our email application, the message will be marked as
read, but without polling the mailbox Asterisk will have no way of
knowing this and will enable the MWI light on the associated device
indefinitely. Finally, we’ve set the related option pollfreq to 30 seconds.
This option controls how often Asterisk will ask the server for the
status of messages: set it appropriately to control the amount of
traffic going to the voicemail server.
Table 18.2, “Additional IMAP voicemail options” shows some of the other options available to us.
Table 18.2. Additional IMAP voicemail options
| Option | Description |
|---|---|
imapfolder | Provides the name of the folder in which to store voicemail messages on your IMAP server. By default they are stored in the INBOX.[a] |
imapgreetings | Defines whether voicemail greetings are stored
on the IMAP server or stored locally on the server. Valid
values are yes or
no. |
imapparentfolder | Defines the parent folder on the IMAP server.
Usually this configured as INBOX on the server, but if it is
called something else, you can specify it here. |
greetingfolder | Specifies the folder in which to save the
voicemail greetings, if you’ve enabled the imapgreetings option by setting it
to yes. By default
greetings are saved in the INBOX. |
authuser | Specifies the master user to use for connecting to your IMAP server, if the server is configured with a single user that has access to all mailboxes. |
authpassword | Complement to the authuser directive. See authuser for more
information. |
opentimeout | Specifies the TCP open timeout (in seconds). |
closetimeout | Specifies the TCP close timeout (in seconds). |
readtimeout | Specifies the TCP read timeout (in seconds). |
writetimeout | Specifies the TCP write timeout (in seconds). |
[a] It is important to store your voicemail messages in a folder other than the INBOX if the number of messages contained in the INBOX could be rather large. Asterisk will try to gather information about all the emails contained in the INBOX, and could either time out before retrieving all the information or just take a very long time to store or retrieve voicemail messages, which is not desirable. | |
With our [general] section configured, let’s define
a mailbox for connecting to the IMAP server.
In Chapter 8, Voicemail we defined some users in the
[shifteight] voicemail context.
Here is the original configuration as defined in that
chapter:
[shifteight] 100 => 0107,Leif Madsen,leif@shifteight.org 101 => 0523,Jim VanMeggelen,jim@shifteight.org,,attach=no|maxmsg=100 102 => 11042,Tilghman Lesher,,,attach=no|tz=central
We’re going to modify mailbox 100 in such a way that it connects to the Gmail IMAP server to store and retrieve voicemail messages:
[shifteight] 100 => 0107,Leif Madsen,,,|imapuser=leif@shifteight.org|imappassword=secret
Note
The voicemail.conf file uses both commas
and pipes as separators, depending on which field
is being used. The first few fields have specific settings in
them, and the last field can contain extra information about the
mailbox, which is separated by the pipe character (|).
We’ve removed the email address from the third field
because we’re not going to use sendmail to
email us voicemails anymore: they are just going to be stored on the
email server directly now. We’ve configured the mailbox to connect
with the IMAP username of leif@shifteight.org (because we’ve enabled
Google Apps for the domain that hosts our email) and are connecting
using the IMAP password secret.
After configuring Asterisk, we
need to reload the app_voicemail.so module. If you enable
console debugging, you should see output similar to the following
upon connection to the voicemail server:
*CLI>core set debug 10*CLI>module reload app_voicemail.soDEBUG[3293]: app_voicemail.c:2734 mm_log: IMAP Info: Trying IP address [74.125.53.109] DEBUG[3293]: app_voicemail.c:2734 mm_log: IMAP Info: Gimap ready for requests from 99.228.XXX.XXX 13if2973206wfc.0 DEBUG[3293]: app_voicemail.c:2757 mm_login: Entering callback mm_login DEBUG[3293]: app_voicemail.c:2650 mm_exists: Entering EXISTS callback for message 7 DEBUG[3293]: app_voicemail.c:3074 set_update: User leif@shifteight.org mailbox set for update. DEBUG[3293]: app_voicemail.c:2510 init_mailstream: Before mail_open, server: {imap.gmail.com:993/imap/ssl/user=leif@shifteight.org}INBOX, box:0 DEBUG[3293]: app_voicemail.c:2734 mm_log: IMAP Info: Reusing connection to gmail-imap.l.google.com/user="leif@shifteight.org"
If you get any ERRORs, check your
configuration and verify that the IMAP library is compiled with SSL
support. Once app_voicemail.so
is connected, try leaving yourself a voicemail; then check your
voicemail via the Gmail web interface and verify that your message
is stored correctly. You should also have an MWI light on your
device if it supports it, and if you’ve configured mailbox=100@shifteight for the device in
sip.conf. If you load the
voicemail message envelope and mark it as read, the MWI light should
turn off within 30 seconds (or whatever value you set pollfreq to in voicemail.conf).
Using XMPP (Jabber) with Asterisk
The eXtensible Messaging and Presence Protocol (XMPP)
(formerly called Jabber) is used for instant messaging and communicating
presence information across networks in near-realtime. Within Asterisk, it
is also used for call setup (signaling). There are various cool things we
can do with XMPP integration once it’s enabled, such as getting a message
whenever someone calls us. We can even send messages back to Asterisk,
redirecting our calls to voicemail or some other location. Additionally,
with chan_gtalk, we can accept and
place calls over the Google Voice network or accept calls from Google Talk
users via the web client.
Compiling Jabber Support into Asterisk
The res_jabber module
contains various dialplan applications and functions that are useful
from the Asterisk dialplan. It is also a dependency of the chan_gtalk and chan_jingle channel modules. To get started
with XMPP integration in Asterisk, we need to compile res_jabber.
CentOS dependencies
To install res_jabber, we need the iksemel development library (http://code.google.com/p/iksemel/). If the OpenSSL
development library is installed, res_jabber will also utilize that for secure
connections (this is recommended). We can install both on CentOS with
the following command:
$ sudo yum install iksemel-devel openssl-develNote
As always, be sure to append
.x86_64 to the module names if
installing on a 64-bit machine.
Ubuntu dependencies
To install res_jabber, we need the iksemel development library. If the OpenSSL
development library is installed, res_jabber will also utilize that for secure
connections (this is recommended). We can install both on Ubuntu with
the following command:
$ sudo apt-get install libiksemel-dev libssl-devJabber Dialplan Commands
Several dialplan applications and functions can be used for communication using the XMPP protocol via Asterisk. We’re going to explore how to connect Asterisk to an XMPP server, how to send messages to the client from the dialplan, and how to route calls based on responses to the initially sent messages. By sending a message via XMPP, we’re essentially creating a simple screen pop application to let users know when calls are coming to the system.
Connecting to an XMPP server
Before we can start sending messages to our XMPP
buddies, we need to connect to an XMPP-enabled server. We’re going to
utilize the XMPP server at Google, as it is open and easily accessible
by anyone. To do so, we need to configure the jabber.conf file in our /etc/asterisk/ configuration directory. The
following example will connect us to the XMPP server at Google.
Note
You must already have a Gmail account, which you can get at http://www.gmail.com.
Our jabber.conf file should look like
this:
[general] debug=no autoprune=no autoregister=yes auth_policy=accept [asterisk] type=client serverhost=talk.google.comusername=asterisk@shifteight.orgsecret=<super_secret_password>port=5222 usetls=yes usesasl=yes status=available statusmessage="Ohai from Asterisk"
Let’s take a quick look at
some of the options we just set so you understand what is going on.
The options are described in Table 18.3, “jabber.conf options”. Note that the first four
options are set in the [general]
section, and the others are set in the peer section.
Table 18.3. jabber.conf options
After configuring our jabber.conf file, we can load (or reload)
the res_jabber.so module. We can
do this from the console with jabber
reload:
*CLI> jabber reload
Jabber Reloaded.and check the connection with the jabber show connections command:
*CLI>jabber show connectionsJabber Users and their status: User: asterisk@shifteight.org - Connected ---- Number of users: 1
If you’re having problems getting connected, you can try unloading the module and then loading it back into memory. If you’re still having problems, you can run the jabber purge nodes command to remove any existing or bad connections from memory. Beyond that, check your configuration and verify that you don’t have any configuration problems or typos. Once you’ve gotten connected, you can move on to the next sections, where the fun starts.
Sending messages with JabberSend()
The JabberSend()
dialplan application is used for sending messages to buddies from the
Asterisk dialplan. You can use this application in any place that you
would normally utilize the dialplan, which makes it quite flexible.
We’re going to use it as a screen pop application for sending a
message to a client prior to placing a call to the user’s phone.
Depending on the client used, you may be able to have the message pop
up on the user’s screen from the task bar.
Here is a simple example to get us started:
[LocalSets]
exten => 104,1,Answer()
; *** This line should not have any line breaks
same => n,JabberSend(asterisk,jim@shifteight.org,Incoming call from
${CALLERID(all)})
same => n,Dial(SIP/0000FFFF0002,30)
same => n,Hangup()This example demonstrates how to
use the JabberSend() application to
send a message to someone prior to dialing a device. Let’s break down
the values we’ve used. The first argument, asterisk, is the section header we defined
in the jabber.conf file as
[asterisk]. In
our jabber.conf example, we set
up a user called asterisk@shifteight.org to send
messages via the Google XMPP server, and asterisk is the section name we defined. The
second argument, jim@shifteight.org, is the buddy we’re
sending the message to. We can define any buddy here, either as a bare
JID (as we’ve done above) or as a full JID with a resource (e.g.,
jim@shifteight.org/laptop). The third argument to
JabberSend() is the message
we want to send to the buddy. In this case we’re sending Incoming call from ${CALLERID(all)}, with
the CALLERID() dialplan function
being used to enter the caller ID information in the message.
Obviously, we would have to
further build out our dialplan to make this useful: specifically, we’d
have to associate the buddy name (e.g., jim@shifteight.org) with the device we’re
calling (SIP/0000FFFF0002) so that
we’re sending the message to the correct buddy. You can save these
associations in any one of several locations, such as the in AstDB, in
a relational database retrieved with func_odbc, or even in a global
variable.
Receiving messages with JABBER_RECEIVE()
The JABBER_RECEIVE()
dialplan function allows us to receive responses via XMPP messages,
capture those responses, and presumably act on them. We would
typically use the JABBER_RECEIVE()
function in conjunction with the JabberSend() dialplan application, as we are
likely to need to send a message to someone and prompt him with the
acceptable values he can return. We could use the JABBER_RECEIVE() function either personally,
to direct calls to a particular device such as a cell phone or desk
phone, or as a text version of an auto attendant to be used when
people who are likely to have difficulty hearing the prompts dial in
(e.g., users who are deaf or work at noisy job sites). In the latter
case, the system would have to be preconfigured to know where to send
the messages to, perhaps based on the caller ID of the person
calling.
Here is a simple example that sends a message to someone, waits for a response, and then routes the call based on the response:
exten => 106,1,Answer()
; All text must be on a single line.
same => n,JabberSend(asterisk,leif.madsen@gmail.com,Incoming call from
${CALLERID(all)}. Press 1 to route to desk. Press 2 to send to voicemail.)
same => n,Set(JabberResponse=${JABBER_RECEIVE(asterisk,leif@shifteight.org)})
same => n,GotoIf($["${JabberResponse}" = "1"]?dial,1)
same => n,GotoIf($["${JabberResponse}" = "2"]?voicemail,1)
same => n,Goto(dial,1)
exten => dial,1,Verbose(2,Calling our desk)
same => n,Dial(SIP/0000FFFF0002,6)
same => n,Goto(voicemail,1)
exten => voicemail,1,Verbose(2,VoiceMail)
; *** This line should not have any line breaks
same => n,Set(VoiceMailStatus=${IF($[${ISNULL(${DIALSTATUS})}
| "${DIALSTATUS}" = "BUSY"]?b:u)})
same => n,Playback(silence/1)
same => n,VoiceMail(100@lmentinc,${VoiceMailStatus})
same => n,Hangup()
Tip
Unfortunately, the JabberSend() application requires all of
the message to be sent on a single line. If you wish to break up
the text onto multiple lines, you will need to send it as multiple
messages on separate lines using JabberSend().
Our simple dialplan first sends a message to a Jabber account
(leif@shifteight.org) via our systems’ Jabber
account (asterisk), as configured
in jabber.conf. We then use the
JABBER_RECEIVE() dialplan function
to wait for a response from leif@shifteight.org. The default timeout is
5 seconds, but you can specify a different timeout with a third
argument to JABBER_RECEIVE(). For
example, to wait 10 seconds for a response, we could have used a line
like this:
Set(JabberResponse=${JABBER_RECEIVE(asterisk,leif@shifteight.org,10)})Once we’ve either received a
response or the timeout has expired, we move on to the next line of
the dialplan, which starts checking the response saved to the ${JabberResponse} channel
variable. If the value is 1, we
continue our dialplan at dial,1 of
the current context. If the response is 2, we continue our dialplan at voicemail,1. If no response (or an unknown
response) is received, we continue the dialplan at dial,1.
The dialplan at dial,1 and voicemail,1 should be fairly self-evident.
This is a non-production example; some additional dialplan should be
implemented to make the values dynamic.
There is a disadvantage to the
way we’ve implemented the JABBER_RECEIVE() function, though. Our
function blocks, or waits, for a response from the endpoint. If we set
the response value low to minimize delay, we don’t give the user we
sent the message to much time to respond. However, if we set the
response long enough to make it comfortable for the user to send a
response, we cause unnecessary delay in calling a device or sending to
voicemail.
We can skirt around this issue by
using a Local channel. This allows us to execute two sections of
dialplan simultaneously, sending a call to the device at the same time
we’re waiting for a response from JABBER_RECEIVE(). If we get a response from
JABBER_RECEIVE() and we need to do
something, we can Answer() the line
and cause that section of dialplan to continue. If the device answers
the phone, our dialplan with JABBER_RECEIVE() will just be hung up. Let’s
take a look at a modified dialplan that implements the Local
channel:
exten => 106,1,Verbose(2,Example using the Local channel)
same => n,Dial(Local/jabber@${CONTEXT}/n&Local/dial@${CONTEXT}/n)
exten => jabber,1,Verbose(2,Send an XMPP message and expect a response)
; *** This line should not have any line breaks
same => n,JabberSend(asterisk,leif.madsen@gmail.com,Incoming call from
${CALLERID(all)}. Press 2 to send to voicemail.)
same => n,Set(JabberResponse=${JABBER_RECEIVE(asterisk,leif@shifteight.org,6)})
same => n,GotoIf($["${JabberResponse}" = "2"]?voicemail,1)
same => n,Hangup()
exten => dial,1,Verbose(2,Calling our desk)
same => n,Dial(SIP/0000FFFF0002,15)
same => n,Goto(voicemail,1)
exten => voicemail,1,Verbose(2,VoiceMail)
same => n,Answer()
; *** This line should not have any line breaks
same => n,Set(VoiceMailStatus=${IF($[${ISNULL(${DIALSTATUS})}
| "${DIALSTATUS}" = "BUSY"]?b:u)})
same => n,Playback(silence/1)
same => n,VoiceMail(100@lmentinc,${VoiceMailStatus})
same => n,Hangup()
By adding a Dial()
statement at the beginning and shifting our Jabber send and receive
functionality into a new extension called jabber, we ensure that we can simultaneously
call the dial extension and the
jabber extension.
Notice that we removed the
Answer() application from the first
line of the example. The reason for this is because we want to
Answer() the line only after a
device has answered (which causes the jabber extension to be hung up); otherwise,
we want the voicemail extension to
Answer() the line. If the voicemail extension has answered the line,
that means either the jabber
extension has received a response and was told to Goto() the voicemail extension, or the Dial() to our device timed out, causing the
voicemail extension to be executed,
thereby causing the line to be Answer()ed.
With the examples provided here
serving as a springboard, you should be able to develop rich
applications that make use of sending and receiving messages via XMPP
servers. Some other dialplan applications and functions exist that may
help in the development of your application, such as JABBER_STATUS() (or the JabberStatus() dialplan application), which
is used for checking on the status of a buddy; the JabberJoin() and JabberLeave() applications, which are used
for joining and leaving XMPP conference rooms; and the JabberSendGroup() application, which allows
you to send messages to an XMPP chat room.
chan_gtalk
The chan_gtalk module
can be used for connecting to Google Talk (GTalk) clients or for sending
and receiving calls via the Google Voice network, which is a PSTN-connected network where you can purchase
minutes just like you would from any other ITSP. GTalk is the web-based
voice system typically found in GMail web interfaces. Other clients and
addons do exist for external applications such as Pidgin, but we’ll be
testing with the web-based client from Google.
Note
As of the beginning of 2011, the Google Voice system can only be used in the US.
Before we can get connected to
chan_gtalk, we need to make sure
we’re connected via res_jabber, so if
you haven’t already done so, review the section called “Connecting to an XMPP server” for information about how to
connect to the Google XMPP servers.
Configuring gtalk.conf
Once we’re connected via res_jabber, we can configure the gtalk.conf file, which is used for
accepting incoming calls from the Google network. The following
configuration enables the guest account, which is required to accept
incoming calls. There is currently no support for authenticating
incoming calls and then separating and sending them to different
contexts, which you may be used to from the configuration of other
channel drivers in Asterisk. For now chan_gtalk is fairly simple, but future
versions of Asterisk may add this feature.
Our gtalk.conf file looks
like this:
[general] bindaddr=0.0.0.0 ; Address to bind to allowguests=yes ; Allow calls from people not in contact list ; Optional arguments ; externip=<external IP of server>; stunaddr=<stun.yourdomain.tld> [guest] ; special account for options on guest account disallow=all allow=ulaw context=gtalk_incoming connection=asterisk ; connection name defined in jabber.conf
If
your Asterisk system lives behind NAT, you may need to add some
additional options to the [general]
section in order to place the correct IP address into the headers. If
you have a static external IP address, you can use the externip option to specify it.
Alternatively, you could use the stunaddr option to specify the address of
your STUN server, which will then look up your address from an
external server and place that information into the headers.
Tip
If you configure the stunaddr option in gtalk.conf and
the lookup is successful, it will override any value specified in
the externip option.
Let’s discuss briefly the options
we’ve configured in gtalk.conf.
In the [general] section, we have
set the bindaddr option to 0.0.0.0, which means to listen on all
interfaces.[160] You can also specify a single interface to listen on by
specifying the IP address of that interface. The next line is allowguests, which can be set to either
yes or no but is only useful when set to yes. Because the module does not offer the
ability to specify different control mechanisms for different users,
all users are treated as guests.[161]
Next we’ve specified the [guest] account, which will let us accept
calls from Google Voice and Google Talk users. This account is only
used for incoming calls. When placing outgoing calls, we’ll use the
account specified in the jabber.conf file. Within the [guest] account, we’ve disabled all codecs
with the disallow=all option, and
then specifically enabled the ulaw codec with allow=ulaw on the following line. Incoming
calls are then directed to the gtalk_incoming context with the context option. We specify which account calls will be
coming from with the connection
option, which we’ve set to the account created in jabber.conf.
The chan_gtalk module does not support reloading
the configuration. If you change the configuration, you will have to
either restart Asterisk or unload and reload the module, which can
only be done when no GTalk calls are up. You can do that using the
following commands:
*CLI>module unload chan_gtalk.so*CLI>module load chan_gtalk.so
Accepting calls from Google Talk
To allow calls from other Google Talk users, we need to
configure our dialplan to accept incoming calls. Inside your extensions.conf file, add the [gtalk_incoming] context:
[gtalk_incoming]
exten => s,1,Verbose(2,Incoming Gtalk call from ${CALLERID(all)})
same => n,Answer()
same => n,Dial(SIP/0000FFFF0001,30)
same => n,Hangup()We’ve now configured a simple test
dialplan that will send calls to the SIP/0000FFFF0001 device and wait 30 seconds
before hanging up the line. The s
extension can be used to match any incoming call from Google Talk or
Google Voice, but if you have multiple accounts that could be coming
into this context, you can match different users by specifying the
username portion of the Gmail email address as the extension. So, for
example, if we had a user
my_asterisk_user@gmail.com, the username portion
would be my_asterisk_user, and this
is what we’d specify in [gtalk_incoming]:
[gtalk_incoming]
exten => my_asterisk_user,1,Verbose(2,Gtalk call from ${CALLERID(all)})
same => n,Answer()
same => n,Dial(SIP/0000FFFF0001,3)
same => n,Hangup()The order of rules used for matching
incoming calls to chan_gtalk
is:
Match the username portion of the Gmail account in the context specified for the
[guest]account.Match the
sextension in the context specified for the[guest]account.Match the
sextension in the[default]context.
Accepting calls from Google Voice
The configuration for accepting calls from Google Voice
is similar (if not identical) to that for Google Talk, which we set up
in the preceding section. A little tip, though, is that sometimes you
can’t disable the call screening functionality (for some reason we
still got it even when we’d disabled it in the Google Voice control
panel). If you run into this problem but don’t want to have to screen
your calls, you can automatically send the DTMF prior to ringing your
device by adding the two boldface lines shown here prior to performing
the Dial():
[gtalk_incoming]
exten => s,1,Verbose(2,Incoming call from ${CALLERID(all)})
same => n,Answer()
same => n,Wait(2)
same => n,SendDTMF(2)
same => n,Dial(SIP/0000FFFF0001,30)
same => n,Hangup()Here, we’re using the Wait() and SendDTMF() applications to first wait 2
seconds after answering the call (which is the time when the call
screening message will start) and then accept the call automatically
(by sending DTMF tones for the number 2). After that, we then send the
call off to our device.
Outgoing calls via Google Talk
To place a call to a Google Talk user, configure your dialplan like so:
[LocalSets] exten => 123,1,Verbose(2,Extension 123 calling some_user@gmail.com) same => n,Dial(Gtalk/asterisk/some_user@gmail.com,30) same => n,Hangup()
The Gtalk/asterisk/some_user@gmail.com part of
the Dial() line can be broken into
three parts. The first part, Gtalk,
is the protocol we’re using for placing the outgoing call. The second
part, asterisk, is the account name
as defined in the jabber.conf
file. The last part, some_user@gmail.com, is the location we’re
attempting to place a call to.
Outgoing calls via Google Voice
To place calls using Google Voice to PSTN numbers, create a dialplan like the following:
[LocalSets]
exten => _1NXXNXXXXXX,1,Verbose(2,Placing call to ${EXTEN} via Google Voice)
same => n,Dial(Gtalk/asterisk/+${EXTEN}@voice.google.com)
same => n,Hangup()Let’s discuss the Dial() line briefly, so you understand what
is going on. We start with Gtalk,
which is the technology we’ll use to place the call. Following that,
we have defined the asterisk user
as the account we’ll use to authenticate with when placing our
outgoing call (this is configured in jabber.conf). Next is the number we’re
attempting to place a call to, as defined in the ${EXTEN} channel variable. We’ve prefixed
the ${EXTEN}
channel variable with a plus sign (+), as it’s required by the Google network
when placing calls. We’ve also appended @voice.google.com to let the Google servers
know this is a call that should be placed through Google
Voice[162] as opposed to to another Google Talk user.
Skype Integration
Skype integration now exists with Asterisk through a
commercial module from Digium called Skype for Asterisk (SFA).[163] The SFA module loads directly into Asterisk and allows
communication with all users on the Skype network directly by using an
account created on the Skype Manager. Previous methods were messy,
requiring the use of a Windows-based computer running another instance of
Skype controlled via an API (application programming interface) and
directing media to a sound card and into Asterisk via chan_oss or chan_alsa. Now, two methods exist: SFA and Skype
Connect (formerly known as Skype for SIP).
As an Asterisk module, Skype for Asterisk does have some features that Skype Connect does not, including text chat, presence updates, and the ability to call a Skype user directly rather an via SkypeIn number. Additionally, Skype for Asterisk utilizes Skype’s encryption for calls, which provides security benefits without the need to use SRTP (secure RTP) with SIP.
Installation of Skype for Asterisk
Since Skype for Asterisk is a commercial product, documentation for installing and configuring the module is available from Digium directly. For the most up-to-date installation documentation and information about the Skype for Asterisk module, see http://www.digium.com/en/products/software/skypeforasterisk.php.
You can download the modules from http://downloads.digium.com/pub/telephony/skypeforasterisk, and the registration utility for registering commercial modules from Digium is available at http://downloads.digium.com/pub/register.
Using Skype for Asterisk
In this section we’ll explore the various ways we can utilize Skype from our dialplan, such as sending calls to and receiving calls from users on the Skype network and exchanging messages with our Skype buddies. We’ll also show you how to implement a clever dialplan that will make it easier to call your friends on the Skype network without having to assign everyone an extension number.
Configuring chan_skype.conf
While the README
file that comes with the Skype module helps to document the chan_skype.conf configuration, and
the sample chan_skype.conf file
is well-documented, it is worth
showing a simple version of the configuration for the purposes of
documenting the usage of Skype from the dialplan.
Note
Users configured in chan_skype.conf must be created with
the Skype Manager interface. Personal Skype IDs are not
allowed.
Our example Skype user will be
pbx.shifteight.org. We’ll configure this user in
chan_skype.conf. There are
additional options that could be set here, but for our purposes we’re
keeping it simple:
[general] default_user=pbx.shifteight.org [pbx.shifteight.org] secret=my_secret_pass context=skype_incoming exten=start buddy_autoadd=true
The default_user option in the [general] section is used to control which
account we should use when placing calls via Skype. If we had multiple
accounts, the default_user would be
used when placing calls unless we specified a different user to place
the call as (we’ll discuss this further in the next section).
We’ve also defined the password
(secret), the context incoming calls will enter into, and
the extension (exten) that will be
executed within the context. If we had multiple Skype users and wanted
to control all of them from the same context, we could give them each different extension
values, such as exten=leifmadsen or
exten=russellbryant.
Additionally, we’ve enabled the ability to automatically add people who contact us to our buddies list.
Placing and receiving calls via Skype
Placing a call to a Skype buddy is relatively
straightforward. Like with other channel types in Asterisk, the
Skype channel type is used to place
calls to endpoints on the Skype network.
Utilizing the Dial() application from the dialplan, we can
place calls to other Skype users:
[LocalSets] exten => 100,1,Answer() same => n,Dial(Skype/vuc.me,30) same => n,Playback(silence/1&user&is-curntly-unavail) same => n,Hangup()
Our dialplan simply answers the
call and attempts to place a call to
vuc.me,[164] wait 30 seconds for that user to answer, and, if there
is no answer, play back a message saying that the user is currently
unavailable before hanging up. We could, of course, be more elaborate
with our dialplan; for example, we could turn this into a Macro() or GoSub() routine so we just needed to pass in
the name of the person we wish to call.
Unfortunately, if you’re utilizing a device that only has a number pad for dialing, you’ll need to assign extension numbers to all your favorite Skype buddies. However, we’ve come up with a clever way of reading back your online buddies to you, which we’ll describe in the section called “Calling your Skype buddies without assigning extension numbers”.
If you have a softphone,
though, you should have the ability to place calls by dialing names
directly. We can use this to our advantage by creating a pattern match
in our dialplan with the prefix of SKYPE:
[LocalSets]
exten => _SKYPE-.,1,Verbose(2,Dialing via Skype)
same => n,Set(NameToDial=${FILTER(a-zA-Z0-9.,${EXTEN:6})})
same => n,Playback(silence/1&pls-wait-connect-call)
same => n,Dial(Skype/${NameToDial},30)
same => n,Playback(user&is-curntly-unavail)
same => n,Hangup()By dialing SKYPE-vuc.me, we can dial the VoIP Users
Conference via Skype from our softphone. The FILTER() function is used here to control
what we’re allowed to pass to the Dial() application. If we didn’t do any
filtering, someone could potentially send a string like SKYPE-nobody&SIP/my_itsp/,
replacing the number 41655512124165551212 with a
number that is very expensive to call. By using FILTER(), we restrict the allowable
characters to alphanumeric characters and periods.
After that, we’re simply
passing the string to the Dial()
application and waiting for an answer for 30 seconds. If no one
answers, an audio message is played back to the caller stating that
the user is unavailable and then the call is hung up.
To receive calls, you simply
need to configure your user in the chan_skype.conf file as described in the section called “Configuring chan_skype.conf”. Once you’ve done that, you can
configure your dialplan to answer calls like so:
[skype_incoming] exten => start,1,Verbose(2,Incoming Skype Call) same => n,Answer() same => n,Dial(SIP/0000FFFF0001,30) same => n,Playback(user&is-curntly-unavail) same => n,Hangup()
Obviously, you can change this
section of the dialplan to be more elaborate; all we’ve done is
configured the dialplan to call our SIP device at 0000FFFF0001, wait for an answer for 30
seconds, and then (if there is no answer or the device is busy or
unavailable) play back a prompt that says the user is currently
unavailable, followed by a hangup.
We’ve just shown you how to place and receive calls via Skype. The following sections will show you how to send and receive messages via the Skype network, and how to place calls to your Skype buddies without assigning extension numbers to them.
Sending and receiving messages via Skype
Sending and receiving messages via Skype is similar to doing this via XMPP (Jabber), which we described in the section called “Sending messages with JabberSend()” and the section called “Receiving messages with JABBER_RECEIVE()”, so we won’t go into quite the detail in these sections as we did there. Please review the sections about XMPP messaging before continuing, as we’ll be using the same basic dialplans to accomplish sending and receiving of messages via Skype, while making adjustments to use the appropriate dialplan applications and functions.
The primary thing to remember
is that messages are sent with the dialplan application SkypeChatSend() and received with the dialplan function SKYPE_CHAT_RECEIVE(). Additionally, messages can only be received when the
SKYPE_CHAT_RECEIVE() function has
been called from the dialplan, and it blocks (does not continue in the
dialplan) while waiting for a message.
Sending a message from the dialplan to a Skype buddy is relatively straightforward. Here is a simple dialplan we can use to send a message from Asterisk to someone on the Skype network:
[LocalSets]
exten => 104,1,Answer()
; *** This line should not have any line breaks
same => n,SkypeChatSend(pbx.shifteight.org,tfot.madsen,Incoming call from
${CALLERID(all)})
same => n,Dial(SIP/0000FFFF0002,30)
same => n,Hangup()Our dialplan is simple. We
created a test extension of 104
that answers the line, then sends a message to Skype user tfot.madsen from the pbx.shifteight.org account (which we
configured in the chan_skype.conf
file). The message sent is “Incoming call from ${CALLERID(all)}”,
where the caller ID is provided by the CALLERID() function. After sending our message, we then dial the device
located at 0000FFFF0002 and hang up
if no one answers within 30 seconds.
That’s it for sending messages
via Skype. Now let’s look at some of the ways we can receive messages
from Skype. Here is the simple example we explored in the section called “Receiving messages with JABBER_RECEIVE()”), with a few changes made to
reflect the technology. This time, we’ll be replacing JabberSend() and JABBER_RECEIVE() with the SkypeChatSend() and SKYPE_CHAT_RECEIVE() dialplan application
and function, respectively:
exten => 106,1,Answer()
; All text must be on a single line.
same => n,SkypeChatSend(pbx.shifteight.org,tfot.madsen,Incoming call from
${CALLERID(all)}. Press 1 to route to desk. Press 2 to send to voicemail.)
; Wait for a response for 6 seconds.
; *** This line should not have any line breaks
same => n,Set(SkypeResponse=
${SKYPE_CHAT_RECEIVE(pbx.shifteight.org,tfot.madsen,6)})
same => n,GotoIf($["${SkypeResponse}" = "1"]?dial,1)
same => n,GotoIf($["${SkypeResponse}" = "2"]?voicemail,1)
same => n,Goto(dial,1)
exten => dial,1,Verbose(2,Calling our desk)
same => n,Dial(SIP/0000FFFF0002,6)
same => n,Goto(voicemail,1)
exten => voicemail,1,Verbose(2,VoiceMail)
; *** This line should not have any line breaks
same => n,Set(VoiceMailStatus=${IF($[${ISNULL(${DIALSTATUS})}
| "${DIALSTATUS}" = "BUSY"]?b:u)})
same => n,Playback(silence/1)
same => n,VoiceMail(100@lmentinc,${VoiceMailStatus})
same => n,Hangup()
There you have it—sending and receiving messages via the Skype network!
Tip
You can also send and receive messages with the Asterisk Manager Interface, the topic of Chapter 20.
We’ve essentially implemented a screen pop solution for
incoming calls, but by allowing messages to be sent back to Asterisk
via Skype within a defined period of time, we’ve also created a
solution for redirecting calls prior to ringing any devices. A more
functional version of the dynamic routing dialplan we just explored
was developed in the section about JABBER_RECEIVE() earlier in this chapter: it
used the Local channel to get around the dialplan blocking issue,
enabling calls can be routed even after a device has started to be
rung.
Calling your Skype buddies without assigning extension numbers
While working on this book, we had some issues with trying to come up with clever ways to use a text-to-speech engine. It seemed that dynamic data would need to be involved for text-to-speech to really make a lot of sense—otherwise, why not use prerecorded prompts instead? However, an idea finally came to us, based on the fact that having to assign extension numbers to each Skype user we wanted to call was not only cumbersome, but was a mental exercise we weren’t willing to take on.
The following dialplan makes
use of the SKYPE_BUDDIES() and SKYPE_BUDDY_FETCH() dialplan functions to retrieve all the Skype buddies in memory on the
server, and to read those buddies’ names back to you along with their
statuses. After each buddy name is read, a prompt asking if this is
who you wish to call is presented, with the option of asking for
another buddy from the list. We’ve utilized the Festival() application for this example (the configuration and setup of which
can be found in the section called “Festival”) to read
back the users’ names. Once a buddy has been marked as selected, it is
then dialed using the Dial()
application.
Our implementation is as follows:
[LocalSets]
exten => 75973,1,Verbose(2,Read off list of Skype accounts)
same => n,Answer()
same => n,Set(ID=${SKYPE_BUDDIES(pbx.shifteight.org)})
same => n(new_buddy),Set(ARRAY(buddy,status)=${SKYPE_BUDDY_FETCH(${ID})})
same => n,GotoIf($[${ISNULL(${buddy})}]?no_more_buddies)
same => n,Festival(${buddy} is ${status})
same => n,Read(Answer,if-correct-press&digits/1&otherwise-press&digits/2,1)
same => n,GotoIf($[${Answer} = 2]?new_buddy)
same => n,Dial(Skype/${buddy},30)
same => n,Playback(user&is-curntly-unavail)
same => n,Hangup()
exten => no_more_buddies,1,Verbose(2,No more buddies to find)
same => n,Playback(dir-nomore)
same => n,Hangup()
LDAP Integration
Asterisk supports the ability to connect to an existing Lightweight Directory Access Protocol (LDAP) server to load information into your Asterisk server using the Asterisk Realtime Architecture (ARA). The advantage of integrating Asterisk and LDAP will become immediately obvious when you start centralizing your authentication mechanisms to the LDAP server and utilizing it for several applications: you significantly cut down the administrative overhead of managing your users by placing all their information into a central location.
There are both commercial and open source LDAP servers available, the most popular commercial solution likely being that implemented by Microsoft Windows servers. A popular open source LDAP server is OpenLDAP (http://www.openldap.org). We will not delve into the configuration of the LDAP server here, but we will show you the schema required to connect Asterisk to your server and to use it to provide SIP connections and voicemail service to your existing user base.
Configuring OpenLDAP
While a discussion of the installation and configuration
of an LDAP server is beyond the scope of this chapter, it is certainly
applicable to show you how we expanded our initial LDAP schema to
include the information required for Asterisk integration. Our initial
installation followed instructions from the Ubuntu documentation page
located at https://help.ubuntu.com/10.04/serverguide/C/openldap-server.html.
We only needed to follow the instructions up to and including the
backend.example.com.ldif import;
the next step after importing the backend configuration is installing
the Asterisk-related schemas.
If you’re following along, with the
backend imported, change into your Asterisk source directory. Then copy
the asterisk.ldap-schema file into
the /etc/ldap/schema/
directory:
$cd ~/src/asterisk-complete/asterisk/1.8/contrib/scripts/$sudo cp asterisk.ldap-schema /etc/ldap/schema/asterisk.schema
With the schema file copied in, restart the OpenLDAP server:
$ sudo /etc/init.d/slapd restartNow
we’re ready to import the contents of asterisk.ldif into our OpenLDAP server. The
asterisk.ldif file is located in
the contrib/scripts/ folder of the
Asterisk source directory:
$ sudo ldapadd -Y EXTERNAL -H ldapi:/// -f asterisk.ldifWe can now continue with the
instructions at https://help.ubuntu.com/10.04/serverguide/C/openldap-server.html
and import the frontend.example.com.ldif file. Within that
file is an initial user, which we can omit for now as we’re going to
modify the user import portion to include an objectClass for Asterisk (i.e., in the example
file, the section of text that starts with uid=john can be deleted).
We’re going to create a user and add the configuration values that will allow the user to register his phone (which will likely be a softphone, since the hardphone on the user’s desk will, in most cases, be configured from a central location) via SIP by using his username and password, just as he would normally log in to check email and such.
The configuration file we’ll create
next will get imported with the ldapadd command and will be added into the people object unit within the
shifteight.org space. Be sure to change the values
to match those of the user you wish to set up in LDAP and to substitute
dc=shifteight,dc=org with your own
location.
Before we create our file, though, we need to convert the password into an MD5 hash. Asterisk will not authenticate phones using plain-text passwords when connecting via LDAP. We can convert the password using the md5sum command:
$echo "my_secret_password" | md5suma7be810a28ca1fc0668effb4ea982e58 -
We’ll insert the returned value (without the hyphen) into the
following file within the userPassword field, prefixed with {md5}:
$cat > astuser.ldifdn: uid=rbryant,ou=people,dc=shifteight,dc=org objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount objectClass: AsteriskSIPUser uid: rbryant sn: Bryant givenName: Russell cn: RussellBryant displayName: Russell Bryant uidNumber: 1001 gidNumber: 10001 userPassword: {md5}a7be810a28ca1fc0668effb4ea982e58 gecos: Russell Bryant loginShell: /bin/bash homeDirectory: /home/russell shadowExpire: -1 shadowFlag: 0 shadowWarning: 7 shadowMin: 8 shadowMax: 999999 shadowLastChange: 10877 mail: russell.bryant@shifteight.org postalCode: 31000 l: Huntsville o: shifteight title: Asterisk User postalAddress: initials: RB AstAccountCallerID: Russell Bryant AstAccountContext: LocalSets AstAccountDTMFMode: rfc2833 AstAccountMailbox: 101@shifteight AstAccountNAT: yes AstAccountQualify: yes AstAccountType: friend AstAccountDisallowedCodec: all AstAccountAllowedCodec: ulaw AstAccountMusicOnHold: defaultCtrl+D
Note
The one field we should
explicitly mention here is the userPassword field. We require that the
value in the LDAP server contain the password we’re going to
authenticate from the phone with to be in the format of an MD5 hash.
In versions prior to Asterisk 1.8.0, the prefix of {md5} in front of the hash was required.
While it is no longer necessary, it is still recommended.
With the file created, we can add the user to our LDAP server:
$sudo ldapadd -x -D cn=admin,dc=shifteight,dc=org -f astusers.ldif -WEnter LDAP Password: adding new entry "uid=rbryant,ou=people,dc=shifteight,dc=org"
Our user has now been imported into LDAP. The next step is to configure Asterisk to connect to the LDAP server and allow users to authenticate and register their phones.
Compiling LDAP Support into Asterisk
With our OpenLDAP server configured and the schema
imported, we need to install the dependencies for Asterisk and compile
the res_config_ldap module. This
module is the key that will allow us to configure Asterisk realtime for
accessing our peers via
LDAP.
Once we’ve installed the
dependency, we need to rerun the ./configure script inside the Asterisk source
directory, then verify that the res_config_ldap module is selected. Then we
can run make install to compile and
install the new module.
Configuring Asterisk for LDAP Support
Now that we’ve configured our LDAP server and installed
the res_config_ldap module, we need
to configure Asterisk to support loading of peers from LDAP. To do this,
we need to configure the res_ldap.conf file to connect to the LDAP
server and the extconfig.conf file
to tell Asterisk what information to get from the LDAP server, and how.
Once that is done, we can configure any remaining module configuration
files, such as sip.conf, iax.conf, voicemail.conf, and so on, where appropriate.
In our example we’ll be configuring Asterisk to load our SIP peers from
realtime using the LDAP server as our database.
Configuring res_ldap.conf
The res_ldap.conf.sample file is a good place
to start because it contains a good set of templates. At the top of
the file, though, under the [_general] section, we need to configure how
Asterisk is going to connect to our LDAP server. Our first option is
url, which will determine how to
connect to the server. We have defined a connection as ldap://172.16.0.103:389, which will connect
to the LDAP server at IP address 172.16.0.103 on port 389. If you have a secure connection to your
LDAP server, you can replace ldap:// with ldaps://. Additionally, we have set protocol=3 to state that we’re connecting
with protocol version 3, which in most (if not all) cases will be
correct.
The last three options,
basedn, user, and pass, are used for authenticating to our
LDAP server. We need to specify:
The
basedn(dc=shifteight,dc=org), which is essentially our domain nameThe
username we’re going to authenticate to the LDAP server as (admin)The password for the user to authenticate with (
canada)
If we put it all together, we end up with something like the following:
[_general] url=ldap://172.16.0.103:389 protocol=3 basedn=dc=shifteight,dc=org user=cn=admin,dc=shifteight,dc=org pass=canada
Beyond this, in the rest of the sample configuration file
we’ll see lots of templates we can use for mapping the information in
Asterisk onto our LDAP schema. Lets take a look at the first lines of
the [sip] template that we’ll be
using to map the information of our SIP peers into the LDAP
database:
[sip] name = cn amaflags = AstAccountAMAFlags callgroup = AstAccountCallGroup callerid = AstAccountCallerID ... lastms = AstAccountLastQualifyMilliseconds useragent = AstAccountUserAgent additionalFilter=(objectClass=AsteriskSIPUser)
On the left
side we have the field name Asterisk will be looking up, and on the
right is the mapping to the LDAP schema for the request. Our first set
of fields is mapping the name field
to the cn field on the LDAP server.
If you look back at the data we imported in the section called “Configuring OpenLDAP”, you’ll see that we have
created a user and assigned the value of RussellBryant to the cn field. So, in this case, we’re mapping
the authentication name (the name
field) from the SIP user to the value of the cn field in the LDAP server (RussellBryant).
This goes for the rest of the
values all the way down, with some fields (i.e., useragent, lastms, ipaddr, etc.) simply needing to exist so
Asterisk can write information (e.g., registration information) to the
LDAP server.
Configuring extconfig.conf
Our next step is to tell Asterisk what information to
load via realtime and what technology to use. Using the extconfig.conf file, we have the option of
loading several modules dynamically (and we can also load files
statically). For more information about Asterisk realtime, see the section called “Using Realtime”.
For our example, we’re going to
configure the sipusers and sippeers dynamic realtime objects to load
our SIP peers from LDAP. In the following example, we have a line
like this:
ldap,"ou=people,dc=shifteight,dc=org",sip
We’ve specified three
arguments. The first is ldap, which
is the technology we’re going to use to connect to our realtime
object. There are other technologies available, such as odbc, pgsql, curl, and so on. Our second argument,
enclosed in double quotes, specifies which database we’re connecting
to. In the case of LDAP, we’re connecting to the object-unit people within the domain shifteight.org. Lastly, our third argument,
sip, defines which template we’re
using (as defined in res_ldap.conf) to map the realtime data to
the LDAP database.
Tip
Additionally, you can specify
a fourth argument, which is the priority. If you define multiple
realtime objects, such as when defining queues or sippeers, you can utilize the priority argument to control
failover if a particular storage engine becomes unavailable.
Priorities must start at 1 and
increment sequentially.
To define the use of sipusers and sippeers from the LDAP server, we would
enable these lines in extconfig.conf:
sipusers => ldap,"ou=people,dc=shifteight,dc=org",sip sippeers => ldap,"ou=people,dc=shifteight,dc=org",sip
Configuring sip.conf for realtime
These steps are optional for configuring SIP for
realtime, although you will likely expect things to work in the manner
we’re going to describe. In the sip.conf file, we will enable a few
realtime options that will cache information into memory as it is
loaded from the database. By doing this, we’ll allow Asterisk to place
calls to devices by simply looking at the information stored in
memory. Not only does caching make realtime potentially more
efficient, but things like device state updates simply can’t work
unless the devices are cached in memory.
Note
A peer is only loaded into memory upon registration of the device or placing a call to the device. If you run the command sip reload on the console, the peers will be cleared from memory as well, so you may need to adjust your registration times if that could cause issues in your system.
To enable peer caching in
Asterisk, use the rtcachefriends
option in sip.conf:
rtcachefriends=yes
There are additional realtime
options as well, such as rtsavesysname, rtupdate, rtautoclear, and ignoreregexpire. These are all explained in
the sip.conf.sample file located
within your Asterisk source.
Text-to-Speech Utilities
Text-to-speech utilities are used to convert strings of words into audio that can be played to your callers. Text-to-speech has been around for many years, and has been continually improving. While we can’t recommend text-to-speech utilities to take the place of professionally recorded prompts, they do offer some degree of usefulness in applications where dynamic data needs to be communicated to a caller.
Festival
Festival is one of the oldest running applications for text-to-speech on Linux. While the quality of Festival is not sufficient for us to recommend it for production use, it is certainly a useful way of testing a text-to-speech-based application. If a more polished sound is required for your application, we recommend you look at Cepstral (covered next).
Installing Festival on CentOS
Installing Festival and its dependencies on CentOS is
straightforward. Simply use yum to
install the festival
package:
$ sudo yum install festivalInstalling Festival on Ubuntu
To install Festival and its dependencies on Ubuntu,
simply use apt-get to install the
festival package:
$ sudo apt-get install festivalUsing Festival with Asterisk
With Festival installed, we
need to modify the festival.scm
file in order to enable Asterisk
to connect to the Festival server. On both CentOS and Ubuntu, the file
is located in /usr/share/festival/. Open the file and
place the following text just above the last line, (provide 'festival):
(define (tts_textasterisk string mode) "(tts_textasterisk STRING MODE) Apply tts to STRING. This function is specifically designed for use in server mode so a single function call may synthesize the string. This function name may be added to the server safe functions." (let ((wholeutt (utt.synth (eval (list 'Utterance 'Text string))))) (utt.wave.resample wholeutt 8000) (utt.wave.rescale wholeutt 5) (utt.send.wave.client wholeutt)))
After adding that, you need to start the Festival server:
$ sudo festival_server 2>&1 > /dev/null &Using
menuselect from your Asterisk
source directory, verify that the app_festival application has been selected
under the Applications heading. If it was not
already selected, be sure to run make
install after selecting it to install the Festival() dialplan application.
Before you can use the Festival() application, you need to tell
Asterisk how to connect to the Festival server. The festival.conf file is used for controlling
how Asterisk connects to and interacts with the Festival server. The
sample festival.conf file located
in the Asterisk source directory is a good place to start, so copy
festival.conf.sample from the
configs/ subdirectory of your
Asterisk source to the /etc/asterisk/ configuration directory
now:
$ cp ~/asterisk-complete/asterisk/1.8/configs/festival.conf.sample \
/etc/asterisk/festival.confThe default configuration is
typically enough to connect to the Festival server running on the
local machine, but you can optionally configure parameters such as the
host where the Festival server is
running (if remote), the port to
connect to, whether to enable caching of files (defaults to no), the location of the cache directory
(defaults to /tmp), and the
command Asterisk passes to the Festival server.
You can verify that the
Festival() dialplan application is
accessible by running core show application
festival from the Asterisk console:
*CLI> core show application festivalIf you don’t get output, you
may need to load the app_festival.so module:
*CLI> module load app_festival.soVerify
that the app_festival.so module
exists in /usr/lib/asterisk/modules/ if you’re still
having issues with loading the module.
After loading the Festival() application into Asterisk, you
need to create a test dialplan extension to verify that Festival() is working:
[LocalSets] exten => 203,1,Verbose(2,This is a Festival test) same => n,Answer() same => n,Playback(silence/1) same => n,Festival(Hello World) same => n,Hangup()
Reload the dialplan with the dialplan reload command from the Asterisk console, and test out the connection
to Festival by dialing extension 203.
Alternatively, if you’re having
issues with the Festival server, you could use the following method to
generate files with the text2wave
application supplied with the festival package:
exten => 202,1,Verbose(2,Trying out Festival) same => n,Answer() ; *** This line should not have any line breaks same => n,System(echo "This is a test of Festival" | /usr/bin/text2wave -scale 1.5 -F 8000 -o /tmp/festival.wav) same => n,Playback(/tmp/festival) same => n,System(rm -f /tmp/festival.wav) same => n,Hangup()
You should now have enough to get started with generating text-to-speech audio for your Asterisk system. The audio quality is not brilliant, and the speech generated is not clear enough to be easy to understand over a telephone, but for development and testing purposes Festival is an application that can fill the gap until you’re ready for a more professional-sounding text-to-speech generator such as Cepstral.
Cepstral
Cepstral is a text-to-speech engine that works in a
similar manner as the Festival()
application in the dialplan, but produces much higher-quality sound. Not
only is the quality significantly better, but Cepstral has developed a
text-to-speech engine that emulates Allison’s voice, so your
text-to-speech engine can sound the same as the English sound files that
ship with Asterisk by default, to give a consistent experience to the
caller.
Cepstral is commercial module, but for around $30 you can have a text-to-speech engine that is clearer, is more consistent with other sound prompts on your system, and provides a more pleasurable experience for your callers. The Cepstral software and installation instructions can be downloaded from the Digium.com webstore at http://www.digium.com/en/products/software/cepstral.php.
Conclusion
In this chapter we focused on integrating Asterisk with external services that may not be directly related to generating or handling calls, but do enable tighter coupling with existing services on your network by providing information for call routing, or information about your users from your existing infrastructure.
[155] And because the authors of this book do not have access to Exchange servers for testing. : )
[156] As of this writing, there is a bug in the process of loading of the calendar modules after Asterisk has been started. It was filed as issue 18067 at https://issues.asterisk.org and hopefully will have been resolved by the time you read this. If not, be aware that you may need to restart Asterisk to get the modules loaded into memory correctly.
[157] In Unix, the epoch is the number of seconds that have elapsed since January 1, 1970, not counting leap seconds.
[158] When we say “it works,” what we mean is that Asterisk will compose the email and submit it to the MTA, and the email will successfully be passed out of the system. What happens to it after it leaves the system is a bit more complicated, and will often involve spam filters treating the mail as suspect and not actually delivering it. This is not really Asterisk’s fault, but it’s something you’ll have to deal with.
[159] We recently checked out the open source webmail project roundcube project as well, and we were quite impressed.
[160] The chan_gtalk module
only support IPv4 interfaces.
[161] Future versions of Asterisk may offer more fine-grained control.
[162] You may have to purchase credits from Google Voice in the control panel in order to place calls to certain destinations.
[163] Skype for Asterisk currently retails for $66 per license, and includes a G.729 license. Each license permits one simultaneous call.
[164] The VUC is the VoIP Users Conference, which runs weekly at 12:00 noon Eastern time (–0500 GMT). More information is available at http://vuc.me.






View 1 comment



