You are on page 1of 14

Write simple scripts in PERL (CGI)

Mar 25th, 2009 by Simon Steed The following guide is intended to provide an insight into what CGI is and how to write simple scripts in PERL.

What is CGI?
CGI Provides a way for Web Servers to run onsite programs and incorporate their output into the pages they serve. These external programs can interact with users and dynamically create new web pages, complete with audio and graphics generated on-thefly. CGI Stands for Common Gateway Interface and is a standardized set of conventions specifying how these external programs are to interface with the Web Server. One thing to note is that CGI is NOT a programming language, although many people will insist that it is.

Can I run CGI programs and How do I do It?


This will depend on whether your ISP allows the use of scripts.The majority of them let you do so but some (i.e. Demon Internet) will only let you run scripts that they provide!. If the answer is yes you then need to find out what language they use (PERL, C, Visual Basic, AppleScript etc) and where the compiler is located on the server. If youre using the Enterprise server (either the Homepages or Virtual Servers) then you can run CGI programs and the language needs to be PERL (Practical Extraction Reporting Language). The PERL scripts can be located anywhere within your directory but I would always reccomend that you create a cgi-xploiter for them. If you are on another providers server, ask their tech support guys to point you in he right direction.

What can CGI scripts be used for?


How large is your imagination? On this site, I use scripts for the following purposes:
a: b: c: d: To To To To provide a counter. provide a guestbook. provide logs so I can see which pages are the most popular. EMAIL me the results of various forms.

There are of course many other applications such as shopping baskets, image rotators, chat pages etc.

Is it easy to write scripts in PERL?


The simple answer is Yes & No. PERL is very similar in structure to C and so if you are a C programmer (like myself), youll find the rocky road to script writing easier than someone who has never programmed anything in their life! Dont be put off by this as there are many resources on th net that can be used to Guide you. I hope that this document will provide some of the answers.

Where do I start
OK 1st get a strong cup of coffee (or Tea) and well start. I would reccomend that you create a cgi-xploiter directory on your server as follows. This assumes your are using WS_FTP. 1/ Log into your directory on the sever and click on the MKDIR icon on the top right of the screen. Call this directory cgi-xploiter and click on OK. 2/ We now need to set the permissions for this directory (see What are the Permissions?). Highlight the new directory using the mouse and RIGHT click on it. A list should then magically appear and you need to select the one marked CHMOD (unix). Check all the boxes that appear. This will have CHMODDED the file to 777. If you have a version of WS_FTP that is less than V4 then you will not see the CHMOD (unix) bit. You need to click on FTP Commands, select SITE and type in chmod 777 cgi-xploiter then click on OK. This proceedure allows full Read, Write and Execute access to this directory.

Our 1st Script!


OK. Using a text editor (wordpad is fine) type in the following code:
#!/usr/bin/perl print "Content-type: text/plain\n\n"; print "This is my first script!";

The space following the 1st line is important. Some servers will not interpret the script properly if there is no space and some will (Enterprise seems to like it either way). The second line tells the browser that it shold interpret any following information as text. The third line prints out the text in the marks. Note that all PERL lines are closed with a ; semi-colon. Save the file as test.cgi (note that Homepages users on the Enterprise server must use the *.cgi extension. Other servers may require the use of *.pl instead). To test this script, upload it to the newly created cgi-xploiter directory making sure that you upload in ASCII mode and not BINARY mode and change its permisions to full read and write access (chmod 777). Now launch your browser and type in the URL to the

file i.e. http://homepages.enterprise.net/login-name/cgi-xploiter/test.cgi and the result should be This is my first script. If it did not work or came up with an internal server error, ensure that you have uploaded the file as ASCII, set the permissions on both the cgi-xploiter directory and the test.cgi file and that you have typed the code correctly.

Next Please!
Right assuming we are all OK on that simple script, we will now attempt something a little more advanced. The following script will once run, create a log of any visitors to your page in a file called main.log.
---------------------------cut here----------------------------------------------#!/usr/bin/perl ####################################################################### ########## # This script was written by TOTO http://www.xploiter.com It may be # # freely copied, edited and anything else but please leave this header intact. # # logger.cgi # # version 1.0 # # You must 1st create a file called main.log in the same directory. This # # must have write permissions set. # ####################################################################### ########## $mainlog = "main.log"; $shortdate = `date +"%D %T %Z"`; chop ($shortdate); print "Content-type: text/plain\n\n "; open (MAINLOG, ">>$mainlog"); print MAINLOG "Time: $shortdate\n"; print MAINLOG "User: $ENV{'REMOTE_IDENT'}\n"; print MAINLOG "Host: $ENV{'REMOTE_HOST'}\n"; print MAINLOG "Addr: $ENV{'REMOTE_ADDR'}\n"; print MAINLOG "With: $ENV{'HTTP_USER_AGENT'}\n"; print MAINLOG "From: $ENV{'HTTP_REFERER'}\n\n"; close (MAINLOG); exit; ----------------------------cut here----------------------------------------------

To use the script cut and paste it into a blank document and save it as logger.cgi. Then read on to see how it works and how to use it. This script brings what are known as Environmental Variables which are explained Here. Taking the script apart we can start to understand what is happening. #!/usr/bin/perl: Tells the script where the Perl interpreter is located on the server. $mainlog: This is a variable (identified by the prefixing $ sign) that tells the script where to send the data. The file main.log in this case will need to be created 1st and full write permissions applied to it (chmod 666). The easiest way to do this is to copy a blank text file over from your hard drive to the server and rename it. Then set the permissions as previously desribed. $shortdate = `date +%D %T %Z`;: This line gets the current date and time. The arguments +%D %T format the results as follows:
%D %T = = The date is formatted as MM/DD/YY The time is formatted as HH:MM:SS

The results of this line are then put into the variable $shortdate so it can be called later by the script. chop $shortdate;: This line takes the variable and uses PERLs chop function and removes the last character from each of its strings in its argument list. Basically it removes any newlines that are passed into the variable. Because PERL does not automatically strip off the trailing newlines from an input line this would be a good practice to continue in any scripts you create in the future. print Content-type: text/plain\n\n ;: This line outputs the MIME contenttype header for plain text. MIME stands for Multipurpose Internet Mail Extensions. This tells the server what type of document is to follow. open (MAINLOG, >>$mainlog);: This line uses PERLs open function to call the variable $mainlog and associates it with the filehandle MAINLOG. The >> symbols mean that the file will only be opened up for appending to.
print MAINLOG "Time: $shortdate\n"; print MAINLOG "User: $ENV{'REMOTE_IDENT'}\n"; print MAINLOG "Host: $ENV{'REMOTE_HOST'}\n"; print MAINLOG "Addr: $ENV{'REMOTE_ADDR'}\n"; print MAINLOG "With: $ENV{'HTTP_USER_AGENT'}\n"; print MAINLOG "From: $ENV{'HTTP_REFERER'}\n\n";

This little bundle actually provides the log when the script is run. The print MAINLOG part prints the output of the particular line to the file main.log which we opened before.

This would either be the time and date as in the 1st line or any of the Environmental Variables as discused further on below.

Using the Script


To utilise this script we need to used SSI (server side includes). These are very similar to HTML commands in look but can perform useful dynamic functions. Common use of SSIs is to add a signature file, such as the company logo and address, to every HTML file. This obviously saves a lot of extra typing. Other uses include adding the current date, or the last modification date, to a file. In our case we will use SSIs to execute the cgi script which will then silently log the Environmental Variables we require. The basic html code is shown below:<HTML> <HEAD> <TITLE>ToTos Test Page</TITLE> </HEAD> <BODY> <H1>ToTos Test Page</H1> Hello there! You have now been logged. <!#exec cgi=/loginname/logger/logger.cgi > <HR> </BODY> </HTML> Notice the <!#exec cgi=/loginname/logger/logger.cgi >. This is the SSI that is actually executing the script and is the syntax as used on the Enterprise server. For other servers you may need to change the path in order for the logger.cgi file to be found. When creating this file it is important that you save the file as filename.shtml. This tells the server to parse the script and treat it as an SSI file instead of a normal html file. Now all you need to do is upload the files logger.cgi, main.log & the *.shtml file to your server. The permissions for the logger.cgi script and main.log need to be set to CHMOD 777. Now via your browser, call the *.shtml file. All that should be displayed is Hello there! You have now been logged. If you now FTP to the directory where you placed the files and open up main.log you should see the Environmental Variables all filled out.
Environmental Variables.

In the CGI environment their are certain variables that are available for us to call at any time into our programs. These are called, not surprisingly, environmental variables. For

example, the environmental variable HTTP_USER_AGENT holds information about the browser type and version number. This could be useful for only sending out information according to browser capabilities; there is little point sending images to a text based Lynx browser.

Data Encoding
Information that is sent to the CGI program from the client is encoded using two simple rules.

Spaces are converted to plus (+) signs. Any character may be represented as a hexidecimal number (representing the characters ASCII number) prefixed with the percent (%) character. So %25 is a percent character, and %2B is a plus sign.

The following table shows a selection of the enviromental environmental variables that are available: Variable Name AUTH_TYPE Description A document may be protected on the HTTP server. If it is, this variable is set to the type of authentication used to verify the remote user. The length of the data being sent from the client. This information is only used with HTML forms. The type of data being sent to the server, from the client. This can be any valid MIME type (but is currently limited to application/x-www-form-urlencoded). This information is only used with HTML forms. The version of the CGI specification that the server is running. The extra path information specified on the URL. e.g. This URL sends /extra/path/info to the sample CGI program: http://www.usi.utah.edu/bin/cgiprogramming/env.sh/extra/path/info Contains the absolute path from the root directory of the server to the directory defined by the extra information added

CONTENT_LENGTH

CONTENT_TYPE

GATEWAY_INTERFACE

PATH_INFO

PATH_TRANSLATED

from PATH_INFO. QUERY_STRING REMOTE_ADDR REMOTE_HOST Information which follows the ? in a URL. The information is encoded using the data encoding rules described above. The IP address of the remote host or browser. The fully qualified domain name of the requesting client. If the name is not available, this variable is not set. Set to the remote user name, however both systems need to support IDENTD if the server is to retieve the user name. This contains the user name that was given in response to a Unauthorised Access (401) status line. The HTTP method being used to send client data to the server. The possible methods are GET and POST. This information is only used with HTML forms. The virtual path to the CGI program. Useful for scripts that must reference themselves in generated URLs. The hostname or IP address of the HTTP server machine (The machine your CGI program is running on.) The TCP/IP port number the server has open and is accepting connections on. The name and version of the protocol being used by the client to communicate with the server. The name and version of the HTTP server software your CGI program is running under.

REMOTE_INDENT

REMOTE_USER

REQUEST_METHOD

SCRIPT_NAME

SERVER_NAME SERVER_PORT SERVER_PROTOCOL

SERVER_SOFTWARE

There is nothing particularly special about environmental variables; they can be used like any other variable, and assigned to other variables with more meaningful names. In Perl we might use code similar to that below to read in data from a form that used the request method GET (GET data enters the CGI via the QUERY_STRING environmental variable).

$buffer = $ENV{'QUERY_STRING'};

This puts the contents of QUERY_STRING into another variable $buffer for later manipulation. All you wanted to know about adding a counter to your site but were afraid to ask! This is a counter that I and many other webmasters have and still use and is a relatively simple one to implement on your site. The only assumptions I make here are that you have a mediocrum of common sense to follow simple instructions. It will not only count how many accesses you are getting but also create a log file of all visits. The only disadvantage is that if your visitors have Auto load Images switched off the counter will not be advanced.

Now for the Code!


-------------------------------------------------cut here-----------------------------------------------------------------#!/usr/bin/perl # counter.cgi -- counts Web page hits # the first line of this script may have to be changed # if your system's Perl interpreter is not located in /usr/bin/perl # Written by P. Lutus Ashland, Oregon lutusp@mind.net 2/21/96 # This is a cute, small, serviceable hits counter program. It uses the x-bitmap format # which is monochrome. It won't look very good if the textcolor and background are the same color # and the browser is made by Netscape. Every other place and browser, OK. # # Call the counter like this: You are Visitor Number <IMG src="counter.cgi?Ownername"> # Ownername can have particular web page designators attached to it like this: Ownername_mypagename # This allows you to count pages independently. # The directory in which counter.cgi is stored must have world read, write and execute access. # The file counter.cgi must have world execute access. # $min_width is the minimum number of digits to print -# the program will always print more as the number grows larger $min_width = 2; # set $collect_stats = 1 if you want to log the origins of accesses

# in a file named Ownername.log $collect_stats = 1; $countname = "counter_dat"; # a default name for the counter file # This is a reasonable character set, sort of blocky but OK $counter_width = 16; $counter_height = 20; @dig_arr = ( '0x00', '0x00', '0x1B', '0x0E', '0x06', '0x18', '0x1C', '0x0E', '0xFC', '0x0F', '0x00', '0x00', '0x18', '0x00', '0x00', '0x18', '0x1C', '0x00', '0x00', '0x00', '0x00', '0x00', '0x1B', '0x00', '0xF0', '0x1B', '0x00', '0x0E', '0xFC', '0x0F', '0x00', '0x00', '0x1B', '0x00', '0xF0', '0x1B', '0x1C', '0x00', '0xFC', '0x0F', '0x00', '0x00', '0x18', '0x0E', '0xF6', '0x1B', '0x1C', '0x00', '0x00', '0x00', '0x00', '0x00', '0x03', '0x0E', '0xF6', '0x03', '0x1C', '0x00', '0xFC', '0x0F', '0x00', '0x00', '0x03', '0x0E', '0xF6', '0x03', '0x1C', '0x0E', '0xFC', '0x0F', '0x00', '0x00', '0x1B', '0x00', '0x00', '0x18', '0x1C', '0x00', '0x00', '0x00', '0x00', '0x00', '0x1B', '0x0E', '0xF6', '0x1B', '0x1C', '0x0E', '0xFC', '0x0F', '0x00', '0x1C', '0x00', '0x1C', '0x00', '0x00', '0x1C', '0x00', '0x1C', '0x00', '0x00', '0x1C', '0xF8', '0x00', '0x00', '0x00', '0x1C', '0xF8', '0x1C', '0x00', '0x00', '0x1C', '0xF8', '0x1C', '0x00', '0x00', '0x00', '0xF8', '0x1C', '0x00', '0x00', '0x00', '0xF8', '0x1C', '0x00', '0x00', '0x1C', '0x00', '0x1C', '0x00', '0x00', '0x1C', '0xF8', '0x1C', '0x00', '0x00', '0x0E', '0x00', '0x0E', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x07', '0x0E', '0x00', '0x00', '0x00', '0x07', '0x00', '0x00', '0x00', '0x0E', '0x07', '0x00', '0x00', '0x00', '0x0E', '0x07', '0x00', '0x00', '0x00', '0x0E', '0x07', '0x0E', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x00', '0x0E', '0x07', '0x0E', '0x00', '0xFC', '0x1C', '0x06', '0x1C', '0x00', '0x1C', '0x00', '0x1C', '0xFC', '0x1C', '0xF6', '0x00', '0xFC', '0x1C', '0xF0', '0x1C', '0x00', '0x1C', '0xF0', '0x1C', '0xFC', '0x00', '0xF0', '0x1C', '0xFC', '0x00', '0xF6', '0x1C', '0xFC', '0x1C', '0x00', '0x1C', '0xFC', '0x1C', '0xF6', '0x1C', '0x0F', '0x0E', '0x18', '0xF6', '0x00', '0x00', '0x18', '0x00', '0x0F', '0x00', '0x03', '0xF6', '0x0F', '0x00', '0x1B', '0xF0', '0x00', '0x0E', '0x1B', '0x00', '0x0F', '0x0E', '0x1B', '0xF0', '0x0F', '0x0E', '0x1B', '0xF6', '0x0F', '0x00', '0x18', '0x00', '0x0F', '0x0E', '0x1B', '0xF6', '0xFA', '0x1C', '0x0E', '0x1B', '0x00', '0x1C', '0x00', '0x18', '0xF8', '0x1C', '0x0E', '0x03', '0xF8', '0x1C', '0x00', '0x1B', '0x02', '0x1C', '0x00', '0x18', '0xFA', '0x00', '0x00', '0x1B', '0xFA', '0x00', '0x0E', '0x1B', '0xF8', '0x1C', '0x00', '0x18', '0xFA', '0x1C', '0x0E', '0x1B', '0x17', '0x0E', '0x1C', '0xFA', '0x10', '0x00', '0x1C', '0x00', '0x17', '0x00', '0x00', '0xFA', '0x17', '0x00', '0x1C', '0xF8', '0x10', '0x0E', '0x1C', '0x00', '0x07', '0x0E', '0x1C', '0xF8', '0x07', '0x0E', '0x1C', '0xFA', '0x17', '0x00', '0x1C', '0x00', '0x17', '0x0E', '0x1C', '0xFA', '0xF6', '0x1C', '0x0E', '0x17', '0x00', '0x1C', '0x00', '0x10', '0xF0', '0x1C', '0x0E', '0x07', '0xF0', '0x1C', '0x00', '0x17', '0x06', '0x1C', '0x00', '0x10', '0xF6', '0x00', '0x00', '0x17', '0xF6', '0x00', '0x0E', '0x17', '0xF0', '0x1C', '0x00', '0x10', '0xF6', '0x1C', '0x0E', '0x17',

'0x00', '0x1B', '0xF6', '0x1C', '0xFC', );

'0x00', '0x0E', '0x1B', '0x00', '0x0F',

'0x00', '0x1C', '0xF8', '0x1C', '0x00',

'0x00', '0x0E', '0x07', '0x00', '0x00',

'0xFC', '0x1C', '0xF0', '0x1C',

'0x0F', '0x0E', '0x1B', '0xF0',

'0xFA', '0x1C', '0x00', '0x1B',

'0x17', '0x0E', '0x1C', '0xF8',

'0xF6', '0x1C', '0x00', '0x17',

@digits = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); # So? Anything is possible! @envname = ( 'REMOTE_ADDR', 'REMOTE_HOST', 'HTTP_REFERER', 'HTTP_USER_AGENT', 'HTTP_ACCEPT', ); if ($ENV{COMSPEC} ne '') { # most definitely MSDOS, so use those old funky names @envname = keys(%ENV); } if ($ENV{'QUERY_STRING'} ne '') { $countname = $ENV{'QUERY_STRING'}; # get the file name from counter.cgi?ownername construct } unless (-e $countname) { # unless this file already exists open (COUNT,">$countname"); # directory has to have world write permission for this to work print COUNT "0\n"; close COUNT; } $count = 0; # now get and increment the counter open (COUNT,$countname) || die "Content-type:text/plain\n\nCan't open $countname!\n"; $count = <COUNT>; close COUNT; $count++; open (COUNT,">$countname"); # I don't lock access so this might break or not count right in busy sites print COUNT "$count\n"; # but locking has problems of its own ... close COUNT; if ($collect_stats) { # owner want to collect data on logons $logname = $countname . ".log"; unless (-e $logname) { # unless this file already exists open (LOG,">$logname"); # directory has to have world write permission for this to work print LOG "Count\tTime"; foreach $envlbl (@envname) { print LOG "\t$envlbl"; }

print LOG "\n"; close LOG; } # done initializing stats file open (LOG,">>$logname"); # now enter this stat record $tim = &readtime; print LOG "$count\t$tim"; foreach $envlbl (@envname) { print LOG "\t$ENV{$envlbl}"; } print LOG "\n"; close LOG; } # end of collect_stats block $size = &fill_digits($count); # now show the actual count &print_digits($size); exit; sub fill_digits { $q = @_[0]; $i = 0; do { $digits[$i++] = $q % 10; $q = int($q/10); } while(($q != 0) || ($i < $min_width)); # force width to be at least $min_width $i = $i; # make it the return value } sub print_digits { $width = @_[0]; $cw = $width * $counter_width; $chh = $counter_height * 2; print "Content-type:image/x-xbitmap\n\n"; print "#define counter_width $cw\n"; print "#define counter_height $counter_height\n"; print "static unsigned char counter_bits[] = {\n"; $start = 1; for($i=0;$i<$chh;$i=$i + 2) { for($j=$width-1;$j>=0;$j--) { for($k=0;$k<2;$k++) { if ($start == 0) { print","; } print "$dig_arr[($digits[$j]*$chh)+$i+$k]"; $start = 0; } } print "\n"; } print "};\n"; } sub readtime { local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);

sprintf ("%2.0f/%02.0f/%04.0f %2.0f:%02.0f:%02.0f",$mon+1,$mday, ($year > 50)?$year+1900:year+2000,$hour,$min,$sec); # to the stack } -------------------------------------------------cut here------------------------------------------------------------------

Cut n paste the above code and save it as a separate file called counter.cgi. To get this script working, you must do the following (I am assuming that you are using WS_FTP): 1/ Make a directory on the server called cgi-xploiter. This is done by clicking on the MKDIR button and typing the name in. I would suggest calling it cgi-xploiter for simplicity. 2/ Copy the counter.cgi file into this directory. Highlight the file using the mouse and RIGHT click on it. A list will then magically appear and you need to select the one marked CHMOD (unix). Check all the boxes that appear. This will have CHMODDED the file to 777. It is very important that you change the upload method to ASCII and not BINARY else it wont work. If you have an older version of WS_FTP than 4.01 then you will not see the CHMOD (unix) bit. You need to click on FTP Commands, select SITE and type in chmod 777 counter.cgi then click on OK. 3/ Come out of that directory and perform stage 2 again but this time on the cgi-xploiter directory instead. 4/ To call the counter, the following code will need to be placed on the page(s). The following syntax assumes you are using the Enterprise servers. You will need to change it to suit your own needs. <CENTER>This Page Has Been Accessed</CENTER> <CENTER><TABLE WIDTH=10% BORDER=5> <TR> <TD BGCOLOR=WHITE><IMG src=http://homepages.enterprise.net/your_login_name/cgi-xploiter/counter.cgi> </TD> </TR> </TABLE></CENTER> <CENTER>Times Since 13th January 1997</CENTER> 5/ Goto http://domain/YOURHOSTNAME/YOURPAGE TO BE COUNTED, and assuming you have done everything correctly, a counter should appear at the botttom of the page. If it does not work 1st time check that you have chmodded all the relevant files correctly and that you uploaded the cgi file in ascii mode. Any probs let me know. The log file will be called counter_dat.log and is located in the same directory as the counter file itself.

Hopefully you can now get this counter to work ok. Good Luck

CHMOD! What the hell is it?


The majority of servers on the net run an operating system called UNIX. Now I am not going to tell you all about this glorious OS here as well as many more sites on the net that can do it much better than I could ever hope to. What I will try to explain is why a perl script will not run on the server unless you set the correct permissions. This is where CHMOD comes in. Each and every file and directory that is located on a UNIX system has attributes set. There are three such permissions on UNIX: Read permission means you can look at a files contents. Write permission means you can change or delete the file. Execute permission means you can run the file as a program. If the permisions are set on a file to +r +w +x, then that file can be read, written to and executed by anyone. For obvious reasons this is not a good idea as anyone with a malicious streak could come along and delete all your hard work. The other problem with this rwx strategy is that some FTP clients such as WS_FTP do not, (as far as I know) recognise them. Instead you have to enter the CHMOD command instead. This command is used as follows: chmod 777

showme.cgi Explained You have uploaded a file called showme.cgi and you wish to make it readable, writable and executable by everybody. You would enter the command as above. But hold on! Where did the 777 come from? Well as you are using the CHMOD command, the letters have no real relevance. Instead a number system is employed as is shown below.

You might also like