#!/usr/bin/perl -w 
# lla.pl v 0.99p1
# Log Analyser for NS 4.11 LDAP and OpenLDAP
# Author : Kris Buytaert
# email : kb@ipng.be

# rewritten: brian harrison <brie@excitehome.net>, 2001-01-03


# Todo :
# 	- Fancy html layout
#       - Open LDAP Support
#	- Fixing the 0 line in the foreach loops ...
# 	- Cleanup Code
# 	- ReImplement speed measurement

# Done:
# 	- Optimize Performance
# 	- Make it possible to run multiple instances on different logfiles
#	  together

# Requirements



# http://www.cpan.org/doc/FAQs/FAQ/PerlFAQ.html#Data_Dates
# Get the modules from http://www.cpan.org/modules/by-module/Time/
# Time-modules-100.010301.tar.gz
# Get the modules from http://www.cpan.org/modules/by-module/Date/
# DateManip-5.39.tar.gz

require 5.004;

use strict;

use Getopt::Long;
use Time::ParseDate;
use Date::Calc qw(:all);
use IO::File;

use vars qw($debug $webbish);
use vars qw(%originatinghost %tally);


##########################  Sub Cleanup  #################################

sub Cleanup {
    print "Entering Cleanup\n" if $debug;
    close DEBUG if $debug >= 3;
}

##########################  Sub OnScreen  #################################

sub Onscreen {
    print "LDAP Log Analyser\n" if $debug;
}


##########################  Sub FewParam  #################################

sub Fewparam {
    print <<'_EOH_';
LDAP Log Analyser is generating statistics from your LDAP log file
Usage : lla.pl -w INPUTFILE [ > OUTPUTFILE ]
Example.  : lla.pl -w  access > stats.html

Copyright by Kris Buytaert 2000					  kb@ipng.be 
This software is released under the terms of the GNU General Public License

Main operation mode:
 -w  		Generate html output
 -t		Generate flatfile output
 -m=kb@ipng.be  Mail flatfile to emailaddress (may be specified multiple times)

_EOH_

    exit();
}


# conn nrs herstarten / dag  		  to be implemented

# We will be analysing the log file checking for
#   - Originating connections $from[ip]   to be implemented
#   - Bind dn				  to be implemented
#   - Srch				  to be implemented
#   Actually all possible operations 	  to be implemented
#   $conn  		= 	Total Number of Connections
#   $sspeed 		= 	Sessions / Second
#   $ospeed		= 	Operations / Second
#   $sconn		=	Connections / Session

##########################  Sub Report  #################################

sub Report {
    my (%total, %average, %counter);

    print "\n\nEntering Report\n" if $debug;

    if ($webbish) {
	print OUTPUT "<html><head><title>LDAP Log Analyse Report</title></head><body bgcolor=\"#ffffff\">";
    }


#    print OUTPUT "Between $starttime and $endtime there were $conntotal sessions started\n";

#    print OUTPUT "<br>" if $webbish;
#    print OUTPUT "Average speed is about $sspeed\n";
    print OUTPUT "<br>" if $webbish;

    # Display all originating hosts .. sorted ...


    my $ip;
    foreach $ip (sort keys %originatinghost) {
	print OUTPUT "Connections from $ip = $originatinghost{$ip}\n";
	print OUTPUT "<br>" if $webbish;
    }

    print OUTPUT "\n\n\n";

    if ($debug >= 3) {
	my $t;
	foreach $t (sort keys %{$tally{operations}}) {
	    print DEBUG "===============================";
	    print DEBUG "Nr Operations $t : $tally{operations}->{$t}\n";
	}
    }

    my $type;
    foreach $type ("operations","totaltime","search","bind","result","res0","res32","res65","res103","mod","add") {

	my $id;
	while (($id) = each %{$tally{$type}}) {
	    my $n = $tally{$type}->{$id};
#	    print OUTPUT "Numbers of $type in Session $id : $n\n";
#	    print OUTPUT "<br>" if $webbish;
	    $total{$type} += $n;
	    $counter{$type}++;
	}
	$average{$type} = $counter{$type} ? $total{$type} / $counter{$type} : 'undef';

	print OUTPUT "Average $type per connection = $average{$type}\n";
	print OUTPUT "<br>" if $webbish;

    }

    print OUTPUT "</body></html>" if $webbish;

    #  Mail plain text if needed ...
}

#########################  Sub Analyse_File   #################################

sub Analyse_File {
    my (%firstline, %lastline);
    my %session;
    
    while (<>) {
	my ($conn) = / conn=(\d+) /;
	
	# Statistics on Originating IP
	if (/connection from (.*) to (.*)/) {
	    # every time we see a new connection it is a new "session",
	    # even if this conn number has been seen before.
	    $session{$conn}++;
	    $originatinghost{$1}++;
	}
	
	my $session = $session{$conn} || 0;
	my $id = join '.', $conn, $session;
	
	$firstline{$id} = $_ unless $firstline{$id};
	$lastline{$id} = $_;
	
	# Counting Searches
	if (/SRCH/) {
	    $tally{search}->{$id}++;
	}
	
	# Counting BIND
	if (/BIND/) {
	    $tally{bind}->{$id}++;
	}
	
	# Analysing Result Type
	if (/RESULT/) {
	    $tally{result}->{$id}++;
	}

	if (/RESULT err=(\d+)/) {
	    $tally{"res" . $1}->{$id}++;
	}
	
	# MOD
	if (/MOD/) {
	    $tally{mod}->{$id}++;
	}
	
	# ADD
	if (/ADD/) {
	    $tally{add}->{$id}++;
	}
    }
    
    my $id;
    while (($id) = each %lastline) {
	
	# Number of operations in a session
	my ($op_count) = ($lastline{$id} =~ / op=(\d+) /);
	$tally{operations}->{$id} = $op_count;
	
	print "Nr Operations : $tally{operations}->{$id}\n" if $debug >= 2;
	print DEBUG "Nr Operations : $tally{operations}->{$id}\n" if $debug >= 3;
	
	# Calculating Session Start
	my ($starttime) = ($firstline{$id} =~ /\[(.*?)\]/);
	my $startseconds = parsedate($starttime);
	
	# Calculating Session End
	my ($endtime) = ($lastline{$id} =~ /\[(.*?)\]/);
	my $endseconds = parsedate($endtime);
	
	# Calculating Session Time
	$tally{totaltime}->{$id} = $endseconds - $startseconds;
	
	# Analysing Bind Types

    }

    undef %firstline;
    undef %lastline;

}


###############  Main #######################

# Onscreen;

#  	0 = no debugging
# 	1 = light debugging
# 	2 = code debugging
#	3 = debugging to file
$debug = 0;
open DEBUG, ">debug.log" if $debug >= 3;

# Could be done better with select()
my $webbish = 0;		# 1 = Results in html
my $text = 0;			# 1 = Results in plain text
my @email = ();
my $email;

GetOptions("debug=i"    => \$debug,
	   "w|web|html" => \$webbish,
	   "t|text"     => \$text,
	   "m|mail=s@"  => \@email);

unless ($webbish or $text or @email) {
    Fewparam();
}

foreach (@ARGV) {
    unless (-e $_) {
	die "usage has changed.  use \"> $_\" to specify the output file instead.\n";
    }
}

if (@email) {
    my $MailSubject = "LDAP Log Analyse Report";
    my $all_email = join ' ', @email;
    open OUTPUT, "| /usr/bin/mail -s \"$MailSubject\" $all_email";
} else {
    open OUTPUT, ">&STDOUT";
}

#

print "Analysing input\n" if $debug;
my $analyse_start = time;
Analyse_File();
print "Analyse time: ", time - $analyse_start, "\n" if $debug;

Report();
Cleanup();


syntax highlighted by Code2HTML, v. 0.9.1