INT420 Labs - Seneca - School of Information & Communications

advertisement
Perl introduction
Objectives:
This exercise will introduce you to Perl scripting. Specifically you will learn how to:
1. Specify the Perl interpreter- first line of the script
2. Print a simple statement to the terminal
3. Use the \n (newline) character with the print statement
Instructions:
• Using your favourite editor, create a script called heIlo.pI with following code in it:
#!/usr/bin/perl
print "Hello Perl! \n";
1. Save the file and exit your editor.
2. Make sure that helIo.pl has execute permissions.
3. Execute the script (by typing ./hello.p| on the command line) and observe the output.
Is it what you were expecting?
4. Edit your script and remove the \n from the printing line.
5. Execute the script again and notice the difference.
6. Experiment how adding more \n characters will affect the output.
Note: the path to the Perl interpreter may vary between systems. You can check it on the command line by
using the which utility.
Additional exercise:
Perl allows defining custom quotes that can be used instead double quotes. This feature comes very handy when
printing strings with double quotes embedded. For example, if you would like to print the following:
<input type="text" name="l_name" />
you would have to use the following Perl line:
print "<input type=\"text\" name=\"l_name\"";
Custom double quotes are defined using the qq operator immediately followed by the custom quote. Please note
that if the custom quote appears anywhere in the string it must be escaped (for example using the backslash). A
popular choice for the custom quote is the ~ character. The above print statement rewritten using custom quotes
would look like this:
print qq~<input type="text" name="|_name"~;
Practice the custom quoting as much as you can. Use of custom quotes is recommended for most CGI print
statements.
1
Perl scalar variables
Objectives:
This exercise will introduce you to scalar variables. Specifically you will learn how to:
1. Use Perl’s warning messages (-w option)
2. Use scalar variables
3. Perform simple math
4. Assign values to scalars and print their values
5. Use of a built in function - localtime
Instructions:
• Using your favourite editor create a script called scaIars.pl with following code in it:
#!/usr/bin/perl
-w
$a = 5;
$b = 3;
$c = $a + $b;
print "5 + 3 = $c\n";
print "3 / 4.5 = ", $b/4.5, "\n";
$a++;
print "5 + 1 = $a\n\n";
$name = "Bob“;
print "Hi! This is $name.\n";
$name = "Mary";
print "Hellol This is $name.\n";
$time = localtime;
print "Here is the date/time stamp: $time\n";
• Set proper permissions for the script and execute it.
• To explore the use of the -w option add following line anywhere in your script:
$not_used = "This variable will never be used";
• After such a change (which is not a syntax error) the script will execute without problems; however the Perl
interpreter will warn you about this unused variable. This is just one example of a run-time warning. As your
scripts become more complex you will see more of them. Make it a habit to include the -w option in all of your
scripts. It will help you out with writing cleaner code and decrease debugging time.
2
Perl arrays and lists
Objectives:
This exercise will introduce Perl arrays. Specifically you will learn how to:
1. Declare arrays
2. Access array elements with the subscript
3. Get input from the user (standard input)
4. Use the chomp function to trim trailing newline character
lnstructions;
1. Using your favourite editor create a script called weekdays.pl with following code in it:
#!/usr/bin/perl -w
@days = qw(Monday Tuesday Wednesday Thursday Friday Saturday Sunday);
print "Enter the number of the day of the week.";
$num = <STDIN>;
chomp $num;
print "$days[$num] is day ($num +1) of the week!";
2. Set proper permissions for the script and execute it.
3. Comment out the line with the chomp function and execute the script again. What is the difference?
For more information on the chomp function look up the perlfunc manual page.
4. See what happens when you enter a negative number or letters for the week day.
5. As you have probably noticed the last line does not add the ($num + 1) expression. Change this line to
perform the addition. Hint: you have to use the comma (,) operator and separate the large string into three
sections. Leave the calculation unquoted.
6. Make changes to the script that will show Monday when 1 is entered (and not Tuesday)...
3
Perl arrays continued
Objectives:
This exercise continues with Perl lists and arrays. Specifically you will learn how to:
1. Use lists and list ranges
2. Access a range of array elements (range of subscripts)
3. Copy arrays
4. Use the reverse function
Instructions:
1. Using your favourite editor create the following Perl script:
#!/usr/bin/perl -w
# This will print a sequence of numbers
print "Here is a list of increasing numbers: ", (1..6), "\n";
# This won’t work - range has to be specified in increasing fashion
print "Here is a list of decreasing numbers:
(11..1), "\n";
# We can achieve "reverse" action by using the reverse function
print "Here is a reversed list of numbers: ", reverse(1..11), "\n";
# Printing a range of letters can be done similarly to numbers
# This includes backward printing
print "How about printing a range of letters? ", ('c'..'p'), "\n";
@months = qw/January February March April May June July August September October
November December/;
# Here are some examples involving lists
print "The first month's name is: $months[O]\n";
print "My favourite months are: @months[3,5,9..10]\n";
@summer = @months[5..7];
print "@summer are summer months\n";
2. Set proper permissions for the script and execute it.
3. Try to alter the script by changing array subscripts and/or specifying subscript out of range.
4
Perl hashes
Objectives:
This exercise will introduce Perl hashes. Specifically you will learn how to:
1. Declare hashes
2. Access hash elements
Instruct ions:
1. Create the following Perl script:
#!/usr/bin/perl -w
%courses = (
"int420", "lnternet Il",
"ndd430", "Network design and diagnostics",
"ops440“, "UNIX administration"
);
%grades
A
B
C
F
= (
=> “80%+”,
=> "70%-79%",
=> "60%-69%",
=> "No comment!" );
print "My favourite course is: $courses{int420}\n";
print "My current Unix course is: $courses{ops440}\n";
print "This is a list of my current courses:
keys %courses, "\n";
print "Since I want to get into CO-OP my GPA has to be at least”,
$grades{B},
"\n";
2. Execute the script and observe the results.
3. Alter the script and try to access hash elements that do not exist. What happens?
4. Change the script again and determine lf the hash keys are case sensitive.
5. By adding new key/value pairs in the hash declaration determine whether you can you use non-alphanumeric
key names.
5
Perl hashes continued
Objectives:
This exercise will continue the topic of Perl hashes. Also for loops will be introduced.
Specifically you will learn how to:
1. Access hash keys and elements within a loop
2. Use the $_ variable
3. Add and remove hash elements
4. Use the sort function
Instruct ions:
1. Create the following Perl script:
#!/usr/bin/perl -w
%ingredients =(
peppers => "8",
rice => "3 cups", .
vegetables => "10 oz",
corn => "1 cup",
"green onions" => "1/4 cup",
salsa =>"13/4 cups",
cheese => "1 cup"
);
print "Here are the ingredients for stuffed peppers :\n";
foreach $item (keys %ingredients)
{
print $item, ": ", $ingredients{$item}, "\n";
}
print "Press Enter to continue...\n";
<STDlN>;
print "Since I like my peppers cheesy there is more cheese required!\n";
$ingredients{cheese} = "1 1/2 cups";
print "Here are the ingredients for cheesy stuffed peppers - this time sorted :\n";
foreach $item (sort(keys %ingredients))
{
print $item, ": ", $ingredients{$item}, "\n";
}
print "Press Enter to continue...\n";
<STDIN>;
print "Just to make it more spicy how about some chili peppers’?\n";
$ingredients{chili} = "1/2 cup";
print "Here is my complete set of ingredients :\n";
foreach (keys %ingredients)
{
print $_, ": ", $ingredients{$_}, "\n";
}
6
2. Execute the script and observe the results. Notice how <STDIN> is used here.
3. Change the script and remove some elements from the ingredient list. Print the modified hash afterwards.
Hint- you will need to use the delete function. Consult the perlfunc manual if necessary.
4. Do you understand what the $_ variable is and how is it used in loops?
5. Do you know when to use the % and when to use the $ characters with hashes?
7
Perl – Introduction to CGI
Objectives:
This exercise will introduce CGI environment variables. Specifically you will learn how to:
1. Create an HTTP header from a script
2. Access and use environment variables and the %ENV hash
3. Use Perl to produce an XHTML-compliant page
4. Use the command line as a debugging tool
5. Analyze Apache logs using Perl
Instructions:
1. Create a new Perl script called variables.cgi in your cgi-bin directory:
#!/usr/bin/perl -w
# print HTTP header
print "Content-Type:
text/html; charset=ISO-8859-1\n\n" ;
# print starting XHTML tags
print qq~
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//w3c//DTD XHTML XHTML 1.1/EN"
"http://w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title>Hello CGI!</title></head>
<body>
~;
# Print page body
print "<p>\n";
print "You are using the following browser: $ENV{HTTP_USER_AGENT} <br/>\n";
print "Your IP address is $ENV{REMOTE_ADDR} <br
/>\n";
print "This server is using $ENV{SERVER_SOFTwARE} and running on port
$ENV{SERVER_PORT} <br />\n";
print "The server‘s document root is $ENV{DOCUMENT_ROOT} <br />\n";
print "</p>\n";
# print ending HTML tags
print “</body></html>\n";
2. Change the permissions so others can execute the script.
3. Execute the script on the command line - notice the result. You will get warnings about uninitialized
variables, as variables such as $ENV{SERVER_SOFTWARE} are only available when executing the script in
the browser. Read all error and warning messages carefully in case some real problems occur.
4. Make sure that your server is configured for CGI and is running.
5. Execute the script through the browser. Hint: you have to type the URL similar to:
http://zenit.senecac.on.ca:8999/cgi-bin/variabIes.cgi. Go back to the editor and comment the line which
produces the HTTP header. Refresh your browser. Do you know why you got an error?
6. Execute the script again in the browser (reload the script). What result did you get?
7. Examine last entries from your access_log and error_log. Suggestion: use the tail utility
8. Fix the HTML markup produced by the script so it validates correctly as XHTML.
8
Perl - file access introduction
Objectives:
This exercise will introduce file access. Specifically you will learn how to:
1. Open files for reading
2. Use file handles to access file content within a while loop
3. Use the split function to extract file fields
4. Utilize hashes for file content storage and quick retrieval
5. Print information from files to the browser
6. Use the strict keyword
Instructions:
1. Using a text editor create a sample data file which will contain names and phone numbers for some of your
friends.
The format will be as following:
Bob 416-123-4562
Alice 905-555-9987
Make sure that there are at least 5 records (lines) and the only spaces are those separating the names from their
phone numbers. Your data file should not contain blank lines, as these will produce unusual errors in your
script.
2. In your cgi-bin directory create a script called friends.cgi.
#!/usr/bin/perl -w
use strict;
my %friends;
my $name;
my $phone;
# Path to the log file may need to be adjusted
open FILE; "...path to file goes here!..." or die ("Cannot open file!\n");
whi1e(<FILE>)
{
chomp;
($name, $phone} = sp1it(" ", $_);
$friends{$name}=$phone;
}
foreach (keys %friends)
{
print “Name: “, $_,”\n”;
print "Phone: ", $friends{$_}, "\n\n";
}
3. Execute the script on the command line. Fix typing errors if necessary.
4. Comment out one of the lines starting with "my" and execute the script again. What kind of error did you get?
Do you know why?
9
5. Now comment out the second line of the script (use strict;). Execute the script again. What is the purpose of
the "strict" approach? If you are not sure consult Perl man pages. Remove both comments once you draw your
conclusions so your script runs again with the strict option on.
6. Edit the script and prepare it for browser access - add the "Content-type..." statement for plain text content.
Execute the script in the browser and see the results. Do you know how to specify the URL to your script? Is
your script in the correct location? Consult the Apache manual, your httpd.conf settings and your error_Iog in
case of problems.
7. Once the script has successfully executed in the browser change it by adding XHTML tags to conform to the
standard. Use the W3C validator to verify your markup. Use a table to display the names and phone numbers.
8. Choose a different field separator for the data file. Make sure it is not present in any of the names or phone
numbers. Change the separator in the data tile for every line. Execute the script again. Were you expecting this
output? Do you know why the output has changed?
9. Change your script so it recognizes correctly the new field separator. Verify your work in the browser.
10. Add another column to your data file, for example, an email address. Change your script so the new
information is displayed as well. Hint- you will need to add another hash (assoc. array) to your script to store
the extra information.
10
Perl - reading from forms
Objectives:
This exercise will introduce interactive CGI.
Specifically you will learn how to:
1. Create HTML forms which submit information to CGI
2. Extract form data using Perl
3. Display form data from a CGI in the browser
Instructions:
1. Create an HTML form on your account similar to the following, using the GET
method:
Make sure that each appropriate form element is named: person, sport, course, gpa, send and reset. Ensure that
the form action points to a script called aboutme.cgi in your script-aliased directory. You will create this script
next.
2. Create the aboutme.cgi script based on the code below:
#!/usr/bin/perl -w
# print a standard 200-level HTTP header
print "Content-Type : text/html\n\n" ;
# the following code will
# get the data from the form, and store it in a hash named %form
# get data from environment variable
$qstring = $ENV{'QUERY_STRING'};
# break data up on ampersands, and store in array
@pairs = split(/&/, $qstring);
# start a loop to process form data
foreach (@pair$) {
# split field name and value on '=', store in 2 scalar variables
($key, $value) = split(/=/);
11
# translate ‘+' signs back to spaces
$value =~ tr/+/ /;
# translate special characters
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack(“C", hex($1))/eg;
# store data in hash
$form{$key} = $value;
}
# now the data is stored in the hash %form
#send output to browser as HTML
print “<html><head><title>Student Survey</title></head>\n";
print "<body>\n“;
# display form data
&displayInfo();
print "</body></html>\n";
# This subroutine will display information received from a form
sub displaylnfo {
print "Full Name:",
$form{"person"}, "<br>“;
print "Favourite Sport:",
$form{"sport"}, "<br>“;
print "Favourite Seneca Course:",
$form{"course"}, "<br>“;
print "GPA:",
$form{"gpa"}, "<br>“;
}
3. Set appropriate permissions for the script and test it with your form. lf you did everything correctly you
should see your form entries displayed back from the script.
4. Submit various information to your script, including embedded HTML tags, spaces and special UNIX shell
characters. You will learn shortly that this is a potential security problem and learn ways to deal with them.
5. As usual, make sure that all output produced by your script is valid XHTML.
Exercise:
In a real-world case hardcoded HTML forms are a rarity. Usually forms are produced by scripts. The task of
converting static forms to CGI is very simple - all you have to do is to print the form from your script. While
adding a separate print statement in front of each HTML line and quoting each line may be the instinctive
direction to take, Perl provides us with a nice shortcut- alternate quoting. Alternate quoting allows us to define
our own quotes and preserves formatting.
Have a look at the following example:
Suppose you would like to print the following HTML from your script:
<form action="/cgi-bin/aboutme.cgi" method="get">
<input type="submit" name="send" value="send">
</form>
With standard quoting you would achieve this through the following Perl code:
print "<form action=\"/cgi-bin/aboutme.cgi\" method=\"get\">\n";
print "\t<input type =\"submit\" name=\“send\" vaIue=\"send\">\n";
print "</form>\n";
12
Note, that since a double quote (") is a string delimiter, whenever you wish to print an actual quote it must be
quoted with a backslash (\). As it is inconvenient with simple forms, it is a big problem for complex forms
because it is a source of hard-to-trace syntax errors.
Alternate quoting allows us to define our own quotes. This way the double quote can be just a simple character.
Here is an improved version of the code above:
print qq~
<form action="/cgi-bin/aboutme.cgi" method="get“>
<input type="submit" name="send" value="send">
</form>
Hopefully you can see the advantage of the latter approach. Please note that in this case we have defined the ~
character as our alternate quote, but you are free to define one which is convenient at any given time.
We will now change the aboutme.cgi script to be used for both producing the form and processing it.
1. Using alternate quoting place your form code (including its formatting) in the aboutme.cgi script as a
subroutine.
2. Make sure that the form uses the POST method.
3. Add an if statement to your script which will check the HTTP method. lf the method is GET - the script will
display the form, but lf the form is POST, the script will process the form.
4. Test your improved script thoroughly. Make sure that you understand it completely.
You will use this approach for the entire semester to build many other scripts, including your project - it is very
important, so learn it properly.
13
Perl - Using a MySQL Database
Objectives:
This exercise will introduce interactive CGI connecting to a MySQL database.
Specifically you will learn how to:
1. Connect to a MySQL database from your CGI script
2. Insert data into a MySQL table .
3. Extract form data using Perl
Instructions:
In order to connect to your MySQL database you need the correct connection information:
User name: Same as your zenit account
Database name: Same as your zenit account
Password: Same as your original zenit account password
Hostname: db-mysqlzenit .
Before we can use your database you will need to create a table called friends.
1. Use the mysql command to connect to your database. Use the example below substituting your information:
$mysql -h db-mysql.zenit -u int420_121a25 -p int420_121a25
When prompted for your password enter your original zenit password.
2. At the mysql> prompt enter the following command to create a table called friends.
create table friends (
id int auto_increment not null,
lname varchar(25) not null,
fname varchar(25) not null,
phone varchar(l0),
email varchar(60),
primary key (id)
);
**In this table the id field is your primary key and is an automatically incrementing number.
3. Create the friends-mysql.cgi script based upon the code below:
#!/usr/bin/perl -w
#Use the DBI (database interface) module
use DBI;
#Declare variables with MySQL connection data
$db=” “;
$user=” “;
$passwd=” ";
Enter in your connection info
$host=” “;
$connectionInfo="dbi:mysql:$db;$host";
#Print HTTP header
print "Content-type:text/html\n\n";
14
#If first time script run display form
if ($ENV {REQUEST_METHOD} eq "GET")
{
&disp1ayform();
exit;
}
#Else process form and insert into DB
else
{
&parseform();
&insertfriend();
exit;
}
#Standard form parsing using POST method
sub parseform
{
Place the standard code for parsing form data here.
}
sub insertfriend
{
#Form SQL insert statement
$insert = qq~insert friends (lname, {name, phone, email)
values(‘$form{lname}',’$form{fname}','Sform {phone}','$form{email}')~;
#Connect to MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#Prepare MySQL statement and create Statement Handler $sth
$sth=$dbh->prepare($insert);
#Execute Statement Handler and test for success
if ($sth->execute())
{
&displaysuccess;
}
else
{
&displayfail;
}
#Disc0nnect database ,
$dbh->disc0nnect();
}
sub displaysuccess
{
print qq~<html>\n
<head> H
<title>My Friends</title>
</head>
<body>
<h2>Record Added</h2>
</body>
</html>
~;
}
sub displayfail
{
print qq~<html>\n
<head>
<title>My Friends</title>
</head>
<body>
<h2>Record NOT Added</h2>
</body>
15
</html>
~;
}
sub displayform
{
print qq~
<htm1>
<head>
<title>My Friends</title>
</head>
<body>
<form action="friends-mysql.cgi" method=post>
<center>
<h2>My Friends</h2>
Last Name: <input type=text name=lname>
<br>
First Name: <input type=text name=fname>
<br>
Phone Number: <input type=text name=phone> (10 digits only please)
<br>
E-mail: <input type=text name=email>
<br>
<input type=submit value="Insert" name=Insert>
<input type=reset value=Reset name=reset>
</form>
</body>
</html>
~;
}
4. Fix all errors by first checking your error_log and retrying your script. Test that the records were successfully
entered into your table by issuing the following command:
mysql>select * from friends;
5. These are vital skills to practice for your project.
16
Perl - Using a MySQL Database Continued
Objectives:
This exercise will continue interactive CGI connecting to a MySQL database.
Specifically you will learn how to:
1. Connect to a MySQL database from your CGI script
2. Select data from a MySQL table and display in a HTML table
3. Use subroutines
4. Extract form data using Perl
Instructions:
For this lab we will edit your previous lab and add additional functionality. Copy your previous lab friendsmysql.cgi to friends2-mysql.cgi and then edit the new file.
We will continue to use the table called friends that you created in the previous lab.
1. Edit the friends2-mysql.cgi script by adding the following subroutine to show the contents of the MySQL
table in the browser.
sub showfriends
{
#Start HTML table
print qq~<html>
<head>
<Title>My Friends</Title>
</head>
<body>
<tab1e border=l>
<tr>
<th>ID</th><th>Last Name</th><th>First Name</th>
<th>Phone Number</th><th>E-Mail</th>
</tr>~;
#Form SQL select statement
$select = qq~select id, lname, fname, phone, email from
friends~;
#Connect to MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#Prepare MySQL statement and create Statement Handler $sth
$sth=$dbh->prepare($select);
#Execute select statement
$sth->execute();
#Loop through each record selected and print in html table
while (@row=$sth->fetchrow_array())
{
print qq~<tr>
<td>$row[0]</td><td>$row[1]</td><td>$row[2]</td>
<td>$row[3]</td><td>$row[4]</td>
17
</tr>~;
}
#Close HTML table
print qq~</table>
</body>
</html>~; _
$dbh->disconnect();
}
2. Modify your script so that the subroutine is called when the script is first accessed and the form displayed,
and also after a new friend is added.
3. Fix all errors by first checking your error_log and retrying your script.
4. These are vital skills to practice for your project.
18
Perl - Returning a Partially Completed Form
Objectives:
This exercise will get you to check from input for complete required fields and return incomplete forms with an
error message for completion.
Specifically you will learn how to:
l. Check form fields for data .
2. Return error messages for each missing field
3. Resend the form with data already entered
4. Use subroutines
5. Extract form data using Perl
Instructions:
For this lab you will edit your previous lab and add additional functionality. Copy your previous lab friends2mysql.cgi to friends3-mysql.cgi and then edit the new file. We will continue to use the table called friends that
you created in the previous lab.
1. Edit the friends3-mysql.cgi script by adding the following subroutine to verify that data has been entered in
all required fields.
** This subroutine creates a hash called %errors that has the same list of keys as the %form hash. The values
of the %errors hash are the error messages to be displayed.
sub verifyform
{
$missing = 0;
###Initialize the $missing flag to 0
foreach (keys %form)
{
if ($form{$_} eq "")
{
$errormsg = "Please enter data for required field";
##If there is missing data set $missing flag to l
$missing = l;
}
else
{
$errormsg = "";
}
###Load the %errors hash with error message
$errors{$_}=$errormsg;
}
if ($missing == 1) ##If $missing flag is 1 resend form and exit
{
&displayform;
exit;
}
}
2. Modify your script so that the subroutine is called after the form data is read but before data is inserted into
the table.
19
3. Fix all errors by first checking your error_log and retrying your script.
4. You should see the form returned if any data was missing from the form.
5. Now we need to make the changes to the script to allow the previously entered data and error messages to be
displayed with the form.
6. Edit the displayform subroutine. When each form element is printed include a value attribute that contains
previously submitted data and after each element any error message for that element.
For example:
Last Name: <input type=text name=lname>
becomes:
Last Name: <input type=text name=lname value="$form{lname}"> $errors {lname}
7. Fix all errors by first checking your error_log and retrying your script.
8. You should see the form returned if any data was missing from the form. This time, however, you should see
previously entered data or an error message for each field.
9. These are vital skills to practice for your project.
20
CGI - Form data verification
Objectives:
This exercise will introduce form input verification.
Specifically you will learn how to:
1. Create regular expressions representing anticipated user input
2. Use regular expressions to verify user input ·
3. Use dynamic error messages
Introduction:
In order to have usable input from forms, user entries must be verified and mandatory fields must be enforced
before an online transaction is completed. You can hardly expect to accept an order from a customer without a
proper shipping address. Input checking is usually done through regular expressions, which map out anticipated
input. In most cases user input can be easily defined; for example, a phone number could have the following
format: 999-999-9999
For those unfamiliar with regular expression here is a very basic tutorial:
Regular expressions are built using literals, symbols and quantifiers. Perl uses the extended set of regular
expressions similar to egrep. Please note that shell expansion characters, although similar to regular expression,
have a different purpose and work differently.
Literals for regular expressions include all alphanumeric characters as well as some other characters in the
ASCII table. For example, a regular expressions to match the name Bob would be /Bob/. The forward slashes
delimit the regular expression, but are not part of the match. Of course such regular expressions are very
limited, since they can only match one string.
For our purposes we are rather interested in a range of matches. This is achieved through a combination of
literals, symbols and quantifiers.
Perl regular expression quantifiers include the following symbols:
? - represents zero or one match
+ - represents one of more matches
{3}- represents a defined number of matches (in this case 3)
{3,6}- represents an inclusive range of matches (in this case between 3 and 6 inclusive)
Please note that Perl has also the asterisk (*) quantifier, but its use for our purposes is discouraged, since it
matches anything. Remember that we want to match specific strings, not just anything. Also note that the +
quantifier should be avoided whenever possible in favour of {number of matches} expressions.
To complete the basic overview here is a very useful expression:
[A-Z] - represents one character (in this case any uppercase letter)
This type of expression can have forms such as: [0-9], [A-Za-z] or [abc386] etc. In all cases this
matches only one character that is enclosed within the square brackets.
Now you can start building more advanced regular expressions, for example:
21
[0-9]{1,3} - will match 1 to 3 digits, for example someone's age.
Note that this only checks the pattern and not the value. If you want to check the value you
have to make a numerical comparison.
[A-Z][a-z]+ - will match any alpha string starting with uppercase, for example customer's first name
coIou?r - will match the word colour or color, which will cover both Canadian and US spelling
Please note how the quantifiers work always with the regular expressions directly before them, and by
themselves the quantifiers do not match anything. This is how regular expressions differ from shell expansion
characters.
You will need to learn how to use regular expressions for this course, including matching special characters and
grouping.
For more information on this topic check the appropriate Perl manual (perlre) and the Internet.
Regular expression matching in Perl
In order to verify form data against regular expressions you will have to create an appropriate expression for
each form field that is required and/or requires a specific format. Once you have this done each form field can
be checked through a simple if statement, such as this:
if ($FORM{element} =~ /[A-Z][a-z]+/)
{
# input is correct
else
{
# input is incorrect
# You will need to perform some corrective actions here
# such as display an error message, display the form again etc...
}
Each of your form fields would have such an if statement. For large forms and cleaner solutions form checking
can be done with a single if statement within a loop.
This approach is introduced by the following exercise.
Exercise:
Create the following CGI on your account. Test it thoroughly and make sure that you understand how it works ask questions as needed.
Make sure to fill in the missing pieces (outlined as comments), as your script will not work correctly
without them.
Once you fully understand the concept, incorporate the ideas from this lab into your project. Start with simple
regular expressions and gradually make them more sophisticated.
#!/usr/bin/perl -w
#############################################################################
# Program: Form Processing and verification
#
# Description: This CGI will verify user input against predefined criteria #
#############################################################################
### Options ###
###############
22
use strict;
### Variable Declarations ###
#############################
my %ERRORS; # Holds errors for required field check
my %FORM;
# will hold all form data
# Holds all form fields
my %FIELDS = (
"lname" => "Last Name",
"phone" => "Phone Number",
"fname“ => "First Name"
);
# holds regular expressions matching desired input for each form field
# Note that these are just samples and you should perfect them.
# For example, consider last names such as O'Malley, or Smith-Johnson
my %PATTERNS = (
"fname" => '[A-Z][a-z]{2,50}',
"phone" => ‘\d{3}-\d{3}-\d{4}‘,
"lname" => ‘[A-Z][A-Za-z]{2,60}'
);
# determines the sequence in which form fields are printed/processed
my @formSequence = ("fname", "lname", "phone");
### HTTP HEADER ###
#################
print "Content-Type: text/html;charset=ISO-8859-1\n\n";
# XHTML PAGE HEADING #
######################
&startXHTML;
if ($ENV{REQUEST_METHOD} eq "POST")
{
&readFormData;
if (&checkRequiredFields)
{
print "Form data validated successfully!";
}
else
{
####################
#
# Print all errors in the %ERROR array
# Hint: use a foreach loop, looping in the order specified by @formSequence
# Print each error on a separate line
#
####################
&printForm;
}
}
else
{
&printForm;
}
print qq ~</body></html>\n~;
### end of main program ###
###########################
### subroutines ###
# this subroutine will check user input
# and return 1 if all input is correct, otherwise return 0
sub checkRequiredFields
{
23
my $success = 1;
foreach (keys (%FIELDS))
{
if ($FORM{$_} !~ $PATTERNS{$_})
{
$ERRORS{$_} = “Error: $FIELDS{$_} is missing or incorrect format\n“;
$success = 0;
}
}
return $success;
}
# this sub-routine produces a form with optional error messages for each field
sub printForm
{
print qq~ <form method="post" action="">\n~;
##########
#
# Create form fields here
# Make sure that field names match names specified in the %FIELDS array.
# Change the formSequence array at the top to reflect the order of fields you used.
# The sequence is important - you want the errors to show in the same order as the
# form fields
# It is recommended that you do not use a loop to create the form
# Creating form fields one at a time is simpler to implement
#
##########
print qq~ <input type="submit" name="save" /><br />\n~;
print qq~ </form>\n~;
}
# This sub-routine will generate XHTML-compliant page headers
sub startXHTML
{
print qq~
#########
#
# Enter a valid XHTML header here
# Your script‘s output should validate with the W3C validator
#
#########
<head>
<title>Taint checking</title>
</head>
<body>
~;
}
sub readFormData
{
# Read and decode form data
my $input = <>;
my @pairs = split(/&/, $input);
my ($name, $value);
foreach (@pairs)
{
($name, $value) = split(/=/, $_);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C“, hex($1))/eg;
24
$FORM{$name} = $value;
}
}
25
Tainted Variables - Exercise 1
Objectives:
The purpose of this lab is to introduce you to the dangers of accepting input from users when you write CGI
scripts. Before doing this lab, make sure your server is running and that it is configured to run CGI scripts.
Make sure the files have correct permissions for access by clients of your server.
Danger! This script contains a tainted variable. Do not leave it unprotected on any web site after you are
finished this lab! You would be creating a serious security hole in the system.
Part 1 - Instructions
1. Create the following web page in your Document Root. Call it taint.htmI.
<html>
<head><title>Tainted Mail</title>
<head>
<body>
<form action="/cgi-bin/taint.pl" method="GET">
What is your email address? <input type="text" name="address" size=40>
<input type="submit" value="send address">
</form>
</body>
</html>
2. Create the following script in your cgi-bin directory. Name it taint.pl.
#!/usr/bin/perl
@mail_to = sp1it(/=/, $ENV{QUERY_STRING});
$mai1_to[1] =~ s/%([a-fA-F0-9][a-fAF0-9])/pack("C", hex($1))/eg;
$mai1_to[1] =~ tr/+/ /;
`mail -s "message" $mai1_to[1] < message.txt`;
print "Content-type:text/html\n\n";
print
print
print
print
“&lthead>title>sender>/tit1e></head>\n";
"<body><p>\n";
"The sender is $mail_to[1]\n";
"</p></body>“;
3. Create a text message of your choice called message.txt which will be emailed to a user. It should be in your
cgi-bin directory.
4. Open taint.html in your browser. Type in your e-mail address and submit. The cgi script should send a
message to your e-mail address. Check your email to verify it.
5. Create three empty files in your cgi-bin directory called f1.test, f2.test and f3.test. Use the ls command to
make sure they are there.
6. Go to taint.html with your browser and submit the following:
bozo.clown@circus;`rm *test`;
Please note that those are back quotes around rm *test
26
7. Check the three empty files you created. What happened? Is this a good thing?
8. Figure out how to email yourself the following files. Hint: you will need to enter complete mail command
somewhere in your form's text box.
 /etc/passwd
 logs/error_log
 taint.pl
 Entire contents of the cgi-bin directory
9. Edit taint.pl and add the switch -T to the end of the first line. Try to submit a query through taint.html. What
happens now?
Part 2 - lnstructions
Using knowledge obtained in class rewrite the script so it will run successfully with the -T option.
Use the command line: perl -c taint.pl to test your script.
Please note that the -T option should be removed while you are securing your script. Put
it back only temporarily whenever you wish to verify your work. The -T option should only be left
permanently in your script AFTER the script is secured.
Keep checking your error log, as it contains useful information if your server fails. The -T option will
create entries in your error log even if the script execution does not result in a 500 server error.
To be successful you may need to do the following:
• Check the user input against a regular expression. Use an if statement as instructed in previous labs. Use only
the untainted user input for the email task.
• Eliminate the back ticks in favour of a plain-Perl solution. See other exercises for hints.
• Secure the PATH environment variable, for example, by setting it to a blank string at the beginning of your
script.
27
Tainted Variables - Exercise 2
Objectives:
The purpose of this lab is to further study the dangers of accepting input from users when you write CGI scripts.
Danger! This script contains a tainted variable. Do not leave it unprotected on any web site after you are
finished this lab! You would be creating a serious security hole in the system.
1. Create the following web page in your Document Root. Call finger.html.
<html>
<head>
<title>User Info</title>
</head>
<body>
<form method="GET" action="cgi-bin/finger.cgi">
<p><input type="text" name="user" size="49"><br>
<input type="submit" va1ue="Show User Info" name="B1"></p>
</form>
</body>
<html>
2. Create the following script in your cgi-bin directory. Name it finger.cgi.
#!/usr/bin/perl -wT
use strict;
# GET THE DATA FROM THE FORM, PUT IT IN %form
my $user = $form{user};
# PRINT HEADER AND START HTML, WITH TITLE OF "user info"
print "<pre>\n";
print `finger $user` ;
print "<\pre>";
#END THE HTML
3. Execute the script on the command line first. Notice what happened.
4. Remove the -T option and execute the script again. You should have no errors or warnings at this point
5. Now open finger.htm in your browser and submit the form without entering any information. Notice the
results.
6. Enter your login name in the form and submit it again. At this point you should know what the intended
purpose is for this CGI.
7. Based on your experience from Exercise 1 try to submit UNIX commands. Start with safe ones such as ls and
be cautious with dangerous commands such as rm.
8. Go back to the finger.cgi script and change it so exploiting it with UNIX commands will not be possible.
Your goal is to make it run with the -T option without any warnings. Here are some hints:
 Use a regular expression and a temporary variable to "cIean up" user input
 Never use back ticks when creating CGI with Perl. It is not only insecure, but also inefficient.
28
Perl/MySQL - Registration/Login script
Objectives:
This exercise will use one way encryption methods to store encrypted passwords and then authenticate users
against those encrypted passwords.
Specifically you will learn how to:
1. Register a username and encrypted password input from a form in a MySQL
database from your CGI script
2. Perform checks on data to ensure data consistency.
3. Use a form to allow a user to login by supplying a username and password.
4. Authenticate the user using the MySQL table
Instructions:
Before we can use your database you will need to create a table called users.
1. Use the mysql command to connect to your database. Use the example below substituting your information:
$mysql -h db-mysql.zenit -u int420_O72a25 -p int420_072a25
When prompted for your password enter your original zenit password.
2. At the mysql> prompt enter the following command to create a table called users.
create table users
(id int auto_increment not null,
name varchar(8),
password varchar(25), I
pdmwv key (id));
3. Create the register.cgi script based upon the code below:
#!/usr/bin/perl -w
#Use the DBI (database interface) module
use DBI;
####Use an encryption digest of your choice###
#Dec1are variables with MySQL connection data
$db="";
$user="";
<<<< Enter in your MySQL Info
$passwd="";
$host="db-mysql.zenit";
$connectionInfo="dbi:mysql:$db;$host";
#Print HTTP header
print "Content-type:text/html\n\n" ;
#If first time script run display form
if ($ENV{REQUEST_METHOD} eq "GET")
{
&displayform;
exit;
}
#Else process form and insert into DB
else
29
{
&parseform();
&verifyform();
&insertuser();
exit;
#Standard form parsing using POST method
sub parseform _
{
#Enter in standard subroutine for parsing form data
}
sub insertuser
{
Scryptpasswd = Use $form{assword} and encryption method of your choice;
#Form SQL insert statement I
$insert = qq~insert users (name, password)
values ('$form {name}', '$cryptpasswd')~;
#Connect t0 MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#Prepare MySQL statement and create StatementHandler $sth
$sth=$dbh->prepare($insert);
#Execute Statement Handler and test for success
if ($sth->execute())
{
&displaysuecess;
}
else
{
&displayfail;
}
#Disconnect database
$dbh->disconnect();
}
sub displaysuccess
{
Display Success message in browser.
}
sub displayfail
{
Display Failure message in browser.
}
sub displayform
{
Print qq~
<html>
<head>
<title>Register User</title>
</head>
<body>
<form action="register.cgi" method=post>
<center>
<h2>Register a User and Password</h2>
User Name: <input type=text name=name value="$form {name} ">
$errors {name}
<br>
<i>Username should be all lowercase and 8 chars or less</i>
<br>
<br>
Password: <input type=password name=password>
30
$errors {password}
<br>
Retype Password: <input type=password name=password2>
$errors {password2}
<br>
<input type=submit value="Insert" name=Insert>
<input type=reset value=Reset name=reset>
</form>
</body>
</html>
~;
}
sub verifyform
{
$missing = 0;
#Test for username between 2 and 8 alphanumerics
if ($form{'name'} !~ /”[a-20-9]{2,8}$/)
{
$errors{'name’} = "Please enter up to 8 character username";
Smissing = l;
}
else
{
#Test for existing username in table
$select = qq~select name from users where name = 'Sform {name}'~;
$dbh=DBI->connect($cormectionInfo,$user,$passwd);
EBsth=$dbh->prepare($select);
$sth·>execute();
if(@row = $sth->fetchrow_array())
{Serrors {'name'} = "Name already registered";
$missing = 1 ;
}
else
{
$errors {'name'} = "";
}
}
#Test for password between 6 and 10 alphanumerics
if ($form {'password'} !~ /^[a-z0-9A-Z] {6,1 0} $/)
{
$errors{ 'password'} = "Enter 6 to 10 character password";
Smissing = 1;
}
else
{
$errors{'password'} = "";
}
#Test for password entered twice
if ($form{'password'} ne $form {password2})
{
$errors {'password2'} = "Passwords do not match";
$missing = 1;
}
else
{
$errors{'password2'} = "";
}
if ($missing == 1)
{
&displayform;
exit;
}
}
4. Fix all errors by first checking your error_log and retrying your script.
31
5. Use your working script to register a few users. Use mysql to see what data is stored in the table user.
6. Now create a 2nd script called login.cgi based on the code below:
#!/usr/bin/perl -w I I
#Use the DBI (database interface) module
use DBI;
### Use the same encryption digest as register. cgi
###
#Declare variables with MySQL connection data
$db="";
$user="";
<<<< Enter in your MySQL Info
$passwd="";
$host="db-mysql.zenit";
$connectionInfo="dbi:mysql:$db;$host";
#Print HTTP header
print "Content-type:text/html\n\n";
#If tirst time script run display form
if ($ENV {REQUEST_METHOD} eq "GET")
{
&displaylogin;
exit;
}
#Else process form and compare user and password, then send message
else
{
&parseform();
&verify();
&sendmessage();
exit;
}
#Standard form parsing using POST method
sub parseform
{
Insert standard subroutine far parsing form data
}
sub displaylogin
{
print qq~
<html>
<head>
<title>Login Page</title>
</head>
<body>
<form action="login.cgi" method=post>
<center>
<h2>Enter Your Username and Password</h2>
User Name: <input type=text name=name va1ue="$form {name} ">
$errors {name}
<br>
Password: <input type=password name=password>
$errors {password}
<br>
<input type=submit value="Insert" name=Insert>
<input type=reset value=Reset name=reset>
</form>
</body>
</html>~;
}
sub verify
{
32
#Form SQL select statement to select users record from table.
#Note the where clause
$select = qq~select id, name, password from users
where name = '$form {name}'~;
#Connect to MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#Prepare MySQL statement and create Statement Handler $sth
$sth=$dbh->prepare($select);
#Execute Statement Handler and test for success
$sth->execute();
#Test if row found in select
if(@row = $sth->fetchrow_array())
{
#If row found compare encrypted passwords
$cryptpasswd = Use $form{password} and same encryption method as
register. cgi;
if ($cryptpasswd ne $row[2])
{
$errors{password} = "Incorrect password";
&displaylogin;
exit;
}
}
else
{
#Row not found display username not found
$errors {name} = "User name not found";
&displaylogin;
exit;
}
sub sendmessage
{
Display Login Success message in browser.
}
7. Fix all errors by first checking your error_log and retrying your script.
8. Test your script and apply your knowledge to stage 1 and 3 of your project.
33
HTTP Cookies
Objectives:
This exercise will demonstrate how Perl CGI module is maintaining state through cookies.
Specifically you will learn how to:
1. Create cookies
2. Send and receive cookies ~
IMPORTANT NOTE: When working with cookies, make sure that you are accessing your zenit account using
its fully qualified domain name (zenit .senecac.on.ca). When you use the local name (zenit) cookies will not be
returned to your script from the browser.
Instructions:
1. Create following html page in your DocumentRoot.
<htrnI>
<head><title>Hey; Dude! </title></head>
<body>
<form action="/cgi-bin/dude.pl" method="post">
<input type="text" name="name">
<input type="subrnit" name="submit" value="Go!">
</form>
</body>
</html>
2. Create a script in your cgi-bin directory. Call it dude.pI.
#!/usr/bin/perl -wT
# put the code to get the data from the form into a hash named %FORM here
# create a subroutine named start_html() which will print the XHTML code to start a
web page up to and #
including the <body> tag;
if($FORM{'submit'} eq "submit")
{
# start headers
print "Set-Cookie: name=$FORM{'name'}\n";
print "Content-Type: text/html\n\n";
# end of headers
&start_html();
print "Nice to meet you $FORM{'name'}", ". I hope to see you again soon!";
print "</body></html>\n";
}
else
{
$old_cookie = $ENV{"HTTP_COOKIE"};
# if cookie was found
if($old_cookie)
{
($cookie_name, $name) = split(/=/, $old_cookie);
# start headers
print "Content-Type: text/html\n\n";
# end of headers
&start_html();
34
print "Hey! I know you dude! You are $name";
print "</body></html>\n";
}
# cookie not found
else
{
# redirect user to the form with a redirect header
print "Location:http://url_to_your_form/\n\n“;
}
}
3. Set proper permissions and open the browser and type the URL to your script. You should be
redirected to your form. Do you know why?
4. In the form enter your name and submit it.
5. Now repeat step #3 again. You should have a different outcome. Do you know why?
6. Open another browser window and repeat step #3. Refresh your browser to make sure that the page is not
cached. You should be redirected again, because the cookie created in this script is sessional - attached only
to one browser window. Going outside of your website does not erase cookies - you can try to go to a totally
different website and come back to your form; you should still see the greeting.
7. Submit another name and observe the results. Do you understand why each browser window shows a
different name stored in the cookie?
8. Research the Internet to expand the lifespan/scope of the cookie. Try to create cookies that expire after a
certain amount of time. Hint: This is done in the line where the cookie is created.
35
Maintaining State with Cookies
IMPORTANT NOT E: When working with cookies, make sure that you are accessing your zenit account
using its fully qualified domain name (zenit .senecac.on.ca). When you use the local name (zenit) cookies
will not be returned to your script from the browser.
Background information
As we know, one way browsers and sewers communicate with each other is through HTTP headers.
To date, the only header we have dealt with is the Content-Type header, which tells the browse what type of
content is contained in the message body. Now we will explore the use of other headers in order to maintain
state during multiple HTTP sessions.
HTTP is often described as being a connectionless protocol. This means that by default, a HTTP session lasts
for one transaction, and then ends. This is problematic If we want to communicate with the same client over
several different HTTP connections, as we will try in our shopping carts. How do we communicate with a
customer using several different web pages, and therefore several different HTTP sessions, without forcing
them to log on to every page with their ID information? This would be annoying and would risk our losing the
customer's interest. One way to accomplish this is through cookies.
You should take some time and understand how cookies work before continuing with this lab. You can read
about cookies in your Aulds text: pp. 402 - 404
You can also read about them at the following sites:
• description of cookies - Cookie Central
• how cookies work - Cookie Central
• how cookies work - How Stuff Works
After you have finished studying cookies, take a look at your browser. Try to find out where your cookies are
stored. Look at the contents of your cookies. You will need to know how to do this before you start
experimenting with cookies.
Cookies are created by servers and sent to browsers, where they are stored on the local host. Cookies are set in
headers. lf you want your script to set a cookie in a client's browser, it must send a cookie header before the
message body. Remember that the last header sent must be followed by a blank line.
The line in a perl CGI script to set a cookie is as follows:
print "Set-Cookie: Name0fCookie=$cookie_variab1e\n“;
lf you want the cookie to be named userid, and contain the value stored in a variable called $id, the syntax
would be:
print "Set-Cookie: userid=$id\n";
As you have read, a cookie can set a number of values. The following cookie is sent from the server to the
browser. It applies to the whole site served by the server (path=/), within the zenith domain, and expires at 11
PM on Sunday, March 2, 2013.
36
print "Set-Cookie: userid=$id; expires=Sun, 02-Mar-2013, 11:00:00 EST;
domain=zenit.senecac.on.ca; path=/\n“ ;
When a browser sends a request to a site for which it has a cookie, it also sends a header. The header looks like
this:
Cookie: userid=tahooti . bonzai@hotmai1 . com
This cookie was set by a site that uses a user's email as a user id.
The cookie data is taken from the header and the name and value are placed in the HTTP_COOKlE
environment variable, where it can be accessed by a script.
The data stored in $ENV{'HTTP_COOKlE'} for the above cookie would look like this:
userid=tahooti.bonzai@hotmai1 . com
Exercise
1. Create a web page that contains a text box that asks for a user name.
2. Using the examples above, and knowledge gained from the previous exercise, create a script called cookie.cgi
that will get the name entered by the user, and:
 create a cookie named userid whose value is the name that was entered by the user.
 Do not set a date.
 Set the path of the cookie to /.
 Set the domain to zenit.senecac.on.ca.
 Send the cookie back to the browser as a header. ·
 Print out the cookie information and send it to the browser for display. ·
3. Create another web page which displays the contents of the HTTP_COOKIE environment variable. Put
a link to this page on the page generated by the previous script
4. Go to the first form with your browser.
5. Fill in a userid in the text box and submit.
6. Click on the link to the page which displays the HTTP_COOKlE
7. Visit other web sites; then come back to this page.
8. telnet to your server, using your port number, as in the http lab. Type
HEAD /cgi_bin/cookie.cgi HTTP/1.0
followed by two new lines. Notice the headers.
9. Look at the cookies in your browser. Look at the contents of the cookie for the zenit domain.
10. Close your browser. Open a new one. Is the cookie still there?
11. Go back to the page which displays the HTTP_COOKlE. Is anything displayed?
12. Change the script which creates a cookie so that the cookie lasts for a day.
13. Repeat steps 4 through 11. Is anything different?
Cookies and your lNT420 Project
You can use cookies in your project to keep track of a user while he/she surfs your site. Even if he/she leaves
your site and comes back, the cookie can be used to identify him/her. Remember that a cookie is stored on the
37
machine from which the browser made its request. lf the machine is
shared, you may not want the cookie to last after the user has closed his/her browser. Send a
cookie with a unique id to anyone who logs on to your site. Once he/she adds to his/her cart and has identified
him/her-self, send another cookie which identifies him/her by his/her unique user id.
38
Perl/MySQL - Adding a Cookie to your Login script
Objectives:
This exercise will add a cookie containing the users' User ID when the he/she logs in.
Specifically you will learn how to:
l. Test for a cookie and if one is found bypass login. lf one is not found prompt for login.
2. Authenticate the user using the MySQL table and then send a cookie to the client containing
the users' id from the table.
Instructions:
1. Make a backup of your login.cgi script and make the following changes to send a cookie after login.
#!/usr/bin/perl -w
…
…
…
#Print HTTP header
<--Remove these 2 lines as you need to
print "Content-type:text/html\n\n"; <--print the header with the cookie
sub sendmessage
{
#Print HTTP header including cookie <-- add these 3 lines to the
print "Set-Cookie: uid=$row[0]\n"; <-- start of the sendmessage
print "Content-type:text/html\n\n"; <-- sub
}
2. Fix all errors by first checking your error_log and retrying your script.
3. Test your script by logging in as a valid user and checking your cookies in your browser. (Firefox is a
good browser for examining/working with cookies)
4. When is the cookie set to expire?
5. Now make the following changes to your login script to test for an existing cookie.
#### Change your main routine so that the method is GET.
#If first time script run display form
if ($ENV{REQUEST_METHOD} eq "GETZ')
{
g if ($ENV{'HTTP_COOKIE' })
{
&gotcookie;
}
else
{
&displaylogin;
}
exit;
}
Test for a cookie
39
6. Now add a subroutine to display the cookie back to the user.
sub gotcookie
{
(cname,cvalue) = split(/=/,$ENV{'HTTP_COOKIE'});
#Send http header
print "Content-type:text/html\n\n";
print qq~
<html>
<head>
<title> Already Logged In </title>
</head>
<body>
<h2>You have already logged in!</h2>
<h2>Your cookie is: $ENV{"HTTP_COOKIE"}</h2>
<h2>Your cookie name is $cname</h2>
<h2>Your cookie value is $cvalue</h2>
</b¤dy>
</html>~;
}
7. Fix all errors by first checking your error_log and retrying your script.
8. Test your script by rerunning the script after logging in.
9. You can apply this knowledge to your project.
40
Perl/MySQL - Administrative Interface Show/Add Records Script
Objectives:
This exercise will take you through the steps of creating an administrative interface for viewing and adding
records to our friends table. We will then use this script as the starting point for further labs to modify records
and delete records from the table. Specifically you will learn how to:
1. Review creating script for showing records in a MySQL table and adding records to the table.
2. Perform checks on data to ensure data consistency.
3. Use the same script for multiple functions.
Instructions:
1. Before we can use your database you will need to create a table called fiiends. You should have already
created this table in a previous exercise that we can reuse. If not login to mysql and create a table called friends
with the following fields:
+---------+-------------+--------+-------+-----------+----------------+
| Field
| Type
| Null
| Key
| Default
| Extra
|
+---------+-------------+--------+-------+-----------+----------------+
| id
| int(11)
| NO
| PR
| NULL
| auto_increment |
| lname
| varchar(25) | NO
|
| NULL
|
|
| fname
| varchar(25) | NO
|
| NULL
|
|
| phone
| varchar(10) | YES
|
| NULL
|
|
| email
| varchar(60) | YES
|
| NULL
|
|
+---------------+------------------------+--------------+-------------+--------------------+-----------------------------+
2. Create the admin.cgi script based upon the code below.
#!/usr/bin/perl -w
###########################################
# Use the DBI (database interface) module #
###########################################
use DBI;
################################################
# Declare variables with MySQL connection data #
################################################
Declare your variables here
#####################
# Print HTTP header #
#####################
print "Content-type:text/html\n\n";
##########################
#
Main Logic of Script #
##############=###########
#### If first time script runs (GET) display table of friends ####
if ($ENV{REQUEST_METHOD} eq "GET")
41
{
&showallfriends;
exit;
}
#################################
# Start of Subroutines
#
#################################
#### Sub for displaying all friends in table form ####
sub showallfriends
{
#### Start HTML table ####
print qq~<html>
<head>
<Title>My Friends</Title>
</head>
<body>
<table border=1>
<ty>
<th>ID</th><th>Last Name</th><th>First Name</th>
<th>Phone Number</th><th>E-Mail</th>
</tr>~;
#### Form SQL select statement ####
Enter correct code here
#### Connect to MySQL and create Database Handler $dbh ####
$dbh=DBI->connect($connectionInf0,$user,$passwd);
#### Prepare MySQL statement and create Statement Handler $sth ####
Enter correct code here
#### Execute select statement ####
Enter correct code here
#### Loop through each record selected and print in html table ###
while (@row=$sth->fetchrow_array())
{
print qq~<tr>
<td>$row[0]</td><td>$row[1]</td><td>$row[2]</td>
<td>$row[3]</td><td>$row[4]</td>
</tr>~;
}
#### Close HTML table ####
print qq~</table>
</body>
</html>~;
#### Disconnect from MySQL database ####
Enter correct code here
}
3. Fix all errors by first checking your error_log and retrying your script.
4. The script should show you the contents of your friends table in a html table. If no records are found, use
mysql to add a few records manually.
5. Now let's add additional functionality to the script. Below the table we will display a simple form with a
single button to add a friend to the table. Edit your admin.cgi script by adding this code to the bottom of the
showallfriends subroutine:
42
#### Display button to add a record #### .
print qq~<form action="admin.cgi" method="post"> `
<input type="submit" name="submit" value="Add a Friend">
</form>
</body>
</html>~;
6. Fix all errors by first checking your error_log and retrying your script.
7. The script should now show a small form with a button after the table. The value of the button
element is very important so make sure it is correct.
8. Now let's add a test to our main logic to see if the that button had been clicked and if it has displayed a form for
adding a new record. Remember to test if the button has been clicked we will need to parse the data from the form first.
Edit your admin.cgi script by changing your main routine to this:
if ($ENV{REQUEST__METHOD} eq "GET")
{
&showallfriends;
exit;
}
#### If POST, parse form data into %form hash ####
else
{
&parseform;
#### Test for value of submit button ####
if ($form{submit} eq "Add a Friend")
{
&displayaddform;
}
}
9. Now add the parseform and displayaddform subroutines.
#### Sub for standard form parsing using POST
sub parseform
{
Enter in standard subroutine for parsing farm data
}
#### Sub for displaying form to insert a friend ####
sub displayaddform
{
print qq~
<html>
<head>
<title>Add a Friend</title>
</head>
<body>
<form action="admin.cgi" method="post">
<center>
<h2>Add a Friend</h2>
Last Name: <input type="text" name="lname" value="">
<br>
First Name: <input type="text" name="fname" value=""> _
<br>
Phone Number: <input type="text" name="phone" value="">
(10 digits only please)
43
<br>
E-mail: <input type="text" name="email" value="">
<br>
<input type="submit" name="submit" value="Insert Friend">
</form>
</body>
</htm1>
~;
}
10. Once again notice the value of the submit button. This is the value we will test for to insert a new r
record into the table.
11. Run the script; it should now display the new form when you click on the "Add a Friend" button.
12. Now add the logic to the script for testing for the "Insert Friend" button and a subroutine for
validating the data. For now just print a success or fail message.
if ($form{submit} eq "Add a Friend")
{
&displayaddform;
}
elsif ($form{submit} eq "lnsert Friend")
{
#### Test for valid data; if true, insert record, if false,
#### resend form ####
if (&validatedata)
{
print "success";
}
else
{
print "fail"; ·
}
}
#### Sub for validating data before insert ####
sub validatedata
{
%patterns = ( 'lname’ => ‘[A-Z]\w+',
'fname' => '[A-Z]\w+',
'phone' => '\d{ 10}',
'email' => '[\w\-\.]+\@[\w\-\.]+',
'submit' => '.*');
$va1id = 1;
foreach (keys %form)
{
if ($form{$_} !~ $patterns{$_})
{
$erromsg = "Please enter valid data for reqd field";
$valid = 0;
}
else
{
$errormsg = "";
}
$errors{$_} =$errormsg;
}
return $valid;
}
44
13. Run the script, testing various combinations of valid and invalid data.
14. Change the script to insert the record or redisplay the form based on the validity of the data. Change the main
routine as follows:
#### Test for valid data; if true, insert record; if false, resend form ####
if (&validatedata)
{
&insertfriend;
&showallfriends;
<-- After the insert show the friends again.
}
else
{
&displayaddform;
}
}
15. Now add the insertfriend subroutine and change the displayaddform to include error messages and
any previously entered data.
#### Sub for inserting new record into table ####
sub insertfriend
{
#Form SQL insert statement
$insert = qq~insert friends (lname, fname, phone, email)
values('$form{lname}’,'$form {fname}','$form{phone}‘, '$form{email}')~;
#Connect to MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#Prepare MySQL statement and create Statement Handler $sth
$sth=$dbh->prepare($insert);
#Execute Statement Handler
$sth->execute();
#Disconnect database
$dbh->disconnect();
}
#### Sub for displaying form to insert a friend ####
sub displayaddform
{
print qq~
<htm1>
<head>
<title>Add a Friend</title>
</head>
<body>
<form action="admin.cgi" method="post">
<center>
<h2>Add a Friend</h2>
Last Name: <input type="text" name="lname" value="$form{lname} ">
$errors{lname}
<br>
First Name: <input type="text" name="fname" value="$form{fname} ">
$errors{fname}
<br>
Phone Number: <input type="text" name="phone" value="$form{phone} ">
45
(10 digits only please)
$errors{phone}
<br>
E-mail: <input type="text" name="email" value="$form{email}">
$errors{email}
<br>
<input type="submit" name="submit" value="Insert Friend"> ·
</form>
</body>
</html>
~;
}
16. Test the script and fix any errors. Make sure you can add records that are valid.
46
Perl/MySQL - Administrative Interface Delete Record Script
Objectives:
This exercise will take you through the steps of extending the administrative interface to include deleting records from
the friends table.
Specifically you will learn how to:
l. Use a perl/CGI script for deleting records from a MySQL table.
2. Use hidden form fields for session maintenance so the script knows which record
is to be deleted.
3. Use the same script for multiple functions.
Instructions:
1. Before you begin you must have completed the previous lab.
2. Edit the admin.cgi and change the showallfriends subroutine to display a small form for each record
in the table. The form will contain 2 elements, a hidden field that contains the id of the record from
the table, and a submit button that is set to a value of "Delete".
#### Sub for displaying all friends in table form ####
sub showallfriends
{
#### Start HTML table ####
print qq~<html>
<head>
<Title>My Friends</Title>
</head>
<body>
<table border=1>
<tr>
<th>Last Name</th><th>First Name</th>
<th>Phone Number</th><th>E-Mai1</th>
<th>Change Friend</th>
<-- New Column for Form.
</tr>~;
#### Form SQL select statement ####
$select = qq~select id, lname, fname, phone, email from friends~;
#### Connect to MySQL and create Database Handler $dbh ####
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#### Prepare MySQL statement and create Statement Handler $sth #####
$sth=$dbh->prepare($select);
#### Execute select statement ####
$sth->execute();
#### Loop through each record selected and print in html table ####
#### Add a form for each record to change record (delete) ####
while (@row=$sth->fetchrow_array())
{
print qq~<tr>
<td>$row[1]</td><td>$row[2]</td>
<td>$row[3]</td><td>$row[4]</td>
<td>
<form action="admin.cgi" method="post">
<input type="hidden" name="id" value="$row[0]">
47
<input type="submit" name="submit" value="Delete">
</form>
</td>
</tr>~;
}
#### Close HTML table ####
print qq~</table>\n
<br><br>\n~;
#### Display button to add a record #### t
print qq~<form action="admin.cgi" method="post">
<input type="submit" name="submit" value="Add a Friend">
</form>
</body>
</html>~;
#### Disconnect from MySQL database ####
$dbh->disconnect();
}
3. Fix all errors by first checking your error_log and retrying your script.
4. The script should now show a simple form for each row with a submit button called "Delete". View the HTML source
to check that each form also has a hidden field for the record ID #.
5. Now modify the main routine to test for the "Delete" button being clicked and running a subroutine for deleting a
record.
#### If first time script runs (GET) display table of friends ####
if (SENV {REQUEST_METHOD} eq "GET")
{
&showallfiiends;
exit;
}
#### If POST parse form data into %form hash ####
else
{
&parseform;
#### Script will perform actions depending on value of the submit button ####
if ($form {submit} eq "Add a Friend")
{
&displayaddform;
}
elsif ($form{submit} eq "lnsert Friend")
{
#### Test for valid data; if true, insert record; if false, resend form ####
if (&validatedata)
{
&insertfriend;
&showalliriends;
}
else
{
&displayaddform;
}
}
elsif ($form{submit} eq "Delete")
{
&deletefriend;
&showallfriends;
48
}
}
6. Now add a subroutine for deleting the record it will use the record sent in the hidden field to know
which record to delete.
#### Sub for deleting record from table ####
sub deletefriend
{
#Form SQL delete statement
$delete = qq~delete from friends where id = '$form{id}'~;
#Connect to MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionlnfo,$user,$passwd);
#Prepare MySQL statement and create Statement Handler $sth
$sth=$dbh->prepare($delete);
#Execute Statement Handler .
$sth->execute();
}
7. Fix all errors by first checking your error_log and retrying your script.
8. Delete and then recreate records in your friends table. Does the id of a deleted record
get reused?
49
Perl/MySQL - Administrative Interface Update Record Script
Objectives:
This exercise will take you through the steps of extending the administrative interface to include updating
records in the friends table.
Specifically you will learn how to:
l. Use a perl/CGI script for updating records in a MySQL table.
2. Use hidden form fields for session maintenance so the script knows which record is to
be updated.
3. Use the same script for multiple functions.
Instructions:
1. Before you begin you must have completed the previous lab.
2. Edit the admin.cgi and change the showallfriends subroutine and modify the form for each record in the table.
Add to the form an additional submit button that is set to a value of "Change".
#### Loop through each record selected and print in html table ###
#### Add a form for each record to change (update or delete) ####
while (@row=$sth->fetchrow_array())
{
print qq~<tr>
<td>$row[l]</td><td>$row[2]</td>
<td>$row[3]</td><td>$row[4]</td>
<td>
<form action="admin.cgi" method="post">
<input type="hidden" name="id" value="$row[0]">
<input type="submit" name="submit" value="Delete">
<input type="submit" name="submit" value="Change">
</form>
</td>
</tr>~;
}
3. The script should now show a simple form for each row with 2 submit buttons called "Delete" and "Change”.
View the HTML source to check that each form still has a hidden field for the record ID #.
4. Now modify the main routine to test for the "Change" button being clicked and running a subroutine for
displaying a form to change a record.
#### If first time script runs (GET) display table of friends ####
if ($ENV{REQUEST_METHOD} eq "GET")
{
&showallfriends;
exit;
}
#### If POST parse form data into %form hash ####
else
50
{
&parseform;
#### Script will perform actions depending on value of the submit button ####
if ($form {submit} eq "Add a Friend")
{
&displayaddform;
}
elsif ($form{submit} eq "Insert Friend")
{
#### Test for valid data; if true, insert record; if false, resend form ####
if (&validatedata) .
{
&insertfriend;
&showallfriends;
}
else
{
&displayaddform;
}
}
elsif ($form{subm1t} eq "Delete")
{
&deletefriend;
&showallfriends;
}
elsif ($form{submit} eq "Change")
{
&displaychangef0rm;
}
}
5. Now add a subroutine for displaying a form that will allow a user to change the contents of the record. It will
use the record id sent in the hidden field to know which record to put in the form.
#### Sub for displaying a form for changing record in table ####
sub displaychangeform
{
#### Form SQL select statement to load current data in the form ####
$select = qq~select id, lname, fname, phone, email from friends
where id = '$form{id}'~;
#### Connect to MySQL and create Database Handler $dbh ####
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#### Prepare MySQL statement and create Statement Handler $sth ####
$sth=$dbh->prepare($select);
#### Execute select statement ####
$sth->execute();
@row=$sth->fetchrow_array(); <-- No loop needed - only one record
<-- returned using the where clause
#### Display form with data from row to be changed ####
print qq~
<htm1>
<head>
<title>Change a Friend</title>
</head>
<body>
<form action="admin.cgi" method="post">
<center>
<h2>Change a Friend</h2>
51
Last Name: <input type="text" name="1name" value="$row[1]">
<br>
First Name: <input type="text" name="fname" value="$row[2]">
<br>
Phone Number: <input type="text" name="phone" value="$row[3]">
(10 digits only please)
<br>
E-mail: <input type="text" name="email" value="$row[4]">
<br>
### Include hidden field for id that won't change
<input type="hidden" name="id" value="$row[0]">
<input type="submit" name="submit" value="Update Friend">
</form>
</body>
6. Test your script to see if the form for changes is correctly displayed.
7. Now modify the main routine to test for "Update Friend" and add a subroutine for updating the
record.
elsif ($form{submit} eq "Change")
{
&displaychangeform;
}
elsif ($form{submit} eq "Update Friend") »
{
&updatefriend;
&showallfriends;
}
#### Sub for changing record in table ###
sub updatefriend
{
#Form SQL update statement
$update = qq~update friends set lname = ’$form{lname'}',
fname = '$form{fname}', phone = '$form {phone}',
email = '$form {email} ' where id = '$form {id} '~;
#Connect to MySQL and create Database Handler $dbh
$dbh=DBI->connect($connectionInfo,$user,$passwd);
#Prepare MySQL statement and create Statement Handler $sth
$sth=$dbh->prepare($update);
#Execute Statement Handler and test for success
$sth->execute()
#Disconnect database
$dbh->disconnect();
}
8. Test the script and update some records.
9. One problem with the update is that it doesn't validate the changed data as it did when adding a record. We
can reuse the validatedata subroutine we already have by modifying the main routine.
elsif ($form{submit} eq "Update Friend")
{
if (&validatedata)
{
&updatefriend;
52
&showallfriends;
}
else
{
&displaychangeform;
}
}
10. One final change must be made to our displaychangeform subroutine to include error messages. It will
already revert back to the current record's data.
print qq~
<html>
<head>
<title>Change a Friend</title>
</head>
<body>
<form action="admin.cgi" method="post">
<center>
<h2>Change a Friend</h2>
LastName: <input type="text" name="lname" value="$row[1]">
$errors{lname}
<br>
First Name: <input type="text" name="fname" value="$row[2]">
Serrors {fname}
<br>
Phone Number: <input type="text" name="phone" value="$row[3]">
(10 digits only please)
$errors{phone}
<br>
E-mail: <input type="text" name="email" value="$row[4]">
$errors{email}
<by>
<input type="hidden" name="id" value="$row[0]">
<input type="submit" name="submit" va1ue="Update Friend">
</form>
</body>
</html>~;
11. Fully test your script and make sure you can add, delete, and update valid records. You can use this script
also as a foundation for your project.
53
Sending email with Perl CGI
Objectives:
1. Learn how to send email from CGI scripts
2. Use data files for mass mailings (spam?'??)
Instructions:
1. Have a look at the example below. This example illustrates how to send the same email
message to many recipients at a time.
#!/usr/bin/perl -wT
0pen(INF,"users.dat");
@ary = <INF>;
close(INF);
foreach $user (@ary)
{
($username, $password, $email)=sp1it(/\|/, $user);
open (MAIL,"|/usr/lib/sendmail -t");
print
print
print
print
Visit
close
}
MAIL "From: me\@macrosoft.com\n";
MAIL "Subject: weekly Update!\n";
MAIL "To: $email.1\n\n“;
MAIL “Great news! Your favourite website www.macros0ft.com has been updated.
today!“;
(MAIL);
To try this example you will have to create a data tile with fields containing appropriate information. You
should be able to determine the file format by looking at the split line.
2. Change the example above to perform the following:
• Add a Cc: or a Bc: header to your email. Hint: headers do not follow a particular order, but they must
be separated from the email body by a blank line.
• Add a field to the data file so it also contains the recipient's full name. Change the script so each email
has a personalized heading such as: Dear Mr. Smith!
• Make the email body come from an external file. Hint: open the file and "print it" into the email using
print MAIL <MESSAGE>; syntax.
• Make the email body HTML. Hint: you will need the "Content-Type..." header. Look in the source of
emails you have for inspiration.
Additional work
lf the email topic interests you, you might want to look into some not-so-light reading: RFC0821 and RFC2821.
54
Analyzing Log Files with Analog
Part 1: Installing and Basic Configuration of Analog
Obtain the Analog source file by visiting http://www.analog.cx/download.html. Download the latest version
(tar.gz tile) to your home directory.
Untar the downloaded source code using an appropriate command. Don't forget to delete the tarball!
Change your present working directory to the Analog source directory (src) located inside the directory you
created in the previous step.
Note: this file does not have to be edited for Analog to run as most of these values can be edited through the
Analog configuration file or Analog command line arguments. Be very careful editing this file. It is not a shell
script. lt is C source code. Do not treat it as a shell script! (e.g. do not uncomment any line unless you are
specifically instructed to do so or you know what you are doing!)
- Set the name of your organization or web server by editing:
#define HOSTNAME to be "[your organization]"
· Set the default name of the logfile you want to analyze by editing:
#define LOGFILE to be "Iogfile.log"
. Set the name of the output file for the Analog results by editing:
#define OUTFlLE to be "stdout"
. Set the name of the default Analog configuration file by editing; ’
#define DEFAULTCONFIGFILE to be "analog.cfg"
Change your present working directory one level up. Compile the configured source code by executing a make
command. Your Analog is ready. All you need to do now is to tell Analog to analyze the access log of our web
server by editing the Analog configuration tile. You will need to specify where your log file is and the target
location of the report file. Use your favourite editor to edit the analog.cfg configuration file.
(Optional) Edit the anlghead.h tile to customize the Analog software.
For example: LOGFILE /homeh4202a12/www/logs/access_Iog
This example assumes that docs is your DocumentRoot, so you can view the report directly in the browser.
Uncomment and change the OUTFlLE setting to the name and location of your report file.
For example: OUTFlLE /homeh4202a12/www./docs/report.htmI
As Analog reports contain links to images you will need to copy them to your server from the Analog directory.
. Make a directory called images in your sewer‘s DocumentRoot directory
· Copy all images from the AnalogSource/images directory to your newly created images directory
Alternatively, you can change the IMAGEDIR setting in analog.cfg.
Run Analog to analyze your log and produce a report. The analog executable (called analog) is located in the
Analog directory.
View the ready report in the browser. If the images are not showing double check the IMAGEDIR setting or
copy the images to your server as instructed above.
Part 2: Customizing and Getting to Know Analog
- Visit http://wwwanalog.cx/docs/Readme.html for more information on configuring and using Analog
- To see the version of Analog you are using: . /analog -version
. To see what has been configured and some of the configurable options, type in the following command and
read the contents of the file: ./analog -settings. Notice the configuration file(s) Analog is using and log files it is
55
configured to analyze.
. When reading the output from Analog, whether reading from a web page through your browser or reading
from standard output, make certain to notice the different types of requests and what they mean. Notice the
different types of reports that Analog is able to provide. Take a look at the different types of reports (ex:
monthly, daily file type, file size etc.) and know what they represent.
· By visiting http://vwwv.analog.cx/docs/basiccmd.htmI and using that information, change the analog.cfg fiIe
to turn certain reports (ex: monthly report) on and off and adjust the Analog output. View the Report.htmI file
as you make the changes to the analog.cfg tile. (Don't forget to run analog again once you make the changes).
· For a quick reference chart on Analog configuration commands, visit
http://www.analogcx/docs/guickref.html#quicksortby
· For information on warnings (which you might be receiving) and errors, visit
http://www.analog.cx/docs/errors.html. The errors/warnings page is very detailed, categorizes the errors
logically, and gives great explanations on what they mean.
Additional work
Search the lnternet for other tools similar to analog. You might want to start with Webalizer and Modlogan.
Download, configure, install and run them on your account. Observe differences between them. Although they
all do essentially the same task, each approaches it in its own way. Find one that suits you and use it from now
on.
56
Download