Beyond the simple tasks discussed in the previous chapter, Perl can also be used to create more sophisticated scripts, such as those that rely on multiple requests, or calls, for data from the user. These scripts can also interact with several files on the server to gather, leave, and change data. One of the best methods of passing data to the Perl script is the use of the HTML form.
In this chapter we will take a quick look at the HTML form. We will then apply it to Perl to develop scripts that create reports; accept letters to the editor and turn them into e-mail; and even allow the user play a game of poker.
To pass data from a Web page to a Perl script over the Internet you must use the Common Gateway Interface (CGI), one of a set of specifications that allow different computer platforms to communicate and use the same data. By far, the heaviest use of the CGI is for the submission of HTML forms. All kinds of information can be gathered and shared through the use of forms, including the guestbook example that was developed in previous chapters.
If you are not familiar with the details of HTML forms, see Chapter 10 for an in-depth discussion.
Originally, Perl was created to produce reports. Reports are used to gather and organize data to help you update your Web site and server. The many ways that reports can be used fall outside this book's focus, but when dealing with Perl it doesn't seem right not to mention at least one example of its ability to produce reports. For example, consider
#!/usr/bin/perl # report.pl format FILES = +++++++++++++++++++++++++++++++++ | @<<<<<<<<<<<<< @<<<<<<<<<< | $file, $size +++++++++++++++++++++++++++++++++ . foreach $file (<*.htm>) { $size=($stat($file))[7]; write FILES; }
This script will get every .htm file in the current directory and print its name and size in the format described by FILES. The size of the field is determined by the size of the space reserved for the variable contents; that is, the number of placeholders, including the @ sign. If the contents are too long, they will be truncated. The example above has a box around the name and size and both fields are left-justified. To change the justification, replace @<<<<<< with @>>>> for right-justified, and @||||| for center-justified.
You may require access restrictions on some of your Web pages. Perl can provide various kinds of login setups to access these pages. The following is a form and associated script that ask for a user's name and password.
<HTML> <TITLE>Login:</TITLE> <BODY> <H1>Login:</H1> <HR> <P> <FORM METHOD="POST" ACTION="http://www.myserver.com/cgi-bin/login.pl"> Please log in with your username and password:<P> Username: <INPUT TYPE="TEXT" NAME="userid" SIZE=3Ø><BR> Password: <INPUT TYPE="PASSWORD" NAME="password" SIZE=3Ø><BR> <P> <INPUT TYPE="SUBMIT" VALUE="Login"> <BR> </BODY> </HTML>
This produces the page shown in Figure 7.1 and the associated Perl script.
Figure 7.1 : The password input page.
#!/usr/bin/perl # password.pl read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs=split(/&/, $buffer); # This is the Name-Value pair splitter.. # Put into $FORM array foreach $pair (@pairs) { ($name,$value)=split(/=/,$pair); $value=~tr/+/ /; $value=~s/%([a-fA-F0-9][a-fA-F0 9])/pack("C",hex($1))/eg; $FORM{$name}=$value; } $found=Ø; open(PASS, "passwd"); while ($line=<PASS>) { ($userid, $password)=split(/:/,$line); if (($userid eq $FORM{userid}) && ($password eq $FORM{password})) { $found=1; } } close(PASS); if ($found==1) { print "Location: http://www.myserver.com/secret.htm\n\n"; } else { print "Content-type: text/html\n\n"; print "<HTML>\n<BODY>\n<TITLE>Login Failed!</TITLE>\n"; print "<H1>Login Failed!</H1>\n<HR>\n<P>\n"; print " Your Userid and Password were invalid. Please click the"; print "<B>Back</B> button and try again.\n"; print "</BODY>\n</HTML>\n"; open(MAIL, "|mail sysadmin\@yourdomain.com"); print MAIL "Subject: Failed Password attempt!\n"; print MAIL "There was a failed password attempt!\n"; print MAIL "\nUsername: $FORM{userid}\n"; print MAIL "\nPassword: $FORM{password}\n"; print MAIL "Attempt from: $ENV{'REMOTE_ADDR'}\n"; close MAIL; }
The "Location" directive, when printed to the browser, tells Perl to load a specified Web page. This saves you the trouble of opening the Web page and printing it to the browser yourself. However, if you can load the Web page this way, anyone else can load it with the browser if he or she knows the URL, making it a security risk, if you want to restrict access to that page.
The only way to limit the number of password tries through the Web Form would be to limit it for everyone. If one person fails three times, and the script shuts down the access, it affects everyone who tries to access the Web page, legitimate or not. The alternative is to mail the System Administrator if the attempt fails, and send some useful information.
If the login fails, the server returns a page as shown in Figure 7.2.
If you want a bit more security, you can store the pages you want to protect from unwanted user access in a less easily accessible directory and then have the program print the page line by line to the browser if it has been authorized to do so. You might want to protect a "members only" section of your site, where different users have paid for the use of those Web pages and their data. Also, the passwords are stored in plain text. WinPerl does not have the crypt() option of its UNIX counterparts. For that reason you cannot encrypt the password unless you create, or roll, your own encryption algorithm. The passwd.pl file, never held in a directory accessible by a Web browser, is just a plain text file with the following format:
userid1:password1 userid2:password2
Not enough emphasis can be placed on developing the healthy practice of logging and reading logs. With Windows NT, the program Event Viewer is ideal for accessing all of your logs, as well as for formatting them to render their data in the most effective way. It is recommended that you regularly review the Security log, System log, and Application log. In the Security log keep a special watch out for the little lock icon, which signifies a failed attempt, or audit. It can inform you whether someone is trying to snoop around in your system. The lock icon is automatically applied by Event Viewer.
If you use passwords with your site, you might consider both posting a short memo to your site regarding a good procedure for password selection for your users to follow, and running a script regularly on your users' password files to check for easily cracked passwords. For more information on creating a good password selection memo and other security concerns try
http://www.yahoo.com/Computers_and_Internet/Security_and_Encryption/
If you are interested in running a password checking script, you might look ahead to the next chapter where there is a basic Perl script that looks for bad passwords.
Many sites benefit from the addition of a little relaxing fun, like a poker game. This program allows you to create a poker game of five-card stud to be played with the user. The script is designed for expansion, so you can also include other game and betting options.
The key to designing any kind of card game is to use associative arrays and to create interesting graphics to be linked to each element. This script does not come with its own card face graphics or scoring system.
#!/usr/bin/perl # poker.pl @orig=("h1","h2","h3","h4","h5","h6","h7","h8","h9","h1Ø","hjack","hqueen","hking"); # Shuffle the deck for ($x=52; $x>Ø; x--) { $w=int(rand($x))+1; push(@deck, @orig[$w]); for ($y=1; $y<=$x; $y++) { if ($y != $w) { @new=@new+$orig[$y]; } else { $y--; } } @orig=@new; undef @new; }
Many Web sites house mailing lists. These provide a way for site users who have mutual interests to share the ideas and information that the site features without having to spend large amounts of time administrating and monitoring the interchange. In addition, mailing lists are easier to create than Usenet newsgroups.
This script will allow you to take new user input and add it to an existing mailing list that your Web site may be hosting.
The HTML form to update the mailing list follows:
<HTML> <TITLE>Subscribe to Mailing List</TITLE> <BODY> <H1>Subscribe to mailing list</H1> <HR> <P> <FORM METHOD="POST" ACTION="http://www.myserver.com/cgi-bin/maillist.pl"> Please enter your E-Mail address:<BR> <INPUT TYPE="TEXT" NAME="email" SIZE=3Ø><BR> <INPUT TYPE="RADIO" name="subscribe" value="1"> Subscribe<BR> <INPUT TYPE="RADIO" name="subscribe" value="Ø"> Unsubscribe<BR> <P> <INPUT TYPE="SUBMIT" VALUE="Subscribe"> <BR> </BODY> </HTML>
This creates a page that is the same as that shown in Figure 7.3.
Figure 7.3 : Subscribing to a mailing list.
And, of course, mailist.pl is as follows:
#!/usr/bin/perl # maillist.pl read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs=split(/&/, $buffer); # This is the Name-Value pair splitter.. # Put into $FORM array foreach $pair (@pairs) { ($name,$value)=split(/=/,$pair); $value=~tr/+/ /; $value=~s/%([a-fA-FØ-9][a-fA-FØ-9])/pack("C",hex($1))/eg; $FORM{$name}=$value; } if ($FORM{subscribe}==1) { open(LIST, ">>mailing.lst"); print LIST "$FORM{email}\n"; close(LIST); } else { open(LIST, "mailing.lst"); undef $/; $file=<LIST>; $/="\n"; close(LIST); $file=~s/$FORM{email}\n//; open(LIST, ">mailing.lst"); print LIST $file; close(LIST); } print "Content-type: text/html\n\n"; print "<HTML>\n<BODY>\n<TITLE>Subscribe Completed!</TITLE>\n"; print "<H1>Subscribe Completed!</H1>\n<HR>\n<P>\n"; print " Your E-Mail address has been successfully "; if ($FORM{subscribe}==1) { print "added to "; } else { print "deleted from "; } print "the mailing list!\n"; print "</BODY>\n</HTML>\n";
When the user is successfully added to the mailing list, the page shown in Figure 7.4 is returned.
Figure 7.4 : Successful addition to an e-mail list.
Getting feedback and response from a site's users is very desirable. This can be done by using a simple form that e-mails the desired user input to a specified e-mail address.
Having the user input e-mailed somewhere is desirable because that way you do not have to give the people who are dealing with this input access to your server. With e-mail they don't even have to be connected to your network, except perhaps via the Internet. For those working with user input, it also eliminates the need to know a lot about how your server is setup and works. They only have to know how to use e-mail.
This next example is a form that accepts letters to the editor for The Imprint, the student newspaper for the University of Waterloo.
The HTML form for the user input looks like this:
<HTML> <HEAD> <TITLE>Letter to the Webmaster!</TITLE> </HEAD> <BODY> <H1>Letter to the Webmaster!</H1><BR> <HR> <P> <B>To the Webmaster:</B> <P> <FORM METHOD=POST ACTION="http://www.myserver.com/cgi-bin/letter.pl"> <Textarea name="body" ROWS=2Ø COLS=5Ø> </Textarea> <P> <B>Sincerely,<B> <P> Name: <input type=text name="name" size=25><BR> <BR> <CENTER><input type=submit value="Send"><input type=reset></CENTER> <P> </FORM> </HTML>
This will produce an output to a browser as shown in Figure 7.5 and Figure 7.6. The script called by the form follows:
Figure 7.5 : Form to submit user input to e-mail (first screen) .
Figure 7.6 : Form to submit user input to e-mail (second screen) .
#!/usr/bin/perl # letter.pl read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs=split(/&/, $buffer); # This is the Name-Value pair splitter.. # Put into $FORM array foreach $pair (@pairs) { ($name,$value)=split(/=/,$pair); $value=~tr/+/ /; $value=~s/%([a-fA-FØ-9][a-fA-FØ-9])/pack("C",hex($1))/eg; $FORM{$name}=$value; } $good="<HTML>\n<TITLE>Thanks!</TITLE>\n<h2>Thank you!</h2><P>Thank you for your submission. While we cannot guarantee that we will print every letter we receive, look for yours in the next edition of <B>Imprint</B>!\n</HTML>\n"; print "Content-type: text/html\n\n"; open(MAIL, "|mail webmaster\@www.myserver.com"); print MAIL "Subject: Letter to the Webmaster from $FORM{name}\n"; print MAIL "\nThe following is a letter to the Webmaster submitted from the Web Page.\n"; print MAIL "To the Webmaster:\n\n"; print MAIL "$FORM{body}\n\n"; print MAIL "$FORM{name}\n"; close MAIL; print $good;
where the users will get a response in the form of a Web page that confirms what they have sent. This response will look like Figure 7.7.
Figure 7.7 : E-mail submission response.
Database manipulation, or DBM routines, is not supported by WinPerl. The best way to do any type of database manipulation is by using the split and join operators to separate plain text fields with delimiters, as we did with the guestbook.
One of the growing uses for the CGI is to put an animated logo for the Web site on the home page. Although the language of choice for this is Java script, Perl can also perform this procedure. The following animated logo makes use of a series of simple JPEG images to create a blinking eye.
<HTML> <BODY> <TITLE>Animated Logo</TITLE> <CENTER> <IMG SRC="http://www.myserver.com/cgi-bin/anim.pl"> <h1> Welcome to the Hand of Vision Page </h1> </CENTER> </HTML> </BODY>
This produces a Web page that looks like Figure 7.8. The eye blinks when it runs in real time on a browser.
Figure 7.8 : A Web page with an animated logo.
#!/usr/bin/perl # anim.pl @files = ("logo-1.gif","logo-2.gif","logo-3.gif","logo-4.gif","logo-5.gif","logo-6.gi f","logo-7.gif"); print "Content-Type: multipart/x-mixed-replace;boundary=myboundary\n\n"; print "--myboundary\n"; foreach $file (@files) { print "Content-Type: image/gif\n\n"; open(LOGO,"$file"); print <LOGO>; close(LOGO); print "\n--myboundary\n"; sleep(1); }
This script will print each of the GIF files in @files in sequence.
The final sleep() command may be modified or removed as required to make the animation more effective. The sleep operator paces how fast or slow your images are presented. For a smoother animation, change this value.
The scripts covered in this chapter are only a smattering of what you can do with Perl to enhance your Web pages. The only limit to how you can use Perl is your imagination, and even then there are always new Perl libraries and archives coming online all the time. Check for these new resources regularly.
The past two chapters have dealt with Perl programs explicitly for your Web pages, but Perl can also be used to help you on your server, which subject is explored in the next chapter. Also, you might now have some questions concerning the CGI and related protocols; these are covered in the final chapters in this book.