--- /dev/null
+#!/usr/bin/perl -T
+
+# $Id$
+# $URL$
+
+use strict;
+use warnings;
+use DBI;
+use Config::General;
+use Sys::Syslog qw(:DEFAULT setlogsock);
+use File::Basename;
+use Getopt::Long;
+use Pod::Usage;
+use Carp qw(cluck);
+use Date::Calc qw( Today Add_Delta_Days );
+
+=head1 NAME
+
+clear-greylist.pl
+
+=head1 DESCRIPTION
+
+Bereinigt die Greylist-Tabelle
+
+=head1 SYNOPSIS
+
+ clear-greylist.pl [-c <configfile>] [-v[ -v...]|-D <debuglevel>]
+ clear-greylist.pl -h
+ clear-greylist.pl -V
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-c <configfile>> - Konfigurationsdatei
+
+Wenn nicht angegeben, wird C</etc/postfix/greylist/greylist.cf> verwendet.
+
+=item B<-v> Verbose-Level (Debug-Level)
+
+Wird durch Mehrfach-Aufzaehlung erhoeht.
+
+=item B<-D level> - Debug-Level
+
+Numerische Angabe des Debug-Levels.
+
+I<Hinweise>:
+
+Die Parameter C<-v> und C<-D> wirken sich gleich aus.
+Wenn beide angegeben werden, wird der hoehere von beiden verwendet.
+
+=item B<-h|-?>
+
+Gibt diesen Hilfebildschirm aus und beendet sich.
+
+=item B<-V>
+
+Gibt die Versionsnummer dieses Programms aus und beendet sich.
+
+=back
+
+=cut
+
+my $Revis = <<'ENDE';
+ $Revision$
+ENDE
+$Revis =~ s/^.*:\s*(\S+)\s*\$.*/$1/s;
+our $VERSION = "1.0." . $Revis;
+
+######################
+## Global Variables
+######################
+my ( $dbh, $configfile );
+######################
+
+# read configuration
+$configfile = dirname($0) . "/greylist.cf";
+
+$| = 1;
+
+Getopt::Long::Configure('bundling');
+
+my ( $test_mode, $help, $show_version, $cmdline_verbose, $DebugLevel, );
+
+unless (
+ GetOptions(
+ "configfile|conf|c=s" => \$configfile,
+ "test|t" => \$test_mode,
+ "help|h|?" => \$help,
+ "verbose|v+" => \$cmdline_verbose,
+ "version|V" => \$show_version,
+ "DebugLevel|Debug|D=i" => \$DebugLevel,
+ )
+ )
+{
+ pod2usage( { -exitval => 1, -verbose => 1 } );
+} ## end unless ( GetOptions( "configfile|conf|c=s" => ...
+
+$cmdline_verbose ||= 0;
+$cmdline_verbose = $DebugLevel if $DebugLevel and $DebugLevel > $cmdline_verbose;
+
+my $verbose = $cmdline_verbose;
+
+if ($help) {
+ $verbose ? pod2usage( -exitstatus => 1, -verbose => 2 ) : pod2usage(1);
+}
+
+if ($show_version) {
+ print "Version $0: " . $VERSION . "\n\n";
+ exit 0;
+}
+
+die "Konfiguration '" . $configfile . "' nicht gefunden.\n" unless -f $configfile;
+
+#
+my $config;
+debug( "reading config file '%s'", $configfile );
+eval {
+ my $conf;
+ $conf = new Config::General($configfile);
+ $config = { $conf->getall() };
+};
+die "Konnte Konfiguration '" . $configfile . "' nicht lesen: " . $@ . "\n" if $@;
+
+$verbose = 1 if $config->{'verbose'};
+
+$0 = 'postfix/greylist';
+
+END {
+ $dbh->disconnect() if $dbh;
+}
+
+#### main ####
+#
+# This process runs as a daemon, so it can't log to a terminal. Use
+# syslog so that people can actually see our messages.
+#
+setlogsock $config->{'syslog'}{'socktype'};
+openlog $0, $config->{'syslog'}{'options'}, $config->{'syslog'}{'facility'};
+
+#
+# Unbuffer standard output.
+#
+select( ( select(STDOUT), $| = 1 )[0] );
+
+open_database() unless $dbh;
+
+db_clean_greylist();
+
+exit 0;
+
+#########################################
+### subs
+#########################################
+
+#
+# Log an error and abort.
+#
+sub fatal_exit {
+ my $first = shift;
+ if ($test_mode) {
+ cluck 'fatal: ' . sprintf( $first, @_ ) . "\n";
+ }
+ else {
+ syslog 'err', 'fatal: ' . $first, @_;
+ }
+ $dbh->disconnect() if $dbh;
+ exit 1;
+} ## end sub fatal_exit
+
+#
+# Log a debug message
+#
+sub debug {
+ return unless $verbose;
+ do_log(@_);
+}
+
+#
+# Log a message
+#
+sub do_log {
+ my $first = shift;
+ my $prio = $config->{'syslog'}{'priority'} || 'info';
+ if ($test_mode) {
+ warn sprintf( $first, @_ ) . "\n";
+ }
+ else {
+ syslog $prio, $first, @_;
+ }
+}
+
+#
+# Open hash database.
+#
+sub open_database {
+ $dbh = DBI->connect(
+ "DBI:" . $config->{'database'}{'db_type'} . ':' . $config->{'database'}{'db_name'},
+ $config->{'database'}{'db_user'},
+ $config->{'database'}{'db_pass'},
+ { AutoCommit => 0, RaiseError => 0, PrintError => 0 }
+ );
+ fatal_exit( "Cannot open database %s: %s", $config->{'database'}{'db_name'}, $DBI::errstr ) unless $dbh;
+ debug( "open db %s", $config->{'database'}{'db_name'} );
+} ## end sub open_database
+
+# clean greylist table
+sub db_clean_greylist {
+
+ my ($sqlst);
+
+ my $days = $config->{'greylist'}{'delete_expired'} || 20;
+ my $table = $config->{'database'}{'greylist_table'};
+ my $date = sprintf( "%4d-%02d-%02d 00:00:00", Add_Delta_Days( Today(), ( $days * (-1) ) ) );
+
+ do_log( "deleting expired greylist entries before '%s'", $date );
+
+ $sqlst = <<ENDE;
+DELETE FROM `$table` WHERE `expire_time` < ?
+ENDE
+
+ my $rows = 0;
+ unless ($test_mode) {
+ $rows = $dbh->do( $sqlst, {}, $date ) or fatal_exit( 'Cannot delete records: %s', $dbh->errstr );
+ }
+ do_log( "%d entries deleted, start optimizing", $rows );
+
+ $sqlst = <<ENDE;
+OPTIMIZE TABLE `$table`
+ENDE
+
+ unless ($test_mode) {
+ $dbh->do( $sqlst ) or fatal_exit( 'Cannot optimize table: %s', $dbh->errstr );
+ }
+ do_log( "optimizing finished" );
+
+} ## end sub db_add_greylist
+
+__END__
+