Glintech integrates Asterisk, Jabber for open source UC platform
A week or so ago, my boss came to me asking if I could write something on what’d I’d done internally with XMPP and Asterisk for someone he knew at Computerworld. I was only too happy to have someone listen to my ramblings, so I wrote up some replies to some of the questions his friend had asked.
Last night my boss sent me a link to the article. Unfortunately, the article makes it look like he was the one who replied to the questions instead of me, but it’s nice to get some of my work out there anyway.
Here’s the full article: Glintech integrates Asterisk, Jabber for open source UC platform.
For those who actually understand what the article is about, you’ll probably realise that what I did here wasn’t anything particularly difficult or out of the ordinary. But hopefully for those who don’t, it might open a few eyes to some of the things that can be done with open source software.
Follow up to SPA942 Personal Directory with LDAP
I just wanted to follow up on my previous post about using LDAP and PHP to push the directories on the SPA942.
I’ve included a whole lot of code below, and please note that a lot of this is me just getting the job done. The code is probably messy and poorly written, and some of it is taken from other sites/samples.
Unfortunately, blogger seems to take away all my nice formatting (indents etc…), so you’ll have to bear with me. Also, long comments appear across multiple lines in here, so that could make it even harder. Copy the code into a program like notepad++ and set language to php to make it easier to read.
Firstly, just need to include a few files etc…
set_time_limit(240);
include_once("common/dbConnection.php");
include_once("common/header.php");
include_once("common/ldapInfo.php");
include_once("voip_ext.php");
dbConnection.php connects to a Mysql DB that I use basically as an asset register. It contains IPs, allocations, extensions, invoice numbers yada yada yada. Originally, I drove this script entirely from LDAP, however once users started to have multiple extensions (depending on which site they were working from), LDAP (well Active Directory actually) just couldn’t cut it. Note that this still uses LDAP for getting user details (display name) and extensions. It cross references these with Mysql.
header.php is just some menu’s etc
ldapInfo.php is the connection to LDAP. It returns the info as $info
voip_ext.php is a list of extensions returned as $extensions. Note that the extensions have changed with firmware updates, so check your phone to see what yours are.
The rest of the code follows.
I’ve tried my best to include comments, and please remember this is not a complete solution (due to DB dependencies etc…) but will hopefully get you started.
If anyone is interested in the DB schema, leave a comment and I can post it here.
//MAIN
//get users extensions
$UserExtensions = getExtensions();
//100 slots to use. fill from bottom, so 99 -> 0
$extID = 99;
//for each extension, find the appropriate info
foreach ($UserExtensions as $ext) {
if ($ext <> "" && $ext <> 0){
$pDir .= ("&".dirEntry($info,$ext));
}
}
//tidy up and sort the pdir string
$pDir = substr($pDir,1);
$pDir = explode("&",$pDir);
sort($pDir);
//assign an extid to each entry
foreach ($pDir as &$entry) {
$entry = ($extensions[$extID]."=n%3D".$entry);
$extID--;
}
//more tidying of pdir
$pDir = implode("&",$pDir);
$pDir .= getGroups($info);
$pDir = str_replace(" ","%20",$pDir);
//need to fill in from extID to 0 to clean vacant entries from pdir
//note that this will wipe out any duplicate names (was occuring when users were leaving)
//this will also wipe out any personal entries (i.e. this script is the only way that entries will appear in the directory)
while ($extID > 0) {
$pDir .= "&".$extensions[$extID]."=;";
$extID--;
echo $extID."<br />";
echo $pDir."<br />";
}
//run the wget (comment this out to test sending to a single phone - use next bit instead)
$phoneIPs = GetPhoneIPs();
foreach ($phoneIPs as $phoneIP) {
updatePhonePDir($phoneIP,$pDir);
}
//uncomment for testing
//updatePhonePDir("192.168.10.170",$pDir);
//FUNCTIONS
function getExtensions(){
//this gets all the phone extensions that i've allocated in mysql. Note that other extensions probably exist in ldap, but they're not assigned to a phone and hence we don't want them here.
$sql = "select allocation.extension from allocation order by allocation.extension asc";
$result = mysql_query($sql);
while ($row = mysql_fetch_assoc($result)) {
$extensions .=$row["extension"].";";
}
mysql_free_result($result);
$extensions = explode(";", $extensions);
return $extensions;
}
function GetPhoneIPs(){
//this gets the ips of the phones. need this when performing the wget command
$sql = "select distinct phone_ip from asset where phone_ip not like ''";
$result = mysql_query($sql);
while ($row = mysql_fetch_assoc($result)) {
$phoneIPs .=$row["phone_ip"].";";
}
mysql_free_result($result);
$phoneIPs = explode(";", $phoneIPs);
return $phoneIPs;
}
function dirEntry($info,$ext){
//this is creating the sting that we eventually want to POST to the phone's pdir
$allocationDetails = getAllocationDetails($ext);
$phoneMAC = $allocationDetails[0];
$phoneIP = $allocationDetails[1];
$siteName = $allocationDetails[2];
$serverIP = $allocationDetails[3];
$userDetails = ldapUserDetails($info,$ext);
$userSamAccountName = $userDetails[0];
$userDisplayName = $userDetails[1];
$userComany = $userDetails[2];
$ringtone = getRingTone($phoneIP);
return "$userDisplayName;p%3D$ext;r%3D$ringtone";
//return "n%3D$userDisplayName;p%3D$ext;r%3D$ringtone";
}
function ldapUserDetails($info,$ext){
//this matches the extensions with the names from ldap (we don't keep names in mysql)
for ($i=0; $i<$info["count"]; $i++) {
if ($info[$i]["ipphone"][0] == $ext) {
$samaccountname = strtolower($info[$i]["samaccountname"][0]);
$displayName = $info[$i]["displayname"][0];
$company = $info[$i]["company"][0];
if ($company == "") $company = "GLiNTECH"; //catch accounts with no company name specified
}
}
$results = array($samaccountname,$displayName,$company);
return $results;
}
function getAllocationDetails($extension){
// this gets more info about the phone (we use multiple sites and servers)
$sql = "select asset.mac, allocation.extension, server.server_ip, asset.phone_ip, site.site_name
from allocation
join asset as asset on allocation.asset_id = asset.asset_id
join site as site on allocation.site_id = site.site_id
join server as server on site.site_id = server.site_id
where allocation.extension = '".$extension."' limit 1";
$result = mysql_query($sql);
if (!$result) {
$thisPhoneMac = $row["0000000000"];
$thisPhoneIP = $row["0.0.0.0"];
$thisSiteName = $row["NULL"];
$thisServerIP = $row["0.0.0.0"];
} elseif (mysql_num_rows($result) == 0) {
$thisPhoneMac = $row["0000000000"];
$thisPhoneIP = $row["0.0.0.0"];
$thisSiteName = $row["NULL"];
$thisServerIP = $row["0.0.0.0"];
} else {
// While a row of data exists, put that row in $row as an associative array
// Note: If you're expecting just one row, no need to use a loop
// Note: If you put extract($row); inside the following loop, you'll
// then create $userid, $fullname, and $userstatus
while ($row = mysql_fetch_assoc($result)) {
$thisPhoneMac = $row["mac"];
$thisPhoneIP = $row["phone_ip"];
$thisSiteName = $row["site_name"];
$thisServerIP = $row["server_ip"];
}
mysql_free_result($result);
}
$results = array($thisPhoneMac,$thisPhoneIP,$thisSiteName,$thisServerIP);
return $results;
}
function getRingTone($phoneIP){
//this checks to see if a user as set a custom ringtone.
if ($xmlstr = file_get_contents("http://$phoneIP/admin/spacfg.xml")){
if (strlen($xmlstr) < 1){
$ringtone = "1";
} else {
$xml = new SimpleXMLElement($xmlstr);
$ringtone = $xml->Default_Ring_1_;
if ($ringtone == "User 1") {
$ringtone = "11";
} elseif ($ringtone == "User 2") {
$ringtone = "12";
}
}
} else {
//default ringtone for people in the directory
$ringtone = "12";
}
return $ringtone;
}
function updatePhonePDir($phoneIP,$pDir){
//this generates the wget command. -t 1 means it will only try once. if it fails then it won't try again until the next day.
$command = "wget --post-data '".$pDir."' http://".$phoneIP."/pdir.spa -t 1";
runCommand($command); // or die("update to $phoneIP failed");
}
function runCommand($command){
//calls the command and writes some debugging to the screen
system($command,$returned);
echo $command."<br />";
echo $returned."<br /><br />";
return $returned;
}
function getGroups($info){
//this is used for getting groups in ldap and finding their exts etc... read up in a previous post for more on this. Note that groups are prepended with '*' to make them appear at the top of the directory
global $extID;
global $extensions;
for ($i=0; $i<$info["count"]; $i++) {
if (ereg("[[6][0-9]{3}]",substr($info[$i]["info"][0],0,6))){
//calling groups
$extensionstring .= "&".$extensions[$extID]."=n%3D*".strtoupper(str_replace(" ","%20",$info[$i]["name"][0])).";";
$extensionstring .= "p%3D".substr($info[$i]["info"][0],1,4).";r%3D11";
$extID--;
}
}
return $extensionstring;
}
Asterisk Bootcamp
Just a quick post to mention that I’m heading to Melbourne next month for the Asterisk Bootcamp (care of work). Should be a good opportunity to learn a lot of info about Asterisk and VoIP in general.
More SPA942 Firmware stuff
Well I’m trailing out the 5.15a firmware for the SPA942 phones. Firstly, I have figured out the problem I was having previously with the first line being one pixel out of alignment. For our users, we display $EXT – Line # on each of the lines, so for me it appears as:
In the previous (5.1.5) firmware, this was left aligned, where as the newer firmwares are right aligned. Because the number 1 is thinner than the numbers 2,3 and 4, the text looked out of line. Other people probably wouldn’t have this issue, because adding in the Line numbers was a custom change I made for my users (in response to too many questions about why there where 4 buttons).
The other issue I was having with
has been changed to
There’s also a new feature that allows you to set a background image that I’m hoping to play around with shortly – more info about that on voip-info
SPA942 Personal Directory with LDAP
A while ago, I posted a blog about the SPA942 Personal Directory. Well I’ve built on it a bit now and managed to get it populated from the information taken out of our LDAP server (Microsoft Active Directory).
When we allocate a phone to someone, we fill in the ipphone field with their extension. We then also add two other ipphone entries, the first being the MAC address of the phone, the second being the phone’s IP.
I’m also managing calling groups (queues) in LDAP by creating security groups and using the Notes field to enter the information that I then use to build Asterisk’s queues.conf file (I might post more on that later if people are interested). An example of one of these entries for the groups is:
fullname = {groupname}
strategy = ringall
timeout = 15
wrapuptime = 0
autofill = yes
autopause = no
maxlen = 0
joinempty = yes
leavewhenempty = yes
reportholdtime = yes
musicclass = default
From this information in LDAP, I have a PHP script that is run as a cron job each night to update the phones with the new Personal Directory.
I’ll post the PHP as one big chunk, and explain it a bit further below.
set_time_limit(300);
//Include a list of all the extensions - this is page that lists the id's for the pdir. $extensions=array('25454','25390','25582', etc...
include('voip_ext.php');
//Doing LDAP connection stuff
$ldap_host = "ldapserver";
$base_dn = "DC=domain,DC=com";
$filter = "(|(ipphone=*)(info=[*))";
$ldap_user = "CN=Admin User,CN=Users,DC=domain,DC=com";
$ldap_pass = "fullysecretpassword";
$connect = ldap_connect($ldap_host,$ldap_port)
or exit(">>Could not connect to LDAP server<<");
ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($connect, LDAP_OPT_REFERRALS, 0);
$bind = ldap_bind($connect, $ldap_user, $ldap_pass)
or exit(">>Could not bind to $ldap_host<<");
$read = ldap_search($connect, $base_dn, $filter)
or exit(">>Unable to search ldap server<<");
$info = ldap_get_entries($connect, $read);
//start doing things
ldapPhoneIPs($info);
//close the ldap connection
ldap_close($connect);
//FUNCTIONS
function ldapPhoneIPs($info){
$pDir = ldapUsers($info);
for ($i=0; $i<$info["count"]; $i++) {
if ($info[$i]["otheripphone"][0] <> "") {
echo "<br />".$info[$i]["samaccountname"][0]."<br />";
$phoneIP = $info[$i]["otheripphone"][0];
$phoneMAC .= $info[$i]["otheripphone"][1];
$ringtone = getRingTone($phoneIP);
$pDir2 = str_replace("r%3D0",$ringtone,$pDir);
updatePhonePDir($phoneIP,$pDir2);
}
}
}
//This just makes sure we don't overwrite somone's customised ringtone
function getRingTone($phoneIP){
$xmlstr = file_get_contents("http://$phoneIP/admin/spacfg.xml");
if (strlen($xmlstr) < 1){
$ringtone = "0";
} else {
$xml = new SimpleXMLElement($xmlstr);
$ringtone = $xml->Default_Ring_1_;
if ($ringtone == "User 1") {
$ringtone = "11";
} elseif ($ringtone == "User 2") {
$ringtone = "12";
}
}
return "r%3D".$ringtone;
}
function updatePhonePDir($phoneIP,$pDir){
$command = "wget --post-data '".$pDir."' http://".$phoneIP."/pdir.spa";
runCommand($command); // or die("update to $phoneIP failed");
}
function ldapUsers($info){
//get the list of users
global $extensions;
$extID = 99;
for ($i=0; $i<$info["count"]; $i++) {
if ($info[$i]["ipphone"][0] <> "") {
//individual users
if ($info[$i]["otheripphone"][0] <> "") {
$extensionstring .= "$extensions[$extID]=n%3D".str_replace(" ","%20",$info[$i]["displayname"][0]).";";
$extensionstring .= "p%3D".$info[$i]["ipphone"][0].";r%3D0&";
$extID--;
}
} elseif (ereg("[[6][0-9]{3}]",substr($info[$i]["info"][0],0,6))){
//calling groups
echo substr($info[$i]["info"][0],0,6);
$extensionstring .= "$extensions[$extID]=n%3D".strtoupper(str_replace(" ","%20",$info[$i]["name"][0])).";";
$extensionstring .= "p%3D".substr($info[$i]["info"][0],1,4).";r%3D0&";
$extID--;
}
}
$extensionstring = substr($extensionstring,0,(strlen($extensionstring)-1));
return $extensionstring;
}
function runCommand($command){
system($command,$returned);
echo $command ."<br />";
return $returned;
}
?>
Ok, so firstly we include another file. There’s nothing flash about this file, in fact all it contains is a list of all the id’s in the Personal Directory page
Next it’s a matter of setting up the LDAP connection and filters. I’m filtering for either an entry in the ipphone field (for individual users) OR the notes(info) field (for the groups).
Then we get into actually doing things.
The first bit parses the info from LDAP and extracts the fields I care about. I’ve added in filtering for custom ringtones, so that if someone has set a custom ringtone for a particular entry, it doesn’t get overwritten.
From there, we go through and construct the wget strings (see my previous post for an explanation on this). It puts regular users in CamelCase and groups in UPPERCASE so that users can distinguish the difference.
Once all that is done, we run the wget command and push out the updates.
