#!/usr/local/bin/perl # AXS Script Set, Administration Module, Version 2.02a # # Copyright 1997 by Fluid Dynamics . Please adhere to # the copyright notice and conditions of use which are described # in the attached help file and hosted at the URL below. # # For latest version and help files, visit: # http://www.xav.com/scripts/axs/ # __________________________________________________________________ $Version = '2.02c'; # Enter the URL of your website. If your site is at both http://me.com # and http://www.me.com, simply enter $http_addr = "me.com";. $http_addr = "http://www.hci.net/~inweb"; # Enter a password. Leave blank ($password = "";) to disable password # locking. The username will always be left blank when you log in. $password = ""; # Enter your anchor page. This will form a link at the top of each AXS # output document: $link_url = "http://www.hci.net/~inweb"; $link_title = "In's Homepage"; # Enter the URL to "red.gif" and "tracker.jpg": $red = "http://www.hci.net/~inweb/axs/red.gif"; $tracker = "http://www.hci.net/~inweb/axs/tracker.jpg"; # Once the script is working to your satisfaction, set the $Debug # variable to OFF. When turned on, it provides more detailed # summaries on memory usage and efficiency, and allows remote queries # of your server's %ENV variables and Perl version. $Debug = "ON"; # We strongly recommend that you 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. However, if you enter them in a different # location, or if you're on a Windows NT platform, you will have to # specify the full path to these files. A full path is something like # "/usr/z/zoltan/log.txt"; it is NOT a URL. $logfile = "log.txt"; $prefs = "axs.dat"; # If for some reason your server doesn't kick out the correct script # name, change this variable to the URL of this script. 98% of you # will want to leave this as is. $cgi_url = $ENV{'SCRIPT_NAME'}; # Enter your favorite network lookup services: $nslookup = "http://www.xav.com/cgi-bin/nslookup.cgi"; $whois = "http://rs.internic.net/cgi-bin/whois"; # Let the request method be either GET or POST. POST is neater, but # some systems, especially IIS, require the GET method. If you receive # a '501 Method Not Implemented' error, you will have to change this # variable to GET: $request_method = 'GET'; # No further editing is necessary, but feel free to play around... # # __________________________________________________________________ # For the debugger in monitoring execution time: $StartTime = time; @GraphDescription = ('null', 'Web Browser (Netscape 3.01 Gold)', 'Abbreviated Browser (Netscape 3.X)', 'Operating System (Windows95)', 'Visitors\' Top Level Domains (.com)', 'First Level Domains (xav.com)', 'Full Server Address (noc.xav.com)', 'Visitor IP Address (206.134.243.3)', 'Hits from Other Sites (Full URL)', 'Hits from Other Sites (Domain Only)', 'Hyperlinks Followed From This Site', 'Hits to Local Documents', 'Average Number of Hits Per Visitor', 'Hits By Day of Year', 'Hits By Day of the Week', 'Hits By Hour of the Day'); @weekdays = ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'); @months = ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'); @mon_array = ('buffer',0,31,59,90,120,151,181,212,243,273,304,334); %db_options = ('a','Sort All by Time','b','Sort All by Visitor','c','Visitor Flow Only'); DEBUG_OPTION: { last DEBUG_OPTION unless ($Debug eq 'ON'); last DEBUG_OPTION unless ($ENV{'QUERY_STRING'} =~ /^debugme$/i); print <) { ($N,$V) = split(/\|/,$_); $PREF{$N} = $V; } close(PREFS); AUTH: { last AUTH if ($ENV{'REMOTE_ADDR'} eq $PREF{'auth_ip'}); if (($password eq $FORM{'password'}) && (!$FORM{'username'})) { $PREF{'auth_ip'} = $ENV{'REMOTE_ADDR'}; last AUTH; } elsif (($FORM{'password'}) || ($FORM{'username'})) { print "Content-type: text/plain\n\n"; print "Invalid password or username.\n"; exit; } print < Authentication Required
Welcome to AXS


Please be advised:
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.
Username:
Password:
EOM print < EOM print <
This script is operated by $ENV{'SERVER_ADMIN'}, 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 noc\@xav.com.


AXS Script Set Version $Version is Copyright 1997 by Fluid Dynamics.
Visit the AXS Page for help files and most recent version.
EOM exit; } # End AUTH block. ($N,$N,$N,$mday,$mon,$this_year,$N,$this_day) = localtime(time); if (($PREF{'last_number_temp'} < $this_day) || (!$PREF{'last_number'})) { $PREF{'last_number'} = $PREF{'last_number_temp'}; $PREF{'last_string'} = $PREF{'last_string_temp'}; } $PREF{'last_number_temp'} = $this_day; $PREF{'last_string_temp'} = "$months[$mon] $mday"; if ($FORM{'special'} eq 'Enable Java') { $PREF{'java'} = 'CHECKED'; } if ($FORM{'incoming'}) { for ('maximum','max_pixel_width','start_date','end_date','file_string','format') { $PREF{$_} = $FORM{$_}; } for ('since_last','recent','java','force_lynx','numsort','new_window') { if ($FORM{$_}) { $PREF{$_} = 'CHECKED'; } else { undef($PREF{$_}); } } for (1..15) { $count = $_; $count = "0$count" if ($count < 10); undef($PREF{"s$count"}); $PREF{"s$count"} = 'CHECKED' if ($FORM{"s$count"}); undef($FORM{"s$count"}); } } $PREF{'max_pixel_width'} = 400 unless ($PREF{'max_pixel_width'}); $user_os = &get_os_type($ENV{'HTTP_USER_AGENT'}); if ($user_os =~ /mac/i) { $PREF{'GRAPH-H'} = 7; } else { $PREF{'GRAPH-H'} = 10; } $usragent = &get_browser_name($ENV{'HTTP_USER_AGENT'}); if (($usragent =~ /lynx|"MSIE v2"/i) || ($PREF{'force_lynx'} eq 'CHECKED')) { undef($PREF{'java'}); $PREF{'output'} = 'Lynx Mode'; } else { $PREF{'output'} = 'Graphical Mode'; } if ($PREF{'new_window'}) { $TARGET = ' TARGET="_blank"'; } else { $TARGET = ''; } open(PREFS,">$prefs") || &Error_Prefs_File; foreach $key (sort keys %PREF) { print PREFS "$key|$PREF{$key}|\n"; } close(PREFS); # End save preferences. # read_access_log opens the log file and stores all of the hits in the # @LINES array. It runs whichever filters are necessary, for date/time # or by-file filtering. It returns @LINES, and also $total_hits. open(LOGFILE,"<$logfile") || &Error_Log_File; @LINES = ; close(LOGFILE); $total_hits = scalar @LINES; if (($FORM{'since_last'}) || ($FORM{'maximum'} =~ /\D+/)) { $start_x_date = ($this_year * 1000) + $PREF{'last_number'}; } elsif ($FORM{'recent'}) { $start_x_date = ($this_year * 1000) + ($this_day - 1); } elsif ($FORM{'start_date'}) { @digits = split(//,$FORM{'start_date'}); $start_x_date = "$digits[0]$digits[1]000"; $month_index = $digits[2].$digits[3]; $start_x_date += $mon_array[$month_index]; $start_x_date += ("$digits[4]$digits[5]" - 1); if ($FORM{'end_date'}) { @digits = split(//,$FORM{'end_date'}); $end_x_date = "$digits[0]$digits[1]000"; $month_index = $digits[2].$digits[3]; $end_x_date += $mon_array[$month_index]; $end_x_date += ("$digits[4]$digits[5]" - 1); } } if ($start_x_date) { $start_number = 0; $end_number = $total_hits; $line_number = 0; foreach (@LINES) { $line_number++; @terms = split(/\|/,$_); $record_x_date = ($terms[11] * 1000) + $terms[13]; if ($start_number) { if ($record_x_date > $end_x_date) { $end_number = $line_number; last; } } else { if ($record_x_date >= $start_x_date) { $start_number = $line_number; last unless ($end_x_date); } } } if ($end_x_date) { splice(@LINES,$end_number,$total_hits); } splice(@LINES,0,$start_number); } # End Date Filtering. # Begin Filter by File String: if ($FORM{'file_string'}) { foreach (@LINES) { @terms = split(/\|/,$_); if ($terms[14] ne 'export') { if ($terms[4] =~ /$FORM{'file_string'}/i) { push(@NEWLINES,$_); } } elsif ($terms[3] =~ /$FORM{'file_string'}/i) { push(@NEWLINES,$_); } } @LINES = @NEWLINES; } # Now we print the HTML header which goes at the top of every page: print < AXS Administrator's Page EOM print < EOM print <
AXS by Fluid Dynamics
[ - Main Menu - Back to $link_title - ]


EOM # Finished printing HTML header. Now determine which subprocedure to # route to based on the input: if ($FORM{'special'} eq 'Enable Java') { &intro; &end_html; exit; } if ($FORM{'show_data'}) { if ($FORM{'format'} eq 'a') { &show_data; } else { &show_data_flow; } } if ($FORM{'s01'}) { &make_stats('5-Type of Web Browser-0'); } if ($FORM{'s02'}) { &make_stats('5-Abbreviated Web Browser-short'); } if ($FORM{'s03'}) { &make_stats('5-Operating System-os'); } if ($FORM{'s04'}) { &make_stats('1-Top Level Domains-tld'); } if ($FORM{'s05'}) { &make_stats('1-Remote Server (Whois with Link)-abbr'); } if ($FORM{'s06'}) { &make_stats('1-Complete Remote Server Address-full'); } if ($FORM{'s07'}) { &make_stats('2-IP Address (Lookup with Link)-0'); } if ($FORM{'s08'}) { &make_stats('3-Full Referring URL-inbound'); } if ($FORM{'s09'}) { &make_stats('3-Referring URL [Domain Only]-inbound_domain'); } if ($FORM{'s10'}) { &make_stats('4-Links Followed-remote'); } if ($FORM{'s11'}) { &make_stats('4-Document Hit-local'); } if ($FORM{'s12'}) { &avg_docs; } if ($FORM{'s13'}) { &make_stats('13-Day of the Year-0'); } if ($FORM{'s14'}) { &make_stats('12-Day of the Week-0'); } if ($FORM{'s15'}) { &make_stats('8-Hour of the Day-0'); } if ($FORM{'terminate'}) { &kill_it; } unless ($graph_made) { if ($FORM{'Target'} eq 'Preferences') { &customize; } else { &intro; } } &end_html; sub end_html { if ($graph_made) { print '


Return to Main Menu"; } print < AXS Script Set Version $Version is Copyright 1997 by Fluid Dynamics.
Visit the AXS Page for help files and most recent version.
EOM # End Print End of HTML. } # Begin Introductory Page Procedure: sub intro { print "
\n" unless ($PREF{'output'} eq "Lynx Mode"); print <AXS keeps records on visits to your site. This companion script, AX-ADMIN, allows you to display these records in meaningful graph and database formats. We currently have $total_hits hits to work with.

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'}.

Create Graphs Based On:
EOM print "
\n" unless ($PREF{'output'} eq "Lynx Mode"); print < EOM for (1..15) { if ($GraphDescription[$_] =~ /^([^\(]*) \((.*)\)$/) { $Description = $1; $Example = $2; } else { $Description = $GraphDescription[$_]; undef($Example); } $Number = $_; $Number = '0'.$Number if (length($_) < 2); print " "; if ($PREF{'java'}) { print "$Description"; } else { print $Description; } print " ($Example)" if ($Example); print '
' . "\n"; } print < \  EOM if ($PREF{'java'} eq 'CHECKED') { print '
\n"; print "\n\n"; } else { print "
\n"; } print <
AXS now has a logo! EOM print "
\n" unless ($PREF{'output'} eq "Lynx Mode"); print <Graphing Filters

By default, AXS will graph all hits in the database. However, with these filters, you can restrict graphs to recent hits, critical files, or both.

\  \  Graph EOM if ($PREF{'java'}) { print <only hits since my last visit on $PREF{'last_string'}
\  \  Graph only hits from yesterday and today, or specify:

Start Date (yymmdd)
End Date (yymmdd)
File or Directory Name
EOM } else { # No Java: print <\n\  \  Graph only hits from yesterday and today, or specify:

\  Start Date (yymmdd)
\  End Date (yymmdd)
\  File or Directory Name
EOM } # End Java print ''; print '

' unless ($PREF{'output'} eq "Lynx Mode"); print <
For best results, customize your settings. EOM } # End Introductory Page. sub customize { $logsize = int((-s $logfile) / 1000); if ($logsize < 500) { $logadvice = "that's not too bad"; } elsif ($logsize < 1000) { $logadvice = "it's starting to get up there"; } else { $logadvice = "you may want to delete it"; } print <
Because you'll typically generate the same graphs repeatedly, AXS allows you to specify default settings. Enter your most common settings below. Later, AX-ADMIN will select these values automatically.
"; if ($PREF{'java'}) { print "$Description"; } else { print $Description; } print " ($Example)" if ($Example); print '
' . "\n"; } print <
Graphing Filters

AXS will automatically graph all hits in the database. Since you'll sometimes want to restrict graphs to recent hits, key files, or both, you can narrow the data range with these filters.
\  \  Graph EOM if ($PREF{'java'}) { print <only hits since my last visit on $PREF{'last_string'}
\  \  Graph only hits from yesterday and today, or specify:

Start Date (yymmdd)
End Date (yymmdd)
File or Directory Name
EOM } else { # No Java: print <\n\  \  Graph only hits from yesterday and today, or specify:

\  Start Date (yymmdd)
\  End Date (yymmdd)
\  File or Directory Name
EOM } # End Java print <
JavaScript
\  \  EOM if ($PREF{'java'} eq 'CHECKED') { print <Enable JavaScript
The JavaScript features are currently turned on and should improve the performance of AXS. However, if they cause errors or aren't needed, you may disable them by unclicking the checkbox above. EOM } elsif ($PREF{'force_lynx'} eq 'CHECKED') { print <Enable JavaScript
JavaScript is currently disabled because we are in Lynx-only mode. You'll have to unselect the Lynx-only output (below) before we can ascend to the complexity of JavaScript. EOM } else { print <Enable JavaScript
JavaScript is currently disabled. We recommend that you turn it on - just click on the checkbox above. This will allow you to use your default set of graphs. EOM } print <

Graphics Output
\  \  EOM if ($PREF{'java'}) { print 'Force Lynx-Style Output
'; print "\n"; print '  Maximum Pixel Width of Graphs
'; } else { print 'Force Lynx-Style Output
'; print "\n"; print '  Maximum Pixel Width of Graphs
'; } if ($PREF{'force_lynx'} eq 'CHECKED') { print <We are currently forcing an HTML output that is optimized for the popular text-only Unix browser, Lynx. In this mode, the Java is automatically disabled, and the interface uses less screen area. This feature is primarily for older browsers with lower screen resolution. EOM } else { print <We are running in full graphic mode. If you are using an older browser, or have a screen resolution below 800x600, you may find the Lynx output friendlier. n.b. - AXS is optimized for Netscape Navigator 3.01, but most anything will work. EOM } print <

Following Links
\  \  EOM if ($PREF{'java'}) { print 'Open Links in a New Window
'; } else { print 'Open Links in a New Window
'; } if ($PREF{'new_window'} eq 'CHECKED') { print <When backtracking links from refering sites, or following other links, the page should come up in a new browser window. This saves your graphs so that you don't have to rebuild them every time. EOM } else { print <All links will open the new page in the current browser window (the default web behavior). EOM } print <

Sorting the Graphs
\  \  EOM if ($PREF{'java'}) { print 'Sort by Highest Number of Hits
'; } else { print 'Sort by Highest Number of Hits
'; } print <Numerical sorting shows the items with most hits at the top, and works it's way down. For some applications this is more useful (see who's linking to you, etc.). For other applications, like browser usage, the default alpha sorting is best.



Log Management

The access log will grow by about a kilobyte for every six hits, eventually becoming too large for processing (it's currently at $logsize kb - $logadvice). 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.
EOM $graph_made = 'true'; } # 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. # # name percent number picture # # alt: # # name (long, more than say 30 characters....) #
percent number picture sub print_line { ($N,$V) = split(/\|/,shift); ($outN = $N) =~ s/\<([^\>]*)\>//g; if ($q == 8) { print "$N\ "; } elsif (length($outN) > 48) { print '', $N; print '', '-' x 48, ''; } else { print "$N"; } if ($relevant_hits) { $percentage_real = $V / $relevant_hits; } $percentage_real = sprintf("%.2f",(100 * $percentage_real)); print "$percentage_real\%"; if ($V) { print "$V"; $width = int($multiplier * $V); $width = 1 unless $width; print "\"";"; } else { print '0
'; } print '', "\n"; } # End Print Line. # Begin Main Graphing Procedure: sub make_stats { ($q, $graph_name, $detail) = split(/\-/,shift); print '





' if ($graph_made eq 'true'); print "$graph_name : Percent : Hits : Graph

\n"; print "\n"; $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]); next if ($xSQL[3] =~ /$http_addr/i); if (($detail =~ /domain/) && ($xSQL[3] =~ /^([^\/]+)\/\/([^\/]*)/)) { $xSQL[3] = $1.'//'.$2; next if ($1 =~ /file/i); } } # $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'); next if ($xSQL[4] =~ /$http_addr/i); } elsif ($detail eq 'local') { $xSQL[4] = $xSQL[3] if ($xSQL[14] eq 'export'); next unless ($xSQL[4] =~ /^$http_addr/i); } } # 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{'max_pixel_width'} / $max_var) if ($max_var > 0); if ($relevant_hits > 0) { if ($q == 13) { $month_count = 0; for ($i=0;$i<365;$i++) { $month_count++ if ($i == $mon_array[$month_count + 2]); $mday = (($i - $mon_array[$month_count + 1]) + 1); $day = "$months[$month_count] $mday"; &print_line("$day|$ASTA{i}"); } } elsif ($q == 12) { for (0..6) { &print_line("$weekdays[$_]|$ASTA{$_}"); } } elsif ($q == 8) { for (0..23) { if ($_ == 0) { $N = 'Midnight'; } elsif ($_ < 12) { $N = $_.' am'; } elsif ($_ == 12) { $N = 'High noon'; } else { $N = ($_ - 12).' pm'; } &print_line("$N|$ASTA{$_}"); } } elsif (($q == 3) || ($q == 4)) { &numsort if ($PREF{'numsort'} eq 'CHECKED'); foreach (sort keys %ASTA) { $N = (m!(\d+)\|(.*)!) ? $2 : $_; $N = &url_format($N); &print_line("$N|$ASTA{$_}"); } } elsif (($q == 1) && ($detail eq 'abbr')) { &numsort if ($PREF{'numsort'} eq 'CHECKED'); foreach (sort keys %ASTA) { $N = (m!(\d+)\|(.*)!) ? $2 : $_; if (/ IP Address Only/) { $N = 'IP Address Only'; } else { $N = "$N"; } &print_line("$N|$ASTA{$_}"); } } elsif ($q == 2) { &numsort if ($PREF{'numsort'} eq 'CHECKED'); foreach (sort keys %ASTA) { $N = (m!(\d+)\|(.*)!) ? $2 : $_; $N = "$N"; &print_line("$N|$ASTA{$_}"); } } else { &numsort if ($PREF{'numsort'} eq 'CHECKED'); foreach (sort keys %ASTA) { $N = (m!(\d+)\|(.*)!) ? $2 : $_; &print_line("$N|$ASTA{$_}"); } } } else { print ''; } print '
No matches found for your search. Sorry.
'; # Print Summary of the Search: print "

\nSummary:

\n"; print "There were $total_hits hits analyzed"; print ", of which $relevant_hits were relevant.
\n"; if ($Debug eq 'ON') { $EndTime = time; $ExecTime = $EndTime - $StartTime; print "Execution Time was $ExecTime seconds.\n"; $EFFS = scalar %NSTA; $EFFN = scalar %ASTA; print "Sorted hash efficiency was $EFFS.\n"; print "Unsorted efficiency was $EFFN.\n"; } if ($FORM{'file_string'}) { print "Searched only files containing \"$FORM{'file_string'}\".
\n"; } if ($start_yday) { print "Restricted to hits occurring between $start_yday "; print "and $end_yday (by day of year).
\n"; } print '
'; $graph_made = 'true'; } # End Summary Report and End Sub-Procedure. # 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] =~ /$http_addr/) { $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 <
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.

Summary:
There were $total_hits hits analyzed, $internal_hits of which were internal.
EOM if ($FORM{'file_string'}) { print "Searched only files containing \"$FORM{'file_string'}\".
\n"; } if ($start_yday) { print "Restricted to hits occurring between $start_yday "; print "and $end_yday (by day of year).
\n"; } print "
\n"; $graph_made = 'true'; } # End Print Average Document Count. # Begin Show Database Procedure: sub show_data { if ($FORM{'maximum'} =~ /\d+/) { $array_size = @LINES; if ($FORM{'maximum'} < $array_size) { splice(@LINES,0,$array_size - $FORM{'maximum'}); } } print "\n
\n\n";
$Total_Entries = @LINES;
for ($i=0; $i<$Total_Entries; $i++) {
	$most_recent = ($Total_Entries - $i);
	$most_recent--;
	@terms = split(/\|/,$LINES[$most_recent]);
	print "A visitor from $terms[1] ($terms[2])";
	if (($terms[14] eq 'export') && ($terms[3] ne $terms[4])) {
		print "\nwas redirected to " . &url_format($terms[4]) . "\n";
		print "from " . &url_format($terms[3]) . "\n";
		}
	elsif ($terms[14] eq 'export') {
		print "\ndropped by " . &url_format($terms[3]) . "\n";
		}
	else {
		if ($terms[3]) {
			print ",\narriving from " . &url_format($terms[3]) . "\n";
			}
		else {
			print ",\narriving without a refering URL,\n";
			}
		print "visited " . &url_format($terms[4]) . "\n";
		}
	if (($terms[8] < 12) || ($terms[8] eq '00')) {
		$terms[8] = 12 if ($terms[8] eq '00');
		print "at $terms[8]:$terms[7]:$terms[6] AM ";
		}
	else {
		$terms[8] = ($terms[8] - 12) unless ($terms[8] eq 12);
		print "at $terms[8]:$terms[7]:$terms[6] PM ";
		}
	print "on $weekdays[$terms[12]], $months[$terms[10]] ";
	print "$terms[9], 19$terms[11].\n";
	print "This visitor used $terms[5].\n\n";
	}
print "\n
\n"; $graph_made = 'true'; } # 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 $LINE (@LINES) { @terms = split(/\|/,$LINE); if ($IPFLOW{$terms[2]}) { $IPFLOW{$terms[2]} = "$IPFLOW{$terms[2]}$delimiter$LINE"; } else { $IPFLOW{$terms[2]} = $LINE; $total_ips++; } } print < Below is a flow chart of your visitors. The time interval between hits is given in hour-minute-second format, followed by the number of days, if any.

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. By sampling a smaller number of hits over a shorter time period, the probability of these errors occuring is reduced.

EOM
foreach $key (keys %IPFLOW) {
	@LINES = split(/$delimiter/,$IPFLOW{$key});
	$num_hits = @LINES;
	if (($num_hits > 1) || ($FORM{'format'} eq 'b'))
		# Multiple documents visited; generate flow chart:
		{
		$multiple_hit_ips++ if ($num_hits > 1);
		@terms = split(/\|/,$LINES[0]);
		$minutes = &clean($terms[7]);
		$seconds = &clean($terms[6]);
		if ($terms[8] < 12)
			{
			$terms[8] = 12 if ($terms[8] < 1);
			$time_string = "$terms[8]:$minutes:$seconds AM";
			}
		else
			{
			$terms[8] = ($terms[8] - 12) unless ($terms[8] eq 12);
			$time_string = "$terms[8]:$minutes:$seconds PM";
			}
		if ($num_hits eq 1)
			{$num_hits = "once";}
		elsif ($num_hits eq 2)
			{$num_hits = "twice";}
		else
			{$num_hits = "$num_hits times";}

print <

A visitor from $terms[1] ($terms[2]) was logged $num_hits,
starting at $time_string on $weekdays[$terms[12]], $months[$terms[10]] $terms[9], 19$terms[11].
The initial browser was $terms[5].

EOM

print "  This visitor first ";
$first = 'true';
foreach $LINE (@LINES) {
		@terms = split(/\|/,$LINE);
		if ($first ne 'true') {
			($seconds,$minutes,$hours,$days) = ($terms[6]-$prev[3],$terms[7]-$prev[2],$terms[8]-$prev[1],$terms[13]-$prev[0]);
			if ($seconds < 0) {
				$seconds = $seconds + 60;
				$minutes--;
				}
			if ($minutes < 0)
				{$minutes = $minutes + 60;
				$hours--;}
			if ($hours < 0)
				{$hours = $hours + 24;
				$days--;}
			$hours = &clean($hours);
			$minutes = &clean($minutes);
			$seconds = &clean($seconds);
			print "  $hours:$minutes:$seconds ";
			if ($days)
				{print "and $days days ";}
			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\n";
			}

$first = 'false';
@prev = ($terms[13],$terms[8],$terms[7],$terms[6]);
} # End foreach hit per IP.
} # End test of more than one hit per IP.
} # End foreach loop through all IP's.

print <

Summary:
There were visits from $total_ips distinct IP addresses. However, only $multiple_hit_ips of these visited more than one document.
EOM $graph_made = 'true'; } # End Show Database-Style Visitor Flow. # Begin Export/Delete Log Procedure: sub kill_it { open(NEWLOG,">$logfile") || &Error_Log_File; print NEWLOG ""; close(NEWLOG); print <<'EOM';
Access Log Deleted
The log file has been successfully deleted.
EOM $graph_made = 'true'; } # End Export/Delete Log Procedure. sub get_browser_name { local($_) = shift; if (m!Mozilla/(\d)!oi) { if (m!compatible!oi) { if (m!WebTV!oi) { return "WebTV"; } elsif (m!MSIE (\d)!oi) { return "MSIE v$1.X"; } elsif (m!AOL (\d)!oi) { return "AOL's Browser v$1.X"; } elsif (m!AOL-IWENG (\d)!oi) { return "AOL's Browser v$1.X"; } else { return "Other Agents"; } } else { return "Netscape v$1.X"; } } elsif (m!Microsoft Internet Explorer/(\d)!oi) { return "MSIE v$1.X"; } elsif (m!IWENG/(\d)!oi) { return "AOL's Browser v$1.X"; } elsif (m!aolbrowser/(\d)!oi) { return "AOL's Browser v$1.X"; } elsif (m!Lynx!oi) { return "Lynx"; } elsif (m!WebExplorer!oi) { return "IBM WebExplorer"; } elsif (m!QuarterDeck!oi) { return "QuarterDeck Mosaic"; } elsif (m!SPRY!oi) { return "Compuserve's SPRY Mosaic"; } elsif (m!Enhanced_Mosaic!oi) { return "NCSA Mosaic (Enhanced)"; } elsif (m!Mosaic!oi) { return "NCSA Mosaic"; } elsif (m!PRODIGY!oi) { return "Prodigy's Browser"; } else { return "Other Agents"; } } sub get_os_type { local($_) = shift; if (m!win95!oi) { return 'Windows 95'; } elsif (m!winNT!oi) { return 'Windows NT'; } elsif (m!win16!oi) { return 'Windows 3.1/95 (16-bit)'; } elsif (m!win32!oi) { return 'Windows 95/NT (32-bit)'; } elsif (m!winweb!oi) { return 'Windows 3.1/95 (16-bit)'; } elsif (m!Windows 95!oi) { return 'Windows 95'; } elsif (m!Windows NT!oi) { return 'Windows NT'; } elsif (m!Windows 3.1!oi) { return 'Windows 3.1'; } elsif (m!Windows!oi) { if (m!32bit!oi) { return 'Windows 95/NT (32-bit)'; } else { return 'Windows 3.1/95 (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 3.1/95 (16-bit)'; } elsif (m!Lynx!oi) { return 'UNIX (Other/Unspecified)'; } elsif (m!WebTV!oi) { return 'WebTV'; } else { return 'Unknown Platform'; } } # &clean($number) inputs a single or double digit number and adds a # leading zero if it's a single digit number. sub clean { $_ = sprintf("%2.f",shift); $_ =~ s/ /0/g; 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 (/altavista\.([^\/\?]*)(.*)\?(.*)/i) { ($Host,$rank) = ($1,0); @parts = split(/\&/,$3); foreach $part (@parts) { if ($part =~ /^q=(.*)/) { $terms = $1; $terms =~ tr/+/ /; $terms =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; } if ($part =~ /^stg=(.*)/) { $rank = $1; } } return "www.altavista.$Host $terms ".($rank + 1).'-'.($rank + 10).''; } elsif (/\:\/\/([^\.]*)\.infoseek\.com(.*)\?(.*)/i) { ($Host,$rank,$Increment) = ($1,0,10); @parts = split(/\&/,$3); foreach $part (@parts) { if ($part =~ /^qt=(.*)/) { $terms = $1; $terms =~ tr/+/ /; $terms =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; } if ($part =~ /^st=(.*)/) { $rank = $1; } if ($part =~ /^nh=(.*)/) { $Increment = $1; } } return "$Host.infoseek.com $terms ".($rank + 1).'-'.($rank + $Increment).''; } 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 "$Host.excite.com $terms " . ($rank + 1) . "-" . ($rank + $Increment) . ""; } elsif (/search.yahoo.com(.*)\?(.*)/i) { @parts = split(/\&/,$2); foreach $part (@parts) { if ($part =~ /^p=(.*)/) { $terms = $1; $terms =~ tr/+/ /; $terms =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; } else { $terms = "unrecovered terms"; } if ($part =~ /^b=(.*)/) { $rank = $1; } else { $rank = 1; } } return "search.yahoo.com $terms " . $rank . "-" . ($rank + 19) . ""; } elsif (/av.yahoo.com(.*)\?(.*)/i) { @parts = split(/\&/,$2); ($terms,$rank) = ("unrecovered terms",1); foreach $part (@parts) { if ($part =~ /^p=(.*)/i) { $terms = $1; $terms =~ tr/+/ /; $terms =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; } if ($part =~ /^b=(.*)/i) { $rank = $1; } } return "av.yahoo.com $terms " . $rank . "-" . ($rank + 19) . ""; } elsif (/hotbot.com(.*)\?(.*)/i) { @parts = split(/\&/,$2); ($terms,$rank) = ("unrecovered terms","1-20"); foreach $part (@parts) { if ($part =~ /^MT=(.*)/i) { $terms = $1; $terms =~ tr/+/ /; $terms =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; } if ($part =~ /^base=(.*)/i) { $rank = ($1 + 11) . "-" . ($1 + 20); } } return "www.hotbot.com $terms $rank"; } elsif ((/$http_addr/i) && ($_ =~ /http\:\/\/(.*)/i)) { return $1; } elsif (/http\:\/\/(.*)/i) { return "$1"; } else { return "$_"; } } # End url_format procedure. sub numsort { # %ASTA is now a large associative array holding ($value,$num_hits) # pairs for all the relevant data. We want to give the ability to sort # based on number of hits instead of the default alpha-sort. The # $STATE{'numsort'} variable controls this. To create a sorted array on # the number of hits, we rebuild ASTA as ($num_hits-$value,$num_hits) # then alpha sort again. Of course logical sorts like by hour and by # weekday will be unaffected. # Clear buffer array: undef(%NSTA); foreach (keys %ASTA) { $x_rank = 9999999 - $ASTA{$_}; $x_index = $x_rank.'|'.$_; $NSTA{$x_index} = $ASTA{$_}; } %ASTA = %NSTA; # Done. } sub Error_Prefs_File { print "Content-type: text/plain\n\n"; print "File Access Error:\n\n"; print " The preferences file for this script is not accessible.\n\n"; print "AXS will not execute without read/write access to it's preference file.\n"; print "Please remedy this situation and try again...\n\n"; print "The variable in question is \$prefs = \"$prefs\";\n"; if (-e $prefs) { print "The system confirms the file location, but denies write access.\n"; print "Try telnetting to this server and running the command:\n\n"; print " chmod 777 $prefs\n\n"; } else { print "No file has been found at this location.\n"; print "Please check the path to the file.\n\n"; } print "See also http://www.xav.com/scripts/axs/ under the section\n"; print " \"Troubleshooting: File Access Error\"\n"; exit; } sub Error_Log_File { print "Content-type: text/plain\n\n"; print "File Access Error:\n\n"; print " The access log file for this script is not accessible.\n\n"; print "AXS will not execute without read/write access to it's log file.\n"; print "Please remedy this situation and try again...\n\n"; print "The variable in question is \$logfile = \"$logfile\";\n"; if (-e $logfile) { print "The system confirms the file location, but denies write access.\n"; print "Try telnetting to this server and running the command:\n\n"; print " chmod 777 $logfile\n\n"; } else { print "No file has been found at this location.\n"; print "Please check the path to the file.\n\n"; } print "See also http://www.xav.com/scripts/axs/ under the section\n"; print " \"Troubleshooting: File Access Error\"\n"; exit; }