#!/usr/local/bin/perl

# AXS Script Set, Administration Module
# Copyright 1997-2000 by Fluid Dynamics
#
# Please adhere to the copyright notice and conditions of use as described at 
# the URL below.  For latest version and help files, visit:
#	http://www.xav.com/scripts/axs/
# ___________________________________________________________________________

$VERSION = '2.3.0.0008';

# You should place the log.txt and axs.dat files in the same directory as 
# this script.  If you do, you won't have to change the variables below. 
# If you want to put the files somewhere else, enter the full path to these 
# files:

$LogFile = '/htdocs/cgi-bin/tracking/log.txt';
$prefs = '/htdocs/cgi-bin/tracking/axs.dat';

#	Other examples:
#	$LogFile = '/usr/www/users/xav/log.txt';
#	$LogFile = 'c:/axs/log.txt';

# Enter the URL of your website, or part of it:

$My_Web_Address = $ENV{'http://www.dhilton.com'};

# Examples:
#	$My_Web_Address = 'http://www.xav.com/';
#	$My_Web_Address = 'xav.com';
# The HTTP_HOST variable is an automatic variable which will work for 90% of 
# users.  If you have trouble with the "Hits from Other Sites" or "HyperLinks 
# Followed from This Site", or if everything shows up in those graphs rather 
# than just remote links, then customize $My_Web_Address to your domain name, 
# like "xav.com".


# Enter your anchor page. This will form a link at the top of each AXS 
# output document:

$link_url = '/';
$link_title = 'My Home Page';

# Enter the URL to red.gif and tracker.jpg:

$RED = 'http://www.dhilton.com/htdocs/cgi-bin/images/red.gif';
$IMG = 'http://www.dhilton.com/htdocs/cgi-bin/images/tracker.jpg';

# Once the script is working to your satisfaction, set the $AllowDebug
# variable to zero:

$AllowDebug = 1;

# ________________________________________________________________________

# Protect AXS with a username and password.  Both are case sensitive.  You 
# can leave them blank to disable password locking.  This is the default:

$Username = '';
$Password = 'WhisperWish';

#	Other examples:
#	$Username = 'root';
#	$Password = 'IronMAN';

# You can allow anyone access to your graphs, while continuing to protect 
# your "Customize" page with a username and password.  If you do this, 
# web visitors will be free to view your statistics, but they won't be 
# able to delete the log file or change your settings.  To allow web 
# visitors to see your graphs without entering a username or password, set 
# this to 1:

$AllowAnonymousForGraphs = 0; # set to 1 to allow

# ________________________________________________________________________

# Most of you shouldn't have to change anything below this line.  If you 
# try the script out and it doesn't work, the help files will suggest 
# changes to the following lines.

# The request method can be either GET or POST.  Setting the method to GET 
# will cause the username and password data to be exposed to the web server 
# logs.  Using GET is inadvisable if others have access to your web server 
# logs.
$Request_Method = 'POST';

# The URL to this script:
$This_Script_Address = $ENV{'SCRIPT_NAME'};

# The admininstrator's email address - use *single* quotes:
$Admin_Email_Address = $ENV{'naika@dhilton.com'};
#	Example:
#	$Admin_Email_Address = 'president@whitehouse.gov';

# Your favorite network lookup services:
$nslookup = 'http://www.xav.com/cgi-bin/nslookup.cgi';
$whois = 'http://www.networksolutions.com/cgi-bin/whois/whois?';

# AXS can collapse web addresses which include the default document.  
# This prevents you from having two database entries for a single file, 
# like http://www.ms.com/ and http://www.ms.com/index.html:

$DefaultDoc = 'index.html';

# If you'd like, local files can show up as their HTML title instead of 
# their URL.  For example, visits to http://www.xav.com/ would show up in 
# your graphs as "Home Page".  To use this option, enter the URL-title 
# pairs below, and set the top variable to "1":

$UseLocalAddressTitlePairs = 0; # Set to "1" to enable.
%LocalAddressTitlePairs = (
	'http://www.xav.com/' , 'Home Page',
	'http://www.xav.com/scripts/' , 'Scripts Page',
	'http://www.xav.com/scripts/axs/' , 'AXS Script Page',
	);

# Uncomment this line if you receive errors about invalid Content-Type 
# headers.  (to support command-line parameters, the HTTP headers are 
# only sent back if the SERVER_SOFTWARE env var is defined; most web 
# servers should set this, but if you're doesn't then you have to set 
# it manually by uncommenting the line below)
#$ENV{'SERVER_SOFTWARE'} = 1;

# No further editing is necessary, but feel free to play around.  The 
# first 1,000 lines of this script are straight HTML and JavaScript, so 
# you can safely customize the look and feel of the output even if you 
# don't know Perl.
# 
# ________________________________________________________________________


%GraphOptions = (
	's01' ,	'Web Browser (Netscape 3.01 Gold)',
	's02' ,	'Abbreviated Browser (Netscape 3.X)',
	's03' ,	'Operating System (Windows 98)',
	's04' ,	'Visitors Top Level Domains (.com)',
	's05' ,	'First Level Domains (xav.com)',
	's06' ,	'Full Server Address (noc.xav.com)',
	's07' ,	'Visitor IP Address (206.134.243.3)',
	's08' ,	'Hits from Other Sites (Full URL)',
	's09' ,	'Hits from Other Sites (Domain Only)',
	's10' ,	'Hyperlinks Followed From This Site',
	's11' ,	'Hits to Local Documents',
	's12' ,	'Average Number of Hits Per Visitor',
	's13' ,	'Hits By Day of Year',
	's14' ,	'Hits By Day of the Week',
	's15' ,	'Hits By Hour of the Day');

@DatabaseOptions = ('Sort All by Time','Sort All by Visitor','Visitor Flow Only');

@LongWeekDays = ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
@ShortWeekDays = ('SUN','MON','TUE','WED','THU','FRI','SAT');
@LongMonths = ('January','February','March','April','May','June','July','August','September','October','November','December');
@ShortMonths = ('JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC');
@ShortDayNames = ('YEST','TOD','TOM');

sub Header {
return <<"END_OF_HTML";

<HTML>
<HEAD>
	<TITLE>AXS Script Set - Administrator's Page</TITLE>
</HEAD>
<BODY BGCOLOR="#ffffff" ALINK="#cccccc" VLINK="#0000aa">

END_OF_HTML
}
# ________________________________________________________________________


sub HTML_Header {
return <<"END_OF_HTML";

<CENTER>
	<FONT SIZE="+1">AXS by Fluid Dynamics<BR></FONT>
	[ - <A HREF="$This_Script_Address">Main Menu</A> 
	  - <A HREF="$This_Script_Address?Target=Preferences">Customize</A>
	  - Back to <A HREF="$link_url">$link_title</A> - ]
</CENTER>
<BR>

END_OF_HTML
}
# ________________________________________________________________________


sub Footer {
return <<"END_OF_HTML";

<H5 ALIGN="center">
	<A HREF="$This_Script_Address">Main Menu</A>
	- <A HREF="$This_Script_Address?Target=Preferences">Customize</A>
	- <A HREF="$This_Script_Address?Target=LogOut">Log Out</A>
	<HR SIZE="1" NOSHADE WIDTH="50%">
	AXS Script Set Version $VERSION is Copyright 1997-2000 by
	<A HREF="http://www.xav.com/">Fluid Dynamics</A>.<BR>
	Visit the <A HREF="http://www.xav.com/scripts/axs/">AXS Page</A>
	for help files and most recent version.
</H5>
</BODY>
</HTML>

END_OF_HTML
}
# ________________________________________________________________________


sub PrintMainPage {

$cur_hits = 0;
if (open(LOG, "<$LogFile")) {
	binmode(LOG);
	while (<LOG>) {
		$cur_hits++;
		}
	close(LOG);
	}

print <<"EOM";

<BLOCKQUOTE>

<TT>AXS</TT> keeps records on visits to your site. This companion 
script, <TT>AX-ADMIN</TT>, allows you to display these records in 
meaningful graph and database formats.  We currently have <B>$cur_hits</B> 
hits to work with.

<FORM METHOD=$Request_Method ACTION="$This_Script_Address" 
	NAME="graphs" OnSubmit="return CheckGraphs()">

<CENTER>
<INPUT TYPE="TEXT" NAME="maximum" SIZE="4" VALUE="$PREF{'maximum'}"> 
<SELECT NAME="format">
EOM

# This is a Perl loop - you don't need to edit it:
foreach $Option (sort @DatabaseOptions) {
	if ($PREF{'format'} eq $Option) {
		print "<OPTION VALUE=\"$Option\" SELECTED>$Option\n";
		}
	else {
		print "<OPTION VALUE=\"$Option\">$Option\n";
		}
	} # end "foreach $Option".

print <<"EOM";
</SELECT> 
<INPUT TYPE="SUBMIT" NAME="show_data" VALUE="View in Database Format">
</CENTER>

<P>Enter the number of recent hits you'd like to view, or leave blank for 
all. Enter "L" to view hits since your last visit on $PREF{'last_string'}.</P>

<P><B><U>Create Graphs Based On:</U></B></P>

<CENTER>
<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
<TR>
<TD VALIGN="top">
EOM

# This is a Perl loop - you don't need to edit it:
foreach $OptionCode (sort keys %GraphOptions) {
	print <<"EOM";
<NOBR>
	<A HREF="javascript:TC(document.graphs.$OptionCode)"
		STYLE="cursor: text; text-decoration: none">
	<INPUT TYPE="checkbox" NAME="$OptionCode">
	<FONT COLOR="#000000">
		$GraphOptions{$OptionCode}
	</FONT></A>
</NOBR><BR>
EOM
	} # end "foreach $OptionCode".
	
print <<"EOM";
</TD>

<TD WIDTH="40"><BR></TD>

<TD ALIGN="center" VALIGN="middle">
<INPUT TYPE="reset" VALUE="Clear"><INPUT TYPE="button" VALUE="Set Defaults" NAME="defaults" 
onClick="setdefs()"><BR>
<INPUT TYPE="submit" VALUE="View in Graphical Format" NAME="MakeGraphs" 
OnClick="JavaMakeGraphs()"><BR>
	<IMG SRC="$IMG" ALT="AXS logo" HEIGHT="205" WIDTH="198">
</TD>
</TR>
</TABLE>
</CENTER>

<P><B><U>Graphing Filters:</U></B></P>

<BLOCKQUOTE>
	<P>By default, <TT>AXS</TT> will graph all hits in the database. 
	However, with these filters, you can restrict graphs to recent hits, 
	critical files, or both.</P>

	<P>
	&nbsp; &nbsp;
	<A HREF="javascript:FormatTimesSinceLast('$PREF{'last_string'}')" STYLE="cursor: text; text-decoration: none">
	<FONT COLOR="#000000">
	<INPUT TYPE="checkbox" $PREF{'since_last'} NAME="since_last">
		Graph only hits since my last visit on $PREF{'last_string'}
	</FONT>
	</A><BR>

	&nbsp; &nbsp;
	<A HREF="javascript:FormatTimesRecent();"
		STYLE="cursor: text; text-decoration: none">
	<FONT COLOR="#000000">
	<INPUT TYPE="checkbox" $PREF{'recent'} NAME="recent">
		Graph only hits from yesterday and today</FONT></A>, or specify:
	</P>

	<P>
	<INPUT TYPE="text" NAME="start_date" SIZE="10" VALUE="$PREF{'start_date'}"
		OnBlur="FormatStartTime(document.graphs.start_date.value)">
		Start Date <I>(<FONT ID="StartTime">mm-dd-year</FONT>)</I><BR>

	<INPUT TYPE="text" NAME="end_date" SIZE="10" VALUE="$PREF{'end_date'}"
		OnBlur="FormatEndTime(document.graphs.end_date.value)">
		End Date <I>(<FONT ID="EndTime">mm-dd-year</FONT>)</I><BR>

	<INPUT TYPE="text" NAME="Filter" SIZE="24" VALUE="$PREF{'Filter'}">
		Filter String</P>

	<P>The filter string may contain a file name, server name, or browser 
	type - if this field is used, all graphs will be designed from log 
	entries with contain this string as a pattern match.</P>
</BLOCKQUOTE>

</FORM>
<BR>
</BLOCKQUOTE>
EOM
}
# ________________________________________________________________________


sub PrintJavaMainPage {
print <<"EOM";
<SCRIPT LANGUAGE="Javascript">
<!-- // Hide the Java...
function setdefs() {
	var1 = 'true';
	var2 = 'false';
EOM
foreach $OptionCode (keys %GraphOptions) {
	print "document.graphs.$OptionCode.checked = ";
	(($PREF{$OptionCode}) && ($PREF{$OptionCode} eq 'CHECKED')) ? print 'true' : print 'false';
	print ";\n";
	}
print <<"EOM";
	}
// End Java Hiding -->
</SCRIPT>
EOM
}
# ________________________________________________________________________


sub PrintCustomizePage {
print <<"EOM";

<BLOCKQUOTE>

<FORM METHOD="$Request_Method" ACTION="$This_Script_Address" NAME="graphs">
<INPUT TYPE="hidden" NAME="Target" VALUE="Preferences">

<P>Because you'll typically generate the same graphs repeatedly, 
<TT>AXS</TT> allows you to specify default settings. Enter your most common 
settings below. Later, <TT>AX-ADMIN</TT> will select these values 
automatically.</P>

<BLOCKQUOTE>
<INPUT TYPE="TEXT" NAME="maximum" SIZE=4 VALUE="$PREF{'maximum'}">
<SELECT NAME="format">
EOM

# This is a Perl loop - you don't need to edit it:
foreach $Option (sort @DatabaseOptions) {
	if ($PREF{'format'} eq $Option) {
		print "<OPTION VALUE=\"$Option\" SELECTED>$Option\n";
		}
	else {
		print "<OPTION VALUE=\"$Option\">$Option\n";
		}
	} # end "foreach $Option".

print <<"EOM";
</SELECT>
</BLOCKQUOTE>

<P>The text box holds the number of recent hits you're interested in. You 
can enter a letter to view recent hits through the day of your last 
visit.</P>

<P><B><U>Most Common Graphs:</U></B></P>

<BLOCKQUOTE>
EOM

# This is a Perl loop - you don't need to edit it:
foreach $OptionCode (sort keys %GraphOptions) {
	print <<"EOM";
<NOBR>
	<A HREF="javascript:TC(document.graphs.$OptionCode)"
		STYLE="cursor: text; text-decoration: none">
	<INPUT TYPE="checkbox" $PREF{$OptionCode} NAME="$OptionCode">
	<FONT COLOR="#000000">
		$GraphOptions{$OptionCode}
	</FONT></A>
</NOBR><BR>
EOM
	} # end "foreach $OptionCode".

print <<"EOM";

</BLOCKQUOTE>

<P><B><U>Graphing Filters:</U></B></P>

<BLOCKQUOTE>
	<P>By default, <TT>AXS</TT> will graph all hits in the database. However, 
	with these filters, you can restrict graphs to recent hits, critical 
	files, or both.</P>

	<P>
	&nbsp; &nbsp;
	<A HREF="javascript:FormatTimesSinceLast('$PREF{'last_string'}')"
		STYLE="cursor: text; text-decoration: none">
	<FONT COLOR="#000000">
	<INPUT TYPE="checkbox" $PREF{'since_last'} NAME="since_last">
		Graph only hits since my last visit on $PREF{'last_string'}
	</FONT>
	</A><BR>

	&nbsp; &nbsp;
	<A HREF="javascript:FormatTimesRecent();"
		STYLE="cursor: text; text-decoration: none">
	<FONT COLOR="#000000">
	<INPUT TYPE="checkbox" $PREF{'recent'} NAME="recent">
		Graph only hits from yesterday and today</FONT></A>, or specify:
	</P>

	<P>
	<INPUT TYPE="text" NAME="start_date" SIZE="10" VALUE="$PREF{'start_date'}"
		OnBlur="FormatStartTime(document.graphs.start_date.value)">
		Start Date <I>(<FONT ID="StartTime">mm-dd-year</FONT>)</I><BR>

	<INPUT TYPE="text" NAME="end_date" SIZE="10" VALUE="$PREF{'end_date'}"
		OnBlur="FormatEndTime(document.graphs.end_date.value)">
		End Date <I>(<FONT ID="EndTime">mm-dd-year</FONT>)</I><BR>

	<INPUT TYPE="text" NAME="Filter" SIZE="24" VALUE="$PREF{'Filter'}">
		Filter String</P>

	<P>The filter string may contain a file name, server name, or browser 
	type - if this field is used, all graphs will be designed from log 
	entries with contain this string as a pattern match.</P>
</BLOCKQUOTE>

<P><B><U>Graphics Output:</U></B><P>

<BLOCKQUOTE>

<DL>

<DT><A HREF="javascript:TC(document.graphs.NumSort)"
	STYLE="cursor: text; text-decoration: none">
<INPUT TYPE="checkbox" $PREF{'NumSort'} NAME="NumSort">
<FONT COLOR="#000000">
	Sort data numerically, with most hits on top
</FONT>
</A></DT><DD><I>By default, graphs are alphabetically sorted by key</I></DD>

<DT><A HREF="javascript:TC(document.graphs.NewWindow)"
	STYLE="cursor: text; text-decoration: none">
<INPUT TYPE="checkbox"  $PREF{'NewWindow'} NAME="NewWindow">
<FONT COLOR="#000000">
	Follow links by opening a separate window
</FONT>
</A></DT><DD></DD>

<DT><A HREF="javascript:TC(document.graphs.Highlight)"
	STYLE="cursor: text; text-decoration: none">
<INPUT TYPE="checkbox" $PREF{'Highlight'} NAME="Highlight">
<FONT COLOR="#000000">
	Highlight the percentage column in graphs
</FONT>
</A></DT><DD></DD>

<DT><A HREF="javascript:TC(document.graphs.HidePoundSigns)"
	STYLE="cursor: text; text-decoration: none">
<INPUT TYPE="checkbox" $PREF{'HidePoundSigns'} NAME="HidePoundSigns">
<FONT COLOR="#000000">
	Compress web addresses that include pound signs
</FONT>
</A></DT>
<DD><U>http://www.xav.com/links.html#localsites</U><I> 
becomes</I><BR><U>http://www.xav.com/links.html</U><BR></DD>

<DT><A HREF="javascript:TC(document.graphs.HideDefaultDoc)"
	STYLE="cursor: text; text-decoration: none">
<INPUT TYPE="checkbox" $PREF{'HideDefaultDoc'} NAME="HideDefaultDoc">
<FONT COLOR="#000000">
	Compress web addresses that include the default document, 
	<TT>$DefaultDoc</TT>
</FONT>
</A></DT>
<DD><U>http://www.xav.com/$DefaultDoc</U><I> 
becomes</I><BR><U>http://www.xav.com/</U></DD>
</DL>

</BLOCKQUOTE>

Set the maximum width of graphs to 
<INPUT TYPE="text" NAME="MaxWidth" SIZE="4" 
VALUE="$PREF{'MaxWidth'}"> pixels.<BR>

<BLOCKQUOTE>
	<INPUT TYPE="hidden" NAME="incoming" VALUE="true">

	<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1" BGCOLOR="#cccccc">
	<TR><TD><INPUT TYPE="submit" VALUE="Commit Changes"></TD></TR>
	</TABLE>
</BLOCKQUOTE>

</FORM>

<BR>
<HR NOSHADE SIZE="1" WIDTH="50%">

<FORM METHOD=$Request_Method ACTION="$This_Script_Address" 
	NAME="Deletion" OnSubmit="return ConfirmDelete()">
<INPUT TYPE=HIDDEN NAME="terminate" VALUE="On">

<P><B><U>Log Management:</U></B></P>

<BLOCKQUOTE>
	<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1" BGCOLOR="#cccccc">
	<TR><TD><INPUT TYPE="SUBMIT" VALUE="Delete Access Log"></TD></TR>
	</TABLE>
</BLOCKQUOTE>

<P>By default, all entries will be deleted. You may choose to delete 
<I>only</I> hits <I>older</I> than a certain date:&nbsp; <INPUT 
TYPE="text" NAME="start_date" SIZE="10" 
OnBlur="FormatDeleteTime(document.Deletion.start_date.value)"> <I>(<FONT 
ID="DeleteTime">mm-dd-year</FONT>)</I></P>

<P>The access log will grow by about a kilobyte for every six hits, 
eventually becoming too large for processing (it's currently at 
$LogSizeKiloBytes kb - $Advice). We recommend deleting the log every so 
often. Before doing so, you'll want to generate your favorite graphs and 
save them to your system as HTML files, as a record of how your site 
traffic evolves over time.</P>

</FORM>
</BLOCKQUOTE>
<BR><BR>

EOM
}
# ________________________________________________________________________


sub Authenticate {
$Target = ($FORM{'Target'} eq 'Preferences') ? 'Preferences' : '';
return <<"END_OF_HTML";

<CENTER>
<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="10">
<TR>
<TD WIDTH="20" BGCOLOR="#cccccc"><BR></TD>
<TD WIDTH="550" ALIGN="left" VALIGN="bottom">
<BR><BR>
<BLOCKQUOTE>

<P><B>Please be advised!!</B></P>

<P>Use of secured computer and network facilities requires prior
	authorization. Unauthorized access is prohibited.  Usage may be
	subject to security testing and monitoring. Abuse is subject to
	criminal, civil, and extra-legal prosecution.</P>

<CENTER>
<FORM ACTION="$This_Script_Address"
	METHOD="$Request_Method"
	NAME="authentication">

<TABLE BORDER="0">
<TR>
	<TD ALIGN="right" VALIGN="bottom">
	<B>Username: </B><INPUT TYPE="text" NAME="username" SIZE="8"><BR>
	<B>Password: </B><INPUT TYPE="password" NAME="password" SIZE="8"></TD>

	<TD ALIGN="center" VALIGN="bottom">
	
	<TABLE BORDER="0" CELLSPACING="0" CELLPADDING="1" BGCOLOR="#cccccc">
	<TR><TD><INPUT TYPE="submit" VALUE="Authenticate"></TD></TR>
	</TABLE></TD>
</TR>
</TABLE>
<INPUT TYPE="hidden" NAME="Target" VALUE="$Target">
</FORM>

<SCRIPT LANGUAGE="JavaScript">
<!--
document.authentication.username.focus();
// -->
</SCRIPT>
</CENTER>

<P>This script is operated by <A HREF="mailto:$Admin_Email_Address">
	$Admin_Email_Address</A>, or a third party reachable via that address. 
	Contact them for help with usernames and passwords. Technical issues, 
	bug reports, and the like may be directed to <A 
	HREF="mailto:noc\@xav.com"><FONT SIZE="-1">noc\@xav.com</FONT></A>.</P>

</BLOCKQUOTE></TD>
</TR>
</TABLE>
</CENTER>
<H5 ALIGN="center">
	<HR SIZE="1" NOSHADE WIDTH="50%">
	AXS Script Set Version $VERSION is Copyright 1997-2000 by
	<A HREF="http://www.xav.com/">Fluid Dynamics</A>.<BR>Visit the 
	<A HREF="http://www.xav.com/scripts/axs/">AXS Page</A> for help 
	files and most recent version.
</H5>
</BODY>
</HTML>

END_OF_HTML
}
# ________________________________________________________________________


sub DatabaseFlowDescription {
return <<"END_OF_HTML";

<BLOCKQUOTE>
<P>Below is a flow chart of your visitors.  Visits are shown with newer hits at 
the top, and older hits towards the bottom, with timestamps taken from the time 
of first visit.  Successive visits by the same user are grouped together, so 
that you can view the path taken through your site.</P>

<P>The time interval between hits is given in Hour:Minute:Second format, followed 
by the number of days, if any.</P>

<P>Note that in most cases, the same individual will have different IP 
addresses with each network logon.  Alternately, the same IP address may 
represent different visitors over time.  Sampling a smaller number of hits 
over a shorter time period reduces the probability of these errors occuring.</P>
</BLOCKQUOTE>

END_OF_HTML
}
# ________________________________________________________________________


sub DatabaseTimeDescription {
return <<"END_OF_HTML";

<BLOCKQUOTE>
<P>Each hit below is listed in the order it was counted, with the most recent 
hits listed first.</P>
</BLOCKQUOTE>

END_OF_HTML
}
# ________________________________________________________________________


sub GraphSummary {
$relevant_hits = &AddCommas($relevant_hits);
$NumGraphLines = &AddCommas($NumGraphLines);
$SummaryText = <<"END_OF_HTML";

<P><B><U>Summary:</U></B></P>
<BLOCKQUOTE>
	<P>There were $total_hits total hits analyzed.  Of these, 
	$relevant_hits were 
END_OF_HTML

if ($NumGraphLines) {
	$SummaryText .= "relevant, and they resulted in $NumGraphLines lines in the table."
	}
else {
	$SummaryText .= 'relevant. ';
	}

if (!$FilterString) {
	$SummaryText .= "No string matching was done against the access log.  ";
	}
elsif ($FilterString =~ m!^host:(.*)$!i) {
	$SummaryText .= "Searched only hits whose hostname matched \"$1\".  ";
	}
elsif ($FilterString =~ m!^ip:(.*)$!i) {
	$SummaryText .= "Searched only hits whose IP address matched \"$1\".  ";
	}
elsif ($FilterString =~ m!^from:(.*)$!i) {
	$SummaryText .= "Searched only hits whose referers matched \"$1\".  ";
	}
elsif ($FilterString =~ m!^to:(.*)$!i) {
	$SummaryText .= "Searched only hits in which the document hit matched \"$1\".  ";
	}
elsif ($FilterString =~ m!^browser:(.*)$!i) {
	$SummaryText .= "Searched only hits in which the browser name matched \"$1\".  ";
	}
else {
	$SummaryText .= "Searched only records whose text matched \"$FilterString\".  ";
	}

if (($StartString) && ($EndString)) {
	$SummaryText .= "Restricted to hits occurring between $StartString, and $EndString.</P>";
	}
elsif ($StartString) {
	$SummaryText .= "Restricted to hits occurring on or after $StartString.</P>";
	}
elsif ($EndString) {
	$SummaryText .= "Restricted to hits occurring on or before $EndString.</P>";
	}
else {
	$SummaryText .= "The log was not filtered by date.</P>";
	}

$SummaryText .= <<"END_OF_HTML";

</BLOCKQUOTE>

END_OF_HTML
return $SummaryText;
} #-----------------------------------------------------------------------


sub JavaLib {
return <<'END_OF_HTML';
<SCRIPT LANGUAGE="JavaScript">
<!-- Hide from non-Java browsers
window.onerror = null;
var version = parseInt(navigator.appVersion);
var isIE = navigator.appVersion.indexOf("MSIE")>0;
var isNav = navigator.appVersion.indexOf("Nav")>0;
var isIE4 = isIE && version>=4;
var isNav4 = isNav && version>=4;

function TC(checkbox) {
	checkbox . checked = ! checkbox . checked;
	}

function StripWhiteSpace (DS) {
while (DS.length && (DS.charAt(0) == ' ')) {
	DS = DS.substring(1,DS.length);
	}
return DS;
}

function AddLeadingZero (Number) {
	/*  kick int properties:  */
	Number++; Number--;
	if (Number < 10) {
		Number = " 0" + Number;
		Number = Number.substr(1,3);
		}
	return Number;
	}

function DateFromString(DS) {
	MonthNames = new Array("January","February","March","April",'May','June','July','August','September','October','November','December');
	CompMonthNames = new Array('JAN','FEB','MAR','APR','MAY','JUN','JUL','AUG','SEP','OCT','NOV','DEC');
	WeekDays = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
	CompWeekDays = new Array('SUN','MON','TUE','WED','THU','FRI','SAT');
	DateSuffix = new Array('th','st','nd','rd','th','th','th','th','th','th','th','th','th','th','th','th','th','th','th','th');
	var AllInt = '0123456789';
	AllCaps = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
	AllLows = 'abcdefghijklmnopqrstuvwxyz';

DS = StripWhiteSpace(DS);

/* test for numeric status of first non-whitespace: */
var N1 = -1;
var MonthFirst = 1;
var TempAlphaString = '';

if ((DS.length) && (AllInt.indexOf(DS.charAt(0)) < 0)) {

	/* non-numeric: */
	/* capture all non-numerics up til first numeric, non-inclusive: */
	var TempAlphaString = '';
	while (DS.length && ((AllInt.indexOf(DS.charAt(0)) < 0) || (DS.charAt(0) == ' '))) {
		OffSet = AllLows.indexOf(DS.charAt(0));
		if (OffSet > -1) {
			TempAlphaString += AllCaps.substring(OffSet,OffSet+1);
			}
		else {
			TempAlphaString += DS.charAt(0);
			}
		DS = DS.substring(1,DS.length);
		}
	for (var i=0;i<12;i++) {
		if (TempAlphaString.indexOf(CompMonthNames[i]) >= 0) {
			N1 = i + 1;
			i = 12;
			}
		}
	}

if ((DS.length) && (N1 == -1)) {
	/* numeric first character.  Capture first 1 or 2 numerics */
	/* 1 if 2nd is non-numeric */
	N1 = parseInt(DS.charAt(0),10);
	DS = DS.substring(1,DS.length);
	if (DS.length && !(AllInt.indexOf(DS.charAt(0)) < 0) && (DS.charAt(0) != ' ')) {
		N1 *= 10;
		N1 += parseInt(DS.charAt(0));
		DS = DS.substring(1,DS.length);
		}
	}

DS = StripWhiteSpace(DS);

/* test for numeric status of first non-whitespace: */
var N2 = -1;
if ((DS.length) && (AllInt.indexOf(DS.charAt(0)) < 0)) {

	/* non-numeric: */
	/* capture all non-numerics up til first numeric, non-inclusive: */
	var TempAlphaString = '';
	while (DS.length && ((AllInt.indexOf(DS.charAt(0)) < 0) || (DS.charAt(0) == ' '))) {
		OffSet = AllLows.indexOf(DS.charAt(0));
		if (OffSet > -1) {
			TempAlphaString += AllCaps.substring(OffSet,OffSet+1);
			}
		else {
			TempAlphaString += DS.charAt(0);
			}
		DS = DS.substring(1,DS.length);
		}
	for (var i=0;i<12;i++) {
		if (TempAlphaString.indexOf(CompMonthNames[i]) >= 0) {
			N2 = i + 1;
			i = 12;
			MonthFirst = 0;
			}
		}
	}

/* continue with num search if text search was aborted or didn't turn */
/* anything up... */
if ((DS.length) && (N2 == -1)) {
	/* numeric first character.  Capture first 1 or 2 numerics */
	/* 1 if 2nd is non-numeric */
	N2 = parseInt(DS.charAt(0),10);
	DS = DS.substring(1,DS.length);
	if (DS.length && (!(AllInt.indexOf(DS.charAt(0)) < 0) && (DS.charAt(0) != ' '))) {
		N2 *= 10;
		N2 += parseInt(DS.charAt(0));
		DS = DS.substring(1,DS.length);
		}
	}

DS = StripWhiteSpace(DS);

/* test for numeric status of first non-whitespace: */
if (DS.length && (AllInt.indexOf(DS.charAt(0)) < 0)) {

	/* non-numeric: */
	/* strip all non-numerics up til first numeric, non-inclusive: */
	var TempAlphaString = '';
	while (DS.length && ((AllInt.indexOf(DS.charAt(0)) < 0) || (DS.charAt(0) == ' '))) {
		TempAlphaString += DS.charAt(0);
		DS = DS.substring(1,DS.length);
		}
	}

var YearNumber = 0;
while (DS.length && !(AllInt.indexOf(DS.charAt(0)) < 0) && (DS.charAt(0) != ' ')) {
	YearNumber = (YearNumber * 10) + parseInt(DS.charAt(0),10);
	DS = DS.substring(1,DS.length);
	}
YearNumber++; YearNumber--;

ThisDay = new Date();
ThisDayNumber = ThisDay.getDay();
NumDays1970 = (ThisDay.getTime()/(24*3600000));


/* if both N1,N2 fail, see if the guy typed in a weekday: */
if ((N1 == -1) && (N2 == -1)) {
	for (i=0; i<7; i++) {
		if (TempAlphaString.length && (TempAlphaString.indexOf(CompWeekDays[i]) >= 0)) {
			NumDaysPast = ((ThisDayNumber - i + 7) % 7);
			NewNumDays1970 = NumDays1970 - NumDaysPast;
			ThisDay.setTime(24*3600000*NewNumDays1970);
			N1 = ThisDay.getMonth() + 1;
			N2 = ThisDay.getDate();
			Year = ThisDay.getYear() + 1900;
			i = 7;
			}
		}
	}
if ((N1 == -1) && (N2 == -1)) {
	if ((TempAlphaString.length) && (TempAlphaString.indexOf("YEST") >= 0)) {
		NewNumDays1970 = NumDays1970 - 1;
		ThisDay.setTime(24*3600000*NewNumDays1970);
		N1 = ThisDay.getMonth() + 1;
		N2 = ThisDay.getDate();
		Year = ThisDay.getYear() + 1900;
		}
	else if ((TempAlphaString.length) && (TempAlphaString.indexOf("TOD") >= 0)) {
		N1 = ThisDay.getMonth() + 1;
		N2 = ThisDay.getDate();
		Year = ThisDay.getYear() + 1900;
		}
	else if (TempAlphaString.length && (TempAlphaString.indexOf("TOM") >= 0)) {
		NewNumDays1970 = NumDays1970 + 1;
		ThisDay.setTime(24*3600000*NewNumDays1970);
		N1 = ThisDay.getMonth() + 1;
		N2 = ThisDay.getDate();
		Year = ThisDay.getYear() + 1900;
		}
	}
if (YearNumber == 0) {
	YearNumber = ThisDay.getYear();
	}
if (YearNumber < 1000) {
	YearNumber += 1900;
	}
/* Date Pattern match not found: */
if ((N1 == -1) || (N2 == -1)) {
	return '';
	}
if (MonthFirst) {
	ThisMonthNum = (N1 - 1);
	ThisDay = N2;
	}
else {
	ThisMonthNum = (N2 - 1);
	ThisDay = N1;
	}
/* return 0 for bad configs: */
if (ThisDay < 1) {
	return 0;
	}
if ((ThisMonthNum < 0) || (ThisMonthNum > 11)) {
	return 0;
	}
DaysInMonth = new Array (31,28,31,30,31,30,31,31,30,31,30,31);
DaysInThisMonth = DaysInMonth[ThisMonthNum];
if (ThisDay > DaysInThisMonth) {
	if (!((ThisMonthNum == 1) && ((YearNumber % 4) == 0) && (ThisDay == 29))) {
		return 0;
		}
	}
/* Date is now set in stone (else we've already aborted).  Now format */
/* as needed for this application. */
MyDate = new Date();
MyDate.setYear(YearNumber);
MyDate.setMonth(ThisMonthNum);
MyDate.setDate(ThisDay);
ThisWeekDay = WeekDays[MyDate.getDay()];
ThisMonthName = MonthNames[ThisMonthNum];
return ThisWeekDay + ", the " + ThisDay + DateSuffix[ThisDay%20] + " of "  + ThisMonthName + ", " + YearNumber;
}
function FormatStartTime(DateString) {
DateString = DateFromString(DateString);
if (DateString != "") {
	window.status = DateString;
	if (isIE4) {
		document.all.StartTime.innerHTML = DateString;
		}
	}
return true;
}
function FormatEndTime(DateString) {
DateString = DateFromString(DateString);
if (DateString != "") {
	window.status = DateString;
	if (isIE4) {
		document.all.EndTime.innerHTML = DateString;
		}
	}
return true;
}
function FormatTimesSinceLast(DateString) {
	document.graphs.since_last.checked = !document.graphs.since_last.checked;
	FormatStartTime(DateString);
	FormatEndTime("Today");
	}
function FormatTimesRecent() {
	document.graphs.recent.checked = !document.graphs.recent.checked;
	FormatStartTime("Yesterday");
	FormatEndTime("Today");
	}
var DeleteTime = '';
function FormatDeleteTime(DateString) {
	DateString = DateFromString(DateString);
	if (DateString != "") {
		window.status = DateString;
		if (isIE4) {
			document.all.DeleteTime.innerHTML = DateString;
			}
		DeleteTime = DateString;
		}
	else {
		DeleteTime = '';
		}
	return true;
	}
function ConfirmDelete() {
	var Confirmation;
	if (DeleteTime != '') {
		Confirmation = "Are you sure you want to delete all log entries before " + DeleteTime + "?\nThere is no undo feature, you know.";
		}
	else {
		Confirmation = "Are you sure you want to delete the entire access log?\nThere is no undo feature, you know.";
		}
	if (confirm(Confirmation)) {
		return true;
		}
	else {
		return false;
		}
	}
var GetGraphs = 0;
function JavaMakeGraphs() {
	GetGraphs = 1;
	return true;
	}
function CheckGraphs() {
	if (GetGraphs == 0) {
		return true;
		}
	else if (document.graphs.s01.checked ||
				document.graphs.s02.checked ||
				document.graphs.s03.checked ||
				document.graphs.s04.checked ||
				document.graphs.s05.checked ||
				document.graphs.s06.checked ||
				document.graphs.s07.checked ||
				document.graphs.s08.checked ||
				document.graphs.s09.checked ||
				document.graphs.s10.checked ||
				document.graphs.s11.checked ||
				document.graphs.s12.checked ||
				document.graphs.s13.checked ||
				document.graphs.s14.checked ||
				document.graphs.s15.checked) {
		return true;
		}
	else {
		Confirmation  = "You must choose something to graph.\n\nYour options are listed on the ";
		Confirmation += "left (from type of \"Web Browser\" through \"Hits by Hour of Day\").";
		Confirmation += "  You can select them by clicking your mouse on the checkbox next to each ";
		Confirmation += "item.\n\nWould you like me to choose a graph for you?";

		if (confirm(Confirmation)) {
			document.graphs.s02.checked = true;
			document.graphs.MakeGraphs.value = 'Click me now!';
			}
		return false;
		}
	}
// End Java Hiding. -->
</SCRIPT>

END_OF_HTML
} #-----------------------------------------------------------------------


# Find out whether this is Microsoft Internet Information Server:
$IIS = ($ENV{'SERVER_SOFTWARE'}&&($ENV{'SERVER_SOFTWARE'}=~m!iis!i))?1:0;

# Build generic timestamp for all functions:
@MyT = localtime(time);

# The following line causes this script to chdir into it's local 
# directory.  This is necessary for some implementations of IIS:
chdir($1) if (($IIS) && ($0 =~ m!(.*)(\\|\/)!));

# The following guesses the script address when $ENV is undefined, which 
# happens during command-line mode:
unless ($This_Script_Address) {
	$This_Script_Address = '';
	$This_Script_Address = $1 if ($0 =~ m!([^\\|\/]+)$!);
	}

# Get started with the HTML header:
if ($ENV{'SERVER_SOFTWARE'}) {
	print "HTTP/1.0 200 OK\r\n" if ($IIS);
	print "Pragma: no-cache\r\n";
	print "Content-Type: text/html\r\n\r\n";
	}
print &Header;

$QueryString = $ENV{'QUERY_STRING'} ? $ENV{'QUERY_STRING'} : '';
if (($AllowDebug == 1) && ($QueryString =~ m!^debugme$!i)) {
	&DebugThenExit;
	}


# Now read and parse the input:
if ($ENV{'REQUEST_METHOD'} && ($ENV{'REQUEST_METHOD'} eq 'POST')) {
	read(STDIN,$IN,$ENV{'CONTENT_LENGTH'});
	}
else {
	$IN = $QueryString;
	}
$IN =~ tr!+! !;

# Allows command-line parameters:
foreach (split(m!\&!,$IN),@ARGV) {
	($N,$V) = split(/\=/,$_);
	$N =~ s/\%(..)/pack(C,hex($1))/eg;
	$V =~ s/\%(..)/pack(C,hex($1))/eg;
	$FORM{$N} = $V;
	}
$FilterString = $FORM{'Filter'} ? $FORM{'Filter'} : '';
%PREF = &AuthPref($prefs);

if ($FORM{'Target'} && ($FORM{'Target'} eq 'LogOut')) {
	print &Authenticate;
	exit;
	}

# Next, we open the log file and import all the records.  This is *only* 
# done if we're going to make graphs this time:

if ($FORM{'show_data'} || $FORM{'MakeGraphs'} || $FORM{'terminate'}) {

	print "<!-- choosing to open the log file -->\r\n";

# Allows the "L" flag to date-filter database results (for reverse 
# compatibility):
if ($FORM{'show_data'} && ($FORM{'maximum'} !~ m!^\d*$!)) {
	$FORM{'since_last'} = 'on';
	}
# If date filtering is enabled, the dates are converted into a format 
# that makes sense to AXS:
($StartNumber,$StartString,$EndNumber,$EndString) = &FormatDates($FORM{'start_date'}, $FORM{'end_date'}, $FORM{'recent'}, $FORM{'since_last'}, $PREF{'last_number'});
# Open the log file and store all of the hits in the 
# @LINES array.  Run whichever filters are necessary, for date/time 
# or by-file filtering.  This preps @LINES and also $total_hits.
unless (open(LOGFILE,"<$LogFile")) {
	&DebugThenExit(1);
	}
if ($FilterString eq '') {
	$FILTER = '(\|[^\|]*){10,10}\|(\d*)\|\d*\|(\d*)';
	}
elsif ($FilterString =~ m!^host:(.*)$!i) {
	$FILTER = '\|[^\|]*'.$1.'[^\|]*(\|[^\|]*){9,9}\|(\d*)\|\d*\|(\d*)';
	}
elsif ($FilterString =~ m!^ip:(.*)$!i) {
	$FILTER = '\|[^\|]*\|[^\|]*'.$1.'[^\|]*(\|[^\|]*){8,8}\|(\d*)\|\d*\|(\d*)';
	}
elsif ($FilterString =~ m!^from:(.*)$!i) {
	$FILTER = '\|[^\|]*\|[^\|]*\|[^\|]*'.$1.'[^\|]*(\|[^\|]*){7,7}\|(\d*)\|\d*\|(\d*)';
	}
elsif ($FilterString =~ m!^to:(.*)$!i) {
	$FILTER = '\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*'.$1.'[^\|]*(\|[^\|]*){6,6}\|(\d*)\|\d*\|(\d*)';
	}
elsif ($FilterString =~ m!^browser:(.*)$!i) {
	$FILTER = '\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*\|[^\|]*'.$1.'[^\|]*(\|[^\|]*){5,5}\|(\d*)\|\d*\|(\d*)';
	}
elsif ($FilterString) {
	$FILTER = '.*'.$FilterString.'(.*)\|(\d*)\|\d*\|(\d*)\|(export\|)?\r?$';
	}
else {
	$FILTER = '(\|[^\|]*){10,10}\|(\d*)\|\d*\|(\d*)';
	}

	#print "<!-- starting loop... -->\r\n";

$total_hits = 0;
if ($StartNumber || $EndNumber || $FilterString) {
	$EndSearchNow = 0;
	foreach (<LOGFILE>) {
		$total_hits++;
		next unless (($EndSearchNow) || (m!^$FILTER!));
		$ThisYDAY = $2 * 1000 + $3 + 1900000;
		next if (($StartNumber) && ($StartNumber > $ThisYDAY));
		if ($EndNumber && ($EndNumber < $ThisYDAY)) {
			$EndSearchNow = 'true';
			next;
			}
		push(@LINES,$_);
		}
	}
else {
	@LINES = <LOGFILE>;
	$total_hits = scalar @LINES;
	}
close(LOGFILE);
$total_hits = &AddCommas($total_hits);

	#print "<!-- done with log file -->\r\n";

	} # End importing data.

# Now we print HTML banner which goes at the top of every page:
print &HTML_Header;

# Finished printing HTML header. Now determine which subprocedure(s) to 
# invoke based on the input:

MAIN: {
	if ($FORM{'show_data'}) {
		if ($FORM{'format'} eq 'Sort All by Time') {
			&show_data;
			}
		else {
			&show_data_flow;
			}
		last MAIN;
		}
	&make_stats(5,'Web Browser',0) if ($FORM{'s01'});
	&make_stats(5,'Web Browser','short') if ($FORM{'s02'});
	&make_stats(5,'Operating System','os') if ($FORM{'s03'});
	&make_stats(1,'TLD','tld') if ($FORM{'s04'});
	&make_stats(1,'Domain','abbr') if ($FORM{'s05'});
	&make_stats(1,'Remote Server','full') if ($FORM{'s06'});
	&make_stats(2,'IP Address',0) if ($FORM{'s07'});
	&make_stats(3,'Referring URL','') if ($FORM{'s08'});
	&make_stats(3,'Referring URL','domain') if ($FORM{'s09'});
	&make_stats(4,'Links Followed','remote') if ($FORM{'s10'});
	&make_stats(4,'Document Hit','local') if ($FORM{'s11'});
	&avg_docs if ($FORM{'s12'});
	&make_stats_year(13,'Day of the Year',0) if ($FORM{'s13'});
	&make_stats_week(12,'Day of the Week',0) if ($FORM{'s14'});
	&make_stats_hour(8,'Hour of the Day',0) if ($FORM{'s15'});
	&kill_it if ($FORM{'terminate'});
	last MAIN if ($graph_made);

	# If no graphs were made, then show the intro page, or allow 
	# the user to set his preferences.  Each of these pages will use the 
	# massive Java library:
	print &JavaLib;

	if (($FORM{'Target'}) && ($FORM{'Target'} eq 'Preferences')) {
		# show preferences:
		$LogSizeKiloBytes = int((-s $LogFile) / 1000);
		if ($LogSizeKiloBytes < 500) {
			$Advice = 'that is not too bad';
			}
		elsif ($LogSizeKiloBytes < 1000) {
			$Advice = 'it is starting to get up there';
			}
		else {
			$Advice = 'you may want to delete it';
			}
		$LogSizeKiloBytes = &AddCommas($LogSizeKiloBytes);
		&PrintCustomizePage;
		}
	else {
		# show main page:
		&PrintJavaMainPage;
		&PrintMainPage;
		}

	} # End MAIN: block.
print &Footer;
exit;

# This is the end - everything below is a sub-procedure called above.
# ________________________________________________________________________


# Prints a line of the graph:
#
#	Format is &print_line(Name,Value) where Name is something 
#	like 'Netscape 3' and Value is the number of hits.
#	<TR><TD> name </TD><TD> percent </TD><TD> number </TD><TD> picture </TD></TR>
sub print_line {
($N,$V) = @_;
print "<TR><TD><NOBR><TT>$N</TT></NOBR>";
print '</TD><TD '.$BGCOLOR.' ALIGN="right"><TT>';
print sprintf("%.2f",($V * $RH100));
print '%</TT></TD><TD ALIGN=RIGHT><TT>';
print "$V</TT></TD>";

# traps minimum width at 1, since width=0 is ignored by browser:
$width = int($multiplier * $V) || 1;

print "<TD ALIGN=LEFT><IMG SRC=\"$RED\" BORDER=\"1\" ALT=\"";
print 'X' x int($width*(30/$PREF{'MaxWidth'}));
print "\" HEIGHT=10 WIDTH=$width>";

# print '<TD><TABLE BGCOLOR="#000000" BORDER="0" CELLSPACING="2" CELLPADDING="0" WIDTH="'.$width.'" HEIGHT="5"><TR><TD BGCOLOR="#cf0000"><FONT SIZE="1"><BR></FONT></TD></TR></TABLE>';

print '</TD></TR>', "\n";
} # End Print Line.


# Prints a line of the graph.  Allows Value = 0.
#
#	Format is &print_line_allow0(Name,Value) where Name is something 
#	like 'Tuesday' and Value is the number of hits.
#	<TR><TD> name </TD><TD> percent </TD><TD> number </TD><TD> picture </TD></TR>
sub print_line_allow0 {
($N,$V) = @_;
$N .= '&nbsp;' x (12 - length($N));
print "<TR><TD><NOBR><TT>$N</TT></NOBR>";
print '</TD><TD '.$BGCOLOR.' ALIGN="right"><TT>';
print sprintf("%.2f",($V * $RH100));
print '%</TT></TD><TD ALIGN="right"><TT>';
if ($V) {
	print $V;
	print '</TT></TD>';
	# traps minimum width at 1, since width=0 is ignored by browser:
	$width = int($multiplier * $V) || 1;
	print "<TD ALIGN=\"left\"><IMG SRC=\"$RED\" BORDER=2 ALT=\"";
	print 'X' x int($width*(30/$PREF{'MaxWidth'}));
	print "\" HEIGHT=10 WIDTH=$width>";
# Above comments out image-using graphs:
#	print '<TD><TABLE BGCOLOR="#cc0000" WIDTH="'.$width.'"><TR><TD><BR></TD></TR></TABLE>';

	}
else {
	print '0</TT></TD><TD><BR>';
	}
print '</TD></TR>', "\n";
} # End Print Line/Allow 0


# Begin Main Graphing Procedure:
sub make_stats {
($q, $graph_name, $detail) = @_;
print '<BR><BR><HR SIZE="1" WIDTH="80%"><BR><BR><BR>' if $graph_made;
print '<TABLE BORDER="0" CELLPADDING="6" CELLSPACING="0">';
print '<TR><TH ALIGN="left"><U>'.$graph_name.':</U></TH><TH COLSPAN="2" ALIGN="center"><U> Hits: </U></TH><TH ALIGN="left"><U>Graph:</U></TH></TR>';
$relevant_hits = 0;
$max_var = 0;
undef(%ASTA);
foreach $RECORD (@LINES) {
	@xSQL = split(/\|/,$RECORD);

	# Special case of referring URLs - the script makes sure first 
	# that there is a non-zero entry in field 3, and then discards 
	# those which appear to be local to the web site.  If the query 
	# is being made for domain name only, the script runs a pattern 
	# match on (somthing)//(something)/(whatever) and saves the first 
	# two fields.  Local file links are discarded for domain-only 
	# queries.

	if ($q == 3) {
		next unless ($xSQL[3]);

	# To protect against those with blank $My_Web_Address variables, this 
	# code will show *all* referrers if $My_Web_Address is blank.  I feel 
	# that this is a better solution that showing *no* referers.
	#
	# code was:
	# next if ($xSQL[3] =~ /$My_Web_Address/i);

		next if (($My_Web_Address) && ($xSQL[3] =~ /$My_Web_Address/i));


		if (($detail eq 'domain') && ($xSQL[3] =~ m!^([^\/]+)\/\/([^\/]*)!)) {
			$xSQL[3] = $1.'//'.$2;
			next if ($1 =~ m!file!i);
			}
		# strip "#" signs from URL's:
		elsif (($PREF{'HidePoundSigns'}) && ($xSQL[3] =~ m!([^\#]+)!)) {
			$xSQL[3] = $1;
			}
		}


	# $q = 1 indicates a query on the server name.  this code 
	# abbreviates the server names to either TLD, host.TLD, or 
	# ' IP Address Only' in the case of non-alpha hosts.

	elsif ($q == 1) {
		if ($xSQL[1] =~ /([^\.]+)\.([^\.|\d]+)$/) {
			if ($detail eq 'tld') {
				$xSQL[1] = $2;
				}
			elsif ($detail eq 'abbr') {
				$xSQL[1] = $1.'.'.$2;
				}
			}
		else {
			$xSQL[1] = ' IP Address Only';
			}
		}


	# Exit Points & Local Documents:

	elsif ($q == 4) {
		if ($detail eq 'remote') {
			next if ($xSQL[14] ne 'export');
			
			# Again, only limit to local web pages if the $My_Web_Address variable 
			# is populated:
			next if (($My_Web_Address) && ($xSQL[4] =~ /$My_Web_Address/i));

			}
		elsif ($detail eq 'local') {
			$xSQL[4] = $xSQL[3] if ($xSQL[14] eq 'export');

			# Again, only limit to local web pages if the $My_Web_Address variable 
			# is populated:

			next unless ($xSQL[4] =~ /$My_Web_Address/i);

			}

		# strip # signs from URL's:
		if (($PREF{'HidePoundSigns'}) && ($xSQL[4] =~ m!([^\#]+)!)) {
			$xSQL[4] = $1;
			}
		if ($xSQL[4] =~ m|([^\/]+)//([^\/]+):80\/(.*)|) {
			$xSQL[4] = "$1//$2/$3";
			}
		if (($PREF{'HideDefaultDoc'}) && ($xSQL[4] =~ m!(.*)/$DefaultDoc$!i)) {
			$xSQL[4] = "$1/";
			}

		}


	# Operating System and Short Web Browser Name:

	elsif ($q == 5) {
		$xSQL[5] = &get_os_type($xSQL[5]) if ($detail eq 'os');
		$xSQL[5] = &get_browser_name($xSQL[5]) if ($detail eq 'short');
		}


	$ASTA{$xSQL[$q]}++;
	$relevant_hits++;
	$max_var++ unless ($max_var >= $ASTA{$xSQL[$q]});
	}
# Finish loop through each hit in log file.


$multiplier = ($PREF{'MaxWidth'} / $max_var) if ($max_var);

$RH100 = 100 / $relevant_hits if ($relevant_hits);
if ($relevant_hits < 1) {
	print '<TR><TD><B>No matches found for your search. Sorry.</B></TD></TR>';
	}
elsif (($q == 3) || ($q == 4)) {
	# q3/4 => hits to local, hits from remote, etc. URL's.
	foreach ($NUMS ? (sort {$ASTA{$b} <=> $ASTA{$a} || $a cmp $b} keys %ASTA) : (sort keys %ASTA)) {
		&print_line(&url_format($_),$ASTA{$_});
		}
	}
elsif (($q == 1) && ($detail eq 'abbr')) {
	# q1 => server names.
	foreach ($NUMS ? (sort {$ASTA{$b} <=> $ASTA{$a} || $a cmp $b} keys %ASTA) : (sort keys %ASTA)) {
		if (/ IP Address Only/) {
			&print_line('<I>IP Address Only</I>',$ASTA{$_});
			}
		else {
			&print_line("<A HREF=\"$whois$_\"$TARGET>$_</A>",$ASTA{$_});
			}
		}
	}
elsif ($q == 2) {
	# q2 => IP addresses.
	foreach ($NUMS ? (sort {$ASTA{$b} <=> $ASTA{$a} || $a cmp $b} keys %ASTA) : (sort keys %ASTA)) {
		&print_line("<A HREF=\"$nslookup?$_\"$TARGET>$_</A>",$ASTA{$_});
		}
	}
else {
	# q* => other.  q5 = browser type, os type, etc.
	foreach ($NUMS ? (sort {$ASTA{$b} <=> $ASTA{$a} || $a cmp $b} keys %ASTA) : (sort keys %ASTA)) {
		&print_line($_,$ASTA{$_});
		}
	}
$NumGraphLines = scalar (keys %ASTA);
print "</TABLE>\n";
print &GraphSummary;
$graph_made++;
} # End Summary Report and End Sub-Procedure.


# Begin Main Graphing Procedure for Day of Year:
sub make_stats_year {

	# Do we have a leap year, or a non-leap year?
	# leap years are divisible by 4.  However, every 100 years 
	# is an exception (non-leap), and every 400 years is an 
	# exception to that (leap).
	
	$this_year = (localtime(time))[5] + 1900;
	
	# Assume normal year:
	@mon_array = (0,31,59,90,120,151,181,212,243,273,304,334);
	$total_days_year = 365;
	
	if (($this_year % 4) == 0) {
		# year is divisible by 4, is leap, probably
		
		if ((($this_year % 100) == 0) && (($this_year % 400) != 0)) {
			# is divisible by 100, and not divisible by 400; 
			# standard exception, leave this as a non-leap year
			}
		else {
			# ok world we have a leap year:
			
			@mon_array = (0,31,60,91,121,152,182,213,244,274,305,335);
			$total_days_year = 366;
			
			}
		}

	print '<BR><BR><HR SIZE=1 WIDTH=80%><BR><BR><BR>' if $graph_made;
	print '<TABLE BORDER="0" CELLPADDING="6" CELLSPACING="0">';
	print '<TR><TH ALIGN="left"><U>Day of Year:</U></TH><TH COLSPAN="2" ALIGN="center"><U> Hits:</U></TH><TH ALIGN="left"><U>Graph:</U> &nbsp; <I>jump to <A HREF="#jump">first hit</A></I></TH></TR>';
	undef($max_var);
	undef(%ASTA);
	$relevant_hits = scalar @LINES;
	foreach (@LINES) {
		$ThisDay = (split(/\|/,$_))[13];
		$DayCount[$ThisDay]++;
		$max_var++ unless ($max_var >= $DayCount[$ThisDay]);
		}
	$multiplier = ($PREF{'MaxWidth'} / $max_var) if ($max_var);
	if ($relevant_hits) {
		$RH100 = 100 / $relevant_hits;
		}
	$month_count = 0;
	$LinkPrinted = 0;
	for (0..($total_days_year - 1)) {
		$month_count++ if ($_ == $mon_array[$month_count + 1]);
		$mday = (($_ - $mon_array[$month_count]) + 1);
		$day = "$LongMonths[$month_count] $mday";
		if (($LinkPrinted == 0) && ($DayCount[$_+1] > 0)) {
			print '</TABLE><A NAME="jump"><TABLE BORDER="0" CELLPADDING="6" CELLSPACING="0">';
			$LinkPrinted = 1;
			}
		&print_line_allow0($day, $DayCount[$_]);
		}
	$NumGraphLines = $total_days_year;
	print "</TABLE></A>\n";
	print &GraphSummary;
	$graph_made++;
	} # End Graph for Day of Year.


# Begin Main Graphing Procedure for Day of Week
sub make_stats_week {
print '<BR><BR><HR SIZE=1 WIDTH=80%><BR><BR><BR>' if $graph_made;
print '<TABLE BORDER="0" CELLPADDING="6" CELLSPACING="0">';
print '<TR><TH ALIGN="left"><U>Day of Week:</U></TH><TH COLSPAN="2" ALIGN="center"><U> Hits: </U></TH><TH ALIGN="left"><U>Graph:</U></TH></TR>';

undef($max_var);
undef(%ASTA);
$relevant_hits = scalar @LINES;
foreach (@LINES) {
	$ThisDay = (split(/\|/,$_))[12];
	$DayCount[$ThisDay]++;
	$max_var++ unless ($max_var >= $DayCount[$ThisDay]);
	}
$multiplier = ($PREF{'MaxWidth'} / $max_var) if ($max_var);
if ($relevant_hits) {
	$RH100 = 100 / $relevant_hits;
	}
# q12 => LongWeekDays
for (0..6) {
	&print_line_allow0($LongWeekDays[$_],$DayCount[$_]);
	}
$NumGraphLines = 7;
print "</TABLE>\n";
print &GraphSummary;
$graph_made++;
} # End Graph for Day of Week



# Begin Main Graphing Procedure for Hour of Day:
sub make_stats_hour {
print '<BR><BR><HR SIZE=1 WIDTH=80%><BR><BR><BR>' if $graph_made;
print '<TABLE BORDER="0" CELLPADDING="6" CELLSPACING="0">';
print '<TR><TH ALIGN="left"><U>Hour of Day:</U></TH><TH COLSPAN="2" ALIGN="center"><U> Hits: </U></TH><TH ALIGN="left"><U>Graph:</U></TH></TR>';
undef($max_var);
undef(%ASTA);

$relevant_hits = scalar @LINES;
foreach (@LINES) {
	$ThisHour = (split(/\|/,$_))[8];
	$HourCount[$ThisHour]++;
	$max_var++ unless ($max_var >= $HourCount[$ThisHour]);
	}
$multiplier = ($PREF{'MaxWidth'} / $max_var) if ($max_var);
if ($relevant_hits) {
	$RH100 = 100 / $relevant_hits;
	}
for (0..23) {
	print '<TR><TD ALIGN="right"><TT>';
	if ($_ == 0) {
		print 'Midnight';
		}
	elsif ($_ < 12) {
		print $_.' AM';
		}
	elsif ($_ == 12) {
		print 'High noon';
		}
	else {
		print $_ - 12;
		print ' PM';
		}
	print '&nbsp;</TT></TD><TD '.$BGCOLOR.' ALIGN="right"><TT>';
	$V = $HourCount[$_];
	print sprintf("%.2f",($V * $RH100));
	print '%</TT></TD><TD ALIGN=RIGHT><TT>';
	if ($V) {
		print "$V</TT></TD>";
		# traps minimum width at 1, since width=0 is ignored by browser:
		$width = int($multiplier * $V) || 1;
		print "<TD ALIGN=LEFT><IMG SRC=\"$RED\" BORDER=2 ALT=\"";
		print 'X' x int($width*(30/$PREF{'MaxWidth'}));
		print "\" HEIGHT=10 WIDTH=$width>";
		}
	else {
		print '0</TT></TD><TD><BR>';
		}
	print '</TD></TR>', "\n";
	}
$NumGraphLines = 24;
print "</TABLE>\n";
print &GraphSummary;
$graph_made++;
} # End make_stats_hour.



# Begin Print Average Document Count:
sub avg_docs {
$internal_hits = 0;
$unique_ip_count = 0;
foreach (@LINES) {
	@terms = split(/\|/,$_);
	$unique_ip_count++ unless ($IP{$terms[2]});
	$IP{$terms[2]}++;
	if ($terms[4] =~ /$My_Web_Address/) {
		$internal_hits++;
		}
	}
if ($unique_ip_count) {
	$avg_docs_per_visitor = $internal_hits / $unique_ip_count;
	}
else {
	$avg_docs_per_visitor = 0;
	}
$avg_docs_per_visitor = sprintf("%.3f",$avg_docs_per_visitor);

print <<EOM;
<P>The average number of documents viewed per visitor is 
$avg_docs_per_visitor. There have been a total of $internal_hits 
internal hits from $unique_ip_count unique IP addresses.</P>
EOM
print &GraphSummary;
$graph_made++;
} # End Print Average Document Count.



sub PrettyTime {
($Hour,$Minutes,$Seconds) = @_;
$Minutes = sprintf("%02.f",$Minutes);
$Seconds = sprintf("%02.f",$Seconds);
if ($Hour < 12) {
	$Hour = sprintf("%2.f",$Hour);
	return "$Hour:$Minutes:$Seconds AM";
	}
else {
	$Hour = $Hour - 12;
	$Hour = sprintf("%2.f",$Hour);
	return "$Hour:$Minutes:$Seconds PM";
	}
}

# Begin Show Database Procedure:
sub show_data {
if ($FORM{'maximum'} =~ /\d+/) {
	$array_size = scalar @LINES;
	if ($FORM{'maximum'} < $array_size) {
		splice(@LINES,0,$array_size - $FORM{'maximum'});
		}
	}

print &DatabaseTimeDescription;
print '<PRE>';

($relevant_hits,$NumGraphLines) = (0,0);
foreach (reverse @LINES) {
	$relevant_hits++;
	($Host,$IPAddress,$T3,$T4,$Browser,$SS,$MM,$HH,$Day,$T10,$Year,$T12,$Redirect) = (split(/\|/,$_))[1..12,14];
	$Referer = $T3 ? &url_format($T3) : '';
	$WebPage = &url_format($T4);
	$HourMinSec = &PrettyTime($HH,$MM,$SS);
	$WeekDay = $LongWeekDays[$T12];
	$Month = $LongMonths[$T10];
	$Year += 1900;
	$Redirect = ($Redirect eq 'export') ? 1 : 0;
	
	print "A visitor from <B>$Host</B> ($IPAddress)\n";
	if (($Redirect) && ($Referer ne $WebPage)) {
		print "was redirected to $WebPage\n";
		print "from $Referer\n";
		}
	elsif ($Redirect) {
		print "visited $WebPage\n";
		}
	else {
		if ($Referer) {
			print "arrived from $Referer,\n";
			}
		else {
			print "arrived without a refering URL,\n";
			}
		print "and visited $WebPage\n";
		}
	print "at $HourMinSec on $WeekDay, $Month $Day, $Year.\n";
	print "This visitor used $Browser.\n";
	print "\n";
	}
print '</PRE>';
print GraphSummary;
$graph_made++;
} # End Show Database Procedure.


# Begin Show Database-Sytle Visitor Flow:
sub show_data_flow {
if ($FORM{'maximum'} =~ /\d+/) {
	$array_size = @LINES;
	if ($FORM{'maximum'} < $array_size) {
		splice(@LINES,0,$array_size - $FORM{'maximum'});
		}
	}
($total_ips,$multiple_hit_ips) = (0,0);
$delimiter = 'Flow_Chart_Delimiter';
foreach (@LINES) {
	next unless (m!^\|([^\|]+)\|([^\|]+)!);
	if ($IPFLOW{$2}) {
		$IPFLOW{$2} .= $delimiter.$_;
		}
	else {
		push(@IPS,$2);
		$IPFLOW{$2} = $_;
		$total_ips++;
		}
	}

print &DatabaseFlowDescription;
print '<PRE>';

foreach $key (reverse @IPS) {
	@LINES = split(/$delimiter/,$IPFLOW{$key});

	$num_hits = scalar @LINES;
	if (($num_hits > 1) || ($FORM{'format'} eq 'Sort All by Visitor')) {

		# Multiple documents visited; generate flow chart:

		$multiple_hit_ips++ if ($num_hits > 1);
		@terms = split(/\|/,$LINES[0]);
		
		$HourMinSec = &PrettyTime($terms[8],$terms[7],$terms[6]);

		if ($num_hits > 2) {
			$NumTimes = "$num_hits times";
			}
		elsif ($num_hits == 1) {
			$NumTimes = 'once';
			}
		else {
			$NumTimes = 'twice';
			}

$FullYear = 1900 + $terms[11];

print <<"EOM";
<HR SIZE="1" WIDTH="80%">

A visitor from <B>$terms[1]</B> ($terms[2]) was logged $NumTimes,
starting at $HourMinSec on $LongWeekDays[$terms[12]], $LongMonths[$terms[10]] $terms[9], $FullYear.
The initial browser was $terms[5].

EOM

print '  This visitor first ';
$first = 'true';
foreach (@LINES) {
		@terms = split(/\|/,$_);
		if ($first ne 'true') {

			$ThisTime = ((((($terms[13] * 24) + $terms[8]) * 60) + $terms[7]) * 60) + $terms[6];

			# $INT is the time interval in seconds:
			
			$INT = ($ThisTime - $PrevTime);
			
			$seconds = sprintf("%02.f",($INT%60));
			$minutes = sprintf("%02.f",(($INT%3600)/60));
			$hours = sprintf("%2.f",(($INT%86400)/3600));

			print "  $hours:$minutes:$seconds";
			if ($days = int($INT/86400)) {
				print " and $days day";
				print 's' if ($days > 1);
				}
			print " later, ";
			}

		if (($terms[14] eq 'export') && ($terms[3] ne $terms[4])) {
			print "was redirected to " . &url_format($terms[4]) . "\n";
			print "      from " . &url_format($terms[3]) . "\n\n";
			}
		elsif ($terms[14] eq 'export') { # Image redirect
			print "dropped by " . &url_format($terms[3]) . "\n\n";
			}
		else {
			if ($terms[3]) {
				print "arrived from " . &url_format($terms[3]) . "\n";
				}
			else {
				print "arrived without a refering URL,\n";
				}
			print "    and visited " . &url_format($terms[4]) . "\n";
			print "\n";
			}
		$first = 'false';

		$PrevTime = ((((($terms[13] * 24) + $terms[8]) * 60) + $terms[7]) * 60) + $terms[6];

		} # End foreach hit per IP.
	} # End test of more than one hit per IP.
} # End foreach loop through all IP's.

print <<"EOM";

<HR SIZE="1" WIDTH="80%">

</PRE>

<P><B><U>Summary:</U></B></P>

<P>There were visits from $total_ips distinct IP addresses. 
However, only $multiple_hit_ips of these visited more than one 
document.</P>
EOM

$graph_made++;
} # End Show Database-Style Visitor Flow.


# Begin Export/Delete Log Procedure:
sub kill_it {

unless (open(NEWLOG,">$LogFile")) {
	&DebugThenExit(1);
	}

if ($StartNumber) {
	$NumLogEntries = scalar @LINES;
	foreach (@LINES) {
		print NEWLOG;
		}
	}
close(NEWLOG);
$NewLogSize = -s $LogFile;
print <<"EOM";

<P><B><U>Access Log Deleted:</U></B></P>
<BLOCKQUOTE>
	<P>The log file has been successfully deleted.</P>

EOM

print <<"EOM" if ($StartNumber);

<P>Hits since $StartString were retained. There are now 
$NumLogEntries entries in the access log. The new log size is 
$NewLogSize bytes.</P>

EOM

print <<"EOM";

</BLOCKQUOTE>

EOM
$graph_made++;
} # End Export/Delete Log Procedure.


sub get_browser_name {
	local($_) = shift;
	return 'Other Agents' unless ($_);
	if (m!Mozilla/(\d)!i) {
		if (m!compatible!i) {
			if (m!WebTV!i) {
				return 'WebTV';
				}
			elsif (m!AOL.(\d)!i) {
				return "AOL's Browser v$1.x";
				}
			elsif (m!AOL-IWENG (\d)!i) {
				return "AOL's Browser v$1.x";
				}
			elsif (m!MSIE.(\d)!i) {
				return "MSIE v$1.x";
				}
			elsif (m!Opera!i) {
				return 'Opera';
				}
			elsif (m!Mozilla/3.01.\(compatible;?\)!) {
				return 'Cache/Proxy server';
				}
			elsif (m!Powermarks!i) {
				return 'Powermarks bookmark thing';
				}
			elsif (m!FDSE.robot!i) {
				return 'FDSE robot';
				}
			elsif (m!NetMind-Minder!i) {
				return 'NetMind-Minder';
				}
			elsif (m!BorderManager!i) {
				return 'Border Manager';
				}
			else {
				return 'Other Agents';
				}
			}
		elsif (m!Mozilla/4.(\d)!i) {
			return "Netscape v4.$1";
			}
		else {
			return "Netscape v$1.x";
			}
		}
	elsif (m!Microsoft Internet Explorer/(\d)!i) {
		return "MSIE v$1.x";
		}
	elsif (m!IWENG/(\d)!i) {
		return "AOL's Browser v$1.x";
		}
	elsif (m!aolbrowser/(\d)!i) {
		return "AOL's Browser v$1.x";
		}
	elsif (m!Lynx!i) {
		return 'Lynx';
		}
	elsif (m!WebExplorer!i) {
		return 'IBM WebExplorer';
		}
	elsif (m!QuarterDeck!i) {
		return 'QuarterDeck Mosaic';
		}
	elsif (m!SPRY!i) {
		return 'Compuserve\'s SPRY Mosaic';
		}
	elsif (m!Enhanced_Mosaic!i) {
		return 'NCSA Mosaic (Enhanced)';
		}
	elsif (m!Mosaic!i) {
		return 'NCSA Mosaic';
		}
	elsif (m!PRODIGY!i) {
		return 'Prodigy\'s Browser';
		}
	else {
		return 'Other Agents';
		}
	}

sub get_os_type {

# this guy needs some work: check for NT 3.51/4.0/5.0; check for Linux 
# versus Unix or whatever; make sure Netscape returns Windows 98 instead 
# of Windows 95.

local($_) = shift;
return 'Unknown Platform' unless $_;
if (m!(win95)|(windows 95)!oi) {
	return 'Windows 95';
	}
elsif (m!(win98)|(windows 98)!oi) {
	return 'Windows 98';
	}
elsif (m!Windows NT 5!i) {
	return 'Windows 2000';
	}
elsif (m!(winNT)|(Windows NT)!oi) {
	return 'Windows NT';
	}
elsif (m!win16!oi) {
	return 'Windows 16-bit';
	}
elsif (m!win32!oi) {
	return 'Windows 32-bit';
	}
elsif (m!Windows 3.1!oi) {
	return 'Windows 3.1';
	}
elsif (m!Windows!oi) {
	if (m!32bit!oi) {
		return 'Windows 32-bit';
		}
	else {
		return 'Windows 16-bit';
		}
	}
elsif (m!Window!oi) {
	return 'X Windows';
	}
elsif (m!Mac!oi) {
	if (m!PPC!oi) {
		return 'Macintosh (PowerPC)';
		}
	elsif (m!PowerPC!oi) {
		return 'Macintosh (PowerPC)';
		}
	else {
		return 'Macintosh (68K)';
		}
	}
elsif (m!Amiga!oi) {
	return 'Amiga';
	}
elsif (m!OS/2!oi) {
	return 'OS/2';
	}
elsif (m!X11!oi) {
	if (m!HP-UX!oi) {
		return 'UNIX (HP-UX)';
		}
	elsif (m!Linux!oi) {
		return 'UNIX (Linux)';
		}
	elsif (m!SunOS!oi) {
		return 'UNIX (SunOS)';
		}
	else {
		return 'UNIX (Other/Unspecified)';
		}
	}
elsif (m!IWENG!oi) {
	return 'Windows 16-bit';
	}
elsif (m!Lynx!oi) {
	return 'UNIX (Other/Unspecified)';
	}
elsif (m!WebTV!oi) {
	return 'WebTV';
	}
else {
	return 'Unknown Platform';
	}
}


sub Escape {
local($_) = shift;
tr/+/ /;
s/%([a-fA-F0-9][a-fA-F0-9])/pack('C',hex($1))/eg;
return $_;
}
			

sub url_format {

# URL Format takes a URL and turns it into a hyperlink with an 
# abbreviated (no "http://") viewable output.  Links from Altavista 
# and other search engines are formatted logically:

local($_) = shift;
if ((m!$My_Web_Address!i) && (m!http://(.*)!i)) {
	# Use %LocalAddressTitlePairs if it exists:
	if ($UseLocalAddressTitlePairs == 1) {
		foreach $Address (keys %LocalAddressTitlePairs) {
			return "<A HREF=\"$_\"$TARGET>$LocalAddressTitlePairs{$_}</A>" if (m!^$Address$!i);
			}
		}
	return $1;
	}
elsif (($_ !~ /\?/) && (m!http://(.*)!i)) {
	return "<A HREF=\"$_\"$TARGET>$1</A>";
	}
elsif ($_ !~ m!\?!) {
	return "<A HREF=\"$_\"$TARGET>$_</A>";
	}
elsif (/\:\/\/([^\/]*)altavista\.([^\/|\?]*)(.*)\?.*q\=([^\&]+).*stq\=(\d+)/i) {
	($Host,$Domain,$Terms,$Rank) = ($1,$2,&Escape($4),$5);
	return "<A HREF=\"$_\"$TARGET>$Host"."altavista.$Domain</A> <I>$Terms ".($Rank+1).'-'.($Rank+10).'</I>';
	}
elsif (/\:\/\/([^\/]*)altavista\.([^\/|\?]*)(.*)\?.*q=([^\&]+).*navig(\d+)?/i) {
	($Host,$Domain,$Terms,$Rank) = ($1,$2,&Escape($4),($5?$5:0));
	return "<A HREF=\"$_\"$TARGET>$Host"."altavista.$Domain</A> <I>$Terms ".($Rank+1).'-'.($Rank+10).'</I>';
	}
elsif (/\:\/\/([^\/]*)altavista\.([^\/|\?]*)(.*)\?.*q\=([^\&]+)/i) {
	($Host,$Domain,$Terms) = ($1,$2,&Escape($4));
	return "<A HREF=\"$_\"$TARGET>$Host"."altavista.$Domain</A> <I>$Terms 1-10</I>";
	}
elsif (/\:\/\/([^\/]*)webcrawler\.([^\/]+)(.*)\?(s|search|searchText)\=([^\&]+).*\&start\=(\d+).*perPage\=(\d+)/i) {
	($Host,$Domain,$Terms,$Rank,$Increment) = ($1,$2,&Escape($5),$6,$7);
	return "<A HREF=\"$_\"$TARGET>$Host"."webcrawler.$Domain</A> <I>$Terms ".($Rank+1)."-".($Rank+$Increment)."</I>";
	}
elsif (/\:\/\/([^\/]*)webcrawler\.([^\/]+).*(s|search|searchText)\=([^\&]+)/i) {
	($Host,$Domain,$Terms) = ($1,$2,&Escape($4));
	return "<A HREF=\"$_\"$TARGET>$Host"."webcrawler.$Domain</A> <I>$Terms 1-25</I>";
	}

# Experimental: metacrawler.com
#http://search4.metacrawler.com/crawler?general=sex&method=0&sid=1704957883&sno=140168&start=20&domainLimit=0&rpp=20&timeout=5&max=10&format=0

elsif (/\:\/\/([^\/]*)metacrawler\.([^\/]+).*general\=([^\&]+).*start\=(\d+).*rpp\=(\d+)/i) {
	($Host,$Domain,$Terms,$Rank,$Increment) = ($1,$2,&Escape($3),$4,$5);
	return "<A HREF=\"$_\"$TARGET>$Host"."metacrawler.$Domain</A> <I>$Terms ".($Rank+1)."-".($Rank+$Increment)."</I>";
	}
elsif (/\:\/\/([^\/]*)metacrawler\.([^\/]+).*general\=([^\&]+)/i) {
	($Host,$Domain,$Terms) = ($1,$2,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host"."metacrawler.$Domain</A> <I>$Terms 1-25</I>";
	}

# Experimental: netfind.aol.com
# http://netfind.aol.com/search.gw?lk=excite_netfind_us&c=web&start=10&showSummary=true&s=sex&perPage=10&next=next

elsif (/\:\/\/([^\/]*)netfind.aol\.([^\/]+).*start=(\d+).*&search=([^\&]+).*start=(\d+).*perPage=(\d+)/i) {
	($Host,$Domain,$Terms,$Rank,$Increment) = ($1,$2,&Escape($5),$4,$6);
	return "<A HREF=\"$_\"$TARGET>$Host"."netfind.aol.$Domain</A> <I>$Terms ".($Rank+1)."-".($Rank+$Increment)."</I>";
	}
elsif (/\:\/\/([^\/]*)netfind\.aol\.([^\/]+).*search=([^\&]+)/i) {
	($Host,$Domain,$Terms) = ($1,$2,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host"."netfind.aol.$Domain</A> <I>$Terms 1-25</I>";
	}
elsif (/\:\/\/([^\.]*)\.infoseek\.com(.*)\?.*qt=([^\&]+).*st=(\d+)?/i) {
	($Host,$Rank,$Terms) = ($1,$5,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host.infoseek.com</A> <I>$Terms ".($Rank+1).'-'.($Rank+10).'</I>';
	}
elsif (/\:\/\/([^\.]*)\.infoseek\.com(.*)\?.*qt=([^\&]+)/i) {
	($Host,$Terms) = ($1,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host.infoseek.com</A> <I>$Terms 1-10</I>";
	}
elsif (/\:\/\/([^\.]*)\.infoseek\.com(.*)\?.*oq=([^\&]+).*(st=)?(\d+)?/i) {
	($Host,$Rank,$Terms) = ($1,$5,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host.infoseek.com</A> <I>$Terms ".($Rank+1).'-'.($Rank+10).'</I>';
	}
elsif (/\:\/\/([^\.]*)\.infoseek\.com(.*)\?.*oq=([^\&]+).*(st=)?(\d+)?/i) {
	($Host,$Terms) = ($1,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host.infoseek.com</A> <I>$Terms 1-10</I>";
	}
elsif (/\:\/\/([^\.]*)\.excite\.com(.*)\?(.*)/i) {
	($Host,$rank,$Increment) = ($1,0,10);
	@parts = split(/\&/,$3);
	foreach $part (@parts) {
		if ($part =~ /^search=(.*)/) {
			$terms = $1;
			$terms =~ tr/+/ /;
			$terms =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack('C',hex($1))/eg;
			}
		if ($part =~ /^perPage=(.*)/) {
			$Increment = $1;
			}
		if ($part =~ /^start=(.*)/) {
			$rank = ($1 + $Increment);
			}
		}
	return "<A HREF=\"$_\"$TARGET>$Host.excite.com</A> <I>$terms " . ($rank + 1) . "-" . ($rank + $Increment) . "</I>";
	}
elsif (/\:\/\/([^\/]*)yahoo\.([^\/]*).*\?.*p=([^\&]+).*b=(\d+)/i) {
	($Host,$Domain,$Terms,$Rank) = ($1,$2,&Escape($3),$4);
	return "<A HREF=\"$_\"$TARGET>$Host"."yahoo.$Domain</A> <I>$Terms ".$Rank.'-'.($Rank + 19).'</I>';
	}
elsif (/\:\/\/([^\/]*)yahoo\.([^\/]*).*\?.*p=([^\&]+)/i) {
	($Host,$Domain,$Terms) = ($1,$2,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host"."yahoo.$Domain</A> <I>$Terms 1-20</I>";
	}
elsif (/\:\/\/([^\/]*)hotbot\.([^\/]*).*\?.*MT=([^\&]+).*base=(\d+)/i) {
	($Host,$Domain,$Terms,$Rank,$NextRank) = ($1,$2,&Escape($3),($4+11),($4+20));
	return "<A HREF=\"$_\"$TARGET>$Host"."hotbot.$Domain</A> <I>$Terms $Rank-$NextRank</I>";
	}
elsif (/\:\/\/([^\/]*)hotbot\.([^\/]*).*\?.*MT=([^\&]+)/i) {
	($Host,$Domain,$Terms) = ($1,$2,&Escape($3));
	return "<A HREF=\"$_\"$TARGET>$Host"."hotbot.$Domain</A> <I>$Terms 1-10</I>";
	}
elsif (/http\:\/\/(.*)/i) {
	return "<A HREF=\"$_\"$TARGET>$1</A>";
	}
else {
	return "<A HREF=\"$_\"$TARGET>$_</A>";
	}
} # End url_format procedure.



sub AuthPref {
($PrefsFile) = @_;
# Try to open the prefs file:
unless (open(PREFS,"<$PrefsFile")) {
	&DebugThenExit(1);
	}
	
# Initialize $PREF{'format'} and {'maximum'}
%PREF = ('format','','maximum','','end_date','','start_date','','since_last','',
		'last_number','','last_number_temp','','AuthIP','','last_string','',
		'MaxWidth',400,'Filter','','recent','','NumSort','CHECKED','NewWindow','CHECKED',
		'Highlight','CHECKED','HideDefaultDoc','CHECKED','HidePoundSigns','CHECKED');

foreach (<PREFS>) {
	next unless (m!^([^\|]+)\|([^\|]*)!);
	$PREF{$1} = $2;
	}
close(PREFS);
# Now authenticate this user:
AUTH: {
last AUTH if (($AllowAnonymousForGraphs == 1) && ($FORM{'Target'} ne 'Preferences') && (!$FORM{'terminate'}));
last AUTH if ($ENV{'REMOTE_ADDR'} eq $PREF{'AuthIP'});
if (($Password eq $FORM{'password'}) && ($Username eq $FORM{'username'})) {
	$PREF{'AuthIP'} = $ENV{'REMOTE_ADDR'};
	last AUTH;
	}
if (($FORM{'password'}) || ($FORM{'username'})) {
	# check to see if crypt is in effect:
	if ($FORM{'username'} eq $Username) {
		last AUTH if ($Password eq crypt($FORM{'password'},substr(0,2,$FORM{'password'})));
		}
	print "<P>Invalid username or password!</P>\n";
	print "<A HREF=\"$This_Script_Address\">feel free to try again...</A>\n";
	}
else {
	print &Authenticate;
	}
exit;
} # End AUTH block.
# User authenticated, continue parsing the preferences.  Save them if 
# necessary:
$ThisDayNum = ($MyT[5] * 1000) + $MyT[7] + 1900000;
if (($PREF{'last_number_temp'} < $ThisDayNum) || (!$PREF{'last_number'})) {
	$PREF{'last_number'} = $PREF{'last_number_temp'};
	$PREF{'last_string'} = $PREF{'last_string_temp'};
	}
$PREF{'last_number_temp'} = $ThisDayNum;
$PREF{'last_string_temp'} = (&DateByNum((@MyT)[4,3],$MyT[5]+1900))[0];
if ($FORM{'incoming'}) {
	for ('maximum','MaxWidth','start_date','end_date','Filter','format') {
		$PREF{$_} = $FORM{$_};
		undef($FORM{$_});
		}
	for (keys %GraphOptions,'since_last','recent','NumSort','NewWindow','Highlight','HideDefaultDoc','HidePoundSigns') {
		$PREF{$_} = $FORM{$_} ? 'CHECKED' : '';
		undef($FORM{$_});
		}
	}
$PREF{'MaxWidth'} = 400 unless ($PREF{'MaxWidth'});
# abbreviated flag for numerical sorting:
$NUMS = $PREF{'NumSort'} eq 'CHECKED' ? 1 : 0;
# abbreviate bgcolor attribute:
if ($PREF{'Highlight'} eq 'CHECKED') {
	$BGCOLOR = 'BGCOLOR="#dddddd"';
	}
else {
	$BGCOLOR = '';
	}
if ($PREF{'NewWindow'}) {
	$TARGET = ' TARGET="_blank"';
	}
else {
	$TARGET = '';
	}
if ($ENV{'REMOTE_ADDR'} eq $PREF{'AuthIP'}) {
	# only write out preferences for authenticated users:
	unless (open(PREFS,">$prefs")) {
		&DebugThenExit(1);
		}
	foreach $key (sort keys %PREF) {
		next if (($key eq 'AuthIP') && ($FORM{'Target'}) && ($FORM{'Target'} eq 'LogOut'));
		print PREFS $key.'|';
		print PREFS $PREF{$key} if ($PREF{$key});
		print PREFS "|\n";
		}
	close(PREFS);
	}
return %PREF;
# End save preferences.
} # End AuthPref();


sub DateIsValid {
	($MM,$DD,$YYYY) = @_;
	for ($MM,$DD,$YYYY) {
		return 0 unless m!^\d*$!;
		}
	return 0 if (($MM < 1) || ($MM > 12) || ($DD < 1));
	if ($YYYY % 4) {
		return 0 if ($DD > (31,29,31,30,31,30,31,31,30,31,30,31)[$MM-1]);
		}
	else {
		return 0 if ($DD > (31,28,31,30,31,30,31,31,30,31,30,31)[$MM-1]);	
		}
	return 1;
	}

sub GetYDAY {
	($MM,$DD,$YYYY) = @_;
	if (($YYYY % 4) == 0) {
		return ((0,31,60,91,121,152,182,213,244,274,305,335)[$MM] + $DD - 1);
		}
	else {
		return ((0,31,59,90,120,151,181,212,243,273,304,334)[$MM] + $DD - 1);
		}
	}

sub DateByNum {
# this is failing for YDAY sometimes.

	# accepts computer date, returns text string, yday.
	($MM, $DD, $YYYY) = @_;

	# test:
	# print "<!-- DateByNum $MM $DD $YYYY -->\n";

	$DD--;$DD++;
	$YYYY += 1900 if ($YYYY < 1000);
	$YDAY = &GetYDAY($MM,$DD,$YYYY);
	$DaysSince1970 = int(($YYYY-1970)*365.25) + $YDAY + 1;
	$WeekDay = $LongWeekDays[(localtime($DaysSince1970 * 86400))[6]];

	# test:
	# print "<!-- resolves to $WeekDay with YDAY as $YDAY -->\n";

	return ("$LongMonths[$MM] $DD, $YYYY", $YDAY);
	}

sub FormatDates {
($StartInput, $EndInput, $Recent, $SinceLast, $LastNumber) = @_;
($StartNumber, $StartString, $EndNumber, $EndString) = (0,'',0,'');
($MM,$DD,$YYYY) = (0,0,0);
MMDDYY: for ($StartInput) {
if ($_) {
	if (m!^\s*(\d{2,2})\D*(\d{2,2})\D*(\d{2,4})?!) {
		($MM,$DD,$YYYY) = ($1,$2,($3 ? $3 : $MyT[5]));
		last MMDDYY if &DateIsValid($MM,$DD,$YYYY);
		}
	if (m!^\s*(\d{1,2})\D+(\d{1,2})\D*(\d{2,4})?!) {
		($MM,$DD,$YYYY) = ($1,$2,($3?$3:$MyT[5]));
		last MMDDYY if &DateIsValid($MM,$DD,$YYYY);
		}
	LITERAL_MONTH: {
		if (m!^\s*(\D+)(\d{1,2})\D*(\d{2,4})?!) {
			($MonthString,$DD,$YYYY) = ($1,$2,($3?$3:$MyT[5]));
			}
		elsif (m!^\s*(\d{1,2})(\D*)(\d{2,4})?!) {
			($MonthString,$DD,$YYYY) = ($2,$1,($3?$3:$MyT[5]));
			}
		else {
			last LITERAL_MONTH;
			}
		for ($MM=1;$MM<=12;$MM++) {
			if ($MonthString =~ m!$ShortMonths[$MM-1]!i) {
				last MMDDYY if &DateIsValid($MM,$DD,$YYYY);
				last LITERAL_MONTH;
				}
			}
		}
	for $X (0..6) {
		if (m!$ShortWeekDays[$X]!i) {
			($MM,$DD,$YYYY) = (localtime(time-((7+$MyT[6]-$X)%7)*86400))[4,3,5];
			$MM++;
			last MMDDYY;
			}
		}
	for $X (0..2) {
		if (m!$ShortDayNames[$X]!i) {
			($MM,$DD,$YYYY) = (localtime(time+($X-1)*86400))[4,3,5];
			$MM++;
			last MMDDYY;
			}
		}
	}
if ($Recent) {
	($MM,$DD,$YYYY) = (localtime((time-86400)))[4,3,5];
	$MM++;
	last MMDDYY;
	}
} # End MMDDYY.

if ($MM && $DD && defined($YYYY)) {
	# kick INT mode, and correct for human->computer month indexing:
	$MM--;
	
	if ($YYYY < 1000) {
		# User is entering an abbreviated date.  Is it 01 for 2001, or 99 for 1999?
		if ($YYYY < 50) {
			$YYYY += 2000;
			}
		else {
			$YYYY += 1900;
			}
		}

	($StartString,$YDAY) = &DateByNum($MM,$DD,$YYYY);
	$StartNumber = ($YYYY * 1000) + $YDAY;
	}
elsif ($SinceLast) {
# or "if $StartInput existed maybe but didn't successfully match anything, and $Recent was not defined,
# but $SinceLast is...
	$StartNumber = $LastNumber;
	$YYYY = int($LastNumber/1000);
	$YDAY = $LastNumber % 1000;
	$DaysSince1970 = int(($YYYY-1970)*365.25) + $YDAY + 1;
	($DD,$MM,$YYYY,$WeekDay) = (localtime($DaysSince1970 * 86400))[3..6];
	$YYYY += 1900;
	$WeekDay = $LongWeekDays[$WeekDay];
	$StartString = "$WeekDay, $LongMonths[$MM] $DD, $YYYY";
	}

# Zero out:
($MM,$DD,$YYYY) = (0,0,0);
MMDDYY: for ($EndInput) {
if ($_) {
	if (m!^\s*(\d{2,2})\D*(\d{2,2})\D*(\d{2,4})?!) {
		($MM,$DD,$YYYY) = ($1,$2,($3?$3:$MyT[5]));
		last MMDDYY if &DateIsValid($MM,$DD,$YYYY);
		}
	if (m!^\s*(\d{1,2})\D+(\d{1,2})\D*(\d{2,4})?!) {
		($MM,$DD,$YYYY) = ($1,$2,($3?$3:$MyT[5]));
		last MMDDYY if &DateIsValid($MM,$DD,$YYYY);
		}
	LITERAL_MONTH: {
		if (m!^\s*(\D+)(\d{1,2})\D*(\d{2,4})?!) {
			($MonthString,$DD,$YYYY) = ($1,$2,($3?$3:$MyT[5]));
			}
		elsif (m!^\s*(\d{1,2})(\D*)(\d{2,4})?!) {
			($MonthString,$DD,$YYYY) = ($2,$1,($3?$3:$MyT[5]));
			}
		else {
			last LITERAL_MONTH;
			}
		for ($MM=1;$MM<=12;$MM++) {
			if ($MonthString =~ m!$ShortMonths[$MM-1]!i) {
				last MMDDYY if &DateIsValid($MM,$DD,$YYYY);
				last LITERAL_MONTH;
				}
			}
		}
	for $X (0..6) {
		if (m!$ShortWeekDays[$X]!i) {
			($MM,$DD,$YYYY) = (localtime(time-((7+$MyT[6]-$X)%7)*86400))[4,3,5];
			$MM++;
			last MMDDYY;
			}
		}
	for $X (0..2) {
		if (m!$ShortDayNames[$X]!i) {
			($MM,$DD,$YYYY) = (localtime(time+($X-1)*86400))[4,3,5];
			$MM++;
			last MMDDYY;
			}
		}
	}
if ($Recent || $SinceLast) {
	($MM,$DD,$YYYY) = (localtime(time))[4,3,5];
	$MM++;
	last MMDDYY;
	}
} # End MMDDYY.
if ($MM && $DD && $YYYY) {
	# kick INT mode, and correct for human->computer month indexing:
	$MM--;
	($EndString,$YDAY) = &DateByNum($MM,$DD,$YYYY);
	unless ($Recent || $SinceLast) {
		if ($YYYY < 1000) {
			# User is entering an abbreviated date.  Is it 01 for 2001, or 99 for 1999?
			if ($YYYY < 50) {
				$YYYY += 2000;
				}
			else {
				$YYYY += 1900;
				}
			}


		$EndNumber = ($YYYY * 1000) + $YDAY;
		}
	}
return ($StartNumber, $StartString, $EndNumber, $EndString);
}

# This runs a filesystem test against $LogFile and dumps a ton of (hopefully) 
# useful information to the screen:
sub DebugThenExit {
if (shift == 1) {
	$err = 'the script encountered a file system error';
	}
else {
	$err = 'you made a debugging request';
	}

# Inform the user that debugging is turned off:
unless ($AllowDebug == 1) {
print <<"EOM";

<BR><BR>
<BLOCKQUOTE>
	<B>This is the AXS debugger.<BR>You're seeing this document because 
	$err.</B>
</BLOCKQUOTE>

<P>Debugging is currently disabled at the script level. To enable it, 
open this file and set: <TT>\$AllowDebug = 1;</TT>. Once you've done 
this, return to this page and you will see the debugging information. If 
there was a file system error, you will see the details and some 
recommendations on how to fix it.</P>
EOM
exit;
	}

print <<"EOM";

<BR><BR>
<BLOCKQUOTE>
	<B>This is the AXS debugger.<BR>You're seeing this document because 
	$err.</B>
</BLOCKQUOTE>

<P><HR SIZE="1"><I>This information may be a security risk, because it 
reveals environment variables, file system information, and your version 
of Perl. However, don't worry about that right now. Use the information 
below to get this script working to your satisfaction. After that's done, 
edit this file (at </I><TT>$0</TT><I>) and turn debugging off by 
setting:</I></P>
<PRE>\t\$AllowDebug = 0;</PRE>
<P><I>This is one tool, of many, to help you out. Read the <A 
HREF="http://www.xav.com/scripts/axs/" TARGET="_blank">trouble-shooting 
guide</A> if you need more detailed assistance.</I><HR SIZE="1"></P>

<DL>
<DT><B><U>Standard Debugging Information:</U></B></DT>
<DD>
<P>You are running the AXS Administration Module version $VERSION in debugging mode.<BR>
The file name of this script is <TT>$0</TT>.<BR>
This script is executing under Perl version $].<BR>
The critical variables are <TT>\$LogFile = "$LogFile";</TT> and 
<TT>\$prefs = "$prefs";</TT>.</P></DD>

<DT><B><U>Filesystem Test for the Log File:</U></B></DT>
<DD>
<PRE>\t\$LogFile = "$LogFile";</PRE>
EOM

if (-e $LogFile) {
	print "<P>The log file, <TT>$LogFile</TT>, exists. ";
	if (open(FILE,">>$LogFile")) {
		close(FILE);
		print "Log file is writable. <B>The filesystem test passed!</B></P>";
		}
	else {
print <<"EOM";
However, the log file is not writable. The filesystem returned <TT>"$!"</TT> 
when this script tried to write to it. You need to change the file 
permissions.<BR><B>The filesystem test failed.</B></P>
EOM
		}
	}
elsif (open(FILE,">>$LogFile")) {
	close(FILE);
print <<"EOM";
<P>The log file, <TT>$LogFile</TT>, did not exist when this script started. 
However, this script attempted to create it for you, and the server 
responded that this was successful. So everything <I>should</I> be fine now. 
Reload this web page, and hopefully you'll see a message that the file system 
test has passed. If it doesn't pass, and instead you get an error or you get 
this message again, then you'll have to manually create the log file and 
set it's permissions.<BR><B>The filesystem test needs to be run again.</B></P>
EOM
	}
else {
print <<"EOM";
<P>The log file, <TT>$LogFile</TT>, doesn't exist. You need to create one and 
give it writable permissions. Alternately, the log file may exist but the 
<TT>\$LogFile</TT> variable might not point to the correct location, in which 
case you will need to change your variable.<BR><B>The filesystem test 
failed.</B></P>
EOM
	}
print <<"EOM";
</DD>

<DT><B><U>Filesystem Test for the Preferences File:</U></B></DT>
<DD>
<PRE>\t\$prefs = "$prefs";</PRE>
EOM

if (-e $prefs) {
	print "<P>The preferences file, <TT>$prefs</TT>, exists. ";
	if (open(FILE,">>$prefs")) {
		close(FILE);
		print "Preferences file is writable. <B>The filesystem test passed!</B></P>";
		}
	else {
print <<"EOM";
However, the preferences file is not writable. The filesystem returned <TT>"$!"</TT> 
when this script tried to write to it. You need to change the file 
permissions.<BR><B>The filesystem test failed.</B></P>
EOM
		}
	}
elsif (open(FILE,">>$prefs")) {
	close(FILE);
print <<"EOM";
<P>The preferences file, <TT>$prefs</TT>, did not exist when this script started. 
However, this script attempted to create it for you, and the server 
responded that this was successful. So everything <I>should</I> be fine now. 
Reload this web page, and hopefully you'll see a message that the file system 
test has passed. If it doesn't pass, and instead you get an error or you get 
this message again, then you'll have to manually create the preferences file and 
set it's permissions.<BR><B>The filesystem test needs to be run again.</B></P>
EOM
	}
else {
print <<"EOM";
<P>The preferences file, <TT>$prefs</TT>, doesn't exist. You need to create one and 
give it writable permissions. Alternately, the preferences file may exist but the 
<TT>\$prefs</TT> variable might not point to the correct location, in which 
case you will need to change your variable.<BR><B>The filesystem test 
failed.</B></P>
EOM
	}
print <<"EOM";
</DD>

<DT><B><U>Usage Instructions:</U></B></DT>

<DD>
<P>These instructions work <I>only</I> if your file system test passed.</P>

<P>To view graphs or database listings of the visits to your site, visit 
this script directly without a query string:
<A HREF="$This_Script_Address">$This_Script_Address</A>. If you have trouble 
getting the graphs to show up, edit the <TT>\$This_Script_Address</TT> variable 
to point to the URL of this script.</P>

<P>To manage your preferences or delete your log file, visit:
<A HREF="$This_Script_Address?Target=Preferences">$This_Script_Address?Target=Preferences</A>. 
You should take a few minutes to familiarize yourself with the options on that 
page.</P></DD>

<DT><B><U>Environment Variables:</U></B></DT>
<DD>
<PRE>
EOM
foreach (sort keys %ENV) {print "$_: $ENV{$_}\n";}
print <<"EOM";
</PRE>
</DD>
</DL>
<HR WIDTH="50%" SIZE="1">
<H5 ALIGN="center">Visit <A HREF="http://www.xav.com/scripts/axs/" 
TARGET="_blank">the AXS help page</A> for more information.  AXS is 
copyright 1997-2000 by Fluid Dynamics.</H5>
EOM
exit;
} # End DebugThenExit.

sub AddCommas {
	$_ = reverse shift;
	s!(\d{3,3})!$1,!g;
	$_ = reverse $_;
	s!^,!!o;
	return $_;
	}
