Grandstream Phone Book Creator for Elastix

Do you run the Elastix PBX management environment to Asterisk? Do you use Grandstream IP Phones? If you answered yes to both of these questions, then this might be helpful to you.

Grandstream GXP21xx/GXP14xx/GXP116x IP phones (and possibly others) can be configured to automatically download an XML formatted phone book file. The Elastix interface already has a place for naming the users, wouldn’t it be great to use that data for making an always up-to-date phone book file? That is the idea with this script. It was written for use with Elastix version 2.0, but will probably work with others (in the 2.x branch), and maybe FreePBX as well, I haven’t really tested.

To “install” the script simply put it in the root of your Elastix web directory space.

The format of the XML output is something like this:

<?xml version="1.0" encoding="UTF-8"?>
<AddressBook>
        <Contact>
                </Lastname>Test User<LastName/>
                <FirstName></FirstName>
                <Phone>
                        <phonenumber>1100</phonenumber>
                        <accountindex>1</accountindex>
                        <downloaded>0</downloaded>
                </Phone>
        </Contact>
</AddressBook>

Two versions of the script are presented, you only need one. The first script uses functions included inside of various files that should be present (AFAIK) in your Elastix web root. It is slower and will undoubtedly use more resources during execution. It may also be more flexible and resilient in that it will always use the latest version of the functions available to it. This is a double-edged sword of course, and might end up breaking it as well. Enter the second script, which uses just the applicable parts of the code from those aforementioned functions without needing to include any files. This script will probably work provided the database table stays the same, but your mileage may vary.

You can download both scripts from here: Download

The shorter version:

<?php
/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4:
  +----------------------------------------------------------------------+
  | Grandstream GXP21xx/GXP14xx/GXP116x IP Phone XML Phone Book Creator  |
  | For Elastix version 2.0 (maybe others) http://www.elastix.org        |
  +----------------------------------------------------------------------+
  | Made with recycled code, applicable rights                           |
  | Palosanto Solutions S. A., Coalescent Systems Inc, FreePBX, et al.   |
  +----------------------------------------------------------------------+
  | The contents of this file are subject to the General Public License  |
  | (GPL) Version 2 (the "License"); you may not use this file except in |
  | compliance with the License. You may obtain a copy of the License at |
  | http://www.opensource.org/licenses/gpl-license.php                   |
  |                                                                      |
  | Software distributed under the License is distributed on an "AS IS"  |
  | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See  |
  | the License for the specific language governing rights and           |
  | limitations under the License.                                       |
  +----------------------------------------------------------------------+
*/

header("Content-Type: text/xml");

require_once('admin/functions.inc.php');
require_once('admin/modules/core/functions.inc.php');
require_once('admin/common/php-asmanager.php');
require_once('DB.php'); //PEAR must be installed (why?)

$db_user = $amp_conf["AMPDBUSER"];
$db_pass = $amp_conf["AMPDBPASS"];
$db_host = $amp_conf["AMPDBHOST"];
$db_name = $amp_conf["AMPDBNAME"];

$datasource = 'mysql://'.$db_user.':'.$db_pass.'@'.$db_host.'/'.$db_name;
$db = DB::connect($datasource);

$extensions = core_users_list();
echo "<?xml version="1.0" encoding="UTF-8"?>\n";
echo "<AddressBook>\n";
$index = 0;
if (isset($extensions)) {
    foreach ($extensions as $key=>$extension) {
        $index= $index + 1;
        echo "\n\n\t<Contact>";
        echo "\n\t\t<LastName>" . $extension[1] . "</LastName>";
        echo "\n\t\t<FirstName></FirstName>";
        echo "\n\t\t<Phone>";
        echo "\n\t\t\t<phonenumber>" . $extension[0] . "</phonenumber>";
        echo "\n\t\t\t<accountindex>" . $index . "</accountindex>";
        echo "\n\t\t</Phone>";
        echo "\n\t</Contact>\n";
        }
}
echo "</AddressBook>\n";
?>

The more monolithic version:

<?php
/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4:
  +----------------------------------------------------------------------+
  | Grandstream GXP21xx/GXP14xx/GXP116x IP Phone XML Phone Book Creator  |
  | For Elastix version 2.0 (maybe others) http://www.elastix.org        |
  +----------------------------------------------------------------------+
  | Made from 99% recycled code, applicable rights                       |
  | Palosanto Solutions S. A., Coalescent Systems Inc, FreePBX, et al.   |
  +----------------------------------------------------------------------+
  | The contents of this file are subject to the General Public License  |
  | (GPL) Version 2 (the "License"); you may not use this file except in |
  | compliance with the License. You may obtain a copy of the License at |
  | http://www.opensource.org/licenses/gpl-license.php                   |
  |                                                                      |
  | Software distributed under the License is distributed on an "AS IS"  |
  | basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See  |
  | the License for the specific language governing rights and           |
  | limitations under the License.                                       |
  +----------------------------------------------------------------------+
*/

header("Content-Type: text/xml");

define("AMP_CONF", "/etc/amportal.conf");

$file = file(AMP_CONF);
if (is_array($file)) {
    foreach ($file as $line) {
        if (preg_match("/^\s*([a-zA-Z0-9_]+)=([a-zA-Z0-9 .&-@=_!<>"\']+)\s*$/",$line,$matches)) {
            $amp_conf[ $matches[1] ] = $matches[2];
        }
    }
}

require_once('
DB.php'); //PEAR must be installed
$db_user = $amp_conf["AMPDBUSER"];
$db_pass = $amp_conf["AMPDBPASS"];
$db_host = $amp_conf["AMPDBHOST"];
$db_name = $amp_conf["AMPDBNAME"];

$datasource = '
mysql://'.$db_user.':'.$db_pass.'@'.$db_host.'/'.$db_name;
$db = DB::connect($datasource); // attempt connection

$type="getAll";
$results = $db->$type("SELECT extension,name,voicemail FROM users ORDER BY extension", null);
foreach($results as $result){
    $extensions[] = array($result[0],$result[1],$result[2]);
}

#$extensions = core_users_list();
echo "<?xml version="1.0" encoding="UTF-8"?>\n";
echo "<AddressBook>\n";
$index = 0;
if (isset($extensions)) {
    foreach ($extensions as $key=>$extension) {
        $index= $index + 1;
        echo "\n\n\t<Contact>";
        echo "\n\t\t<LastName>" . $extension[1] . "</LastName>";
        echo "\n\t\t<FirstName></FirstName>";
        echo "\n\t\t<Phone>";
        echo "\n\t\t\t<phonenumber>" . $extension[0] . "</phonenumber>";
        echo "\n\t\t\t<accountindex>" . $index . "</accountindex>";
        echo "\n\t\t</Phone>";
        echo "\n\t</Contact>\n";
    }
}
echo "</AddressBook>\n";
?>

Before you can use the new phone book you have to update the phone to make it aware of it. I wrote a script that I talk about in another article for this purpose. If you only need to update one phone, or would rather do it manually the basic steps are outlined below.

These screens differ with firmware version, but the general concept should remain roughly the same, for example on newer firmware versions for my phone the phone book options are under Phonebook and then Phonebook Management

To make use of the phone book with the phone, navigate to the web interface on the device and login. I’m using a Grandstream GXP2120 phone. The default password is “admin”.
grandstream_web_login

After logging in click on the Settings tab and then choose Advanced Settings
grandstream_web_settings_advanced

Scroll down to the field Phonebook XML Download My Elastix installation redirects HTTP request to HTTPS. The Grandstream phone doesn’t seem to care if the certificate is backed by a common certificate authority. Most likely you’ll want to select HTTPS as shown. For Server Path leave off the protocol specification of the URL (e.g. http://, https://). Make sure to set a valid Download Interval (something of at least 5 or higher). Optionally chose to remove old entries in the phone book, this might help to avoid confusing stale data.
grandstream_phonebook_settings

Once the changes have been made you’ll need to reboot.
grandstream_reboot

Posted in General Nonsense, Telephony | Tagged , , | 11 Comments

Fun with Garmin Forensics

As you’ll recall from my Christmas post the company I work for is replacing their fleet management equipment, and in part that means upgrading some of our older Garmin GPS screens. As a result a steady stream of old equipment has been trickling into my office.

old_garmins

Even though these units no longer fit our needs, there is still plenty of life left to them.

Science!

Wikipedia states that “forensic science is the scientific method of gathering and examining information about the past“, our methods might not be too scientific, but these devices are chock-full of information about the past.

In preparation for finding them new homes, I thought it might be wise to see what kind of private data these things have stored. I came across a nice overview presentation on GPS forensics that covers a variety of models and brands. That presentation listed some files of interest:

Current.gpx
Archive.gpx
Position.gpx
GarminDevice.xml

After connecting the Garmin to a computer and waiting for the Garmin’s operating system to start I was able to see the Garmin as a storage device.

garmin_usb

Navigating to the \Garmin\GPX directory I was able to locate Current.gpx.
garmin_nuvi_5000_mass_storage

garmin_nuvi_5000_root_folder

garmin_nuvi_5000_garmin_folder

garmin_nuvi_5000_gpx_folder

GPX Format

The GPX in the GPX file format stands for GPS eXchange Format. It’s a fairly simple XML format with plenty of human readable items of interest. Here are some (slightly sanitized) snippets from the GPX file I recovered to give you an idea of the format. The file I was working with was devoid of line breaks, but I’ve added some here for clarity.

After a quick header we get into some more interesting stuff.

<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
<gpx xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1" creator="nĂ¼vi 5000" version="1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd">

The “metadata” section seems pretty boring. I think the time listed here is the last time the unit was used.

<metadata>
 <link href="http://www.garmin.com">
  <text>Garmin International</text>
  </link>
  <time>2014-01-16T21:44:20Z</time>
</metadata>

Now on to some good stuff, Waypoints.

<wpt lat="23.137160" lon="-81.687469">
  <ele>241.17</ele>
  <name>001</name>
  <sym>Waypoint</sym>
</wpt>

Some Waypoint entries contain “extensions”, these might include Address Book entries

<extensions>
 <gpxx:WaypointExtension>
  <gpxx:Categories>
    <gpxx:Category>Address Book</gpxx:Category>
  </gpxx:Categories>

Or just addresses

<extensions>
 <gpxx:WaypointExtension>
  <gpxx:Address>
    <gpxx:StreetAddress>101 W. Flagler St</gpxx:StreetAddress>
    <gpxx:City>Miami</gpxx:City>
    <gpxx:State>FL</gpxx:State>
    <gpxx:Country>USA</gpxx:Country>
    <gpxx:PostalCode>33130</gpxx:PostalCode>
  </gpxx:Address>
 </gpxx:WaypointExtension>
</extensions>

Far more interesting than the Waypoints however is the Tracks log. This data is a series of positions at a given time which leaves a trail of breadcrumbs which we can use to reconstruct the journey.

Here is part of one particular journey

<trk>
  <name>ACTIVE LOG: 08 JAN 2014 12:06</name>
   <trkseg>

After the “trkseg” element begins a series of coordinates, elevations, and times follow

<trkpt lat="23.884920" lon="-81.686757">
  <ele>196.25</ele>
  <time>2014-01-08T17:06:41Z</time>
</trkpt>
<trkpt lat="23.884317" lon="-81.686580">
  <ele>196.73</ele>
  <time>2014-01-08T17:06:43Z</time>
</trkpt>
<trkpt lat="23.884317" lon="-81.686580">
  <ele>196.73</ele>
  <time>2014-01-08T17:06:44Z</time>
</trkpt>
<trkpt lat="23.884317" lon="-81.686580">
  <ele>196.73</ele>
  <time>2014-01-08T17:06:45Z</time>
</trkpt>

…And so on

Google Earth

As you can see this information is easy to work with to suit your needs. If your needs are just to watch what happened and when, Google Earth is great tool for this. It turns out Google Earth already understands the GPX format, so we don’t need to extract any data manually.

Just click on Tools then GPS
google_earth_import_1

Select the Import from file option followed by the Import button
google_earth_import_2

Navigate to your Current.gpx file (in my case \Garmin\GPX), select it and click Open
google_earth_import_3

Google Earth will tell you what data it was able to find. Click OK
google_earth_import_4

On the left panel you can move between waypoints to see them on the map.
google_earth_view_imported_2

Fun

For even more fun, select one of the “Tracks” logs, and click the play button with breadcrumb icon (I assume that’s what that is anyhow).
google_earth_view_imported_3

Here is some sample video I captured from the playback of some track log data. The data isn’t real-time of course (that would be boring), but there is still some relevant timing preserved. Notice how you can even tell which stop lights were red by the time elapsed between movements. Very interesting and potentially revealing stuff.



Posted in General Nonsense, Software | Leave a comment

Garland & Garmin Greetings

My office at work wasn’t feeling very Christmas-like, so I flaked out a little and hatched a plan to address the stark lack of festiveness.

I have a bunch of these old Garmin Nuvi 5000 model GPS displays in my office right now.

garmin_5000

We are replacing our fleet management software and with it our vehicle tracking system.

Both the old and the new system have the ability to connect to Garmin GPS displays via FMI cable in order to push waypoints and information to the driver. Unfortunately the Garmin Nuvi 5000 displays are kind of old at this point, the FMI interface isn’t compatible with our new system (hence a pile of GPS screens on my desk). This cable previously wired in to the vehicle accessory power.

garmin_fmi_serial

I compiled a pile of useful ingredients.
christmas_tools

I twisted the power wires of the FMI cables together, soldered it up to some spare wire, and covered it with some shrink tube.
fmi_twisted

fmi_twisted_soldered

Using the multimeter I measured the current of one of the Garmin displays, it fluctuated a bit as the system booted up and looked for satellites, but it seemed to stay pretty well under 150mA. I ended up using 6 Garmins, so figure maybe 900mA, add some buffer room, and I decided that this 12 volt DC 2 Amp power supply would be a good donor.

garmin_xmas_power

The whole time I had Garmin’s commerical jingles stuck in my head.

Peace & Joy

Posted in General Nonsense | Leave a comment

Bash Snippet: Decimal to Binary Conversion

Convert a decimal value into the binary representation and vice versa in Bash using only built-ins. If you know a better way, please let me know. To ensure a properly formatted expression for the arithmetic expansion in bin2dec, the dec2bin function prefixes zeros as needed to pad to a character count evenly divisible by 8.

dec2bin () {
    num="$1"
    bin=""
    padding=""
    base2=(0 1)
    while [ "$num" -gt 0 ];
    do
            bin=${base2[$(($num % 2))]}$bin
            num=$(($num / 2))
    done
    if [ $((8 - (${#bin} % 8))) -ne 8 ]; then
            printf -v padding '%*s' $((8 - (${#bin} % 8))) ''
            padding=${padding// /0}
    fi
    echo $padding$bin
}


bin2dec () {
        echo $((2#$1))
}

Examples:

user@host:~$ dec2bin 1
00000001
user@host:~$ bin2dec 00000001
1
user@host:~$ dec2bin 255
11111111
user@host:~$ bin2dec 11111111
255
user@host:~$ bin2dec 1010101010101010
43690
user@host:~$ dec2bin 43690
1010101010101010

Perhaps not the most efficient way, but at least for small numbers it appears to be quicker than opening a subshell.

user@host:~$ time dec2bin 43690
1010101010101010

real    0m0.001s
user    0m0.000s
sys 0m0.000s
user@host:~$ time echo "obase=2;43690" | bc
1010101010101010

real    0m0.002s
user    0m0.000s
sys 0m0.000s
Posted in General Nonsense | 2 Comments

Firefox javascript:alert(); Not Working in Location Bar

Did you notice that the “javascript:” URI scheme has been removed from Firefox 6.0 and later? Sometimes I think Firefox is purposely trying to get me to switch browsers, but the intent of this change was evidently to fix a security issue. The downside is no more ad hoc debugging with javascript:alert(var); from the location bar. This is an article about the new way to do that kind of thing (in anecdotal form).

Some SSL and site validation services provide seals for their customers, little badges of warm “security” feelings. Say you want to demonstrate to a friend how meaningless those security seal images are. Just right-click and copy the image right?

firefox_javascriptimage-to-grab

Well usually, but Thawte (our example) was a bit more clever than many of the other snake-oil salesmen, and they added a little javascript to obfuscate the process. My right-click is hijacked, and I get a popup window instead.

firefox_javascript-right-click-hijack

No problem, we’ll just turn off javascript temporarily and try again.

firefox_javascript-disable-javascript

Hmm.. OK, so I guess they use javascript to load the image. Clever girl.

firefox_javascript-disabled-no-image

Alright, we’ll re-enable javascript and look at the page source. Ah, here, this looks promising.

firefox_javascript-viewsource-1

This seems to be what I want, but what’s in this seal_url variable, and these other things, this is starting to seem like too much work. Oh, hey, lets look at the “u2” variable, that seems like it might be helpful.

firefox_javascript-viewsource-2

So, we try to do the ‘ol javascript:alert(u2); and…nothing.

firefox-javascript-alert-uri-deprecated

No suprise, we already said that feature was deprecated, so how then do we get around it? One way is with a bookmarklet. Yeah, turns out “bookmarklet” is a word now, all it means is putting javascript in a bookmark location though (did we need a new word for that?).

firefox-javascript-bookmarklet

Clicking on the new bookmark returns the results we were hoping for, but it’s kind of a chore. Another method is to use the Web Developer Scratchpad. You can navigate to it as shown below, but that’s not much quicker than constantly editing a bookmark *ahem* bookmark-let. Hitting Shift+F4 however also brings up the Scratchpad, and is far more acceptable.

firefox-javascript-webdev-scratchpad-1

In the scratchpad we add in our alert(u2); call.

firefox-javascript-webdev-scratchpad-2

Run the script.

firefox-javascript-webdev-scratchpad-run

And, victory is mine.

firefox-javascript-success

The story of Firefox and the javascript ends here, but the story of the Thawte seal misappropriation has one more, unexpected detail. Thawte apparently was clever enough to look at the referrer passed by the browser during the image request. If the referer domain doesn’t match the validated domain, the end user is presented with an emblem indicating there is a problem with the “trust” that seal would normally instill. We could easy download the image file ourselves from the website, but it’s visibly date stamped, so that would lose some panache. Instead we could just make the request for the image in the user’s behalf without bothering to send a referrer.

<?php
header('Content-type: image/gif');
echo file_get_contents ('https://seal.thawte.com/getthawteseal?at=0&sealid=0&dn=WWW.THAWTE.COM&lang=en&gmtoff=300');
?>
Posted in General Nonsense | Leave a comment