#!/usr/bin/perl
# count.gif - Perl script to keep count of users of this
# home page.
#
# Credits:
# Original C code: Frans van Hoesel (hoesel@chem.rug.nl)
# Original port to Perl: Dan Rich (drich@corp.sgi.com)
# Modifications for cgi: Michael Nelson (m.l.nelson@larc.nasa.gov)
# Modifications for NSF: Mike Morse (mmorse@nsf.gov)
# Modified from NSF version: Spencer Thomas (spencer.thomas@med.umich.edu)
# Modified to handle multiple pages: Adam J. Griff, Ph.D. (griff@computer.org)
# Modified to block out pages from specific domains Dr. Griff
# Modified to produce xbm or gif :Dr. G. www.griffmonster.com
# This version is NSF specific, but you can easily modify it
# for your own needs. The main NSF-ism is that we run three
# servers on the same machine, and this script needs to figure
# out which server it is being called in behalf of.
# To install:
# 1. Look at initialize() and fix everything you need to.
# 2. Create the files referenced in initialize(), with the
# same owner as your server runs under, or world writable.
# 3. Stick the script in cgi-bin. My experience is that the
# script file must have the extension ".xbm".
# 4. Stick something like this in your home page:
# Number of home page visitors:
#
#
# The second line is only if you use our "stats" program, you
# can put anything there, or just use the IMG SRC bare.
# 5. When the home page is displayed, Mosaic clients will retrieve
# the in-line bitmap. This will invoke this script, which
# responds with a bitmap it builds on the fly, that looks like
# an automobile odometer. If you include the A HREF, the odometer
# will be surrounded by a border (and look better), and users
# can click the odometer to get some other information.
# 6. Don't worry if older WinMosaic versions display "Error" for
# the bitmap. The script is doing the Right Thing.
#
# 7. Extensions may be .text .xbm .dot or .gif - depending on the server
# conviguration the program needs to be copied 4 times or links can be
# used when acceptable by the server.
#
# REQUIRED CHANGES
# Modified by Griff to handle multiple pages.
# Just call the counter followed by a ?unique_page_name.
# Change the directory path for $serverRoot in the init procedure to the
# path on your server
# for example $serverRoot = "/home/www/htdocs"
# The files dot.gif and access.gif must be in the $serverRoot/images directory
use POSIX;
use NDBM_File;
&initialize;
if (&banned) {
print ("Content-type: image/gif\n\n");
open (FILE,"<$serverRoot/images/access.gif");
print ;
close(FILE);
} else {
&incrementCount;
if ($ENV{SCRIPT_NAME} && $ENV{SCRIPT_NAME} =~ /.*text(.*)/) {
&writeTextCounter;
} elsif ($ENV{SCRIPT_NAME} && $ENV{SCRIPT_NAME} =~ /.*dot(.*)/) {
print ("Content-type: image/gif\n\n");
open (FILE,"<$serverRoot/images/dot.gif");
print ;
close(FILE);
} elsif ($ENV{SCRIPT_NAME} && $ENV{SCRIPT_NAME} =~ /.*xbm(.*)/) {
&generateBitmap;
&writeBitmap;
} else {
print ("Content-type: image/gif\n\n");
open (IMAGE, "/usr/bin/text2gif -t \"$totalReads\" 2>&1|");
print ;
close(IMAGE);
}
exit(0);
}
sub initialize {
$serverRoot = "/home/www/www.griffmonster.com/htdocs";
$host_addr = $ENV{REMOTE_ADDR};
($user = $ENV{REMOTE_USER}) if (defined $ENV{REMOTE_USER});
if (defined $ENV{REMOTE_HOST}) {
$host = $ENV{REMOTE_HOST};
} else {
open(TEMP, "/usr/bin/host $host_addr 2>&1|");
while ((!defined $host) && ($temp = )) {
if ($temp =~ /domain name pointer\s*(\S*)\./) {
$host = $1;
}
}
close (TEMP);
}
($host = $host_addr) if (!defined $host);
$pagename = $ENV{QUERY_STRING};
# track where the pointer is located.
$referer = $ENV{HTTP_REFERER};
open(LOG,">>$serverRoot/logs/tracker.$pagename") || die "$0: can\'t open tracker $!\n";
print (LOG "$referer\t$user\@$host\n");
close(LOG);
$minLen = 7; # minimum number of digits in bigmap
$isHigh = 1; # if 1, digits are 16 pixels high, to
# allow room for border
$isInverse = 1; # If 1, digits are white on black
$logFiles = "$serverRoot/logs/counter";
$dbmFile = "$serverRoot/logs/hosts";
$lockWait = 5; # number of seconds to wait for lock
# bitmap for each digit
# Each digit is 8 pixels wide, 10 high
# @invdigits are white on black, @digits black on white
@invdigits = ("c3 99 99 99 99 99 99 99 99 c3", # 0
"cf c7 cf cf cf cf cf cf cf c7", # 1
"c3 99 9f 9f cf e7 f3 f9 f9 81", # 2
"c3 99 9f 9f c7 9f 9f 9f 99 c3", # 3
"cf cf c7 c7 cb cb cd 81 cf 87", # 4
"81 f9 f9 f9 c1 9f 9f 9f 99 c3", # 5
"c7 f3 f9 f9 c1 99 99 99 99 c3", # 6
"81 99 9f 9f cf cf e7 e7 f3 f3", # 7
"c3 99 99 99 c3 99 99 99 99 c3", # 8
"c3 99 99 99 99 83 9f 9f cf e3"); # 9
@digits = ("3c 66 66 66 66 66 66 66 66 3c", # 0
"30 38 30 30 30 30 30 30 30 30", # 1
"3c 66 60 60 30 18 0c 06 06 7e", # 2
"3c 66 60 60 38 60 60 60 66 3c", # 3
"30 30 38 38 34 34 32 7e 30 78", # 4
"7e 06 06 06 3e 60 60 60 66 3c", # 5
"38 0c 06 06 3e 66 66 66 66 3c", # 6
"7e 66 60 60 30 30 18 18 0c 0c", # 7
"3c 66 66 66 3c 66 66 66 66 3c", # 8
"3c 66 66 66 66 7c 60 60 30 1c"); # 9
}
sub banned {
if ($referer =~ /http:\/\/.*(web.mit.edu|ouray.cudenver.edu|www.netaxs.com|www.iaf.nl|rpi.edu|www.webcrawler.com|www.uroulette.com|www.geopages.com|www.phone.net|spidome.net|localhost.com)/) {
return 1;
} else {
return 0;
}
}
sub writeTextCounter {
print ("Content-type: text/html\n\n");
print ("Visitor counter for $pagename\n");
print ("You are visitor number $totalReads to the $pagename\n");
}
sub writeBitmap {
print ("Content-type: image/x-xbitmap\n\n");
if ($isHigh) {
printf ("#define count_width %d\n#define count_height 16\n",
$len*8);
}
else {
printf ("#define count_width %d\n#define count_height 10\n",
$len*8);
}
printf STDOUT "static char count_bits[] = {\n";
for($i = 0; $i < ($#bytes + 1); $i++) {
print("0x$bytes[$i]");
if ($i != $#bytes) {
print(",");
if (($i+1) % 7 == 0) {
print("\n");
}
}
}
print("};\n");
}
# generateBitmap() - $count contains number to display
# $minLen contains minimum number of digits to display
# $isHigh is one for 16 bit high numbers (else 10)
# $isInverse is one for reverse video (white on black);
sub generateBitmap {
$count = $totalReads;
@bytes = ();
$len = length($count) > $minLen ? length($count) : $minLen;
$formattedCount = sprintf("%0${len}d",$count);
if ($isHigh) {
for ($i = 0; $i < $len*3; $i++ ) {
if ($isInverse) {
push(@bytes,"ff"); # add three blank rows to each digit
}
else {
push(@bytes,"00");
}
}
}
for ($y=0; $y < 10; $y++) {
for ($x=0; $x < $len; $x++) {
$digit = substr($formattedCount,$x,1);
if ($isInverse) { # $inv = 1 for inverted text
$byte = substr(@invdigits[$digit],$y*3,2);
}
else {
$byte = substr(@digits[$digit],$y*3,2);
}
push(@bytes,$byte);
}
}
if ($isHigh) {
for ($i = 0; $i < $len*3; $i++ ) {
if ($isInverse) {
push(@bytes,"ff"); # add three blank rows to each digit
}
else {
push(@bytes,"00");
}
}
}
}
sub incrementCount {
&makeLogEntry;
if (&lockFile == 1) {
$count = "0";
return;
}
&incrementTotalReads;
&incrementHosts;
&unlockFile;
}
sub unlockFile {
unlink("$logFiles.$pagename.lock");
}
sub lockFile {
$lockCount = 0;
while (-f "$logFiles.$pagename.lock") {
if ($lockCount > $lockWait) {
$count = 0;
return 1; # forget it (would be nice to log though)
}
sleep 1;
$lockCount++;
}
open(LOCK,">$logFiles.$pagename.lock") || die("Can't open $logFiles.$pagename.lock: $!\n");
return 0;
}
sub incrementTotalReads {
$totalReads = 0;
if (-e "$logFiles.$pagename.txt") {
open(COUNT,"$logFiles.$pagename.txt") || die("Can't open $logFiles.$pagename.txt: $!\n");
$totalReads = ;
chop $totalReads;
close(COUNT);
}
$totalReads++;
# protection from loosing count in case the file write fails
open(COUNT,">$logFiles.$pagename.txt.tmp") || die "$0: can\'t open $logFiles.$pagename.txt.tmp: $!\n";
(print (COUNT "$totalReads\n")) || die "$0: can\'t write to $logFiles.$pagename.txt.tmp: $!\n";
close(COUNT) || die "$0: can\'t close $logFiles.$pagename.txt.tmp: $!\n";
rename("$logFiles.$pagename.txt.tmp","$logFiles.$pagename.txt");
}
sub makeLogEntry {
local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime(time);
$date = sprintf("%04d/%02d/%02d %02d:%02d:%02d",($year+1900),$mon+1,$mday,
$hour,$min,$sec);
open(LOG,">>$logFiles.$pagename.log") || die "$0: can\'t open $logFiles.$pagename.log: $!\n";
print (LOG "$date\t$user\@$host\n");
close(LOG);
}
sub incrementHosts {
$totalHosts = 0;
if (-e "$logFiles.$pagename.hosts.txt") {
open(COUNT,"$logFiles.$pagename.hosts.txt") || die("Can't open $logFiles.$pagename.hosts.txt: $!\n");
$totalHosts = ;
chop $totalHosts;
close(COUNT);
}
tie(%HOSTS, 'NDBM_File', "$dbmFile.$pagename", O_RDWR | O_CREAT, 0664);
if (! $host) {
$host = "unknown";
}
$HOSTS{$host}++;
if ($HOSTS{$host} == 1) {
$totalHosts++;
}
untie(%HOSTS);
open(COUNT,">$logFiles.$pagename.hosts.txt.tmp") || die "$0: can\'t open $logFiles.$pagename.hosts.txt.tmp: $!\n";
(print (COUNT "$totalHosts\n")) || die "$0: can\'t write to $logFiles.$pagename.hosts.txt.tmp: $!\n";
close(COUNT) || die "$0: can\'t close $logFiles.$pagename.hosts.txt.tmp: $!\n";
rename("$logFiles.$pagename.hosts.txt.tmp","$logFiles.$pagename.hosts.txt");
}