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.



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