#!/usr/bin/perl
#
# Master Link Monitor Version 1.0
# Created February 20, 2000
# Author: William Bontrager
# Author's E-mail: william@willmaster.com
# Author's Website: http://www.willmaster.com/
#
# Master Link Monitor is designed to redirect users to a URL specified
#    in a database. While doing that, it logs the redirection.
# The database lookup is by a code attached to the URL used to access
#    this script. The code arrives with the GET method; i.e.
#    http://domain.com/cgi-bin/MasterLinkMonitor.cgi?code
# Master Link Monitor is designed to be executed with Perl 5; it won't
#    work with versions prior to 5.
#
# COPYRIGHT NOTICE
# Copyright (c) 1998-2000 by William and Mari Bontrager. All rights reserved.
#
# Master Link Monitor Version 1.0 may be used and modified free of charge
# by anyone so long as this header information and copyright notice
# remain intact. By using and/or modifying Master Link Monitor Version 1.0
# you agree to indemnify William Bontrager and Mari Bontrager and their
# associates from any liability that might arise from using and/or
# modifying it.
#
# Selling the code for Master Link Monitor Version 1.0 without prior written
# consent from the copyright owner is expressly forbidden. You must have
# permission before redistributing Master Link Monitor Version 1.0. In all
# cases, this header information and copyright notice must remain intact.
#
#	-------	End: header information and copyright notice	-------
#
#===============================
#
#         I N S T R U C T I O N S
#
#
# First, ensure that the first line of this file points to Perl 5+
#    on your server.
#
#
#
# Next, it is probable that this script will be accessed with an
#    invalid code (one that is not in your database (see below))
#    or even with no code at all. In such case, what page do you
#    want to display? Put that default page's URL between the
#    apostrophes (single quotes) of the next line:
$DefaultURL = 'http://www.datingclass.com/';

#
#
# Okay, now we make the database which will contain your codes and
#    the URL's where you want people redirected to.
#
# The database is one line per code/URL pair and the code and URL
#    are separated with a space; i.e.:
#
#          code http://willmaster.com/
#          anothercode http://www.domain.com/aff.cgi?a=H12
#          1and1 http://www.oneandonlynetwork.com/welcome.htm?MID=31050
#
#    (The lines in the file are not indented.) you can have as many
#    different codes as you want, one code and URL pair per line.
#
# NOTE: Codes are not case sensitive. "ACode" equals "aCode" equals "acode"
#    equals "ACODE".
#
# The code may contain only printable characters -- no spaces. You may
#    use any non-alphanumeric characters except these three: ? & =
#
# You can leave comments for yourself in the code database. Just put
#    an octothorpe (also called pound, hash, and number sign) as the
#    first character of each comment line.
#
# The program will ignore any blank lines in your database.
#
# (For those of you who have not read the main article, Issue # 31 of
#    WillMaster Possibilities suggests several implementation ideas.
#    The issue is linked from
#    http://www.willmaster.com/possibilities/archives/
#    and can be emailed to yourself.)
#
# The database is an ASCII/plain text file. Upload it to your server
#    that way. Put it in the same directory where Master Link Monitor
#    will be.
#
# Put the name of the file (you can call it whatever you want) between
#    the apostrophes in the next line:
$CodeDatabase = 'codes.txt';

#
#
# Master Link Monitor will log each code's activity in a separate log.
#    The names of these log files will be the name of the code plus
#    a .log extension: nameofcode.log
#
# The log will have the date/time and the referrer. The referrer will
#    only be present when a referring URL is available. Most email
#    links will not have a referrer -- the exceptions are web-based
#    emails such as hotmail et al.
#
#
#
# Master Link Monitor will also log those instances when invalid or
#    no codes are used. The same log will hold any error messages
#    that Master Link Monitor wants you to know about. For example,
#    if Master Link Monitor can't append to a code's log file, it
#    would inform you via this default log.
#
# The default log should have a name that is unlikely to be repeated
#    as one of your redirector codes. Otherwise, both the code and
#    the default logs would be one and the same.
#
# Put the name of the default log between the apostrophes in the
#    next line:
$DefaultLog = 'monitor.log';

#
#
# Your link will be something like this:
#
#        http://www.yourdomain.com/cgi-bin/MasterLinkMonitor.cgi?code
#
#    and whenever it is clicked, Master Link Monitor will look for the
#    code in the database.
#
# If it is found, the click is logged and the visitor is redirected
#    to the correct URL.
#
# If it is not found, that fact is logged in the default log and the
#    visitor is redirected to the default URL.
#
#
#
# Note: Master Link Monitor uses a file locking system to reduce the
#    chance of log file corruption. When you see files with a .lock
#    extension in the directory where Master Link Monitor is located,
#    it is the locking system's files.
#
#
#
# Now, upload Master Link Monitor to your server. Upload it as an
#    ASCII/plain text file.
#
# The name of the program can be changed, if you want. A shorter name
#    will make a shorter URL for your email links.
#
# Once uploaded, set the permissions by CHMODing to 766.
#
#
#
#
# NO USER CONFIGURABLE CODE BELOW THIS POINT
#
#===============================


require 5;
$Date = localtime;
$Ref = $ENV{HTTP_REFERER} ? $ENV{HTTP_REFERER} : 'No referring URL available.';
&LogRedirect if &CodeExtract;
print "Location: $URL\n\n";
exit;

sub CodeExtract
{
	$URL = $DefaultURL;
	return &LogDefault('No code present.') unless $ENV{QUERY_STRING} =~ /\w/;
	$Code = lc($ENV{QUERY_STRING});
	$Code =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
	$Code =~ s/\s//g;
	return &LogDefault('No code present.') unless $Code =~ /\w/;
	return &LogDefault("Unable to open database file \"$CodeDatabase\" to look for \"$Code\"") unless open(R,"$CodeDatabase");
	while(<R>)
	{
		next if index($_,'#') == 0;
		next unless /\w/;
		my $s = $_;
		$s =~ s/\A\s+//;
		$s =~ s/\s+\Z//;
		$s =~ s/\s\s+/ /g;
		my($c,$u) = split(/ /,$s,2);
		if(lc($c) eq $Code) { $URL = $u; last; }
	}
	close R;
	return &LogDefault("\"$Code\" not found in database file \"$CodeDatabase\"") if $URL eq $DefaultURL;
	return 1;
} # sub CodeExtract

sub LogRedirect
{
	my $f = $Code . '.log';
	LockFile($f);
	unless(-e $f) { CreateLog($f); }
	if(open(W,">>$f"))
	{
		print W "$Date -- $Ref\n";
		close W;
	}
	else { &LogDefault("Can't log redirection for code \"$Code\""); }
	UnLockFile($f);
} # sub LogRedirect

sub LogDefault
{
	LockFile($DefaultLog);
	unless(-e $DefaultLog) { CreateLog($DefaultLog); }
	if(open(W,">>$DefaultLog"))
	{
		print W "$Date ::: $_[0] ::: $Ref\n";
		close W;
	}
	UnLockFile($DefaultLog);
	return 0;
} # sub LogDefault

sub CreateLog
{
	open(W,">$_[0]");
	print W "Log created: $Date\n";
	close W;
} # sub CreateLog;

sub LockFile
{
	my $f = "$_[0]\.lock";
	my $tm = time;
	my $l;
	while((time - $tm) < 10)
	{
		last unless open(LOCKFILE,"$f");
		$l = <LOCKFILE>;
		close(LOCKFILE);
		last if index($l,'UNLOCKED') > -1;
		sleep 2;
	}
	open(LOCKFILE,">$f");
	print LOCKFILE "LOCKED\n";
	close(LOCKFILE);
} # sub LockFile

sub UnLockFile
{
	my $f = "$_[0]\.lock";
	open(LOCKFILE,">$f");
	print LOCKFILE "UNLOCKED\n";
	close(LOCKFILE);
} # sub UnLockFile

__END__
