#!/usr/bin/perl -w

use strict;
use Time::Local 'timelocal_nocheck';

my $LOGFILE="/var/log/dovecot.log";		# dovecot logfile
my $NETWORKTABLE="/etc/postfix/network_table";	# postfix network table
my $TIMESTAMPS="/etc/postfix/popb4smtp.timestamps"; # data for this script
my $ALLOWED=60*15; # how long a user may send mail in seconds
my $REMOVEINTERVAL=10; # interval in which to check for IP-adresses to remove
my $OUTPUT="/var/log/popbeforesmtp.log";	# log for this script

#----8<----8<----8<----

my $VERSION="0.2-dev";

my @timestamps;
my $lastcheck=0;
my $line;

print "Reading timestamps:\n";
open TIMESTAMPFILE, $TIMESTAMPS;
while (<TIMESTAMPFILE>){
	my $i;
	if (/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\|([0-9]*)$/){
		$i=$#timestamps+1;
		$timestamps[$i][0]=$1;
		$timestamps[$i][1]=$2;
		print "\t- $1: ".(localtime($2))."\n";
	}
}
close TIMESTAMPFILE;

if (fork()){
	print "Daemonizing...\n";
	exit(1);
}

if (fork()){
	open LOG, ">>$OUTPUT";
	select LOG;
	$|++;
	select STDOUT;

	print LOG localtime().": Dovecot/Postfix POP/IMAP before SMTP starting up\n";

	open LOGFILEFD, "/usr/bin/tail -f $LOGFILE |";
	while ($line = <LOGFILEFD>){
		if ($line =~ /login: (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([0-9]{1,2}) ([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}) Info: Login: ([a-zA-Z0-9]*) \[([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\]/){
			my ($mon, $mday, $hour, $min, $sec, $username, $ip) = ($1, $2, $3, $4, $5, $6, $7);
			my (undef,undef,undef,undef,undef,$year,undef,undef,undef) = localtime(time);
			for ($mon){
				if (/Jan/){ $mon=0; }
				elsif (/Feb/){ $mon=1; }
				elsif (/Mar/){ $mon=2; }
				elsif (/Apr/){ $mon=3; }
				elsif (/May/){ $mon=4; }
				elsif (/Jun/){ $mon=5; }
				elsif (/Jul/){ $mon=6; }
				elsif (/Aug/){ $mon=7; }
				elsif (/Sep/){ $mon=8; }
				elsif (/Oct/){ $mon=9; }
				elsif (/Nov/){ $mon=10; }
				elsif (/Dec/){ $mon=11; };
			}
			my $time = timelocal_nocheck($sec,$min,$hour,$mday,$mon,$year);
			$time += $ALLOWED;

			if ($time < time()){
				# this only happens at startup anyway
				print LOG localtime().": ";
				print LOG "removing $ip\n";
				`sed '/^$ip\$/d' -i $NETWORKTABLE`;
				`/usr/sbin/postfix reload`;
				next;
			}

			my $found=0;
			my $i;
			for ($i=0; $i <= $#timestamps && $found==0; $i++){
				if ($timestamps[$i][0] eq "$ip"){
					print LOG localtime().": ";
					print LOG "updating $ip: ".(localtime($time))."\n";
					$timestamps[$i][1]=$time;
					$found=1;
				}
			}
			if ($found == 0){
				$i=$#timestamps+1;
				$timestamps[$i][0]=$ip;
				$timestamps[$i][1]=$time;
				print LOG localtime().": ";
				print LOG "adding $ip: ".localtime($time)."\n";
				open NETWORKTABLE, ">>$NETWORKTABLE";
				print NETWORKTABLE "$ip\n";
				close NETWORKTABLE;
				`/usr/sbin/postfix reload`;
			}
			open TIMESTAMPS, ">$TIMESTAMPS";
			foreach my $pair (@timestamps){
				($ip, $time) = @$pair;
				next if $time == 0;
				print TIMESTAMPS "$ip|$time\n";
			}
			close TIMESTAMPS;
		}
	}
	close LOGFILEFD;
} else {
	open LOG, ">>$OUTPUT";
	select LOG;
	$|++;
	select STDOUT;
	my $changed;
	while (1){
		$changed=0;
		sleep($REMOVEINTERVAL);
		for (my $i=0; $i <= $#timestamps; $i++){
			my ($ip, $time) = ($timestamps[$i][0], $timestamps[$i][1]);
			next if $time == 0;
			if ($time < time()){
				$timestamps[$i][1]=0;
				print LOG localtime().": ";
				print LOG "removing $ip\n";
				`sed '/^$ip\$/d' -i $NETWORKTABLE`;
				`/usr/sbin/postfix reload`;
				$changed++;
			}
		}
		$lastcheck=time();
		next if $changed == 0;
		open TIMESTAMPS, ">$TIMESTAMPS";
		foreach my $pair (@timestamps){
			my ($ip, $time) = @$pair;
			next if $time == 0;
			print TIMESTAMPS "$ip|$time\n";
		}
		close TIMESTAMPS;
	}
}
close LOG;
