http://www.developer.com/

Back to article

Tracking Users Using WML


March 27, 2003

This article describes how to provide Web content to mobile devices through WML (Wireless Markup Language). More specifically, this article covers how to track users; that is, how to recognize a repeat visitor to your site.

WML and WMLScript version 1.1 are supported by the majority of mobile devices in use today. The articles assume a working knowledge of HTML and general Web technologies, and further assume that you have read the previous article(s) in this series.

The Value of Recognizing Users

There are a variety of reasons to implement a recognition system that acknowledges that a user that has previously visited your site. The most useful of these reasons is to remember user preferences.

For example, suppose that you offer a service of finding particular restaurants near a user. Each user may prefer a certain type of food, environment, etc., and knowing where the user is located is important so that you can find restaurants in that vicinity. Of course, users don't want to input all their preferences every time they visit your site—it's better to save most of the settings, recognize users when they return, and recall their settings.

How To Recognize Users

When WML was first implemented, the code could retrieve the user's cell phone number from the device; this phone number could act as a unique identifier. Unfortunately, this ability was recognized as an invasion of privacy, and the feature was discontinued. Now there is no way to retrieve a unique identifier from the user's device.

What options are left? You could have the user input his or her telephone number on each visit, assign the visitor a login name, or have the user enter an identifier during each visit. Better yet, why not store the identifier on the user's device for recall each time he or she visits your site?

Cookies: The Good, the Bad, and the Ugly

The term cookie refers to the HTTP technology that allows a site to store data on a user's machine. When cookies were first used, they were fairly innocuous, intended primarily for storing user preferences. However, it didn't take long for the business side of the Web to realize the potential of cookies and begin using them for their own purposes—tracking user activities, shopping habits, and other bits of personal information.

Shortly thereafter, the media and user advocates led the charge against cookies, causing most modern browsers to offer the option of refusing to store any cookies, or allowing the user to choose when a cookie should or should not be stored. Unfortunately, refusing cookies causes problems for Web sites that use cookies to store user preferences and login info, requiring the user to reenter such info on each visit.

Note: I don't condone the illicit use of cookies for tracking users' personal information, but do recognize the utility of storing frequently used information to aid the user experience.

How Cookies Work

Cookies work by storing data on the user's local computer/device. This data is stored via an HTTP dialog between the server and the client. That data can then be recalled by the server, processed, updated, etc. Figure 1 shows the data paths associated with storing and retrieving cookies.


Figure 1 - Cookie data is passed back and forth between the server and client via the HTTP stream, but the data is actually stored on the client side.

The information stored in the cookie can be just about any type of data: string, date, an integer, or a real number. Most sites choose to encode cookie data into a lengthy string that can be decoded and parsed by the site code.

Note: Windows users can examine the cookies that have been stored on the local machine. Look in your local settings directory(ies) for "cookies" files. Windows XP users can find the cookies in the following directory:

C:\Documents and Settings\{Username}\Cookies

In addition to data, cookies are stored with a time to live (TTL), specifying how long the cookie should be stored before being discarded. The cookie also indicates the scope for which it should be used—that is, what directory(ies) on the server are valid for that cookie. This allows a site to store multiple cookies with the same name, but different scopes. For example, a site with several sections could store preferences for each section in a cookie named "prefs."

Using Cookies with WML

Because WML doesn't have any built-in cookie functions, you have to use other technologies to store and retrieve cookies. This article shows how to use Perl, which has robust cookie-handling abilities.

We'll use the HTTP header Set-Cookie to set cookies in the examples. Although WML has a <head> tag, don't confuse the HTTP header with the WML card head—they're different animals. Cookies must be set before the end of the HTTP header; the WML <head> comes after the HTTP header has been sent, and therefore it cannot be used to set cookies.

You can use almost any language that supports cookie functions to accomplish the goals in this article. For example, PHP's header() function can be used to set cookies. Even more control can be accomplished with PHP's setcookie() function. Several variable structures exist in PHP to read cookies, including $HTTP_COOKIE_VARS and $_COOKIE arrays.

Handling Cookies with Perl

There are many options for handling cookies in Perl, including simple HTTP methods and even dedicated Perl libraries such as cookie-lib.pl. We'll use the simple HTTP methods for this article.

For more robust cookie management, the reader is encouraged to check out other cookie-handling methods, such as cookie-lib.pl and HTTP::Cookies. The former is available online at The CGI Resource Index (http://cgi.resourceindex.com/Programs_and_Scripts/Perl/Cookies/); the latter is from CPAN (http://search.cpan.org/author/RSE/lcwa-1.0.0/lib/lwp/lib/HTTP/Cookies.pm).

Setting a Cookie with Perl

As discussed earlier, we can use the HTTP header Set-Cookie to set cookies. In its simplest usage, this header takes the following form:

Set-Cookie: <name of cookie>=<value of cookie>

For example, a real header might be as follows:

Set-Cookie: name=Steve

When this header is passed to a browser, it sets the cookie "name" equal to "Steve." Because the header doesn't include a time to live (TTL), the cookie is only valid for the current session. When the browser is closed, the cookie expires and is deleted.

To include a TTL, you add the parameter expires as shown in the following example:

Set-Cookie: name=Steve; expires=Monday, 24-Mar-03 23:59:59 GMT

Notice the use of a semicolon (;) to delimit the parameters. The date is in the format "weekday, dd/Mon/yy hh:mm:ss." In the example above, the cookie will expire at one second before midnight on Monday, March 24, 2003, Greenwich Mean Time.

Tip: It's important to include at least one blank line after the Set-Cookie header and before the WML headers so the client correctly identifies the WML headers.

Let's look at a real example of using Perl to set a cookie for a WML deck. The following code snippet shows how Perl is used to set a cookie and output a status ("Cookie set") message:

#!/usr/bin/perl# Define minimal deck$deck = '<wml>  <card>    <p>      Cookie set.    </p>  </card></wml>';# Send Content-type and Set-Cookie headersprint "Content-type: text/vnd.wap.wml \n";print "Set-Cookie: name=Steve \n";# Send WML header infoprint "\n<?xml version=\"1.0\"?>\n";print "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\""   . " \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n";# Send deckprint $deck;

The cookie in the code above was set without a TTL. To set an expiration date, we draw on the Date::Calc module to do our date calculations:

#!/usr/bin/perl# Include Date::Calc.use Date::Calc':all';# Get today in GMT($year,$month,$day) = Today([$gmt]);# Add a year (365 days)($year,$month,$day) =  Add_Delta_Days($year,$month,$day,"365");# Get textual representations of month and day of week$dow =  Day_of_Week_to_Text(Day_of_Week($year,$month,$day));$month = Month_to_Text($month);# Make sure day is two digitsif ($day<10){ $day = '0'.$day;}# Assemble expiration date$date = $dow.", ".$day."-".$month."-".$year." 23:59:59 GMT";# Define deck$deck = '<wml>  <card>    <p>      Cookie set.    </p>  </card></wml>';# Send Content-type and Set-Cookie headersprint "Content-type: text/vnd.wap.wml \n";print "Set-Cookie: name=Steve; expires=$date; \n";# Send WML headersprint "\n<?xml version=\"1.0\"?>\n";print "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\""   . " \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n";# Send the deckprint $deck;

Note: The Date::Calc module has been covered in several previous articles. The module is available from CPAN, at http://search.cpan.org/author/STBEY/Date-Calc-5.3/Calc.pod.

Reading Cookies with Perl

Reading cookies with Perl is even easier than setting them, thanks to the environment variable HTTP_COOKIE. This variable contains name/value pairs for all applicable cookies (those matching the current scope).

To parse the name/value list, you could use the following code:

@nvpairs=split(/[,;] */, $ENV{'HTTP_COOKIE'});foreach $pair (@nvpairs) {   ($name, $value) = split(/=/, $pair);   $cookie{$name} = $value;}

This code effectively parses the cookie list into the array $cookie, where each value can be accessed by its name. For example, our earlier "name" example would yield:

$cookie{'name'} = "Steve"

An extended example, displaying the cookie value in WML, is shown below:

#!/usr/bin/perl# Break cookies into name/value pairs# and store into cookie array@nvpairs=split(/[,;] */, $ENV{'HTTP_COOKIE'});foreach $pair (@nvpairs) {   ($name, $value) = split(/=/, $pair);   $cookie{$name} = $value;}# Define WML deck$deck = '<wml>  <card>    <p>      Cookie (name) = '.$cookie{'name'}.'    </p>  </card></wml>';# Send Content-type and Set-Cookie headersprint "Content-type: text/vnd.wap.wml \n";print "Set-Cookie: name=Steve; expires=$date; \n";# Send WML headersprint "\n<?xml version=\"1.0\"?>\n";print "<!DOCTYPE wml PUBLIC \"-//WAPFORUM//DTD WML 1.1//EN\""   . " \"http://www.wapforum.org/DTD/wml_1.1.xml\">\n";# Send the deckprint $deck;

If the cookie was still set, this code would display the following:

Cookie (name) = Steve
Deleting Cookies

To remove a cookie, you simply set the expiration of the cookie to a date/time that has already passed. For example, we could use the code earlier but subtract a day or two to expire the cookie. The code, using Date::Calc functions, would resemble the following:

# Get today in GMT($year,$month,$day) = Today([$gmt]);# Subtract a year (365 days)($year,$month,$day) =  Add_Delta_Days($year,$month,$day,"-365");# Get textual representations of month and day of week$dow =  Day_of_Week_to_Text(Day_of_Week($year,$month,$day));$month = Month_to_Text($month);# Make sure day is two digitsif ($day<10){ $day = '0'.$day;}# Assemble expiration date$date = $dow.", ".$day."-".$month."-".$year." 23:59:59 GMT";

Note the use of a minus sign in the Add_Delta_Days function to subtract a year from today. We could just as easily subtract only one day.

Tying It All Together

Now that we can set and retrieve cookies, what exactly do we do with them? As stated earlier, we could store a variety of information in cookies for use in helping the user interface when the visitor returns to our site.

In the earlier restaurant example, for instance, you could utilize the following cookies:

Name:User's name
Phone-Number: Device phone number
Zipcode:Target ZIP code for restaurant matches
Genre:User's favorite type of food
...

Alternatively, you could encode all this data into one lengthy string.

My advice, however, would be to tie one unique piece of data that identifies the user to a database record. Basically, store only what you need on the client device, and store the rest of the data in a database accessible by the server. The flowchart for an application using this method might resemble Figure 2.



Click here for larger image

Figure 2 - Flowchart of application using described method

On entering the site, the user's browser is checked for an ID cookie. If the cookie is found, the user's preferences are retrieved from the server's database and the site is displayed with those preferences. If the cookie is not set, the user is taken to a preference form and queried for his/her preferences. Those preferences are stored in the server's database and the user's ID is stored as a cookie.

What Do You Want from WML?

I'm interested in hearing what you need/want to do with WML. I'll use some of the more challenging or common ideas in upcoming articles. Send your ideas to the address below.

About the Author

Steve Schafer is the chief operating officer of Progeny Linux Systems, a Linux-based consulting company in Indianapolis, Indiana. He has written several technical books and articles and can be reached at sschafer@synergy-tech.com.

# # #

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date