#!/usr/local/bin/perl
#
# Copyright (c) 1999 by Per Cederberg
#
# The code in this file is made freely available for any purpose
# whatsoever. The authors do not take any responsability for the
# correctness or suitability for some particular purpose.
#
######################################################################
#
# Contains common subroutines and constants for CGI scripts.
#
# Authors: Per Cederberg, per at percederberg dot net
# Version: 1.2.2
# Date: 17th of January 2000
#
#
# Description:
# ------------
# Contains common subroutines and global constants for CGI scripts.
#
#
# Revision history:
# -----------------
#
# Version 1.2.2 (2000-01-17, Per Cederberg)
# - Corrected bug with BASEURL ending with an '/'.
#
# Version 1.2.1 (1999-09-26, Per Cederberg)
# - Changed TEMPLATEDIR and removed TEMPLATEURL.
#
# Version 1.2 (1998-11-17, Per Cederberg)
# - Changed all directory constants.
# - Added TEMPLATEDIR and TEMPLATEURL.
# - Removed ICONDIR and ICONURL.
# - Removed BACKGROUNDDIR and BACKGROUNDURL.
# - Added CollectFields routine.
#
# Version 1.1.2 (98-06-24, Per Cederberg)
# - Added optional HTML codes to CreateHTMLImage.
# - Added optional HTML codes to CreateHTMLLink.
#
# Version 1.1.1 (98-06-23, Per Cederberg)
# - Added ICONURL and BAKGROUNDURL constants.
#
# Version 1.1 (98-06-22, Per Cederberg)
# - Changed implentation of FileExists to use -e operator.
# - Added Directory Exists routine.
# - Added FindMatchingFiles routine.
# - Changed to use COMMONFILE as the file variable everywhere.
# - Added ReadDataFile and WriteDataFile routines.
# - Added ReadAccessCount and IncreaseAccessCount routines.
#
# Version 1.0.1 (98-06-18, Per Cederberg)
# - Corrected spelling bug in PrintErrorMessage.
# - Added the FileExists routine.
# - Added the LockFile and UnlockFile routines.
#
# Version 1.0 (98-06-16, Per Cederberg)
# - First version of this script.
#
######################################################################
######################################################################
#
# GLOBAL CONSTANTS AND VARIABLES
#
###### Constants for directories ######
$BASEDIR = "/home/d93/d93-pol/public_html";
$CGIDIR = "/public/www.student/cgi-bin/d93-pol";
$TEMPLATEDIR = "$CGIDIR/templates";
###### Constants for URLs ######
$BASEURL = "http://www.student.nada.kth.se/~d93-pol";
$CGIURL = "http://cgi.student.nada.kth.se/cgi-bin/d93-pol";
###### Other global constants ######
$MAILPROG = '/usr/lib/sendmail'; # The sendmail program
$CURDATE = 'No value yet'; # Current date, '1998-06-10' for example
$CURTIME = 'No value yet'; # Current time, '13:39' for example
$CURCENTURY = 'No value yet'; # Current century, '19' or '20'
###### Global variables ######
$method = 'No value yet'; # The form method, 'GET' or 'POST'
%form = {}; # The form data exchange variable
$errors = 0; # The number of errors found in the form
%formerror = {}; # The errors for each field in the form
######################################################################
#
# INITIALIZING
#
#
# Initialize
#
# Initializing CURDATE, CURTIME and CURCENTURY. This routine is
# executed once upon loading this script.
#
sub Initialize
{
local ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
if ($year gt '95') {
$CURCENTURY = "19"
} else {
$year = $year - 100;
$CURCENTURY = "20";
}
$CURDATE = sprintf("%.2d%.2d-%.2d-%.2d", $CURCENTURY, $year, $mon+1,
$mday);
$CURTIME = sprintf("%.2d:%.2d", $hour, $min);
}
&Initialize();
######################################################################
#
# INPUT ROUTINES
#
#
# ParseInput
#
# Parses the input sent via the CGI standard. The global variables
# $method and %form will be set to new values when executing this
# function. Each form field in the input will be found in the form
# variable as a key with value. On multiple value for a single field
# the values are concatenated with '\0' characters in the same string.
#
# The special input format in the CGI standard is handled and
# converted to normal Perl strings. Also some substitutions of
# characters with a special meaning in HTML is done.
#
sub ParseInput
{
local ($buffer, @pairs);
# Read input
if ($ENV{'REQUEST_METHOD'} eq 'POST')
{
$method = 'POST';
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
}
else
{
$method = 'GET';
@pairs = split(/&/, $ENV{'QUERY_STRING'});
}
# Parse input
%form = {};
foreach (@pairs)
{
local ($name, $value) = split(/=/, $_);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
$value =~ s/&/&/g;
$value =~ s/</g;
$value =~ s/>/>/g;
$value =~ s/\"/"/g;
# $value =~ s/å/å/g;
# $value =~ s/Å/Å/g;
# $value =~ s/ä/ä/g;
# $value =~ s/Ä/Ä/g;
# $value =~ s/ö/ö/g;
# $value =~ s/Ö/Ö/g;
$form{$name} .= "\0" if (defined $form{$name} && $value ne '');
$form{$name} .= $value;
}
return %form;
}
#
# RevertSubstitutions
#
# Reverts some of the imput substitutions made in the ParseInput
# function, ie. converts some HTML sequences to single characters.
#
# Arguments:
# string - the string containing the HTML special sequences
#
# Returns:
# A string without the HTML sequences created in the input parsing.
#
sub RevertSubstitutions
{
local ($string) = @_;
$string =~ s/"/\"/g;
$string =~ s/<//g;
$string =~ s/&/&/g;
return $string;
}
######################################################################
#
# LIST HANDLING ROUTINES
#
#
# PosInList
#
# Returns the position of a string in a given list.
#
# Arguments:
# string - the string to search for
# list - the list to search in
#
# Returns:
# An index in the list (from zero), or -1 if the string wasn't found.
#
sub PosInList
{
local ($string, @list) = @_;
local ($index);
for ($index = 0; $index <= $#list; $index++)
{
if ($list[$index] eq $string)
{
return $index;
}
}
return -1;
}
#
# MergeLists
#
# Merges several lists into one, removing copies and empty strings.
#
# Arguments:
# list1, list2, ... - the lists to merge
#
# Returns:
# A list with all the distinct elements from the given lists.
#
sub MergeLists
{
local (@list) = @_;
local ($i, @new);
for ($i = 0; $i <= $#list; $i++)
{
if (&PosInList($list[$i],@new) == -1 && $list[$i] ne '')
{
$new[$#new+1] = $list[$i];
}
}
return @new;
}
#
# RemoveElement
#
# Removes all occurrencies of an element from a list.
#
# Arguments:
# elem - a list element
# list - a list
#
# Returns:
# A list where there are no occurrencies of the given element.
#
sub RemoveElement
{
local ($elem, @list) = @_;
local ($pos);
while (($pos = &PosInList($elem, @list)) > -1)
{
splice(@list, $pos, 1);
}
return @list;
}
#
# SumList
#
# Sums all elements in a list. The elements has to be valid numbers.
#
# Arguments:
# list - a list with numbers
#
# Returns:
# The sum of all the numbers in the list.
#
sub SumList {
local (@list) = @_;
local ($i, $sum);
$sum = 0;
for ($i = 0; $i <= $#list; $i++)
{
$sum += $list[$i];
}
return $sum;
}
#
# WrapList
#
# Returns a new list where each element is "wrapped up" into some
# template element.
#
# Arguments:
# tag - a tag in the template string (where to put each list elem)
# string - a template string
# list - a list
#
# Returns:
# A list where each element is of the template form but with the
# original list element inserted on each occurrence of the tag.
#
sub WrapList
{
local ($tag, $string, @list) = @_;
local ($temp, @new);
foreach (@list)
{
$temp = $string;
$temp =~ s/$tag/$_/g;
$new[$#new+1] = $temp;
}
return @new;
}
######################################################################
#
# LIST TO AND FROM FORM ROUTINES
#
#
# CollectFields
#
# Collects all form fields with a given name base into an
# ordered list based on the field numbers.
#
# Arguments:
# fieldname - a base field name
#
# Returns:
# A list with all the field values in order from the lowest
# numbered field to the highest.
#
sub CollectFields
{
local ($fieldname) = @_;
local ($i, @res);
$i = 1;
@res = ();
while ($form{"$fieldname$i"} ne '')
{
$res[$#res+1] = $form{"$fieldname$i"};
$i++;
}
return @res;
}
######################################################################
#
# STRING MANIPULATION ROUTINES
#
#
# ChopWhitespace
#
# Removes trailing whitespaces on each line in a string, but keeps
# the number of newlines.
#
# Arguments:
# string - a string, possibly consisting of several lines
#
# Returns:
# A string where all the trailing whitespace characters have
# been removed for each line.
#
sub ChopWhitespace
{
local ($string) = @_;
local ($result, $lastchar, $newlinecount, @lines);
# Count the number of newlines
$newlinecount = 0;
while ($string =~m/\n/g) {
$newlinecount++;
}
# Chop off all whitespace
@lines = split(/\n/, $string);
foreach (@lines)
{
if (length($_) > 0)
{
$lastchar = chop($_);
while (length($_) > 0 && $lastchar =~ m/[ \n\r\t\v]/)
{
$lastchar = chop($_);
}
if ($lastchar =~ m/[ \n\r\t\v]/)
{
$lastchar = '';
}
$result .= "$_$lastchar\n";
}
else
{
$result .= "\n";
}
}
chop ($result); # To remove last newline added
# Add missing newlines
while ($result =~ m/\n/g)
{
$newlinecount--;
}
while ($newlinecount > 0)
{
$result .= "\n";
$newlinecount--;
}
return $result;
}
#
# SplitLine
#
# Splits a string on a single line into a string containing
# several lines. The trailing whitespace characters on each
# line will be removed with ChopWhitespace.
#
# Arguments:
# line - a string, possibly with newline characters
# maxlen - a value for the maximum line lenght
#
# Returns:
# A string where no line exceeds the maximum line length.
#
sub SplitLine
{
local ($string, $maxlen) = @_;
local (@lines, $result, $index, $lastchar);
$string = &ChopWhitespace($string);
if (length($string) <= $maxlen)
{
return $string
}
@lines = split(/\n/, $string);
foreach (@lines)
{
while (length($_) > $maxlen)
{
$index = rindex($_, " ", $maxlen-1);
if ($index < 1)
{
$result .= substr($_, 0, $maxlen-1);
$result .= "-\n";
$_ = substr($_, $maxlen-1);
}
else
{
$result .= substr($_, 0, $index+1);
$result .= "\n";
$_ = substr($_, $index+1);
}
}
$result .= "$_\n";
}
return $result;
}
#
# Linkify
#
# Detects URIs in a string and replaced them with HTML hyperlinks.
#
# Arguments:
# string - a string
#
# Returns:
# A string where all URI are linked to their respective address.
#
sub Linkify
{
local ($string) = @_;
local (@links);
@links = ($string =~ m/http:[^ \n\r\t\v]+/g);
@links = &MergeLists(@links); # To remove unneccesary copies
foreach (@links)
{
$string =~ s|$_|$_|g;
}
return $string;
}
######################################################################
#
# HTML CREATION ROUTINES
#
#
# CreateHTMLLink
#
# Creates a HTML link.
#
# Arguments:
# url - the URL to link to
# text - the link text
# extra - optional extra arguments to the A tag
#
# Returns:
# A string containing the HTML hyperlink code.
#
sub CreateHTMLLink
{
local ($url, $text, $extra) = @_;
local ($result);
$extra = " $extra" if ($extra ne '');
$result = "$text\n";
return $result;
}
#
# CreateHTMLImage
#
# Creates a HTML image insertion.
#
# Arguments:
# src - the source of the image
# text - the alternate text, can be left empty
# extra - optional extra arguments to the IMG tag
#
# Returns:
# A string containing the HTML image code.
#
sub CreateHTMLImage
{
local ($src, $text, $extra) = @_;
local ($result);
$result = " or
# missing.
#
sub CreateHTMLList
{
local ($emptymsg, @list) = @_;
local ($htmllist);
if ($list[0] eq 0)
{
return 0;
}
if (@list)
{
foreach (@list)
{
$htmllist .= "
$_
\n";
}
}
else
{
$htmllist = "
$emptymsg
\n";
}
return $htmllist;
}
#
# CreateHTMLOptions
#
# Returns a html option list from a given list.
#
# Arguments:
# default - the default option
# list - a list of options
#
# Returns:
# A string containing HTML code for the options.
#
sub CreateHTMLOptions
{
local ($default, @list) = @_;
local ($options);
if ($list[0] eq 0)
{
return 0;
}
$options = "\n";
foreach (@list)
{
if ($default eq $_)
{
$options .= "