#!perl -w
#
# Copyright (c) Ensim Corporation, 1999-2001   All Rights Reserved.
#
# This software is furnished under a license and may be used and copied
# only  in  accordance  with  the  terms  of such  license and with the
# inclusion of the above copyright notice. This software or any other
# copies thereof may not be provided or otherwise made available to any
# other person. No title to and ownership of the software is hereby
# transferred.
#
# The information in this software is subject to change without notice
# and  should  not be  construed  as  a commitment by Ensim Corporation.
# Ensim assumes no responsibility for the use or  reliability  of its
# software on equipment which is not supplied by Ensim.
#
# Exit codes (on failure error message goes to stderr):
#  0 - success
#  non 0 - failure
# 
# This script regenerates statistics in the analog computer readable
# format. All data is rebuilt from existing logs, and the cache is 
# removed and replaced.  The result is written to the standard
# output.
#
# Usage:
#   updatestats.pl domain service mode [clearcache=0]
#   domain: domainname|ip|siteadmin|all
#   service: web|ftp
#   mode: thisweek|lastweek|twoweeksago|threeweeksago|running|all
#   clearcache: 1|0
# 


require Time::Local;

use Win32::Registry; 
use integer;

my($oneday) = 86400;
my($oneweek) = 604800;
my($time) = time;
my($base) = "$ENV{'OCW_VD_HOME'}";

my($home, $loghome, $usagehome, $cachepath, $analogconffile, $runningoption, $logoption);
my($name, $service, $mode, $clearcache) = @ARGV;
my($DomainKeyPath) = "SOFTWARE\\Ensim\\WEBppliance\\domains";

sub analyze {
  print STDERR "\n\n    [ analog $_[0] ]\n";
  system ("c:\\analog\\analog_wrapper.bat $_[0]");
}

$name and $service and $mode or die "Usage: updatestats.pl name service mode\n";
$service =~ /ftp|web/ or die "Service must be web or ftp, not $service.\n";
$mode =~ /thisweek|lastweek|twoweeksago|threeweeksago|running|all|none/ or die "Invalid mode $mode.\n";

print "\n\n\n======================= ANALYZING ====================\n\n\n";

if ($name =~ /^all$/)
{	
	# generate stats for all the domains 
	$main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath", $DomainBaseKey) || die "Open: $!";
	$DomainBaseKey->GetKeys(\@AllDomains);
	foreach $DomainName (@AllDomains)
	{
		$main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath\\$DomainName\\services", $DomainSubKey) || die "Open: $!";
		$DomainSubKey->GetValues(\%ServiceFlags);
		$val = $ServiceFlags{winanalog};
		$fAnalog = $$val[2];

		$main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath\\$DomainName", $DomainSubKey) || die "Open: $!";
		$DomainSubKey->GetValues(\%DomainConf);
		$val = $DomainConf{user};
		$UserName = $$val[2];
		
		if($fAnalog =~ /1/)
		{
			print "\nAnalog is enabled for $DomainName\n";	
			&buildstats($DomainName, $UserName);
		}
	}
}
else
{
	# generate stats for specific domain
	
	$main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath", $DomainBaseKey) || die "Open: $!";
	$DomainBaseKey->GetKeys(\@AllDomains);
	@SearchedDomain = grep /\b$name\b/i, @AllDomains;
	if (scalar(@SearchedDomain) != 1)
    {
        print "@SearchedDomain";
        die "$name does not uniquely identify a domain. [ip|admin|domain]\n";
    }

	$DomainName = $SearchedDomain[0];
	
	$main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath\\$DomainName\\services", $DomainSubKey) || die "Open: $!";
	$DomainSubKey->GetValues(\%ServiceFlags);
	$val = $ServiceFlags{winanalog};
	$fAnalog = $$val[2];

	$main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath\\$DomainName", $DomainSubKey) || die "Open: $!";
	$DomainSubKey->GetValues(\%DomainConf);
	$val = $DomainConf{user};
	$UserName = $$val[2];

	if($fAnalog =~ /1/)
	{
		print "\nAnalog is enabled for $DomainName\n";	
		&buildstats($DomainName, $UserName);
	}
}


exit 0;

sub initvars()
{
    my ($mydomain, $myuser) = @_;
    my ($log);
    $domain = $mydomain;
    $user = $myuser;
    $main::HKEY_LOCAL_MACHINE->Open("$DomainKeyPath\\$domain", $DomainSubKey) || die "Open: $!";
    $DomainSubKey->GetValues(\%DomainConf);
    $val = $DomainConf{dataShortPath};
    $home = $$val[2];
    if ($service =~ /ftp/)
    {
        $log = "msftpsvc";
        $usagehome = "$home\\var\\usage\\ftp";
        $cachepath = "$usagehome\\cache";
        $analogconffile = "C:\\analog\\analog.ftp.cfg";
    }
    else
    {
        $log = "w3svc";
        $usagehome = "$home\\var\\usage\\web";
        $cachepath = "$usagehome\\cache";
        $analogconffile = "C:\\analog\\analog.web.cfg";
    }
    # -G dis-includes analog.cfg +g includes specified file
    $runningoption = "-G +g$analogconffile"; 

    opendir(LOGDIR, "$home\\LogFiles");
    my @dirs = readdir LOGDIR;
    closedir LOGDIR;
    my $dir;
    my $match = 0;
    foreach $dir (@dirs)
    {
        if (lc($dir) =~ /^$log/)
        {
            $loghome = "$home\\LogFiles\\$dir";
            $match = 1;
        }
    }

    unless ($match)
    {
        print "  There are no $service logs for $domain.\n";
        $loghome = "";
        $logoption = "";
    }
    else
    {
        my ($logfile) = &gettodayslog();
        if ($logfile)
        {
            $logoption = "+C\"LOGFILE $logfile\"";
        }
        else
        {
            $logoption = "";
        }
    }
}

sub IsWindows2003 {
	my $WWP;
	my ($type, $value);

	$main::HKEY_LOCAL_MACHINE->Open("SOFTWARE\\Ensim\\Webppliance", $WWP);
	if ($WWP->QueryValueEx("win2003", $type, $value)) {
		return 1;
	} else {
		return 0;
	}
}


sub buildstats()
{
    my ($mydomain, $myuser) = @_;
    &initvars($mydomain, $myuser);
    print "Rebuilding $domain for $user \n";

    # make sure cache is up-to-date
    # if no logs available use cache as-is
    if ($clearcache && $loghome)
    {
        &buildcache();
    }
    elsif ($loghome)
    {
        &updatecache();
    }

    # use cache to generate all statistics
    if ($mode =~ /thisweek|all/)
    {
        &weekly(0);
    }

    if ($mode =~ /lastweek|all/)
    {
        &weekly(1);
    }

    if ($mode =~ /twoweeksago|all/)
    {
        &weekly(2);
    }

    if ($mode =~ /threeweeksago|all/)
    {
        &weekly(3);
    }

    if ($mode =~ /running|all/)
    {
        &running();
    }

    # remove old reports and cache files
    @stattodelete = ("$usagehome\\week".&getweeksbefore(4,$time),
                     "$usagehome\\week".&getweeksbefore(5,$time),
                     "$cachepath".&getweeksbefore(4,$time),
                     "$cachepath".&getweeksbefore(5,$time));
    map {unlink($_)} @stattodelete;

    # update ownership of files
    if (!IsWindows2003()) {
        if ($loghome)
        {
            system("subinacl /subdirectories $loghome\\*.* /setowner=$user");
        }
        system("subinacl /subdirectories $usagehome\\*.* /setowner=$user");
    }

}

sub buildcache()
{
    print " clearing $service cache of $domain for $user\n";
    my ($cachefile) = $cachepath;
    my ($thisweek) = &getweeksbefore(0, $time);

    my $file;
    foreach $file ($cachefile, $cachefile.$thisweek, 
                   $cachefile.($thisweek - 1),
                   $cachefile.($thisweek - 2),
                   $cachefile.($thisweek - 3),
                   $cachefile.($thisweek - 4))
    {
        if ( -e $file)
        {
            rename($file, "$file.old") or die " Error backing up $file.\n";
        }
    }

    if (-e "$cachefile.new")
    {
        unlink "$cachefile.new" or die " Error unlinking $cachefile.new.\n";
    }

    opendir(LOGDIR, $loghome);
    my (@logfiles) = readdir LOGDIR;
    closedir(LOGDIR);

    my ($logfile,$logmtime,$cachemtime,$statinfo);
    foreach $logfile (@logfiles)
    {
        # skip . and ..
        if ($logfile =~ /^\.(\.)?$/)
        {
            next;
        }

        # skip today's logs
        # expect filename to be ex770831.log or in770831.log
        my ($today) = &gettoday();
        if ($logfile =~ /^..$today/)
        {
            next;
        }

        my $week;
        # expect filename to be ex770831.log or in770831.log
        # don't do weekly cache for old logs
        if ($logfile =~ /^..(\d\d)(\d\d)(\d\d)/)
        {
            my ($yr, $mo, $day) = ($1, $2, $3);
            # add 100 to year because it is after 2000
            $week = &getweek($yr + 100, $mo-1, $day);
            if ($week > $thisweek)
            {
                # recent rollover for end of year
                my ($oldweek) = &getweeksbefore(4, $time);
                if  ($week > $oldweek)
                {
                    $week = 0;
                }
            }
            elsif ($thisweek - $week > 3)
            {
                # don't create caches if older than 3 weeks ago
                $week = 0;
            }
        }
        else
        {
            $week = 0;
        }
        print "\n\nProcessing =======> $logfile = week$week <==========\n";

        # remove .new file if it exists so cache can be generated
        if (-e "$cachefile$week.new")
        {
            unlink "$cachefile$week.new" or die " Error unlinking $cachefile$week.new.\n";
        }

        # build new cache using old cache and log file to cachefile.new 
        analyze("none $runningoption  +C\"CACHEFILE $cachefile\" +C\"LOGFILE $loghome\\$logfile\" +C\"CACHEOUTFILE $cachefile.new\" +C\"OUTPUT NONE\"");

        # only create cache files for past 4 weeks
        if ($week)
        {
            # add log into its weekly cache
            analyze("none $runningoption  +C\"CACHEFILE $cachefile$week\" +C\"LOGFILE $loghome\\$logfile\" +C\"CACHEOUTFILE $cachefile$week.new\" +C\"MONTHLY OFF\" +C\"OUTPUT NONE\"");
        }

        # update access and modification times if logfile newer than cache
        # cache should have mod time of the newest processed logfile
        if (@statinfo = stat ("$loghome\\$logfile")) 
        {
            $logmtime = $statinfo[9];
            if(@statinfo = stat ("$cachefile")) 
            {
                $cachemtime = $statinfo[9];
            } 
            else 
            {
                $cachemtime = 0;
            }
            if ($cachemtime < $logmtime)
            {
                utime($logmtime, $logmtime, "$cachefile.new");
            }

            print "  log=$logmtime cache=$cachemtime";
            if ($week)
            {
                # make timestamp of weekly cache match newest logfile
                if(@statinfo = stat ("$cachefile$week")) 
                {
                    $cachemtime = $statinfo[9];
                } 
                else 
                {
                    $cachemtime = 0;
                }
                if ($cachemtime < $logmtime)
                {
                    utime($logmtime, $logmtime, "$cachefile$week.new");
                }

                print " week=$cachemtime.\n";
            }
            else
            {
                print "\n";
            }
        }

        # make newly created cachefile into official cachefile
        if (-e "$cachefile.new")
        {
            rename("$cachefile.new", $cachefile) or die " Error processing $logfile into $cachefile.\n";
        }

        if ($week)
        {
            # also update weekly cache file
            if (-e "$cachefile$week.new")
            {
                rename("$cachefile$week.new", "$cachefile$week") or die " Error processing $logfile into $cachefile.\n";
            }
        }
    }

    foreach $file ($cachefile, $cachefile.$thisweek, 
                   $cachefile.($thisweek - 1),
                   $cachefile.($thisweek - 2),
                   $cachefile.($thisweek - 3),
                   $cachefile.($thisweek - 4))
    {
        if (( -e $file) and  ( -e "$file.old"))
        {
            unlink "$file.old";
        }
    }
}

sub updatecache()
{
    print " updating $service cache of $domain for $user\n";
    my ($cachefile) = $cachepath;
    my ($thisweek) = &getweeksbefore(0, $time);

    if (-e "$cachefile.new")
    {
        unlink "$cachefile.new" or die " Error unlinking $cachefile.new.\n";
    }

    opendir(LOGDIR, $loghome);
    my (@logfiles) = readdir LOGDIR;
    closedir(LOGDIR);
    
    my ($logfile,$logmtime,$cachemtime,$statinfo,$logoption,$lastlogmtime);

    # get last modified on cache file
    if(@statinfo = stat ("$cachefile")) 
    {
        $cachemtime = $statinfo[9];
    } 
    else 
    {
        $cachemtime = 0;
    }
    $lastlogmtime = $cachemtime;
    print " Age on cache is $cachemtime.\n";

    # initialize variables for running weekly caches
    my (%wlastlogmtime) = ();
    my (%wcachemtime) = ();

    # find logs newer than cache file
    $logoption = "";
    $lastlogmtime = 0;
    foreach $logfile (@logfiles)
    {
        # skip . and ..
        if ($logfile =~ /^\.(\.)?$/)
        {
            next;
        }

        # skip today's logs (wait until rotated)
        # expect filename to be ex770831.log or in770831.log
        my ($today) = &gettoday();
        if ($logfile =~ /^..$today/)
        {
            next;
        }

        my $week;
        # expect filename to be ex770831.log or in770831.log
        # don't do weekly cache for old logs
        if ($logfile =~ /^..(\d\d)(\d\d)(\d\d)/)
        {
            my ($yr, $mo, $day) = ($1, $2, $3);
            # add 100 to year because it is after 2000
            $week = &getweek($yr + 100, $mo-1, $day);
            if ($week > $thisweek)
            {
                # recent rollover for end of year
                my ($oldweek) = &getweeksbefore(4, $time);
                if  ($week > $oldweek)
                {
                    $week = 0;
                }
            }
            elsif ($thisweek - $week > 3)
            {
                #don't create caches if older than 3 week ago
                $week = 0;
            }
        }
        else
        {
            $week = 0;
        }
        print "\n\n Processing =====> $logfile = week$week <==========\n";

        if ($week)
        {
            # remove .new file if it exists so cache can be generated
            if (-e "$cachefile$week.new")
            {
                unlink "$cachefile$week.new" or die " Error unlinking $cachefile$week.new.\n";
            }
            
            unless (defined($wcachemtime{$week}))
            {
                # make timestamp of weekly cache match newest logfile
                if(@statinfo = stat ("$cachefile$week")) 
                {
                    $wcachemtime{$week} = $statinfo[9];
                } 
                else 
                {
                    $wcachemtime{$week} = 0;
                }
                $wlastlogmtime{$week} = $wcachemtime{$week};
            }
        }

        # update access and modification times if logfile newer than cache
        # cache should have mod time of the newest processed logfile
        if (@statinfo = stat ("$loghome\\$logfile")) 
        {
            $logmtime = $statinfo[9];
            
            # only update cache if this file is newer than the cache
            if ($cachemtime < $logmtime)
            {
                # build new cache using old cache and log file to cachefile.new 
                analyze("none $runningoption  +C\"CACHEFILE $cachefile\" +C\"LOGFILE $loghome\\$logfile\" +C\"CACHEOUTFILE $cachefile.new\" +C\"OUTPUT NONE\"");
                # make newly created cachefile into official cachefile
                if (-e "$cachefile.new")
                {
                    rename("$cachefile.new", $cachefile) or die " Error processing $logfile into $cachefile.\n";
                }

                # if this log is newest encountered update timestamp
                if ($lastlogmtime < $logmtime)
                {
                    $lastlogmtime = $logmtime;
                    utime($logmtime, $logmtime, "$cachefile");
                }
            }

            if ($week)
            {
                # make timestamp of weekly cache match newest logfile

                # check timestamp of log against original timestamp of cache
                if ($wcachemtime{$week} < $logmtime)
                {
                    # add log into its weekly cache
                    analyze("none $runningoption  +C\"CACHEFILE $cachefile$week\" +C\"LOGFILE $loghome\\$logfile\" +C\"CACHEOUTFILE $cachefile$week.new\" +C\"MONTHLY OFF\" +C\"OUTPUT NONE\"");
                    # make newly create cachefile into official cachefile
                    if (-e "$cachefile$week.new")
                    {
                        rename("$cachefile$week.new", "$cachefile$week") or die " Error processing $logfile into $cachefile.\n";
                    }

                }
                
                # update timestamp if this is newest log so far
                if ($wlastlogmtime{$week} < $logmtime)
                {
                    $wlastlogmtime{$week} = $logmtime;
                    utime($logmtime, $logmtime, "$cachefile$week");
                }
            }

        }
    }
}

sub weekly()
{
    my ($weeksback) = @_; 
    my ($week) = &getweeksbefore($weeksback, $time);
    my ($firstday) = &firstdayofweek($weeksback, $time);
    my ($lastday) = &lastdayofweek($weeksback, $time);
    print " Generating statistics for week $week ($firstday-$lastday).\n";
    my ($statfile) = "$usagehome\\week$week";
    my ($timeoption) = "+F$firstday +T$lastday";
    analyze("$statfile $runningoption +C\"CACHEFILE $cachepath$week\" +C\"MONTHLY OFF\"  $timeoption $logoption");
}

sub running()
{
    analyze("$usagehome\\running  +C\"CACHEFILE $cachepath\" $runningoption $logoption");
}

# returns the name of the log file for today if it exists
sub gettodayslog()
{
    opendir(LOGDIR, $loghome);
    my (@logfiles) = readdir LOGDIR;
    closedir(LOGDIR);

    my ($logfile);
    my ($today) = &gettoday();
    foreach $logfile (@logfiles)
    {
        if ($logfile =~ /^..$today/)
        {
            return "$loghome\\$logfile";
        }
    }

    return "";
}

#returns the number of the week of the year we were in x weeks before $time
sub getweeksbefore {
  my ($diff,$time) = @_;
  $time -= $oneweek*$diff; # $diff weeks ago
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time);
  if ($wday > $yday) { #this week started last year, so we count it there
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time-$oneweek);
    $yday+=7;
  }
  return 1+($yday-$wday)/7; #1..53
}

sub getweek {
    my ($yr, $mo, $da) = @_;
    my ($weektime) = Time::Local::timelocal(0,0,0,$da,$mo,$yr,0,0,0);
    return &getweeksbefore(0, $weektime);
}

sub gettoday {
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time);
    return sprintf ("%02d%02d%02d",substr($year+1900, 2,2),$mon+1,$mday);
}

sub firstdayofweek {
  my ($diff,$time) = @_;
  $time -= $oneweek*$diff; # $diff weeks ago
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time);
  $time -= $oneday*$wday; # last Sunday
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time);
  $year = ($year>=100 ? $year%100 : $year);
  return sprintf ("%02d%02d%02d",$year,$mon+1,$mday);
}

sub lastdayofweek {
  my ($diff,$time) = @_;
  $time -= $oneweek*$diff; # $diff weeks ago
  my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time);
  $time += $oneday*(6-$wday); # this Saturday
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)= localtime($time);
  $year = ($year>=100 ? $year%100 : $year);
  return sprintf ("%02d%02d%02d",$year,$mon+1,$mday);
}
