Приглашаем посетить
Page Control
#!/usr/local/bin/perl
$NOT = $NO = 0;
$ONLY = $YES = 1;
############################################################################
# #
# PageControl Version 2.0 #
# Written by Craig A. Patchett craig@patchett.com #
# Created 3/12/96 Last Modified 12/10/96 #
# #
# Copyright 1997 Craig Patchett & Matthew Wright. All Rights Reserved. #
# This program is part of The CGI/Perl Cookbook from John Wiley & Sons. #
# License to use this program or install it on a server (in original or #
# modified form) is granted only to those who have purchased a copy of The #
# CGI/Perl Cookbook. (This notice must remain as part of the source code.) #
# #
############################################################################
############################################################################
# Define configuration constants #
############################################################################
# @NO_COUNT_HOSTS holds a list of host names or addresses that will not be
# included in the visitor counts. Partial names and addresses are fine (i.e.
# somewhere.com will match somebody.somewhere.com and 111.222.333. will
# match 111.222.333.444).
@NO_COUNT_HOSTS = ('mydomain.com');
# If $ALLOW_HOSTS equals $NOT (or 0) then any hosts in @ALLOW_HOSTS will
# be barred from accessing pages. Otherwise ONLY hosts in @ALLOW_HOSTS
# will be allowed access.
$ALLOW_HOSTS = $NOT;
@ALLOW_HOSTS = ('badhost.com', 'badguy@domain.com', '100.100.100');
# $AUTH_CHECK should be set to $YES or $NO depending on whether or not you
# want the program to check to see if the visitor is currently authenticated.
# If not, the program will automatically call the Authenticate program
# provided elsewhere in this book.
$AUTH_CHECK = $YES;
# $REPEAT_LIMIT is the minimum time in seconds that must separate visits from
# the same host in order for them to be considered unique
$REPEAT_LIMIT = 30;
# $LOG_DIR is the full path to the directory where your log files will be
# kept (Note that it should include a directory delimiter at the end.)
$LOG_DIR = '/home/web/pagecontrol/logs/';
# $BASE_DIR is the full path to the root directory from which pages can
# be accessed. (Note that it should include a directory delimiter at the
# end.)
$BASE_DIR = '/home/protected/pages';
# $RELATIVE_URL is the URL for the root directory for relative links, usually
# $BASE_DIR
$RELATIVE_URL = 'http://www.domain.com/pctest';
# $LOCK_DIR is the full path to the directory where all lock files will be
# placed (Note that it should include a directory delimiter at the end.) If
# you're using a UNIX web server with flock it must be a directory outside
# the web space, i.e. '/tmp/'
$LOCK_DIR = '/tmp/';
# $LOG_FILE is the name of the main access log file. It will be created
# and updated in $LOG_DIR.
$LOG_FILE = 'access_log';
# $S is the character(s) that will be used to separate fields in the log
# file. It has a short name to make the code easier to read
# (Warning: Don't change this once you've started using the program
# without updating any existing log files)
$S = "\t";
# $LOG_HEADER is an optional field name header for the log file. It will
# only be written when the file is first created
$LOG_HEADER = 'user_id' . $S . 'remote_addr' . $S . 'host_name' . $S .
'visit_time' . $S . 'page_name' . $S . 'refer_page' . $S .
'browser' . $S . 'error_code\n';
# $LINK_INFO holds the default format for displaying link statistics.
# $LINK_COUNT_NONE holds the default message for links that have not been
# accessed yet.
$LINK_INFO = '(<<COUNT>> accesses since <<FIRST>>)';
$LINK_COUNT_NONE = '(No accesses yet)';
# $DATE_FORMAT holds the default format for link dates (in &format_date()
# form)
$DATE_FORMAT = '<m>/<d>/<yr>';
# $LOCAL_FORMAT holds the &format_date() format for the local time
# $VISIT_FORMAT holds the &format_date() format for various visit dates (first,
# last, etc.) $MOD_FORMAT holds the &format_date() format for the file
# modification time
$LOCAL_FORMAT = 'Local time is <weekday>, <month> <d>, <year> at <time>';
$VISIT_FORMAT = '<month> <d>, <year>';
$MOD_FORMAT = '<month> <d>, <year> at <time>';
# $AUTH_URL is the URL for the Authenticate program you wish to use
# for authenticating visitors. If you choose not to use the authentication
# option then this should be set to ''.
$AUTH_URL = 'http://www.domain.com/cgi-bin/authenticate.cgi';
# $AUTH_DIR is the full path to the directory where your authentication
# files will be kept. (Note that it should include a directory delimiter at
# the end.)
$AUTH_DIR = '/home/protected/track/';
# $MAX_AGE is the maximum age in minutes since it was last accessed that a
# file in $AUTH_DIR should still be considered valid.
$MAX_AGE = 10;
# $NOT_ALLOWED_URL is the URL of the file to link to if the current host is
# not allowed access. If it is left undefined then a default message will be
# shown instead.
$NOT_ALLOWED_URL = '';
# $MAX_WAIT is the maximum number of seconds the lock program will wait for a
# locked file to become unlocked
$MAX_WAIT = 5;
# $NAME_LEN is the maximum filename length in characters (not including
# extensions)
$NAME_LEN = 32;
# $ERROR_PAGE is the full path to the HTML file that contains the template
# for an error message. This message should contain '<<ERROR_TITLE>>',
# '<<ERROR_NAME>>', and '<<ERROR_MSG>>' (without quotes) wherever you want
# the page title, error name, and error message to appear.
$ERROR_PAGE = '/home/web/error.html';
# $PERMISSIONS is the type of permissions you want to give to the files
# the program creates. They are expressed in standard UNIX octal format
# (i.e. 0666). If your server is running on a system that doesn't support
# file permissions then you should set $PERMISSIONS to 0.
$PERMISSIONS = 0666;
# $REQUIRE_DIR is the directory in which all of your required files are
# placed. On most systems, if you leave the required files in the same
# directory as the CGI program, you can leave this variable blank.
# Otherwise, if you move the required files to another directory, specify
# the full or relative path here.
$REQUIRE_DIR = 'require';
############################################################################
# Get required subroutines which need to be included. #
############################################################################
# Push $REQUIRE_DIR onto the @INC array for include file directories
# and list required files.
push(@INC, $REQUIRE_DIR) if $REQUIRE_DIR;
require 'addrhost.pl';
require 'error.pl';
require 'formdate.pl';
require 'locksubs.pl';
require 'template.pl';
require 'ipconvrt.pl';
require 'authchck.pl';
############################################################################
# Initialize other constants #
############################################################################
$PROGRAM_URL = "http://$ENV{'SERVER_NAME'}$ENV{'SCRIPT_NAME'}";
@DAYS = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday');
@MONTHS = ('January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December');
$DAY_SECS = 86400;
############################################################################
# Determine current date and time #
############################################################################
$page_time = time;
$VAR{'DATENOW'} = &format_date($page_time, $LOCAL_FORMAT);
($page_year, $page_year_day) = (localtime($page_time))[5,7];
############################################################################
# Call the authentication routine if the authentication option is set #
############################################################################
if ($AUTH_CHECK) {
($VAR{'USER_ID'}, $VAR{'PREVVISIT'}) = &auth_check(1);
$VAR{'PREVVISIT'} = &format_date($VAR{'PREVVISIT'}, $MOD_FORMAT);
}
############################################################################
# Set the requested page based on how the program was called #
############################################################################
if ($ENV{'DOCUMENT_URI'}) { # Called as SSI
$page_name = $ENV{'DOCUMENT_URI'};
$ssi=$YES;
}
else { $page_name = $ENV{'PATH_INFO'} }
$request = $page_name = substr($page_name, 1);
############################################################################
# Check to see if this is a request for a link and parse accordingly #
############################################################################
if ($page_name =~ m|^(.+)/link:(.+)|) {
# This is an internal link
$link_file = $1;
$link_name = $2;
$link_name =~ tr/+/ /;
$link_request = 1;
}
elsif ($page_name =~ s|^([a-zA-Z]+:/)(.+)|$1/$2|) {
# This is a URL for an external link
$requested_link = $page_name;
$link_request = 2;
}
elsif ($page_name !~ m|\.s?html?(\?[^.]*)*$|i) {
# This is a relative external link
$requested_link = $page_name;
$link_request = 3;
}
elsif ($page_name =~ m|^tmp:(.+)|) {
# This is a temporary page
$page_name = $1;
$temporary_page = $YES;
}
############################################################################
# If this is an html page make sure it exists and is readable #
############################################################################
if (!$link_request) {
# Set the page path
$page_path = "$BASE_DIR$page_name";
# Check to see if it's a valid, readable file
if (!(-f $page_path)) {
$error_msg = "$page_name is an invalid file name ($!).";
$VAR{'USER_ID'} = '*';
$access_error = 2;
}
elsif (!(-r $page_path)) {
$error_msg = "$page_name is not readable ($!).";
$VAR{'USER_ID'} = '*';
$access_error = 3;
}
elsif (!open(PAGE, $page_path)) {
$error_msg = "$page_name could not be opened ($!).";
$VAR{'USER_ID'} = '*';
$access_error = 4;
}
############################################################################
# Read the requested page into memory and check for options near the top #
############################################################################
for ($line = 1; <PAGE>; ++$line) {
if (($line < 3) && /<!-- *PAGE OPTIONS/i) {
# Check for name of count file
if (/COUNT="([^"]+)"/i) { $count_file = $1 }
# Check for default statistics footer switch
if (/FOOTER(="([^"]+)")?/gi) {
$footer_file = $2;
$footer = $YES
}
# Check for link count switch
if (/LINKCOUNT/i) { $link_count = $YES }
# Check for link count format specification
if (/LINKINFO="([^"]+)"/i) { $link_info = $1 }
# Check for variable substitution
if (/VARIABLES/i) { $variables = $YES }
}
else { push (@page_data, $_) }
}
close(PAGE);
# If this is a temporary page then delete it
if ($temporary_page) {
unlink($page_path) || &error("$page_name could not be deleted ($!).");
}
############################################################################
# Set the count and link file names if not set already #
############################################################################
# Check first to see if a count file name was specified in the page file
if (!$count_file) {
# If not, remove directory separators and periods from $page_path
$count_file = $page_name;
$count_file =~ tr/\/\\:\.//d;
# Shorten the name if necessary and add the directory info and
# extension
if ($NAME_LEN && ($NAME_LEN < length($count_file))) {
$count_file = substr($count_file, -$NAME_LEN);
}
$count_file .= '.log';
}
$count_path = "$LOG_DIR$count_file";
# Set the link file name
$link_file = $count_file;
$link_file =~ s/\.[^.]*$//;
$link_file .= '.lnk';
############################################################################
# Check to see if we should ignore this host for counting purposes #
############################################################################
foreach $host (@NO_COUNT_HOSTS) {
if (($ENV{'REMOTE_HOST'} =~ /$host/i) || ($ENV{'REMOTE_ADDR'} =~ /$host/)) {
$ignore = $YES;
last;
}
}
}
############################################################################
# Read in any link information for this page #
############################################################################
# Set link file path
$link_path = "$LOG_DIR$link_file";
# If this is a link and the link file doesn't exist, set up an error message
if (($link_request == 1) && !(-e $link_path)) {
$error_msg = "\"$link_file\" is not a valid link file.";
$VAR{'USER_ID'} = '*';
$access_error = 6;
}
elsif ($link_request < 2) {
if (&lock($link_file, $LOCK_DIR, $MAX_WAIT)) { &error("$Error_Message") }
if (!(-e $link_path) || (-z $link_path)) {
open(LINK, ">>$link_path") || &error("Could not create $link_file ($!).");
chmod $PERMISSIONS, $link_path if $PERMISSIONS;
print LINK "$page_name\n";
}
else {
open(LINK, $link_path) || &error("Could not open $link_file ($!).");
# Make sure the link file's page name matches the current page
$file_page = <LINK>;
chop($file_page) if $file_page =~ /\n$/;
if ($link_request) {
$page_name = $file_page;
}
if ($file_page ne $page_name) {
if (&unlock($link_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("The link file names for $page_name and $file_page are the same.");
}
# Read the link data into memory
while ($link = <LINK>) {
chop($link);
chop($link_names{$link} = <LINK>);
chop($link_titles{$link} = <LINK>);
chop($link_count{$link} = <LINK>);
chop($link_added{$link} = <LINK>);
chop($link_first{$link} = <LINK>);
chop($link_last{$link} = <LINK>);
$links{$link_names{$link}} = $link;
}
}
close(LINK);
if (&unlock($link_file, $LOCK_DIR)) { &error("$Error_Message") }
# If this is a link request, make sure it's a valid link name
if ($link_request) {
$requested_link = $links{$link_name};
if (!$requested_link) {
$error_msg = "\"$link_name\" is not a valid link name for this page.";
$VAR{'USER_ID'} = '*';
$access_error = 5;
}
}
}
############################################################################
# Determine whether or not this host is allowed access #
############################################################################
# Go through the list of allowed/disallowed hosts and check for a match
foreach $host (@ALLOW_HOSTS) {
if (($ENV{'REMOTE_HOST'} =~ /$host/i) || ($ENV{'REMOTE_ADDR'} =~ /$host/)) {
# There's a match so see if it means this host is allowed or not
# allowed
$allowed = $ALLOW_HOSTS ? $YES : $NO;
last;
}
}
# Check to see if there was no match and the list is of allowed hosts
if (!defined($allowed)) {
$allowed = ($ALLOW_HOSTS == $ONLY) ? $NO : $YES;
}
# Set flags if access denied
if ($allowed == $NO) {
$VAR{'USER_ID'} = '*';
$access_error = 1;
}
############################################################################
# Update site access log #
############################################################################
# Convert the IP to a host name if no host name given
if (!$ENV{'REMOTE_HOST'} || ($ENV{'REMOTE_HOST'} =~ /\d+\.\d+\.\d+\.\d+/)) {
$VAR{'HOST_NAME'} = &addr_to_host($ENV{'REMOTE_ADDR'});
}
else { $VAR{'HOST_NAME'} = $ENV{'REMOTE_HOST'} }
if (!$ignore) {
# Go ahead and append the log file
if (&lock($LOG_FILE, $LOCK_DIR, $MAX_WAIT)) {
&error("$Error_Message");
}
$access_log = "$LOG_DIR$LOG_FILE";
open(LOG, ">>$access_log") || &error("$access_log could not be opened ($!).");
# If we just created the log file, make sure its permissions are set
# properly and write the header line if one has been specified
if (-z $access_log) {
chmod $PERMISSIONS, $access_log if $PERMISSIONS;
if ($LOG_HEADER) { print LOG $LOG_HEADER }
}
# Add the new info to the log file then close and unlock the file
if ($access_error) { $page_name = $request }
print LOG "$VAR{'USER_ID'}$S$ENV{'REMOTE_ADDR'}$S$VAR{'HOST_NAME'}$S";
print LOG "$page_time$S$page_name$S$ENV{'HTTP_REFERER'}$S";
print LOG "$ENV{'HTTP_USER_AGENT'}$S$access_error\n";
close(LOG);
if (&unlock($LOG_FILE, $LOCK_DIR)) { &error("$Error_Message") }
}
# Give them an error message if there was something wrong with their request
if ($access_error > 1) { &error($error_msg) }
############################################################################
# Show them to the door if they are not allowed access #
############################################################################
if ($allowed == $NO) {
if ($NOT_ALLOWED_URL) {
print "Location:$NOT_ALLOWED_URL\n\n";
exit(0);
}
else {
&error('You are not authorized to access this page.',
'*** ACCESS DENIED ***');
}
}
############################################################################
# Process link requests and redirect accordingly #
############################################################################
if ($link_request) {
if (!$ignore && $link_request == 1) {
# Update the data for this link
++$link_count{$requested_link};
if (!$link_first{$requested_link}) {
$link_first{$requested_link} = $page_time;
}
$link_last{$requested_link} = $page_time;
# Write out the link data
&write_links;
}
# Redirect to the linked file and exit the program
if (!($requested_link =~ m|[^/]+://|)) {
if ($requested_link =~ /^#/) {
# This is an internal anchor
$requested_link = "$PROGRAM_URL/$page_name$requested_link";
}
else {
# This is a relative link
$requested_link = "$RELATIVE_URL/$requested_link";
}
}
print "Location: $requested_link\n\n";
exit(0);
}
############################################################################
# Retrieve and update page counts #
############################################################################
# Set the backup file name
$backup_path = $count_path;
$backup_path =~ s/\.[^.]*$//;
$backup_path .= '.bak';
# Lock the count file for this page
if (&lock($count_file, $LOCK_DIR, $MAX_WAIT)) { &error("$Error_Message") }
# Create and initialize the count file if necessary
$total_visits = $day_visits = $today_visits = 0;
if (!(-e $count_path)) {
if (!open(COUNT, ">$count_path")) {
$open_err = $!;
if (&unlock($count_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("Could not create count file $count_file ($open_err).");
}
chmod $PERMISSIONS, $count_path if $PERMISSIONS;
$first_visit = time;
print COUNT "$page_name\n$total_visits\n$first_visit\n" if !$ignore;
}
else {
# Make sure we can open the count file
if (!open(COUNT, $count_path)) {
$open_err = $!;
if (&unlock($count_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("Could not open count file $count_file ($open_err).");
}
# Make sure the count file's page name matches the current page
$file_page = <COUNT>;
chop($file_page);
if ($file_page ne $page_name) {
if (&unlock($count_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("The count file names for $page_name and $file_page are the same.");
}
# Read the old total visits and first visit date
$total_visits = <COUNT>;
chop($total_visits);
$first_visit = <COUNT>;
chop($first_visit);
# Read through the visit data and calculate statistics
while (($visit_time = <COUNT>) && ($visit_host = <COUNT>)) {
chop($visit_time);
chop($visit_host);
$last_visit = $visit_time;
# Is it within the last 24 hours?
if ($page_time - $visit_time < $DAY_SECS) {
# Update the counts
++$day_visits;
($year, $year_day) = (localtime($visit_time))[5,7];
if ($year == $page_year && $year_day == $page_year_day) {
++$today_visits;
}
# Save the visit data
push(@visit_data, $visit_time, $visit_host);
# Save the visit time if it's our current host
if ($visit_host eq $ENV{'REMOTE_ADDR'}) {
$prev_visit = $visit_time;
}
}
}
}
close(COUNT);
# Update counts if it's not a duplicate and we're not ignoring
if (!$ignore && ($page_time - $prev_visit > $REPEAT_LIMIT)) {
++$total_visits;
++$day_visits;
++$today_visits;
push (@visit_data, $page_time, $ENV{'REMOTE_ADDR'});
}
elsif (!$ignore) { $VAR{'DUPLICATE'} = $YES }
# Create a backup file
if (!open(BACKUP, ">$backup_path")) {
$open_err = $!;
if (&unlock($count_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("Could not create backup file $backup_file ($open_err).");
}
# Write the new data into the backup file
print BACKUP "$page_name\n$total_visits\n$first_visit\n";
$, = $\ = "\n";
print BACKUP @visit_data;
close(BACKUP);
$\ = "";
# Copy the backup file to the count file and unlock the count file
unlink($count_path);
if (!rename($backup_path, $count_path)) {
$open_err = $!;
if (&unlock($count_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("Could not restore count file $count_file ($open_err).");
}
if (&unlock($count_file, $LOCK_DIR)) { &error("$Error_Message") }
############################################################################
# Save all the variables into the VAR array #
############################################################################
$VAR{'TOTALVISITS'} = $total_visits;
$VAR{'24HRVISITS'} = $day_visits;
$VAR{'TODAYVISITS'} = $today_visits;
$VAR{'FIRSTVISIT'} = &format_date($first_visit, $VISIT_FORMAT);
$VAR{'LASTVISIT'} = &format_date($last_visit, $VISIT_FORMAT);
$VAR{'PREVVISIT'} = &format_date($prev_visit, $MOD_FORMAT) if $prev_visit;
$file_mod = $^T - int((-M $page_path) * $DAY_SECS);
$VAR{'LASTMOD'} = &format_date($file_mod, $MOD_FORMAT);
############################################################################
# Process body of page #
############################################################################
# Pass through the page, substituting variables where necessary
$start_body = $body_found = $NO;
$line_num = 0;
foreach $line (@page_data) {
# Check to see if we've started the body of the page yet
if (!$start_body && $line =~ /<BODY.*>/i) { $body_found = $YES }
# Check to see if this is the end of the body of the page
if ($start_body && ($footer || $ssi) && $line =~ /<\/BODY>/i) { last }
# Check to see if there are links on this line that need to be processed
$link = '';
if ($link_count) {
# Check first to see if this is the start or end of a LINKSORT block
if ($line =~ m|<(/?)LINKSORT *([^ >]*)|i) {
if (!$1) {
# This is the start of a block, so check for sort field
# and order and set the sort flag
$sort_field = $2 ? $2 : 'COUNT';
$Sort_Reverse = ($line =~ /REVERSE/i);
$link_sort = $YES;
undef %block_links;
}
elsif ($link_sort && defined(%block_links)) {
# This is the end of an existing block with links in it, so
# save the links in the requested sort order
foreach $line (sort by_field (keys %block_links)) {
$page_data[$line_num++] = $line
}
$link_sort = $NO;
}
next;
}
# Next, check for links
$line_copy = '';
while ($line =~ /<A LINKNAME="([^"]+)" HREF="([^"]+)/i) {
# Give the results easy-to-understand names
$link = $2;
$link_name = $1;
$before_match = $`;
$after_match = $';
# If it's already being passed through the program, remove the
# program name temporarily.
#$link =~ s/^$PROGRAM_URL\///;
# Check for duplicate link name
if ($link_name && $links{$link_name}
&& ($links{$link_name} ne $link)) {
&error("$links{$link_name} and $link have both been given the name \"$link_name\".");
}
elsif ($link_name) {
# If it's a new link then update the link data
if (!$link_names{$link}) {
$link_names{$link} = $link_name;
$links{$link_name} = $link;
$link_added{$link} = $page_time;
$link_update = $YES;
}
# Modify the link so it's passed through this program
$name_conv = $link_name;
$name_conv =~ tr/ /+/;
$line_copy .= $before_match . "<A HREF=\"$PROGRAM_URL/$link_file/link:$name_conv";
}
else {
# There's no link name so we can't process it
$line_copy .= $before_match . "<A HREF=\"$link";
}
# Save link title if it's on the same line
$title_start = index($after_match, '>') + 1;
$title_end = index($after_match, '</A>', $title_start);
if ($title_end > $title_start) {
$link_titles{$link} = substr($after_match, $title_start, $title_end - $title_start);
}
# Only check from this match to the end of the line next time
$line = $after_match;
}
# Set $line according to whether or not any matches were found
$line = $line_copy ? $line_copy . $after_match : $line;
}
# Substitute any other variables on this line if the variable option
# is on
$line_copy = '';
$changes = 0;
if ($variables) {
# Scan the line for variables
while ($line =~ /<<([^>]+)>>/) {
$variable = $1;
$before_match = $`;
$after_match = $';
# Check for link variables if allowed, scanning through line
# and substituting values
undef($value);
if ($link_count && $variable =~ /^LINK:([^ ]+) *'(.+)'/i) {
# Convert the variable name to uppercase
$variable = $1;
$variable =~ tr/a-z/A-Z/;
# Translate the link name into the link
$link = $links{$2};
# Substitute the appropriate variables
if ($variable eq 'COUNT' && defined($link_count{$link})) {
$value = sprintf("%0d", $link_count{$link});
}
elsif ($variable eq 'TITLE' && $link_titles{$link}) {
$value = $link_titles{$link};
}
elsif ($variable eq 'ADDED' && $link_added{$link}) {
$value = &format_date($link_added{$link}, $DATE_FORMAT);
}
elsif ($variable eq 'FIRST' && $link_first{$link}) {
$value = &format_date($link_first{$link}, $DATE_FORMAT);
}
elsif ($variable eq 'LAST' && $link_last{$link}) {
$value = &format_date($link_last{$link}, $DATE_FORMAT);
}
elsif ($variable eq 'INFO') {
# Check for a format definition and count for this link
$value = $link_info ? $link_info : $LINK_INFO;
if ($value && $link_count{$link}) {
# Substitue the appropriate variables
$count = sprintf("%0d", $link_count{$link});
$value =~ s/<<COUNT>>/$count/gi;
$first = &format_date($link_first{$link}, $DATE_FORMAT);
$value =~ s/<<FIRST>>/$first/gi;
$last = &format_date($link_last{$link}, $DATE_FORMAT);
$value =~ s/<<LAST>>/$last/gi;
}
elsif ($value) { $value = $LINK_COUNT_NONE }
}
}
elsif (defined($ENV{$variable})) { $value = $ENV{$variable} }
elsif (defined($VAR{$variable})) { $value = $VAR{$variable} }
# Substitute the appropriate value if any are found
if (defined($value)) { ++$changes }
$line_copy .= $before_match . $value;
# Only check from this match to the end of the line next time
$line = $after_match;
}
# Set $line according to whether or not any matches were found
$line = $line_copy ? $line_copy . $after_match : $line;
}
# Check to see if this line is a link in the middle of a linksort block
if ($link && $link_sort) {
# It is, so save the line and sort field until the end of the block
if ($sort_field =~ /COUNT/i) { $sort_value = $link_count{$link} }
elsif ($sort_field =~ /ADDED/i) { $sort_value = $link_added{$link} }
elsif ($sort_field =~ /LAST/i) { $sort_value = $link_last{$link} }
elsif ($sort_field =~ /FIRST/i) { $sort_value = $link_first{$link} }
$block_links{$line} = $sort_value;
}
else {
# Save line depending on presence of 0: and variables
if (!($line =~ s/^0://) || (!$line_copy)
|| ($line_copy && $changes)) {
if (!$ssi || $start_body) { $page_data[$line_num++] = $line }
else { $start_body = $body_found }
}
}
}
# Print the MIME header
print "Content-type: text/html\n\n" if !$ssi;
# Print the modified lines
foreach $line (splice(@page_data, 0, $line_num)) { print $line }
# Update link_file
if ($link_update) { &write_links }
############################################################################
# Add footer if requested #
############################################################################
if ($footer) {
if ($footer_file) {
if (!&parse_template($footer_file, *STDOUT)) {
&error("Could not open footer file $footer_file ($!).");
}
}
else {
print "<P><CENTER><HR SIZE=3 WIDTH=75%><I>\n";
print "<BR>$VAR{'DATENOW'}\n";
print "<BR>This page was last updated on $VAR{'LASTMOD'}.\n";
if ($VAR{'TOTALVISITS'} == 1) {
print "<P>You are the first person to visit this page!\n";
}
else {
print "<P>There have been $VAR{'TOTALVISITS'} visits to this page since $VAR{'FIRSTVISIT'}.\n";
if ($VAR{'TODAYVISITS'} == 1) {
print "<BR>There has been one visit to this page so far today, ";
}
else {
print "<BR>There have been $VAR{'TODAYVISITS'} visits to this page so far today, ";
}
if ($VAR{'24HRVISITS'} == 1) {
print "one visit in the last 24 hours.\n";
}
else {
print "$VAR{'24HRVISITS'} visits in the last 24 hours.\n";
}
}
print "</I></CENTER>\n";
}
}
############################################################################
# Finish page if not called as SSI #
############################################################################
print "</BODY></HTML>\n" if ($footer && !$ssi);
############################################################################
# End of main program #
############################################################################
exit(0);
############################################################################
# Local subroutine to write links file #
############################################################################
sub write_links {
# Set the backup file name
$backup_path = $link_path;
$backup_path =~ s/nk$/bk/;
# Lock the link file and create a backup file
if (&lock($link_file, $LOCK_DIR, $MAX_WAIT)) {
&error("$Error_Message");
}
if (!open(BACKUP, ">$backup_path")) {
$open_err = $!;
if (&unlock($link_file, $LOCK_DIR)) { &error("$Error_Message") }
&error("Could not create backup file $backup_file ($open_err).");
}
# Write the updated link data into the backup file
print BACKUP "$page_name\n";
foreach $link_name (sort keys %links) {
$link = $links{$link_name};
print BACKUP "$link\n";
print BACKUP "$link_name\n";
print BACKUP "$link_titles{$link}\n";
print BACKUP sprintf("%0d\n", $link_count{$link});
print BACKUP "$link_added{$link}\n";
print BACKUP "$link_first{$link}\n";
print BACKUP "$link_last{$link}\n";
}
close(BACKUP);
# Copy the backup file to the link file and unlock the link file
unlink($link_path);
rename($backup_path, $link_path)
|| &error("Could not restore count file $count_file ($!).");
if (&unlock($link_file, $LOCK_DIR)) { &error("$Error_Message") }
}
############################################################################
# Local subroutine to sort links in link block #
############################################################################
sub by_field {
if ($Sort_Reverse) { $block_links{$b} <=> $block_links{$a} || $b cmp $a }
else { $block_links{$a} <=> $block_links{$b} || $a cmp $b }
}