I’m still working on customising the the Cisco 7940 I use with SIP firmware for VoIP calls and one of the items that’s now working well is the directory services functionality.
At the most basic level, the directory_url
directive may be set in one of the SIP configuration files (either SIPDefault.cnf or SIPmacaddress.cnf), for example:
directory_url: â€http://webserver/directory.xmlâ€
The contents of the directory.xml file are actually quite simple:
<CiscoIPPhoneDirectory>
<Title>IP Telephony Directory</Title>
<Prompt>People reachable via VoIP</Prompt>
<DirectoryEntry>
<Name>Bob</Name>
<Telephone>1234</Telephone>
</DirectoryEntry>
<DirectoryEntry>
<Name>Joe</Name>
<Telephone>1357</Telephone>
</DirectoryEntry>
<DirectoryEntry>
<Name>Operator</Name>
<Telephone>0</Telephone>
</DirectoryEntry>
</CiscoIPPhoneDirectory>
The trouble with this is that it’s just a static file. If I have a large directory, then I need to keep it up-to-date. That’s where a directory service comes into play. The Open 79xx XML Directory looks useful but it’s another application to install and manage on my infrastructure. I already have a directory (Microsoft Active Directory), so I thought it would be great if a piece of code could query the AD and output the file in a format that the 7940 understands.
Luckily I found such a piece of code, courtesy of a message posted to the Asterisk Users forum back in 2004 by Jeff Gustafson:
<?php
$ds=ldap_connect("ldapserver"); // must be a valid LDAP server!
if ($ds) {
$r=ldap_bind($ds); // this is an "anonymous" bind, typically read-only access
$sr=ldap_search($ds, "ou=People,dc=domainname,dc=com",
"telephoneNumber=*");
echo "<CiscoIPPhoneDirectory>\n";
echo "<Title>IP Telephony Directory</Title>\n";
echo "<Prompt>People reachable via VoIP</Prompt>\n";
$info = ldap_get_entries($ds, $sr);
for ($i=0; $i<$info["count"]; $i++) {
echo "<DirectoryEntry>\n";
echo "<Name>" . $info[$i]["cn"][0] . "</Name>\n";
echo "<Telephone>" . $info[$i]["telephonenumber"][0] .
"</Telephone>\n";
echo "</DirectoryEntry>\n";
}
echo "</CiscoIPPhoneDirectory>";
ldap_close($ds);
} else {
echo "error";
}
?>
Jeff’s code is great (my PHP skills are certainly not good enough to have written this myself) but Active Directory has an attribute for IP phone numbers (ipPhone), so I made a couple of edits to change the phone prompts and to make the LDAP query search on the ipPhone attribute:
<?php
$ds=ldap_connect("domaincontroller.domainname.tld"); // must be a valid LDAP server!
if ($ds) {
$r=ldap_bind($ds); // this is an "anonymous" bind, typically read-only access
$sr=ldap_search($ds, "ou=directorycontainer,dc=domainname,dc=tld",
"ipphone=*");
echo "<CiscoIPPhoneDirectory>\n";
echo "<Title>IP Telephony Directory</Title>\n";
echo "<Prompt>Active Directory Users</Prompt>\n";
$info = ldap_get_entries($ds, $sr);
for ($i=0; $i<$info["count"]; $i++) {
echo "<DirectoryEntry>\n";
echo "<Name>" . $info[$i]["displayname"][0] . "</Name>\n";
echo "<Telephone>" . $info[$i]["ipphone"][0] .
"</Telephone>\n";
echo "</DirectoryEntry>\n";
}
echo "</CiscoIPPhoneDirectory>";
ldap_close($ds);
} else {
echo "error";
}
?>
I still needed a couple of tweaks to get this working though – not to the script, just to: the webserver I used to serve it; to Active Directory; and finally to the phone configuration.
First up, you need a web server with PHP installed (I used PHP 5.2.6 on IIS 6.0). This also needs the LDAP extension to be enabled by uncommenting extension=php_ldap.dll
in php.ini. The extensions folder (e.g. C:\phpinstallationfolder\extensionfolder) also needs to be appended to the %path%
system variable.
The script is actually for a generic LDAP directory (nothing wrong with that) but recent versions of Active Directory do not allow anonymous access by default. Daniel Petri has a detailed article on anonymous LDAP operations in Windows 2003 AD and that gave me the information that I needed to open up the parts of the directory that I wanted the script to read – basically: setting the 7th bit of the dsHeuristics flag on CN=Directory Service,CN=Windows NT,CN=Services,DC=domainname,DC=tld to 2
on the forest root domain; waiting for replication to complete; granting ANONYMOUS LOGON read access on the appropriate objects and List Contents access on the OU that contains the object(s). Alternatively, it should be possible to edit the script to use an authenticated logon (and sorting by surname wouldn’t go amiss either) but it’s getting late now and that will have to wait for another day! In the meantime, Geoff Jacobs’ post on creating a personal directory for the Linksys SPA942 using LDAP should provide some inspiration.
Last, but by no means least, the directory_url directive needs to be edited to reflect the name of the PHP script instead of the original static XML, for example:
directory_url: â€http://webserver/directory.phpâ€
In order to pick up the changes, the phone will need a reset.
Now, when I access the external directory from the phone using the directory button and option 5, I’m presented with a list of contacts from Active Directory. Furthermore, because the web server uses dynamic content, the details are as current as the directory server that it refers to.