9780596517342
ExternalServices.html

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.

--Patrick B. Oliphant

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/<Gmail Email Address>/events/
user=<Gmail Email Address>
secret=<Gmail Password>
refresh=15
timeframe=60
Ctrl+D

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 calendars
Calendar             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 myGoogleCal
Name              : 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 calendars
Calendar             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

OptionDescription
EXTRACFLAGS="-fPIC"Required when building on 64-bit platforms.
EXTRACFLAGS="-I/usr/include/openssl"Used for building in OpenSSL support.
IP6=4Many 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 menuselect

From 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.

Figure 18.1. Enabling Gmail IMAP

Enabling Gmail IMAP

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

OptionDescription
imapfolderProvides the name of the folder in which to store voicemail messages on your IMAP server. By default they are stored in the INBOX.[a]
imapgreetingsDefines whether voicemail greetings are stored on the IMAP server or stored locally on the server. Valid values are yes or no.
imapparentfolderDefines 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.
greetingfolderSpecifies 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.
authuserSpecifies 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.
authpasswordComplement to the authuser directive. See authuser for more information.
opentimeoutSpecifies the TCP open timeout (in seconds).
closetimeoutSpecifies the TCP close timeout (in seconds).
readtimeoutSpecifies the TCP read timeout (in seconds).
writetimeoutSpecifies 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.so
DEBUG[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-devel

Note

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-dev

Jabber 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.com
username=asterisk@shifteight.org
secret=<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

OptionDescription
debugEnables/disables XMPP message debugging (which can be quite verbose). Available options are yes or no.
autopruneEnables/disables autoremoval of users from your buddy list each time res_jabber.so connects to your accounts. Do not use this for accounts you might use outside of Asterisk (e.g., your personal account). Available options are yes or no.
autoregisterSpecifies whether to automatically register users from your buddy list into memory. Available options are yes or no.
auth_policyDetermines whether or not we should automatically accept subscription requests. Available options are accept or deny.
typeSets the type of client we will connect as. Available options are client or component. (You will almost always want client.)
serverhostIndicates which host this connection should connect to (e.g., talk.google.com).
usernameProvides the username that will be used to connect to the serverhost (e.g., asterisk@gmail.com).
secretSpecifies the password that will be used to connect to the serverhost.
portIndicates which port we will attempt the connection to serverhost on (e.g., 5222).
usetlsSpecifies whether to use TLS or not when connecting to serverhost. Available options are yes or no.
usesaslSpecifies whether to use SASL or not when connecting to serverhost. Available options are yes or no.
statusDefines our default connection status when signed into our account. Available options are: chat, available, away, xaway, and dnd.
statusmessageSets a custom status message to use when connected with Asterisk, such as "Connected Via Asterisk". Use double quotes around the message.
buddyUsed to manually add buddies to the list upon connection to the server. You can specify multiple buddies on multiple buddy lines (e.g., buddy=jim@shifteight.org).
timeoutSpecifies the timeout (in seconds) that messages are stored on the message stack. Defaults to 5 seconds. This option only applies to incoming messages, which are intended to be processed by the JABBER_RECEIVE() dialplan function.
priorityDefines the priority of this resource in relation to other resources. The lower the number, the higher the priority.

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 connections
Jabber 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:

  1. Match the username portion of the Gmail account in the context specified for the [guest] account.

  2. Match the s extension in the context specified for the [guest] account.

  3. Match the s extension 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/4165551212, replacing the number 4165551212 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 restart

Now 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.ldif

We 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" | md5sum
a7be810a28ca1fc0668effb4ea982e58  -

We’ll insert the returned value (without the hyphen) into the following file within the userPassword field, prefixed with {md5}:

$ cat > astuser.ldif

dn: 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: default

Ctrl+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 -W
Enter 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.

Ubuntu dependencies

On Ubuntu, we need to install the openldap-dev package to provide the dependency for the res_config_ldap module:

$ sudo apt-get install openldap-dev

CentOS dependencies

On CentOS, we need to install the openldap-devel package to provide the dependency for the res_config_ldap module:

$ sudo yum install openldap-devel

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 name

  • The user name 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 festival

Installing Festival on Ubuntu

To install Festival and its dependencies on Ubuntu, simply use apt-get to install the festival package:

$ sudo apt-get install festival

Using 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.conf

The 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 festival

If you don’t get output, you may need to load the app_festival.so module:

*CLI> module load app_festival.so

Verify 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.

Site last updated on: September 27, 2011 at 10:30:41 AM PDT
Cover for Asterisk: The Definitive Guide

    View 1 comment

    1. Leif Madsen – Posted Nov. 4, 2010

      ...I don't get it

      The author has indicated that the issue raised in this comment has been resolved.

    View 2 comments

    1. Nicholas Barnes – Posted Oct. 25, 2010

      No full-stop at the end of the sentence.

      The author has indicated that the issue raised in this comment has been resolved.

    2. Leif Madsen – Posted Nov. 4, 2010

      Fixed.

      The author has indicated that the issue raised in this comment has been resolved.

    View 2 comments

    1. Nicholas Barnes – Posted Oct. 25, 2010

      No full-stop at the end of the sentence.

      The author has indicated that the issue raised in this comment has been resolved.

    2. Leif Madsen – Posted Nov. 4, 2010

      Fixed.

      The author has indicated that the issue raised in this comment has been resolved.

    View 2 comments

    1. Nicholas Barnes – Posted Oct. 25, 2010

      No full-stop at the end of the sentence.

      The author has indicated that the issue raised in this comment has been resolved.

    2. Leif Madsen – Posted Nov. 4, 2010

      Fixed.

      The author has indicated that the issue raised in this comment has been resolved.

      View 2 comments

      1. Nicholas Barnes – Posted Oct. 25, 2010

        No full-stop at the end of the sentence.

        The author has indicated that the issue raised in this comment has been resolved.

      2. Leif Madsen – Posted Nov. 4, 2010

        Fixed.

        The author has indicated that the issue raised in this comment has been resolved.

        View 2 comments

        1. Nicholas Barnes – Posted Oct. 25, 2010

          Suggest remove "we selected"

          The author has indicated that the issue raised in this comment has been resolved.

        2. Leif Madsen – Posted Nov. 4, 2010

          Done.

          The author has indicated that the issue raised in this comment has been resolved.

        View 2 comments

        1. Nicholas Barnes – Posted Oct. 25, 2010

          Suggest change:

          "...we have the ability to manipulate..."

          to:

          "...you have the ability to manipulate..."

          The author has indicated that the issue raised in this comment has been resolved.

        2. Leif Madsen – Posted Nov. 4, 2010

          Fixed.

          The author has indicated that the issue raised in this comment has been resolved.

                                    View 2 comments

                                    1. Nicholas Barnes – Posted Oct. 25, 2010

                                      Incorrect use of "i.e.". There are other methods of synchronising time.

                                      The author has indicated that the issue raised in this comment has been resolved.

                                    2. Leif Madsen – Posted Nov. 4, 2010

                                      Probably, but NTP is pretty much the most widely deployed and known. I don't even know of another method that you can use.

                                      The author has indicated that the issue raised in this comment has been resolved.

                                                                                                              View 2 comments

                                                                                                              1. Nicholas Barnes – Posted Oct. 25, 2010

                                                                                                                The application is "VoiceMail()" (note punctuation of the 'M').

                                                                                                                The author has indicated that the issue raised in this comment has been resolved.

                                                                                                              2. Leif Madsen – Posted Nov. 4, 2010

                                                                                                                s/Voicemail/VoiceMail/g complete.

                                                                                                                The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                      View 1 comment

                                                                                                                                      1. pabelanger – Posted Oct. 20, 2010

                                                                                                                                        $ sudo apt-get install libc-client-dev

                                                                                                                                        :)

                                                                                                                                        The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                      View 2 comments

                                                                                                                                                      1. Nicholas Barnes – Posted Oct. 25, 2010

                                                                                                                                                        Suggest change: "...sure your IMAP library did build" to: "...sure your IMAP library was built successfully"

                                                                                                                                                        The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                      2. Leif Madsen – Posted Nov. 4, 2010

                                                                                                                                                        Fixed.

                                                                                                                                                        The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                            View 1 comment

                                                                                                                                                                                            1. Nicholas Barnes – Posted Oct. 25, 2010

                                                                                                                                                                                              Remove the 's' in "Compiling Jabber Supports Into Asterisk".

                                                                                                                                                                                              The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                View 2 comments

                                                                                                                                                                                                1. Igor Nikolaev – Posted Nov. 2, 2010

                                                                                                                                                                                                  libiksemel-dev

                                                                                                                                                                                                  The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                2. Leif Madsen – Posted Nov. 4, 2010

                                                                                                                                                                                                  Changed for Ubuntu but not CentOS.

                                                                                                                                                                                                  The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                  View 1 comment

                                                                                                                                                                                                  1. Igor Nikolaev – Posted Nov. 2, 2010

                                                                                                                                                                                                    libiksemel-dev

                                                                                                                                                                                                    The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                                                                          View 2 comments

                                                                                                                                                                                                                                                          1. Mark Petersen – Posted Oct. 21, 2010

                                                                                                                                                                                                                                                            this shut contain a date, as the book may be around for more than one year

                                                                                                                                                                                                                                                            The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                                                                          2. Leif Madsen – Posted Nov. 4, 2010

                                                                                                                                                                                                                                                            There is a publication and print date at the beginning of the book :)

                                                                                                                                                                                                                                                            The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                                                                                                                            View 2 comments

                                                                                                                                                                                                                                                                                                            1. roderickmontgomery – Posted Oct. 21, 2010

                                                                                                                                                                                                                                                                                                              Business Control Panel is now "Skype Manager."

                                                                                                                                                                                                                                                                                                              Also, "Skype Connect" (fka Skype for SIP) is an alternative method to connect Asterisk and Skype, so unqualified "all the other methods" may be overreaching.

                                                                                                                                                                                                                                                                                                              As an Asterisk module, Skype for Asterisk does have some features that Skype Connect does not, including text chat, presence, and the ability to call a Skype user directly (not using a SkypeIn number). Oh, and it uses Skype's encryption, which is arguably more secure than SIP RTP streams.

                                                                                                                                                                                                                                                                                                              The author has indicated that the issue raised in this comment has been resolved.

                                                                                                                                                                                                                                                                                                            2. Leif Madsen – Posted Nov. 16, 2010

                                                                                                                                                                                                                                                                                                              Thanks Rod! I've taken your information and updated this section.

                                                                                                                                                                                                                                                                                                              The author has indicated that the issue raised in this comment has been resolved.