#!/usr/bin/perl -T
use strict;
use vars qw($VERSION); $VERSION="1.0";

# AWIndex - HTML Index Generator for AWStats
# Written by Nick Levay <nick at nicklevay dot net>
#
# This is free software.  You may copy, modify, and distribute this program
# under the same terms as perl.
#
# AWIndex is a CGI script that generates a HTML index for multiple sites
# using AWStats.  It reads AWStats config files and creates an index page
# which can be sorted by site name, current month's visit count, or current
# month's unique visitor count.

# These two paths must be correct
my $awlinkpath  = "/cgi-bin/awstats.cgi"; # path to awstats script
my $configdir   = "/etc/awstats"; # awstats config directory

# From here out, changes are optional
my $lmode       = 'visits'; # default list mode (name/visits/unique)
my $showdomains = 1; # show domains column (0 or 1)
my $prettyregex = 1; # make most simple regex more human readable (0 or 1)

# Header
my $header = <<END;
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"/>
<html>
<head>
  <title>Web Site Traffic Stats</title>
</head>
<style type="text/css">
table { border: 1px dotted black; border-collapse: collapse; }
td, th { border: 1px dotted black; }
</style>
<body>
<h1>Web Site Traffic Stats</h1>
END

# Footer
my $footer = <<END;
<hr>
<!-- PLUG -->
Generated by <a href="http://www.nicklevay.net/projects/awindex/">AWIndex</a>
v$VERSION for <a href="http://awstats.sourceforge.net">AWStats</a>
by <a href="http://www.nicklevay.net">Nick Levay</a>
&lt;<a href="mailto:nick\@nicklevay.net">nick\@nicklevay.net</a>&gt;
<!-- PLUG -->

</body></html>
END

# Text strings
my $str     = {
	site     => 'Site', # site button/header label
	visits   => 'Visits', # visits button/header label
	unique   => 'Unique', # unique button/header label
	domains  => 'Domains' # domains buttons/header label
};

# END OF CONFIGURATION
######################

# sites -> name -> file (string), visits (int), unique (int), domains (array)
my $sites = {};

# Check config dir for config files (exclude awstats.model.conf)
opendir(DIR, $configdir)
	|| die "Could not scan $configdir: $!";
while (my $file = readdir(DIR)) {
	if ($file =~ m/awstats\.(.+)\.conf/ && $1 ne 'model') {
		my $name = $1;
		$sites->{$name} = { file => $file };
	}
}
close(DIR);

# Read files
foreach my $name (keys %{ $sites }) {
	# Read Config File
	open(CONF, "$configdir/$sites->{$name}->{file}")
		|| die "Could not read $configdir/$sites->{$name}->{file}: $!";
	while (my $line = <CONF>) {
		next if $line =~ m/^#.*/;
		next unless $line =~ m/^(.*)="(.*)"$/;
		my ($token, $value) = ($1, $2);
		if ($token eq 'DirData') {
			$sites->{$name}->{datadir} = $value;
		} elsif ($token eq 'SiteDomain' && $showdomains) {
			push(@{ $sites->{$name}->{domains} }, $value);
		} elsif ($token eq 'HostAliases' && $showdomains) {
			foreach my $h (split(/ /, $value)) {
				next if $h eq 'localhost';
				next if $h eq '127.0.0.1';
				if ($h =~ m/REGEX\[\^\.(.*)\$\]/ && $prettyregex) {
					my $hostreg = $1;
					$hostreg =~ s/\\//g;
					push(@{ $sites->{$name}->{domains} }, $hostreg);
				} else {
					push(@{ $sites->{$name}->{domains} }, $h);
				}
			}
		}
		if ($showdomains) {
			last if $sites->{$name}->{domains}
				&& $sites->{$name}->{visits} && $sites->{$name}->{unique};
		} else {
			last if $sites->{$name}->{visits} && $sites->{$name}->{unique};
		}
	}
	close(CONF);
	# Get current visitor & unique count
	my $mytime = sprintf("%02d", (localtime)[4] + 1) . ((localtime)[5] + 1900);
	my $dfname = "awstats$mytime.$name.txt";
	my $datadir;
	if ($sites->{$name}->{datadir} =~ m/^\.\/.*/) {
		$datadir = $sites->{$name}->{datadir};
		$datadir =~ s/^\.//;
		$datadir = $configdir . $datadir;
	} elsif ($sites->{$name}->{datadir} =~ m/^\.\.\//) {
		$datadir = $sites->{$name}->{datadir};
		$datadir =~ s/^\.\.//;
		$datadir = $configdir . $datadir;
	} elsif ($sites->{$name}->{datadir} =~ m/^\//) {
		$datadir = $sites->{$name}->{datadir};
	}
	my $datafile = $datadir . "/" . $dfname;
	if (-r $datafile) {
		open(DATA, $datafile)
			|| die "Could not read $datafile: $!";
		while (my $line = <DATA>) {
			chomp $line;
			if ($line =~ m/^TotalVisits (\d+)/) {
				$sites->{$name}->{visits} = $1;
			} elsif ($line =~ m/^TotalUnique (\d+)/) {
				$sites->{$name}->{unique} = $1;
			}
			last if $sites->{$name}->{visits} && $sites->{$name}->{unique};
		}
		close(DATA);
	} else {
		$sites->{$name}->{visits} = 0;
		$sites->{$name}->{unique} = 0;
	}	
}

# Figure out mode & URI
my $dmode = $lmode;
foreach my $mode ('site', 'visits', 'unique') {
	$lmode = $mode if $ENV{REQUEST_URI} =~ m/\?s=$mode/;
}
my $ruri = $ENV{REQUEST_URI};
$ruri =~ s/\?.*$//;

# Print Page
print "Content-Type:  text/html\n\n";
print $header;
print "<table>\n<tr>";
foreach my $mode ('site', 'visits', 'unique') {
	if ($lmode eq $mode) {
		print "<th>$str->{$mode}</th>";
	} else {
		my $uri = $ruri;
		$uri .= "?s=$mode" unless $dmode eq $mode;
		print "<th><a href=\"$uri\">$str->{$mode}</a></th>";
	}
}
print "<th>Domains</th>" if $showdomains;
print "</tr>\n";
foreach my $name (
	sort {
		if ($lmode eq 'visits') {
			($sites->{$b}->{visits} <=> $sites->{$a}->{visits}) or ($a cmp $b)
		} elsif ($lmode eq 'unique') {
			($sites->{$b}->{unique} <=> $sites->{$a}->{unique}) or ($a cmp $b)
		} else {
			$a cmp $b
		}
	}
	keys %{ $sites }
) {
	print "<tr><td><a href=\"$awlinkpath?config=$name\">$name</a></td>";
	print "<td>$sites->{$name}->{visits}</td>";
	print "<td>$sites->{$name}->{unique}</td>";
	print "<td>" . join(" ", @{ $sites->{$name}->{domains} }) . "</td>"
		if $showdomains;
	print "</tr>\n";
}
print "</table>\n";
print $footer;

# EOF
