5.5. POST Example: License Plates

Second only to the issues surrounding tattooing and tattoo removal, the hardest decision one ever has to make is, upon moving to California and buying a convertible, what personalized license plate should one get? In the past, this was a slow and embarrassing process, requiring one to go to the Motor Vehicles office, shuffle up to the clerk, and meekly request "HOTBABE," only to receive the crushing news that someone else has, somehow, already thought of that and taken it as her own personalized license plate. While there are 66,220,518,000 possible combinations,[2] it is apparently a devoted pursuit of the state's 30-odd million residents to think of personalized license plates. As with Internet domain names, if you can think of it, someone probably already has it.

[2] This is based on the formula: $c += 35 ** $_ - 10 ** $_ for 2 .. 7; print $c;. (The 35 is because letter O is treated as digit zero. The 10 is because all-digit plates are not allowed.)

But now the California Department of Motor Vehicles has understood our plight, and has put up the web site plates.ca.gov so that we can sit at home and use the Web to see which of our license plate ideas is available. It has a simple HTML form interface, shown in Figure 5-2.

Figure 5-2: California License Plate Search

Figure 5-2. California License Plate Search

However, it's so draining to have to plant the mouse in the search box, type "PL8DV8" or whatever other license plate you want, mouse over to the submit button and press it, see the next screen report either "Plate configuration PL8DV8 is unavailable" or "this plate is tentatively acceptable and available," then mouse over to the Back button, press it, and so on for every possibility that occurs to us. Just as a true power user would never use the web interface to whois but would instead insist on the command-line tool, we too would be happiest with a command-line interface to this license plate search form.

5.5.1. The Form

Viewing the source of the search form at http://plates.ca.gov/search/ shows that, omitting some table-formatting codes, it really just consists of:

<form method=POST action="search.php3">
<input type=text size=7 name=plate maxlength=7>
<br>
2. Choose a search option.
<br>
<input type=submit value="Check Plate Availability" name="search">
<br>Use this method to see if your exact configuration is available.
<br>
<input type=submit value="See Existing Similar Plates" name="search">
...
Enter 2 to 7 letters or letters and numbers (number only plates are no
longer offered)
...
</form>

From what we learned earlier about how different kinds of form elements produce different kinds of pairs, we can deduce that filling "PL8DV8" in the type=text box, then pressing the "Check Plate Availability" button will cause two form pairs to be submitted: plate=PL8DV8 and search=Check Plate Availability.

In each case, the first part of the form pair comes from the element's name attribute. With the first pair, we follow the rule for text input elements, and get the value from whatever the user has typed into that box (or whatever is there by default). With either submit button, we follow the rule for type=submit elements and make a form pair from the value attribute (if there is such an attribute and if this is the button that the user is pressing in order to submit the form).

5.5.2. Use formpairs.pl

We can save a local copy of the form's HTML source and edit the form element's action attribute to point to some server where we've set up as a CGI the formpairs.pl program from earlier in this chapter. The form element will then read:

<form method=POST action="http://someserver.int/cgi-bin/formpairs.pl">

If we then open the local copy of the form in our browser, fill in "PL8DV8" in the search box, and hit the first Submit button, formpairs.pl will report:

POST data:
(
  "plate" => "PL8DV8",
  "search" => "Check Plate Availability",
)

Our idea of what form pairs get sent was correct! (The second button would predictably send a "search" value of "See Existing Similar Plates", but that function is outside the scope of our interest.)

5.5.3. Translating This into LWP

Simply put that list of form pairs into a call to $browser->post(url, pairs_arrayref). Specifically, the call will look like this:

my $response = $browser->post(
  'http://plates.ca.gov/search/search.php3',
  [
    'plate'  => $plate,
    'search' => 'Check Plate Availability'
  ],
);

Knowing this, it's simple to write code that takes an argument from the command line and puts it into $plate, performs the above POST request, then checks the response. Example 5-3 is the complete program.

Example 5-3. pl8.pl

#!/usr/bin/perl -w
# pl8.pl -  query California license plate database
 
use strict;
use LWP::UserAgent;
my $plate = $ARGV[0] || die "Plate to search for?\n";
$plate = uc $plate;
$plate =~ tr/O/0/;  # we use zero for letter-oh
die "$plate is invalid.\n"
 unless $plate =~ m/^[A-Z0-9]{2,7}$/
    and $plate !~ m/^\d+$/;  # no all-digit plates
 
my $browser = LWP::UserAgent->new;
my $response = $browser->post(
  'http://plates.ca.gov/search/search.php3',
  [
    'plate'  => $plate,
    'search' => 'Check Plate Availability'
  ],
);
die "Error: ", $response->status_line
 unless $response->is_success;
 
if($response->content =~ m/is unavailable/) {
  print "$plate is already taken.\n";
} elsif($response->content =~ m/and available/) {
  print "$plate is AVAILABLE!\n";
} else {
  print "$plate... Can't make sense of response?!\n";
}
exit;

Saved into pl8.pl, it runs happily from the command line:

% perl pl8.pl
Plate to search for?
% perl pl8.pl 314159
314159 is invalid.
% perl pl8.pl pl8dv8
PL8DV8 is AVAILABLE!
% perl pl8.pl elbarto
ELBART0 is already taken.
% perl pl8.pl ilikepie
ILIKEPIE is invalid.
% perl pl8.pl pieman
PIEMAN is already taken.
% perl pl8.pl pielady
PIELADY is already taken.
% perl pl8.pl pieboy
PIEB0Y is AVAILABLE!
% perl pl8.pl piegirl
PIEGIRL is AVAILABLE!
% perl pl8.pl shazbot
SHAZB0T is already taken.
% perl pl8.pl lwpbot
LWPB0T is AVAILABLE!