http://www.developer.com/ws/proto/article.php/1577921/Building-WML-Gadgets-Phone-Message-Application.htm
This series of articles describes how to provide Webcontent to mobile devices through WML (Wireless Markup Language). This articlecovers creating an application to aid the user of a mobile phone. Note: These articles cover WML and WMLScript version 1.1,which are supported by the majority of mobile devices in use today. Thearticles assume a working knowledge of HTML and general Web technologies, andfurther assume that you have read the previous article(s) in this series. Not all wireless applications have to be super-applications.Some of the best wireless applications perform simple tasks to improve wirelessfunctionality. The last few articles in this series have shown how simple,single-purpose gadgets can boost the functionality of mobile devices. Thisarticle will present a slightly more complex application in the same"extending functionality" vein. This article will cover how to build a simple phone messageapplication. Although we live in a time of portable phones, intelligentvoicemail, and other electronic telephone magic, there are still times whenmessages are taken by one person (operator) and passed to others (recipients).For example, consider a businessman who often travels outside the home office.Many of his customers and contacts may occasionally call the home office andleave messages with his secretary. Using a simple Web form, the secretary canpass the message to the businessman's cell phone, where he can review themessage and even return the call with the simple press of a button. This application will utilize the following components: Essentially, the application operates as shown in thefollowing diagram: FIGURE 1 - Our application's design. The operator uses a Webform to send the data to a CGI script that stores the data in a database. Thesame script is used by a mobile user (recipient) to access that data. We'll use Perl for the CGI script, for the same reasonswe've used it previously: It's available for most platforms and extensibleenough to perform almost any task necessary. Note: As with previous articles, teaching Perl is out of thescope of this series. There are numerous sources on the Internet for learningPerl, including the tutorial at Let's break down the individual processes and then code foreach. Our "database" will be a simple delimited flatfile. Although we could go the fancy route with an actual database format, thedelimited format will work well for our simple application. The database will contain the following fields: We'll use a double vertical bar for our delimiter. We coulduse a more standard delimiter, such as a comma, but we need something thatwouldn't end up in the middle of the message field. In short, our databaserecords will resemble the following: <data and time>||<caller's name>||<caller'smessage>||<caller's number> We'll use a simple HTML form for entering the data: Listing: msgform.html Note that we include an extra field, "cmd." Thishidden field will be passed to our CGI script to tell it what to do; namely,"save" the data. Our CGI script will be one multipurpose script, performingthe following functions: The script could also display the input form. However, formaximum portability, we'll use a simple HTML file. With this method, our formcan easily be included in almost any Web page template, simply by applying astyle sheet or by cutting-and-pasting the "guts" of the form intoanother page. The following code fragment saves the data entered into theform: Listing: phonemsg.pl - Save data fragment Because the form and the mobile device could both beutilizing the script (and hence, the database) simultaneously, we must use filelockingto avoid having two processes accessing the database and corrupting ourdata. We'll use a simple method: creating a file to lock the databaseif thefile exists, the process waits for it to be deleted before accessing thedatabase. When a process is done with the database, the lock file is deletedand other processes are allowed to access the database. Instead of failingright away if the file is locked, or waiting forever for the file to beunlocked, the code loops 10 times, waiting a tenth of a second between theiterations. If the file is still locked, we assume that something has gonewrong and exit with an appropriate error message. Windows users: Some implementations of Perl on the Windowsplatform don't support the four-argument call of "select" used in ourfile-locking loop. If this code generates errors on a Windows system,substitute another delay function. The following code fragment comprises the code to list fiverecords on the mobile device, along with an option to move to the next fiverecords: Listing: phonemsg.pl - List records fragment Most of the work for this fragment happens in the lastquarter of the code. After the stage has been set (WML card defined), thedatabase is read into an array and then the correct set of records isdisplayed, five records at a time. Initially, the first five records aredisplayed and an option is set for the next five to be selected (through the"nextitem" variable). If the user picks the "Next" option,the script is called again, with the name/value pair "nextitem" setto the "next item" to display. Each record is displayed as an <option> whose "onpick"attribute results in the script being called with the record number to display.(See the next section for the record-display format and the whole scriptsection for details on the format and use of the $cmd variable.) The file-locking routine for the database differs only inthe message that's displayed if a lock cannot be achieved. The messageincorporates enough WML code to complete the card, ensuring that the user getsthe failure message instead of a WML compile error. When the user picks a record from the laundry list ofrecords, the script needs to be able to display the record's details. Thefollowing code takes care of that activity: Listing: phonemsg.pl - Display specific record fragment The record number to display is passed to the script via the"rec" variable. As in the other cases, the file lock is checked, thedatabase is locked, the database is read into an array, and then the databaseis released (unlocked). The required record is then read from the array,unpacked into fields (based on the delimiter), and displayed. Notice that two links are placed at the bottom of thedisplayed record: "Call" and "Call & Del(ete)". Thefirst simply uses the "call this number" URL format to make themobile device dial a number. The latter option allows the user to delete the messagebefore placing the call, since the message is being returned and should nolonger be stored in the database as an open message. The script handles returning a call by including a link to aURL containing the number to call (see the previous section). Deleting a recordbefore the call is slightly more complex, and is handled by the following code: Listing: phonemsg.pl - Place call/delete record fragment To delete and call a number, the script is passed the numberand the record number to delete. The number could be retrieved from the recordbefore the deletion, but is passed separately so it doesn't have to be parsedfrom the record. This design decision falls into the "half-a-dozen versussix" category of decisions; I opted to pass the number we already have,saving the lines required to decode the record before deleting it. To perform the deletion, we read the entire file into anarray and then reconstruct the file by writing all records except the deletedrecord back to the file. After adding a few declaration lines, the controllingstructure using the $cmd variable, and some connecting tissue, our completedscript becomes the following: Listing: phonemsg.pl Note that the default action of the script is to list therecords. This allows the mobile device to call the script without arguments("http://URL/phonemsg.pl") to get the ball rolling. Subsequent callsare handled by the script ("display," "callNdel," etc.)where it controls the parameters, saving the mobile user from having toenter/bookmark them. To test the application, we seed the database with thefollowing data: Listing: msgfile.txt - Sample data Using this data, our application resembles the followinggraphics on a mobile device: FIGURE 2 - The laundry list of messages. FIGURE 3 - A selected message is displayed. FIGURE 4 - Two links at the bottom of the record allow theuser to call and optionally delete the message. Images are courtesy Openwave Systems Inc. (Openwave, the Openwave logo, Openwave SDK, Openwave SDK Universal Edition, Openwave SDK WAP Edition are trademarks of Openwave Systems Inc. All rights reserved.) This application makes a nice, general phone message system.However, given time and incentive, the following improvements could be made: 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. Steve Schafer is the chief operating officer of ProgenyLinux Systems, a Linux-based consulting company in Indianapolis, Indiana. Hehas written several technical books and articles and can be reached at sschafer@synergy-tech.com. # # #
Building WML Gadgets: Phone Message Application
January 31, 2003
Simple Applications
The Application
Application Specifications

Coding the Application
Database Design
HTML Form for Data Entry
<html><body><form name="msgform" method="post" action="phonemsg.pl"><center>Telephone Message</center><br><table><tr><td><input type="hidden" name="cmd" value="save">From:</td><td><input type="text" name="from" size="100" maxlength="100"></td></tr><tr><td>Message:</td><td><textarea name="message" cols="100" rows="5" wrap="virtual"></textarea></td></tr><tr><td>Callback #:</td><td><input type="text" name="number" size="12" maxlength="12"></td></tr><tr><td colspan="2"><center><input type="submit" value="Submit"></center></td></tr></table></form></body></html>
The CGI Script
Saving the Data
# Start response page print header; print "<html><head>"; print "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"2; URL='msgform.html'\">"; print "</head><body>Please wait...<p>"; # Grab the parameters $from = param('from'); $message = param('message'); $callback = param('number'); # Remove any vertical bars $message =~tr/|/ /; # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file! </body></html>"; } } open LOCK, ">msgfile.lock"; # Write new record to end of file open FILE, ">>msgfile.txt"; print FILE $date ." ". $time ."||"; print FILE $from ."||". $message ."||". $callback; print FILE "\n"; close FILE; close LOCK; unlink "msgfile.lock"; # All done, close response page print "</body></html>";Listing the Records on the Mobile Device
#Pass WML header print header(-type=>'text/vnd.wap.wml');# Print WML header and beginning tagsprint <<ENDHEADER;<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><card>ENDHEADER # Print start of cardprint <<BEGINCARD;<p mode="nowrap">Call List<br/><select name="Display" title="Display">BEGINCARD # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file! </select></p></card></wml>"; } } open LOCK, ">msgfile.lock"; # Read records into array open FILE,"msgfile.txt"; push(@lines,$_) while (<FILE>); close FILE; close LOCK; unlink "msgfile.lock"; # Nextitem = first item to display $nextitem = param('nextitem'); if ("$nextitem" eq "") { $nextitem = 0; } # Lastitem = last item to display # (nextitem + 4 or end of file) if ($nextitem + 4 <= @lines) { $lastitem = $nextitem + 4; } else { $lastitem = @lines - 1; } # Print first item through last item for ($i = $nextitem; $i <= $lastitem; $i++) { ($datetime,$from,$message,$callback) = split /\|\|/,$lines[$i]; print "<option onpick='phonemsg.pl?cmd=display&rec=$i'>"; print $from ." (". $datetime .") \n"; print "</option> \n"; } # Set nextitem for NEXT function $nextitem = $lastitem + 1; # Display NEXT option print "<option onpick='phonemsg.pl?cmd=list&nextitem=$nextitem'>"; print "Next</option> \n"; print "</select> \n</p> \n"; print "</card> \n</wml> \n";Displaying a Specific Record on the Mobile Device
# What record to display $rec = param('rec'); #Pass WML header print header(-type=>'text/vnd.wap.wml');# Print beginning of WML fileprint <<ENDHEADER;<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><card>ENDHEADER print "<p mode=\"wrap\"> \n"; # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file!</p></card></wml>"; } } open LOCK, ">msgfile.lock"; # Load records into array open FILE,"msgfile.txt"; push(@lines,$_) while (<FILE>); close FILE; close LOCK; unlink "msgfile.lock"; # Split record into fields ($datetime,$from,$message,$callback) = split /\|\|/,$lines[$rec];# Display record with "call" # and "call & delete" optionsprint <<MESSAGE;$datetime<br/>$from<br/><br/>$message<br/>Callback Number:<br/>$callback<br/><a href="wtai://wp/mc;$callback" title="Call">Call</a> <a href="./phonemsg.pl?cmd=callNdel&number=$callback&rec=$rec" title="CallnDel">Call & Del</a></p></card></wml>MESSAGEPlacing a Call and Optionally Deleting a Record
# What number to call and what record to delete $number = param('number'); $rec = param('rec'); #Pass WML header print header(-type=>'text/vnd.wap.wml');# Print start of WML fileprint <<ENDHEADER;<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><card>ENDHEADER# Print beginning of card # (Number is dialed in 10 secs)print <<BEGCARD; <onevent type="ontimer"> <go href="wtai://wp/mc;$number" /></onevent><timer name="delay" value="100"/><p mode="wrap">Deleting record $rec, preparing to call...BEGCARD # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file!</p></card></wml>"; } } open LOCK, ">msgfile.lock"; # Read records into array open FILE,"msgfile.txt"; push(@lines,$_) while (<FILE>); close FILE; # Print all records (except deleted) # back to file open FILE,">msgfile.txt"; for ($i = 0; $i < @lines; $i++) { if ($i ne $rec) { print FILE $lines[$i]; } } close FILE; close LOCK; unlink "msgfile.lock"; # Close card print "</p> \n </card> \n </wml>";The Completed Script
#!/usr/bin/perl# Include modulesuse CGI qw(:standard);use Date::Calc qw(:all);# Set command to execute; default = list$cmd = param('cmd');if ("$cmd" eq "") { $cmd = "list"; }# Set current time/date($year,$month,$day) = Today();($hour,$min,$sec) = Now();$time = $hour.":".$min;$date = $month."/".$day."/".$year; # # Call from HTML form, save the data, and return to formif ("$cmd" eq "save") { # Start response page print header; print "<html><head>"; print "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"2; URL='msgform.html'\">"; print "</head><body>Please wait...<p>"; # Grab the parameters $from = param('from'); $message = param('message'); $callback = param('number'); # Remove any vertical bars $message =~tr/|/ /; # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file! </body></html>"; } } open LOCK, ">msgfile.lock"; # Write new record to end of file open FILE, ">>msgfile.txt"; print FILE $date ." ". $time ."||"; print FILE $from ."||". $message ."||". $callback; print FILE "\n"; close FILE; close LOCK; unlink "msgfile.lock"; # All done, close response page print "</body></html>";}# # Call from mobile device to list callsif ("$cmd" eq "list") { #Pass WML header print header(-type=>'text/vnd.wap.wml');# Print WML header and beginning tagsprint <<ENDHEADER;<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><card>ENDHEADER # Print start of cardprint <<BEGINCARD;<p mode="nowrap">Call List<br/><select name="Display" title="Display">BEGINCARD # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file! </select></p></card></wml>"; } } open LOCK, ">msgfile.lock"; # Read records into array open FILE,"msgfile.txt"; push(@lines,$_) while (<FILE>); close FILE; close LOCK; unlink "msgfile.lock"; # Nextitem = first item to display $nextitem = param('nextitem'); if ("$nextitem" eq "") { $nextitem = 0; } # Lastitem = last item to display # (nextitem + 4 or end of file) if ($nextitem + 4 <= @lines) { $lastitem = $nextitem + 4; } else { $lastitem = @lines - 1; } # Print first item through last item for ($i = $nextitem; $i <= $lastitem; $i++) { ($datetime,$from,$message,$callback) = split /\|\|/,$lines[$i]; print "<option onpick='phonemsg.pl?cmd=display&rec=$i'>"; print $from ." (". $datetime .") \n"; print "</option> \n"; } # Set nextitem for NEXT function $nextitem = $lastitem + 1; # Display NEXT option print "<option onpick='phonemsg.pl?cmd=list&nextitem=$nextitem'>"; print "Next</option> \n"; print "</select> \n</p> \n"; print "</card> \n</wml> \n";}# # Call from mobile device to show call detailif ("$cmd" eq "display") { # What record to display $rec = param('rec'); #Pass WML header print header(-type=>'text/vnd.wap.wml');# Print beginning of WML fileprint <<ENDHEADER;<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><card>ENDHEADER print "<p mode=\"wrap\"> \n"; # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file!</p></card></wml>"; } } open LOCK, ">msgfile.lock"; # Load records into array open FILE,"msgfile.txt"; push(@lines,$_) while (<FILE>); close FILE; close LOCK; unlink "msgfile.lock"; # Split record into fields ($datetime,$from,$message,$callback) = split /\|\|/,$lines[$rec];# Display record with "call" # and "call & delete" optionsprint <<MESSAGE;$datetime<br/>$from<br/><br/>$message<br/>Callback Number:<br/>$callback<br/><a href="wtai://wp/mc;$callback" title="Call">Call</a> <a href="./phonemsg.pl?cmd=callNdel&number=$callback&rec=$rec" title="CallnDel">Call & Del</a></p></card></wml>MESSAGE}## Call from mobile device to dial number and del recordif ("$cmd" eq "callNdel" ) { # What number to call and what record to delete $number = param('number'); $rec = param('rec'); #Pass WML header print header(-type=>'text/vnd.wap.wml');# Print start of WML fileprint <<ENDHEADER;<?xml version="1.0"?><!DOCTYPE wml PUBLIC "-//WAPFORUM//DTD WML 1.1//EN" "http://www.wapforum.org/DTD/wml_1.1.xml"><wml><card>ENDHEADER# Print beginning of card # (Number is dialed in 10 secs)print <<BEGCARD; <onevent type="ontimer"> <go href="wtai://wp/mc;$number" /></onevent><timer name="delay" value="100"/><p mode="wrap">Deleting record $rec, preparing to call...BEGCARD # Check file lock $count = 0; if (-e "msgfile.lock") { select(undef,undef,undef,0.1); $count++; if ($count = 10) { die "Can't open message file!</p></card></wml>"; } } open LOCK, ">msgfile.lock"; # Read records into array open FILE,"msgfile.txt"; push(@lines,$_) while (<FILE>); close FILE; # Print all records (except deleted) # back to file open FILE,">msgfile.txt"; for ($i = 0; $i < @lines; $i++) { if ($i ne $rec) { print FILE $lines[$i]; } } close FILE; close LOCK; unlink "msgfile.lock"; # Close card print "</p> \n </card> \n </wml>";}1/25/2003 12:15||Caller Number01||Sample message, from sample caller.||317-555-12121/25/2003 12:25||Caller Number02||Sample message, from sample caller.||317-555-12121/25/2003 12:35||Caller Number03||Sample message, from sample caller.||317-555-12121/25/2003 13:15||Caller Number04||Sample message, from sample caller.||317-555-12121/26/2003 14:15||Caller Number05||Sample message, from sample caller.||317-555-12121/26/2003 14:23||Caller Number06||Sample message, from sample caller.||317-555-12121/26/2003 15:15||Caller Number07||Sample message, from sample caller.||317-555-12121/27/2003 9:15||Caller Number08||Sample message, from sample caller.||317-555-12121/27/2003 9:35||Caller Number09||Sample message, from sample caller.||317-555-12121/28/2003 8:05||Caller Number10||Sample message, from sample caller.||317-555-12121/28/2003 10:15||Caller Number11||Sample message, from sample caller.||317-555-12121/28/2003 11:11||Caller Number12||Sample message, from sample caller.||317-555-12121/28/2003 12:01||Caller Number13||Sample message, from sample caller.||317-555-12121/28/2003 14:15||Caller Number14||Sample message, from sample caller.||317-555-12121/28/2003 16:45||Caller Number15||Sample message, from sample caller.||317-555-12121/29/2003 8:25||Caller Number16||Sample message, from sample caller.||317-555-12121/29/2003 9:04||Caller Number17||Sample message, from sample caller.||317-555-12121/29/2003 10:35||Caller Number18||Sample message, from sample caller.||317-555-12121/29/2003 10:39||Caller Number19||Sample message, from sample caller.||317-555-12121/30/2003 12:15||Caller Number20||Sample message, from sample caller.||317-555-12121/30/2003 15:02||Caller Number21||Sample message, from sample caller.||317-555-12121/30/2003 16:05||Caller Number21||Sample message, from sample caller.||317-555-1212



Room for Improvement
What Do You Want from WML?
About the Author