Store your stuff in the cloud, for free!


Featured


28
Jun 11

Gantter Project – A Great, Free Way to Manage Projects

I’ve used OpenProj for several years. It’s a nice, free alternative to MS Project. The only problem is sharing the project plans with folks – rarely do people I work with have OpenProj installed. Fortunately, OpenProj gives you the option of saving as a MS Project XML file. This makes it easier but I was still looking for the ability to save the projects in PDF format. This is possible, but only with the paid version of OpenProj. This is when I found the Gantter Project.

Gantter is written to work with Google Apps. It enables you to import MS Project project plans, save them as PDF and even save them directly to Google Docs.

For me, Google Docs and the Gantter Project work best with my custom domain – mlynn.org – but you can use it even if you don’t have a custom domain. Visit Gantter.com to find out more information about this great, free tool to help you manage your projects.


9
Aug 10

How to Monitor your Mac Desktop with Dropbox

I’m a huge fan of the fabulous, free storage service called Dropbox so when I saw a recent episode of Hak5 which covered monitoring your desktop with Dropbox, obviously, I jumped right in. Turns out, however that the episode only covers how to do this using MS Windows. I decided to whip something up for the rest of us and this article covers how I accomplished it. However, I’m not really sure how realistic it is to think of this as a real security tool. First of all it’s unless you’ve enabled passwordless login, or someone has compromised your password, or you’ve left your mac unlocked… it’s highly unlikely that you’ll find this a much value beyond a science experiment. So that’s what I’m calling it… an experiment in Mac Desktop Monitoring using Dropbox.

If you’re not using Mac OS – you’ll want to head over to either the Hak5 episode or to this link on the Dropbox forum which covers how to accomplish this with windows.

Link to Script Download

Dropbox Mac Desktop Monitoring Script (202)

Requirements

  • First, you need a free dropbox account. I would encourage you to click here and sign-up if you don’t have one already. After you’ve installed Dropbox and have it running, you should have a folder that you’ll use to store files and make them available to multiple computers using the same Dropbox account. In this tutorial, we’ll leverage dropbox to share images of your computer’s desktop in the event that some activity is taking place.
  • Second, you need to install ImageMagick. This is a suite of tools that allows you to create, edit and manipulate graphic files from the command line. The installation is simple if you download the binary for Mac OS X, or you can use MacPorts to install it. Instructions on available on the ImageMagick Site.
  • Lastly, you’ll need to know your way around the terminal app. This script was written in shell and will need to be started via cron on a regular basis or you can enable the loop option by editing the code as directed (Around lines 172 and 242.) If you’re unfamiliar with the Mac OS Terminal or shell commands, Click Here. This package comes with an example crontab entry file called dbmon.crontab. You’ll need to add the contents of this file to your user crontab using the crontab command… eg: crontab -e or crontab < dbmon.crontab.

What’s in the script package?

  • ./dbmon.crontab – Example crontab to run the script every minute.
  • ./dbmon.sh – The monitoring script.
  • ./gpl.txt – GPL Text – this script is covered under the terms of the GNU Public License.
  • ./start – Start script to enable the script once you’ve set it to run via crontab.
  • ./status – Check whether or not the script is enabled or disabled.
  • ./stop – Stop the script – disable it from running.

How does it work?

The script works by taking periodic screenshots and comparing them to each other. Using ImageMagick’s compare utility, we can assign a value to the level of difference between two images. The script allows you to set a tolerance for the difference detection – the default seems to work well and is set at a level of 6. Any difference greater than the tolerance probably means that someone is on your desktop doing something. The script will move the screen captures to a configurable directory (typically under your dropbox folder) and send you an email with the screen capture to alert you.

If you’re using Dropbox from a computer other than the one you’re monitoring during the time an alert fires, you’ll be notified by the dropbox update daemon telling you that you’ve added several images to your dropbox folder.

Why do I need this?

Let’s say you’re a mobile worker, with several machines that you use on a regular basis. You probably have a desktop at work, a laptop and maybe several home computers that you use regularly. Dropbox allows you to have a common file share between all of these computers. This script allows you to leverage the communication and storage channels of dropbox to alert you when someone attempts to use one of your computers.

Combine Dropbox with a remote access tool such as Logmein and you’ve got a pretty good remote monitoring and access suite.

How do I install it?

The script was written in shell and can run pretty much from any directory. I would recommend something like the following to install it and test it out…

  1. Make a directory under your home directory
    mkdir /Users/you/dbmon
  2. Download the script package, untar it and place it in a file under that directory… call it something like dbmon.sh.
    tar zxvf dbmon-v1.0.tar.gz
  3. Make it executable
    chmod +x /Users/you/dbmon/dbmon.sh
    
  4. Edit the script or simply create a .dbmon configuration file in your home directory (~/.dbmon) and change the variables to reflect your specific environment.
    computer:dbmon mlynn$ vi ~/.dbmon
    

    ~/.dbmon

    DBMON_TOLERANCE=9
    DBMON_ACTIVE="1"
    DBMON_SCOUNT="2"
    DBMON_SLEEP="1"
    DBMON_VERBOSE='ON'
    DBMON_PROGNAME='DBMON'
    # LABEL="ON" doesn't work... bug in ImageMagick Version 6.6.3
    DBMON_LABEL="OFF"
    DBMON_LABELMESSAGE="Screen Capture $HOSTNAME "
    DBMON_DBPATH='/Users/you/Dropbox/dbmon'
    DBMON_TMPPATH='/Users/you/dbmon/tmp'
    DBMON_ALERTEMAIL='you@gmail.com'
    DBMON_REMOTEOFFSWITCH='/Users/you/Dropbox/dbmon/OFF'
    DBMON_CONVERT="$MAGICK_HOME/bin/convert"
    DBMON_COMPARE="$MAGICK_HOME/bin/compare"
    DBMON_SCAP="/usr/sbin/screencapture"
    
  5. Now you should be able to test the script out by simply running it.
    computer:dbmon you$ ./start
    computer:dbmon you$ ./dbmon.sh
    [08/09/2010 21:20:49] DBMON: Moving /Users/you/dbmon/tmp/alert/current.png to /Users/you/dbmon/tmp/alert/previous.png...
    [08/09/2010 21:20:49] DBMON: Snapping /Users/you/dbmon/tmp/alert/current.png...
    [08/09/2010 21:20:50] DBMON: Comparing /Users/you/dbmon/tmp/alert/current.png to /Users/you/dbmon/tmp/alert/previous.png...
    [08/09/2010 21:20:51] DBMON: Difference [2925] exceeds tolerance [9]!!
    [08/09/2010 21:20:51] DBMON: Copying /Users/you/dbmon/tmp/alert/current.png to /Users/you/Dropbox/dbmon/201008/20100809-212049.png...
    [08/09/2010 21:20:51] DBMON: Sending alert email to you@gmail.com...
    [08/09/2010 21:20:51] DBMON: Snapping 1
    [08/09/2010 21:20:54] DBMON: Snapping 2
    computer:dbmon you$ ./stop 
    
  6. Once you’ve got it working, you can create a crontab entry to run it on a regular basis. Place the following in your crontab…
    * * * * * /Users/you/dbmon/dbmon.sh > /dev/null 2>&1
    

    Keep in mind – this will run EACH AND EVERY MINUTE OF EVERY HOUR OF EVERY DAY OF EVERY MONTH – unless of course you implement the remote off switch file. I created two scripts “start” and “stop” for use in conjunction with the crontab file. The start file simply removes the remote off switch and the stop script replaces it by “touch”ing that file.

    start

    #!/bin/sh
    #===========================================================================
    #= Script : Monitoring your Mac Desktop with Dropbox
    #= File   : start
    #= Version: 0.2
    #= Author : Mike Lynn
    #= Email  : merlynn@gmail.com
    #= Website: http://www.mlynn.org/2010/08/how-to-monitor-your-mac-desktop-with-dropbox/
    #===========================================================================
    #= Copyright (c) 2010 Mike Lynn
    #= You are free to use and modify this script as long as this header
    #= section stays intact
    #= This file is part of the "Monitoring your Mac Desktop with Dropbox" script
    #=
    #= This program is free software; you can redistribute it and/or modify
    #= it under the terms of the GNU General Public License as published by
    #= the Free Software Foundation; either version 2 of the License, or
    #= ( at your option ) any later version.
    #=
    #= This program is distributed in the hope that it will be useful,
    #= but WITHOUT ANY WARRANTY; without even the implied warranty of
    #= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #= GNU General Public License for more details.
    #=
    #= You should have received a copy of the GNU General Public License
    #= along with Mobile Detection Script ; if not, write to the Free Software
    #= Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    #===========================================================================
    
    . ~/.dbmon
    if [ -z $DBMON_REMOTEOFFSWITCH ]; then
    	echo "No remote off switch set for dropbox monitor.  Exiting."
    	exit 1
    fi
    rm $DBMON_REMOTEOFFSWITCH
    

    stop

    #!/bin/sh
    #===========================================================================
    #= Script : Monitoring your Mac Desktop with Dropbox
    #= File   : stop
    #= Version: 0.2
    #= Author : Mike Lynn
    #= Email  : merlynn@gmail.com
    #= Website: http://www.mlynn.org/2010/08/how-to-monitor-your-mac-desktop-with-dropbox/
    #===========================================================================
    #= Copyright (c) 2010 Mike Lynn
    #= You are free to use and modify this script as long as this header
    #= section stays intact
    #= This file is part of the "Monitoring your Mac Desktop with Dropbox" script
    #=
    #= This program is free software; you can redistribute it and/or modify
    #= it under the terms of the GNU General Public License as published by
    #= the Free Software Foundation; either version 2 of the License, or
    #= ( at your option ) any later version.
    #=
    #= This program is distributed in the hope that it will be useful,
    #= but WITHOUT ANY WARRANTY; without even the implied warranty of
    #= MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #= GNU General Public License for more details.
    #=
    #= You should have received a copy of the GNU General Public License
    #= along with Mobile Detection Script ; if not, write to the Free Software
    #= Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    #===========================================================================
    . ~/.dbmon
    if [ -z $DBMON_REMOTEOFFSWITCH ]; then
            echo "No remote off switch set for dropbox monitor.  Exiting."
            exit 1
    fi
    touch $DBMON_REMOTEOFFSWITCH
    

    Be sure to edit the path’s to the REMOTEOFFSWITCH variable in the .dbmon configuration file located in your home directory.

You will probably want to make sure you have the start and stop scripts configured on several of your machines so that you can remotely control execution of the monitoring script. Keep in mind your environment (like where you have Dropbox installed) may change – so you will need to make sure you configure the REMOTEOFFSWITCH variable accordingly for each installed computer.

Notes and warnings

  • Dropbox offers free storage starting at 2GB – a lot of space… but keep in mind that if you accidentally leave this monitoring script running while you’re logged on you’re going to eventually fill it up and run out of space.
  • A note regarding multiple monitors: This script will only monitor the primary monitor so if you’re using multiple monitors, keep this in mind.
  • Make sure that when you’re testing, you’re haven’t configured the script to write your temporary screen capture files to a place that will visibly change the monitor – like the desktop for example.
  • This initial version of the script contains the configuration variables and might be difficult for someone to implement if they’re not command shell savvy. The next version will probably be a bit more sophisticated and might even incorporate a gui component… let me know if you’re interested in something like by leaving a comment or sending me an email.

20
Jul 10

Mobile Device Detection and Redirection with Perl and CGI

Last month, I took some time and wrote about Mobile Device Detection and Redirection with PHP. Well, it turns out that some folks can’t (or choose not to) make use of PHP and favor the Perl/CGI approach to web development. This article covers the same bases and presents a set of examples implemented using Perl and Lincoln Stein’s great CGI.pm module.

Download Code and Examples Here:
From Github: http://github.com/mrlynn/MobileBrowserDetectionExample

or locally:
Version 0.3 (Latest – includes perl/cgi AND PHP examples)
Mobile Browser Detection and Redirection Scripts (3048)

Version 0.2 (Older version – PHP examples only)
Mobile Browser Detection and Redirection Scripts (3565)

View the working example using Perl/CGI here: http://mlynn.org/uatest/cgi-bin/index.cgi

Redirecting a user based on the type of device they are using is not rocket science. In fact, it can be accomplished quite simply using an apache redirect in a .htaccess file.

RewriteCond %{HTTP_USER_AGENT} ^.*iPad.*$
RewriteRule ^(.*)$ http://ipad.yourdomain.com [R=301]

The issue with this approach is that users coming to your site using an iPad will never have the ability to see the other versions of your site. for many, that may be a satisfactory solution. However, if you want users to have the ability to view a version of your site designed specifically for their type of mobile device by default – but have the option to click a link and either view or optionally set a preference for another version of the site, this article is for you.

Perl and CGI have been around for many years and are in wide use on the web. Perl is extremely extensible and using the very popular CGI.pm Perl Module written by Lincoln Stein, it’s very easy to write code to dynamically manage your web site.

Where PHP has built-in variables designed to expose the server and apache session environment ($_SERVER, $_SESSION), Perl and CGI.pm make use of %ENV and module variables such as param(). Consider the following table which contrasts PHP and Perl/CGI.

User Agent

PHP

$_SERVER['HTTP_USER_AGENT']

Perl/CGI

$ENV{HTTP_USER_AGENT}

Reading Cookies

PHP

$cookievalue = $_COOKIE['COOKIENAME']

Perl/CGI

use CGI;
$query=new CGI;
$cookievalue = $query-&gt;cookie('COOKIENAME');

Setting Cookies

PHP

setcookie(name, value, expire, path, domain);

Perl/CGI

$cookie = $query-&gt;cookie(-name=&gt;'COOKIENAME',
			 -value=&gt;'COOKIEVALUE',
			 -expires=&gt;'+4h',
			 -path=&gt;'/');
print $query-&gt;header(-cookie=&gt;$cookie);

Now that we have the basics down, the rest is easy. We’ll want to check the users’ device type by querying the user agent, check the value of a cookie to see if they’ve specified a preference for something other than the site designed for their specific device and redirect accordingly.

The initial landing page does most of the heavy lifting.

index.cgi

#!/usr/bin/perl
require 'includes/config.inc.pl';
require 'includes/functions.inc.pl';

use CGI;
$query = new CGI; 

$useragent = $ENV{HTTP_USER_AGENT};
$sitepref = $query->cookie( 'SITEPREF' );

if ( ismobile( $useragent ) ) {
        if ( !$sitepref ) {
          if ( isipad( $useragent ) ) {
                  print $query->redirect( "$IPADURL" );
          } else {
              if ( isiphone( $useragent ) ) {
                          print $query->redirect( "$IPHONEURL" );
                  } else {
                          print $query->redirect( "$MOBILEURL" );
                  }
          }
        } else {
          if ( $sitepref=='MOBILE' ) {
                  print $query->redirect( "$MOBILEURL" );
          } else {
                  if ( $sitepref=='IPHONE' ) {
                          print $query->redirect( "$IPHONEURL" );
                  } else {
                          if ( $sitepref=='IPAD' ) {
                                  print $query->redirect( "$IPADURL" );
                          } else {
                                  print $query->redirect( "$NORMALURL" );
                          }
                  }
            }
        }
} else {
    if ( !$sitepref ) {
          print $query->redirect( "$NORMALURL" );
    } else {
      if ( $sitepref=='MOBILE' ) {
          print $query->redirect( "$MOBILEURL" );
      } else {
          if ( $sitepref=='IPAD' ) {
              print $query->redirect( "$IPADURL" );
          } else {
              if ( $sitepref=='IPHONE' ) {
                  print $query->redirect( "$IPHONEURL" );
              } else {
                  print $query->redirect( "$NORMALURL" );
              }
          }
      }
    }
}

And the included files… which set the URL values and contain many of the functions.

functions.inc.pl

sub setcookiealive() {
        my ($name,$value,$expires,$redirect) = @_;
        $cookie = $query->cookie(-name=>"$name",
                         -value=>"$value",
                         -expires=>'+5d',
                         -path=>'/');
        if ($redirect) {
                print $query->redirect("$redirect","-cookie=>$cookie");
        } else {
                print $query->header(-cookie=>$cookie);
        }
}

sub in_array {
     my ($arr,$search_for) = @_;
     my %items = map {$_ => 1} @$arr; # create a hash out of the array values
     return (exists($items{$search_for}))?1:0;
}

sub ismobile {
        $useragent=lc(@_);
        $is_mobile = '0';

        if($useragent =~ m/(android|up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone)/i) {
                $is_mobile=1;
        }

        if((index($ENV{HTTP_ACCEPT},'application/vnd.wap.xhtml+xml')>0) || ($ENV{HTTP_X_WAP_PROFILE} || $ENV{HTTP_PROFILE})) {
                $is_mobile=1;
        }

        $mobile_ua = lc(substr $ENV{HTTP_USER_AGENT},0,4);
        @mobile_agents = ('w3c ','acs-','alav','alca','amoi','andr','audi','avan','benq','bird','blac','blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno','ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-','maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-','newt','noki','oper','palm','pana','pant','phil','play','port','prox','qwap','sage','sams','sany','sch-','sec-','send','seri','sgh-','shar','sie-','siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-','tosh','tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp','wapr','webc','winw','winw','xda','xda-');

        if(in_array(\@mobile_agents,$mobile_ua)) {
                $is_mobile=1;
        }

        if ($ENV{ALL_HTTP}) {
                if (index(lc($ENV{ALL_HTTP}),'OperaMini')>0) {
                        $is_mobile=1;
                }
        }

        if (index(lc($ENV{HTTP_USER_AGENT}),'windows')>0) {
                $is_mobile=0;
        }
    return $is_mobile;
}

sub isiphone {

        $useragent = @_;
        $iphone=0;
        if (lc($useragent) =~ m/iphone/) {
                $iphone=1;
        }
        return $iphone;
}

sub isipad {

        $useragent = @_;
        $ipad=0;
        if (lc($useragent) =~ m/ipad/) {
                $ipad=1;
        }
        return $ipad;
}
1;

config.inc.pl

$SITEURL='http://mlynn.org/uatest/cgi-bin/index.cgi';
$LOGFILE='debug.log';
$MOBILEURL='http://mlynn.org/uatest/cgi-bin/mobile/index.cgi';
$IPHONEURL='http://mlynn.org/uatest/cgi-bin/iphone/index.cgi';
$IPADURL='http://mlynn.org/uatest/cgi-bin/ipad/index.cgi';
$NORMALURL='http://mlynn.org/uatest/cgi-bin/normal/index.cgi';
1;

Related Sites and Interesting Links

Have a site you’d like to add to this list? Send me an email or submit a comment.


14
Jul 10

SOLVED: My WordPress Thumbnails Stopped Thumbnailing

The server where I host this blog experienced some oddness and the web server daemon (httpd) had to be restarted.  Once it came back up and it was determined that the primary DNS server on which we rely had crapped out, we were able to point to another DNS server and things seemed to get back to normal.  With a few minor exceptions that is… most notably, thumbnails were no longer thumbnailing.

This WordPress theme uses a

TimThumb script created by Tim McDaniels and Darren Hoyt with tweaks by Ben Gillbanks

. To diagnose the problem, the first place I looked (like every good web administrator) is the logs.

[Wed Jul 14 05:41:11 2010] [error] [client xx.xx.xx.xx] PHP Fatal error:  Allowed memory size of 31457280 bytes exhausted (tried to allocate 12288 bytes) in /path/to/my/web/home/httpdocs/wp-content/themes/mytheme/thumb.php on line 271, referer: http://www.mlynn.org/wp-admin/post.php?post=127&amp;action=edit&amp;message=1

AHA! The problem became obvious and easy to solve. The problem appears to be that the memory_limit value in the php configuration file (php.ini) has been set to low. Sure enough it was set to 4M.

A quick edit of the php.ini file…

;;;;;;;;;;;;;;;;;;;
; Resource Limits ;
;;;;;;;;;;;;;;;;;;;

max_execution_time = 30     ; Maximum execution time of each script, in seconds
max_input_time = 60     ; Maximum amount of time each script may spend parsing request data
memory_limit = 48M      ; Maximum amount of memory a script may consume</pre>
And a quick restart of apache...
<pre>/etc/init.d/httpd graceful

and blam-o… we’re back in business…. thumbnail’s a thumb’n.

If you don’t have root permissions – or don’t have access to your own php.ini file, you may need to contact the support team where you host your web site. Questions? Comments?


30
Jun 10

Mobile Device Detection and Redirection with PHP

** Updated 07/20/2010 **
See also http://www.mlynn.org/2010/07/mobile-device-detection-and-redirection-with-perl-and-cgi/ for additional information on Mobile Browser Detection and Redirection using Perl / CGI.

Target Audience

Web Content Publishers, Web Server Administrators and anyone considering preparing and publishing content that may be consumed by users with mobile devices.

Overview

The mobile phone and computing market is exploding. With more and more phones and hand held devices gaining web browsing capability, it only makes sense to ensure that your site is at least viewable by these mobile browsers. In this article, I present one possible solution for detecting and redirecting incoming mobile browsers so you can customize and enhance the mobile users’ experience.

In this article, I’ll discuss an example web site where we want to present customized web content to four distinct types of user based on the browser device their using when they visit the site. I’ll also discuss how to enable the users to specify a preference for one of the other versions of the site. For example, if I’m an iphone user but I really want to see the full site.

Redirecting a user based on the type of device they are using is not rocket science. In fact, it can be accomplished quite simply using an apache redirect in a .htaccess file.

RewriteCond %{HTTP_USER_AGENT} ^.*iPad.*$
RewriteRule ^(.*)$ http://ipad.yourdomain.com [R=301]

The issue with this approach is that users coming to your site using an iPad will never have the ability to see the other versions of your site. for many, that may be a satisfactory solution. However, if you want users to have the ability to view a version of your site designed specifically for their type of mobile device by default – but have the option to click a link and either view or optionally set a preference for another version of the site, this article is for you.

Examples and Source Code

I know – you’re in a hurry… you’ve read the intro and you know this is what you’re after… well, here you go… The examples for this site are available for demonstration at http://mlynn.org/uatest/ and the code for this is available for download from the following link:

Version 0.3 – Includes PHP and Perl/CGI Examples
Mobile Browser Detection and Redirection Scripts (3048)

Version 0.2 – Includes only PHP Examples
Mobile Browser Detection and Redirection Scripts (3565)

You may additionally download or review the source for this example at github:
http://github.com/mrlynn/MobileBrowserDetectionExample

Before diving in to my solution, let’s cover some of the background and key factors. If you’re a seasoned web publisher and you already understand the basics of the web and how it works, you’ll probably want to skip down a bit.

There are several components involved in any web user interaction.

Web Browsing Device (Device)

This can be any device running some form of browser.  Desktop computers are the most popular devices browsing the web today but mobile devices are swiftly increasing in numbers.  According to statcounter.com, mobile browsers have experienced a 3 percent increase since 2008 and with the advent of hybrid devices such as the ipad and netbook, you can bet this number will continue to grow.

Web Browsing Program (Browser)

This refers to the program being run by the device for the purpose of browsing web content.  Popular browsers include Firefox, Google Chrome, Microsoft Internet Explorer and Opera.  Each browser communicates with web servers in a similar manner.  When the web browser contacts a web server, it sends several pieces of data identifying its program, version and even the device being used for this browsing session.

Web Server (Server)

The web server is responsible for presenting web content to the browsers.  Apache HTTPD and Microsoft Internet Information Server (IIS) are the two most popular web servers.

Web Content

This is the stuff you’re sending to the browser… the stuff you’re reading right now.  Depending on the type of site and your intended audience, you’re likely to have several types of content being delivered to your browsers.

The Conversation

When you type an address into your browser, or click on a link to a web site, the browser initiates a conversation with the target web server.  Much of this conversation is handled by the browser and hidden from you, the viewer.  The following is an example of a conversation initiated by a browser.

GET /uatest/index.php HTTP/1.1
Host: www.mlynn.org
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)

In this example, a user is requesting the following url: http://www.mlynn.org/uatest/index.php from an instance of Firefox browser running on a windows device.

Here’s a list of other possible values sent along with a request for web content.

Header Value
Host www.mlynn.org
Connection keep-alive
User-Agent Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.86 Safari/533.4
Referer mlynn.org
Accept application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Encoding gzip,deflate,sdch
Accept-Language en-US,en;q=0.8
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie __utma=2051572.1472781444.1277919157.1277919157.1277919157.1; __utmc=2051572; __utmb=2051572.1.10.1277919157

Here’s a look at what your actual request headers look like when you’re browser requested this page:

And this is the response sent back to your browser from the instance of apache httpd running on my server:

Apache’s httpd server exposes the values of some of these request and response headers.  If you’re using the Pre-Hypertext Programming language – or PHP as it’s better known, you can access these values using built-in functions.  See PHP: apache_request_headers and PHP: apache_response_headers for more information.

Identifying Mobile Devices

Now that we have a good understanding of the conversation between the browser and the server, let’s focus on identifying mobile devices and redirection.  As I briefly explained earlier, the web browser identifies the browser program and version along with any requests for content from your web server.  The browser identifies these items using the string “User-Agent.”  The trick to identifying mobile devices is all about knowing what is sent in the User-Agent request field by each and every mobile device.  The following is an example sent by a mobile device.

User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile/7A341 Safari/528.16

Redirecting Based on User Agent

So now we understand that the browser identifies itself, and if we’re using Apache httpd, we can access the value of that identifier. Let’s take a look a simple redirection example written in PHP.

if (preg_match('/iphone|ipad/',strtolower($_SERVER['HTTP_USER_AGENT']))) {
    header("Location: http://mobile.mlynn.org");
} else {
    header("Location: http://www.mlynn.org");
}

This example interrogates the user agent variable using a php regular expression function and redirects accordingly. This redirection is facilitated using the PHP header() function.

There are several other mechanisms to accomplish similar redirection. For example, you could implement this redirection using a .htaccess file which would be parsed by Apache. This mechanism is discussed in detail on this blog entry. For now however, let’s stick to our PHP implementation.

If all you’re concerned about is redirecting users to another version of the site, this will probably work for you. You’ll simply need to understand and test for the right user-agent values.

However, let’s assume that we want to get a bit trickier. Let’s say we want to have different content or formatting for four separate versions of our site – one for each variant of browsing device that we want to support. One for normal desktop computer browsers, another for iPhone browsers, another for iPad users and one more for all other mobile browsing devices. The reasons for this are many and varied. You may want simply to vary the advertising code you expose to each user based on the type of device or browser they’re using.

To make this type of implementation work, I’ve created a configuration file to initialize some variables for our site. For each distinct version of the site, I’ll create a variable with the appropriate url. I’ll use these later when we write the code for our redirection script.

config.inc.php

$SITEURL='http://mlynn.org/uatest/';
$LOGFILE='debug.log';
$MOBILEURL='http://mlynn.org/uatest/mobile/';   // Generic Mobile Device URL
$IPHONEURL='http://mlynn.org/uatest/iphone/';   // iPhone URL
$IPADURL='http://mlynn.org/uatest/ipad/';           // iPad URL
$NORMALURL='http://mlynn.org/uatest/normal/'; // Normal - Full site URL

Now let’s take a look at what our main index page or landing site will look like. This will be the initial page visited by users before being automatically redirected based on their browser device type.

$useragent=$_SERVER['HTTP_USER_AGENT'];
if (ismobile($useragent)) {
    if (isipad($_SERVER['HTTP_USER_AGENT'])) {
        header("Location: $IPADURL");
    } else {
        if (isiphone($_SERVER['HTTP_USER_AGENT'])) {
            header("Location: $IPHONEURL");
        } else {
            header("Location: $MOBILEURL");
        }
    }
} else {
    header("Location: $NORMALURL");
}

This code snippet relies on several important custom functions: ismobile(), isiphone(), and isipad().

The first function examines the user agent value and returns a ’0′ if the device is not recognized as a mobile device and a ’1′ if it is mobile.

 function ismobile() {
    $is_mobile = '0'; 

    if(preg_match('/(android|up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone)/i', strtolower($_SERVER['HTTP_USER_AGENT']))) {
        $is_mobile=1;
    }

    if((strpos(strtolower($_SERVER['HTTP_ACCEPT']),'application/vnd.wap.xhtml+xml')>0) or ((isset($_SERVER['HTTP_X_WAP_PROFILE']) or isset($_SERVER['HTTP_PROFILE'])))) {
        $is_mobile=1;
    }

    $mobile_ua = strtolower(substr($_SERVER['HTTP_USER_AGENT'],0,4));    $mobile_agents = array('w3c ','acs-','alav','alca','amoi','andr','audi','avan','benq','bird','blac','blaz','brew','cell','cldc','cmd-','dang','doco','eric','hipt','inno','ipaq','java','jigs','kddi','keji','leno','lg-c','lg-d','lg-g','lge-','maui','maxo','midp','mits','mmef','mobi','mot-','moto','mwbp','nec-','newt','noki','oper','palm','pana','pant','phil','play','port','prox','qwap','sage','sams','sany','sch-','sec-','send','seri','sgh-','shar','sie-','siem','smal','smar','sony','sph-','symb','t-mo','teli','tim-','tosh','tsm-','upg1','upsi','vk-v','voda','wap-','wapa','wapi','wapp','wapr','webc','winw','winw','xda','xda-');

    if(in_array($mobile_ua,$mobile_agents)) {
        $is_mobile=1;
    }

    if (isset($_SERVER['ALL_HTTP'])) {
        if (strpos(strtolower($_SERVER['ALL_HTTP']),'OperaMini')>0) {
            $is_mobile=1;
        }
    }

    if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']),'windows')>0) {
        $is_mobile=0;
    }

    return $is_mobile;
}

The following functions are used to detect specific device types.

function isiphone($useragent) {
    $iphone=0;
    if (preg_match('/iphone/',strtolower($useragent))) {
        $iphone=1;
    }
    return $iphone;
}

function isipad($useragent) {
    $ipad=0;
    if (preg_match('/ipad/',strtolower($useragent))) {
        $ipad=1;
    }
    return $ipad;
}

Now when a user visits our site, they will be redirected automatically based on the specific device they are using. This works in most cases, but let’s assume we want to give users the option of visiting a site other than the default for their device? Let’s say I’m on an iPhone – but I want to see what the real, full site looks like with my browser. To implement this, we’ll need some way for the user to specify a preference for a specific version of the site.

One way to accomplish this is using cookies. Cookies are small bits of data stored on your computer and referenced when you visit or revisit a web site.

There are several ways to set and get the value of cookies. Javascript can be used but depends heavily on the implementation of javascript on the device. Additionally, you must be certain that the user has not disabled javascript on their device in order for this to work.

Fortunately, cookies can be maintained using server-side code such as php. For our example, we’ll stick with PHP.

To accomplish setting and maintaining cookies via php for our users’ site preference, I created the following php script called setpref.php.

include_once('includes/config.inc.php');
include_once('includes/functions.inc.php');

$pref = (isset($_POST['SITEPREF']) ? $_POST['SITEPREF'] : $_GET['SITEPREF']);
switch ($pref) {
    case 'MOBILE':
        setcookiealive('SITEPREF','MOBILE',time()+3600);
        header("Location: $MOBILEURL");
        break;
    case 'IPHONE':
        setcookiealive('SITEPREF','IPHONE',time()+3600);
        header("Location: $IPHONEURL");
        break;
    case 'IPAD':
        setcookiealive('SITEPREF','IPAD',time()+3600);
        header("Location: $IPADURL");
        break;
    case 'NORMAL':
        setcookiealive('SITEPREF','NORMAL',time()+3600);
        header("Location: $NORMALURL");
        break;
    default:
        setcookiealive('SITEPREF','NORMAL',time()+3600);
        header("Location: $NORMALURL");
}

This function relies on another function called setcookiealive which simply creates the cookie using php’s setcookie() function and also sets the value of the $_COOKIE variable so that the value is available immediately.

function setcookiealive($name,$value,$expires) {
    $_COOKIE[$name] = $value;
    setcookie($name,$value,$expires);
}

In order for this to work, we need to give the user’s links to the alternate versions of the sites. Let’s look at one of the example site versions.

<?php
// Main iPad landing Page
include_once('../includes/config.inc.php');
?>
<html>
<head>
<title>iPad Site</title>
</head>
<body>
<h1>iPad Site</h1>
<div id='content'>
<h2>This is the iPad site.  You got here because you are either using an iPad device or you specified a preference for the iPad site.</h2>
<p>
<?
echo "User Agent: ".$_SERVER['HTTP_USER_AGENT'];
?>
<p>
</div>
<div id='footer'>
Visit Other Site Versions:
<a href="<? echo $NORMALURL;?>"/>Full Site</a> ||
<a href="<? echo $MOBILEURL;?>"/>Generic Mobile Site</a> ||
<a href="<? echo $IPHONEURL;?>"/>iPhone Site</a>
<p>
Set your Preference to other site versions:
<a href="/uatest/setpref.php?SITEPREF=NORMAL"/>Full Site</a> ||
<a href="/uatest/setpref.php?SITEPREF=MOBILE"/>Generic Mobile Site</a> ||
<a href="/uatest/setpref.php?SITEPREF=IPHONE"/>iPhone Site</a>
<p>
<?
if (isset($_COOKIE['SITEPREF'])) {
    $sitepref=$_COOKIE['SITEPREF'];
} else {
    $sitepref='UNSET';
}
print "YOUR CURRENT SITE PREFERENCE: ".$sitepref;

?>
</div>
</html>

I’ve explained the components involved in any standard web user interaction, how they relate specifically to device detection, and I’ve showed one implementation of detection and redirection. If you’d like to implement something similar, feel free to download the code using the following link.
Mobile Browser Detection and Redirection Scripts (3565)

If you do end up using these scripts, drop me an email or log a comment to let me know how you made out. You can also try out this working example.

Example Site Links:

Related Sites and Interesting Links

Have a site you’d like to add to this list? Send me an email or submit a comment.


21
Mar 10

Find Your Domain Name, Whatever It Might Be

Here’s a little ExtJS project I wrote to help you find domain names. The backend is written in PHP and utilizes a MySQL database which contains the contents of the English dictionary. You search for domain names that match a specific string, or use the dictionary to find domain names that start with, end with or contain a specific piece of text.


19
Mar 10

10 Tips to Help You Become a Better ExtJS, PHP, MySQL Developer

Before I begin, let me first say that I am not an expert ExtJS, PHP or MySQL developer. I have however, learned over the years from some basic design principles and tips gleaned far smarter and more talented developers than myself. I thought I’d create a post in the hope that you may benefit from what I have learned. Some of these tips will be pretty basic for most skilled developers, but it never hurts to review, right?

Audience – Who is This Article For?

This article was intended for the novice to intermediate web developer who specializes in use of the following technologies:

Tip 1 – Stop Using Print Statements to Debug

Developing solutions using the elegant Javascript framework ExtJS can be a mix of pleasure and pain.  While debugging the Javascript components is made easy with the handy Firefox extension FireBug, getting debug information back from the php side of your app can be tricky.  Especially if you’re expecting to maintain JSON communication with your javascript components. There are basically two methods of debugging the php side of your app.  You can either rely on JSON encoded strings sent back to your javascript components, decode them on the other side and display them either in and alert window, or in the console using console.log, or you can use print statements in your php.

The latter approach will commands will likely cause a javascript interpreter error and force you to reload the page to continue testing. I prefer the latter approach because its simpler.  However, instead of simply using the php echo or print commands, I like to create a logging function which uses file_print_contents to populate a debug log. Here’s my function:

function logit($text) {
    $now=date('Y-m-d h:m:s');
    $debugdata=debug_backtrace();
    $out=$now.' '.$_SERVER['SERVER_NAME'].' '.$_SERVER['PHP_SELF'].' '.$debugdata[2]['function'].' '.$text;
    file_put_contents('debug.log',$out,FILE_APPEND);
}

With this function, debugging is as easy as calling your logit() function instead of using print. I typically open another window and execute a tail -f debug.log to maintain a constant view of streamed debug data.

Tip 2 – Centralize your Database Updates

Creating a single reusable function to update your database can save your years of debugging.  It also provides a single place where you can add logging features to your updates.  For example, let’s say you want to create an update_log table for the database your updating.  This table will contain a record of every column, row and table that you’ve updated along with the mechanism used to update it and the date/time it was updated.

If you’ve got your mysql_query code scattered about your project, you’re going to need to add this logic all over the place.  If you’ve centralized your updates into a single function, you merely need to add the update_log logic to your update function.

Tip 3 – Borrow Heavily but Don’t Be A Script-Kiddie

We all do it.  Why re-invent the wheel, right?  I’m referring to the common practice of code-theft.  There are copious examples of many of the required components of most application challenges you’ll come up against.  So why not?  Why not, simply grab the code and slap it into your project?

It makes perfect sense – but you need to understand the code you’re lifting if your project is going to be a successful and supportable one.

A good friend of mine introduced me to a term over 12 years go – Script-kiddie.  A Script-Kiddie is a derogatory term used to describe someone that steals scripts or code, uses them but never fully understands how they work or in some cases, what they even do.  The term is typically used in a security context.

Simply put, don’t be one.

Tip 4 – Buy a Book (or two)

There are great books out there to help you with the PHP/MySQL/ExtJS development paradigm.  Here are a few that I own:

  1. Learning Ext JS
  2. Professional JavaScript Frameworks: Prototype,YUI, ExtJS, Dojo and MooTools (Wrox Programmer to Programmer)
  3. Learning PHP, MySQL, and JavaScript: A Step-By-Step Guide to Creating Dynamic Websites (Animal Guide)
  4. PHP and MySQL Web Development (4th Edition)
  5. PHP Phrasebook
  6. JavaScript: The Definitive Guide
  7. MySQL (4th Edition)

And here’s one that’s not fully released – but looks to be pretty awesome:

Tip 5 – Use FireBug

Firebug is a fantastic FireFox browser extension that enables you to inspect HTML and javascript, and even modify style and layout in real-time. I simply can’t imagine developing javascript, HTML or CSS based solutions without it.

Here’s a good screencast which explains the basics of getting, installing and using Firebug.

This assumes you can use the fabulous FireFox browser. In some cases, you can’t. For instance – my employer does not permit use of FireFox. In those cases, you can use IE addons to accomplish a minimal amount of the same capabilities as Firebug. Another option is to leverage the ExtJS Debug Console.

Tip 6 – Modularize Your Javascript

The ExtJS development paradigm is one that lends itself to rapid production. You’re madly cutting/pasting or typing in your component syntax and you’re application begins to take on a beautiful life of its own. It’s very easy to simply keep slapping components into your main javascript file, testing, debugging, and slapping more code on. I’m guilty. My first ExtJS application has over 4300 lines in a single javascript file… ugh… I’m embarrassed. Don’t get me wrong – the app works… it’s just nearly impossible to debug.

One way to implement modular code is to leverage iframes. The managed iframe user extension makes this very easy to do. Saki’s Example Page makes use of this extension beautifully. The code to create the main viewport and the layout panels is in one javascript file, but when you click on a link in the navigation tree, the code for the main or center panel is included in an iframe. This permits you to create one main application.js file and another separate javascript code file for each of the options in your tree.

Tip 7 – Use extjs-debug-all.js and extjs-debug-base.js During Development

To use the ExtJS Framework, you simply include the basic Javascript libraries and CSS stylesheet information in your HTML document. Here’s an example:



        Application Title
        
        
        
        




Tip 8 – Check for Trailing Comma’s

IE is much more picky about trailing comma’s than Firefox or Safari. Since I develop on a Mac, I’m forever missing trailing comma’s. I have an ancient laptop running Windows XP that I keep around for the sole purpose of testing apps in IE. Because javascript is interpreted, not compiled, you don’t get that piece of mind that comes from passing your work through the compiler before placing it in front of the user.

9 times out of 10 when I have an error in IE, it’s because of a trailing comma. ExtJS uses objects configured using JSON like syntax. Here’s an example:

var myExample = function() {
    return {
        foo: 'bar',
        boo: 'far'
    }
};

In this example myExample is an object which is configured with a function. The function in turn is configured with a return object which has parameters foo and boo. The syntax for this example is correct. Consider, on the other hand, this example:

var myExample = function() {
    return {
        foo: 'bar',
        boo: 'far',
    }
};

See the difference? That one minor, trailing comma after boo: ‘far’ will cause IE to stop interpretation of the entire javascript.

One way to catch this error is to copy and paste the source code of your javascript into JSLINT.

JSLINT will return the following:

Error:

Problem at line 4 character 19: Extra comma.

boo: ‘far’,

Problem at line 5 character 6: Missing semicolon.

}

Tip 9 – Create an Automated Backup Routine for Nightly Backups

There’s nothing worse than after weeks of steady positive progress, you suddenly find yourself unable to figure out what you’ve done to break things. Having a nightly backup strategy can provide valuable insight into the path you took to get where you are now.

For nearly every large scale project, I create a simple script to backup the code and data and ship it offsite somewhere – typically back home to my home development lab.

If you’re able to create a public/private key pair and configure passwordless login between your development environment and your home machine, you can create a simple script which can be executed automatically via cron to securely copy your development work back down to your home machine.

Here’s a link to creating a secure passwordless login scheme with SSH.

Once you have that in place, here’s a little script that you can customize to create backups for your work.

#!/bin/sh
# mlynn - standard backup script
# ----- - Remember to implement passwordless ssh
#
# Modify these variables
LOCALDIR=/Users/mlynn/Projects/WSS
REMOTEDIR=/var/www/vhosts/mlynn/httpdocs/wss/payroll/v2
LOCALEXTDIR=/Users/mlynn/www/extjs
HOST=your remote hostname.com
USER=your remote username
DATE=`date "+%m%d%Y"`
#
cd $LOCALDIR
mkdir -p backups/$DATE/v2
cd backups/$DATE/v2
scp -r ${USER}@${HOST}:${REMOTEDIR}/*.html .
scp -r ${USER}@${HOST}:${REMOTEDIR}/*.php .
scp -r ${USER}@${HOST}:${REMOTEDIR}/*.json .
scp -r ${USER}@${HOST}:${REMOTEDIR}/js .
scp -r ${USER}@${HOST}:${REMOTEDIR}/includes .
scp -r ${USER}@${HOST}:${REMOTEDIR}/images .
scp -r ${USER}@${HOST}:${REMOTEDIR}/css .
# Add additional directories or files that you want to capture
#
# This line links your local extjs directory so you don't have to download nightly
ln -s $LOCALEXTDIRs .

This will create a fully functional copy of your web development area on your local machine. The last line of that script creates a symbolic link to the ExtJS library directory on your local machine. This assumes that you don’t want to copy down the extjs javascript library distribution every night.

Make sure you modify the following variables to customize the script for use on your local machine.

LOCALDIR – this is the local directory on your home development machine.
REMOTEDIR – this is the root directory of your development work on your client or remote machine.
LOCALEXTDIR – this is the full path to the extjs libraries on your local machine.

Once you’ve got all that setup – add your script to cron to kick it off nightly (or even more frequently.)

Tip 10 – Visit ExtJS.com Often

ExtJS.com is frequently updated with new versions, featured implementations and blog posts designed to simplify use of this great framework.

The site is well organized and has several sections specifically designed to help you getting started using the framework. The Learn section is a great place to start.

Bonus Tip – Use an Alert Service To Notify You of Updates on ExtJS.com

Since timely information is very valuable when you’re working on a project with tight deadlines, you may consider using an alerting service to tell you when new information has been added to the extjs.com site. http://www.changedetect.com is one option that offers a free web site modification alerting service.


16
Feb 10

Projects and Payroll

This is a screenshot of a system I wrote from scratch using ExtJS, PHP and MySQL. It features a multi-company employee information, timesheet and scheduling system as well as integration with Google Maps to show geolocations of past and present projects.