Contacts/Phonebook on Cisco 7900 series

As I said if you want to experiment with 7821 phones I’ll be happy to ship you a few.

All I can report is my own experience that the 7961 I have in my test lab gave an error until the doctype was changed. This was not required for other 79xx phone models. However, the other 79xx phones I have in the lab didn’t care one way or another about what the doctype was set to.

My concern is your claim in your guide that it will apply equally to all 79xx phones. I don’t believe that is true as Cisco had subtle differences in firmware for all of these models. in particular some SEPMAC directives are ignored in certain firmware versions while others are not ignored. My advice is that you explicitly list the phones you have tested with and state that what you put together works out of box with those and their firmware, and state that other phones in the 79xx model series SHOULD work but are not guaranteed. Cisco released a plethora of different models of that phone and a lot of different firmware for them. In particular the 7960’s don’t use the SEPMAC config file at all they use a SIPMAC file which is completely different.

I do have certain phone models in this series that have cracked plastic and so on but the electronics are good. If you really and truly want to play and test them I’ll ship them to you.

Nowadays my main interest is interoperability between the usecallmanager patch and the 8941 phones and 8845 phones and I’m not researching the other phone models anymore. I first got into these because I got a lot of the 7941s for free. I did not know about the BLF issues or the way Cisco had messed up the enterprise firmware for their phones.

The Cisco phones I currently have registered into chan_sip patched with the usecallmanager patch on my test PBX are

CP-9971
CP-7841
CP-7960G
CP-8941
CP-7961G (with additional outrigger keypad attached, and used for other extension monitoring)
CP-6941
CP-6921

The 69xx series are newer than the 79xx series. After the 7940 and 7960 which are the oldest and use a SIPMAC file, the subsequent models that looked a lot like the 7940/7960 all seemed to end up with the last model digit something other than 0. These use the SEPMAC file. Cisco seems to have made some major changes to the SEPMAC file for newer phones and for phones that are more full featured.

The 8941’s are full video phones as is the 9971 and can make video calls to each other. All of the phones have directory keys and all will digest XML directory files. However, depending on the model in use the SEPMAC directives to get them to do this vary. I DON’T know that all of the phones can do the servicesURL though.

Cisco never really documented the SEPMAC file. What is mostly known about it comes from SEPMAC files people got from old UCMs from before the time that Cisco started encrypting the SEPMAC files and from smatterings of mentions in the Cisco documentation. Some of the oldest SPEMACS got posted onto sites like www.voip-info.org when Cisco admins were still dealing with unencrypted SEPMACs and those posts are still out there 2 decades later. Through trial and error people discovered that these same SEPMACs worked for all of the Cisco phones up through ones currently being produced, with slight modifications. Periodically someone gets a bee in their bonnet and tries to summarize the information like the following effort:

GitHub - NamoDev/Cisco-IP-Phone-Provisioning-Files: Samples of Cisco IP phone configuration/XML files for use with Asterisk!

What I’ve found is that it appears the most full featured SEPMAC ever released was for the 9971 and it works (with slight modifications) with the current 88xx and 78xx models. Someone did document that and it’s formed the basis for every other SEPMAC purported to be for more modern phones.

Oh and one last interesting thing is the Cisco softphone, Cisco IP Communicator. That is also provisioned via SEPMAC and I believe it also would work with your directory idea as well. But it uses a nonstandard addition and the tftp server must be patched to configure it under Asterisk.

It’s obvious that Cisco’s developers probably have some hidden secret document that explains all SEPMAC entries for all phone model and all firmware versions because in order for the UCM to auto-provision those models it would have to know about all these permutations. It’s also obvious Cisco knows just how vulnerable their UCM is because we almost have all the pieces needed to create a custom built FreePBX system that would be a drop-in replacement for a Cisco UCM.

Anyway, you implemented your directory by creating a separate database which has the advantage that the admin can prevent internal extensions and so on that they don’t want the users making calls to from appearing. But it might be handy to add some scripts to populate that database in the first place.

Here’s what I use to create an XML file from the Asterisk extension database:

<?php
// File: gs_phonebook.php
// version: 1.1
// Date: 2011-03-16
// Modified: 2015-06/26
// Author: Yves Menge ([email protected])
// Modified
// Description: Generating a XML Phonebook from FreePBX MySQL DB
// Modification: Use 'users' table instead of devices allowing exclusion of some devices
// Populated both first and last name fields, changed output filename

//!!Enable for Debug only!!
// error_reporting(E_ALL);
// ini_set("display_errors", 'ON');

// Database settings
$DBhost = "localhost"; //** Insert your host here
$DBuser = "root"; //** Insert your DB user here
$DBpass = "asterisk"; //** Insert your password here
$DBdatabase = "asterisk"; //** change only when installed Free PBX in a non-common way!

// Connect to the Database and get all users
$DBlink = mysqli_connect($DBhost, $DBuser, $DBpass) or die("Could not connect to host.");
mysqli_select_db($DBlink, $DBdatabase) or die("Could not find database.");
session_start();
$DBquery = "SELECT extension, name FROM users ORDER BY name ASC";
$QUERYresult = mysqli_query($DBlink, $DBquery) or die("Data not found.");
//Setup XMLWriter
$writer = new XMLWriter();
// $writer->openURI('/var/www/html/cisco/phonebook.xml'); //** If your TFTP server is using another root directory as /tfptboot, chang the path here!
// comment the following line and uncomment above line if output to file
$writer->openMemory();
$writer->openURI('php://output');
$writer->setIndent(4);

//Begin output
$writer->startDocument('1.0');
$writer->startElement('CiscoIPPhoneDirectory');
$writer->startElement('Title');
$writer->writeRaw('IP Telephone Directory');
$writer->endElement();
$writer->startElement('Prompt');
$writer->writeRaw('Beach House Extensions');
$writer->endElement();

//Add extensions / contacts from devices to the xml phonebook
while ($contact=mysqli_fetch_array($QUERYresult)){
list($fn,$ln) = explode(' ',$contact['name'],2); //** Separate first name by space.  Last name = remainder of field
//!!Enable for Debug only!!
// print $fn . $ln . $contact['extension'] . '<br>';
$writer->startElement('DirectoryEntry');
$writer->writeElement('Name',$ln);
$writer->writeElement('Telephone',$contact['extension']);
$writer->endElement();
}

$writer->endElement();
$writer->endDocument();
$writer->flush();
// remove following if output to file
$writer->outputMemory();
?>

It is crude of course but then I load the XML into an editor and delete the extensions I don’t want, the result is directly digestible by the phones.

What you could do is write a parser that would slurp up the XML then stuff it into your database.

Here’s another script from here:
PHP – LDAP Query AD for Computer Accounts – Geeks Hangout
that is an example of dumping all accounts from a Microsoft AD

It could be easily modified to pull names and extensions from AD and also create an XML file the phones could digest, or that could be edited and then used to populate your database:

<?php
 
//LDAP Bind paramters, need to be a normal AD User account.
$ldap_password = 'AD_Password';
$ldap_username = '[email protected]';
$ldap_connection = ldap_connect("domain.tld");
 
if (FALSE === $ldap_connection) {
    // Uh-oh, something is wrong...
    echo 'Unable to connect to the ldap server';
}
 
// We have to set this option for the version of Active Directory we are using.
ldap_set_option($ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3) or die('Unable to set LDAP protocol version');
ldap_set_option($ldap_connection, LDAP_OPT_REFERRALS, 0); // We need this for doing an LDAP search.
 
if (TRUE === ldap_bind($ldap_connection, $ldap_username, $ldap_password)) {
 
    //Your domains DN to query
    $ldap_base_dn = 'DC=domain,DC=tld,DC=tld';
 
    //Get standard users and contacts
    $search_filter = '(|(objectCategory=Computer))';
 
    //Connect to LDAP
    $result = ldap_search($ldap_connection, $ldap_base_dn, $search_filter);
 
    if (FALSE !== $result) {
        $entries = ldap_get_entries($ldap_connection, $result);
 
        // Uncomment the below if you want to write all entries to debug somethingthing 
        //var_dump($entries);
 
        //Create a table to display the output 
        echo '<h2>AD Computer Results</h2></br>';
        echo '<table border = "1"><tr bgcolor="#cccccc"><td>Name</td><td>Descrption</td></tr>';
 
        //For each account returned by the search
        for ($x = 0; $x < $entries['count']; $x++) {
 
            //
            //Retrieve values from Active Directory
            //
 
            //Common Name
            $LDAP_CN = "";
 
            if (!empty($entries[$x]['cn'][0])) {
                $LDAP_CN = $entries[$x]['cn'][0];
                if ($LDAP_CN == "NULL") {
                    $LDAP_CN = "";
                }
            }
 
            //Description
            $LDAP_Description = "";
 
            if (!empty($entries[$x]['description'][0])) {
                $LDAP_Description = $entries[$x]['description'][0];
                if ($LDAP_Description == "NULL") {
                    $LDAP_Description = "";
                }
            }
 
            echo "<tr><td><strong>" . $LDAP_CN . "</strong></td><td>" . $LDAP_Description . "</td></tr>";
        } //END for loop
    } //END FALSE !== $result
 
    ldap_unbind($ldap_connection); // Clean up after ourselves.
    echo ("</table>"); //close the table
 
} //END ldap_bind

(lost post from 2025-03-19 08:14:25 restored by mod on 2025-03-22 and requested review by @tmittelstaedt)