#!/usr/bin/perl use strict; my $VERSION = '0.11'; use Fcntl qw(:DEFAULT :flock :seek); use Data::Dumper; our %options; map { my ($k, $v) = split '='; $options{$k} = $v; } @ARGV; $options{trace} ||= 1; our ($accept_list, $trace); `echo $$ > /var/run/accept-recipient.pid`; sub trace ($;$) { my ($m, $l) = @_; $l = 1 if not defined $l; warn "$m\n" if $options{trace} >= $l; return $m; } our %file_tail = ( name => '/var/log/mail', maxinterval => 10, interval => 5, adjustafter => 3, tail => -1, resetafter => 30, ); trace "Calling tail_init"; tail_init(); $| = 1; my (%user, %sender); # Which users have ~/.accept-list files? my $accept_list = exists $options{'accept-list'} ? $options{'accept-list'} : '.accept-list'; for ( glob "/home/*/$accept_list" ) { $user{ ( split '/' )[2] } = $_; } trace Dumper \%user; while ( 'repeat forever' ) { $_ = tail_getline(); my ($queue_id, $from, $to); if ( ($queue_id, $from) = /([\da-zA-Z]+): from=<([^@]+)@/o ) { next if $from =~ /\+spam-x/; $sender{ $queue_id } = $from; } elsif ( ($queue_id, $to) = /([\da-zA-Z]+): to=<([^>]+)>/o ) { if ( my $accept_list = $user{ $sender{ $queue_id } } ) { open ACCEPT_LIST, ">>$accept_list"; print ACCEPT_LIST $to, "\n"; close ACCEPT_LIST; } } } exit; # Logfile-reading functions # stolen from pop-before-smtp sub tail_init { &tail_open; $::tail_buf = ''; if ($file_tail{'tail'} >= 0) { if (defined sysseek(TAIL, -$file_tail{'tail'}*128, SEEK_END)) { &tail_getline; # dump partial line } else { sysseek(TAIL, 0, SEEK_SET); } } } sub tail_open { open(TAIL, $file_tail{'name'}) or die "Unable to open $file_tail{'name'}: $!"; $::tail_inode = (stat(TAIL))[1]; } sub tail_getline { while (1) { return $1 if $::tail_buf =~ s/^(.*\n)//; my $cnt = 1; my $slept = 0; my $i = $file_tail{'interval'}; while (1) { last if sysread(TAIL, $::tail_buf, 2048, length($::tail_buf)); sysseek(TAIL, 0, SEEK_CUR); if ($slept >= $file_tail{'resetafter'}) { $slept = 0; my $new_inode; foreach (1..5) { $new_inode = (stat($file_tail{'name'}))[1]; last if $new_inode; sleep 2; } if ($::tail_inode != $new_inode) { &tail_open; $cnt = 1; } } if ($cnt % $file_tail{'adjustafter'} == 0) { $i += 2; $i = $file_tail{'maxinterval'} if $i > $file_tail{'maxinterval'}; } sleep $i; $cnt++; $slept += $i; } } }