Grandstream Simple HTTP Configuration Pusher

Here is a simple bash script that logs into the web interface of a Grandstream IP phone and submits some configuration changes, and then reboots the device for the changes to take effect. I wrote this to update a handful of Grandstream phones with a new phonebook XML URL, but it could be used for other configuration changes as well. This was tested with a Grandstream GXP2120 on firmware versions 1.0.1.56, 1.0.1.64, 1.0.1.105, 1.0.4.23, and 1.0.6.7, but might adaptable for other Grandstream devices as well.

Grandstream has configuration template data for a number of devices. You can download a zip archive of them from the following URL: http://www.grandstream.com/tools/GAPSLITE/config-template.zip. The configuration is made up of a pair of values, a P-code and the P-codes applicable value, Grandstream calls these P values in some of their documentation.

My goal was to get my Grandstream phones to periodically download my automatically generated phonebook file discussed in a previous blog post. The applicable configuration details in my case are shown below.

# Enable Phonebook XML Download. # 0 - No, 1 - YES, HTTP, 2 - YES, TFTP, 3 - Yes, HTTPS. Default is 0
# Number: 0, 1, 2, 3
# Mandatory
#P330 = 0

# Phonebook XML Server Path
# This is a string of up to 256 characters that should contain a path to the XML file. It MUST be in the host/path format.
# For example: directory.grandstream.com/engineering
# String
#P331 =

# Phonebook Download Interval (in minutes)
# Valid value range is 5-720. Default is 0 for disabled
# Number: 0, 5-720;
#P332 = 0

# Remove Manually-edited entries on Download. 0 - No, 1 - Yes. Default is 1
# Number: 0, 1
# Mandatory
#P333 = 1

# Sort Phonebook by. 0 - Last Name. 1 - First Name. Default is 0
# Number: 0, 1
# Mandatory
#P2914 = 0

My Enable Phonebook XML Download method is HTTPS, my Phonebook XML Server Path is “10.0.0.1/phonebook.php“, I want the phone to download on a 30 minute interval and I want it to replace any phone book entries the user might have created.

My applicable P-values were:
P330=3
P331=10.0.0.1/phonebook.php
P332=30
P333=1
I then concatenate these P-values into one long string without any white space using the ampersand as a separator:

P330=3&P331=10.0.0.1/phonebook.php&P332=30&P333=1

Example usage

Here is how I updated a single phone with the P-values discussed earlier. First I set a shell variable containing the values. My device is located on the LAN at 10.0.0.172. I use this IP as the first argument to the script, and the P-values variable in the second argument. If you want to specify the P-values directly to the script without a shell variable, just make sure to quote them so that the ampersands don’t get misinterpreted by your shell. The final argument to the script is the web interface password of the device being updated.

Pvalues="P330=3&P331=10.0.0.1/phonebook.php&P332=30&P333=1"
[root@phone scripts]# ./gs_push.sh 10.0.0.172 $Pvalues admin
Config sent, rebooting...
Saving User Data...Done
Blocks Used ( 119 / 1024 )

Success!

Only one argument (the IP address) is required, the script will ask for the additional data if it isn’t passed in to it from the command line.

[root@phone scripts]# ./gs_push.sh
Usage: ./gs_push.sh <IP Address of Grandstream device> [P-value pairs] [password]

Here is an example of updating just the phone book URL using a more interactive mode to the script

[root@phone scripts]# ./gs_push.sh 10.0.0.172
Enter password [admin]: *****
Enter the P-code and value pair. Use '&' to separate multiple P-values.
Example: P330=3&P331=10.0.0.1/phonebook.xml&P332=30&P333=1
P-values:P331=10.0.0.4/phonebook.php
Config sent, rebooting...
Saving User Data...Done
Blocks Used ( 119 / 1024 )

Success!

Here are the commands I used to update all of the Grandstream devices communicating with my Asterisk PBX. I grep for the Grandstream OUI in the list of MAC addresses outputted from the “arp” command. These devices will usually be communicating with the Asterisk server periodically anyhow, and so probably they are already listed in the ARP table. To try and hedge that bet, all the devices are first sent a ping request to generate some new traffic. The output from the gs_push.sh script is suppressed by redirecting it to /dev/null, however in the event of a failure the script should announce via STDERR the IP address of the device it had trouble with.

Pvalues="P330=3&P331=10.0.0.4/phonebook.php&P332=30&P333=1"
asterisk -rx "sip show peers" |awk '{print $2}' |grep '\.' |xargs -n1 ping -c1 >/dev/null
arp |grep -i '00:0b:82' |awk '{print $1}' |xargs -i{} -n1 ./gs_push.sh "{}" "$Pvalues" admin >/dev/null

Download

#!/bin/bash

#  +----------------------------------------------------------------------+
#  | Grandstream GXP21xx IP Phone HTTP POSTing P-value config pusher      |
#  | May work with with other Grandstream devices, use at your own risk.  |
#  +----------------------------------------------------------------------+
#  | For the latest P-value descriptions consult the applicatble firmware |
#  | release notes and configuration template descriptions:               |
#  | http://www.grandstream.com/tools/GAPSLITE/config-template.zip        |
#  +----------------------------------------------------------------------+
#  | 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.                                       |
#  +----------------------------------------------------------------------+

get_password () {
        saveIFS="$IFS"
        IFS=$'\n'
        while read -s -n 1 char
        do
                case $(printf "%d\n" \'$char) in

                127)
                        if [ "${#passwd}" -gt 0 ]; then
                                echo -ne "\b \b"
                                passwd="${passwd:0:${#passwd}-1}"
                        fi
                        ;;
                0)
                        echo
                        break
                        ;;
                *)
                        echo -n "*"
                        passwd+=$char
                esac
        done
        IFS=$saveIFS
}

target_ip="$1"
if [ -z "$target_ip" ]; then
        echo "Usage: $0 <IP Address of Grandstream device> [P-value pairs] [password]" >&2
        exit 1
fi

Pvalues="$2"
passwd="$3"

if [ -z "$passwd" ]; then
        # If the second argument isn't a valid P-code pair use it as the password
        if [ "${Pvalues//'='}" == "$Pvalues" ]; then
                passwd="$Pvalues"
                unset Pvalues
        fi
fi
if [ -z "$passwd" ]; then
                echo -n "Enter password [admin]: "
                get_password
                if [ -z "$passwd" ]; then
                        passwd="admin"
                fi
fi

if [ -z "$Pvalues" ]; then
        # See if anything is waiting on stdin
        read -t1 Pvalues
        if [ -z "$PValues" ]; then
                # Or just ask the user..
                echo "Enter the P-code and value pair. Use '&' to separate multiple P-values."
                echo "Example: P330=3&P331=10.0.0.1/phonebook.xml&P332=30&P333=1"
                echo -n "P-values: "
                read Pvalues
        fi
fi

get_sid() {
        while read -r || [[ -n "$REPLY" ]]
        do
                if [ -n "$sid" ]; then
                        break
                fi
                if [ "${REPLY:0:23}" == "Set-Cookie: session_id="  ]; then
                        sid="${REPLY:23}"
                        sid="${sid//;}"
                fi
                if [ "${REPLY//'"sid"'}" != "$REPLY" ]; then
                        sid="${REPLY#*'"sid"'}"
                        sid="${sid#*'"'}"
                        sid="${sid%%'"'*}"
                fi

        done
        echo "$sid"
}


post_status() {
        # Old firmware says need to reboot, new firmware says success
        # new firmware doesn't end with a line break :(
        while read -r || [[ -n "$REPLY" ]]
        do
                if [ "${REPLY//eboot}" != "$REPLY" ]; then
                        echo "1"
                        break
                fi
                if [ "${REPLY//success}" != "$REPLY" ]; then
                        echo "1"
                        break
                fi
        done
}

reboot_status() {
        success=0
        headers=0
        while read -r || [[ -n "$REPLY" ]]
        do
                if [ -z "$REPLY" ]; then
                        # header data done
                        headers=1
                fi
                if [ "${REPLY//'{"results":['}" != "$REPLY" ]; then
                        if [ "${REPLY//[1]}" != "$REPLY" ]; then
                                success=1
                        fi
                        break
                fi
                if [ "${REPLY//'savereboot'}" != "$REPLY" ]; then
                        success=1
                        break
                fi
                if [ "${REPLY//'relogin'}" != "$REPLY" ]; then
                        success=1
                        break
                fi
                # fw 1.0.1.56 requires a different reboot action
                if [ "${REPLY//'The requested URL was not found'}" != "$REPLY" ]; then
                        curl -s -i -b "session_id=$sid" "http://$target_ip/cgi-bin/rs" |reboot_status
                        success=2
                fi

                # print extra saving info mixed in the headers, but ignore other bits
                if [ "${REPLY//:}" != "$REPLY" ]; then
                        unset REPLY
                fi
                if [ "${REPLY:0:4}" == "HTTP" ]; then
                        unset REPLY
                fi
                if [ "${REPLY:0:1}" == "<" ]; then
                        unset REPLY
                fi
                if [ "$headers" == "0" ] && [ -n "$REPLY" ]; then
                        echo "$REPLY"
                fi

        done
        if [ "$success" == "1" ]; then
                echo "Success!"
        fi
        if [ "$success" == "0" ]; then
                echo "Fail. ($target_ip)" >&2
        fi
}


if [ -z "$Pvalues" ]; then
        echo "No configuration parameters specified." >&2
        exit 1
fi

# Older firmware passed password as "P2", newer firmware passes as "password"
# <form> action is the same
sid="$(curl -s -i -d "P2=$passwd&password=$passwd" "http://$target_ip/cgi-bin/dologin" |get_sid)"

if [ -z "$sid" ]; then
        echo "Login failed." >&2
        exit 1
fi

# <form> action for config update is different across firmware versions
# Try old firmware style first
status="$(curl -s -i -b "session_id=$sid" -d "$Pvalues&update=Update" "http://$target_ip/cgi-bin/update" |post_status)"

if [ -z "$status" ]; then
        # Try new firmware config update action
        status="$(curl -s -i -b "session_id=$sid" -d "sid=$sid&$Pvalues" "http://$target_ip/cgi-bin/api.values.post" |post_status)"
fi

# Try new firmware way if the last attempt failed
if [ -n "$status" ]; then
        echo "Config sent, rebooting..."
        # Old firmware used query_string to pass REBOOT, new firmware uses POST data
        curl -s -i -d "request=REBOOT&sid=$sid" -b "session_id=$sid" "http://$target_ip/cgi-bin/api-sys_operation?REBOOT" |reboot_status
else
        echo "Couldn't update configuration ($target_ip)" >&2
fi
Posted in General Nonsense, Telephony | Tagged , , | Leave a comment

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 0.5, but will probably work with others, 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 and you have PHP PEAR installed, 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 0.5 (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

$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 0.5 (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 , , | Leave a comment

Blog Update

Oh no, a “blog update” post! That seems to be the harbinger of blog doom from what I’ve noticed across the web. I hope that isn’t the case here, but it has been 9 months without a peep, so I figure a little explanation is in order.

In the beginning…

As I’ve explained in the first post to this blog, I had the ethertubes.com domain sitting around not doing much for a while. I let it expire and then snatched it back up with new purpose. I had taken a different job and thought it would be fun to chronicle some of the work I was doing, the problems I ran into, and general related ranting. No one reads this blog, I’m under no delusion about that. With no readers it seemed folly to write about my work experience in a journal format or with focus on my personal perspective. When I did something that I thought other people could benefit from I would write about it, sharing the relevant information (and then some). That post format has managed to turn up the occasional poor soul who finds my content through various search engines. As someone who often searches the web for answers to my own problems, I enjoy occasionally being able to help others. It is this notion of doing something helpful that gives me the motivation to write new articles.

A “new” new-job

About 9 months ago I took a new job, it has been keeping me pretty busy and entertained. I’ve managed to find some time to work on things that interest me outside of work as well, but not much extra time for writing about them. I’ve been missing the ability to spew my genius on to the world however, and I’m resolved to try to create more time for sharing my projects with the Internet. I suppose “resolved to try” doesn’t sound all that promising.

Crash

If I had found any extra time to contribute to the blog it would have been spent on resurrecting the server. I had a bargain-basement discount VPS from ChicagoVPS, and it did what you might expect a bargain-basement VPS to do. ChicagoVPS informed me that all my data was gone. For as little as I paid them I really can’t be too sore about it. Luckily, I had my own not-so-outdated backups. My renewal date with ChicagoVPS was fast approaching and in the face of the crash I decided I’d move on to a higher class of service at a different provider. The problem is I didn’t pick a provider. I worked at my new job and my website remained offline. My personal emails bounced in the wind, and my Google search results and ranking faded away. They say nothing on the web ever truly goes away, but without search engines to navigate you, I doubt you would have found many of my posts (a few have been recycled on other blogs).

Resurrection

I found enough time to signup with Digital Ocean as my new provider, setup my base Linux install, revive my email (more or less), and bring my old blog content back online. That was 2 months ago, so I’m long over do for some fresh content. My continued absence certainly hasn’t been for a lack of things to write about. With 182 articles drafted in various forms of readiness there is a lot I could churn out. While I’m working to find more time to create new content, I’ll try to appease my imaginary readers by releasing some of the reserved stock.

Sincerely,
-Chris / Ethertubes.com

Posted in General Nonsense | Leave a comment

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

SanDisk 25th Anniversary Contest

sandisk_contest

A pile of SanDisk micro SD cards showed up on my desk today, part of an order for some new “smart” phones. I noticed that there was some print about a 25th anniversary contest on the package. The odds on something like that surely sucks, but I figured, “hey, I have a pile of these, why not?“.

Inside the packaging of eligible SanDisk brand products there is a stamped bit of plastic with your special code. I was quickly disheartened to learn that all of my special codes were the same code.

sandisk_code

“I must be looking at the wrong number” I thought, but a visit to the SanDisk contest site confirmed my suspicion. If you want to play this game legitimately without a purchase you can mail in a 3×5″ card, but that seems pretty pointless since evidently these special codes are just to fool the rubes and are in no way unique. According to the official rules (and enforced by the website) each person/email address can try up to 10 codes. Each code puts in an entry for the $25,000 prize and gives 5 instant spins of the prize wheel. I have at least 17 left over codes to use, but I guess someone else will have to use mine. As you can see from the picture, all of my codes were marked 54-53-10242. I had hoped for some Real Genius-style Frito-Lay contest fun, but these rules were not compatible.

sandisk-slot-machine

The official rules do not list the odds in winning, probably because you can’t really determine odds without knowing how many people will play (sorry Lazlo). Suffice it to say the odds appear to be worse than 1 in 50, as none of my instant win attempts succeeded. If you feel like wasting your time on a game you too can almost certainly not win, I’d recommend the SanDisk 25th Anniversary Contest. It’s a fun 15 minute diversion from your otherwise productive day.

sandisk-lost

I played around with Firebug until I felt better, but it wasn’t much consolation.

sandisk-won-nothing

Posted in General Nonsense | Leave a comment

Remove Hidden Hardware in Device Manager

This article applies to Windows 2003 and Windows XP, it may apply to other versions of Windows as well, but I have not tested.

After converting a Windows 2003 server to a virtual machine I had some problems. The OS would not boot properly, although I could still boot into “Safe Mode with Command Prompt”. Many times this problem is caused by the OS trying to load device drivers for components no longer in the system. Here is a quick tip on how to “remove” devices that are no longer present on your system.

If the “devmgr_show_nonpresent_devices” environment variable is set to “1” when Device Manager is opened it will show devices on the system that are no longer present as well as devices that are currently detected. This feature is documented in Microsoft knowledge base article 315539 http://support.microsoft.com/kb/315539

After booting into the command prompt I ran the following commands:

Microsoft Windows [Version 5.2.3790]
(C) Copyright 1985-2003 Microsoft Corp.

C:\Documents and Settings\Administrator>set devmgr_show_nonpresent_devices=1

C:\Documents and Settings\Administrator>start devmgmt.msc

Enable “Show hidden devices“. Although “Show hidden devices” is always available from the “View” menu, it does not show non-present hardware if “devmgr_show_nonpresent_devices” is not set to “1”.

Several items show up now with faded/ghostly versions of the normal item icon. The “HP” SCSI drives listed below are no longer present in the system and can be removed. On my system several network card drivers, some SCSI drivers, a display driver, and other system components that were no longer required were found.

Posted in General Nonsense | Leave a comment

Canon, [Third-]World Class Support

Here is rant of no redeeming quality other than maybe a little amusement. I was searching for a way to use both legal sized and 8.5×11″ paper in a Canon MF8350Cdn without needing to constantly switch out the paper and adjust the paper tray. I found a “cassette feeding unit” under “Supplies & Accessories” for my device at Canon’s site, but there was no additional info. The “250-sheet” description lead me to believe that maybe paper was involved, so I was feeling a little optimistic.

There was no link to click for additional details, nothing of any use for getting a little more insight about the accessory. I tried to use Canon’s search feature for “Cassette Feeding Unit-V1″, which turned up nothing. I managed to find a possible part number from a third-party website, but that too failed to lead me to any product literature on Canon’s site.

I checked some vendor websites for details, or at least a clue as to what this thing is, I figure maybe if I see a picture I’ll know if this is worth perusing.

CDW had no picture.

TigerDirect had no picture.

Results from a Google image search produced conflicting pictures. One of the pictures looks like it’s an additional tray (desired) and the other looks like it’s some kind of funky attachment that maybe holds more paper in the multi-purpose tray (not desired). Well, at least this accessory appears to be related to paper of some kind.

This device costs like $200, and I’m not even sure that it does what I want. I’d like to know it’s at least in the ball park of what I’m looking for before ordering. I broke down and emailed Canon customer service. The website, as I mostly anticipated, did not provide me with an email address, but rather directed me to some obnoxious html form (which FYI, in turn generates a form email to “carecenter@cits.canon.com”).

Oh look! Awesome, my printer isn’t even in the list. Sarcasm, growing.

So finally I send the following message:

I have a color imageCLASS MF8350Cdn. I am inquiring about the optional accessory “Canon Cassette Feeding Unit-V1″ (0732A024AA), there is essentially no information available about this item on the Canon USA website. Using the accessory can I configure the primary tray for legal sized paper, and use the “Canon Cassette Feeding Unit-V1″ for 8.5×11″ paper? Does the additional tray provided by the “Canon Cassette Feeding Unit-V1″ work independently of the primary tray?

A day later I get this irrelevant response.

It’s not like this is the first time I’ve gotten brushed off by a company. I mean I get it, I understand; you have thousands of people asking you questions and there just isn’t a cheap enough way to answer them all. This is a dead end, I give up. What’s this? The fun doesn’t stop here? Canon turns over my info to some other company: “MarketTools CustomerSat” (http://www.customersat.com/) who nags me 3 days after my inquiry for a customer satisfaction survey.. yeah right, trust me fella, you don’t want to know. Another 3 days go by, another nagging message from CustomerSat.. RAGE!

I opt-out of the communications, but I’m pretty irritated at this point so I complete the survey despite suspecting no one will ever read it. If they don’t have the money to hire people with enough patience to read my initial question, why would they pay someone to read my response to their response? It seemed likely to me that the whole thing was probably a contraption invented to allow inevitably frustrated people to vent without tying up more costly telephone time.

Incidentally, I ended up ordering the part and it was what I was hoping for, but I didn’t know for sure until I opened the box it shipped in.

EDIT: I’m continuing to receive unsolicited emails from Canon despite explicitly removing myself from all the lists they’ll let me remove myself from.

Posted in Rants | Leave a comment