#!/usr/bin/perl
#
# smtp_after_pop.pl by Richard Lippmann
# V0.03 2001-01-10: allow duplicate keys (127.0.0.1) in access-db-file
# V0.02 2001-12-07: expire did not work
# V0.01 2001-12-07
#
# Checks /var/log/maillog for entries made by ipop3d
# when a user logs successfully in. If so it writes 
# a file /etc/mail/access_by_pop which contains ip's and expiredates
# as comments and "ip RELAY" as data for the hash-file created
# after that for sendmail.
#
# Usage (1):
# # for example in /etc/rc.d/rc.local
# (/root/bin/smtp_after_pop.pl runforever) &
#
# Usage (2):
# In root's crontab like this:
# # expire for smtp_after_pop
# 0,30 * * * * (/root/bin/smtp_after_pop.pl expirecheck) >/dev/null 2>&1
#
#
use strict;

my $version = "0.03";
my $me = "smtp_after_pop.pl";
# In which directory is the file "access_by_pop"
my ($abp_fqdn) = "/etc/mail";
# What's its filename there:
my ($abp_fn) = "access_by_pop";
my ($abp_fqn) = "$abp_fqdn/$abp_fn";
# What's the filename of the original "access" file of sendmail
my $access_fn = "access";


unless ($ARGV[0] =~ /^(runforever|expirecheck)$/) {
	print "Usage: $0 runforever  -> checks maillog for popusers forever\n";
	print "       $0 expirecheck -> only check for expiration (cronjob)\n";
	exit;
}

if ($ARGV[0] =~ /runforever/) {
	open (MAILLOG,"tail -f /var/log/maillog |");
	while (<MAILLOG>) {
		my $zeile = $_;
		# look for a line like this:
		# Dec  8 13:35:53 brainstorm ipop3d[9033]: Login user=lippmann host=pD951D32D.dip.t-dialin.net [217.81.211.45] nmsgs=0/0
		if ($zeile =~ / ipop3d.+(Login|Auth) user=(.+) .+\[(.+)\]/) {
			my %expires;
			my ($pop_user,$pop_ip) = ($2,$3);
			my ($now,$pop_expires) = (&now,&expires);
	
			&relay_me_too(pop_ip=>$pop_ip,pop_user=>$pop_user,pop_expires=>$pop_expires);
		}
	}
	close MAILLOG;
}

if ($ARGV[0] =~ /expirecheck/) {
	&relay_me_too(pop_ip=>undef);
}

exit;

sub expires {
	# return timestamp dd.mm.yyyy hh:mm:ss for in one hour
	my ($s,$min,$h,$d,$m,$y)=(localtime(time+3600))[0,1,2,3,4,5];
	$m++; $y = 1900 + $y;
	my $now_plus1h = sprintf("%04d.%02d.%02d %02d:%02d:%02d", $y,$m,$d,$h,$min,$s);
	return $now_plus1h;
}

sub now {
	# return timestamp dd.mm.yyyy hh:mm:ss
	my ($s,$min,$h,$d,$m,$y)=(localtime(time))[0,1,2,3,4,5];
	$m++; $y = 1900 + $y;
	my $now = sprintf("%04d.%02d.%02d %02d:%02d:%02d", $y,$m,$d,$h,$min,$s);
	return $now;
}

sub relay_me_too {
	my (%args)=@_;
	# If these arguments are not given: check only for expired ip's
	my $pop_ip = $args{pop_ip};
	my $pop_user = $args{pop_user};
	my $pop_expires = $args{pop_expires};
	my $nr_expires = 0;
	my $nr_allowed = 0;
	my @allowed_users;

	system("logger \"$me says: allow popuser $pop_ip to sendmail until $pop_expires\"") if $pop_ip;

	# hash for all valid relay qualified by pop3-login
	my %expires;
	# to check for expired ip-addresses:
	my $now = &now;

	# access_by_pop is a file like that:
	# #131.188.1.1|expires=2001-11-10 13:10:00
	# 131.188.1.1 RELAY
	# #161.10.2.10|expires=2001-11-10 13:18:40
	# 161.10.2.10 RELAY
	#

	# Read "access_by_pop"-file, build hash
	open (ABP,$abp_fqn);
	while (<ABP>) {
		next unless /^#/;
		# divide IP-address and expire-timestamp
		if (/^#(.+)\|expires=(.+)\|popuser=(.+)$/) {
			$expires{$1}= { expires => $2, popuser => $3};
		}
	}
	close ABP;
	# append (maybe overwrite) current pop-address 
	if ($pop_ip) {
		$expires{$pop_ip}= { expires => $pop_expires, popuser => $pop_user };
		system ("logger \"$me says: popuser $pop_user logged in from ip-address $pop_ip\"");
	}

	# write current file back
	open (ABP,">$abp_fqn");
	foreach (keys %expires) {
		# don't allow expired hosts to be relayed
		if ($now gt $expires{$_}->{expires}) {
			system ("logger \"$me says: expired ip $_ (was user $expires{$_}->{popuser}) was expired $expires{$_}->{expires} and is not allowed to send mail anymore\"");
			$nr_expires++;
			next;
		}
		# write a line to recognize expiredate
		printf ABP "#%s\|expires=%s\|popuser=%s\n",$_,$expires{$_}->{expires},$expires{$_}->{popuser};
		# write the line needed for creating the database
		printf ABP "$_  RELAY\n";
		$nr_allowed++;
		push(@allowed_users,$expires{$_}->{popuser});		
	}
	close ABP;

	# create new database file for sendmail
	# which allow relaying for the pop-host
	my $cmd = "(cd $abp_fqdn; cat $access_fn $abp_fn | makemap -d hash access)";
	system $cmd;

	# you can take a look into the database by:
	# makemap -u hash access.db
	$nr_expires = "No" if ($nr_expires == 0);
	system ("logger \"$me says: $nr_expires expired ip-addresses were deleted\"");
	$nr_allowed = "No" if ($nr_allowed == 0);
	system ("logger \"$me says: $nr_allowed ip-addresses from outside now allowed to send relayed mail\"");
	# for information only, I don't have THAT much users
	@allowed_users = ("<nobody>") if ($#allowed_users == -1);
	system ("logger \"$me says: allowed smtp-users from outside are now: " . join ("\, ",@allowed_users) . "\"");
}

