#!/usr/bin/perl #*********************************************************************** # # mdfdb-clean # # mimedefang-filter database info lister # # $Id: mdfdb-info.pl,v 1.27 2007/10/17 16:43:27 jonas Exp $ # # This program may be distributed under the terms of the GNU General # Public License, Version 2, or (at your option) any later version. # #*********************************************************************** #*********************************************************************** # Code... #*********************************************************************** use strict; use DBI; use Date::Format; my $sqldb; my $sqldbd = '?'; my $sqlr; my $max = -1; my $all = 0; #*********************************************************************** # Config. #*********************************************************************** my %Features; $Features{'Path:SPOOLDIR'} = '/var/spool/MIMEDefang'; # Add setting to config parser my %cfgcfg = (); $cfgcfg{'@i'}{i} = 0; $cfgcfg{'@i'}{c} = 0; sub add_cfg_cfg { my ($c,$v,$d,$t,$f) = @_; $d = '' unless (defined($d)); $t = 's' unless ($t); $f = '' unless ($f); $c =~ s/[-_]+//g; $c = lc($c); $$v = $d; $cfgcfg{'@i'}{c} ++; $cfgcfg{$c}{v} = $v; $cfgcfg{$c}{t} = lc($t); $cfgcfg{$c}{f} = lc($f); $cfgcfg{$c}{i} = $cfgcfg{'@i'}{c}; $cfgcfg{$c}{x} = 0; } use vars qw($gdb_black $gdb_grey $gdb_white $gdb_host_white $nsdb_expire $rdb_expire $dc_time_window $dc_limit $out_expire $gsnd_expire); add_cfg_cfg('gdb_black',\$gdb_black,3*60,'i'); add_cfg_cfg('gdb_grey',\$gdb_grey,72*60*60,'i'); add_cfg_cfg('gdb_white',\$gdb_white,36*24*60*60,'i'); add_cfg_cfg('gdb_host_white',\$gdb_host_white,7*24*60*60,'i'); add_cfg_cfg('nsdb_expire',\$nsdb_expire,30*24*60*60,'i'); add_cfg_cfg('rdb_expire',\$rdb_expire,30*24*60*60,'i'); add_cfg_cfg('dc_time_window',\$dc_time_window,3*60,'i'); add_cfg_cfg('dc_limit',\$dc_limit,10,'i'); add_cfg_cfg('out_expire',\$out_expire,30*24*60*60,'i'); add_cfg_cfg('gsnd_expire',\$gsnd_expire,30*24*60*60,'i'); use vars qw($smtp_cache_good $smtp_cache_fail $smtp_cache_bad); add_cfg_cfg('smtp_cache_good',\$smtp_cache_good,60*60,'i'); add_cfg_cfg('smtp_cache_fail',\$smtp_cache_fail,10*60,'i'); add_cfg_cfg('smtp_cache_bad',\$smtp_cache_bad,60,'i'); use vars qw($expn_cache); add_cfg_cfg('expn_cache',\$expn_cache,20*60,'i'); use vars qw($pgp_expire $pgp_expire_bad); add_cfg_cfg('pgp_expire',\$pgp_expire,14*24*60*60,'i'); add_cfg_cfg('pgp_expire_bad',\$pgp_expire_bad,1*24*60*60,'i'); use vars qw($mx_cache_valid $mx_cache_invalid $sc_cache_valid $sc_cache_invalid $sc_cache_unknown $sc_cache_invalid_add $sc_cache_invalid_max); add_cfg_cfg('mx_cache_valid',\$mx_cache_valid,7*24*60*60,'i'); add_cfg_cfg('mx_cache_invalid',\$mx_cache_invalid,60,'i'); add_cfg_cfg('sc_cachevalid',\$sc_cache_valid,7*24*60*60,'i'); add_cfg_cfg('sc_cacheinvalid',\$sc_cache_invalid,60*60,'i'); add_cfg_cfg('sc_cacheunknown',\$sc_cache_unknown,7*24*60*60,'i'); add_cfg_cfg('sc_cacheinvalidadd',\$sc_cache_invalid_add,60*60,'i'); add_cfg_cfg('sc_cacheinvalidmax',\$sc_cache_invalid_max,24*60*60,'i'); use vars qw ($hilo_entries); add_cfg_cfg('hilo_entries',\$hilo_entries,0,'i'); use vars qw($vircache_local $vircache_external $virus_keep); add_cfg_cfg('AVCacheLocal',\$vircache_local,0,'i'); add_cfg_cfg('AVCacheExternal',\$vircache_external,0,'i'); add_cfg_cfg('avc_keep',\$virus_keep,7*24*60*60,'i'); use vars qw($database_spec $database_user $database_pass); add_cfg_cfg('database_spec',\$database_spec,'dbi:SQLite:dbname=%s/filterdata.db','p'); add_cfg_cfg('database_user',\$database_user,'','s'); add_cfg_cfg('database_pass',\$database_pass,'','s'); # Get a file path name sub get_file_path_name { my $f = shift; return $f if ($f =~ /[\/\\]/); foreach my $d (('/usr/local/etc/mimedefang/filter','/etc/mimedefang/filter', '/usr/local/etc/mimedefang','/etc/mimedefang', '/usr/local/etc/mail','/etc/mail', )) { if ($d =~ /mimedefang/) { return "$d/$f" if (-f "$d/$f"); return "$d/mimedefang-$f" if (-f "$d/mimedefang-$f"); } else { return "$d/mimedefang-$f" if (-f "$d/mimedefang-$f"); return "$d/$f" if (-f "$d/$f"); } } return ''; } # read the configuration file sub read_cfg_params { my @pp = @ARGV; @ARGV = (); foreach my $p (@pp) { $p =~ s/^-+//; if ($p =~ /^d(ata)?b(ase)?[-_]?s(pec|pecification)?[:=]\s*(.*?)\s*$/i) { $database_spec = $4; } elsif ($p =~ /^d(ata)?b(ase)?[-_]?u(sr|ser)?[:=]\s*(.*?)\s*$/i) { $database_user = $4; } elsif ($p =~ /^d(ata)?b(ase)?[-_]?p(ass|assword|wd)?[:=]\s*(.*?)\s*$/i) { $database_pass = $4; } else { push @ARGV, $p; } } } sub read_cfg_cfg { my $cfgfn = get_file_path_name('filter.conf'); if ($cfgfn) { die('No filter config!') unless ($cfgfn); $Features{'Path:CONFDIR'} = $cfgfn; $Features{'Path:CONFDIR'} =~ s/(\/filter)?\/[^\/]*$//; die('Cannot read filter config!') unless (open(F,'<',$cfgfn)); #md_syslog('info',"Filter config: $cfgfn"); while (my $l = ) { $l =~ s/[\r\n]+//gs; next unless ($l); next if ($l =~ /^\s*[#;]/); if ($l =~ /^\s*(\S+)\s*?[\s:=]\s*(\S.*?)\s*$/) { my $c = lc($1); my $v = $2; $c =~ s/[-_]+//g; next unless ($c); next if ($c =~ /^\@/); if (defined($cfgcfg{$c})) { if ($cfgcfg{$c}{x} && $cfgcfg{$c}{t} =~ /^m/i) { ${$cfgcfg{$c}{v}} .= ';' if (${$cfgcfg{$c}{v}} ne ''); ${$cfgcfg{$c}{v}} .= $v; } else { ${$cfgcfg{$c}{v}} = $v; $cfgcfg{$c}{x} = 1; } } } } close(F); #return; } else { $Features{'Path:CONFDIR'} = '.'; } read_cfg_params(); $Features{'Path:SPOOLDIR'} = '.' unless ($Features{'Path:SPOOLDIR'} && (-d $Features{'Path:SPOOLDIR'})); my @ck = sort { $cfgcfg{$a}{i} <=> $cfgcfg{$b}{i} } keys %cfgcfg; foreach my $c (@ck) { next if ($c =~ /^\@/); ${$cfgcfg{$c}{v}} = ${$cfgcfg{$cfgcfg{$c}{f}}{v}} if ($cfgcfg{$c}{f} && !${$cfgcfg{$c}{v}}); } foreach my $c (@ck) { next if ($c =~ /^\@/); if ($cfgcfg{$c}{t} eq 'l') { ${$cfgcfg{$c}{v}} = '' unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} =~ s/\s//g; ${$cfgcfg{$c}{v}} =~ s/\./\\./g if (${$cfgcfg{$c}{v}} !~ /\\\./); ${$cfgcfg{$c}{v}} =~ s/\@/\\\@/g if (${$cfgcfg{$c}{v}} !~ /\\\@/); ${$cfgcfg{$c}{v}} =~ s/\./\\./g if (${$cfgcfg{$c}{v}} !~ /\\\./); ${$cfgcfg{$c}{v}} =~ s/,/\|/g if (${$cfgcfg{$c}{v}} !~ /[\(\)\|\{\}]/ && ${$cfgcfg{$c}{v}} =~ /\,/); ${$cfgcfg{$c}{v}} = sprintf('(%s)',${$cfgcfg{$c}{v}}) if (${$cfgcfg{$c}{v}} !~ /[\(\)]/ && ${$cfgcfg{$c}{v}} =~ /\|/); } elsif ($cfgcfg{$c}{t} eq 'a') { ${$cfgcfg{$c}{v}} = '' unless (${$cfgcfg{$c}{v}}); next unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} .= '@'.${$cfgcfg{'myfilterhostname'}{v}} if (${$cfgcfg{$c}{v}} =~ /^[^@]+$/); } elsif ($cfgcfg{$c}{t} eq 't') { ${$cfgcfg{$c}{v}} = '' unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = sprintf(${$cfgcfg{$c}{v}},${$cfgcfg{'myfilterhostname'}{v}}); ${$cfgcfg{$c}{v}} =~ s/[\r\n]*$/\n\n/s; } elsif ($cfgcfg{$c}{t} eq 'b') { ${$cfgcfg{$c}{v}} = 0 unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = 0 if (${$cfgcfg{$c}{v}} =~ /^\s*(false|no?|off|0+)\s*$/); ${$cfgcfg{$c}{v}} = 1 if (${$cfgcfg{$c}{v}}); } elsif ($cfgcfg{$c}{t} eq 'i') { ${$cfgcfg{$c}{v}} = 0 unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = eval(${$cfgcfg{$c}{v}}); } elsif ($cfgcfg{$c}{t} eq 'ps') { ${$cfgcfg{$c}{v}} = $Features{'Path:SPOOLDIR'} unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = sprintf('%s/%s',$Features{'Path:SPOOLDIR'},${$cfgcfg{$c}{v}}) if (${$cfgcfg{$c}{v}} !~ /[\/\\]/); } elsif ($cfgcfg{$c}{t} eq 'pc') { ${$cfgcfg{$c}{v}} = $Features{'Path:CONFDIR'} unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = sprintf('%s/%s',$Features{'Path:CONFDIR'},${$cfgcfg{$c}{v}}) if (${$cfgcfg{$c}{v}} !~ /[\/\\]/); } elsif ($cfgcfg{$c}{t} eq 'p') { ${$cfgcfg{$c}{v}} = $Features{'Path:CONFDIR'} unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = sprintf(${$cfgcfg{$c}{v}},$Features{'Path:SPOOLDIR'}); } elsif ($cfgcfg{$c}{t} eq 'mpsm') { my @pil = split(/\s*;\s*/,${$cfgcfg{$c}{v}}); for (my $i=0;$i<@pil;$i++) { my ($fn,$ft,$fo,$x) = split(/\s*,\s*/,$pil[$i]); $fn = sprintf('%s/%s',${$cfgcfg{'sendmailconfig'}{v}},$fn) if ($fn !~ /[\/\\]/); $fo = '?' unless ($fo); unless ($ft) { if ($fn =~ /table/i) { $ft = 'table' } else { $ft = 'list'; } } $pil[$i] = join(',',$fn,lc($ft),lc($fo)); } ${$cfgcfg{$c}{v}} = join(';',@pil); } elsif ($cfgcfg{$c}{t} eq 'mbs') { ${$cfgcfg{$c}{v}} = 0 unless (${$cfgcfg{$c}{v}}); ${$cfgcfg{$c}{v}} = 0 if (${$cfgcfg{$c}{v}} =~ /^\s*(false|no|n|off|0+)\s*$/); ${$cfgcfg{$c}{v}} = 1 if (${$cfgcfg{$c}{v}} =~ /^\s*(true|yes|y|on|\d*[1-9]\d*)\s*$/); } else { ${$cfgcfg{$c}{v}} = '' unless (${$cfgcfg{$c}{v}}); } } if ($database_spec =~ /sqlite/i) { $sqldbd = 'L'; } elsif ($database_spec =~ /mysql/i) { $sqldbd = 'M'; } } #*********************************************************************** # SQL. #*********************************************************************** sub sql_translate { my ($cmd) = @_; $cmd =~ s/ LIMIT -1$//; return $cmd; } sub sql_command { for (my $i=0;$i<@_;$i++) { my $cmd = sql_translate(@_[$i]); return 0 unless ($sqldb && $cmd); $sqlr = $sqldb->do($cmd); return 0 unless (defined($sqlr)); } return 1; } sub sql_select_one_row { my $cmd = sql_translate(shift); #print "$cmd\n"; return undef unless ($sqldb && $cmd); my $st = $sqldb->prepare("$cmd"); return undef unless ($st); $st->execute; my @res = $st->fetchrow_array; $st->finish; return \@res; } sub sql_select_one { my $res = sql_select_one_row(@_); return undef unless ($res && @{$res}); return $res->[0]; } #*********************************************************************** # Info. #*********************************************************************** sub rdb_list { print "RDB\n"; my %db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM relaylist LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless (@res>1); next unless ($res[0] && $res[1]); next if (!$all && (($rdb_expire>0) && ($res[2] < $now-$rdb_expire))); $res[3] = 0 unless ($res[3]); $res[4] = 0 unless ($res[4]); my %ri = ( a => $res[0], w => $res[1], t => $res[2], s => $res[3], h => $res[4], ); if ($res[4] == 0 && $res[3] > 0) { $ri{r} = 999999999999+$res[3]; } elsif ($res[3] == 0 && $res[4] > 0) { $ri{r} = -$res[4]; } elsif ($res[4] == 0 && $res[3] == 0) { $ri{r} = 0; } else { $ri{r} = $res[3]/$res[4]; $ri{r} = 999999999999 if ($ri{r} > 999999999999); } my $w = '?'; if ($res[0] =~ /^\d+\.\d+\.\d+\.\d+$/) { $w = 'relay'; } elsif ($res[0] =~ /\@/) { $w = 'sender'; } elsif ($res[0] =~ /\S+\.\S+/) { $w = 'domain'; } unless ($db{$w}) { my @l = (); $db{$w} = \@l; } push @{$db{$w}}, \%ri; } $st->finish; return unless (%db); my $tc = 0; foreach my $w (keys %db) { my $c = 0; foreach my $i (sort { $a->{r} <=> $b->{r} } @{$db{$w}}) { my $r = $i->{r}; if ($r >= 999999) { $r = '>'; } elsif ($r < 0) { $r = '<'; } else { $r = sprintf('%.1f',$r); } print sprintf("%-10s %-40s %6u %6u %9s %s %3u %s %3u\n", $w,$i->{a},$i->{h},$i->{s},$r, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), ); $c ++; $tc ++; } print sprintf("%-10s %u\n","$w:",$c); } print sprintf("%-10s %u\n",'rdb:',$tc); } sub nsdb_list { print "NSDB\n"; my %db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM nospamlist LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless (@res>1); next unless ($res[0] && $res[1]); next if (!$all && (($nsdb_expire>0) && ($res[2] < $now-$nsdb_expire))); $res[3] = 0 unless ($res[3]); my %ri = ( a => $res[0], w => $res[1], t => $res[2], c => $res[3], ); my $w = '?'; if ($res[0] =~ /^\d+\.\d+\.\d+\.\d+$/) { $w = 'relay'; } elsif ($res[0] =~ /\@/) { $w = 'sender'; } elsif ($res[0] =~ /\S+\.\S+/) { $w = 'domain'; } unless ($db{$w}) { my @l = (); $db{$w} = \@l; } push @{$db{$w}}, \%ri; } $st->finish; return unless (%db); my $tc = 0; foreach my $w (keys %db) { my $c = 0; foreach my $i (sort { $a->{c} <=> $b->{c} } @{$db{$w}}) { if ($i->{t} == $i->{w}) { print sprintf("%-10s %-40s %6i %s %3u\n", $w,$i->{a},$i->{c}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), ); } else { print sprintf("%-10s %-40s %6i %s %3u %s %3u\n", $w,$i->{a},$i->{c}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), ); } $c ++; $tc ++; } print sprintf("%-10s %u\n","$w:",$c); } print sprintf("%-10s %u\n",'nsdb:',$tc); } sub gdb_cmp { my $c = ($b->{c} cmp $a->{c}); $c = ($b->{a} <=> $a->{a}) unless ($c); $c = ($b->{x} <=> $a->{x}) unless ($c); return $c; } sub gdb_list { print "GDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM greylist LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless (@res>1); $res[0] = 0 unless ($res[0]); $res[1] = 0 unless ($res[1]); $res[2] = 0 unless ($res[2]); $res[3] = 0 unless ($res[3]); $res[4] = 0 unless ($res[4]); my %ri = ( x => $res[0], a => $res[1], h => $res[2], s => $res[3], r => $res[4], ); if ($now < $res[0]+$gdb_black) { $ri{c} = 'black'; } elsif (($now < $res[0]+$gdb_grey) || (($res[1] > 0) && ($now < $res[1] + $gdb_white))) { $ri{c} = 'white'; } elsif ($all) { $ri{c} = ''; } else { next; } push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort gdb_cmp @db) { if ($i->{a} && $i->{x}) { print sprintf("%-6s %-90s %s %5u %s %5u\n", $i->{c}, join(' ',$i->{h},$i->{s},$i->{r}), time2str('%Y-%m-%d %H:%M:%S',$i->{x}),($now-$i->{x})/(60*60*24), time2str('%Y-%m-%d %H:%M:%S',$i->{a}),($now-$i->{a})/(60*60*24), ); } elsif ($i->{x}) { print sprintf("%-6s %-90s %s %5u\n", $i->{c}, join(' ',$i->{h},$i->{s},$i->{r}), time2str('%Y-%m-%d %H:%M:%S',$i->{x}),($now-$i->{x})/(60*60*24), ); } elsif ($i->{a}) { print sprintf("%-6s %-90s %25s %s %5u\n", $i->{c}, join(' ',$i->{h},$i->{s},$i->{r}),'', time2str('%Y-%m-%d %H:%M:%S',$i->{x}),($now-$i->{x})/(60*60*24), ); } else { print sprintf("%-6s %s\n", $i->{c}, join(' ',$i->{h},$i->{s},$i->{r}), ); } $tc ++; } print sprintf("%-10s %u\n",'ghdb:',$tc); } sub hdb_list { print "GHDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM hostlist LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless (@res>1); next unless ($res[0] && $res[1]); $res[2] = 0 unless ($res[2]); next unless ($all || ($now < $res[2]+$gdb_host_white)); my %ri = ( h => $res[0], w => $res[1], t => $res[2], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort { $b->{t} <=> $a->{t} } @db) { if ($i->{t} == $i->{w}) { print sprintf("%-40s %s %5u\n", $i->{h}, time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), ); } else { print sprintf("%-40s %s %5u %s %5u\n", $i->{h}, time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), ); } $tc ++; } print sprintf("%-10s %u\n",'ghdb:',$tc); } sub mxdb_cmp { my $c = ($b->{e} cmp $a->{e}); $c = ($b->{w} <=> $a->{w}) unless ($c); return $c; } sub mxdb_list { print "MXDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM mxcheck LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless (@res>1); next unless ($res[0] && $res[1]); unless ($all) { if ($res[2]) { next unless ($now < $res[1]+$mx_cache_invalid); } else { next unless ($now < $res[1]+$mx_cache_valid); } } $res[2] = '' unless ($res[2]); my %ri = ( d => $res[0], w => $res[1], e => $res[2], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort mxdb_cmp @db) { if ($i->{e}) { print sprintf("%-40s %s %5u %s\n", $i->{d}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), $i->{e}, ); } else { print sprintf("%-40s %s %5u\n", $i->{d}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), ); } $tc ++; } print sprintf("%-10s %u\n",'mxdb:',$tc); } sub scdb_cmp { my $c = ($a->{r} <=> $b->{r}); $c = ($a->{s} cmp $b->{s}) unless ($c); $c = ($b->{w} <=> $a->{w}) unless ($c); return $c; } sub scdb_list { print "SCDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM sendercheck LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless (@res>1); next unless ($res[0] && $res[1]); $res[2] = 0 unless ($res[2]); $res[3] = 0 unless ($res[3]); $res[4] = '' unless ($res[4]); unless ($all) { my $ct; if (!$res[3]) { $ct = $sc_cache_invalid + ($sc_cache_invalid_add * ($res[2] - 1)); $ct = $sc_cache_invalid_max if ($ct > $sc_cache_invalid_max); } elsif ($res[3] == 1) { $ct = $sc_cache_valid; } else { $ct = $sc_cache_unknown; } next if ($now - $res[1] > $ct); } my %ri = ( a => $res[0], w => $res[1], c => $res[2], r => $res[3], s => $res[4], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort scdb_cmp @db) { if ($i->{s}) { print sprintf("%-40s %s %5u %u %u %s\n", $i->{a}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), $i->{c},$i->{r},$i->{s}, ); } else { print sprintf("%-40s %s %5u %u %u\n", $i->{a}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), $i->{c},$i->{r}, ); } $tc ++; } print sprintf("%-10s %u\n",'scdb:',$tc); } sub dcdb_cmp { my $c = ($b->{c} <=> $a->{c}); $c = ($b->{t} <=> $a->{t}) unless ($c); $c = ($b->{w} <=> $a->{w}) unless ($c); return $c; } sub dcdb_list { print "DCDB\n"; my @db = (); my %dbi = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM dictionary LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[1]); my $o = ($now < $res[0]+$dc_time_window); next unless ($o || $all); my $i = $dbi{$res[1]}; unless (defined($i)) { my %i = (h=>$res[1],c=>0,t=>0,w=>0); push @db, \%i; $i = $#db; $dbi{$res[1]} = $i; } $db[$i]->{c} ++ if ($o); $db[$i]->{t} ++; $db[$i]->{w} = $res[0] if ($res[0] > $db[$i]->{w}); } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort dcdb_cmp @db) { if ($all) { print sprintf("%-15s %5u %5u %s %5u\n", $i->{h},$i->{c},$i->{t}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), ); } else { print sprintf("%-15s %5u %s %5u\n", $i->{h},$i->{c}, time2str('%Y-%m-%d %H:%M:%S',$i->{w}),($now-$i->{w})/(60*60*24), ); } $tc ++; } print sprintf("%-10s %u\n",'dcdb:',$tc); } sub pgpdb_cmp { my $c = ($b->{k} <=> $a->{k}); $c = ($b->{t} <=> $a->{t}) unless ($c); return $c; } sub pgpdb_list { print "PGPDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM pgpring LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[1]); next if (!$all && (($pgp_expire>0) && ($res[1] < $now-$pgp_expire))); next if (!$all && $res[1] eq '#' && (($pgp_expire_bad>0) && ($res[1] < $now-$pgp_expire_bad))); $res[2] = '' unless ($res[2]); $res[3] = '' unless ($res[3]); $res[4] = '' unless ($res[4]); $res[5] = '' unless ($res[5]); my %ri = ( i => $res[0], t => $res[1], n => $res[2], k => $res[3], a => $res[4], p => $res[5], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort pgpdb_cmp @db) { my $kg = 'bad'; $kg = 'good' if ($i->{k} && $i->{k} ne '#'); my $s = sprintf('%-4s %-16s %s %5u', $kg,$i->{i}, time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24) ); $s .= ' '.$i->{a} if ($i->{a}); $s .= ' '.$i->{p} if ($i->{p}); $s .= ' '.$i->{n} if ($i->{n}); print "$s\n"; $tc ++; } print sprintf("%-10s %u\n",'pgpdb:',$tc); } sub tsdb_cmp { my $c = ($a->{e} <=> $b->{e}); $c = ($a->{t} <=> $b->{t}) unless ($c); return $c; } sub tsdb_list { print "TSDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM times LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[1]); $res[2] = 0 unless ($res[2]); $res[3] = 0 unless ($res[3]); my %ri = ( i => $res[0], t => $res[1], e => $res[2], g => $res[3], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; my $tcg = 0; my $tg = 0; foreach my $i (sort tsdb_cmp @db) { if ($i->{e}) { print sprintf("%s %5u %5u\n", time2str('%Y-%m-%d %H:%M:%S',$i->{e}),($now-$i->{e})/(60*60*24),$i->{g}); $tcg ++; $tg += $i->{g}; } else { print sprintf("%s %5u\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24)); } $tc ++; } print sprintf(" %7.1f\n",$tg/$tcg) if ($tcg); print sprintf("%-10s %u\n",'tsdb:',$tc); } sub msgldb_cmp { my $c = ($a->{t} <=> $b->{t}); $c = ($a->{c} <=> $b->{c}) unless ($c); return $c; } sub msgldb_list { print "MSGDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM messages LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[2] && $res[3] && $res[4]); $res[0] =~ s/[\r\n]+//gs; $res[1] = '' unless ($res[1]); my %ri = ( i => $res[0], f => $res[1], r => $res[2], t => $res[3], c => $res[4], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; my $tcl = 0; if ($all) { foreach my $i (sort msgldb_cmp @db) { print sprintf("%10u %s %5u %-50s %s\n", $i->{c}, time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{f},$i->{r}); $tc ++; } print sprintf("%-10s %u\n",'msgdb:',$tc); } else { my @dbx = (); my %dbx = (); foreach my $i (@db) { if ($dbx{$i->{i}}) { $dbx[$dbx{$i->{i}}]->{t} = $i->{t} if ($i->{t} < $dbx[$dbx{$i->{i}}]->{t}); $dbx[$dbx{$i->{i}}]->{c} += $i->{c}; push @{$dbx[$dbx{$i->{i}}]->{f}}, $i->{f} unless ($dbx[$dbx{$i->{i}}]->{fh}{lc($i->{f})}); push @{$dbx[$dbx{$i->{i}}]->{r}}, $i->{r} unless ($dbx[$dbx{$i->{i}}]->{rh}{lc($i->{r})}); } else { my %mi = %{$i}; $mi{f} = [$i->{f}]; $mi{r} = [$i->{r}]; $mi{fh}{lc($i->{f})} = 1; $mi{rh}{lc($i->{r})} = 1; push @dbx, \%mi; $dbx{$i->{i}} = $#dbx; } $tc ++; } foreach my $i (sort msgldb_cmp @dbx) { print sprintf("%10u %s %5u %-50s %s\n", $i->{c}, time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), join(',',@{$i->{f}}),join(',',@{$i->{r}})); $tcl ++; } print sprintf("%-10s %u (%u)\n",'msgdb:',$tc,$tcl); } } sub outdb_cmp { return ($a->{t} <=> $b->{t}); } sub outdb_list { print "OUTDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM sentout LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[1] && $res[2]); next if (!$all && (($out_expire>0) && ($res[5] < $now-$out_expire))); $res[0] = '' unless ($res[0]); $res[3] = '' unless ($res[3]); $res[4] = '' unless ($res[4]); $res[5] = 0 unless ($res[5]); my %ri = ( i => $res[0], f => $res[1], u => $res[2], d => $res[3], h => $res[4], t => $res[5], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort outdb_cmp @db) { print sprintf("%s %5u %s %s@%s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{f},$i->{u},$i->{d},$i->{h}); $tc ++; } print sprintf("%-10s %u\n",'outdb:',$tc); } sub smtpdb_cmp { return ($a->{t} <=> $b->{t}); } sub smtpdb_list { print "SMTPDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM smtpcheck LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[3] && $res[4]); unless ($all) { next if ($res[4] eq 'CONTINUE' && $res[3] < $now-$smtp_cache_good); next if ($res[4] eq 'TEMPFAIL' && $res[3] < $now-$smtp_cache_fail); next if ($res[4] eq 'REJECT' && $res[3] < $now-$smtp_cache_bad); } my %ri = ( f => $res[0], r => $res[1], h => $res[2], t => $res[3], o => $res[4], m => $res[5], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort smtpdb_cmp @db) { print sprintf("%s %5u %-15s %-8s %-40s %s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{h},$i->{o},$i->{r},$i->{f},$i->{m}); $tc ++; } print sprintf("%-10s %u\n",'smtpdb:',$tc); } sub expndb_cmp { return ($a->{t} <=> $b->{t}); } sub expndb_list { print "EXPNDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM expncheck LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[2]); next unless ($all || $res[2] >= $now-$expn_cache); my %ri = ( o => $res[0], n => $res[1], t => $res[2], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort expndb_cmp @db) { print sprintf("%s %5u %-40s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{o},$i->{n}); $tc ++; } print sprintf("%-10s %u\n",'expndb:',$tc); } sub gsnddb_cmp { return ($a->{t} <=> $b->{t}); } sub gsnddb_list { print "GSNDDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM generatedsenders LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0]); next if (!$all && (($gsnd_expire>0) && ($res[1] < $now-$gsnd_expire))); $res[0] = '' unless ($res[0]); $res[1] = 0 unless ($res[1]); $res[2] = 0 unless ($res[2]); $res[3] = 0 unless ($res[3]); my %ri = ( e => $res[0], t => $res[1], r => $res[2], b => $res[3], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort gsnddb_cmp @db) { print sprintf("%s %5u %5u %5u %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{r},$i->{b},$i->{e}); $tc ++; } print sprintf("%-10s %u\n",'gsnddb:',$tc); } sub virus_cmp { return ($a->{t} <=> $b->{t}); } sub virus_list { print "AVCDB\n"; my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM virusresults LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0]); unless ($all) { next unless ($res[4] >= $now-$virus_keep); if ($res[1]) { next unless ($res[4] >= $now-$vircache_local); } else { next unless ($res[4] >= $now-$vircache_external); } } $res[0] = '' unless ($res[0]); $res[4] = 0 unless ($res[4]); my %ri = ( h => $res[0], l => $res[1], e => $res[2], v => $res[3], t => $res[4], r => $res[5], c => $res[6], d => $res[7], ); push @db, \%ri; } $st->finish; return unless (@db); my $tc = 0; foreach my $i (sort virus_cmp @db) { print sprintf("%s %5u %-8s %-7s %01u %-20s %-15s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{l}?'local':'external',$i->{e}?'entity':'message',$i->{v}, $i->{c},$i->{d},$i->{r},$i->{h}); $tc ++; } print sprintf("%-10s %u\n",'avcdb:',$tc); } sub logs_cmp_all { return ($a->{t} <=> $b->{t}); } sub logs_cmp_sum { return ($a->{h} <=> $b->{h} ? $a->{h} <=> $b->{h} : $a->{l} <=> $b->{l}); } sub logs_list { print "LOGSDB\n"; my $tc = 0; if ($all) { my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT * FROM logs LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[1] && $res[2]); my %ri = ( t => $res[1], w => $res[2], c => $res[3], i => $res[4], l => $res[5], ); push @db, \%ri; } $st->finish; return unless (@db); foreach my $i (sort logs_cmp_all @db) { print sprintf("%s %5u %-15s %-15s %-14s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{w},$i->{c},$i->{i},$i->{l}); $tc ++; } } else { my @db = (); my $st = $sqldb->prepare(sql_translate("SELECT logs_type,logs_cont,COUNT(*),MIN(logs_stamp),MAX(logs_stamp) FROM logs GROUP BY logs_type,logs_cont LIMIT $max")); return unless ($st); my $now = time(); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[0] && $res[1]); my %ri = ( w => $res[0], v => $res[1], c => $res[2], l => $res[3], h => $res[4], ); push @db, \%ri; } $st->finish; return unless (@db); foreach my $i (sort logs_cmp_sum @db) { print sprintf("%s %5u %s %5u %5u %-15s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{l}),($now-$i->{l})/(60*60*24), time2str('%Y-%m-%d %H:%M:%S',$i->{h}),($now-$i->{h})/(60*60*24), $i->{c},$i->{w},$i->{v}); $tc += $i->{c}; } } print sprintf("%-10s %u\n",'logsdb:',$tc); } sub hilodb_select { my $ord = shift; my $lmt = shift; my $dbl = shift; my $st = $sqldb->prepare(sql_translate("SELECT hilo_id,hilo_stamp,hilo_score,hilo_info FROM hiloscores ORDER BY hilo_score $ord LIMIT $lmt")); return unless ($st); $st->execute; while (my @res = $st->fetchrow_array) { next unless ($res[1] && $res[2] && $res[3]); my %ri = ( i => $res[0], t => $res[1], h => $res[2], f => '', r => '', ); foreach my $l (split(/[\r\n]+/,$res[3])) { if ($l =~ /^Mail From:\s*(.*?)$/i) { $ri{f} .= ",$1"; } elsif ($l =~ /^RCPT To:\s*(.*?)$/i) { $ri{r} .= ",$1"; } } $ri{f} =~ s/^,//; $ri{r} =~ s/^,//; push @$dbl, \%ri; } $st->finish; } sub hilodb_list { print "HILODB\n"; my @db = (); my $now = time(); return if ($max == 0); if ($all || ($hilo_entries && $hilo_entries<0)) { hilodb_select('DESC',$max,\@db); } else { return unless ($hilo_entries); my $le = $hilo_entries; if ($max > 0) { my $me = sprintf('%.0f',$max/2) ; $le = $me if ($me < $le); } hilodb_select('DESC',$le,\@db); if ($max > 0) { my $me = int($max/2); $le = $me if ($me < $le); } my @dbx = (); hilodb_select('ASC',$le,\@dbx); push @db, reverse @dbx; } return unless (@db); my $tc = 0; foreach my $i (@db) { print sprintf("%s %5u <%04u> %-15.3f %-55s %s\n", time2str('%Y-%m-%d %H:%M:%S',$i->{t}),($now-$i->{t})/(60*60*24), $i->{i},$i->{h},$i->{f},$i->{r}); $tc ++; } print sprintf("%-10s %u\n",'hilodb:',$tc); } sub hilodb_show { my $id = shift; print "HILDODB:$id\n"; my $st = $sqldb->prepare(sql_translate("SELECT * FROM hiloscores WHERE hilo_id=$id")); return unless ($st); $st->execute; my @res = $st->fetchrow_array; $st->finish; return unless (@res); print "\n"; print sprintf("%s (%u)\n",time2str('%Y-%m-%d %H:%M:%S',$res[1]),(time-$res[1])/(60*60*24)); if ($res[3]) { print "Spam\n\n"; } else { print "Ham\n\n"; } print "$res[6]\n"; print "Names: $res[4]\n\n"; print "$res[5]\n"; print $res[7]; } sub db_counts { unless (@_) { return db_counts( 'dcdb' => 'dictionary', 'rdb' => 'relaylist', 'nsdb' => 'nospamlist', 'gdb' => 'greylist', 'msgdb' => 'messages', 'outdb' => 'sentout', 'smtpdb' => 'smtpcheck', 'expndb' => 'expncheck', 'ghdb' => 'hostlist', 'mxdb' => 'mxcheck', 'scdb' => 'sendercheck', 'pgpdb' => 'pgpring', 'gsnddb' => 'generatedsenders', 'avcdb' => 'virusresults', 'hilodb' => 'hiloscores', 'logs' => 'logs', 'ts' => 'times', ); } my $c = 0; while (@_) { my $dbh = uc(shift @_); my $dbn = shift @_; next unless ($dbh && $dbn); my $cnt = sql_select_one("SELECT count(*) FROM $dbn"); return unless (defined($cnt)); print sprintf("%-10s %u\n",$dbh,$cnt); $c += $cnt; } print sprintf("%-10s %u\n",'',$c); } #*********************************************************************** # Main. #*********************************************************************** read_cfg_cfg(); $sqldb = DBI->connect($database_spec,$database_user,$database_pass,{RaiseError=>0,AutoCommit=>1}); die ('Database?') unless ($sqldb); for (my $i=0;$i<@ARGV;$i++) { print "\n" if ($i); my $p = $ARGV[$i]; $p =~ s/[-_+\s]//g; if ($p =~ /^(ma)?x[=:](\d*)$/i) { $max = $2; $max = -1 unless ($max); } elsif ($p =~ /^a(ll)?$/i) { $all = 1; } elsif ($p =~ /^r(elay)?db$/i) { rdb_list(); } elsif ($p =~ /^no?s(pam)?db$/i) { nsdb_list(); } elsif ($p =~ /^(g|grey)?h(ost)?db$/i) { hdb_list(); } elsif ($p =~ /^mxdb$/i) { mxdb_list(); } elsif ($p =~ /^s(ender)?c(heck)?db$/i) { scdb_list(); } elsif ($p =~ /^g(r[ae]y)?db$/i) { gdb_list(); } elsif ($p =~ /^d(c|ict)?db$/i) { dcdb_list(); } elsif ($p =~ /^pgpdb$/i) { pgpdb_list(); } elsif ($p =~ /^m(sg|essages?)db$/i) { msgldb_list(); } elsif ($p =~ /^t(ime)?s(tamps?)?db$/i) { tsdb_list(); } elsif ($p =~ /^(s|sent)?o(ut)?db$/i) { outdb_list(); } elsif ($p =~ /^s(mtp)?db$/i) { smtpdb_list(); } elsif ($p =~ /^e(xpn)?db$/i) { expndb_list(); } elsif ($p =~ /^g(en(erated)?)?se?nd(ers?)?(db)?$/i) { gsnddb_list(); } elsif ($p =~ /^a(nti)?v(ir(us)?)?c(ache)?(db)?$/i) { virus_list(); } elsif ($p =~ /^h(i|igh)?l(o|ow)?(s|score)?db$/i) { hilodb_list(); } elsif ($p =~ /^h(i|igh)?l(o|ow)?(s|score)?db:(\d+)$/i) { hilodb_show($4); } elsif ($p =~ /^l(ogs?)?db$/i) { logs_list(); } elsif ($p =~ /^c(ounts?)?$/i) { db_counts(); } else { print "? $p\n"; } } $sqldb->disconnect(); $sqldb = undef;