#!/usr/bin/perl -w
###
# Project: pflogstats
# Program: sqwmstats.pl
# Description: Main program
#
# Copyright (C) 2002-2003 by Dr. Peter Bieringer <pbieringer at aerasec dot de>
# ftp://ftp.aerasec.de/pub/linux/postfix/pflogsumm/
#
# License: GNU GPL v2
# CVS: $Id: sqwmstats.pl,v 1.11 2005/04/26 16:04:34 peter Exp $
#
# See also following files: LICENSE, ChangeLog
###
###
# ChangeLog
# 0.01
# - split-off related code from non-modular version
# 0.02
# - bring to work
# 0.03
# - adjustments for external network check module
# 0.04
# - make Perl 5.0 compatible
# 0.05
# - account only request with valid return codes
# - fix bugs in user rewriting on sqwebmail login
# - add support for timerange (get)
# - add support for addresmappingtable
# 0.06
# - add network exclusion support
# 0.07
# - replace number format function call
###
# ToDo
# - timerange (set)
# - use show_users
# - implement "format"
# - implement fallback accounting (request size)
###
use strict;
use Getopt::Long;
## Name and version
use vars qw{$release $progName};
$release = "0.07";
$progName = "sqwmstats.pl";
## Define global variables
# option handling
use vars qw{%options %opts};
$options{'help|h|?'} = \$opts{'help'};
$options{"version"} = \$opts{'version'};
# module hooks
use vars qw{%hooks};
# Number formats
use vars qw{%numberformat};
## Module loader
# 1st: look into current directory
push @INC, ".";
# 2nd: look into /usr/local/lib/pflogstats
push @INC, "/usr/local/lib/pflogstats";
# 3rd: look into /usr/lib/pflogstats
push @INC, "/usr/lib/pflogstats";
# General
require "pflogstats-common-support.pm";
require "pflogstats-extensions-addressmapping.pm";
require "pflogstats-extensions-networking.pm";
## Print options (debug)
#for my $key (keys %options) {
# print $key . "\n";
#};
#exit 0;
# Local variables
my %accounting_sqwebmail;
my @mainhelptext;
my $p_hook;
# Time range of logdata
use vars qw{$timemin $timemax};
my ($time);
my %monthNums = qw(
Jan 0 Feb 1 Mar 2 Apr 3 May 4 Jun 5
Jul 6 Aug 7 Sep 8 Oct 9 Nov 10 Dec 11);
# Local functions prototyping
sub print_sqwm_stats();
## Help function
sub print_help() {
print "$progName $release\n\n";
print STDERR " Options from included modules (new style):\n\n";
## Hook 'help'
for my $p_hook (sort keys %{$hooks{'help'}}) {
my $helpstring = &{$hooks{'help'}->{$p_hook}};
print STDERR " Options from module '" . $p_hook . "':";
print STDERR $helpstring . "\n";
};
};
## Hook 'early_begin'
for my $p_hook (keys %{$hooks{'early_begin'}}) {
&{$hooks{'early_begin'}->{$p_hook}};
};
## Get options
my $ret = GetOptions(%options);
if (! $ret ) {
print_help();
exit 1;
};
# Print help or version
if(defined($opts{'help'})) {
print_help();
exit 0;
};
if(defined($opts{'version'})) {
print "$progName $release\n";
exit 0;
};
## Hook 'checkoptions'
for $p_hook (keys %{$hooks{'checkoptions'}}) {
&{$hooks{'checkoptions'}->{$p_hook}};
};
## Hook 'beforemainloopstarts'
for $p_hook (keys %{$hooks{'beforemainloopstarts'}}) {
&{$hooks{'beforemainloopstarts'}->{$p_hook}};
};
print "DEBUG: start parsing logfile\n" if ($opts{'debug'});
## Start parsing logfile #################################################
my ($user, $ip, $rcvd, $sent, $returncode, $request, $url, $date, $size);
my $skip;
while(<>) {
chomp;
$_ =~ s/^M$//g; # Remove trailing CR
$~ =~ s/^[[:space:][:cntrl:]]+$//g; # Remove spaces and ctrl chars only
next if (length($_) == 0); # skip empty lines
# Parsing web log for sqwebmail
# Todo: Datematching!!!!
undef $user; undef $ip; undef $rcvd; undef $sent; undef $returncode; undef $url; undef $date; undef $size;
# Logline: 1.2.3.4 - peter@domain.example [04/Jun/2003:11:42:46 +0200] "GET /webmail/cgi-bin/sqwebmail/login/peter%40domain.example.authdaemon/01362C3A300354FF66A540CB6D4BC929/1054719691?folder=INBOX&form=quickadd&pos=15&newname= HTTP/1.1" 200 6701 "https://server.domain.example/webmail/cgi-bin/sqwebmail/login/peter%40domain.example.authdaemon/01362C3A300354FF66A540CB6D4BC929/1054719691" "Opera/7.11 (Linux 2.4.20-13.7. i686; U) [en]" IN=1327 OUT=9296
# Get content
#printf STDERR "DEBUG: ACC_SQWEBMAIL: line: " . $_ . "\n" if ( $opts{'debug'} & 0x0020 ) ;
($ip, $user, $date, $request, $url, $returncode, $size) = /^([^\s]+)\s+\-\s+([^\s]+)\s+\[(.*)\]\s+\"(GET|HEAD|PUT|POST) ([^"]*)\"\s+(\d+)\s+(\d+|\-)\s+/;
#printf STDERR "DEBUG: ACC_SQWEBMAIL: returncode: " . $returncode . "\n" if ( $opts{'debug'} & 0x0020 ) ;
if (! defined $request) {
# Skip undefined requests
next;
};
if ((! defined $returncode) || ($returncode >= 400)) {
# Skip errors
next;
};
# Calculate Unixtime
if (! ($date =~ /^(\d+)\/(.*)\/(\d+):(\d+):(\d+):(\d+) /)) {
print STDERR "ERROR while parsing date: $date\n";
};
$time = timelocal( $6, $5, $4, $1, $monthNums{$2}, $3);
# Catch min/max times for late timerange display
if (! defined $timemin || ! defined $timemax ) {
# initial values
if (! defined $timemin) { $timemin = $time };
if (! defined $timemax) { $timemax = $time };
} else {
# get min/max
if ($time < $timemin) { $timemin = $time; }
elsif ($time > $timemax) { $timemax = $time; };
};
if ( /\s+IN=([0-9]+).*$/ ) {
$rcvd = $1;
};
if ( /\s+OUT=([0-9]+).*$/ ) {
$sent = $1;
};
if ((! defined $sent) && (! defined $rcvd)) {
# logio values without any prefix tokens at the end of the log line
if ( /([0-9]+)\s+([0-9]+)$/ ) {
$rcvd = $1;
$sent = $2;
};
};
if ((! defined $sent) && (! defined $rcvd)) {
# use request size, we still have nothing else
if ($size =~ /^[0-9]+$/) {
$sent = $size;
$rcvd = 0;
};
};
if ( ! (defined $user && defined $ip && defined $rcvd && defined $sent ) ) {
# not a proper accounting line
print STDERR "DEBUG: ACC_SQWEBMAIL: not a proper line\n" if ( $opts{'debug'} & 0x0010 ) ;
next;
};
# Get URL
if (defined $request && defined $url ) {
#print STDERR "DEBUG: ACC_SQWEBMAIL/URL: $url \n" if ( $opts{'debug'} & 0x0040 ) ;
# Extract user
if ($url =~ /sqwebmail\/login\/(.*)\.authdaemon\//o) {
if ((defined $1) && ($1 ne "")) {
print STDERR "DEBUG: ACC_SQWEBMAIL: user replaced: " . $user . " -> " if ( $opts{'debug'} & 0x0040 ) ;
$user = $1;
# Replace %xx tags
$user =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
print STDERR $user . "\n" if ( $opts{'debug'} & 0x0040 ) ;
};
} elsif ($url !~ /\/webmail\//o) {
next;
};
};
if (! defined $user ) { next; };
if ( $user eq "-" ) { next; };
# Append domain to user
if (! ( $user =~ /\@/ ) ) {
$user .= "@" . $opts{'mydomainname'};
};
## Hook 'modifyaddress'
for $p_hook (keys %{$hooks{'modifyaddress'}}) {
$user = &{$hooks{'modifyaddress'}->{$p_hook}} ($user);
};
printf STDERR "DEBUG: ACC_SQWEBMAIL: user=%s ip=[%s] rcvd=%s sent=%s\n", $user, $ip, $rcvd, $sent if ($opts{'debug'} & 0x0010 ) ;
# Hook "testipaddress"
$skip = 0;
for my $p_hook (keys %{$main::hooks{'testipaddress'}}) {
if ( &{$main::hooks{'testipaddress'}->{$p_hook}} ($ip) != 0 ) {
# excluded
printf STDERR "DEBUG: ACC_SQWEBMAIL: excluded from accounting\n" if ($opts{'debug'} & 0x0010 );
$skip = 1;
last;
};
};
if ($skip == 0) {
$accounting_sqwebmail{'user'}->{$user} += $rcvd + $sent;
$accounting_sqwebmail{'domain'}->{extract_domain($user)} += $rcvd + $sent;
};
};
print "DEBUG: end parsing logfile\n" if ($opts{'debug'});
print_sqwm_stats();
### END
# Sqwebmail statistics
sub print_sqwm_stats() {
my $sum;
my %accounting;
my $p_hash;
print_headline("SqWebMail accounting statistics", "default");
$p_hash = $accounting_sqwebmail{'user'};
%accounting = %$p_hash;
$sum = 0;
print '='x75 . "\n";
printf "%-50s: %6s\n", "User", "BytesTraffic";
print_timerange_normal();
print '-'x75 . "\n";
for my $user (sort keys %accounting) {
if ( ! defined $accounting{$user} ) { $accounting{$user} = 0; };
$sum += $accounting{$user};
printf "%-50s: %9d %9s\n",
substr ($user, 0, 50),
$accounting{$user},
format_number($accounting{$user});
};
print '-'x75 . "\n";
printf "%-50s: %9d %9s\n", "Total",
$sum,
format_number($sum);
print '='x75 . "\n";
print "\n";
$p_hash = $accounting_sqwebmail{'domain'};
%accounting = %$p_hash;
$sum = 0;
print '='x75 . "\n";
printf "%-50s: %6s\n", "Domain", "BytesTraffic";
print_timerange_normal();
print '-'x75 . "\n";
for my $domain (sort keys %accounting) {
if ( ! defined $accounting{$domain} ) { $accounting{$domain} = 0; };
$sum += $accounting{$domain};
printf "%-50s: %9d %9s\n",
substr ($domain, 0, 50),
$accounting{$domain},
format_number($accounting{$domain});
};
print '-'x75 . "\n";
printf "%-50s: %9d %9s\n", "Total",
$sum,
format_number($sum);
print '='x75 . "\n";
return 0;
};
syntax highlighted by Code2HTML, v. 0.9.1