Customising a Cisco 79xx IP Phone: directory services

This content is 16 years old. I don't routinely update old blog posts as they are only intended to represent a view at a particular point in time. Please be warned that the information here may be out of date.

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.

6 thoughts on “Customising a Cisco 79xx IP Phone: directory services

  1. You should be XML-encoding those values you putting into the tags:

    function xmlencode($text)
    {
    $text = str_replace(“&”, “&amp;”, $text);
    $text = str_replace(“”, “&gt;”, $text);
    $text = str_replace(“‘”, “&apos;”, $text);
    $text = str_replace(“\””, “&quot;”, $text);
    return $text;
    }

  2. Thanks for this Duncan. As you can see, I’m certainly no developer so it’s good to get some advice from someone who actually knows what he’s doing!

  3. Hi. Configure a Cisco 79xx to use with SIP firmware for VoIP calls, and I need to use the LDAP search. How do I get the user / pass login. Should I use ldapUser under ldap_bind?. You made it work without specify the ldap port?

  4. @zzbo – you don’t need any credentials because you’re using anonymous logons (at least, that’s what I described above). As for the port number, it’s not specified because the default LDAP port is used.

  5. I found the error, and can get the users. The php source has the correct structure as you show in the static file above. Looks perfect in my webbrowser. But when I try it on the cisco (7940) I get : CMXL error and XML parse error. Also tried to put in headers : header (“content-type: text/xml”);
    and also header(‘Content-Type: application/xml’); but no luck.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.