Programing in Perl
Programing in Perl
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.
There are of course many other applications such as shopping baskets, image rotators, chat pages etc.
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.
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.
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_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.
# 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',
@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
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.