Utilities |
UP Browser | https://www.phone.com/developers/index.html |
HTML::Mason | CPAN, https://www.masonhq.com |
CGI::WML | CPAN |
Apache::WAP::AutoIndex | CPAN |
Apache::WAP::MailPeek | CPAN |
Mail::Cclient | CPAN |
Mobility! In my day we had to pick up the phone and put it on the acoustic modem. Now you can get stuff any time, anywhere. These kids...
I bought a new mobile phone with wireless Internet not long ago. The prospect of tracking news headlines, stock prices, and checking email over the phone seemed appealing. But after only a few minutes of browsing, I felt disappointed. Navigation was cumbersome, the viewscreen very small, and most frustrating of all, it took a really long time to get all the information I wanted. I read a bit more about the various portals that consolidate data from various sources, but after using one of them, I realized that no existing service would give me the variety of content I wanted. Besides, I've never much liked the idea of my username, passwords, and messages passing through someone else's servers. And this was about the same time I read the fine print on my service contract, explaining that I was paying for talk-time whenever I used my wireless Internet access.
In this article, I'll explore application development for wireless devices, first providing an overview of the WAP (Wireless Application Protocol) architecture, and then introducing some Perl modules to help create WAP applications.
A quick look at WAP
Wireless web browsers are the embodiment of a dominant trend in modern information technology. This trend is to equate "client" with "accessor", and "server" with "provider". While that definition might apply to most network applications, it has one of its simplest models in WAP, where dynamic functionality is best executed at the server, and no data is typically stored at the client. (Granted, you can, if youy try hard enough, use cookies with wireless devices, but such processing is interface-centric, like client-side JavaScript. Furthermore, support for such features is inconsistent.) This paradigm shift from client-distributed computing is evidenced by the popularity of personalized "my-" portals, web-based messaging, centralized file storage services, and the porting of major applications to the web.
WAP (Wireless Application Protocol) is a communications standard that includes markup, session, transaction, security, and transport application layers. These standards are maintained by the WAP Forum, founded by Ericsson, Nokia, Motorola, and Phone.com. (Phone.com was originally Unwired Planet, the company that pioneered wireless Internet services in the mid-1990s. Consequently, most of the browsers within modern phones run the Phone.com software.) Although the WAP Forum is currently working on version 1.3 of the specifications, most phones currently support only version 1.1.
Figure 1
The basics of WAP architecture are shown in Figure 1. An application server on the Internet receives WAP requests and responds with data (typically WML documents), sent over the Internet between the application server and a WAP gateway. The gateway routes and translates WAP requests to HTTP requests, determining what WAP data gets sent over a wireless network to a communications tower and eventually to your phone.
Enabling WAP on Apache
Thanks to the WAP gateway, any web server can deliver WML-formatted documents over HTTP by simply adding them to the MIME types for the server. In the case of Apache, you can add the following to an .htaccess
file to WAP-enable the server. (Most newer Apache servers already have these types in their mime.types
file):
addtype text/vnd.wap.wml wml addtype text/vnd.wap.wmlscript wmls addtype application/vnd.wap.wmlc wmlc addtype application/vnd.wap.wmlscriptc wmlsc addtype image/vnd.wap.wbmp wbmpWhen a wireless user wants to visit your site, they enter the URL. Unless you want to require your visitors to type .wml after a request, Apache will usually serve up an HTML document by default. To remedy this situation, we could use Apache's mod_rewrite engine to rewrite every request for an
.html
file to look for a
.wml
file in the same directory.
1
Again, since the WAP gateway supports HTTP headers, we can do this easily by adding the following to httpd.conf. Additional
HTTP_USER_AGENT
entries would need to be added, since this one will only rewrite Phone.com's UP.Browser client:
RewriteEngine on
RewriteLog logs/rewrite
RewriteLogLevel 9
RewriteCond %{HTTP_USER_AGENT} UP\.Browser
RewriteRule ^(.+)\.html$ $1.wml
There is another option, which makes use of Apache's HTTP_ACCEPT variable to list the MIME types that the connecting browser supports. However, most WAP clients report the ability to accept responses of type text/html, even though they lack the ability to translate the data.
A better option is to determine if the connecting browser supports WML, since most HTTP browsers cannot
read WML.
RewriteCond %{HTTP_ACCEPT} text\/vnd\.wap\.wmlOf course, to serve any dynamic content will require a bit more work. Before delving into that, let's look a bit at WML.
WML basics
WML (Wireless Markup Language) is a standard set of XML tags for the display of documents on mobile devices. Similar in function to HTML, these tags allow content to be formatted and linked. But given the limitations of wireless displays, the feature set is much more limited than HTML. And this is a good thing.
Since it is XML, WML requires strict formatting. Any errors in the markup, such as unterminated paragraph tags (<p></p>) should cause a client to return an error. (So long as we're talking about them, note that all text content must be within paragraph tags.) XML is usually explained to novices as the opposite of display data: rather than containing information about the formatting of data, as in HTML, XML contains information about the data itself: its structure, interrelation, and organization. WML would seem to be an exception -- but when it comes to wireless applications, the appearance of the data and its structure become intertwined.
A simple WML document is shown below. If you aren't familiar with XML, all you need to know about the document header is that it occurs within every WML document, and provides a data type definition for the client parsing the code. This header will only change if documents use different versions of WAP. Most do not.
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "https://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card id="main" title="First Example"> <p> Hello WAP World! </p> </card> </wml>
Figure 2
When saved as hello.wml
in a web server directory and requested from a wireless client, it will display what's shown in Figure 2.
If our web server enabled rewrites using the httpd.conf
directives shown earlier, this document could be requested as hello.html
, and still produce the desired result.
There are many tools you can use to develop wireless applications. The screenshots in this article are taken from Phone.com's Up.Simulator program, which allows you to browse the wireless web without using your phone (or the precious minutes you pay for if you share my service plan). Keep in mind that no two wireless browsers are identical. Although there are far fewer hornet's nests in the nested tags of WML than there are for HTML, there is no real consolation here for those craving a "write-once, read-the-same-everywhere" environment. But it is a standard that is adhered to between enterprises, if not functionally, at least syntactically. (Are you out there, XHTML?)
You got to know when to fold 'em
Latency is a bigger issue for WAP than for HTTP, since the system architecture itself is a many-hop, many-protocol network. In part to address the problem of delivery time, but also to fit markup to the WAP model, WAP applications don't use pages as they exist on the web. Instead, WAP has cards, where a card is simply what is displayed on a device at any given time. A single WML document might contain many cards. A collection of cards within a document is referred to as a deck, and can be explained as many analogous web pages folded into a single document with hyperlinks between the cards. The following example shows a WML document containing three cards and a menu to navigate between them, defined in the <template></template>
element.
<?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "https://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <template> <do type="accept" label="Back"> <prev/> </do> <do type="accept" label="Football"> <go href="#football"/> </do> <do type="accept" label="Basketball"> <go href="#basketball"/> </do> </template> <card id="main" title="Main"> <p> Use the option menu to find sports scores. </p> </card> <card id="football" title="football"> <p> Saints <b>31</b>, Rams <b>24</b><br/> Chargers <b>17</b>, Chiefs <b>16</b><br/> Eagles <b>23</b>, Redskins <b>20</b><br/> </p> </card> <card id="basketball" title="Basketball"> <p> Lakers <b>102</b>, Clippers <b>98</b><br/> Timberwolves <b>88</b>, Magic <b>87</b><br/> </p> </card> </wml>
Figure 3
Figure 4
Figure 5
I won't touch on the markup here much, other than to explain that the <do/>
tags specify an options menu to be displayed when the "Options" button on a phone is selected. Note that the href links specify a link within the local document, prefixed with the #. (These links could also be fully-qualified URLs to non-local cards.) Also, the <template>
tags enclose data that will be applied to every card in the deck, saving space. Initially, this doc-ument will display the screen shown in Figure 3.
Pushing the button beneath "Menu" will bring up
the <do/>
option menu, enclosed in the <template/>
element and dis-played in Figure 4.
Selecting the "Football" option from the list will take us to the card labeled football
and displayed in Figure 5.
A set of cards organized into a tree can collect many pages of data before sending a network request back to the web server, resulting in quicker WAP applications.
WML has other features intended to eliminate costly round-trips between mobile devices and WAP application servers. For example, WML has a <select/>
element that is much more functional than its HTML cousin, with <option/>
elements that may contain events to handle a given selection. For example:
<card id="products" title="Products"> <p> <select title="Flavors"> <option onpick="#vanilla">Vanilla</option> <option onpick="#chocolate">Chocolate</option> </select> <do type="accept" label="Go"> <noop/> </do> </p> </card>
In this example, selecting an option from the <select>
menu loads a different card from the deck, without another trip to the server and back.
Variables are another part of the core WML specification, and use a familiar syntax:
<setvar name="phone" value="432-0911">
<p>
Call me at $phone.
</p>
Variables can even be used within select lists to carry variables between cards, much the same way as the HTML <input type="hidden">
tag is often used to make data persistent across requests.
<select title="products" name="product"> <option value="Model B">Model B</option> <option value="Model D">Model D</option> </select>
A subsequent card can then use the selected value, stored in $product:
Are you sure you want to buy a $(product) Steinway?<br/>
And finally, variables can be posted to a server using the WML <go/>
elements with <postfield/>
tags, placed inside an <anchor/>
element in order to link responses of "Yes" or "No" to their appropriate locations:2
<anchor> Yes <go method="post" href="https://www.mypianostore.com/buy.cgi"> <postfield name="product" value="$(product)"/> </go> </anchor> <anchor> <go href="#products"/> No </anchor>
For a comprehensive look at WML, I recommend the new O'Reilly text Learning WML & WMLScript by Martin Frost. Wrox's Professional WAP contains more information on general mobile phone development (beyond WML), but is not as concise a read as the Frost book, probably due to the many-author model that Wrox seems to favor.
Developing WAP applications
Typically, the purpose of a display markup language is to enhance the appearance of data, sometimes to a fault. This is true of WML. However, the simplicity of wireless displays demands that the formatting of display elements be basic and straightforward. For this reason, it is generally not too much work to develop an entire WAP site using a simple text editor. Introducing dynamic content into a document, however, presents many breeds of challenges. With HTML, creating dynamic content usually means adding one type of markup to another; for instance, adding Perl or PHP "programmatic markup" to the conventional display markup. An alternative solution is to print the HTML directly from a program either by embedding HTML within the program code, or by calling functions that create the interface themselves, much as CGI.pm's printing functions automate the creation of HTML. A third approach is a total separation of content (such as XML) from interface (such as HTML/CSS or XSL), with application data stored elsewhere.
I won't argue here for a particular model. As is the case with HTML, each has its own strengths for WML development. Because of the simplicity of WML, display code generally represents a smaller portion of the total application than with HTML development. On the other hand, WML's notion of cards moves much of the application logic to the interface, simplifying the server. In the case of using XML with XSL to generate WML, there are serious issues of complexity and overhead, especially considering the limited information being generated.<sup>3</sup> There are many such considerations to be made when choosing a design model for WAP applications. For the remainder of this article, I'll explore three styles of WML generation: using the CGI::WML module to automate the creation of WML, marking up WML with Perl using a web development kit like Mason, and embedding WML directly within Perl programs. I'll show examples of each.
CGI::WML
If you ever shied away from using CGI.pm's printing functions, you probably justified it because of the complexity of your HTML. No such excuse can be made when delivering WML. The CGI::WML module, by Angus Wood, subclasses Stein's CGI.pm to give users a familiar interface to WML programming. All of the input parameter processing functions remain, as well as new functions to help you create WML documents:
use CGI::WML; $cgi = new CGI::WML; print $cgi->header(), $cgi->start_wml(), $cgi->template(-content=>$cgi->prev()), $cgi->card(-id=>"first_card", -title=>"First card", -content=>"<p><b>No one</b> when he has got <i>sufficient</i> ". "furniture for his house <b>dreams</b> of making further ". "purchases on this head, but of silver no one <i>ever</i> ". "yet possessed so much that he was forced to cry ". "\"enough.\"</p>"), $cgi->end_wml();
This example displays:
Content-Type: text/vnd.wap.wml; charset=ISO-8859-1 <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "https://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <template> <do type="accept" label="Back"> <prev/> </do> </template> <card id="first_card" title="First card" > <p><b>No one</b> when he has got <i>sufficient</i> furniture for his house <b>dreams</b> of making further purchases on this head, but of silver no one <i>ever</i> yet possessed so much that he was forced to cry "enough."</p> </card> </wml>
Figure 6
The result is shown in Figure 6.
(For those interested, the text is from "On Revenues" by Xenophon. Good stuff. Emphasis mine.)
The CGI::WML module also has functions for automated HTML-to-WML conversion and WML-to-WMLC conversion. WMLC documents compress deck content for quicker delivery, a task usually performed by a WAP gateway. WMLC conversion is also useful for testing the size of a compiled WML deck, since most WAP browsers have a maximum compiled deck size around 2000 bytes. Using the wml_to_wmlc()
function, you can continually check what size your deck will be after it has passed through the WAP gateway:
if (length($cgi->wml_to_wmlc($wml)) > 2000) { # do something }
For the moment, CGI::WML lacks functions to automate the creation of many WML 1.1 elements, including option menus and variables. These will be added in a future version, according to Wood.
A small file browser with CGI::WML and mod_perl
To use CGI::WML within an application, we'll write a short file browser. Often, when an HTTP browser accesses a web server directory without an index file, Apache's mod_autoindex module generates a file list. WAP browsers can't display a listing unless the WAP gateway being used is translating HTML responses, which is unlikely. We'll write a very basic module to provide this capability.
To start, we need a way for our module to know whether to handle this request. In a production environment, we'd want to determine if the requesting browser was a WAP client, as well as look for any index files within that directory prior to handling the request. Look at the Apache::AutoIndex module for code to perform such tasks. For now, we're going to treat any incoming request that ends in filelist.wml
as a directory request. Here's the module:
package Apache::WAP::AutoIndex; use strict; use CGI::WML; use Apache::Constants qw(:common); sub handler { my $r = shift; my $cgi = new CGI::WML; my $filename = $r->filename; my $url_filename = $r->uri; $filename =~ s/filelist\.wml$//; $url_filename =~ s/filelist\.wml$//; unless (opendir DH, $filename) { return FORBIDDEN; } my $content = "<p>Directory $url_filename:<br/>"; my $filelink; foreach my $file ( readdir DH ){ if (-d "$filename/$file") { $file .= "/"; $filelink = $file . "filelist.wml"; } else { $filelink = $file; } $content .= CGI::a({href => "$filelink"}, "$file"); } $content .= "</p>"; close DH; $r->print( $cgi->header(), $cgi->start_wml(), $cgi->template(-content=>$cgi->prev()), $cgi->card(-id=>"dirlist", -title=>"Directory $filename", -content=> $content), $cgi->end_wml() ); } 1;
To enable it, we need to add the following to httpd.conf
:
PerlModule Apache::WAP::AutoIndex <Location ~ "filelist\.wml$"> SetHandler perl-script PerlHandler Apache::WAP::AutoIndex </Location>
Figure 7
Figure 7 shows the result when pointed at /wap/filelist.wml on the site.
Generating WML with HTML::Mason
Sometimes it is easier to work directly with the WML markup and surround with it program code. If you'd prefer to mark up WML code with Perl, Mason is a good choice.
Mason, described in TPJ#17 is a comprehensive site development and delivery engine in pure Perl. Running with mod_perl under Apache, it allows you to create dynamic and modular web sites easily. Incorporating programmatic features into web pages is as simple as importing a module and directly calling its functions from within that page, and component-based development means that you can reuse and consolidate information that appears within many pages (or decks, in our case).
For those who haven't used Mason, you can download and install it from CPAN using the standard perl Makefile.PL
, make
, make test
, and make install
commands. There are a few prerequisites, and you need to configure mod_perl to load the HTML::Mason modules. Consult the Mason documentation for help on this; it's quick and painless. Once installed and loaded, you simply tell Mason which directories or files to handle, and then write your pages. You embed Perl directly into your HTML, by placing it within some predefined Mason tags, such as a <%perl>
block:
<%perl> use DBI; my $DSN = 'dbi:mysql:books'; my $dbh = DBI->connect($DSN, "user", "pass", { RaiseError => 1 } ) or die "Couldn't connect to database: $!\n"; </%perl>
Single lines of Perl can be placed into pages by prefixing them with a %
:
% my $query = $dbh->prepare("select name,author from books"); % $query->execute;
Mason treats anything not beginning with %
as HTML to be displayed. Mason's ability to understand blocks of Perl code, interspersed with HTML, makes this a powerful feature for program flow:
% # Print out all the books and authors % while (my $dat = $query->fetchrow_hashref) { <% $dat->{name} %> by <% $dat->{author} %><br> % }
If you let Mason handle your HTTP headers, it will trust Apache to print a header appropriate for the type of file (determined by the extension). More to the point, this means that if Mason is handling all requests for a given directory by having this in your httpd.conf
file:
<Location /mason>
SetHandler perl-script
PerlHandler HTML::Mason
</Location>
...then you can simply save Mason files in this directory with a .wml extension, and they will be delivered with the proper MIME type. You can also test the user agent within a Mason file, and thereby deliver the MIME type and content appropriate for the browser. This is done using the Mason <%init>
section, which makes an Apache request object available as the familiar $r:
4
<%init> my $content_type; if ($r->header_in('Accept') =~ /text\/vnd\.wap\.wml/) { $content_type = "wml"; $r->content_type('text/vnd.wap.wml'); } else { $content_type = "html"; $r->content_type('text/html'); } </%init>
Note what happens here: to determine the browser type, we're using the HTTP header HTTP_ACCEPT
(seen by mod_perl as Accept
), rather than the User Agent. (The only potential problem here: if future HTTP browsers support WML, they'll display the WML instead of the HTML.)
Throughout the rest of the document, different content could be served for the two content types:
% if ($content_type eq "wml") {
<card id="first_card" title="First card" >
<p>The weather for today is cold and hard.</p>
</card>
% } else {
<p><font size=3>The weather for today is cold and
hard.</font></p>
% }
A WML phone directory with Mason
The real benefits of placing program code markup within interface documents arises when the bulk of the content is dynamic. Assume we had an address book, and we wanted users to be able to browse it on WAP devices and directly call numbers in the address book. In the following example, the list of addresses is being generated from a MySQL database. We've stored the WML header information in a file, header.wml
, which is loaded as a component using the <& &>
syntax.
<%perl> use DBI; my $DSN = 'dbi:mysql:directory'; my $dbh = DBI->connect($DSN,"user","pass", { RaiseError => 1 } ) or die "Couldn't connect to database: $!\n"; my $query = $dbh->prepare("select name,phone from addresses"); $query->execute; </%perl> <& header.wml &> <wml> <card id="phonelist"> <p>Place a call to: <do type="accept"> <go href="wtai://vc/sc;$number;1" /> </do> <select name="number"> % while (my $dat = $query->fetchrow_hashref) { <option value="<% $dat->{phone} %>"><% $date->{name} %></option> % } </select> </p> </card> </wml>
Figure 8
The href
tag here makes use of the WTAI standard WAP libraries, which contain functions for interacting with the phone itself. This example accesses the vc (Voice Call Control) library to dial the number stored in variable $number
. The resulting display appears in Figure 8, and selecting a name from the list causes the phone to dial that number.
Of course, this could easily be extended to include more information from the address book, placing different types of information in different cards.
WML with straight Perl
When working with a markup language as simple as WML, there's something to be said for developing an application entirely in plain Perl, and embedding the WML tags directly into your program. While that may sound like a regression to the hard-coded CGIs of our youth, the simplicity of WML actually makes such interfaces easy to update and manage. Speaking of hard-coded CGIs, remember how useful that printenv
or env.cgi
script was? It's just as useful for debugging environments for WAP applications. Here it is in straight Perl with WML output:
#!/usr/bin/perl $output = <<EOF; Content-type: text/vnd.wap.wml <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "https://www.wapforum.org/DTD/wml_1.1.xml"> <wml><card id="env"><p> EOF foreach $var (sort(keys(%ENV))) { $val = $ENV{$var}; $val =~ s|\n|\\n|g; $val =~ s|"|\\"|g; $val =~ s|<|<|g; $val =~ s|>|>|g; $output .= "${var}=\"${val}\"<br/>\n"; } $output .= "</p></card></wml>"; print $output;
A remote control for home automation
Accessing information is only one use for wireless browsers. Wouldn't it be great if you could also use your phone to perform tasks? An interface to the Perl home automation package Control::X10 (see TPJ #17) makes just that possible.
Our application will have the user choose a house location and an appliance, and then be prompted to turn it on or off. The deck will consist of eight cards; the last card (turn on/off) will use values from previous cards for the action prompt, and will then post to the web server.
Translating this to a WML expression is trivial. In this example, we will place the deck directly into our Perl program. This excerpt could be run within a Perl module under mod_perl, or as a CGI script (gasp!). In our example, this is running within a module, with an Apache directive causing this module to handle all requests to /mister on the server.
Figure 9
First, we'll create our main card, shown in Figure 9.
my $main_card = <<EOF; <card id="main" title="Main"> <p> Select an area: <select title="Areas"> <option onpick="#outside">Outside</option> <option onpick="#living">Living Room</option> <option onpick="#kitchen">Kitchen</option> </select> </p> <do type="accept" label="Back"> <prev/> </do> <do type="accept" label="Go"> <noop/> </do> </card> EOF
Figure 10
Next, the "outside" card (Figure 10):
my $outside_card = <<EOF; <card id="outside" title="Outside"> <p> Select an appliance: <select name="appliance" title="Outside"> <option value="flood light">Flood light</option> <option value="christmas lights">Christmas lights</option> </select> </p> <do type="accept" label="Back"> <prev/> </do> <do type="accept" label="Go"> <go href="#toggle"/> </do> </card> EOF
Figure 11
We'll skip the cards for "Living Room" and "Kitchen", and skip to the last card of the deck (Figure 11).
my $toggle_card = <<EOF; <card id="toggle" title="Toggle appliance"> <p> Turn on/off the \$(appliance)?<br/> <a href="/mister?toggle=\$(appliance:e)">Yes</a> </p> <do type="accept" label="Back"> <prev/> </do> </card> EOF
The $(appliance:e)
syntax above causes the value of $appliance to be escaped for inclusion on the URL line. Keep in mind that this entire deck will be sent to the client at once. The server will not hear back from the client until the user gets to the last card and selects the "Yes" href
link, which will repost to the same program. Assuming that we are running this code as a mod_perl module, here's the rest of the program.
package MisterHouseWAP; use Apache::Constants qw(:common); use CGI::WML; require 'start_port.pl'; use ControlX10::CM17; our %appliances = ('christmas lights' => 'A1J', 'flood light' => 'A4J'); sub handler { my $r = shift; my @msgnos = (); my %params = $r->method eq 'POST' ? $r->content : $r->args; if ($params{'toggle'}) { toggle($params{'toggle'}); } my $deck = CGI::WML::header() . "<wml>\n" . $main_card . $outside_card . $toggle_card . "</wml>"; $r->send_http_header('text/vnd.wap.wml'); $r->print($deck); } sub toggle { # pseudo-sending code; see the X10 modules for real examples my $serial = open_port('COM1'); send_cm17($serial, $appliances{$_[0]}); $serial->close; } 1;
Creating a personal portal
Finally, we come to my personal motivation for this article. The difficulty of navigating information on commercial portals prompted me to create my own. It's really quite simple, consisting of eight cards, and using CPAN modules to interface with stock quotes (Finance::Quote), news, new mail summaries (Mail::Cclient), and DBM files for everything else. Everything is delivered in a single deck, so I can connect with my phone, download the deck, disconnect, and be able to browse the updates offline.
The following example allows a user to browse unread mail messages by first prompting for a username and password, and then using the Mail::Cclient module by Malcolm Beattie to fetch the unread messages from an IMAP server.
First, the login screen, which we'll call login.wml
:
<?xml version="1.0"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "https://www.wapforum.org/DTD/wml_1.1.xml"> <wml> <card title="Login"> <!-- Reset fields when entered backwards. --> <onevent type="onenterbackward"> <refresh> <setvar name="username" value=""/> <setvar name="password" value=""/> </refresh> </onevent> <!-- Read login and password from user. --> <p>Username: <input name="username"/></p> <p>Password: <input type="password" name="password"/></p> <!-- Submit button sends data to server. --> <do type="accept" label="Submit"> <go href="/wmlmail/" method="post"> <postfield name="username" value="$(username)"/> <postfield name="password" value="$(password)"/> </go> </do> </card> </wml>
Figure 12
Figure 13
This will prompt for a username and password as shown in Figures 12 and 13.
The module which handles the post from the above deck is handling all requests to /wmlmail/ using a Location directive in httpd.conf. If the login is successful, the user gets a list of unread mail messages with the sender and the subject, shown in Figure 4.
package Apache::WAP::MailPeek; use strict; use Apache::Constants qw(:common); use Mail::Cclient; our $mail_server = 'brians.org'; Mail::Cclient::parameters( 'NIL', RSHTIMEOUT => 0, OPENTIMEOUT => 1, READTIMEOUT => 1, CLOSETIMEOUT => 1, MAXLOGINTRIALS => 1, ); sub handler { my $r = shift; my @msgnos = (); my %params = $r->method eq 'POST' ? $r->content : $r->args; Mail::Cclient::set_callback login => sub { return $params{'username'}, $params{'password'} }, searched => sub { push (@msgnos, $_[1]); }, log => sub { print @_ }, dlog => sub { print @_}; my $mail = Mail::Cclient->new("{$mail_server/imap}") or die $!; $r->content_type('text/vnd.wap.wml'); $r->send_http_header; $r->print(<<END); <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "https://www.wapforum.org/DTD/wml_1.1.xml"> <wml><card id="mail"> END $mail->search("UNSEEN"); foreach my $msgno (@msgnos) { my ($envelope,$body) = $mail->fetchstructure($msgno); my $subject = $envelope->subject; my $from = ${$envelope->{from}}[0]->{personal} || ${$envelope->{from}}[0]->{mailbox} . "@" . ${$envelope->{from}}[0]->{host}; $from =~ s/\&/\&\;/g; $subject =~ s/\&/\&\;/g; $from =~ s/\$/\$\$/g; $subject =~ s/\$/\$\$/g; $r->print ("<p><b>", $from, "</b>: ", $subject, "</p>\n"); } $mail->close; $r->print("</card></wml>"); } 1;
Figure 14
In time, I'll abstract the entire portal enough to get it on CPAN. But as you can see, creating of such a system is quite straightforward, thanks to the compact nature of WML and the ease of developing applications in Perl.
Whether you opt to embed WML within scripts, mark up WML with Perl using a web development kit like Mason, or use modules to automate the creation of WML, Perl makes such development extremely simple and rapid. With tools like this, its a wonder we use anyone else's web services at all. I'm kidding.
Dan is a composer, linguist, mentalist, gamer, and father of two. By day he masquerades as a software engineer at Verio, Inc.
1 If mod_rewrite isn't part of your Apache installation, and you've compiled the Apache apxs utility, you can add mod_rewrite from the Apache 1.3.12 source directory with apxs -i src/modules/standard/mod_rewrite.c.
2 WML also supports some traditional HTML tags for ease of use. For example, <a/> tags may be used in place of <anchor/>.
3 While I'm a big fan of XSLT, being able to transform a single data set to many types of interfaces does not necessarily address the core issue here. A WAP application will hopefully not differ from an HTTP application only in its presentation, but also in its design, flow, and function. WAP usability concerns require that applications go far beyond simply translating data for presentation.
4 If you aren't familiar with the Apache API as provided by mod_perl, consult O'Reilly's Writing Apache Modules with Perl and C by Stein and MacEachern for information that will change your development life and world view.
footnotes
HexWeb HTML
1 If mod_rewrite isn't part of your Apache installation, and you've compiled the Apache apxs utility, you can add mod_rewrite from the Apache 1.3.12 source directory with apxs -i src/modules/standard/mod_rewrite.c.
2 WML also supports some traditional HTML tags for ease of use. For example, <a/> tags may be used in place of <anchor/>.
3 While I'm a big fan of XSLT, being able to transform a single data set to many types of interfaces does not necessarily address the core issue here. A WAP application will hopefully not differ from an HTTP application only in its presentation, but also in its design, flow, and function. WAP usability concerns require that applications go far beyond simply translating data for presentation.
4 If you aren't familiar with the Apache API as provided by mod_perl, consult O'Reilly's Writing Apache Modules with Perl and C by Stein and MacEachern for information that will change your development life and world view.
utilities
HexWeb HTML
UP Browser https://www.phone.com/developers/index.html HTML::Mason CPAN, https://www.masonhq.com CGI::WML CPAN Apache::WAP::AutoIndex CPAN Apache::WAP::MailPeek CPAN Mail::Cclient CPAN