Lesson 10 -- Printer Friendly Version
INSTRUCTIONS:
IMPORTANT: This version of your lesson is for saving or printing only. All links and images have been disabled to decrease download time and help you avoid printer difficulties.
Chapter 1
Today, you're going to make three modifications to the addrec.cgi program we created in the last lesson. First, you'll learn how to validate your form data and prevent users from submitting a form with blank fields. Then, you'll learn how to prevent unauthorized users from modifying your database without permission. And finally, you'll learn how to lock your database file to prevent two or more users from making simultaneous modifications.Then, you'll write another program that will teach you how to search your database and extract records that meet a user's criteria.
Next week, you'll learn how to create multiple passwords and how to allow your users to edit and delete records from the database.
-The Form-
Let's begin by making a couple of slight modifications to the 'addrec.html' file we created in the last lesson. First, open the file named 'addrec.html' in Notepad or your favorite text editor. Then, change the <form> tag in the file so it points to a program in your cgi-bin directory named 'addrecv2.cgi.' Also, add a new input device of type 'password.' Assign a name of 'password' to the new device. A password input device looks and works just like a text box on your form, except for one thing: any text typed in a password box will be completely hidden from eavesdroppers, thereby preserving the sanctity of your password.
Here's how the file should look. As always, check the class website for the source code if your e-mail program cannot handle HTML:
<html>
<head>
<title>Add Record </title>
</head>
<body>
<form action="cgi-bin/addrecv2.cgi" method="POST"><P>This form adds a new product to the price list database.</P>
<P>Password:<BR>
<input type="password" name="password"></P><P>Year: <BR>
<input type="text" name="year"></P><P>Winery:<BR>
<input type="text" name="winery"></P><P>Wine:<BR>
<input type="text" name="wine"></P><P>Price:<BR>
<input type="text" name="price"></P><input type="submit">
</form>
</body>
</html>
When you finish, save the file as "addrecv2.html"
Chapter 2
Now, let's write a program that will validate the form data, check the password, and lock the database while it is in use. This program begins much like the 'addrec.cgi' program like lesson 9, so no explanation is needed for the first few lines of code:#!/usr/local/bin/perl require "cgi-lib.pl"; #process incoming form data &ReadParse; #remove '$' character from price field (if one was typed) $in{'price'} =~ s/\$//g;Now, we'll want to perform a test to see if any of the form fields are blank. Check lesson 4 again if you don't remember how 'if' statements work.A blank field will be treated as an empty string. You can represent an empty string with two double quote marks in a row (""). Because we're looking for a string, we have to use the 'eq' operator instead of the numeric '==' operator.
In this example, we're not going to allow any of the form fields to be blank. When you create your program, you might not want to be so strict. If you feel it would be permissible for users to leave certain fields empty, then don't include those fields in your 'if' statement.
if ($in{'password'}eq"" || $in{'year'}eq"" || $in{'winery'}eq"" || $in{'wine'}eq"" || $in{'price'}eq"") {Note: the statement above that starts with 'if' and ends with '$in{'price'}eq"")' should all be on one line in your program. The open curly bracket can go on the next line.We are now inside of the 'if' statement, which means that one or more of the form fields was empty. Let's create a web page that asks the user to try again. First, we'll need to set the content type by running the PrintHeader subroutine from the cgi-lib.pl library:
print &PrintHeader;Then, you'll want to recreate the form. The HTML tags below are virtually identical to those in the 'addrecv2.html' from above--with one exception. Look closely, and you'll notice that I've added a 'value' parameter to each of the input devices. The value just re-inserts the information our user entered on the form when they first submitted it.I did this as a courtesy to our user. Instead of forcing the user to retype everything on the form, all he or she will have to do is supply the missing information. Everything else he or she typed earlier will still be there.
Notice how the FORM tag points right back to this program. We have our user trapped in a loop. Basically, he or she is going to be forced to keep submitting the form data to this program until they finally get it right!
Also, notice how the FORM tag contains the full URL of the CGI program. This form is not coming from the same directory as the rest of our HTML documents. Instead, it is being generated on the fly. I used the complete URL because I want to make absolutely certain that the program responsible for processing the form data is found!
print <<"PrintTag"; <HTML> <HEAD> <TITLE>Error!</TITLE> </HEAD> <BODY BGCOLOR="white" TEXT="black"> <form action="http://username.hypermart.net/cgi- bin/addrecv2.cgi" method="POST"> <P>You forgot to fill in one or more of the fields below. Please try again.</P> <P>Password: <BR> <input type="password" name="password" value="$in{'password'}"></P> <P>Year: <BR> <input type="text" name="year" value="$in{'year'}"> </P> <P>Winery:<BR> <input type="text" name="winery" value="$in{'winery'}"> </P> <P>Wine:<BR> <input type="text" name="wine" value="$in{'wine'}"> </P> <P>Price:<BR> <input type="text" name="price" value="$in{'price'}"> </P> <input type="submit"> </form> </body> </html> PrintTagNow, let's tell our program to shut down so the user can try submitting their corrected form data once again: exit(0); And finally, let's type the closing curly bracket to end the 'if' statement.
}Okay, now let's say the user made it past that little gauntlet. This means that they typed something in each and every one of their form fields.
Chapter 3
Before we allow them to add their form data, we'd better test the password field. We'll hard code the password into our program, which will make it difficult for anyone who hasn't seen our perl source code from guessing the password.If you value the security of your data, you should choose your password carefully. The ideal password would be one with between 8 and 10 characters. Passwords are case- sensitive, so you should mix uppercase and lowercase letters with numbers. The password should never be a word, a name, a birthdate, a portion of your social security number, or any other easy-to-guess value. Here's an example of a great password:
Sf7oPyeQ
To make things easy, I'm going to use the word "test" as our password. This is actually a very poor choice of a password. Because I used a word that appears in most dictionaries, hackers running a cracking utility would be able to guess this password in two or three minutes. Although this simple password will serve us well for this sample program, I would advise you to change it if you plan to use this program with your own database.
The line below will compare the password typed on the form to the word "test." If the password the user typed is not equal to the word "test," then we'll shut down the program (after generating a web page with a message for our unauthorized visitor):
if ($in{'password'} ne "test") { print &PrintHeader; print <<"PrintTag"; <HTML> <HEAD> <TITLE>Error!</TITLE> </HEAD> <BODY BGCOLOR="white" TEXT="black"> <H1>Sorry, Charlie!</H1> <BLOCKQUOTE> I am sorry, but you do not have permission to modify this database. </BLOCKQUOTE> </BODY> </HTML> PrintTag exit(0); }If the user made it past that last test, then we can assume he or she typed the correct password. The last thing we'll want to do is make sure that two or more people are not trying to add data to our file simultaneously. Let me explain why this is important with an example:Let's say that we have a database containing a few names and phone numbers. Suppose the database looks like this, initially:
Joe|123-4567 Kevin|199-9987 Laura|144-2241Now, suppose Susan opens the data base for appending and adds a record. The database now looks like this:Joe|123-4567 Kevin|199-9987 Laura|144-2241 Susan|154-0901But let's say that Alex came along and just happened to open the same database just before Susan finished submitting her addition. Because Susan wasn't yet done, the copy of the database Alex received did not yet include Susan's record. Alex adds his name and phone number to his copy of the database, and it looks like this:Joe|123-4567 Kevin|199-9987 Laura|144-2241 Alex|164-0902When Alex submits his version of the database, it will overwrite Susan's version. As a consequence, Susan's name will vanish forevermore.It is imperative, then, that we never allow two people to write to two different copies of the database at the same moment in time. If we allow that to happen, changes made by one user will be lost forever.
One way to prevent two users from accessing the database at the same time involves the creation of a 'lock file.' A lock file is simply an empty file that your program creates while the database is in use, and deletes when the program is not being used. Your program should always check for the existence of this lock file. If the lock file exists, the database is not available for modification. If the lock file does not exist, the database is available for modification.
Please note that it is permissible to allow multiple users to READ the database simultaneously. Lock files are only needed if the database is going to be modified. There is no requirement to lock the file if it will not be modified. The readfile.cgi program you wrote yesterday and the searchfile.cgi program you will be writing today do not make any changes to the database file. As such, they do not require a file lock.
First, we'll perform a test to see if the file (let's call it "lock.fil") exists. As you learned in an earlier lesson, all you have to do to test for the existence of a file name is type '-e' followed by a space and the name of the file you seek:
if (-e "lock.fil") {If it does exist, let's break the bad news to our user with a web page. Then, we'll shut down the program.print &PrintHeader; print <<"PrintTag"; <HTML> <HEAD> <TITLE>File in use</TITLE> </HEAD> <BODY BGCOLOR="white" TEXT="black"> <H1>Try again!</H1> <BLOCKQUOTE> The database is in use. Please try again later. </BLOCKQUOTE> </BODY> </HTML> PrintTag exit(0); }If you've reached this point, then the database is available. Let's start by opening a new file named "lock.fil" so that future users of the program will be prevented from corrupting our work. Let's call the file "LOCK_FILE."open(LOCK_FILE, ">lock.fil");Next, we'll need to open the database in append mode. While it's open, let's call it "FILE":open(FILE,">>data.txt") || die "Can't find database\n";And now, let's add our form data to the database and close it:print FILE "$in{'year'}|$in{'winery'}|$in{'wine'}|$in{'price'}\n"; close(FILE);Now, let's close the lock file:close(LOCK_FILE);We also need to delete the lock file so that future users can now be given access to the database. Don't forget this line, or you'll never write to your database again!unlink("lock.fil");Let's finish by showing the user the contents of our database. We'll do this by running the 'readfile.cgi' program we wrote in lesson 9.print "Location:http://username.hypermart.net/cgi- bin/readfile.cgi\n\n";That's it! Please note: the two lines above should actually be one line, and should contain the URL of YOUR readfile program.
Chapter 4
-Here's the complete code-#!/usr/local/bin/perl require "cgi-lib.pl"; #process incoming form data &ReadParse; #remove '$' character from price field $in{'price'} =~ s/\$//g; #check for blank form fields if ($in{'password'}eq"" || $in{'year'}eq"" || $in{'winery'}eq"" || $in{'wine'}eq"" || $in{'price'}eq"") { #set content type print &PrintHeader; #re-create form and shut down program print <<"PrintTag"; <HTML> <HEAD> <TITLE>Error!</TITLE> </HEAD> <BODY BGCOLOR="white" TEXT="black"> <form action="http://username.hypermart.net/cgi- bin/addrecv2.cgi" method="POST"> <P>You forgot to fill in one or more of the fields below. Please try again.</P> <P>Password: <BR> <input type="password" name="password" value="$in{'password'}"></P> <P>Year: <BR> <input type="text" name="year" value="$in{'year'}"> </P> <P>Winery:<BR> <input type="text" name="winery" value="$in{'winery'}"> </P> <P>Wine:<BR> <input type="text" name="wine" value="$in{'wine'}"> </P> <P>Price:<BR> <input type="text" name="price" value="$in{'price'}"> </P> <input type="submit"> </form> </body> </html> PrintTag exit(0); } #check for proper password if ($in{'password'} ne "test") { #invalid password--create error message and exit print &PrintHeader; print <<"PrintTag"; <HTML> <HEAD> <TITLE>Error!</TITLE> </HEAD> <BODY BGCOLOR="white" TEXT="black"> <H1>Sorry, Charlie!</H1> <BLOCKQUOTE> I am sorry, but you do not have permission to modify this database. </BLOCKQUOTE> </BODY> </HTML> PrintTag exit(0); } #check for existence of lock file if (-e "lock.fil") { #lock file exists! print message & shut down print &PrintHeader; print <<"PrintTag"; <HTML> <HEAD> <TITLE>File in use</TITLE> </HEAD> <BODY BGCOLOR="white" TEXT="black"> <H1>Try again!</H1> <BLOCKQUOTE> The database is in use. Please try again later. </BLOCKQUOTE> </BODY> </HTML> PrintTag exit(0); } #everything is okay. Create lock file. open(LOCK_FILE, ">lock.fil"); #open, append record, and close database open(FILE,">>data.txt") || die "Can't find database\n"; print FILE "$in{'year'}|$in{'winery'}|$in{'wine'}|$in{'price'}\n"; close(FILE); #close lock file close(LOCK_FILE); #delete lock file unlink("lock.fil"); #print database contents print "Location:http://username.hypermart.net/cgi- bin/readfile.cgi\n\n"; #end of programSave the program as addrecv2.cgi. Then, use your FTP program to upload the addrecv2.html and addrecv2.cgi files in ASCII (text) mode to your server. Place the addrecv2.html file with your other HTML files and place the addrecv2.cgi file in your cgi-bin directory. Use the chmod command to set file permissions on the addrecv2.cgi file as follows:Owner - read, write, and execute privileges.
Group - read and execute privileges.
Other/World - read and execute privileges only.
Now, open the addrecv2.html file in your browser. Try leaving a field or two blank and submit the form to see what happens. Leave a different field blank and submit again. Fill in all the fields but type your password incorrectly and submit again. Then, fill out everything properly and submit one last time.
Chapter 5
Now, let's write a program that will allow users to search the database. First, we'll need to create a form so our users can specify their search criteria. For this example, let's allow our user to search for a type of wine. Be aware that you could create other forms that would allow our user to search other fields.-The Form-
Please create the following file with your text editor:
<html> <head> <title>Search Records</title> </head> <body> <form action="cgi-bin/searchfile.cgi" method="POST"> <P>Please tell us what type of wine you seek:</P> <P>Wine:<BR> <input type="text" name="wine"></P> <input type="submit"> </form> </body> </html>Save this file as 'searchfile.html'-The Program-
Now, let's write a program to process our user's request. To do so, start a new file in your text editor, and follow the instructions below.
Start the program as usual:
#!/usr/local/bin/perl require "cgi-lib.pl";But let's create a variable that we'll use to determine if any records matching our user's criteria were found. We'll set it to "no" initially. If it's still set to "no" at the end of our program, we'll generate an error message.$match="no";Now, let's use cgi-lib.pl subroutines to pull in the form data and set the content type:&ReadParse; print &PrintHeader;Next, let's open the database in read-only mode. Since we're not modifying the database, there will be no need to lock it:open(FILE,"data.txt") || die "Can't find database\n";As we've done many times before, we'll store the contents of the file in an array. We're only reading the file (not modifying it), so we don't need to use file locking:@indata = <FILE>;We're done with the file, so let's close it:close(FILE);Now, let's print the first row of our table with some headings:print <<PrintTag; <html> <head> <title>Search Results</title> </head> <body> <table border=1> <tr> <th>Year</th> <th>Winery</th> <th>Wine</th> <th>Price</th> </tr> PrintTagNext, we'll use the same foreach loop we used in lesson 9 to assign a name to each field in each record in the array:foreach $i (@indata) { chop($i); ($year,$winery,$wine,$price) = split(/\|/,$i);As the loop works through the database, one record at a time, let's perform a little test. If the field we're naming '$wine' contains the word(s) our user entered on the form, we have a match. Let's set the value of the variable named '$match' to 'yes' and then print the record.if ($wine=~/$in{'wine'}/i) { $match="yes"; print "<tr>"; print "<td>$year</td>"; print "<td>$winery</td>"; print "<td>$wine</td>"; print "<td align=\"right\">$price</td>"; print "</tr>\n"; }This is the end of the loop, so let's type the closing curly bracket and then close the table:} print "</table>";Now, let's check the value of the variable named $match. If it's still set to 'no,' then no matches for the user's search terms were found. If that's the case, let's add an error message to the page.if ($match eq "no") { print "<p>Sorry. No matching records were found.</p>"; }Finish up by closing the HTML document:print "</body></html>";That's it!-Here's the complete code-
#!/usr/local/bin/perl require "cgi-lib.pl"; #create a variable to determine whether a match was found $match="no"; #place form data in an associative array &ReadParse; print &PrintHeader; #open the database in read-only mode open(FILE,"data.txt") || die "Can't find database\n"; #store database contents in an array and close file @indata = <FILE>; close(FILE); #start web page, produce first row of table print <<PrintTag; <html> <head> <title>Search Results</title> </head> <body> <table border=1> <tr> <th>Year</th> <th>Winery</th> <th>Wine</th> <th>Price</th> </tr> PrintTag #use a foreach loop to process each record in the array foreach $i (@indata) { #remove hard return character from each record chop($i); #split fields on pipe character #assign a variable name to each of the fields ($year,$winery,$wine,$price) = split(/\|/,$i); #add a new row to the table for each record that #meets the user's search criteria if ($wine=~/$in{'wine'}/i) { #a match was found! $match="yes"; #print table row print "<tr>"; print "<td>$year</td>"; print "<td>$winery</td>"; print "<td>$wine</td>"; print "<td align=\"right\">$price</td>"; print "</tr>\n"; } #close the loop } #close the table print "</table>"; #if no matches found, print an error message if ($match eq "no") { print "<p>Sorry. No matching records were found.</p>"; } #close the HTML document print "</body></html>"; #end of programSave the program as 'searchfile.cgi.' Then, use your FTP program to upload the searchfile.html and searchfile.cgi files in ASCII (text) mode to your server. Place the searchfile.html file with your other HTML files and place the searchfile.cgi file in your cgi-bin directory. Use the chmod command to set file permissions on the searchfile.cgi file as follows:Owner - read, write, and execute privileges.
Group - read and execute privileges.
Other/World - read and execute privileges only.
Now, open the searchfile.html file in your browser. Try searching for 'merlot' or 'MERLOT' or 'Merlot' or 'chablis' or 'cabernet' or 'Cab' and see what happens.
Chapter 6
-Quiz 10-No quiz this time.
-Assignment G-
Create your own searchable database and upload it to the web. I don't want you to create the wine database we created in lessons 9 and 10--I'd like you to create something a bit more original. Build a database that might meet your needs instead.
Requirements:
-Viewing Records-
I want you to be able to view the contents of your database. You must write a program that prints out your database in any format you find attractive.
-Adding Records-
I want you to be able to add a record to your database. You must write a program that will allow you to do this. The program should validate at least one field and reject your submission if you try to enter inappropriate data. Also, the program should lock the file while you're adding a record.
-Searching-
I would like you to be able to search your database. Write a program that will allow you to search for records and will display those that meet your criteria.
No need to send me your database, but please post a message in the Discussion Area if you have any difficulties.
IMPORTANT: This version of your lesson is for saving or printing only. All links and images have been disabled to decrease download time and help you avoid printer difficulties. Do NOT attempt to click any of the links on this page.
Copyright 1999 by ed2go.com. All rights reserved.
No reproduction or redistribution without written
permission.