#!/usr/bin/env perl # # SendmailAnalyzer: maillog parser and statistics reports tool for Sendmail # Copyright (C) 2002-2020 Gilles Darold # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # use vars qw($SOFTWARE $VERSION $AUTHOR $COPYRIGHT); use strict; no strict qw/refs/; use CGI qw(:all); use Benchmark; use MIME::QuotedPrint qw(decode_qp); use MIME::Base64 qw(decode_base64); use Time::Local 'timelocal_nocheck'; use POSIX qw/ strftime /; $| = 1; # Configuration storage hash my %CONFIG = (); # Other configuration directives my $CONFIG_FILE = "/usr/local/sendmailanalyzer/sendmailanalyzer.conf"; my $LAST_PARSE_FILE = 'LAST_PARSED'; $SOFTWARE = "SendmailAnalyzer"; $VERSION = '9.4'; $AUTHOR = "Gilles Darold "; $COPYRIGHT = "© 2002-2020 - Gilles Darold "; our %TRANSLATE = (); our %SMTP_ERROR_CODE = (); our %ESMTP_ERROR_CODE = (); our %ANTISPAM_NAME = ( 'spamdmilter' => 'Spamd-Milter', 'jchkmail' => 'J-ChkMail', 'dnsbl' => 'RBL Check', 'spamassassin' => 'SpamAssassin', 'amavis' => 'Amavis', 'mimedefang' => 'MIMEDefang', 'dnsblmilter' => 'DNSBL-Milter', 'spamd' => 'Spamd', 'policydweight' => 'Policyd-weight', ); my $CGI = new CGI; my $HOST = $CGI->param('host') || ''; my $CURDATE = $CGI->param('date') || ''; my $DOMAIN = $CGI->param('domain') || ''; my $TYPE = $CGI->param('type') || ''; my $PERI = $CGI->param('peri') || ''; my $SEARCH = $CGI->param('search') || ''; my $HOUR = $CGI->param('hour') || ''; my $VIEW = $CGI->param('view') || ''; my $LANG = $CGI->param('lang') || ''; my $WEEK = $CGI->param('week') || ''; my $DOWNLOAD= $CGI->param('download') || ''; my $MAXPIECOUNT = 10; my $MIN_SHOW_PIE = 2; my $DEFAULT_CHARSET='utf-8'; my $WEEK_START_MONDAY = 1; # Format the week number $WEEK = sprintf("%02d", $WEEK) if ($WEEK); # Read configuration file &read_config($CONFIG_FILE); # Write sendmailanalyzer header $CGI->charset($CONFIG{HTML_CHARSET} || $DEFAULT_CHARSET); if (!$DOWNLOAD) { print $CGI->header(); print $CGI->start_html(-title=>"sendmailanalyzer v$VERSION"); print qq{ }; } # CSS style used in temporal menu for the current hour/day/month my $SELCURRENT = ' style="color: #4179a1; font-weight: bold;"'; if (my $ret = &secure_params()) { &logerror("Bad CGI param, hacking attempt: $ret"); print $CGI->end_html() if (!$DOWNLOAD); die "FATAL: Bad CGI param, hacking attempt: $ret\n"; } # Check if output dir exist if (!-d $CONFIG{OUT_DIR}) { die "FATAL: Output directory $CONFIG{OUT_DIR} should exists !\n"; } # Check dynamic change of language if ($LANG) { $CONFIG{LANG} =~ s/\/[^\/]+$/\/$LANG/; if (!-e $CONFIG{LANG}) { # Falling back to default $CONFIG{LANG} =~ s/\/[^\/]+$/\/en_US/; } } # Check if lang file is readable if (!-e $CONFIG{LANG}) { &logerror("Language file $CONFIG{LANG} doesn't exist!"); print $CGI->end_html() if (!$DOWNLOAD); die "FATAL: Language file is not readable, $CONFIG{LANG}\n"; } else { my $file = $CONFIG{LANG}; $file = './' . $CONFIG{LANG} if ($CONFIG{LANG} !~ /^\//); do "$file"; } $CURDATE ||= &get_curdate(); # Check if smtp error code file is readable if (-e $CONFIG{ERROR_CODE}) { my $file = $CONFIG{ERROR_CODE}; $file = './' . $CONFIG{ERROR_CODE} if ($CONFIG{ERROR_CODE} !~ /^\//); do "$file"; } # Print global header &sa_header($CGI) if (!$VIEW && !$DOWNLOAD); # Check if we have a SQL-host configured for virtual domains if ( $CONFIG{VIRTUAL_DOMAIN_DB} && $CONFIG{VIRTUAL_DOMAIN_DB_QUERY} ) { eval("use DBI;"); my $dbh = DBI->connect( $CONFIG{VIRTUAL_DOMAIN_DB}, $CONFIG{VIRTUAL_DOMAIN_DB_USER}, $CONFIG{VIRTUAL_DOMAIN_DB_PASS}, ) or die "Couldn't connect to database: " . DBI->errstr; # Get all virtual domains and stores them in $CONFIG{LOCAL_DOMAIN} array my $sth = $dbh->prepare( $CONFIG{VIRTUAL_DOMAIN_DB_QUERY} ); $sth->execute(); while (my @row = $sth->fetchrow_array) { push(@{ $CONFIG{LOCAL_DOMAIN} }, $row[0]); } $sth->finish(); $dbh->disconnect() if (defined $dbh); } # Set default host report if there's only one host and no per domain report. my @syshost = get_list_host(); if ( !$HOST && ($#syshost == 0) && ($#{$CONFIG{DOMAIN_REPORT}} == -1) && (scalar keys %{$CONFIG{DOMAIN_HOST_REPORT}} == 0) ) { $HOST = $syshost[0]; } # Choose the interface to display if (!$HOST) { # List available statistics &show_list_host(@syshost) if (&check_auth); } elsif ($TYPE && $PERI) { # Show details if (!$DOWNLOAD) { &show_detail($HOST, $CURDATE, $HOUR, $TYPE, $PERI, $SEARCH) if (&check_auth); } else { &show_download_detail($HOST, $CURDATE, $HOUR, $TYPE, $PERI, $SEARCH) if (&check_auth); } } elsif (!$VIEW) { print "
\n"; &show_temporal_menu($HOST, $CURDATE, $HOUR, $DOMAIN, $WEEK); my $iframe_param = "view=empty&host=$HOST&date=$CURDATE&lang=$LANG&domain=$DOMAIN"; if ($CGI->param('update') ne '') { foreach my $p ($CGI->param()) { next if ( ($p eq 'update') || ($p eq 'view') ); $iframe_param .= "&$p=" . $CGI->param($p); } $iframe_param = "view=" . $CGI->param('update') . "&" . $iframe_param; } print qq{

$TRANSLATE{'Global Statistics'} $TRANSLATE{'Top Statistics'} }; if ($CONFIG{SPAM_DETAIL}) { print qq{ $TRANSLATE{'AntiSpam details'} }; } print qq{
}; } elsif ($VIEW eq 'empty') { my $curdomain = ''; $curdomain = " [$DOMAIN]" if ($DOMAIN); my $back = ''; if ($CONFIG{ADMIN} && $ENV{REMOTE_USER} && !grep(/^$ENV{REMOTE_USER}$/, split(/[\s\t,;]+/, $CONFIG{ADMIN})) ) { $back = "?host=$HOST&date=$CURDATE&lang=$LANG&domain=$DOMAIN&week=$WEEK" if ($DOMAIN); } elsif ($LANG) { $back = "?lang=$LANG"; } print qq{
\U$HOST\E$curdomain

}; my $last = &get_last_parse(); $last =~ s/^(...\s+\d+\s+\d+:\d+:\d+).*/$1/; print "$TRANSLATE{'Last record'}: ", 1900+(localtime(time))[5], " $last\n" if ($last); print "
\n"; } else { print qq{
} if (!$DOWNLOAD); &show_stats($HOST, $CURDATE, $HOUR, $DOMAIN, $VIEW, $WEEK) if (&check_auth); print "\n
\n" if (!$DOWNLOAD); } # Write sendmailanalyzer footer &sa_footer($CGI) if (!$VIEW && !$DOWNLOAD); print $CGI->end_html() if (!$DOWNLOAD); exit 0; #-------------------------------- ROUTINES ------------------------------------ our %GLOBAL_STATUS = (); our %STATS = (); our %AUTH = (); our $SIZE_UNIT = 1; our $UNIQID = 0; our %topsender = (); our %toprcpt = (); our %topspam = (); our %topvirus = (); our %topreject = (); our %toperr = (); our %topdsn = (); our %topmaxrcpt = (); our %topmaxsize = (); our %delivery = (); our %messaging = (); our %spam = (); our %virus = (); our %reject = (); our %err = (); our %dsn = (); our %topspamdetail = (); our %auth = (); our %topauth = (); our %postgrey = (); our %toppostgrey = (); our %starttls = (); #### # Read last parsed line from file #### sub get_last_parse { my $last_parsed = ''; if (-e "$CONFIG{OUT_DIR}/$LAST_PARSE_FILE") { if (not open(IN, "$CONFIG{OUT_DIR}/$LAST_PARSE_FILE") ) { &logerror("Can't read to file $CONFIG{OUT_DIR}/$LAST_PARSE_FILE: $!"); } else { $last_parsed = ; close(IN); } } return $last_parsed; } #### # Routine used to log sendmailanalyzer errors or send emails alert if requested #### sub logerror { my $str = shift; print "

ERROR: $str

\n"; } #### # Read configuration file #### sub read_config { my $file = shift; if (!-e $file) { $file = '/etc/sendmailanalyzer.conf'; } if (!-e $file) { &logerror("Configuration file $file doesn't exists"); return; } else { if (not open(IN, $file)) { &logerror("Can't read configuration file $file: $!"); } else { while () { chomp; s/#.*//; s/^[\s\t]+//; s/[\s\t]$//; if ($_ ne '') { my ($var, $val) = split(/[\s\t]+/, $_, 2); if ($var =~ /DOMAIN_USER/i) { my ($usr, @doms) = split(/[\t,;\s]/, $val); push(@{$CONFIG{DOMAIN_USER}{$usr}}, @doms); } elsif ($var =~ /DOMAIN_HOST_REPORT/i) { my ($hst, @doms) = split(/[\t,;\s]/, $val); push(@{$CONFIG{DOMAIN_HOST_REPORT}{$hst}}, @doms); } elsif ($var =~ /DOMAIN_REPORT/i) { push(@{$CONFIG{DOMAIN_REPORT}}, split(/[\t,;\s]/, $val)); } elsif ($var =~ /LOCAL_DOMAIN/i) { if (-e $val) { if (open(my $in, '<', $val)) { @{$CONFIG{LOCAL_DOMAIN}} = <$in>; chomp(@{$CONFIG{LOCAL_DOMAIN}}); close($in); } else { &logerror("LOCAL_DOMAIN file $val can not be read, $!"); } } else { push(@{$CONFIG{LOCAL_DOMAIN}}, split(/[\t,;\s]/, $val)); } } elsif ($var =~ /LOCAL_HOST_DOMAIN/i) { my ($hst, @doms) = split(/[\t,;\s]/, $val); if ($#doms == 0 && -e $doms[0]) { if (open(my $in, '<', $doms[0])) { @{$CONFIG{LOCAL_HOST_DOMAIN}{$hst}} = <$in>; chomp(@{$CONFIG{LOCAL_HOST_DOMAIN}{$hst}}); close($in); } else { &logerror("LOCAL_DOMAIN file $doms[0] can not be read, $!"); } } else { push(@{$CONFIG{LOCAL_HOST_DOMAIN}{$hst}}, @doms); } } elsif ($var =~ /REPLACE_HOST/i) { my ($pat, $repl) = split(/[\t,;\s]/, $val); $CONFIG{REPLACE_HOST}{$pat} = $repl; } else { $CONFIG{$var} = $val if (!defined $CONFIG{$var} && ($val ne '')); } } } close(IN); } } # Set default values $CONFIG{OUT_DIR} ||= '/var/www/sendmailanalyzer'; $CONFIG{TOP} ||= 25; $CONFIG{MAIL_HUB} ||= ''; $CONFIG{MAIL_GW} ||= ''; $CONFIG{LANG} ||= 'lang/en_US'; # Normalyze size unit if ($CONFIG{SIZE_UNIT} eq 'KBytes') { $SIZE_UNIT = 1000; } elsif ($CONFIG{SIZE_UNIT} eq 'MBytes') { $SIZE_UNIT = 1000000; } else { $CONFIG{SIZE_UNIT} = 'KBytes'; $SIZE_UNIT = 1; } if (!exists $CONFIG{SPAM_DETAIL}) { $CONFIG{SPAM_DETAIL} = 1; } if (!exists $CONFIG{SMTP_AUTH}) { $CONFIG{SMTP_AUTH} = 1; } if (!exists $CONFIG{SPAM_VIEW}) { $CONFIG{SPAM_VIEW} = 1; } if (!exists $CONFIG{VIRUS_VIEW}) { $CONFIG{VIRUS_VIEW} = 1; } if (!exists $CONFIG{DSN_VIEW}) { $CONFIG{DSN_VIEW} = 1; } if (!exists $CONFIG{SHOW_DIRECTION}) { $CONFIG{SHOW_DIRECTION} = 1; } if ($CONFIG{SPAM_TOOLS}) { my @names = split(/[,;\s\t]+/, $CONFIG{SPAM_TOOLS}); foreach my $k (keys %ANTISPAM_NAME) { if (!grep(/^$k$/i, @names)) { delete $ANTISPAM_NAME{$k}; } } } if (!exists $CONFIG{URL_JSCRIPT}) { $CONFIG{URL_JSCRIPT} = 'flotr2.js'; } if (!exists $CONFIG{URL_SORTABLE}) { $CONFIG{URL_SORTABLE} = 'sorttable.js'; } } sub sa_header { my $q = shift; my $title = 'Sendmail Report'; if ($CONFIG{ANONYMIZE}) { $title = 'Sendmail Anonymized Report'; } my $back = ''; if ($CONFIG{ADMIN} && $ENV{REMOTE_USER} && !grep(/^$ENV{REMOTE_USER}$/, split(/[\s\t,;]+/, $CONFIG{ADMIN})) ) { $back = "?host=$HOST&date=$CURDATE&lang=$LANG&domain=$DOMAIN" if ($DOMAIN); } elsif ($LANG) { $back = "?lang=$LANG"; } print "\n"; print "$title\n"; if ($CONFIG{ADMIN} && $ENV{REMOTE_USER} && !grep(/^$ENV{REMOTE_USER}$/, split(/[\s\t,;]+/, $CONFIG{ADMIN})) && !exists($CONFIG{DOMAIN_USER}{$ENV{REMOTE_USER}}) ) { &logerror("Access denied for user: $ENV{REMOTE_USER}."); &sa_footer($q); exit 0; } print $q->start_form(); } sub sa_footer { my $q = shift; print qq{
Powered by $SOFTWARE v$VERSION (GPLv3)
$TRANSLATE{'Acknowledgement'}
}; } #### # Search previous record before a given date #### sub get_previous { my ($hostname, $year, $month, $day) = @_; if ($day ne '00') { $day--; $day = sprintf("%02d", $day); while (!-d "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day") { $day--; if ($day <= 0) { $day = 31; $month--; } if ($month <= 0) { $month = 12; $year--; } $day = sprintf("%02d", $day); $month = sprintf("%02d", $month); last if (!-d "$CONFIG{OUT_DIR}/$hostname/$year/$month"); } return "$year$month$day" if (-d "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day"); } elsif ($month ne '00') { $month--; $month = sprintf("%02d", $month); while (!-d "$CONFIG{OUT_DIR}/$hostname/$year/$month") { $month--; if ($month <= 0) { $month = 12; $year--; } $month = sprintf("%02d", $month); last if (!-d "$CONFIG{OUT_DIR}/$hostname/$year"); } return "$year$month" . "00" if (-d "$CONFIG{OUT_DIR}/$hostname/$year/$month"); } else { $year--; if (-d "$CONFIG{OUT_DIR}/$hostname/$year") { return $year . "0000" } } return; } #### # Search next record after a given date #### sub get_next { my ($hostname, $year, $month, $day) = @_; if ($day ne '00') { $day++; $day = sprintf("%02d", $day); while (!-d "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day") { $day++; if ($day > 31) { $day = 1; $month++; } if ($month > 12) { $month = 1; $year++; } $day = sprintf("%02d", $day); $month = sprintf("%02d", $month); last if (!-d "$CONFIG{OUT_DIR}/$hostname/$year/$month"); } return "$year$month$day" if (-d "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day"); } elsif ($month ne '00') { $month++; $month = sprintf("%02d", $month); while (!-d "$CONFIG{OUT_DIR}/$hostname/$year/$month") { $month++; if ($month > 12) { $month = 1; $year++; } $month = sprintf("%02d", $month); last if (!-d "$CONFIG{OUT_DIR}/$hostname/$year"); } return "$year$month" . "00" if (-d "$CONFIG{OUT_DIR}/$hostname/$year/$month"); } else { $year--; if (-d "$CONFIG{OUT_DIR}/$hostname/$year") { return $year . "0000"; } } return; } #### # Display Month navigator #### sub month_link { my ($hostname, $year, $month, $domain) = @_; my $str = qq{ }; for my $d ("01" .. "12") { $str .= "" if (grep(/^$d$/,'01','05','09')); if ( !-d "$CONFIG{OUT_DIR}/$hostname/$year/$d" ) { $str .= ""; } else { my $date = $year . $d . '00'; my $tag = 'td'; $tag = 'th' if ($d eq $month); my $type = ''; $type = $SELCURRENT if ($d eq $month); $str .= "<$tag>$TRANSLATE{$d}"; } $str .= "\n" if ( grep(/^$d$/, '04', '08', '12') ); } $str .= "
$TRANSLATE{'Month View'}
$TRANSLATE{$d}
\n"; return $str; } #### # Display Hour navigator #### sub hour_link { my ($hostname, $year, $month, $day, $hour, $domain) = @_; my ($sec,$min,$h,$mday,$mon,$y,$wday,$yday,$isdst) = localtime(time); $mon++; $mon = sprintf("%02d", $mon); $mday = sprintf("%02d", $mday); $y += 1900; return if ( ($mon ne $month) || ($day eq '00')); my $str = "\n\n"; for my $d ("00" .. "23") { if ( ("$year$month$day" eq "$y$mon$mday") && ($d > $h) ) { $str .= ""; } else { my $tag = 'td'; $tag = 'th' if (($hour ne '') && ($d == $hour)); my $type = ''; $type = $SELCURRENT if (($hour ne '') && ($d == $hour)); my $date = $year . $month . $day; $str .= "<$tag>$d"; } } $str .= "\n
$TRANSLATE{'Hour View'}
$d
\n"; return $str; } sub IsLeapYear { return ((($_[0] & 3) == 0) && (($_[0] % 100 != 0) || ($_[0] % 400 == 0))); } #### # Display day navigator #### sub day_link { my ($hostname, $year, $month, $day, $domain) = @_; my $str = "\n\n"; my @wday = qw(Mo Tu We Th Fr Sa Su); my @std_day = qw(Su Mo Tu We Th Fr Sa); my %day_lbl = (); if (exists $TRANSLATE{WeekDay}) { my @tmpwday = split(/\s+/, $TRANSLATE{WeekDay}); for (my $i = 0; $i <= $#std_day; $i++) { $day_lbl{$std_day[$i]} = $tmpwday[$i]; } } else { for (my $i = 0; $i <= $#wday; $i++) { $day_lbl{$wday[$i]} = $wday[$i]; } } $str .= ""; map { $str .= ''; } @wday; $str .= "\n"; my @currow = ('','','','','','',''); my $wd = 0; my $wn = 0; my $date = ''; for my $d ("01" .. "31") { last if (($d == 31) && grep(/^$month$/, '04','06','09','11')); last if (($d == 30) && ($month eq '02')); last if (($d == 29) && ($month eq '02') && !&IsLeapYear($year)); $wd = &get_day_of_week($year,$month,$d); $wn = &get_week_number($year,$month,$d); next if ($wn == -1); $date = $year . $month . $d; if ( !-d "$CONFIG{OUT_DIR}/$hostname/$year/$month/$d" ) { $currow[$wd] = ""; } else { my $type = ''; $type = $SELCURRENT if ($d eq $day); $currow[$wd] = ""; } if ($wd == 6) { my $week = sprintf("%02d", $wn); if (grep(/href/, @currow)) { $week = ""; } else { $week = "" } for (my $i = 0; $i <= $#currow; $i++) { $currow[$i] = "" if ($currow[$i] eq ''); } $str .= "$week" . join('', @currow) . "\n"; @currow = ('','','','','','',''); } } if ( ($wd != 6) || ($currow[0] ne '') ) { my $week = sprintf("%02d", $wn); if (grep(/href/, @currow)) { $week = ""; } else { $week = "" } for (my $i = 0; $i <= $#currow; $i++) { $currow[$i] = "" if ($currow[$i] eq ''); } $str .= "$week" . join('', @currow) . "\n"; @currow = ('','','','','','',''); } $str .= "
$TRANSLATE{'Day View'}
 ' . $day_lbl{$_} . '
$d$d$week$week 
$week$week 
\n"; return $str; } #### # Get the week day of a date #### sub get_day_of_week { my ($year, $month, $day) = @_; # %u The day of the week as a decimal, range 1 to 7, Monday being 1. # %w The day of the week as a decimal, range 0 to 6, Sunday being 0. my $weekDay = ''; if (!$WEEK_START_MONDAY) { # Start on sunday = 0 $weekDay = POSIX::strftime("%w", 1,1,1,$day,--$month,$year-1900); } else { # Start on monday = 1 $weekDay = POSIX::strftime("%u", 1,1,1,$day,--$month,$year-1900); $weekDay--; } return $weekDay; } #### # Get week number #### sub get_week_number { my ($year, $month, $day) = @_; # %U The week number of the current year as a decimal number, range 00 to 53, starting with the first # Sunday as the first day of week 01. # %V The ISO 8601 week number (see NOTES) of the current year as a decimal number, range 01 to 53, # where week 1 is the first week that has at least 4 days in the new year. # %W The week number of the current year as a decimal number, range 00 to 53, starting with the first # Monday as the first day of week 01. # Check if the date is valid first my $datefmt = POSIX::strftime("%Y-%m-%d", 1, 1, 1, $day, $month - 1, $year - 1900); if ($datefmt ne "$year-$month-$day") { return -1; } my $weekNumber = ''; if (!$WEEK_START_MONDAY) { $weekNumber = POSIX::strftime("%U", 1, 1, 1, $day, $month - 1, $year - 1900); } else { $weekNumber = POSIX::strftime("%W", 1, 1, 1, $day, $month - 1, $year - 1900); } return sprintf("%02d", $weekNumber+1); } #### # Display year navigator #### sub year_link { my ($hostname, $domain, $curyear) = @_; # Look at all years directories in data dir if (not opendir(DIR, "$CONFIG{OUT_DIR}/$hostname")) { &logerror("Can't open directory $CONFIG{OUT_DIR}/$hostname: $!\n"); return; } my @dirs = grep { /^\d+$/ && -d "$CONFIG{OUT_DIR}/$hostname/$_" } readdir(DIR); closedir(DIR); # Append current year if it is not present my $current_year = (localtime(time))[5]+1900; if (!grep(/$current_year$/, @dirs)) { push(@dirs, $current_year); } my $str = qq{ \n"; return $str; } #### # Show temporal menu #### sub show_temporal_menu { my ($hostname, $date, $hour, $domain, $week) = @_; my ($current, $year, $mon, $mday, $period, $x_label, $begin, $end) = &normalyze_date($date, $hour, $week); my $timestamp = $year; $timestamp = $TRANSLATE{$mon} . ' ' . $timestamp if ($mon ne '00'); $timestamp = $mday . ' ' . $timestamp if ($mday ne '00'); my $previous = &get_previous($hostname, $year, $mon, $mday); my $next = &get_next($hostname, $year, $mon, $mday); # Compute time navigation menus my $year_view = &year_link($hostname, $domain, $year); my $month_view = &month_link($hostname,$year,$mon,$domain); my $day_view = &day_link($hostname,$year,$mon,$mday,$domain); my $hour_view = &hour_link($hostname,$year,$mon,$mday,$hour,$domain); # On year view remove empty daily calendar if ($date =~ /0000$/) { $day_view = ''; } print qq{
$TRANSLATE{Years}: $year_view }; if ($previous) { print "<< $TRANSLATE{Previous}"; } else { print "<< $TRANSLATE{Previous}"; } print "    -    [$timestamp]    -    "; if ($next) { print "$TRANSLATE{Next} >>"; } else { print "$TRANSLATE{Next} >>"; } my $curdomain = ''; $curdomain = " [$DOMAIN]" if ($DOMAIN); print qq{
$day_view
$month_view
$period$curdomain
$hour_view
}; } #### # Display all host statistics collected #### sub show_list_host { my @dirs = @_; print "
 
\n"; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon++; $year += 1900; $mon = sprintf("%02d",$mon); $mday = sprintf("%02d",$mday); my $curday = $year . $mon . $mday; print "
\n"; foreach my $h (@dirs) { print "
  • $TRANSLATE{'Consult global statistics for'} '$h'\n" if (!$ENV{REMOTE_USER} || !$CONFIG{ADMIN} || grep(/^$ENV{REMOTE_USER}$/, split(/[\s\t,;]+/, $CONFIG{ADMIN})) ); print "
      " if (($#{$CONFIG{DOMAIN_REPORT}} >= 0) || (scalar keys %{$CONFIG{DOMAIN_HOST_REPORT}} > 0)); if (!$ENV{REMOTE_USER} || !$CONFIG{ADMIN} || grep(/^$ENV{REMOTE_USER}$/, split(/[\s\t,;]+/, $CONFIG{ADMIN})) ) { foreach my $d (@{$CONFIG{DOMAIN_REPORT}}) { print "
    • $TRANSLATE{'Statistics for domain'} '$d'
    • \n"; } if (exists $CONFIG{DOMAIN_HOST_REPORT}{$h}) { foreach my $d (@{$CONFIG{DOMAIN_HOST_REPORT}{$h}}) { print "
    • $TRANSLATE{'Statistics for domain'} '$d'
    • \n"; } } } else { foreach my $d (@{$CONFIG{DOMAIN_REPORT}}) { print "
    • $TRANSLATE{'Statistics for domain'} '$d'
    • \n" if (!$ENV{REMOTE_USER} || grep(/^$d$/, @{$CONFIG{DOMAIN_USER}{$ENV{REMOTE_USER}}})); } if (exists $CONFIG{DOMAIN_HOST_REPORT}{$h}) { foreach my $d (@{$CONFIG{DOMAIN_HOST_REPORT}{$h}}) { print "
    • $TRANSLATE{'Statistics for domain'} '$d'
    • \n" if (!$ENV{REMOTE_USER} || grep(/^$d$/, @{$CONFIG{DOMAIN_USER}{$ENV{REMOTE_USER}}})); } } } print "
    " if (($#{$CONFIG{DOMAIN_REPORT}} >= 0) || (scalar keys %{$CONFIG{DOMAIN_HOST_REPORT}} > 0)); print "
"; } print "
\n"; } sub normalyze_date { my ($date, $hour, $week) = @_; # Set default to current timestamp my ($s,$m,$h,$mday,$mon,$year,$wd,$yd,$isdst) = localtime(time); $mon++; $year += 1900; my $current = $year . sprintf("%02d",$mon) . sprintf("%02d",$mday); # Normalyze date parameter if ($date =~ /^(\d{4})(\d{2})(\d{2})$/) { $year = $1; $mon = $2; $mday = $3; } elsif ($date =~ /^(\d{4})(\d{2})$/) { $year = $1; $mon = $2; $mday = '00'; } elsif ($date =~ /^(\d{4})$/) { $year = $1; $mon = '00'; $mday = '00'; } $mon = sprintf("%02d",$mon); $mday = sprintf("%02d",$mday); if ($week) { $mday = '00'; my $period = $TRANSLATE{'Weekly'}; my $x_label = $TRANSLATE{'Days of the week'}; my @days = &get_week_boundaries($year, $week); return ($current, $year, $mon, $mday, $period, $x_label, join(':', @days)); } # Set default labels for graph output my $period = $TRANSLATE{'Monthly'}; my $x_label = $TRANSLATE{'Days of the month'}; my $begin = '01'; my $end = '31'; if ($hour ne '') { $period = $TRANSLATE{'Hourly'}; $x_label = $TRANSLATE{'Minutes of the hour'}; $begin = '00'; $end = '60'; } elsif ($mday ne '00') { $period = $TRANSLATE{'Daily'}; $x_label = $TRANSLATE{'Hours of the day'}; $begin = '00'; $end = '23'; } elsif ($mon eq '00') { $period = $TRANSLATE{'Yearly'}; $x_label = $TRANSLATE{'Months of the year'}; $end = '12'; } return ($current, $year, $mon, $mday, $period, $x_label, $begin, $end); } #### # Get all days of a week #### sub get_week_boundaries { my ($year, $week) = @_; my @days = (); my $wn = 0; for my $m ('01' .. '12') { for my $d ('01' .. '31') { $wn = &get_week_number($year,$m,$d); next if ($wn == -1); push(@days, "$d") if ($wn == $week); } } return @days; } #### # Show global statistics #### sub show_stats { my ($hostname, $date, $hour, $domain, $type, $week) = @_; my ($current, $year, $mon, $mday, $period, $x_label, $begin, $end) = &normalyze_date($date, $hour, $week); my %period_stats = (); my $cache_path = $date; if ($week) { $cache_path = $year . '/weeks/' . $week; } $cache_path =~ s/^(\d{4})(\d{2})(\d{2})$/$1\/$2\/$3/; $cache_path =~ s/^(\d{4})(\d{2})$/$1\/$2/; $cache_path =~ s/\/00//g if (!$week); # Prevent Out of memory if ( ($mday eq '00') && !-e "$CONFIG{OUT_DIR}/$hostname/$cache_path/cache.pm") { print "

No cache file found ($CONFIG{OUT_DIR}/$hostname/$cache_path/cache.pm) , please run sa_cache as explain in the documentation (See Caching).

\n"; return; } # Use cache files if possible in hour view if ($hour && -e "$CONFIG{OUT_DIR}/$hostname/$cache_path/${hour}cache.pm\U$domain\E") { my $file = "$CONFIG{OUT_DIR}/$hostname/$cache_path/${hour}cache.pm\U$domain\E"; do "$file"; } elsif ($hour && -e "$CONFIG{OUT_DIR}/$hostname/$cache_path/cache.pm\U$domain\E") { # For caching usage to limit out of memory problem print "

No cache file yet for this hour, please wait for next run of 'sa_cache --actual-day-only'.

\n"; # Use cache files if possible for week or in year/month/day view } elsif (!$hour && -e "$CONFIG{OUT_DIR}/$hostname/$cache_path/cache.pm\U$domain\E") { my $file = "$CONFIG{OUT_DIR}/$hostname/$cache_path/cache.pm\U$domain\E"; do "$file"; # No cache file, we need to compute stat from scratch } else { # Compute statistics for the given period for (my $i = "$mon$mday"; $i <= 1231; $i++) { $i =~ s/^00/01/; $i =~ s/00$/01/; if ($i =~ /(\d{2})32/) { $i = "$1" . "01"; $i += 100; } last if ($i > 1231); $i = sprintf("%04d", $i); $i =~ /(\d{2})(\d{2})/; my $m = "$1"; my $d = "$2"; last if (($mon ne '00') && ($m > $mon)); if ($type eq 'messageflow') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'spamflow') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get spamming statistics &get_spam_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'virusflow') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get virus statistics &get_virus_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'dsnflow') { # Get DSN statistics &get_dsn_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'rejectflow') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get rejection statistics &get_rejected_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get syserr statistics &get_syserr_stat($hostname,$year,$m,$d,$hour); } elsif ($type eq 'statusflow') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get rejection statistics &get_rejected_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get syserr statistics &get_syserr_stat($hostname,$year,$m,$d,$hour); # Get spamming statistics &get_spam_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get virus statistics &get_virus_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'authflow') { # Get auth statistics &get_auth_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'postgreyflow') { # Get postgrey statistics &get_postgrey_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'topsender') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'toprecipient') { # Get recipient statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour) if ($DOMAIN); &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'topspam') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get spamming statistics &get_spam_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'topvirus') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour, 1); # Get virus statistics &get_virus_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'topdsn') { # Get dsn statistics &get_dsn_top_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'topreject') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get rejection statistics &get_rejected_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get syserr statistics &get_syserr_stat($hostname,$year,$m,$d,$hour); } elsif ($type eq 'toplimit') { # Get sender statistics &get_sender_stat($hostname,$year,$m,$d,$mon,$mday,$hour); # Get recipient statistics and delivery status &get_recipient_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif (grep(/^$type$/, keys %ANTISPAM_NAME)) { # Get spam details &get_spamdetail_stat($type,$hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'topauth') { # Get auth statistics &get_auth_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } elsif ($type eq 'toppostgrey') { &get_postgrey_stat($hostname,$year,$m,$d,$mon,$mday,$hour); } last if ($mday ne '00'); } if ($type eq 'messageflow') { %period_stats = &compute_messageflow($hostname); } elsif ($type eq 'spamflow') { %period_stats = &compute_spamflow($hostname); } elsif ($type eq 'virusflow') { %period_stats = &compute_virusflow($hostname); } elsif ($type eq 'dsnflow') { %period_stats = &compute_dsnflow($hostname); } elsif ($type eq 'rejectflow') { %period_stats = &compute_rejectflow(); } elsif ($type eq 'statusflow') { &compute_statusflow(); } elsif ($type eq 'authflow') { %period_stats = &compute_authflow(); } elsif ($type eq 'postgreyflow') { %period_stats = &compute_postgreyflow($hostname); } elsif ($type eq 'topsender') { &compute_top_sender(); } elsif ($type eq 'toprecipient') { &compute_top_recipient(); } elsif ($type eq 'topspam') { &compute_top_spam(); } elsif ($type eq 'topvirus') { &compute_top_virus(); } elsif ($type eq 'topdsn') { &compute_top_dsn(); } elsif ($type eq 'topreject') { &compute_top_reject(); } elsif ($type eq 'toplimit') { &compute_top_limit(); } elsif (grep(/^$type$/, keys %ANTISPAM_NAME)) { &compute_top_spamdetail($type); } elsif ($type eq 'topauth') { &compute_top_auth(); } elsif ($type eq 'toppostgrey') { &compute_top_postgrey(); } } if ($type eq 'messageflow') { &summarize_messageflow($begin, $end, %period_stats); &display_messageflow($hostname, $date, $hour, $x_label); } elsif ($type eq 'spamflow') { &summarize_spamflow($begin, $end, %period_stats); &display_spamflow($x_label); } elsif ($type eq 'virusflow') { &summarize_virusflow($begin, $end, %period_stats); &display_virusflow($x_label); } elsif ($type eq 'dsnflow') { &summarize_dsnflow($begin, $end, %period_stats); &display_dsnflow($x_label); } elsif ($type eq 'rejectflow') { &summarize_rejectflow($begin, $end, %period_stats); &display_rejectflow($x_label); } elsif ($type eq 'statusflow') { &display_statusflow($x_label); } elsif ($type eq 'authflow') { &summarize_authflow($begin, $end, %period_stats); &display_authflow($x_label); } elsif ($type eq 'postgreyflow') { &summarize_postgreyflow($begin, $end, %period_stats); &display_postgreyflow($x_label); } elsif ($type eq 'topsender') { if (!$DOWNLOAD) { &display_top_sender($hostname, $date, $hour); } else { &dump_top_sender($hostname, $date, $hour); } } elsif ($type eq 'toprecipient') { if (!$DOWNLOAD) { &display_top_recipient($hostname, $date, $hour); } else { &dump_top_recipient($hostname, $date, $hour); } } elsif ($type eq 'topspam') { &display_top_spam($hostname, $date, $hour); } elsif ($type eq 'topvirus') { &display_top_virus($hostname, $date, $hour); } elsif ($type eq 'topdsn') { &display_top_dsn($hostname, $date, $hour); } elsif ($type eq 'topreject') { &display_top_reject($hostname, $date, $hour); } elsif ($type eq 'toplimit') { &display_top_limit($hostname, $date, $hour); } elsif (grep(/^$type$/, keys %ANTISPAM_NAME)) { &display_top_spamdetail($hostname, $date, $type, $hour); } elsif ($type eq 'topauth') { &display_top_auth($hostname, $date, $hour); } elsif ($type eq 'toppostgrey') { &display_top_postgrey($hostname, $date, $hour); } } #### # Get senders statistics #### sub get_sender_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/senders.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; $STATS{$data[1]}{hour} = $data[0]; $STATS{$data[1]}{sender} = $data[2]; $STATS{$data[1]}{size} = $data[3]; $STATS{$data[1]}{nrcpt} = $data[4]; $STATS{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $STATS{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $STATS{$data[1]}{subject} .= $data[$i]; } my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_sender} = "$idx"; } close(IN); } #### # Get recipient statistics #### sub get_recipient_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour, $enable_queued) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/recipient.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); next if ($data[4] =~ /Queued/); push(@{$STATS{$data[1]}{rcpt}}, $data[2]); push(@{$STATS{$data[1]}{rcpt_relay}}, $data[3]); push(@{$STATS{$data[1]}{status}}, $data[4]); my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_rcpt} = "$idx"; } close(IN); if ($enable_queued) { open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); next if (exists $STATS{$data[1]}{rcpt} || ($data[4] !~ /Queued/)); push(@{$STATS{$data[1]}{rcpt}}, $data[2]); push(@{$STATS{$data[1]}{rcpt_relay}}, $data[3]); push(@{$STATS{$data[1]}{status}}, $data[4]); my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_rcpt} = "$idx"; } close(IN); } } #### # Get rejected statistics #### sub get_rejected_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/rejected.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:Rule:Relay:Arg1:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); $STATS{$data[1]}{rule} = $data[2]; if (!$STATS{$data[1]}{sender_relay}) { $STATS{$data[1]}{sender_relay} = $data[3]; } if ($#data > 4) { if ($data[2] eq 'check_relay') { $STATS{$data[1]}{sender_relay} = $data[4]; } elsif ($data[2] eq 'check_rcpt') { push(@{$STATS{$data[1]}{chck_rcpt}}, $data[4]); } elsif ($data[5] eq 'postscreen reject') { $data[5] .= " (score: $data[4])"; } else { # $data[2] eq 'check_mail' or POSTFIX $STATS{$data[1]}{sender} = $data[4]; } push(@{$STATS{$data[1]}{chck_status}}, $data[5]); } else { push(@{$STATS{$data[1]}{chck_status}}, $data[4]); } my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_reject} = "$idx"; } close(IN); } #### # Get DSN statistics #### sub get_dsn_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/dsn.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:SourceId:Status my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $STATS{$data[2]}{dsnstatus} = $data[3]; $STATS{$data[2]}{srcid} = $data[1]; my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[2]}{idx_dsn} = "$idx"; } close(IN); $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/senders.dat"; # Format: Hour:Id:Sender:Size:Nrcpts:Relay if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; next if (!exists $STATS{$data[1]}); $STATS{$data[1]}{hour} = $data[0]; $STATS{$data[1]}{sender} = $data[2]; $STATS{$data[1]}{size} = $data[3]; $STATS{$data[1]}{nrcpt} = $data[4]; $STATS{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $STATS{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $STATS{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); next if (!exists $STATS{$data[1]}); next if ($data[4] =~ /Queued/); push(@{$STATS{$data[1]}{rcpt}}, $data[2]); push(@{$STATS{$data[1]}{rcpt_relay}}, $data[3]); push(@{$STATS{$data[1]}{status}}, $data[4]); } close(IN); } } #### # Get DSN top statistics #### sub get_dsn_top_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/dsn.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:SourceId:Status my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $STATS{$data[2]}{dsnstatus} = $data[3]; $STATS{$data[2]}{srcid} = $data[1]; my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[2]}{idx_dsn} = "$idx"; } close(IN); $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/senders.dat"; # Format: Hour:Id:Sender:Size:Nrcpts:Relay if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); next if (!exists $STATS{$data[1]}); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; $STATS{$data[1]}{sender} = $data[2]; $STATS{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $STATS{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $STATS{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); next if (!exists $STATS{$data[1]}); $data[3] = &clean_relay($data[3]); next if ($data[4] eq 'Sent'); next if ($data[4] =~ /Queued/); push(@{$STATS{$data[1]}{rcpt}}, $data[2]); } close(IN); } } #### # Get Spam statistics #### sub get_spam_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $path = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day"; my $file = "$path/spam.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:From:To:Spam my @data = split(/:/, $l, 5); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $STATS{$data[1]}{hour} = $data[0] if (!exists $STATS{$data[1]}{hour}); $STATS{$data[1]}{sender} = $data[2] if (!exists $STATS{$data[1]}{sender}); foreach my $a (split(/,/, $data[3])) { if (!grep(/^$a$/i, @{$STATS{$data[1]}{rcpt}})) { push(@{$STATS{$data[1]}{rcpt}}, $a); } } $STATS{$data[1]}{spam} = $data[4]; my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_spam} = "$idx"; } close(IN); } #### # Get Virus statistics #### sub get_virus_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/virus.dat"; open(IN, $file) || return; my @done = (); while (my $l = ) { chomp($l); # Format: Hour:Id:file:virus my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $STATS{$data[1]}{hour} = $data[0] if (!exists $STATS{$data[1]}{hour}); $STATS{$data[1]}{file} = $data[2]; $STATS{$data[1]}{virus} = $data[3]; my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_virus} = "$idx"; } close(IN); } #### # Get Sys error statistics #### sub get_syserr_stat { my ($hostname, $year, $month, $day, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/syserr.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Message my @data = split(/:/, $l, 3); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); shift(@data); my $id = shift(@data); $STATS{$id}{error} = join(':', @data); } close(IN); } $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/other.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Message my @data = split(/:/, $l, 2); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); shift(@data); $STATS{$UNIQID}{error} = join(':', @data); $UNIQID++; } close(IN); $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/starttls.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:FAIL=count;NO=count;OK=count my @data = split(/:/, $l, 2); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); foreach my $p (split(/;/, $data[1])) { my ($k, $v) = split(/=/, $p); $STATS{'STARTTLS'}{$k} += $v; } } close(IN); } #### # Get Spam details #### sub get_spamdetail_stat { my ($type, $hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/$type.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:type:score:cache:autolearn:spam my @data = split(/:/, $l, 7); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $STATS{$data[1]}{hour} = $data[0]; $STATS{$data[1]}{type} = $data[2]; $STATS{$data[1]}{score} = $data[3]; $STATS{$data[1]}{cache} = $data[4]; $STATS{$data[1]}{autolearn} = $data[5]; $STATS{$data[1]}{spam} = $data[6]; my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_spamd} = "$idx"; } close(IN); $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/spam.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:From:To:Spam my @data = split(/:/, $l, 5); next if (!exists $STATS{$data[1]}); $STATS{$data[1]}{sender} = $data[2] if (!exists $STATS{$data[1]}{sender}); foreach my $a (split(/,/, $data[3])) { if (!grep(/^$a$/i, @{$STATS{$data[1]}{rcpt}})) { push(@{$STATS{$data[1]}{rcpt}}, $a); } } } close(IN); } #### # Get authent statistics #### sub get_auth_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/auth.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:Relay:Mech:Type my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); @data = split(/:/, $l); $data[2] = &clean_relay($data[2]); push(@{$AUTH{$data[1]}{relay}}, $data[2]); push(@{$AUTH{$data[1]}{mech}}, $data[3]); push(@{$AUTH{$data[1]}{type}}, $data[4]); my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } push(@{$AUTH{$data[1]}{idx}}, "$idx"); } close(IN); } #### # Get postgrey statistics #### sub get_postgrey_stat { my ($hostname, $year, $month, $day, $mon, $mday, $hour) = @_; my $file = "$CONFIG{OUT_DIR}/$hostname/$year/$month/$day/postgrey.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:Relay:From:To:Action:Reason my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); @data = split(/:/, $l); $data[2] = &clean_relay($data[2]); $STATS{$data[1]}{sender_relay} = $data[2] || ''; $STATS{$data[1]}{sender} = $data[3] || ''; push(@{$STATS{$data[1]}{rcpt}}, $data[4]) if ($data[4]); $STATS{$data[1]}{action} = $data[5] || ''; $STATS{$data[1]}{reason} = $data[6] || ''; my $idx = $month; if ($hour ne '') { $data[0] =~ /(\d{2})\d{2}$/; $idx = sprintf("%02d", $1); } elsif ($mday ne '00') { $data[0] =~ s/\d{4}$//; $idx = sprintf("%02d", $data[0]); } elsif ($mon ne '00') { $idx = $day; } $STATS{$data[1]}{idx_postgrey} = "$idx"; } close(IN); } sub set_direction { my ($sender_relay, $recipient_relay, $hostname) = @_; # By default all mail are considered issued from local computer. my $direction = 'Int_'; ###### Check for sender origine if ($sender_relay) { # This host is a gateway and it forward mails to an internal hub or outside if (!$CONFIG{MAIL_GW} && $CONFIG{MAIL_HUB}) { if (!grep($sender_relay =~ /$_/i, split(/[\s\t,;]/, $CONFIG{MAIL_HUB}))) { # if message doesn't come from localhost or user defined loca relay it comes from outside if (exists $CONFIG{LOCAL_HOST_DOMAIN}{$hostname} && ($#{$CONFIG{LOCAL_HOST_DOMAIN}{$hostname}} > -1) ) { $direction = 'Ext_' if (!grep($sender_relay =~ /\b$_$/, 'localhost', @{$CONFIG{LOCAL_HOST_DOMAIN}{$hostname}})); } elsif (exists $CONFIG{LOCAL_DOMAIN} && ($#{$CONFIG{LOCAL_DOMAIN}} > -1)) { $direction = 'Ext_' if (!grep($sender_relay =~ /\b$_$/, 'localhost', @{$CONFIG{LOCAL_DOMAIN}})); } } # This host received all messages from a gateway } elsif ($CONFIG{MAIL_GW} && !$CONFIG{MAIL_HUB}) { # If sender relay is the gateway, it comes from outside $direction = 'Ext_' if (grep($sender_relay =~ /$_/i, split(/[\s\t,;]/, $CONFIG{MAIL_GW}) )); # This host is a hub, it received all messages from a gateway and forward them to other host } elsif ($CONFIG{MAIL_GW} && $CONFIG{MAIL_HUB}) { # If sender relay is the gateway, it comes from outside $direction = 'Ext_' if (grep($sender_relay =~ /$_/i, split(/[\s\t,;]/, $CONFIG{MAIL_GW}))); } else { # if message doesn't come from localhost or user defined loca relay it comes from outside if (exists $CONFIG{LOCAL_HOST_DOMAIN}{$hostname} && ($#{$CONFIG{LOCAL_HOST_DOMAIN}{$hostname}} > -1) ) { $direction = 'Ext_' if (!grep($sender_relay =~ /\b$_$/, 'localhost', @{$CONFIG{LOCAL_HOST_DOMAIN}{$hostname}})); } elsif (exists $CONFIG{LOCAL_DOMAIN} && ($#{$CONFIG{LOCAL_DOMAIN}} > -1)) { $direction = 'Ext_' if (!grep($sender_relay =~ /\b$_$/, 'localhost', @{$CONFIG{LOCAL_DOMAIN}})); } } } else { $direction = 'Unk_'; } ###### Now check for destination if ($recipient_relay) { # If the recipient relay is localhost, it should be distributed internally if ($recipient_relay eq 'localhost') { $direction .= 'Int'; # If this host is a mail gateway and the recipient relay match one of # our destination hub lets say it should be distributed internally } elsif ($CONFIG{MAIL_HUB} && (grep($recipient_relay =~ /$_/, split(/[\s\t,;]/, $CONFIG{MAIL_HUB}) )) ) { $direction .= 'Int'; # If the recipient relay match any of our local domain # lets say the mail should be distributed internally } elsif (exists $CONFIG{LOCAL_HOST_DOMAIN}{$hostname} && ($#{$CONFIG{LOCAL_HOST_DOMAIN}{$hostname}} > -1) ){ if (grep($recipient_relay =~ /\b$_$/i, @{$CONFIG{LOCAL_HOST_DOMAIN}{$hostname}})) { $direction .= 'Int'; } else { $direction .= 'Ext'; } } elsif (exists $CONFIG{LOCAL_DOMAIN} && ($#{$CONFIG{LOCAL_DOMAIN}} > -1)) { if (grep($recipient_relay =~ /\b$_$/i, @{$CONFIG{LOCAL_DOMAIN}})) { $direction .= 'Int'; } else { $direction .= 'Ext'; } # Finally his only way is to go outside } else { $direction .= 'Ext'; } } else { $direction .= 'Unk'; } return $direction; } sub compute_messageflow { my ($hostname, $mailbox) = @_; my %period_stat = (); foreach my $id (keys %STATS) { if ( ($STATS{$id}{sender} ne 'DSN@localhost') && (exists $STATS{$id}{nrcpt}) ) { if (!$DOMAIN || ($STATS{$id}{sender} =~ /$DOMAIN/) || grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})) { if ($STATS{$id}{sender_relay} eq 'localhost') { $messaging{local_inbound}++; $messaging{local_inbound_bytes} += $STATS{$id}{size} || 0; } else { $messaging{inbound}++; $messaging{inbound_bytes} += $STATS{$id}{size} || 0; } $period_stat{flow}{$STATS{$id}{idx_sender}}{inbound}++; $period_stat{flow}{$STATS{$id}{idx_sender}}{inbound_bytes} += $STATS{$id}{size} || 0; } } for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && $STATS{$id}{rcpt}[$i] !~ /$DOMAIN/); $GLOBAL_STATUS{"$STATS{$id}{status}[$i]"}++; $GLOBAL_STATUS{"$STATS{$id}{status}[$i]" . '_bytes'} += $STATS{$id}{size}; if ($STATS{$id}{status}[$i] eq 'Sent') { $period_stat{flow}{$STATS{$id}{idx_rcpt}}{outbound}++; $period_stat{flow}{$STATS{$id}{idx_rcpt}}{outbound_bytes} += $STATS{$id}{size}; $messaging{nbsender}{"$STATS{$id}{sender}"} = ''; $messaging{nbrcpt}{"$STATS{$id}{rcpt}[$i]"} = ''; $delivery{'total'}++; my $direction = &set_direction($STATS{$id}{sender_relay}, $STATS{$id}{rcpt_relay}[$i], $hostname); $delivery{$direction}++; $direction .= '_bytes'; $delivery{$direction} += $STATS{$id}{size}; if ($direction =~ /_Int/) { $messaging{local_outbound}++; $messaging{local_outbound_bytes} += $STATS{$id}{size} || 0; } else { $messaging{outbound}++; $messaging{outbound_bytes} += $STATS{$id}{size} || 0; } } } } %STATS = (); return %period_stat; } sub summarize_messageflow { my ($begin, $end, %period_stat) = @_; $messaging{inbound} ||= 0; $messaging{local_inbound} ||= 0; $messaging{outbound} ||= 0; $messaging{local_outbound} ||= 0; $messaging{total_inbound} = $messaging{inbound} + $messaging{local_inbound}; $messaging{total_inbound_bytes} = $messaging{inbound_bytes} + $messaging{local_inbound_bytes}; $messaging{total_inbound_bytes} = sprintf("%.2f", $messaging{total_inbound_bytes}/$SIZE_UNIT); $messaging{inbound_bytes} = sprintf("%.2f", $messaging{inbound_bytes}/$SIZE_UNIT); $messaging{local_inbound_bytes} = sprintf("%.2f", $messaging{local_inbound_bytes}/$SIZE_UNIT); $messaging{total_outbound} = $messaging{outbound} + $messaging{local_outbound}; $messaging{total_outbound_bytes} = $messaging{outbound_bytes} + $messaging{local_outbound_bytes}; $messaging{total_outbound_bytes} = sprintf("%.2f", $messaging{total_outbound_bytes}/$SIZE_UNIT); $messaging{outbound_bytes} = sprintf("%.2f", $messaging{outbound_bytes}/$SIZE_UNIT); $messaging{local_outbound_bytes} = sprintf("%.2f", $messaging{local_outbound_bytes}/$SIZE_UNIT); if (!exists $messaging{lbls}) { if ($end && ($end ne '60')) { foreach my $b ("$begin" .. "$end") { $messaging{lbls} .= "$b:"; $messaging{values} .= ($period_stat{flow}{"$b"}{inbound} || 0) . ':'; $messaging{values1} .= ($period_stat{flow}{"$b"}{outbound} || 0) . ':'; $messaging{values_bytes} .= (sprintf("%.2f", $period_stat{flow}{"$b"}{inbound_bytes}/$SIZE_UNIT) || 0) . ':'; $messaging{values1_bytes} .= (sprintf("%.2f", $period_stat{flow}{"$b"}{outbound_bytes}/$SIZE_UNIT) || 0) . ':'; } } elsif ($end) { for (my $i = 5; $i <= 60; $i += 5) { $messaging{lbls} .= sprintf("%02d", $i) . ":"; my $count = 0; my $count_bytes = 0; my $count1 = 0; my $count1_bytes = 0; foreach my $b (keys %{$period_stat{flow}}) { if ( ($b < $i) && ($b >= ($i - 5)) ) { $count += $period_stat{flow}{"$b"}{inbound}; $count1 += $period_stat{flow}{"$b"}{outbound}; $count_bytes += $period_stat{flow}{"$b"}{inbound_bytes}; $count1_bytes += $period_stat{flow}{"$b"}{outbound_bytes}; } } $messaging{values} .= ($count || 0) . ':'; $messaging{values1} .= ($count1 || 0) . ':'; $messaging{values_bytes} .= (sprintf("%.2f", $count_bytes/$SIZE_UNIT) || 0) . ':'; $messaging{values1_bytes} .= (sprintf("%.2f", $count1_bytes/$SIZE_UNIT) || 0) . ':'; } } else { foreach my $b (split(/:/, $begin)) { $messaging{lbls} .= "$b:"; $messaging{values} .= ($period_stat{flow}{"$b"}{inbound} || 0) . ':'; $messaging{values1} .= ($period_stat{flow}{"$b"}{outbound} || 0) . ':'; $messaging{values_bytes} .= (sprintf("%.2f", $period_stat{flow}{"$b"}{inbound_bytes}/$SIZE_UNIT) || 0) . ':'; $messaging{values1_bytes} .= (sprintf("%.2f", $period_stat{flow}{"$b"}{outbound_bytes}/$SIZE_UNIT) || 0) . ':'; } } } else { my @values_bytes = split(/:/, $messaging{values_bytes}); my @values1_bytes = split(/:/, $messaging{values1_bytes}); $messaging{values_bytes} = ''; $messaging{values1_bytes} = ''; foreach (@values_bytes) { $messaging{values_bytes} .= (sprintf("%.2f", $_/$SIZE_UNIT) || 0) . ':'; } foreach (@values1_bytes) { $messaging{values1_bytes} .= (sprintf("%.2f", $_/$SIZE_UNIT) || 0) . ':'; } } $messaging{lbls} =~ s/:$//; $messaging{values} =~ s/:$//; $messaging{values1} =~ s/:$//; $messaging{values_bytes} =~ s/:$//; $messaging{values1_bytes} =~ s/:$//; $delivery{'Int_Int'} ||= 0; $delivery{'Int_Ext'} ||= 0; $delivery{'Ext_Ext'} ||= 0; $delivery{'Ext_Int'} ||= 0; $delivery{'Int_Unk'} ||= 0; $delivery{'Unk_Int'} ||= 0; $delivery{'Ext_Unk'} ||= 0; $delivery{'Unk_Ext'} ||= 0; $delivery{'Unk_Unk'} ||= 0; $delivery{'Int_Int_bytes'} = sprintf("%.2f", $delivery{'Int_Int_bytes'}/$SIZE_UNIT); $delivery{'Int_Ext_bytes'} = sprintf("%.2f", $delivery{'Int_Ext_bytes'}/$SIZE_UNIT); $delivery{'Ext_Ext_bytes'} = sprintf("%.2f", $delivery{'Ext_Ext_bytes'}/$SIZE_UNIT); $delivery{'Ext_Int_bytes'} = sprintf("%.2f", $delivery{'Ext_Int_bytes'}/$SIZE_UNIT); $delivery{'Unk_Int_bytes'} = sprintf("%.2f", $delivery{'Unk_Int_bytes'}/$SIZE_UNIT); $delivery{'Unk_Ext_bytes'} = sprintf("%.2f", $delivery{'Unk_Ext_bytes'}/$SIZE_UNIT); $delivery{'Int_Unk_bytes'} = sprintf("%.2f", $delivery{'Int_Unk_bytes'}/$SIZE_UNIT); $delivery{'Ext_Unk_bytes'} = sprintf("%.2f", $delivery{'Ext_Unk_bytes'}/$SIZE_UNIT); $delivery{'Unk_Unk_bytes'} = sprintf("%.2f", $delivery{'Unk_Unk_bytes'}/$SIZE_UNIT); } sub display_messageflow { my ($hostname, $date, $hour, $x_label) = @_; $messaging{inbound_mean} = sprintf("%.2f", $messaging{inbound_bytes} / ($messaging{inbound} || 1)); $messaging{local_inbound_mean} = sprintf("%.2f", $messaging{local_inbound_bytes} / ($messaging{local_inbound} || 1)); $messaging{total_inbound_mean} = sprintf("%.2f", $messaging{total_inbound_bytes} / ($messaging{total_inbound} || 1)); $messaging{outbound_mean} = sprintf("%.2f", $messaging{outbound_bytes} / ($messaging{outbound} || 1)); $messaging{local_outbound_mean} = sprintf("%.2f", $messaging{local_outbound_bytes} / ($messaging{local_outbound} || 1)); $messaging{total_outbound_mean} = sprintf("%.2f", $messaging{total_outbound_bytes} / ($messaging{total_outbound} || 1)); if (!$messaging{total_inbound} && !$messaging{total_outbound}) { print qq{
$TRANSLATE{'No dataset'}
}; return; } # Messaging flows print qq{
$TRANSLATE{'Messaging flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Incoming'}$messaging{inbound}$messaging{inbound_bytes}$messaging{inbound_mean}
$TRANSLATE{'Local incoming'}$messaging{local_inbound}$messaging{local_inbound_bytes}$messaging{local_inbound_mean}
$TRANSLATE{'Total incoming'}$messaging{total_inbound}$messaging{total_inbound_bytes}$messaging{total_inbound_mean}
$TRANSLATE{'Outgoing'}$messaging{outbound}$messaging{outbound_bytes}$messaging{outbound_mean}
$TRANSLATE{'Local delivery'}$messaging{local_outbound}$messaging{local_outbound_bytes}$messaging{local_outbound_mean}
$TRANSLATE{'Total outgoing'}$messaging{total_outbound}$messaging{total_outbound_bytes}$messaging{total_outbound_mean}
 
}; print &grafit( labels => $messaging{lbls}, title => $TRANSLATE{'Messaging Flow'}, values => $messaging{values}, legend => $TRANSLATE{'Inbound'}, values1 => $messaging{values1}, legend1 => $TRANSLATE{'Outbound'}, x_label => $x_label, y_label => $TRANSLATE{'Number of message'}, divid => 'messagingflow' ); print qq{
}; print &grafit( labels => $messaging{lbls}, title => $TRANSLATE{'Messaging Size Flow'}, values => $messaging{values_bytes}, legend => $TRANSLATE{'Inbound'}, values1 => $messaging{values1_bytes}, legend1 => $TRANSLATE{'Outbound'}, x_label => $x_label, y_label => "$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})", divid => 'messagingflowsize' ); print qq{
  }; # Message delivery flows my %data = (); $delivery{total} = $GLOBAL_STATUS{Sent} || 1; $delivery{total_bytes} = $GLOBAL_STATUS{Sent_bytes} || 1; $delivery{'Ext_Int_percent'} = sprintf("%.2f", ($delivery{'Ext_Int'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Ext -> Int'}} = $delivery{'Ext_Int_percent'}; $delivery{'Ext_Ext_percent'} = sprintf("%.2f", ($delivery{'Ext_Ext'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Ext -> Ext'}} = $delivery{'Ext_Ext_percent'}; $delivery{'Int_Int_percent'} = sprintf("%.2f", ($delivery{'Int_Int'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Int -> Int'}} = $delivery{'Int_Int_percent'}; $delivery{'Int_Ext_percent'} = sprintf("%.2f", ($delivery{'Int_Ext'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Int -> Ext'}} = $delivery{'Int_Ext_percent'}; $delivery{lbls} = "$TRANSLATE{'Ext -> Int'}:$TRANSLATE{'Ext -> Ext'}:$TRANSLATE{'Int -> Int'}:$TRANSLATE{'Int -> Ext'}"; if ($delivery{'Unk_Int'}) { $delivery{'Unk_Int_percent'} = sprintf("%.2f", ($delivery{'Unk_Int'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Unk -> Int'}} = $delivery{'Unk_Int_percent'}; $delivery{lbls} .= ":$TRANSLATE{'Unk -> Int'}"; } if ($delivery{'Unk_Ext'}) { $delivery{'Unk_Ext_percent'} = sprintf("%.2f", ($delivery{'Unk_Ext'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Unk -> Ext'}} = $delivery{'Unk_Ext_percent'}; $delivery{lbls} .= ":$TRANSLATE{'Unk -> Ext'}"; } if ($delivery{'Int_Unk'}) { $delivery{'Int_Unk_percent'} = sprintf("%.2f", ($delivery{'Int_Unk'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Int -> Unk'}} = $delivery{'Int_Unk_percent'}; $delivery{lbls} .= ":$TRANSLATE{'Int_Unk'}"; } if ($delivery{'Ext_Unk'}) { $delivery{'Ext_Unk_percent'} = sprintf("%.2f", ($delivery{'Ext_Unk'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Ext -> Unk'}} = $delivery{'Ext_Unk_percent'}; $delivery{lbls} .= ":$TRANSLATE{'Ext_Unk'}"; } if ($delivery{'Unk_Unk'}) { $delivery{'Unk_Unk_percent'} = sprintf("%.2f", ($delivery{'Unk_Unk'}*100) / $delivery{'total'}); $data{$TRANSLATE{'Unk -> Unk'}} = $delivery{'Unk_Unk_percent'}; $delivery{lbls} .= ":$TRANSLATE{'Unk_Unk'}"; } my $nbsender = 0; my $nbrcpt = 0; if (ref $messaging{nbsender} eq 'HASH') { $nbsender = scalar keys %{$messaging{nbsender}} || 0; $nbrcpt = scalar keys %{$messaging{nbrcpt}} || 0; delete $messaging{nbsender}; delete $messaging{nbrcpt}; } elsif ($messaging{nbsender} =~ /:/) { foreach (split(/:/, $messaging{nbsender})) { $nbsender += $_; } foreach (split(/:/, $messaging{nbrcpt})) { $nbrcpt += $_; } } else { $nbsender = $messaging{nbsender} || 0; $nbrcpt = $messaging{nbrcpt} || 0; } if ($CONFIG{SHOW_DIRECTION}) { print qq { }; print qq { } if ($delivery{'Unk_Int'}); print qq { } if ($delivery{'Unk_Ext'}); print qq { } if ($delivery{'Int_Unk'}); print qq { } if ($delivery{'Int_Unk'}); print qq { } if ($delivery{'Unk_Unk'}); print qq {
$TRANSLATE{'Message delivery flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Percentage'}
$TRANSLATE{'Internet -> Internal'}$delivery{'Ext_Int'}$delivery{'Ext_Int_bytes'}$delivery{'Ext_Int_percent'} %
$TRANSLATE{'Internet -> Internet'}$delivery{'Ext_Ext'}$delivery{'Ext_Ext_bytes'}$delivery{'Ext_Ext_percent'} %
$TRANSLATE{'Internal -> Internal'}$delivery{'Int_Int'}$delivery{'Int_Int_bytes'}$delivery{'Int_Int_percent'} %
$TRANSLATE{'Internal -> Internet'}$delivery{'Int_Ext'}$delivery{'Int_Ext_bytes'}$delivery{'Int_Ext_percent'} %
$TRANSLATE{'Unknown -> Internal'}$delivery{'Unk_Int'}$delivery{'Unk_Int_bytes'}$delivery{'Unk_Int_percent'} %
$TRANSLATE{'Unknown -> Internet'}$delivery{'Unk_Ext'}$delivery{'Unk_Ext_bytes'}$delivery{'Unk_Ext_percent'} %
$TRANSLATE{'Internal -> Unknown'}$delivery{'Int_Unk'}$delivery{'Int_Unk_bytes'}$delivery{'Int_Unk_percent'} %
$TRANSLATE{'Internet -> Unknown'}$delivery{'Ext_Unk'}$delivery{'Ext_Unk_bytes'}$delivery{'Ext_Unk_percent'} %
$TRANSLATE{'Unknown -> Unknown'}$delivery{'Unk_Unk'}$delivery{'Unk_Unk_bytes'}$delivery{'Unk_Unk_percent'} %
}; print &grafit_pie( title => $TRANSLATE{'Delivery Direction'}, values => \%data, x_label => $TRANSLATE{'Direction'}, y_label => $TRANSLATE{'Percentage of message'}, divid => 'messagedeliveryflow' ); print qq {
}; } else { print "\n"; } print qq{
$TRANSLATE{'Different senders/recipients'}
$TRANSLATE{'Senders'}$TRANSLATE{'Recipients'}
$nbsender$nbrcpt
 
}; if ($messaging{nbsender} =~ /:/) { print qq{
}; print &grafit(labels => $messaging{lbls}, values => $messaging{nbsender}, values1 => $messaging{nbrcpt}, legend => $TRANSLATE{'Senders'}, legend1 => $TRANSLATE{'Recipients'}, title => $TRANSLATE{'Different senders/recipients'}, x_label => $x_label, divid => 'messagesenderrecipientflow' ); print qq {
}; } print "\n
\n"; } sub compute_spamflow { my ($hostname, $mailbox) = @_; my %period_stat = (); foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{spam}) { $period_stat{spam}{$STATS{$id}{idx_spam}}++; $GLOBAL_STATUS{Spam}++; $GLOBAL_STATUS{Spam_bytes} += $STATS{$id}{size}; if ($STATS{$id}{sender_relay} ne 'localhost') { $spam{local_inbound}++; $spam{local_inbound_bytes} += $STATS{$id}{size} || 0; } else { $spam{inbound}++; $spam{inbound_bytes} += $STATS{$id}{size} || 0; } for (my $i = 0; $i <= $#{$STATS{$id}{status}}; $i++) { next if ($mailbox && ($STATS{$id}{rcpt}[$i] !~ /$mailbox\@/) ); if ($STATS{$id}{status}[$i] eq 'Sent') { my $direction = &set_direction($STATS{$id}{sender_relay}, $STATS{$id}{rcpt_relay}[$i], $hostname); $spam{$direction}++; $direction .= '_bytes'; $spam{$direction} += $STATS{$id}{size}; if ($direction =~ /_Int/) { $spam{local_outbound}++; $spam{local_outbound_bytes} += $STATS{$id}{size} || 0; } else { $spam{outbound}++; $spam{outbound_bytes} += $STATS{$id}{size} || 0; } } } } } %STATS = (); return %period_stat; } sub summarize_spamflow { my ($begin, $end, %period_stat) = @_; if (!exists $spam{lbls}) { if ($end && ($end ne '60')) { foreach ("$begin" .. "$end") { $spam{lbls} .= "$_:"; $spam{values} .= ($period_stat{spam}{"$_"} || 0) . ':'; } } elsif ($end) { for (my $i = 5; $i <= 60; $i += 5) { $spam{lbls} .= sprintf("%02d", $i) . ":"; my $count = 0; foreach my $b (keys %{$period_stat{spam}}) { if ( ($b < $i) && ($b >= ($i - 5)) ) { $count += $period_stat{spam}{"$b"}; } } $spam{values} .= ($count || 0) . ':'; } } else { foreach my $b (split(/:/, $begin)) { $spam{lbls} .= "$b:"; $spam{values} .= ($period_stat{spam}{"$b"} || 0) . ':'; } } } $spam{lbls} =~ s/:$//; $spam{values} =~ s/:$//; $spam{inbound} ||= 0; $spam{local_inbound} ||= 0; $spam{outbound} ||= 0; $spam{local_outbound} ||= 0; $spam{total_inbound} = $spam{inbound} + $spam{local_inbound}; $spam{total_inbound_bytes} = $spam{inbound_bytes} + $spam{local_inbound_bytes}; $spam{total_outbound} = $spam{outbound} + $spam{local_outbound}; $spam{total_outbound_bytes} = $spam{outbound_bytes} + $spam{local_outbound_bytes}; $spam{total_inbound_bytes} = sprintf("%.2f", $spam{total_inbound_bytes}/$SIZE_UNIT); $spam{inbound_bytes} = sprintf("%.2f", $spam{inbound_bytes}/$SIZE_UNIT); $spam{local_inbound_bytes} = sprintf("%.2f", $spam{local_inbound_bytes}/$SIZE_UNIT); $spam{total_outbound_bytes} = sprintf("%.2f", $spam{total_outbound_bytes}/$SIZE_UNIT); $spam{outbound_bytes} = sprintf("%.2f", $spam{outbound_bytes}/$SIZE_UNIT); $spam{local_outbound_bytes} = sprintf("%.2f", $spam{local_outbound_bytes}/$SIZE_UNIT); $spam{'Int_Int'} ||= 0; $spam{'Int_Ext'} ||= 0; $spam{'Ext_Ext'} ||= 0; $spam{'Ext_Int'} ||= 0; } sub display_spamflow { my ($x_label) = @_; # Spamming flows + Spam delivery flows $spam{inbound_mean} = sprintf("%.2f", $spam{inbound_bytes} / ($spam{inbound} || 1)); $spam{local_inbound_mean} = sprintf("%.2f", $spam{local_inbound_bytes} / ($spam{local_inbound} || 1)); $spam{total_inbound_mean} = sprintf("%.2f", $spam{total_inbound_bytes} / ($spam{total_inbound} || 1)); $spam{outbound_mean} = sprintf("%.2f", $spam{outbound_bytes} / ($spam{outbound} || 1)); $spam{local_outbound_mean} = sprintf("%.2f", $spam{local_outbound_bytes} / ($spam{local_outbound} || 1)); $spam{total_outbound_mean} = sprintf("%.2f", $spam{total_outbound_bytes} / ($spam{total_outbound} || 1)); $spam{Ext_Int_mean} = sprintf("%.2f", $spam{Ext_Int} / ($spam{Ext_Int_bytes} || 1)); $spam{Int_Int_mean} = sprintf("%.2f", $spam{Int_Int} / ($spam{Int_Int_bytes} || 1)); $spam{Int_Ext_mean} = sprintf("%.2f", $spam{Int_Ext} / ($spam{Int_Ext_bytes} || 1)); $spam{Ext_Ext_mean} = sprintf("%.2f", $spam{Ext_Ext} / ($spam{Ext_Ext_bytes} || 1)); $spam{'Int_Int_bytes'} = sprintf("%.2f", $spam{'Int_Int_bytes'}/$SIZE_UNIT); $spam{'Int_Ext_bytes'} = sprintf("%.2f", $spam{'Int_Ext_bytes'}/$SIZE_UNIT); $spam{'Ext_Ext_bytes'} = sprintf("%.2f", $spam{'Ext_Ext_bytes'}/$SIZE_UNIT); $spam{'Ext_Int_bytes'} = sprintf("%.2f", $spam{'Ext_Int_bytes'}/$SIZE_UNIT); if (!$spam{total_inbound} && !$spam{total_outbound}) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq {
$TRANSLATE{'Spamming flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Incoming'}$spam{inbound}$spam{inbound_bytes}$spam{inbound_mean}
$TRANSLATE{'Local incoming'}$spam{local_inbound}$spam{local_inbound_bytes}$spam{local_inbound_mean}
$TRANSLATE{'Total incoming'}$spam{total_inbound}$spam{total_inbound_bytes}$spam{total_inbound_mean}
$TRANSLATE{'Outgoing'}$spam{outbound}$spam{outbound_bytes}$spam{outbound_mean}
$TRANSLATE{'Local delivery'}$spam{local_outbound}$spam{local_outbound_bytes}$spam{local_outbound_mean}
$TRANSLATE{'Total outgoing'}$spam{total_outbound}$spam{total_outbound_bytes}$spam{total_outbound_mean}
 
}; if ($CONFIG{SHOW_DIRECTION}) { print qq{
$TRANSLATE{'Spam delivery flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Internet -> Internal'}$spam{'Ext_Int'}$spam{'Ext_Int_bytes'}$spam{Ext_Int_mean}
$TRANSLATE{'Internet -> Internet'}$spam{'Ext_Ext'}$spam{'Ext_Ext_bytes'}$spam{Ext_Ext_mean}
$TRANSLATE{'Internal -> Internal'}$spam{'Int_Int'}$spam{'Int_Int_bytes'}$spam{Int_Int_mean}
$TRANSLATE{'Internal -> Internet'}$spam{'Int_Ext'}$spam{'Int_Ext_bytes'}$spam{Int_Ext_mean}
 
}; print &grafit( labels => $spam{lbls}, values => $spam{values}, title => $TRANSLATE{'Spamming Flow'}, x_label => $x_label, y_label => $TRANSLATE{'Number of spam'}, divid => 'spamflow' ); print qq{
}; } print "\n"; } sub compute_virusflow { my ($hostname, $mailbox) = @_; my %period_stat = (); foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{virus}) { $period_stat{virus}{$STATS{$id}{idx_virus}}++; $GLOBAL_STATUS{Virus}++; $GLOBAL_STATUS{Virus_bytes} += $STATS{$id}{size}; if ($STATS{$id}{sender_relay} ne 'localhost') { $virus{local_inbound}++; $virus{local_inbound_bytes} += $STATS{$id}{size} || 0; } else { $virus{inbound}++; $virus{inbound_bytes} += $STATS{$id}{size} || 0; } for (my $i = 0; $i <= $#{$STATS{$id}{status}}; $i++) { next if ($mailbox && ($STATS{$id}{rcpt}[$i] !~ /$mailbox\@/) ); if ($STATS{$id}{status}[$i] eq 'Sent') { my $direction = &set_direction($STATS{$id}{sender_relay}, $STATS{$id}{rcpt_relay}[$i], $hostname); $virus{$direction}++; $direction .= '_bytes'; $virus{$direction} += $STATS{$id}{size}; if ($direction =~ /_Int/) { $virus{local_outbound}++; $virus{local_outbound_bytes} += $STATS{$id}{size} || 0; } else { $virus{outbound}++; $virus{outbound_bytes} += $STATS{$id}{size} || 0; } } } } } %STATS = (); return %period_stat; } sub summarize_virusflow { my ($begin, $end, %period_stat) = @_; $virus{inbound} ||= 0; $virus{local_inbound} ||= 0; $virus{outbound} ||= 0; $virus{local_outbound} ||= 0; $virus{total_inbound} = $virus{inbound} + $virus{local_inbound}; $virus{total_inbound_bytes} = $virus{inbound_bytes} + $virus{local_inbound_bytes}; $virus{total_outbound} = $virus{outbound} + $virus{local_outbound}; $virus{total_outbound_bytes} = $virus{outbound_bytes} + $virus{local_outbound_bytes}; $virus{total_inbound_bytes} = sprintf("%.2f", $virus{total_inbound_bytes}/$SIZE_UNIT); $virus{inbound_bytes} = sprintf("%.2f", $virus{inbound_bytes}/$SIZE_UNIT); $virus{local_inbound_bytes} = sprintf("%.2f", $virus{local_inbound_bytes}/$SIZE_UNIT); $virus{total_outbound_bytes} = sprintf("%.2f", $virus{total_outbound_bytes}/$SIZE_UNIT); $virus{outbound_bytes} = sprintf("%.2f", $virus{outbound_bytes}/$SIZE_UNIT); $virus{local_outbound_bytes} = sprintf("%.2f", $virus{local_outbound_bytes}/$SIZE_UNIT); if (!exists $virus{lbls}) { if ($end && ($end ne '60')) { foreach ("$begin" .. "$end") { $virus{lbls} .= "$_:"; $virus{values} .= ($period_stat{virus}{"$_"} || 0) . ':'; } } elsif ($end) { for (my $i = 5; $i <= 60; $i += 5) { $virus{lbls} .= sprintf("%02d", $i) . ":"; my $count = 0; foreach my $b (keys %{$period_stat{virus}}) { if ( ($b < $i) && ($b >= ($i - 5)) ) { $count += $period_stat{virus}{"$b"}; } } $virus{values} .= ($count || 0) . ':'; } } else { foreach my $b (split(/:/, $begin)) { $virus{lbls} .= "$b:"; $virus{values} .= ($period_stat{virus}{"$b"} || 0) . ':'; } } } $virus{lbls} =~ s/:$//; $virus{values} =~ s/:$//; $virus{'Int_Int'} ||= 0; $virus{'Int_Ext'} ||= 0; $virus{'Ext_Ext'} ||= 0; $virus{'Ext_Int'} ||= 0; } sub display_virusflow { my ($x_label) = @_; # Viruses flows / Viruses delivery flows / syserr flows $virus{inbound_mean} = sprintf("%.2f", $virus{inbound_bytes} / ($virus{inbound} || 1)); $virus{local_inbound_mean} = sprintf("%.2f", $virus{local_inbound_bytes} / ($virus{local_inbound} || 1)); $virus{total_inbound_mean} = sprintf("%.2f", $virus{total_inbound_bytes} / ($virus{total_inbound} || 1)); $virus{outbound_mean} = sprintf("%.2f", $virus{outbound_bytes} / ($virus{outbound} || 1)); $virus{local_outbound_mean} = sprintf("%.2f", $virus{local_outbound_bytes} / ($virus{local_outbound} || 1)); $virus{total_outbound_mean} = sprintf("%.2f", $virus{total_outbound_bytes} / ($virus{total_outbound} || 1)); $virus{Ext_Int_mean} = sprintf("%.2f", $virus{Ext_Int} / ($virus{Ext_Int_bytes} || 1)); $virus{Int_Int_mean} = sprintf("%.2f", $virus{Int_Int} / ($virus{Int_Int_bytes} || 1)); $virus{Int_Ext_mean} = sprintf("%.2f", $virus{Int_Ext} / ($virus{Int_Ext_bytes} || 1)); $virus{Ext_Ext_mean} = sprintf("%.2f", $virus{Ext_Ext} / ($virus{Ext_Ext_bytes} || 1)); $virus{'Int_Int_bytes'} = sprintf("%.2f", $virus{'Int_Int_bytes'}/$SIZE_UNIT); $virus{'Int_Ext_bytes'} = sprintf("%.2f", $virus{'Int_Ext_bytes'}/$SIZE_UNIT); $virus{'Ext_Ext_bytes'} = sprintf("%.2f", $virus{'Ext_Ext_bytes'}/$SIZE_UNIT); $virus{'Ext_Int_bytes'} = sprintf("%.2f", $virus{'Ext_Int_bytes'}/$SIZE_UNIT); if (!$virus{total_inbound} && !$virus{total_outbound}) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq {
$TRANSLATE{'Viruses flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Incoming'}$virus{inbound}$virus{inbound_bytes}$virus{inbound_mean}
$TRANSLATE{'Local incoming'}$virus{local_inbound}$virus{local_inbound_bytes}$virus{local_inbound_mean}
$TRANSLATE{'Total incoming'}$virus{total_inbound}$virus{total_inbound_bytes}$virus{total_inbound_mean}
$TRANSLATE{'Outgoing'}$virus{outbound}$virus{outbound_bytes}$virus{outbound_mean}
$TRANSLATE{'Local delivery'}$virus{local_outbound}$virus{local_outbound_bytes}$virus{local_outbound_mean}
$TRANSLATE{'Total outgoing'}$virus{total_outbound}$virus{total_outbound_bytes}$virus{total_outbound_mean}
 
}; if ($CONFIG{SHOW_DIRECTION}) { print qq{
$TRANSLATE{'Viruses delivery flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Internet -> Internal'}$virus{'Ext_Int'}$virus{'Ext_Int_bytes'}$virus{Ext_Int_mean}
$TRANSLATE{'Internet -> Internet'}$virus{'Ext_Ext'}$virus{'Ext_Ext_bytes'}$virus{Ext_Ext_mean}
$TRANSLATE{'Internal -> Internal'}$virus{'Int_Int'}$virus{'Int_Int_bytes'}$virus{Int_Int_mean}
$TRANSLATE{'Internal -> Internet'}$virus{'Int_Ext'}$virus{'Int_Ext_bytes'}$virus{Int_Ext_mean}
 
}; } print qq{
}; print &grafit( labels => $virus{lbls}, values => $virus{values}, title => $TRANSLATE{'Viruses Flow'}, x_label => $x_label, y_label => $TRANSLATE{'Number of virus'}, divid => 'virusflow' ); print qq{
}; } sub compute_dsnflow { my ($hostname, $mailbox) = @_; my %period_stat = (); foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$STATS{$id}{srcid}}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{dsnstatus}) { $period_stat{dsn}{$STATS{$id}{idx_dsn}}++; for (my $i = 0; $i <= $#{$STATS{$id}{status}}; $i++) { next if ($mailbox && ($STATS{$id}{rcpt}[$i] !~ /$mailbox\@/) ); if ($STATS{$id}{status}[$i] eq 'Sent') { my $direction = &set_direction($STATS{$id}{sender_relay}, $STATS{$id}{rcpt_relay}[$i], $hostname); $dsn{$direction}++; if ($direction =~ /_Int$/) { $dsn{local_outbound}++; } else { $dsn{outbound}++; } } else { $dsn{error}++; } } } } %STATS = (); return %period_stat; } sub summarize_dsnflow { my ($begin, $end, %period_stat) = @_; $dsn{outbound} ||= 0; $dsn{local_outbound} ||= 0; $dsn{total_outbound} = $dsn{outbound} + $dsn{local_outbound}; $dsn{error} ||= 0; if (!exists $dsn{lbls}) { if ($end && ($end ne '60')) { foreach ("$begin" .. "$end") { $dsn{lbls} .= "$_:"; $dsn{values} .= ($period_stat{dsn}{"$_"} || 0) . ':'; } } elsif ($end) { for (my $i = 5; $i <= 60; $i += 5) { $dsn{lbls} .= sprintf("%02d", $i) . ":"; my $count = 0; foreach my $b (keys %{$period_stat{dsn}}) { if ( ($b < $i) && ($b >= ($i - 5)) ) { $count += $period_stat{dsn}{"$b"}; } } $dsn{values} .= ($count || 0) . ':'; } } else { foreach my $b (split(/:/, $begin)) { $dsn{lbls} .= "$b:"; $dsn{values} .= ($period_stat{dsn}{"$b"} || 0) . ':'; } } } $dsn{lbls} =~ s/:$//; $dsn{values} =~ s/:$//; $dsn{'Int_Int'} ||= 0; $dsn{'Int_Ext'} ||= 0; } sub display_dsnflow { my ($x_label) = @_; # DSN flow my $total_dsn = $dsn{total_outbound} + $dsn{error}; if (!$total_dsn) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq {
$TRANSLATE{'DSN flows'}
 $TRANSLATE{'Messages'}
$TRANSLATE{'Outgoing'}$dsn{total_outbound}
$TRANSLATE{'In Error'}$dsn{error}
$TRANSLATE{'Total'}$total_dsn
 
}; if ($CONFIG{SHOW_DIRECTION}) { print qq{
$TRANSLATE{'DSN delivery flows'}
 $TRANSLATE{'Messages'}
$TRANSLATE{'Internal -> Internal'}$dsn{'Int_Int'}
$TRANSLATE{'Internal -> Internet'}$dsn{'Int_Ext'}
 
}; } print qq{
}; print &grafit( labels => $dsn{lbls}, values => $dsn{values}, title => $TRANSLATE{'DSN Flow'}, x_label => $x_label, y_label => $TRANSLATE{'Number of dsn'}, divid => 'dsnflow' ); print qq{
}; } sub compute_rejectflow { my ($mailbox) = @_; my %period_stat = (); foreach my $id (keys %STATS) { next if ($id eq 'STARTTLS'); next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{rule}) { next if ($id =~ /r\d{17}/); $period_stat{reject}{$STATS{$id}{idx_reject}}++; $GLOBAL_STATUS{Rejected}++; $GLOBAL_STATUS{Rejected_bytes} += $STATS{$id}{size}; if ($STATS{$id}{sender_relay} ne 'localhost') { $reject{local_inbound}++; $reject{local_inbound_bytes} += $STATS{$id}{size}; } elsif ($CONFIG{MAIL_HUB} && $STATS{$id}{sender_relay} =~ /$CONFIG{MAIL_HUB}/) { $reject{local_inbound}++; $reject{local_inbound_bytes} += $STATS{$id}{size}; } else { $reject{inbound}++; $reject{inbound_bytes} += ($STATS{$id}{size} || 0); } } if (exists $STATS{$id}{error}) { $GLOBAL_STATUS{SysErr}++; $GLOBAL_STATUS{SysErr_bytes} += $STATS{$id}{size}; if ($STATS{$id}{sender_relay} ne 'localhost') { $err{local_inbound}++; $err{local_inbound_bytes} += $STATS{$id}{size}; } elsif ($CONFIG{MAIL_HUB} && $STATS{$id}{sender_relay} =~ /$CONFIG{MAIL_HUB}/) { $err{local_inbound}++; $err{local_inbound_bytes} += $STATS{$id}{size}; } else { $err{inbound}++; $err{inbound_bytes} += $STATS{$id}{size}; } } } %STATS = (); return %period_stat; } sub summarize_rejectflow { my ($begin, $end, %period_stat) = @_; $reject{inbound} ||= 0; $reject{local_inbound} ||= 0; $reject{total_inbound} = $reject{inbound} + $reject{local_inbound}; $reject{total_inbound_bytes} = $reject{inbound_bytes} + $reject{local_inbound_bytes}; $err{inbound} ||= 0; $err{local_inbound} ||= 0; $err{total_inbound} = $err{inbound} + $err{local_inbound}; $err{total_inbound} = $err{inbound} + $err{local_inbound}; $err{total_inbound_bytes} = $err{inbound_bytes} + $err{local_inbound_bytes}; $reject{total_inbound_bytes} = sprintf("%.2f", $reject{total_inbound_bytes}/$SIZE_UNIT); $reject{inbound_bytes} = sprintf("%.2f", $reject{inbound_bytes}/$SIZE_UNIT); $reject{local_inbound_bytes} = sprintf("%.2f", $reject{local_inbound_bytes}/$SIZE_UNIT); $err{total_inbound_bytes} = sprintf("%.2f", $err{total_inbound_bytes}/$SIZE_UNIT); $err{inbound_bytes} = sprintf("%.2f", $err{inbound_bytes}/$SIZE_UNIT); $err{local_inbound_bytes} = sprintf("%.2f", $err{local_inbound_bytes}/$SIZE_UNIT); } sub display_rejectflow { my ($x_label) = @_; $reject{inbound_mean} = sprintf("%.2f", $reject{inbound_bytes} / ($reject{inbound} || 1)); $reject{local_inbound_mean} = sprintf("%.2f", $reject{local_inbound_bytes} / ($reject{local_inbound} || 1)); $reject{total_inbound_mean} = sprintf("%.2f", $reject{total_inbound_bytes} / ($reject{total_inbound} || 1)); if (!$reject{total_inbound} && !$err{total_inbound}) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq {
$TRANSLATE{'Rejection flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Incoming'}$reject{inbound}$reject{inbound_bytes}$reject{inbound_mean}
$TRANSLATE{'Local incoming'}$reject{local_inbound}$reject{local_inbound_bytes}$reject{local_inbound_mean}
$TRANSLATE{'Total incoming'}$reject{total_inbound}$reject{total_inbound_bytes}$reject{total_inbound_mean}
 
}; $err{inbound_mean} = sprintf("%.2f", $err{inbound_bytes} / ($err{inbound} || 1)); $err{local_inbound_mean} = sprintf("%.2f", $err{local_inbound_bytes} / ($err{local_inbound} || 1)); $err{total_inbound_mean} = sprintf("%.2f", $err{total_inbound_bytes} / ($err{total_inbound} || 1)); print qq {
$TRANSLATE{'Syserr flows'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Mean'}
$TRANSLATE{'Incoming'}$err{inbound}$err{inbound_bytes}$err{inbound_mean}
$TRANSLATE{'Local incoming'}$err{local_inbound}$err{local_inbound_bytes}$err{local_inbound_mean}
$TRANSLATE{'Total incoming'}$err{total_inbound}$err{total_inbound_bytes}$err{total_inbound_mean}
 
}; } sub compute_statusflow { my ($mailbox) = @_; foreach my $id (keys %STATS) { next if ($id eq 'STARTTLS'); next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); for (my $i = 0; $i <= $#{$STATS{$id}{status}}; $i++) { next if (!$STATS{$id}{rcpt}[$i]); next if ($mailbox && ($STATS{$id}{rcpt}[$i] !~ /$mailbox\@/) ); $GLOBAL_STATUS{"$STATS{$id}{status}[$i]"}++; $GLOBAL_STATUS{"$STATS{$id}{status}[$i]" . '_bytes'} += $STATS{$id}{size}; if ($STATS{$id}{status}[$i] eq 'Sent') { if (exists $STATS{$id}{spam}) { $GLOBAL_STATUS{Spam_Sent}++; } elsif (exists $STATS{$id}{virus}) { $GLOBAL_STATUS{Virus_Sent}++; } } } if (exists $STATS{$id}{spam}) { $GLOBAL_STATUS{Spam}++; $GLOBAL_STATUS{Spam_bytes} += $STATS{$id}{size}; } if (exists $STATS{$id}{virus}) { $GLOBAL_STATUS{Virus}++; $GLOBAL_STATUS{Virus_bytes} += $STATS{$id}{size}; } if (exists $STATS{$id}{rule}) { next if ($id =~ /r\d{17}/); $GLOBAL_STATUS{Rejected}++; $GLOBAL_STATUS{Rejected_bytes} += $STATS{$id}{size}; } if (exists $STATS{$id}{error}) { $GLOBAL_STATUS{SysErr}++; $GLOBAL_STATUS{SysErr_bytes} += $STATS{$id}{size}; } } foreach my $v (keys %{$STATS{'STARTTLS'}}) { $starttls{$v} += $STATS{'STARTTLS'}{$v}; } } sub display_statusflow { my ($x_label) = @_; delete $GLOBAL_STATUS{'Queued'}; delete $GLOBAL_STATUS{'Queued_bytes'}; if (scalar keys %GLOBAL_STATUS == 0) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq{
}; my $delivery_global_total = 0; foreach my $s (sort {$GLOBAL_STATUS{$b} <=> $GLOBAL_STATUS{$a}} keys %GLOBAL_STATUS) { next if ( ($s eq '') || ($s =~ /Command rejected/)); next if ( ($s =~ /(_bytes|Virus|Spam)/i) || ($s eq 'STARTTLS')); $delivery_global_total += $GLOBAL_STATUS{$s}; } $delivery_global_total ||= 1; my $delivery_total = $GLOBAL_STATUS{Sent} || 1; my $delivery_total_bytes = $GLOBAL_STATUS{Sent_bytes} || 1; my $total_percent = 0; my %status = (); my $piecount = 0; foreach my $s (sort {$GLOBAL_STATUS{$b} <=> $GLOBAL_STATUS{$a}} keys %GLOBAL_STATUS) { next if ( ($s eq '') || ($s =~ /Command rejected/)); next if ( ($s =~ /(_bytes|Virus|Spam)/i) || ($s eq 'STARTTLS')); my $percent = sprintf("%.2f", ($GLOBAL_STATUS{$s}/$delivery_global_total) * 100); if ($s =~ /^(\d{3}) \d\.(\d\.\d)$/) { if (exists $SMTP_ERROR_CODE{$1} || exists $ESMTP_ERROR_CODE{$2}) { print ""; } else { print ""; } } else { print ""; } print "\n"; if ( ($piecount < $MAXPIECOUNT) && ($percent > $MIN_SHOW_PIE)) { $status{"$s"} = $percent; $total_percent += $GLOBAL_STATUS{$s}; $piecount++; } } my $other_percent = 100 - sprintf("%.2f", ($total_percent/$delivery_global_total) * 100); $status{"Others"} = $other_percent if ($other_percent > 0); print qq{
$TRANSLATE{'Messaging Status'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Size'} ($TRANSLATE{$CONFIG{'SIZE_UNIT'}})$TRANSLATE{'Percentage'}
$s ",$SMTP_ERROR_CODE{$1} . " " . $ESMTP_ERROR_CODE{$2} , "
$s
$s$GLOBAL_STATUS{$s}", sprintf("%.2f", $GLOBAL_STATUS{$s . '_bytes'}/$SIZE_UNIT), "$percent %
 
}; print &grafit_pie( labels => $status{lbls}, values => \%status, title => $TRANSLATE{'Messaging status'}, divid => 'messagingstatus' ); print qq{
}; my $starttls_total = 0; foreach my $v (keys %starttls) { $starttls_total += $starttls{$v}; } if ($starttls_total > 0) { print qq{
}; $total_percent = 0; my %localstarttls = (); $piecount = 0; foreach my $s (sort keys %starttls) { my $percent = sprintf("%.2f", ($starttls{$s}/$starttls_total) * 100); if ( ($piecount < $MAXPIECOUNT) && ($percent > $MIN_SHOW_PIE)) { $localstarttls{"$s"} = $percent; $total_percent += $starttls{$s}; $piecount++; } } print &grafit_pie( labels => $localstarttls{lbls}, values => \%localstarttls, title => $TRANSLATE{'STARTTLS status'}, divid => 'starttlsstatus' ); } print qq{
}; } sub compute_authflow { my ($mailbox) = @_; return if ($DOMAIN); my %period_stat = (); foreach my $id (keys %AUTH) { for (my $i = 0; $i <= $#{$AUTH{$id}{mech}}; $i++) { $period_stat{auth}{$AUTH{$id}{type}[$i]}{$AUTH{$id}{idx}[$i]}++; $auth{$AUTH{$id}{type}[$i]}{$AUTH{$id}{mech}[$i]}++; } } %AUTH = (); return %period_stat; } sub summarize_authflow { my ($begin, $end, %period_stat) = @_; foreach my $type (keys %{$period_stat{auth}}) { if (!exists $auth{$type}{lbls}) { if ($end && ($end ne '60')) { foreach ("$begin" .. "$end") { $auth{$type}{lbls} .= "$_:"; $auth{$type}{values} .= ($period_stat{auth}{$type}{"$_"} || 0) . ':'; } } elsif ($end) { for (my $i = 5; $i <= 60; $i += 5) { $auth{$type}{lbls} .= sprintf("%02d", $i) . ":"; my $count = 0; foreach my $b (keys %{$period_stat{auth}{$type}}) { if ( ($b < $i) && ($b >= ($i - 5)) ) { $count += $period_stat{auth}{$type}{"$b"}; } } $auth{$type}{values} .= ($count || 0) . ':'; } } else { foreach my $b (split(/:/, $begin)) { $auth{$type}{lbls} .= "$b:"; $auth{$type}{values} .= ($period_stat{auth}{$type}{"$b"} || 0) . ':'; } } } $auth{$type}{lbls} =~ s/:$//; $auth{$type}{values} =~ s/:$//; } } sub display_authflow { my ($x_label) = @_; if (scalar keys %auth) { print qq{ }; my $i = 1; foreach my $type (sort keys %auth) { print qq{
}; my $total = 0; foreach my $mech (sort keys %{$auth{$type}}) { next if ($mech =~ /(lbls|values|x_label)/); print "\n"; $total += $auth{$type}{$mech}; } print qq{
$TRANSLATE{'SMTP Auth'}: $type
$TRANSLATE{'Mechanism'}$TRANSLATE{'Count'}
$mech$auth{$type}{$mech}
$TRANSLATE{'Total'}$total
 
}; print &grafit( labels => $auth{$type}{lbls}, values => $auth{$type}{values}, title => "$TRANSLATE{'Authent Flow'}: $type", x_label => $x_label, y_label => $TRANSLATE{'Number of connection'}, divid => "connection_$i" ); print qq{
}; $i++; } } else { print qq{
$TRANSLATE{'No dataset'}
}; } } sub compute_postgreyflow { my ($hostname) = @_; my %period_stat = (); foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{reason}) { $period_stat{postgrey}{$STATS{$id}{reason}}{$STATS{$id}{idx_postgrey}}++; $postgrey{reason}{$STATS{$id}{reason}}++; } } %STATS = (); return %period_stat; } sub summarize_postgreyflow { my ($begin, $end, %period_stat) = @_; foreach my $k (keys %{$postgrey{reason}}) { $postgrey{total_reason} += $postgrey{reason}{$k}; } } sub display_postgreyflow { my ($x_label) = @_; if (scalar keys %{$postgrey{reason}} == 0) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq{
}; my $piecount = 0; my %graph_data = (); foreach my $k (sort { $postgrey{reason}{$b} <=> $postgrey{reason}{$a} } keys %{$postgrey{reason}}) { next if ($k eq ''); my $percent = sprintf("%.2f", ($postgrey{reason}{$k}/$postgrey{total_reason}) * 100); print "\n"; if ( ($piecount < $MAXPIECOUNT) && ($percent > $MIN_SHOW_PIE)) { $graph_data{$k} = $percent; $piecount++; } } print qq{
$TRANSLATE{'Postgrey Status'}
 $TRANSLATE{'Messages'}$TRANSLATE{'Percentage'}
$k$postgrey{reason}{$k}$percent %
 
}; print &grafit_pie( values => \%graph_data, title => $TRANSLATE{'Postgrey Status'}, divid => 'postgreyflow', width => 600, height => 250 ); print qq{
}; } sub detail_link { my ($hostname, $date, $type, $peri, $name, $hour) = @_; $name ||= '<>'; my ($sec,$min,$h,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon++; $date =~ /\d{4}(\d{2})/; my $month = $1; my @files = (); my $path = $CONFIG{OUT_DIR} . '/' . $hostname . '/' . $date; $path =~ s/(\d{4})(\d{2})(\d{2})$/$1\/$2\/$3\//; if (($date !~ /00$/) && !$WEEK) { if (not opendir(DIR, "$path")) { &logerror("Can't open directory $CONFIG{OUT_DIR}: $!\n"); } else { @files = grep { /.*\.dat$/ } readdir(DIR); closedir(DIR); } } my $tmpname = &decode_str($name); $tmpname =~ s/_/:/g if ($peri eq 'relay'); # Return a link if we still have dat file. if ( ($date !~ /00$/) && ($#files >= 0) ) { return "escape($name) . "\">" . $CGI->unescape($tmpname) . ""; } return substr($CGI->unescape(&decode_str($tmpname)), 0, 124); } sub detail_download_link { my ($hostname, $date, $type, $peri, $name, $hour) = @_; $name ||= '<>'; my ($sec,$min,$h,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon++; $date =~ /\d{4}(\d{2})/; my $month = $1; my @files = (); my $path = $CONFIG{OUT_DIR} . '/' . $hostname . '/' . $date; $path =~ s/(\d{4})(\d{2})(\d{2})$/$1\/$2\/$3\//; if (($date !~ /00$/) && !$WEEK) { if (not opendir(DIR, "$path")) { &logerror("Can't open directory $CONFIG{OUT_DIR}: $!\n"); } else { @files = grep { /.*\.dat$/ } readdir(DIR); closedir(DIR); } } # Return a link if we still have dat file. if ( ($date !~ /00$/) && ($#files >= 0) ) { $name = $CGI->escape($name); return "[csv]"; } } sub compute_top_sender { foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); # Only compute top sender on sent messages next if (!grep(/Sent/, @{$STATS{$id}{status}})); $topsender{email}{$STATS{$id}{sender}}++; if ($STATS{$id}{sender} =~ /\@(.*)/) { $topsender{domain}{$1}++; } else { $topsender{domain}{$STATS{$id}{sender}}++; } $topsender{relay}{$STATS{$id}{sender_relay}}++; } %STATS = (); } sub display_top_sender { my ($hostname, $date, $hour) = @_; # Top sender statistics my $topdomain = ''; my $top = 0; foreach my $d (sort { $topsender{domain}{$b} <=> $topsender{domain}{$a} } keys %{$topsender{domain}}) { last if ($top == $CONFIG{TOP}); $topdomain .= &detail_link($hostname,$date,'sender','domain',$d,$hour) . " ($topsender{domain}{$d})
"; $top++; } delete $topsender{domain}; my $totalrelay = 0; foreach my $d (keys %{$topsender{relay}}) { $totalrelay += $topsender{relay}{$d}; } my $toprelay = ''; $top = 0; my %relays = (); my $piecount = 0; my $percent_total = 0; foreach my $d (sort { $topsender{relay}{$b} <=> $topsender{relay}{$a} } keys %{$topsender{relay}}) { last if ($top == $CONFIG{TOP}); my $percent = sprintf("%.2f", ($topsender{relay}{$d}*100)/$totalrelay); $toprelay .= &detail_link($hostname,$date,'sender','relay',$d,$hour) . " ($topsender{relay}{$d})
"; if ( ($piecount < $MAXPIECOUNT) && ($percent > $MIN_SHOW_PIE)) { $relays{"$d"} = $percent; $percent_total += $topsender{relay}{$d}; $piecount++; } $top++; } my $other_percent = 100 - sprintf("%.2f", ($percent_total*100)/($totalrelay||1)); $relays{"Others"} = $other_percent if ($other_percent > 0); delete $topsender{relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $topemail = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topsender{email}{$b} <=> $topsender{email}{$a} } keys %{$topsender{email}}) { last if ($top == $CONFIG{TOP}); $topemail .= &detail_link($hostname,$date,'sender','address',$d,$hour) . " ($topsender{email}{$d})
"; $top++; } } %topsender = (); if (!$topdomain) { print qq{
$TRANSLATE{'No dataset'}
}; return; } if ($CONFIG{ANONYMIZE}) { $topemail = ' '; } print qq{
}; print &grafit_hbar( values => \%relays, title => $TRANSLATE{'Top Sender Relay'}, divid => 'topsenderrelay', width => 900, height => 250 ); print qq{
$TRANSLATE{'Top Sender Domain'} $TRANSLATE{'Top Sender Relay'} $TRANSLATE{'Top Sender Address'}
$topdomain $toprelay $topemail
}; } sub dump_top_sender { my ($hostname, $date, $hour) = @_; # Top sender statistics my $filename = "$hostname-$date"; $filename .= "-$hour" if ($hour); $filename .= "-topsender.csv"; print "Content-Type:application/x-download\n"; print "Content-Disposition:attachment;filename=$filename\n\n"; my $top = 0; print "Top Sender domain;Number;Top Sender relay;Number"; if (!$CONFIG{ANONYMIZE}) { print ";Top Sender Address;Number"; } print "\n"; my @data = (); foreach my $d (sort { $topsender{domain}{$b} <=> $topsender{domain}{$a} } keys %{$topsender{domain}}) { last if ($top == $CONFIG{TOP}); push(@data, "$d;$topsender{domain}{$d}"); $top++; } delete $topsender{domain}; $top = 0; foreach my $d (sort { $topsender{relay}{$b} <=> $topsender{relay}{$a} } keys %{$topsender{relay}}) { last if ($top == $CONFIG{TOP}); my $tmp = $d || '<>'; $tmp =~ s/_/:/g; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { next if (!$pat || !$CONFIG{REPLACE_HOST}{$pat}); last if ($tmp =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/); } } if (! $data[$top]) { push(@data, ";;$tmp;$topsender{relay}{$d}"); } else { $data[$top] .= ";$tmp;$topsender{relay}{$d}"; } $top++; } delete $topsender{relay}; if (!$CONFIG{ANONYMIZE}) { $top = 0; foreach my $d (sort { $topsender{email}{$b} <=> $topsender{email}{$a} } keys %{$topsender{email}}) { last if ($top == $CONFIG{TOP}); if (! $data[$top]) { push(@data, ";;;;$d;$topsender{email}{$d}"); } else { $data[$top] .= ";$d;$topsender{email}{$d}"; } $top++; } } foreach (@data) { print "$_\n"; } } sub compute_top_recipient { foreach my $id (keys %STATS) { if ($DOMAIN) { if ($STATS{$id}{sender} =~ /$DOMAIN/) { for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { next if ($STATS{$id}{status}[$i] ne 'Sent'); $toprcpt{email}{$STATS{$id}{rcpt}[$i]}++; $STATS{$id}{rcpt}[$i] =~ s/^.*\@//; $toprcpt{domain}{$STATS{$id}{rcpt}[$i]}++; $toprcpt{relay}{$STATS{$id}{rcpt_relay}[$i]}++; } } elsif (grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})) { for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { next if ($STATS{$id}{status}[$i] ne 'Sent'); $toprcpt{email}{$STATS{$id}{rcpt}[$i]}++; $STATS{$id}{rcpt}[$i] =~ s/^.*\@//; $toprcpt{domain}{$STATS{$id}{rcpt}[$i]}++; $toprcpt{relay}{$STATS{$id}{rcpt_relay}[$i]}++; } } } else { for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { next if ($STATS{$id}{status}[$i] ne 'Sent'); $toprcpt{email}{$STATS{$id}{rcpt}[$i]}++; $STATS{$id}{rcpt}[$i] =~ s/^.*\@//; $toprcpt{domain}{$STATS{$id}{rcpt}[$i]}++; $toprcpt{relay}{$STATS{$id}{rcpt_relay}[$i]}++; } } } %STATS = (); } sub display_top_recipient { my ($hostname, $date, $hour) = @_; # Top recipient statistics my $topdomain = ''; my $top = 0; foreach my $d (sort { $toprcpt{domain}{$b} <=> $toprcpt{domain}{$a} } keys %{$toprcpt{domain}}) { last if ($top == $CONFIG{TOP}); $topdomain .= &detail_link($hostname,$date,'recipient','domain',$d,$hour) . " ($toprcpt{domain}{$d})
"; $top++; } delete $toprcpt{domain}; my $totalrelay = 0; foreach my $d (keys %{$toprcpt{relay}}) { $totalrelay += $toprcpt{relay}{$d}; } my $toprelay = ''; $top = 0; my %relays = (); my $piecount = 0; my $percent_total = 0; foreach my $d (sort { $toprcpt{relay}{$b} <=> $toprcpt{relay}{$a} } keys %{$toprcpt{relay}}) { last if ($top == $CONFIG{TOP}); my $percent = sprintf("%.2f", ($toprcpt{relay}{$d}*100)/($totalrelay||1)); $toprelay .= &detail_link($hostname,$date,'recipient','relay',$d,$hour) . " ($toprcpt{relay}{$d})
"; if ( ($piecount < $MAXPIECOUNT) && ($percent > $MIN_SHOW_PIE)) { $relays{"$d"} = $percent; $percent_total += $toprcpt{relay}{$d}; $piecount++; } $top++; } my $other_percent = 100 - sprintf("%.2f", ($percent_total*100)/($totalrelay||1)); $relays{"Others"} = $other_percent if ($other_percent > 0); delete $toprcpt{relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $topemail = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $toprcpt{email}{$b} <=> $toprcpt{email}{$a} } keys %{$toprcpt{email}}) { last if ($top == $CONFIG{TOP}); $topemail .= &detail_link($hostname,$date,'recipient','address',$d,$hour) . " ($toprcpt{email}{$d})
"; $top++; } } %toprcpt = (); if (!$topdomain) { print qq{
$TRANSLATE{'No dataset'}
}; return; } if ($CONFIG{ANONYMIZE}) { $topemail = ' '; } print qq {
}; print &grafit_hbar( values => \%relays, title => $TRANSLATE{'Top Recipient Relay'}, divid => 'toprecipientrelay', width => 900, height => 250 ); print qq{
$TRANSLATE{'Top Recipient Domain'} $TRANSLATE{'Top Recipient Relay'} $TRANSLATE{'Top Recipients Address'}
$topdomain $toprelay $topemail
}; } sub dump_top_recipient { my ($hostname, $date, $hour) = @_; # Top recipient statistics my $filename = "$hostname-$date"; $filename .= "-$hour" if ($hour); $filename .= "-toprcpt.csv"; print "Content-Type:application/x-download\n"; print "Content-Disposition:attachment;filename=$filename\n\n"; print "Top Recipients Domain;Number;Top Recipients Relay;Number"; if (!$CONFIG{ANONYMIZE}) { print ";Top Recipients Address;Number"; } print "\n"; my $top = 0; my @data = (); foreach my $d (sort { $toprcpt{domain}{$b} <=> $toprcpt{domain}{$a} } keys %{$toprcpt{domain}}) { last if ($top == $CONFIG{TOP}); push(@data, "$d;$toprcpt{domain}{$d}"); $top++; } delete $toprcpt{domain}; $top = 0; foreach my $d (sort { $toprcpt{relay}{$b} <=> $toprcpt{relay}{$a} } keys %{$toprcpt{relay}}) { last if ($top == $CONFIG{TOP}); my $tmp = $d || '<>'; $tmp =~ s/_/:/g; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { next if (!$pat || !$CONFIG{REPLACE_HOST}{$pat}); last if ($tmp =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/); } } if (! $data[$top]) { push(@data, ";;$tmp;$toprcpt{relay}{$d}"); } else { $data[$top] .= ";$tmp;$toprcpt{relay}{$d}"; } $top++; } delete $toprcpt{relay}; if (!$CONFIG{ANONYMIZE}) { $top = 0; foreach my $d (sort { $toprcpt{email}{$b} <=> $toprcpt{email}{$a} } keys %{$toprcpt{email}}) { last if ($top == $CONFIG{TOP}); if (! $data[$top]) { push(@data, ";;;;$d;$toprcpt{email}{$d}"); } else { $data[$top] .= ";$d;$toprcpt{email}{$d}"; } $top++; } } foreach (@data) { print "$_\n"; } } sub compute_top_spam { foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{spam}) { $topspam{sender}{$STATS{$id}{sender}}++; $STATS{$id}{sender} =~ s/^.*\@//; $topspam{domain}{$STATS{$id}{sender}}++; $topspam{sender_relay}{$STATS{$id}{sender_relay}}++; $topspam{rule}{$STATS{$id}{spam}}++; for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { $topspam{rcpt}{$STATS{$id}{rcpt}[$i]}++; } } } %STATS = (); } sub display_top_spam { my ($hostname, $date, $hour) = @_; # Top spam statistics my $toprule = ''; my $top = 0; foreach my $d (sort { $topspam{rule}{$b} <=> $topspam{rule}{$a} } keys %{$topspam{rule}}) { last if ($top == $CONFIG{TOP}); $toprule .= &detail_link($hostname,$date,'spam','rule',$d,$hour) . " ($topspam{rule}{$d})
"; $top++; } delete $topspam{rule}; my $topdomain = ''; $top = 0; foreach my $d (sort { $topspam{domain}{$b} <=> $topspam{domain}{$a} } keys %{$topspam{domain}}) { last if ($top == $CONFIG{TOP}); $topdomain .= &detail_link($hostname,$date,'spam','domain',$d,$hour) . " ($topspam{domain}{$d})
"; $top++; } delete $topspam{domain}; my $toprelay = ''; $top = 0; foreach my $d (sort { $topspam{sender_relay}{$b} <=> $topspam{sender_relay}{$a} } keys %{$topspam{sender_relay}}) { last if ($top == $CONFIG{TOP}); $toprelay .= &detail_link($hostname,$date,'spam','relay',$d,$hour) . " ($topspam{sender_relay}{$d})
"; $top++; } delete $topspam{sender_relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $topemail = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topspam{sender}{$b} <=> $topspam{sender}{$a} } keys %{$topspam{sender}}) { last if ($top == $CONFIG{TOP}); $topemail .= &detail_link($hostname,$date,'spam','sender',$d,$hour) . " ($topspam{sender}{$d})
"; $top++; } } delete $topspam{sender}; my $topdest = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topspam{rcpt}{$b} <=> $topspam{rcpt}{$a} } keys %{$topspam{rcpt}}) { last if ($top == $CONFIG{TOP}); next if (($d eq '') || ($d eq '<>')); $topdest .= &detail_link($hostname,$date,'spam','recipient',$d,$hour) . " ($topspam{rcpt}{$d})
"; $top++; } } %topspam = (); if ($toprule) { print qq { }; print qq{ } if ($topemail); print qq{ } if ($topdest); print "
$TRANSLATE{'Spam Statistics'} (top $CONFIG{TOP})
$TRANSLATE{'Top Spams Rules'}$toprule
$TRANSLATE{'Top Spammers Domain'}$topdomain
$TRANSLATE{'Top Spammers Relays'}$toprelay
$TRANSLATE{'Top Spammers Address'}$topemail
$TRANSLATE{'Top Recipients Address'}$topdest
\n"; } else { print qq{
$TRANSLATE{'No dataset'}
}; } } sub compute_top_virus { foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{virus}) { $topvirus{sender}{$STATS{$id}{sender}}++; $topvirus{relay}{$STATS{$id}{sender_relay}}++; $topvirus{file}{$STATS{$id}{file}}++; $topvirus{virus}{$STATS{$id}{virus}}++; for (my $i = 0; $i <= $#{$STATS{$id}{status}}; $i++) { $topvirus{rcpt}{$STATS{$id}{rcpt}[$i]}++; } } } %STATS = (); } sub display_top_virus { my ($hostname, $date, $hour) = @_; # Top virus statistics my $topvirus = ''; my $top = 0; foreach my $d (sort { $topvirus{virus}{$b} <=> $topvirus{virus}{$a} } keys %{$topvirus{virus}}) { last if ($top == $CONFIG{TOP}); $topvirus .= &detail_link($hostname,$date,'virus','virus',$d,$hour) . " ($topvirus{virus}{$d})
"; $top++; } delete $topvirus{virus}; my $topsender = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topvirus{sender}{$b} <=> $topvirus{sender}{$a} } keys %{$topvirus{sender}}) { last if ($top == $CONFIG{TOP}); $topsender .= &detail_link($hostname,$date,'virus','sender',$d,$hour) . " ($topvirus{sender}{$d})
"; $top++; } } delete $topvirus{sender}; my $toprelay = ''; $top = 0; foreach my $d (sort { $topvirus{relay}{$b} <=> $topvirus{relay}{$a} } keys %{$topvirus{relay}}) { last if ($top == $CONFIG{TOP}); $toprelay .= &detail_link($hostname,$date,'virus','relay',$d,$hour) . " ($topvirus{relay}{$d})
"; $top++; } delete $topvirus{relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $topfile = ''; $top = 0; foreach my $d (sort { $topvirus{file}{$b} <=> $topvirus{file}{$a} } keys %{$topvirus{file}}) { last if ($top == $CONFIG{TOP}); $topfile .= &detail_link($hostname,$date,'virus','file',$d,$hour) . " ($topvirus{file}{$d})
"; $top++; } delete $topvirus{file}; my $topemail = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topvirus{rcpt}{$b} <=> $topvirus{rcpt}{$a} } keys %{$topvirus{rcpt}}) { last if ($top == $CONFIG{TOP}); $topemail .= &detail_link($hostname,$date,'virus','recipient',$d,$hour) . " ($topvirus{rcpt}{$d})
"; $top++; } } if ($topvirus) { print qq { }; if (!$CONFIG{ANONYMIZE}) { print qq { }; } print qq { }; if (!$CONFIG{ANONYMIZE}) { print qq { }; } print "
$TRANSLATE{'Virus Statistics'} (top $CONFIG{TOP})
$TRANSLATE{'Top Virus'}$topvirus
$TRANSLATE{'Top Virus Senders'}$topsender
$TRANSLATE{'Top Virus Relays'}$toprelay
$TRANSLATE{'Top Virus Filenames'}$topfile
$TRANSLATE{'Top Recipient Address'}$topemail
\n"; } else { print qq{
$TRANSLATE{'No dataset'}
}; } } sub compute_top_dsn { foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{dsnstatus}) { $STATS{$id}{sender} = 'unknown' if (!exists $STATS{$id}{sender}); $STATS{$id}{sender_relay} = 'unknown' if (!exists $STATS{$id}{sender_relay}); $topdsn{dsnstatus}{$STATS{$id}{dsnstatus}}++; $topdsn{sender}{$STATS{$id}{sender}}++; $topdsn{relay}{$STATS{$id}{sender_relay}}++; for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { $topdsn{rcpt}{$STATS{$id}{rcpt}[$i]}++; $topdsn{status}{$STATS{$id}{status}[$i]}++; } } } %STATS = (); } sub display_top_dsn { my ($hostname, $date, $hour) = @_; # Top dsn statistics my $topdsnstatus = ''; my $top = 0; foreach my $d (sort { $topdsn{dsnstatus}{$b} <=> $topdsn{dsnstatus}{$a} } keys %{$topdsn{dsnstatus}}) { last if ($top == $CONFIG{TOP}); $topdsnstatus .= &detail_link($hostname,$date,'dsn','dsnstatus',$d,$hour) . " ($topdsn{dsnstatus}{$d})
"; $top++; } delete $topdsn{dsnstatus}; my $topsender = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topdsn{sender}{$b} <=> $topdsn{sender}{$a} } keys %{$topdsn{sender}}) { last if ($top == $CONFIG{TOP}); $topsender .= "$d ($topdsn{sender}{$d})
"; $top++; } } delete $topdsn{sender}; my $toprelay = ''; $top = 0; foreach my $d (sort { $topdsn{relay}{$b} <=> $topdsn{relay}{$a} } keys %{$topdsn{relay}}) { last if ($top == $CONFIG{TOP}); $toprelay .= "$d ($topdsn{relay}{$d})
"; $top++; } delete $topdsn{relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $toprcpt = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topdsn{rcpt}{$b} <=> $topdsn{rcpt}{$a} } keys %{$topdsn{rcpt}}) { last if ($top == $CONFIG{TOP}); $toprcpt .= "$d ($topdsn{rcpt}{$d})
"; $top++; } } delete $topdsn{rcpt}; if (!$topdsnstatus) { print qq{
$TRANSLATE{'No dataset'}
}; return; } print qq { }; print qq { } if (!$CONFIG{ANONYMIZE}); print qq { }; print qq { } if (!$CONFIG{ANONYMIZE}); print qq {
$TRANSLATE{'DSN Statistics'} (top $CONFIG{TOP})
$TRANSLATE{'Top DSN Status'}$topdsnstatus
$TRANSLATE{'Top DSN Senders'}$topsender
$TRANSLATE{'Top DSN Relays'}$toprelay
$TRANSLATE{'Top DSN Recipients'}$toprcpt
}; } sub compute_top_reject { foreach my $id (keys %STATS) { next if ($id eq 'STARTTLS'); next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{rule}) { $topreject{sender}{$STATS{$id}{sender}}++; $STATS{$id}{sender} =~ s/^.*\@//; $STATS{$id}{sender} ||= 'Unknown'; $topreject{domain}{$STATS{$id}{sender}}++; $topreject{relay}{$STATS{$id}{sender_relay}}++; $topreject{rule}{$STATS{$id}{rule}}++; for (my $i = 0; $i <= $#{$STATS{$id}{chck_status}}; $i++) { next if ($STATS{$id}{chck_status}[$i] =~ /Queued/); $topreject{chck_status}{$STATS{$id}{chck_status}[$i]}++; } } if (exists $STATS{$id}{error}) { # Skip already registered as rejection if (!exists $STATS{$id}{rule}) { $toperr{$STATS{$id}{error}}++; } } } %STATS = (); } sub display_top_reject { my ($hostname, $date, $hour) = @_; # Top rejection statistics my $toprule = ''; my $top = 0; foreach my $d (sort { $topreject{rule}{$b} <=> $topreject{rule}{$a} } keys %{$topreject{rule}}) { last if ($top == $CONFIG{TOP}); $toprule .= &detail_link($hostname,$date,'reject','rule',$d,$hour) . " ($topreject{rule}{$d})
"; $top++; } delete $topreject{rule}; my $topdomain = ''; $top = 0; foreach my $d (sort { $topreject{domain}{$b} <=> $topreject{domain}{$a} } keys %{$topreject{domain}}) { last if ($top == $CONFIG{TOP}); if ($d ne '<>') { $topdomain .= &detail_link($hostname,$date,'reject','domain',$d,$hour) . " ($topreject{domain}{$d})
"; } else { $topdomain .= "$d ($topreject{domain}{$d})
"; } $top++; } delete $topreject{domain}; my $toprelay = ''; $top = 0; foreach my $d (sort { $topreject{relay}{$b} <=> $topreject{relay}{$a} } keys %{$topreject{relay}}) { last if ($top == $CONFIG{TOP}); $toprelay .= &detail_link($hostname,$date,'reject','relay',$d,$hour) . " ($topreject{relay}{$d})
"; $top++; } delete $topreject{relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $topstatus = ''; $top = 0; foreach my $d (sort { $topreject{chck_status}{$b} <=> $topreject{chck_status}{$a} } keys %{$topreject{chck_status}}) { last if ($top == $CONFIG{TOP}); if ($d =~ /^(\d{3}) \d\.(\d\.\d)$/) { if (exists $SMTP_ERROR_CODE{$1} || exists $ESMTP_ERROR_CODE{$2}) { $topstatus .= &detail_link($hostname,$date,'reject','status',"$d $SMTP_ERROR_CODE{$1} $ESMTP_ERROR_CODE{$2}",$hour); } else { $topstatus .= &detail_link($hostname,$date,'reject','status',$d,$hour); } } else { $topstatus .= &detail_link($hostname,$date,'reject','status',$d,$hour); } $topstatus .= " ($topreject{chck_status}{$d})
"; $top++; } delete $topreject{status}; my $topemail = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $topreject{sender}{$b} <=> $topreject{sender}{$a} } keys %{$topreject{sender}}) { last if ($top == $CONFIG{TOP}); $topemail .= &detail_link($hostname,$date,'reject','address',$d,$hour) . " ($topreject{sender}{$d})
"; $top++; } } %topreject =(); if (!$toprule && (scalar keys %toperr == 0)) { print qq{
$TRANSLATE{'No dataset'}
}; return; } if ($toprule) { print qq { }; if (!$CONFIG{ANONYMIZE}) { print qq { }; } print qq { }; print "
$TRANSLATE{'Rejection Statistics'} (top $CONFIG{TOP})
$TRANSLATE{'Top Rules'}$toprule
$TRANSLATE{'Top Domains'}$topdomain
$TRANSLATE{'Top Relays'}$toprelay
$TRANSLATE{'Top Senders'}$topemail
$TRANSLATE{'Top status'}$topstatus
\n"; } if (scalar keys %toperr > 0) { $top = 0; print qq{ }; foreach my $msg (sort {$toperr{$b} <=> $toperr{$a}} keys %toperr) { my $display = $msg; $display =~ s//\>/g; if ($msg =~ /^(\d{3}) \d\.(\d\.\d)$/) { if (exists $SMTP_ERROR_CODE{$1} || exists $ESMTP_ERROR_CODE{$2}) { print "\n"; } else { print "\n"; } } else { print "\n"; } $top++; } print "
$TRANSLATE{'System messages'}
$TRANSLATE{'Message'}$TRANSLATE{'Count'}
$display $SMTP_ERROR_CODE{$1} $ESMTP_ERROR_CODE{$2}$toperr{$msg}
$display$toperr{$msg}
$display$toperr{$msg}
\n"; } } sub compute_top_limit { foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if ($STATS{$id}{nrcpt} > $CONFIG{MAX_RCPT}) { push(@{$topmaxrcpt{$STATS{$id}{nrcpt}}{sender}}, $STATS{$id}{sender}); push(@{$topmaxrcpt{$STATS{$id}{nrcpt}}{sa_id}}, $id); } if ($STATS{$id}{size} && $STATS{$id}{nrcpt}) { if ($STATS{$id}{size} > $CONFIG{MAX_SIZE}) { $topmaxsize{$id}{size} = $STATS{$id}{size}; $topmaxsize{$id}{sender} = $STATS{$id}{sender}; $topmaxsize{$id}{nrcpt} = $STATS{$id}{nrcpt}; } } } %STATS = (); } sub display_top_limit { my ($hostname, $date, $hour) = @_; if ((scalar keys %topmaxrcpt == 0) && (scalar keys %topmaxsize == 0)) { print qq{
$TRANSLATE{'No dataset'}
}; return; } if (scalar keys %topmaxrcpt > 0) { print qq{ }; my $top = 0; foreach my $nb (sort { $b <=> $a } keys %topmaxrcpt) { last if ($top == $CONFIG{TOP}); print "\n"; $top++; } print "
$TRANSLATE{'Max Number of Recipients'} (top $CONFIG{TOP})
$TRANSLATE{'Number of Recipients'}$TRANSLATE{'Senders'}
$nb"; for (my $i = 0; $i <= $#{$topmaxrcpt{$nb}{sender}}; $i++) { my $whosend = $topmaxrcpt{$nb}{sender}[$i]; $whosend =~ s/^.*\@/anonymized\@/ if ($CONFIG{ANONYMIZE}); print "$whosend (", &detail_link($hostname,$date,'sender','topmax',$topmaxrcpt{$nb}{sa_id}[$i],$hour), ")
\n"; } print "
\n"; } if (scalar keys %topmaxsize > 0) { my $top = 0; print qq{ }; foreach my $id (sort { ($topmaxsize{$b}{size}*$topmaxsize{$b}{nrcpt}) <=> ($topmaxsize{$a}{size}*$topmaxsize{$b}{nrcpt}) } keys %topmaxsize) { last if ($top == $CONFIG{TOP}); $topmaxsize{$id}{sender} =~ s/^.*\@/anonymized\@/ if ($CONFIG{ANONYMIZE}); print "\n"; $top++; } print "
$TRANSLATE{'Max Size Senders'} (top $CONFIG{TOP})
$TRANSLATE{'Message size'}$TRANSLATE{'Number of Recipients'}$TRANSLATE{'Senders'}
$topmaxsize{$id}{size}$topmaxsize{$id}{nrcpt}$topmaxsize{$id}{sender} (", &detail_link($hostname,$date,'sender','topmax',$id,$hour), ")
\n"; } } sub compute_top_spamdetail { my ($type) = @_; foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); $STATS{$id}{spam} =~ s/;.*// if ($type eq 'dnsbl'); $topspamdetail{$type}{score}{$STATS{$id}{score}}++ if ($STATS{$id}{score}); $topspamdetail{$type}{rule}{$STATS{$id}{spam}}++; $topspamdetail{$type}{cache}{$STATS{$id}{cache}}++ if ($STATS{$id}{cache}); $topspamdetail{$type}{autolearn}{$STATS{$id}{autolearn}}++ if ($STATS{$id}{autolearn}); } %STATS = (); } sub display_top_spamdetail { my ($hostname, $date, $type, $hour) = @_; if (scalar keys %{$topspamdetail{$type}} == 0) { print qq{
$TRANSLATE{'No dataset'}
}; return; } if (exists $topspamdetail{$type}{rule}) { print qq{ }; my $top = 0; foreach my $spam (sort { $topspamdetail{$type}{rule}{$b} <=> $topspamdetail{$type}{rule}{$a} } keys %{$topspamdetail{$type}{rule}}) { last if ($top == $CONFIG{TOP}); print "\n"; $top++; } print "
$TRANSLATE{'Top Spams'} (top $CONFIG{TOP})
$TRANSLATE{'Count'}$TRANSLATE{'Rule'}
$topspamdetail{$type}{rule}{$spam}", &detail_link($hostname,$date,'spam_'.$VIEW,'rule',$spam,$hour) . "
\n

 

\n"; } delete $topspamdetail{$type}{rule}; print "
\n"; if (exists $topspamdetail{$type}{score} && (scalar keys %{$topspamdetail{$type}{score}} > 0)) { print qq{ }; my $top = 0; foreach my $nb (sort { $topspamdetail{$type}{score}{$b} <=> $topspamdetail{$type}{score}{$a} } keys %{$topspamdetail{$type}{score}}) { last if ($top == $CONFIG{TOP}); print ""; $top++; } print "
$TRANSLATE{'Top spam scores'}
$TRANSLATE{'Score'}$TRANSLATE{'Count'}
", &detail_link($hostname,$date,'spam_'.$VIEW,'score',$nb,$hour), "$topspamdetail{$type}{score}{$nb}
\n"; } delete $topspamdetail{$type}{score}; print "
\n"; if (exists $topspamdetail{$type}{cache} && (scalar keys %{$topspamdetail{$type}{cache}} > 0) ) { print qq{ }; my $top = 0; foreach my $nb (sort { $topspamdetail{$type}{cache}{$b} <=> $topspamdetail{$type}{cache}{$a} } keys %{$topspamdetail{$type}{cache}}) { last if ($top == $CONFIG{TOP}); print ""; $top++; } print "
$TRANSLATE{'Caching statistics'}
$TRANSLATE{'Cache'}$TRANSLATE{'Count'}
", &detail_link($hostname,$date,'spam_'.$VIEW,'cache',$nb,$hour), "$topspamdetail{$type}{cache}{$nb}
\n"; } delete $topspamdetail{$type}{cache}; print "
\n"; if (exists $topspamdetail{$type}{autolearn} && (scalar keys %{$topspamdetail{$type}{autolearn}} > 0) ) { print qq{ }; my $top = 0; foreach my $nb (sort { $topspamdetail{$type}{autolearn}{$b} <=> $topspamdetail{$type}{autolearn}{$a} } keys %{$topspamdetail{$type}{autolearn}}) { last if ($top == $CONFIG{TOP}); print ""; $top++; } print "
$TRANSLATE{'Autolearn Statistics'}
$TRANSLATE{'Autolearn'}$TRANSLATE{'Count'}
", &detail_link($hostname,$date,'spam_'.$VIEW,'autolearn',$nb,$hour), "$topspamdetail{$type}{autolearn}{$nb}
\n

 

"; } print "
\n"; delete $topspamdetail{$type}{autolearn}; } sub compute_top_auth { return if ($DOMAIN); foreach my $id (keys %AUTH) { $topauth{authid}{$id} += ($#{$AUTH{$id}{relay}} + 1); for (my $i = 0; $i <= $#{$AUTH{$id}{relay}}; $i++) { $topauth{relay}{$AUTH{$id}{relay}[$i]}++; $topauth{mech}{$AUTH{$id}{mech}[$i]}++; } } %AUTH = (); } sub display_top_auth { my ($hostname, $date, $hour) = @_; if (scalar keys %{$topauth{authid}} == 0) { print qq{
$TRANSLATE{'No dataset'}
}; return; } my $topmech = ''; foreach my $k (sort {$topauth{mech}{$b} <=> $topauth{mech}{$a} } keys %{$topauth{mech}} ) { $topmech .= "$k ($topauth{mech}{$k})
\n"; } delete $topauth{mech}; my $toprelay = ''; my $top = 0; foreach my $k (sort {$topauth{relay}{$b} <=> $topauth{relay}{$a} } keys %{$topauth{relay}} ) { last if ($top == $CONFIG{TOP}); $toprelay .= "$k ($topauth{relay}{$k})
\n"; $top++; } delete $topauth{relay}; my $topid = ''; $top = 0; foreach my $k (sort {$topauth{authid}{$b} <=> $topauth{authid}{$a} } keys %{$topauth{authid}} ) { last if ($top == $CONFIG{TOP}); my $user = $k; $user = "user_$top" if ($CONFIG{ANONYMIZE}); $topid .= "$user ($topauth{authid}{$k})
\n"; $top++; } print qq{
$TRANSLATE{'SMTP Auth Statistics'} (top $CONFIG{TOP})
$TRANSLATE{'Top Mechanism'}$TRANSLATE{'Top Relay'}$TRANSLATE{'Top Authid'}
$topmech$toprelay$topid
}; } sub compute_top_postgrey { foreach my $id (keys %STATS) { next if ($DOMAIN && ($STATS{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$STATS{$id}{rcpt}})); if (exists $STATS{$id}{reason}) { $toppostgrey{sender}{$STATS{$id}{sender}}++; $STATS{$id}{sender} =~ s/^.*\@//; $STATS{$id}{sender} ||= 'Unknown'; $toppostgrey{domain}{$STATS{$id}{sender}}++; $toppostgrey{sender_relay}{$STATS{$id}{sender_relay}}++; for (my $i = 0; $i <= $#{$STATS{$id}{rcpt}}; $i++) { $toppostgrey{rcpt}{$STATS{$id}{rcpt}[$i]}++; } $toppostgrey{reason}{$STATS{$id}{reason}}++; } } %STATS = (); } sub display_top_postgrey { my ($hostname, $date, $hour) = @_; print qq{\n"; } $params{width} ||= 400; $params{height} ||= 200; return < #$params{divid} { width : $params{width}px; height: $params{height}px; background:#F3F2ED; border:10px solid white; padding:0 10px; margin:30px 10px 30px 10px; border-radius:10px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; }
}; # Top postgrey statistics my $topreason = ''; my $top = 0; foreach my $d (sort { $toppostgrey{reason}{$b} <=> $toppostgrey{reason}{$a} } keys %{$toppostgrey{reason}}) { last if ($top == $CONFIG{TOP}); $topreason .= &detail_link($hostname,$date,'postgrey','reason',$d,$hour) . " ($toppostgrey{reason}{$d})
"; $top++; } delete $toppostgrey{reason}; my $topdomain = ''; $top = 0; foreach my $d (sort { $toppostgrey{domain}{$b} <=> $toppostgrey{domain}{$a} } keys %{$toppostgrey{domain}}) { last if ($top == $CONFIG{TOP}); if ($d ne '<>') { $topdomain .= &detail_link($hostname,$date,'postgrey','domain',$d,$hour) . " ($toppostgrey{domain}{$d})
"; } else { $topdomain .= "$d ($toppostgrey{domain}{$d})
"; } $top++; } delete $toppostgrey{domain}; my $topemail = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $toppostgrey{sender}{$b} <=> $toppostgrey{sender}{$a} } keys %{$toppostgrey{sender}}) { last if ($top == $CONFIG{TOP}); $topemail .= &detail_link($hostname,$date,'postgrey','address',$d,$hour) . " ($toppostgrey{sender}{$d})
"; $top++; } } my $toprelay = ''; $top = 0; foreach my $d (sort { $toppostgrey{sender_relay}{$b} <=> $toppostgrey{sender_relay}{$a} } keys %{$toppostgrey{sender_relay}}) { last if ($top == $CONFIG{TOP}); $toprelay .= &detail_link($hostname,$date,'postgrey','relay',$d,$hour) . " ($toppostgrey{sender_relay}{$d})
"; $top++; } delete $toppostgrey{sender_relay}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $toprelay =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } my $topdest = ''; $top = 0; if (!$CONFIG{ANONYMIZE}) { foreach my $d (sort { $toppostgrey{rcpt}{$b} <=> $toppostgrey{rcpt}{$a} } keys %{$toppostgrey{rcpt}}) { last if ($top == $CONFIG{TOP}); next if (($d eq '') || ($d eq '<>')); $topdest .= &detail_link($hostname,$date,'postgrey','recipient',$d,$hour) . " ($toppostgrey{rcpt}{$d})
"; $top++; } } %toppostgrey = (); if (!$topreason) { print qq{
$TRANSLATE{'No dataset'}
}; return; } if ($topreason) { print qq { }; if (!$CONFIG{ANONYMIZE}) { print qq { }; } print qq{ } if ($topdest); print "
$TRANSLATE{'Postgrey Statistics'} (top $CONFIG{TOP})
$TRANSLATE{'Top Reasons'}$topreason
$TRANSLATE{'Top Domains'}$topdomain
$TRANSLATE{'Top Relays'}$toprelay
$TRANSLATE{'Top Senders'}$topemail
$TRANSLATE{'Top Recipients Address'}$topdest
\n

 

"; } } sub get_detail_stat { my ($hostname, $date, $hour, $type, $peri, $search) = @_; my $path = $CONFIG{OUT_DIR} . '/' . $hostname . '/' . $date; $path =~ s/(\d{4})(\d{2})(\d{2})$/$1\/$2\/$3\//; $search = '' if ($search eq '<>'); my %lstat = (); if ($type eq 'sender') { %lstat = &get_sender_detail($path, $peri, $search, $hour); } elsif ($type eq 'recipient') { %lstat = &get_recipient_detail($path, $peri, $search, $hour); } elsif ($type eq 'reject') { %lstat = &get_reject_detail($path, $peri, $search, $hour); } elsif ($type eq 'spam') { %lstat = &get_spam_detail($path, $peri, $search, $hour); } elsif ($type =~ /spam_(.*)/) { %lstat = &get_spaminfo_detail($path, $1, $peri, $search, $hour); } elsif ($type eq 'virus') { %lstat = &get_virus_detail($path, $peri, $search, $hour); } elsif ($type eq 'dsn') { %lstat = &get_dsn_detail($path, $peri, $search, $hour); } elsif ($type eq 'dsnsrc') { %lstat = &get_dsnsrc_detail($path, $peri, $search, $hour); } elsif ($type eq 'postgrey') { %lstat = &get_postgrey_detail($path, $peri, $search, $hour); } elsif ($type eq 'flow') { %lstat = &get_flow_detail($path, $peri, $search, $hour, $hostname); } else { print "BAD DETAIL TYPE\n"; } return %lstat; } sub show_detail { my ($hostname, $date, $hour, $type, $peri, $search) = @_; my $path = $CONFIG{OUT_DIR} . '/' . $hostname . '/' . $date; $path =~ s/(\d{4})(\d{2})(\d{2})$/$1\/$2\/$3\//; $search = '' if ($search eq '<>'); my %lstat = &get_detail_stat($hostname, $date, $hour, $type, $peri, $search); my $thedate = $date; $thedate =~ s/(\d{4})(\d{2})(\d{2})/$1-$2-$3/; my $dlink = $ENV{SCRIPT_NAME} . '?download=csv'; my @params = $CGI->param(); foreach my $p (@params) { my $val = $CGI->param($p) || ''; $dlink .= '&' . "$p=" . $CGI->escape($val); } print qq{
$search - [csv]
\n}; if ($type eq 'dsn') { print qq{}; } else { print qq{}; } print qq{}; print qq{} if (($type ne 'dsn') && ($type ne 'postgrey')); print qq{}; print qq{\n}; if ($type !~ /spam|reject|postgrey/) { print qq{\n}; } if ($type !~ /spam/) { print qq{\n}; } if (!grep(/$type/, 'dsn', 'dsnsrc', 'sender', 'recipient', 'postgrey', 'flow')) { if ($type eq 'spam') { print qq{\n}; } elsif ($type =~ /spam_/) { print qq{\n}; } elsif ($type eq 'virus') { print qq{\n}; } else { print qq{\n}; } } if ($CONFIG{SHOW_SUBJECT}) { print qq{\n}; } print "\n"; my $line = 1; foreach my $id (sort { $lstat{$a}{hour} <=> $lstat{$b}{hour} } keys %lstat) { next if ($DOMAIN && ($lstat{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$lstat{$id}{rcpt}})); last if ($line > $CONFIG{MAX_LINE}); $lstat{$id}{hour} =~ s/(\d{2})(\d{2})(\d{2})/$1:$2:$3/; if (length($lstat{$id}{hour}) == 2) { $lstat{$id}{hour} .= ':00:00'; } $lstat{$id}{sender} =~ s/^.*\@/anonymized\@/ if ($CONFIG{ANONYMIZE}); $lstat{$id}{sender_relay} =~ s/_/:/g; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $lstat{$id}{sender_relay} =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } $lstat{$id}{sender} ||= ' '; $lstat{$id}{size} ||= ' '; $lstat{$id}{sender_relay} ||= ' '; print qq{}; if (($type eq 'dsn') && ($type ne 'postgrey')) { $lstat{$id}{srcid} = &detail_link($hostname,$date,'sender','id',$lstat{$id}{srcid}, $hour); print qq{}; } print qq{}; print qq{} if (($type ne 'dsn') && ($type ne 'postgrey')); print qq{}; print ""; if ($type !~ /spam|reject|postgrey/) { print ""; } if ($type !~ /spam/) { print ""; } if (!grep(/$type/, 'dsn', 'dsnsrc', 'sender', 'recipient', 'postgrey', 'flow')) { if ($type eq 'spam') { $lstat{$id}{spam} ||= ' '; print qq{}; } elsif ($type =~ /spam_/) { $lstat{$id}{spam} ||= ' '; $lstat{$id}{score} ||= ' '; $lstat{$id}{cache} ||= ' '; $lstat{$id}{autolearn} ||= ' '; $lstat{$id}{spam} = $CGI->unescape(&decode_str($lstat{$id}{spam})); print qq{}; } elsif ($type eq 'virus') { $lstat{$id}{virus} ||= ' '; print qq{}; } else { $lstat{$id}{rule} ||= ' '; print qq{}; } } if ($CONFIG{SHOW_SUBJECT}) { $lstat{$id}{subject} ||= ' '; print qq{}; } print "\n"; $line++; } print "
 $TRANSLATE{'Hour'}$TRANSLATE{'Original Id'}IdId$TRANSLATE{'Sender'}$TRANSLATE{'Size'}$TRANSLATE{'Sender Relay'}$TRANSLATE{'Recipients'}$TRANSLATE{'Recipient Relay'}$TRANSLATE{'Status'}$TRANSLATE{'Spam'}$TRANSLATE{'Score'}$TRANSLATE{'Cache'}$TRANSLATE{'Autolearn'}$TRANSLATE{'Spam'}$TRANSLATE{'Virus'}$TRANSLATE{'File'}$TRANSLATE{'Rule'}$TRANSLATE{'Subject'}
$line$thedate $lstat{$id}{hour}$id$lstat{$id}{srcid}$lstat{$id}{sender}$lstat{$id}{size}$lstat{$id}{sender_relay}"; if (defined $lstat{$id}{chck_rcpt}) { if ($CONFIG{ANONYMIZE}) { map { s/^.*\@/anonymized\@/ } @{$lstat{$id}{rcpt}}; } if ($#{$lstat{$id}{chck_rcpt}} > 0) { if (grep(!/$lstat{$id}{chck_rcpt}[0]/, @{$lstat{$id}{chck_rcpt}})) { my $onchange = "document.getElementById('relay_$id').selectedIndex = this.selectedIndex; document.getElementById('status_$id').selectedIndex = this.selectedIndex;"; print "\n"; } else { print $lstat{$id}{chck_rcpt}[0], '(x', ($#{$lstat{$id}{chck_rcpt}}+1), ')'; } } else { print $lstat{$id}{chck_rcpt}[0]; } } elsif (defined $lstat{$id}{rcpt}) { if ($CONFIG{ANONYMIZE}) { map { s/^.*\@/anonymized\@/ } @{$lstat{$id}{rcpt}}; } if ($#{$lstat{$id}{rcpt}} > 0) { if (grep(!/$lstat{$id}{rcpt}[0]/, @{$lstat{$id}{rcpt}})) { my $onchange = "document.getElementById('relay_$id').selectedIndex = this.selectedIndex; document.getElementById('status_$id').selectedIndex = this.selectedIndex;"; print "\n"; } else { print $lstat{$id}{rcpt}[0], '(x', ($#{$lstat{$id}{rcpt}}+1), ')'; } } else { print $lstat{$id}{rcpt}[0]; } } else { print " " } print ""; if (defined $lstat{$id}{rcpt_relay} && ($#{$lstat{$id}{rcpt_relay}} >= 0)) { map { s/_/:/g; } @{$lstat{$id}{rcpt_relay}}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { map { s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g } @{$lstat{$id}{rcpt_relay}}; } } if ( ($#{$lstat{$id}{rcpt_relay}} > 0) && grep(!/$lstat{$id}{rcpt_relay}[0]/, @{$lstat{$id}{rcpt_relay}})) { my $onchange = "document.getElementById('rcpt_$id').selectedIndex = this.selectedIndex; document.getElementById('status_$id').selectedIndex = this.selectedIndex;"; print "\n"; } else { print $lstat{$id}{rcpt_relay}[0]; } } else { print " " } print ""; if (defined $lstat{$id}{chck_status}) { if ( ($#{$lstat{$id}{chck_status}} > 0) && grep(!/$lstat{$id}{chck_status}[0]/, @{$lstat{$id}{chck_status}})) { my $onchange = "document.getElementById('rcpt_$id').selectedIndex = this.selectedIndex; document.getElementById('relay_$id').selectedIndex = this.selectedIndex;"; print "\n"; } else { print $lstat{$id}{chck_status}[0]; } } elsif (defined $lstat{$id}{status}) { if ( ($#{$lstat{$id}{status}} > 0) && grep(!/$lstat{$id}{status}[0]/, @{$lstat{$id}{status}})) { my $onchange = "document.getElementById('rcpt_$id').selectedIndex = this.selectedIndex; document.getElementById('relay_$id').selectedIndex = this.selectedIndex;"; print "\n"; } else { print $lstat{$id}{status}[0]; } } elsif (defined $lstat{$id}{reason}) { print $lstat{$id}{reason}; } else { print " " } print "$lstat{$id}{spam}$lstat{$id}{score}$lstat{$id}{cache}$lstat{$id}{autolearn}$lstat{$id}{spam}$lstat{$id}{virus}$lstat{$id}{file}$lstat{$id}{rule}$lstat{$id}{subject}
\n
\n"; } sub show_download_detail { my ($hostname, $date, $hour, $type, $peri, $search) = @_; my $path = $CONFIG{OUT_DIR} . '/' . $hostname . '/' . $date; $path =~ s/(\d{4})(\d{2})(\d{2})$/$1\/$2\/$3\//; $search = '' if ($search eq '<>'); my %lstat = &get_detail_stat($hostname, $date, $hour, $type, $peri, $search); my $thedate = $date; $thedate =~ s/(\d{4})(\d{2})(\d{2})/$1-$2-$3/; my $filename = "$hostname-$date"; $filename .= "-$hour" if ($hour); $filename .= "-detailed-$type-($search).csv"; print "Content-Type:application/x-download\n"; print "Content-Disposition:attachment;filename=$filename\n\n"; print "Num;$TRANSLATE{'Hour'};"; if ($type eq 'dsn') { print "$TRANSLATE{'Original Id'};"; } print "Id;$TRANSLATE{'Sender'};"; print "$TRANSLATE{'Size'};" if (($type ne 'dsn') && ($type ne 'postgrey')); print "$TRANSLATE{'Sender Relay'};$TRANSLATE{'Recipients'};"; if ($type !~ /spam|reject|postgrey/) { print "$TRANSLATE{'Recipient Relay'};"; } if ($type !~ /spam/) { print "$TRANSLATE{'Status'};"; } if (!grep(/$type/, 'dsn', 'dsnsrc', 'sender', 'recipient', 'postgrey', 'flow')) { if ($type eq 'spam') { print "$TRANSLATE{'Spam'};"; } elsif ($type =~ /spam_/) { print "$TRANSLATE{'Score'};$TRANSLATE{'Cache'};$TRANSLATE{'Autolearn'};$TRANSLATE{'Spam'};"; } elsif ($type eq 'virus') { print "$TRANSLATE{'Virus'};$TRANSLATE{'File'};"; } else { print "$TRANSLATE{'Rule'};"; } } if ($CONFIG{SHOW_SUBJECT}) { print "$TRANSLATE{'Subject'};"; } print "\n"; my $line = 1; foreach my $id (sort { $lstat{$a}{hour} <=> $lstat{$b}{hour} } keys %lstat) { next if ($DOMAIN && ($lstat{$id}{sender} !~ /$DOMAIN/) && !grep(/$DOMAIN/, @{$lstat{$id}{rcpt}})); last if ($line > $CONFIG{MAX_LINE}); $lstat{$id}{hour} =~ s/(\d{2})(\d{2})(\d{2})/$1:$2:$3/; if (length($lstat{$id}{hour}) == 2) { $lstat{$id}{hour} .= ':00:00'; } $lstat{$id}{sender} =~ s/^.*\@/anonymized\@/ if ($CONFIG{ANONYMIZE}); if (exists $CONFIG{REPLACE_HOST}) { $lstat{$id}{sender_relay} =~ s/_/:/g; foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { $lstat{$id}{sender_relay} =~ s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g; } } print "$line;$thedate $lstat{$id}{hour};$id;"; if (($type eq 'dsn') && ($type ne 'postgrey')) { $lstat{$id}{srcid} = &detail_link($hostname,$date,'sender','id',$lstat{$id}{srcid}, $hour); print "$lstat{$id}{srcid};"; } print "$lstat{$id}{sender};"; print "$lstat{$id}{size};" if (($type ne 'dsn') && ($type ne 'postgrey')); print "$lstat{$id}{sender_relay};"; if (defined $lstat{$id}{chck_rcpt}) { if ($CONFIG{ANONYMIZE}) { map { s/^.*\@/anonymized\@/ } @{$lstat{$id}{rcpt}}; } if ($#{$lstat{$id}{chck_rcpt}} > 0) { if (grep(!/$lstat{$id}{chck_rcpt}[0]/, @{$lstat{$id}{chck_rcpt}})) { for (my $i = 0; $i <= $#{$lstat{$id}{chck_rcpt}}; $i++) { print "$lstat{$id}{chck_rcpt}[$i],"; } } else { print $lstat{$id}{chck_rcpt}[0]; } } else { print $lstat{$id}{chck_rcpt}[0]; } } elsif (defined $lstat{$id}{rcpt}) { if ($CONFIG{ANONYMIZE}) { map { s/^.*\@/anonymized\@/ } @{$lstat{$id}{rcpt}}; } if ($#{$lstat{$id}{rcpt}} > 0) { if (grep(!/$lstat{$id}{rcpt}[0]/, @{$lstat{$id}{rcpt}})) { for (my $i = 0; $i <= $#{$lstat{$id}{rcpt}}; $i++) { print "$lstat{$id}{rcpt}[$i],"; } } else { print $lstat{$id}{rcpt}[0]; } } else { print $lstat{$id}{rcpt}[0]; } } print ";"; if ($type !~ /spam|reject|postgrey/) { if (defined $lstat{$id}{rcpt_relay} && ($#{$lstat{$id}{rcpt_relay}} >= 0)) { map { s/_/:/g; } @{$lstat{$id}{rcpt_relay}}; if (exists $CONFIG{REPLACE_HOST}) { foreach my $pat (keys %{$CONFIG{REPLACE_HOST}}) { map { s/$pat/$CONFIG{REPLACE_HOST}{$pat}/g } @{$lstat{$id}{rcpt_relay}}; } } if ( ($#{$lstat{$id}{rcpt_relay}} > 0) && grep(!/$lstat{$id}{rcpt_relay}[0]/, @{$lstat{$id}{rcpt_relay}})) { for (my $i = 0; $i <= $#{$lstat{$id}{rcpt_relay}}; $i++) { print "$lstat{$id}{rcpt_relay}[$i],"; } } else { print $lstat{$id}{rcpt_relay}[0]; } } print ";" } if ($type !~ /spam/) { if (defined $lstat{$id}{chck_status}) { if ( ($#{$lstat{$id}{chck_status}} > 0) && grep(!/$lstat{$id}{chck_status}[0]/, @{$lstat{$id}{chck_status}})) { for (my $i = 0; $i <= $#{$lstat{$id}{chck_status}}; $i++) { print "$lstat{$id}{chck_status}[$i],"; } } else { print $lstat{$id}{chck_status}[0]; } } elsif (defined $lstat{$id}{status}) { if ( ($#{$lstat{$id}{status}} > 0) && grep(!/$lstat{$id}{status}[0]/, @{$lstat{$id}{status}})) { for (my $i = 0; $i <= $#{$lstat{$id}{status}}; $i++) { print "$lstat{$id}{status}[$i],"; } } else { print $lstat{$id}{status}[0]; } } elsif (defined $lstat{$id}{reason}) { print $lstat{$id}{reason}; } print ";" } if (!grep(/$type/, 'dsn', 'dsnsrc', 'sender', 'recipient', 'postgrey', 'flow')) { if ($type eq 'spam') { print "$lstat{$id}{spam};"; } elsif ($type =~ /spam_/) { print "$lstat{$id}{score};$lstat{$id}{cache};$lstat{$id}{autolearn};$lstat{$id}{spam};"; } elsif ($type eq 'virus') { print "$lstat{$id}{virus};$lstat{$id}{file};"; } else { print "$lstat{$id}{rule};"; } } if ($CONFIG{SHOW_SUBJECT}) { print "$lstat{$id}{subject};"; } print "\n"; $line++; } } sub get_sender_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; if ($peri eq 'domain') { if (!$search) { next if ($data[2] ne '<>'); } else { next if ($data[2] !~ /$search/); } } elsif ($peri eq 'relay') { next if ($data[5] ne $search); } elsif ($peri eq 'address') { if (!$search) { next if ($data[2] ne '<>'); } else { next if ($data[2] ne $search); } } elsif ( ($peri eq 'topmax') || ($peri eq 'id')) { next if ($data[1] ne $search); } $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$path/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[3] = &clean_relay($data[3]); if ($data[4] !~ /Queued/) { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } } close(IN); } $file = "$path/rejected.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Rule:Relay:Arg1:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[3] = &clean_relay($data[3]); $local_stat{$data[1]}{rule} = $data[2]; $local_stat{$data[1]}{sender_relay} = $data[3] if (!$local_stat{$data[1]}{sender_relay}); if ($#data > 4) { if ($data[2] eq 'check_relay') { $local_stat{$data[1]}{sender_relay} = $data[4]; } elsif ($data[2] eq 'check_rcpt') { push(@{$local_stat{$data[1]}{chck_rcpt}}, $data[4]); } else { # $data[2] eq 'check_mail' or POSTFIX $local_stat{$data[1]}{sender} = $data[4]; } push(@{$local_stat{$data[1]}{chck_status}}, $data[5]); } else { push(@{$local_stat{$data[1]}{chck_status}}, $data[4]); } } close(IN); } $file = "$path/spam.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:From:To:Spam my @data = split(/:/, $l, 5); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{sender} = $data[2] if (!exists $local_stat{$data[1]}{sender}); foreach my $a (split(/,/, $data[3])) { if (!grep(/^$a$/i, @{$local_stat{$data[1]}{rcpt}})) { push(@{$local_stat{$data[1]}{rcpt}}, $a); } } $local_stat{$data[1]}{spam} = $data[4]; } close(IN); } $file = "$path/virus.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:file:virus my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{file} = $data[2]; $local_stat{$data[1]}{virus} = $data[3]; } close(IN); } $file = "$path/syserr.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Message my @data = split(/:/, $l, 3); next if ($data[2] !~ /\s/); # Skip single word error $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $l =~ /^\d+:([^\:]+):(.*)/; shift(@data); shift(@data); $local_stat{$1}{error} = join(':', @data); } close(IN); } return %local_stat; } sub get_recipient_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); $data[3] ||= '<>'; if ($peri eq 'domain') { next if ($data[2] !~ /$search/); } elsif ($peri eq 'relay') { next if ($data[3] ne $search); } elsif ($peri eq 'address') { next if ($data[2] ne $search); } if ($data[4] =~ /Sent/) { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } } close(IN); } # Format: Hour:Id:Sender:Size:Nrcpts:Relay:subject $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); # If we have ipv6 address $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$path/rejected.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Rule:Relay:Arg1:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $local_stat{$data[1]}{rule} = $data[2]; $data[3] = &clean_relay($data[3]); $local_stat{$data[1]}{sender_relay} = $data[3] if (!$local_stat{$data[1]}{sender_relay}); if ($#data > 4) { if ($data[2] eq 'check_relay') { $local_stat{$data[1]}{sender_relay} = $data[4]; } elsif ($data[2] eq 'check_rcpt') { push(@{$local_stat{$data[1]}{chck_rcpt}}, $data[4]); } else { # $data[2] eq 'check_mail' or POSTFIX $local_stat{$data[1]}{sender} = $data[4]; } push(@{$local_stat{$data[1]}{chck_status}}, $data[5]); } else { push(@{$local_stat{$data[1]}{chck_status}}, $data[4]); } } close(IN); } $file = "$path/spam.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:From:To:Spam my @data = split(/:/, $l, 5); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{sender} = $data[2] if (!exists $local_stat{$data[1]}{sender}); foreach my $a (split(/,/, $data[3])) { if (!grep(/^$a$/i, @{$local_stat{$data[1]}{rcpt}})) { push(@{$local_stat{$data[1]}{rcpt}}, $a); } } $local_stat{$data[1]}{spam} = $data[4]; } close(IN); } $file = "$path/virus.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:file:virus my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{file} = $data[2]; $local_stat{$data[1]}{virus} = $data[3]; } close(IN); } $file = "$path/syserr.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Message my @data = split(/:/, $l, 3); next if ($data[2] !~ /\s/); # Skip single word error $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); shift(@data); my $id = shift(@data); $local_stat{$id}{error} = join(':', @data); } close(IN); } return %local_stat; } sub get_reject_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/rejected.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Rule:Relay:Arg1:Status my @data = split(/:/, $l, 6); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); if ($peri eq 'rule') { next if ($data[2] !~ /$search/); } elsif ($peri eq 'domain') { next if ( ($data[4] !~ /$search/) && ($data[3] !~ /$search/) ); } elsif ($peri eq 'address') { next if ( ($data[4] !~ /$search/) && ($data[3] !~ /$search/) ); } elsif ($peri eq 'relay') { next if ($data[3] ne $search); } elsif ($peri eq 'status') { next if ($data[-1] !~ /$search/); } $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{rule} = $data[2]; $local_stat{$data[1]}{sender_relay} = $data[3]; if ($#data > 4) { if ($data[2] eq 'check_relay') { $local_stat{$data[1]}{sender_relay} = $data[4]; } elsif ($data[2] eq 'check_rcpt') { push(@{$local_stat{$data[1]}{chck_rcpt}}, $data[4]); } elsif ($data[5] eq 'postscreen reject' && $data[4] ne '') { $data[5] .= " (score: $data[4])"; } else { # $data[2] eq 'check_mail' or POSTFIX $local_stat{$data[1]}{sender} = $data[4]; } push(@{$local_stat{$data[1]}{chck_status}}, $data[5]); } else { push(@{$local_stat{$data[1]}{chck_status}}, $data[4]); } } close(IN); } $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; if ($peri eq 'domain') { if ($data[2] !~ /$search/) { delete $local_stat{$data[1]}; next; } } elsif ($peri eq 'address') { if ($data[2] ne $search) { delete $local_stat{$data[1]}; next; } } elsif ($peri eq 'relay') { if ($data[5] ne $search) { delete $local_stat{$data[1]}; next; } } $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } return %local_stat; } sub get_spam_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/spam.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:From:To:Spam my @data = split(/:/, $l, 5); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); if ($peri eq 'rule') { next if ($data[4] ne $search); } elsif ($peri eq 'sender') { next if ($data[2] ne $search); } elsif ($peri eq 'domain') { next if ($data[2] !~ /$search/); } elsif ($peri eq 'recipient') { next if ($data[3] ne $search); } $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{sender} = $data[2] if (!exists $local_stat{$data[1]}{sender}); $local_stat{$data[1]}{spam} = $data[4]; foreach my $a (split(/,/, $data[3])) { if (!grep(/^$a$/i, @{$local_stat{$data[1]}{rcpt}})) { push(@{$local_stat{$data[1]}{rcpt}}, $a); } } } close(IN); } $file = "$path/recipient.dat"; if (open(IN, $file)) { my $i = 0; while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[3] = &clean_relay($data[3]); if ($peri eq 'recipient') { next if ($data[2] !~ /$search/); } if (($data[4] !~ /Queued/) && !grep(/^$data[2]$/, @{$local_stat{$data[1]}{rcpt}}) ) { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } } close(IN); } $file = "$path/senders.dat"; if (open(IN, $file)) { my $i = 0; while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; if ($peri eq 'relay') { next if ($data[5] !~ /$search/); } $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } foreach my $id (keys %local_stat) { if ($peri eq 'relay') { if (!grep(/$search/, @{$local_stat{$id}{rcpt_relay}}) && ($local_stat{$id}{sender_relay} !~ /$search/) ) { delete($local_stat{$id}); } } } return %local_stat; } sub get_spaminfo_detail { my ($path, $type, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/$type.dat"; open(IN, $file) || return; while (my $l = ) { chomp($l); # Format: Hour:Id:type:score:cache:autolearn:spam my @data = split(/:/, $l, 7); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); if ($peri eq 'rule') { next if ($data[6] ne $search); } elsif ($peri eq 'score') { next if ($data[3] ne $search); } elsif ($peri eq 'autolearn') { next if ($data[5] ne $search); } elsif ($peri eq 'cache') { next if ($data[4] ne $search); } $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{score} = $data[3]; $local_stat{$data[1]}{spam} = $data[6]; $local_stat{$data[1]}{cache} = $data[4]; $local_stat{$data[1]}{autolearn} = $data[5]; } close(IN); $file = "$path/spam.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:From:To:Spam my @data = split(/:/, $l, 5); next if (!exists $local_stat{$data[1]}); $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{spam} = "$data[4]: " . $local_stat{$data[1]}{spam}; foreach my $a (split(/,/, $data[3])) { if (!grep(/^$a$/i, @{$local_stat{$data[1]}{rcpt}})) { push(@{$local_stat{$data[1]}{rcpt}}, $a); } } } close(IN); } $file = "$path/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); next if (!exists $local_stat{$data[1]}); $data[3] = &clean_relay($data[3]); if ($data[4] !~ /Queued/) { if (!grep(/^$data[2]$/i, @{$local_stat{$data[1]}{rcpt}})) { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } } } close(IN); } $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); next if (!exists $local_stat{$data[1]}); $data[5] = &clean_relay($data[5]); $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } return %local_stat; } sub get_virus_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/virus.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:file:virus my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); if ($peri eq 'virus') { next if ($data[3] !~ /$search/); } elsif ($peri eq 'file') { next if ($data[2] !~ /$search/); } $local_stat{$data[1]}{hour} = $data[0] if (!exists $local_stat{$data[1]}{hour}); $local_stat{$data[1]}{file} = $data[2]; $local_stat{$data[1]}{virus} = $data[3]; } close(IN); } $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; if ($peri eq 'sender') { next if ($data[3] ne $search); } elsif ($peri eq 'relay') { next if ($data[2] ne $search); } $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$path/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[3] = &clean_relay($data[3]); if ($peri eq 'recipient') { next if ($data[2] !~ /$search/); } if ($data[4] !~ /Queued/) { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } else { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); } } close(IN); } return %local_stat; } sub get_dsn_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/dsn.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:SourceId:Status my @data = split(/:/, $l, 4); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); if ($peri eq 'dsnstatus') { next if ($data[3] !~ /$search/); } $local_stat{$data[2]}{hour} = $data[0] if (!exists $local_stat{$data[2]}{hour}); push(@{$local_stat{$data[2]}{dsnstatus}}, $data[3]); $local_stat{$data[2]}{srcid} = $data[1]; } close(IN); } $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$path/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); next if (!exists $local_stat{$data[1]}); $data[3] = &clean_relay($data[3]); next if ($data[4] =~ /Queued/); push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } close(IN); } foreach my $k (keys %local_stat) { delete $local_stat{$k} if (!exists $local_stat{$k}{dsnstatus}); } return %local_stat; } sub get_postgrey_detail { my ($path, $peri, $search, $hour) = @_; my %local_stat = (); my $file = "$path/postgrey.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Relay:From:To:Action:Reason my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[2] = &clean_relay($data[2]); if ($peri eq 'relay') { next if ($data[2] !~ /$search/); } elsif ($peri eq 'address') { next if ($data[3] ne $search); } elsif ($peri eq 'recipient') { next if ($data[4] !~ /$search/); } elsif ($peri eq 'reason') { next if ($data[-1] !~ /$search/); } elsif ($peri eq 'domain') { next if ($data[3] !~ /$search/); } $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender_relay} = $data[2]; $local_stat{$data[1]}{sender} = $data[3]; push(@{$local_stat{$data[1]}{rcpt}}, $data[4]); $local_stat{$data[1]}{action} = $data[5]; $local_stat{$data[1]}{reason} = $data[6]; } close(IN); } return %local_stat; } sub get_flow_detail { my ($path, $peri, $search, $hour, $hostname) = @_; my %local_stat = (); my $file = "$path/senders.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:Sender:Size:Nrcpts:Relay:Subject my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[5] = &clean_relay($data[5]); $data[2] ||= '<>'; $local_stat{$data[1]}{hour} = $data[0]; $local_stat{$data[1]}{sender} = $data[2]; $local_stat{$data[1]}{size} = $data[3]; $local_stat{$data[1]}{nrcpt} = $data[4]; $local_stat{$data[1]}{sender_relay} = $data[5]; for (my $i = 6; $i <= $#data; $i++) { $local_stat{$data[1]}{subject} .= ($i > 6) ? ':' : ''; $local_stat{$data[1]}{subject} .= $data[$i]; } } close(IN); } $file = "$path/recipient.dat"; if (open(IN, $file)) { while (my $l = ) { chomp($l); # Format: Hour:Id:recipient:Relay:Status my @data = split(/:/, $l); $data[0] =~ /^(\d{2})/; next if (($hour ne '') && ($1 != $hour)); $data[3] = &clean_relay($data[3]); if ($data[4] eq 'Sent') { my $direction = &set_direction($local_stat{$data[1]}{sender_relay}, $data[3], $hostname); if ($direction eq $search) { push(@{$local_stat{$data[1]}{rcpt}}, $data[2]); push(@{$local_stat{$data[1]}{rcpt_relay}}, $data[3]); push(@{$local_stat{$data[1]}{status}}, $data[4]); } } } close(IN); # Eliminate unwanted sender only entries foreach my $k (keys %local_stat) { delete $local_stat{$k} if (!exists $local_stat{$k}{rcpt} || $#{$local_stat{$k}{rcpt}} == -1); } } return %local_stat; } sub check_auth { # Allow if site with no auth or user is an admin return 1 if (!$ENV{REMOTE_USER} || !$CONFIG{ADMIN} || grep(/^$ENV{REMOTE_USER}$/, split(/[\s\t,;]+/, $CONFIG{ADMIN}))); # If the domain is not in the domain list if ($DOMAIN && grep(/^$DOMAIN$/, @{$CONFIG{DOMAIN_USER}{$ENV{REMOTE_USER}}}) ) { return 1; } else { &logerror("Access denied."); } return 0; } sub decode_str { my ($str) = @_; $str =~ s/=\?(.*?)\?Q\?(.*?)\?=/decode_qp($2)/ige; $str =~ s/=\?(.*?)\?B\?(.*?)\?=/decode_base64($2)/ige; return $str; } #### # Display all host statistics collected #### sub get_list_host { if (not opendir(DIR, "$CONFIG{OUT_DIR}")) { &logerror("Can't open directory $CONFIG{OUT_DIR}: $!\n"); return; } my @dirs = grep { !/^\./ && -d "$CONFIG{OUT_DIR}/$_" } readdir(DIR); closedir(DIR); return @dirs; } #### # Normalyze CGI parameters. #### sub secure_params { if ($HOST && ($HOST =~ s/[^a-z0-9\-\_\.\@]//ig)) { return "host: $HOST"; } elsif ($CURDATE && ($CURDATE =~ s/[^0-9\/]//g)) { return "date: $CURDATE"; } elsif ($DOMAIN && ($DOMAIN =~ s/[^a-z0-9\-\_\.\@]//ig)) { return "domain: $DOMAIN"; } elsif ($TYPE && ($TYPE =~ s/[^a-z\_]//ig)) { return "type: $TYPE"; } elsif ($HOUR && ($HOUR =~ s/[^0-9]//g)) { return "hour: $HOUR"; } elsif ($LANG && ($LANG =~ s/[^a-z\_]//ig)) { return "lang: $LANG"; } elsif ($VIEW && ($VIEW =~ s/[^a-z]//g)) { return "view: $VIEW"; } elsif ($PERI && ($PERI =~ s/[^a-z]//g)) { return "peri: $PERI"; } return ''; } #### # Return current date of the form YYYY/MM/DD #### sub get_curdate { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); $mon++; $mon = sprintf("%02d", $mon); $mday = sprintf("%02d", $mday); $year += 1900; return "$year/$mon/$mday" } #### # Use to return a flotr2 javascript code to draw graph. #### sub grafit { my (%params) = @_; my $data1 = ''; my $data2 = ''; my @xdata = split(/:/, $params{labels}); my @ydata = split(/:/, $params{values}); my @std_day = qw(Su Mo Tu We Th Fr Sa); my %day_lbl = (); if (exists $TRANSLATE{WeekDay}) { my @tmpwday = split(/\s+/, $TRANSLATE{WeekDay}); for (my $i = 0; $i <= $#std_day; $i++) { $day_lbl{$std_day[$i]} = $tmpwday[$i]; } } else { for (my $i = 0; $i <= $#std_day; $i++) { $day_lbl{$std_day[$i]} = $std_day[$i]; } } my @wdays = ('Mo','Tu','We','Th','Fr','Sa','Su'); for (my $i = 0; $i <= $#xdata; $i++) { if ($#xdata == 6) { $data1 .= "[$i,$ydata[$i]],"; $wdays[$i] = "$day_lbl{$wdays[$i]}-$xdata[$i]"; } else { $data1 .= "['$xdata[$i]',$ydata[$i]],"; } } $data1 =~ s/,$//; if ($params{values1}) { @ydata = split(/:/, $params{values1}); for (my $i = 0; $i <= $#xdata; $i++) { if ($#xdata == 6) { $data2 .= "[$i,$ydata[$i]],"; } else { $data2 .= "['$xdata[$i]',$ydata[$i]],"; } } $data2 =~ s/,$//; } $data1 = "var d1 = [$data1];"; $data2 = "var d2 = [$data2];"; my $xlabel = ''; my $numticks = $#xdata + 1; if ($HOUR) { $xlabel = qq{tickFormatter: function(x) { var x = parseInt(x); return x; }, }; } elsif ($#xdata == 11) { $xlabel = qq{tickFormatter: function(x) { var x = parseInt(x); var months = [ "$TRANSLATE{'01'}", "$TRANSLATE{'02'}", "$TRANSLATE{'03'}", "$TRANSLATE{'04'}", "$TRANSLATE{'05'}", "$TRANSLATE{'06'}", "$TRANSLATE{'07'}", "$TRANSLATE{'08'}", "$TRANSLATE{'09'}", "$TRANSLATE{'10'}", "$TRANSLATE{'11'}", "$TRANSLATE{'12'}" ]; return months[(x -1) % 12]; }, }; } elsif ($#xdata == 30) { $xlabel = qq{tickFormatter: function(x) { var x = parseInt(x); var days = ['01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23','24','25','26','27','28','29','30','31']; return days[(x - 1) % 31]; }, }; } elsif ($#xdata <= 6) { my $lbls = join("','", @wdays); $xlabel = qq{tickFormatter: function(x) { var x = parseInt(x); var wdays = new Array('$lbls'); return wdays[x]; }, }; } else { $xlabel = qq{tickFormatter: function(x) { var x = parseInt(x); var hours = ['00','01','02','03','04','05','06','07','08','09','10','11','12','13','14','15','16','17','18','19','20','21','22','23']; return hours[x % 24]; }, }; } $params{width} ||= 400; $params{height} ||= 200; return < #$params{divid} { width : $params{width}px; height: $params{height}px; background:#F3F2ED; border:10px solid white; padding:0 10px; margin:30px 10px 30px 10px; border-radius:10px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; }
EOF } sub grafit_pie { my (%params) = @_; my @datadef = (); my @contdef = (); my $i = 1; foreach my $k (sort {$params{values}->{$b} <=> $params{values}->{$a}} keys %{$params{values}}) { push(@datadef, "var d$i = [ [0,$params{values}->{$k}] ];\n"); push(@contdef, "{ data: d$i, label: \"$k\" },\n"); $i++; } $params{width} ||= 400; $params{height} ||= 200; return < #$params{divid} { width : $params{width}px; height: $params{height}px; background:#F3F2ED; border:10px solid white; padding:0 10px; margin:30px 10px 30px 10px; border-radius:10px; -moz-border-radius:10px; -webkit-border-radius:10px; box-shadow:3px 3px 6px 2px #A9A9A9; -moz-box-shadow:3px 3px 6px 2px #A9A9A9; -webkit-box-shadow:3px 3px 6px #A9A9A9; }
EOF } #### # Fix relay not sanityzed in sendmailanalyzer script #### sub clean_relay { my $relay = shift; if ( $relay =~ s/\(([a-fA-F0-9\.\:]+)\)// ) { $relay = $1; } $relay =~ s/:/_/g; # fix ipv6 to remove data field separator return $relay; } sub grafit_hbar { my (%params) = @_; my $hbar_graph = ''; foreach my $k (sort {$params{values}->{$b} <=> $params{values}->{$a}} keys %{$params{values}}) { my $name = $k || '<>'; $hbar_graph .= "
$name" . sprintf("%.2f", $params{values}->{$k}) . " %" . ("█" x int($params{values}->{$k})) . "
$hbar_graph
$params{title}
 

Others: sum of relays representing less than $MIN_SHOW_PIE %

EOF }