Tag Archives: script

Open-source IP telephony and the systems administration dilemma

To effectively set up, operate, and customize an open-source phone system like Asterisk or FreeSWITCH, you need to be competent at Linux systems administration.
When Asterisk and FreeSWITCH first came about, this was clear, because to go though the process of building the software and running it on your server, you had to decipher and successfully follow compiling instructions and use systems administration skills to get it to work.
Enter the Asterisk+FreePBX “distro.” There are several of them out there now, and their promise is big: insert this CD or flash drive, boot your server, and out comes a working phone system with a configuration GUI. The installers even do a decent job of making the system secure, so that you’re not the victim of the first script kiddie to happen upon your server’s IP address.
Linux is pretty good at figuring out hardware configurations and installing drivers and setting up automatically. So, chances are, when the installer is done, you really do have a working system.
At this point, the person maintaining the phone system has to hope that all problems can be effectively diagnosed and solved through the GUI, that nothing interesting happens with the underlying hardware or operating system that would require troubleshooting, that all logging is available through the GUI, and that the security of the GUI’s web server (typically Apache, firewalled by iptables) is correct. 
What if you are running Asterisk+FreePBX and have a SIP communications issue with a provider? You can’t turn on SIP debugging in FreePBX. You can use the Asterisk Logs module to get some log information but you’d do much better with command line tools such as grep. The truth is, inability to navigate Linux and the Asterisk command line is crippling.
Taking advantage of the open-source system’s openness to customize and add new features, such as Google Voice integration, means getting into the nuts and bolts. Or wait for someone else to write a (probably buggy) GUI module for you.
The fact is, if you want to be hands-off with systems administration, you need to get an appliance-like system such as SwitchVox or the big name brand small business PBXes. You have to pay for these. With your money you will get professional support to operate the low-level controls when an error appears, and you’ll get full GUI access (probably) to everything you should have access to. If it’s not in the GUI, you don’t have access to it.
If you want free, you need to learn systems administration so that you can be your own support person. The forums of FreePBX and various distros are full of questions like, “My phone doesn’t work, what do I do?” Ask them what appears in the logs and they have no idea what you are talking about. It just doesn’t work like that.
This is the dilemma. Free and customizable, with the cost of learning systems administration (and maybe some scripting/programming) skills, or hands-off with the cost of paying the vendor for the software and support?
To be a little more specific about what I am calling systems administration skills, here are the things I believe a person needs to know in order to competently install and maintain an open-source phone system, with or without a GUI, and whether from a CD-installed distro or from RPM packages or from source (not an exhaustive list):
  • ability to navigate the Linux filesystem comfortably and know where certain kinds of files typically are (logs are in /var/log, config files are in /etc, executables in /usr/bin and so on)
  • ability to use a text editor
  • handiness with find, grep
  • ability to correctly configure a firewall or packet filter such as iptables
  • ability to read syslogs and follow clues to solve a problem
  • familiarity with the operating system’s package system (such as yum/RPM with CentOS) so that he/she can easily load necessary tools
  • some familiarity with development tools such as make, gcc, configure scripts, cvs and svn, and the ability to decipher output they produce
  • ability to configure user accounts and passwords
  • some familiarity with network diagnostic tools like netstat and tcpdump
  • for FreePBX, familiarity with command-line MySQL for database troubleshooting
  • for Asterisk’s mail needs (voicemail or fax to e-mail), ability to configure some mail sender

Is it a tall order? Yes, there is a learning curve. It’s not Windows 7. But, like learning a foreign language, when you go into the foreign land (Linux console) and can speak the language, you are empowered.

A crash course in Perl

Found in a posting on the unofficial Penn State intranet (Yammer):

for those who already know the basic building blocks of programming or scripting (statements, blocks, conditionals, variables, operations, and so on).
Perl is always in my toolbox. Real programming languages are for Real Programmers. Perl is for systems administrators and hackers. You can do anything with it; it will probably just take longer to run (but if you don’t care about that, it will probably be quicker to write).

Appendix: analog remix debugging and notes

Part 1: Intro, Part 2: Tropo/PHP, Part 3: Asterisk Manager/Perl

These notes are from a weekend of hacking and debugging with Michigan Telephone, the first guinea pig to try out the “analog remix” scripts. Thanks for the extra challenges. 🙂

Asterisk 1.4, 1.8 and the Asterisk Manager Interface

  • Asterisk 1.4’s Manager calls the channel that is bridged to the current channel “Link.” Newer versions, more sensibly, call it “BridgedChannel.” I updated the Perl CGI to use whichever is defined, so now the CGI will work with newer versions of Asterisk, and not just 1.4.
  • A bug exists on specific versions of Asterisk 1.4, 1.6 and 1.8 that breaks call transfer from the Manager (as well as SIP blind transfers). The current -rc1 versions, as of this writing, have the fix: 1.4.39-rc1, 1.6.2.16-rc1, and 1.8.2-rc1. This bug has been in 1.4 since 1.4.38-rc1; in 1.6 since 1.6.2.15-rc1; and in 1.8 since 1.8.0-rc4. If you’re on a buggy version, the transfer will just hang up the channel.
  • How to set up the Manager user may not be obvious. Just make a new user block in manager.conf or manager_custom.conf (FreePBX) with the following permissions:

    [tropousername]
    secret = tropopassword
    deny=0.0.0.0/0.0.0.0
    permit=127.0.0.1/255.255.255.0
    read = call
    write = call

    Set the username and password to whatever you want, and then use these in the Perl CGI in the AMI::Common::new() function. Your manager user needs only “call” permissions to perform the actions in the CGI.

Perl

  • MT notes that you may have to install the AnyEvent and parent Perl modules before Asterisk::AMI. Use CPAN’s installer if you can: perl -MCPAN -e shell and install parent, AnyEvent, and Asterisk::AMI
  • The Data::Dumper module can be really helpful if you find that things aren’t going well with your CGI. Just use Data::Dumper at the top of the CGI, then print STDERR Dumper($variablename) wherever you need it. Try this on a hash, such as $currchan, and you’ll get a dump of the whole thing.

Firewall/ACL

You may want to set up a separate Apache VirtualHost with access controls (username/password and/or IP access list) for your Tropo CGI. Or, if your web server is firewalled, you’ll need to let Tropo in to make the request. Tropo’s support team indicated that requests will come from 66.193.54.21, so this IP can be allowed through your firewall. They will announce any changes to this on changes.tropo.com.

Analog remix part 2: Tropo

Tropo, a project of Voxeo Corporation, adds voice and short-message-communications functions to PHP, JavaScript, Ruby, Python, and Groovy through a set of classes. They host the platform and your scripts, which are then triggered through the web or by PSTN/SIP, SMS, Twitter, or chat events. You can use the system as long as you want for free for development, or pay reasonable hosting rates to go production. Oh, and their support team is terrific, even for developers who haven’t yet paid them a dime.
So what can you do with it?
  • Interactive voice-response (IVR) systems
  • Chat or Twitter robot
  • Dialer
  • Call routing based on logic in the script
  • Text-to-speech
  • Speech-to-text
  • Audio recording
  • Whatever you can normally do with the aforementioned scripting languages, but note that the stdout will be a communications outlet such as a SIP channel, a Twitter feed or conversation, a chat, or an SMS–not a web page
My first Tropo application, the “local operator,” is the topic of part two of the analog remix. The platform has so many features, though, that I look forward to spending more time developing in it.
I chose to use PHP because it’s the language with which I am most familiar and also has Curl libraries built in, which I am using to make a CGI callback to my own Asterisk server. The purpose should become clear when you read the code, but you may ask, “why not just send a SIP REFER to do the call transfer?” The answer is that you can’t with the Tropo platform. All transfers go through Tropo; there’s no option for a REFER. So if you want to use your own dialplan and not have your call going out to Tropo and back to use local resources, you have to do the transfer through the Asterisk Manager, thus the CGI call.
Number, Please?
The script answers the call (which will come in by SIP URI), asks, “Number, please?”, validates and confirms the spoken number with the caller, then uses the Curl functions to go back to the calling Asterisk server (running a web server) and transfer the call.
Getting There From Asterisk
Once you’ve written a script and uploaded it to  Tropo (or typed it directly into Tropo’s file-editing interface), you go to the Applications section and choose your script as the source. Once you’ve submitted it, you’ll be given a SIP URI (amongst other methods) by which to access the application. So as part of a FreePBX Custom Extension or in your extensions.conf file, you can make an extension for your new application by referencing SIP/somenumber@sip.tropo.com. I set mine up as extension 1100, which I mentioned in the previous post.
Now, if I go off hook from one of my internal extensions and dial 1100, or simply pick up the analog extension on which I configured the Warm Line/Hot Line, I’ve got the operator ready to take my number and transfer my call. Only one piece left after this: the Asterisk Manager CGI script.

Appendix: the PHP code (Number, Please?)

<?php

// Main

// SSML tags needed for advanced text-to-speech manipulation
$ssml_start = "<?xml version='1.0'?><speak>";
$sayas_end="</say-as>";
$sayas_digits ="<say-as interpret-as='vxml:digits'>";
$ssml_end = "</speak>";

answer();

// These IDs are used to identify the caller and the Asterisk box
$callid = $currentCall->getHeader('x-sbc-call-id');
$hostid = trim((strstr($callid, '@')),'@');

_log("Call-ID is $callid");
_log("Host is $hostid");

wait(1000);

$haveNumber = false;

while (!$haveNumber) {
$number = ask($ssml_start . "Number <emphasis level='reduced'>please?</emphasis>" .
$ssml_end,
array(voice => "allison",
attempts => 3,
bargein => false,
choices => "[4-16 DIGITS]",
minConfidence => 0.4,
timeout => 8,
onBadChoice => "handlerBadChoice",
onError => "handlerError",
onHangup => "handlerHangup",
onTimeout => "handlerTimeout"
));

// Confirm the number with the caller
// 1 and 0 are options for testing with DTMF
$yesno = ask($ssml_start . $sayas_digits . $number->value . $sayas_end .
", is this correct?" . $ssml_end,
array(voice => "allison",
attempts => 2,
bargein => false,
choices => "yes, no, 1, 0",
minConfidence => 0.5,
timeout => 3,
onBadChoice => "handlerBadChoice",
onError => "handlerError",
onHangup => "handlerHangup",
onTimeout => "handlerTimeout"
));

_log("Caller said $yesno->value.");

if ($yesno->value == "yes" || $yesno->value == "1") {
$haveNumber = true;
} else {
say("OK, let's try again.");
}
}

// The CGI callback. Refer to a future posting for the design
// Customize for your server. Use SSL for security if you can.
$url = "http://$hostid/tropo.cgi?f=xfer&callid=" . urlencode($callid) .
"&dest=" . $number->value;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, "USERNAME:PASSWORD");
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);

_log("Received from CGI: " . $result = curl_exec($ch));

if (!$result) {
_log(curl_error($ch));
}
curl_close($ch);

// If the transfer didn't happen, the caller is still here...

if ($currentCall->isActive()) {
_log("Transfer failed and caller still on the line.");
say("I'm sorry, something went wrong. Please try again later.");
hangup();
}

return;
// END OF MAIN

// Function definitions

function handlerBadChoice($event) {
say("I'm sorry, I didn't understand you.");
if($event->attempt >= 3) {
say("Goodbye.");
hangup();
}
return;
}

function handlerError($event) {
_log("Error occurred: $event->value");
say("I'm sorry, an error occurred. Goodbye");
hangup();
return;
}

function handlerHangup($event) {
_log("Caller hung up.");
exit;
}

function handlerTimeout($event) {
say("I'm sorry, I didn't hear you.");
if($event->attempt >= 3) {
say("Goodbye.");
hangup();
}
return;
}

?>

PHP + libcurl for a filtering reverse-proxy application

Long time, no blog. We’re steadily making progress toward upgrading CallManager 4.1.3 to Unified Communications Manager 6.1.2. Date is set for December 20, and many efforts within the VoIP team right now are focused on getting the cluster ready for that date.

One outstanding issue has been how to offer CCMUser, the Communications Manager user settings page, to the Penn State VoIP community. We want to use PSU Access Accounts–ideally, WebAccess–to login. CUCM offers LDAP and Active Directory user integration, but neither option will work correctly in the PSU environment. We don’t have access to the code of the CCMUser web site to hack at that, either.

Using Apache, WebAccess, and PHP with the libcurl module, I wrote an authentication, authorization and filtering reverse-proxy wrapper. It’s not elegant, but it sure works! I can’t post the code, for obvious security considerations, but here are the basic steps the script follows when a user comes along to access CCMUser:

  • Hello, you must be new here. Go authenticate with WebAccess and come back with a valid user ID.
  • Set up a PHP session to store information that needs to be maintained for the CCMUser site.
  • Check the session for stored cookies from CCMUser. If there are no cookies stored, the user hasn’t been authorized there yet. Using the WebAccess user ID, in the background, take the necessary steps to authorize with CCMUser and store the resulting session cookies in the PHP session.
  • If authorization is successful, start reverse-proxying the CCMUser site via the PHP script and libcurl to the user, beginning with the CCMUser home page.
  • Filter URLs and other information as it passes through the proxy so that the user continues to interact with the site only through the confines of the proxy script.
  • A custom Logout button replaces the CCMUser logout that destroys both the backend session with CCMUser and the PHP session with the user, then redirects to the WebAccess logout.

With this fairly simple script, the user gets the experience of single-sign-on and full functionality of the CCMUser site; we get the security of hiding CCMUser behind a firewall so that only the proxy server interacts with it; and it appears to the user as if he is directly using CCMUser.