Si vacation
<syntaxhighlight lang="perl">
- !/usr/bin/perl
- Copyright (c) 2002 by Siegrist(SystemLoesungen) (PSS @ ZweierNet.ch)
- http://pss.ZweierNet.ch
- All Rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- NAME
- si_vacation - Email Vacation Auto-Responder.
- SYNOPSIS
- si_vacation -i <FQDA-mailaddr,FQDA-mailaddr,...>
- DESCRIPTION
- This is a replacement to the BSD vacation program.
- As BSD vacation does only deal with login resp. alias names
- si_vacation was made to deal with FQDA Mailaddresses.
- This may be usefull in relation to sendmails virtualtable
- mechanism. With other words it processes all Mailaddrs supported
- by the MTA.
- To enable si_vacation put a line of the following form into
- the .forwarw file of the users home directory:
- \username, "|/usr/local/bin/si_vacation [-i <FQDA-mailaddr,FQDA-mailaddr,...>]"
- Options:
- With no arguments the hole mailaccount will be handled.
- -i <comma seperated list> . processes only the given mailaddresses (aliases). FQDA-mailaddr must be rfc822 conform email address.
- This sends one copy of an incoming message to username while
- another copy is piped into si_vacation.
- si_acation will not respond to automatically generated mails
- (like mails generated by vacation progs). si_vacation will also not
- respond to mails from either postmaster or mailer-daemon.
- Further all messages with a precedence:(bulk|list|junk) header field
- will not be processed.
- To avoid multiple replies to the same sender the program uses a
- database file .vacation.bdb in the users home directory. You can
- simply delete this file to reset this mechanism.
- The '$span' variable in the program settings defines the interval
- in days we send such a reply.
- The database file holds an entry of each sender address with the appropriate
- timestamp to calculate the interval.
- FILES
- ~/.forward Contains e-mail adresses or programs
- to which mail must be forwarded.
- ~/.vacation.msg The reply message. The tag "$SUBJECT" in this message
- can be used tu substitute the subject of received mail.
- ~/.vacation.bdb Berkeley Database file.
- AUTHOR
- Peter Siegrist <PSS at ZweierNet.ch>
- NOTES
- This script has been tested with Gentoo Linux. Note that
- for other systems it can be necessary to modify some things.
- The script needs the BerkeleyDB Module vom CPAN.
- To be sure the program can be processed out of the .forward file
- consult the 'smrsh' manpage.
$VERSION = "0.9";
use strict; use BerkeleyDB;
- --- Program Settings -----------------------------
- path to sendmail
my $sendmail = "/usr/sbin/sendmail";
- name of logfile
my $logfile = "/var/log/si_vacation.log";
- the vacation message file
my $vacation_msg = ".vacation.msg";
- the database file
my $vacation_db = ".vacation.bdb";
- file contains all domains handled by the mailsystem
my $local_hostnames = "/etc/mail/local-host-names";
- (days): send a vacation message every $span days. 0 = never send more than one message.
my $span = 1;
- --------------------------------------------------
$span *= 86400; # convert days to secs sub TRUE { 1; } sub FALSE { 0; } my $fwall = FALSE;
- RFC822 check. Regular expression von Jeffrey Friedl's Beispiel in
- "Reguleare Ausdruecke" (http://www.ora.com/catalog/regexp/).
my $RFC822PAT = <<'EOF'; [\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\ xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xf f\n\015()]*)*\)[\040\t]*)*(?:(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\x ff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n\015 "]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\ xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80 -\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]* )*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\ \\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\ x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x8 0-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|"[^\\\x80-\xff\n \015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[\040\t]*(?:\([^\\\x 80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^ \x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040 \t]*)*)*@[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([ ^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\ \\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\ x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80- \xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015() ]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\ x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\04 0\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\ n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\ 015()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?! [^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\ ]]|\\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\ x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\01 5()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*|(?:[^(\040)<>@,;:". \\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff] )|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^ ()<>@,;:".\\\[\]\x80-\xff\000-\010\012-\037]*(?:(?:\([^\\\x80-\xff\n\0 15()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][ ^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)|"[^\\\x80-\xff\ n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015"]*)*")[^()<>@,;:".\\\[\]\ x80-\xff\000-\010\012-\037]*)*<[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?
- (?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-
\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:@[\040\t]* (?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015 ()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015() ]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\0 40)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\ [^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\ xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]* )*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80 -\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x 80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t ]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\ \[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])
- \])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x
80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80 -\xff\n\015()]*)*\)[\040\t]*)*)*(?:,[\040\t]*(?:\([^\\\x80-\xff\n\015( )]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\ \x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*@[\040\t ]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\0 15()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015 ()]*)*\)[\040\t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^( \040)<>@,;:".\\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]| \\[^\x80-\xff])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80 -\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015() ]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x 80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^ \x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040 \t]*)*(?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:". \\\[\]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff ])*\])[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\ \x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x 80-\xff\n\015()]*)*\)[\040\t]*)*)*)*:[\040\t]*(?:\([^\\\x80-\xff\n\015 ()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\ \\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)?(?:[^ (\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000- \037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\xff\ n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]| \([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\)) [^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff \n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\x ff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*( ?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\ 000-\037\x80-\xff])|"[^\\\x80-\xff\n\015"]*(?:\\[^\x80-\xff][^\\\x80-\ xff\n\015"]*)*")[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\x ff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)
- \))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*)*@[\040\t]*(?:\([^\\\x80-\x
ff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80- \xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)
- (?:[^(\040)<>@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\
]\000-\037\x80-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\] )[\040\t]*(?:\([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80- \xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\x ff\n\015()]*)*\)[\040\t]*)*(?:\.[\040\t]*(?:\([^\\\x80-\xff\n\015()]*( ?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]*(?:\\[^\x80-\xff][^\\\x80 -\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)*\)[\040\t]*)*(?:[^(\040)< >@,;:".\\\[\]\000-\037\x80-\xff]+(?![^(\040)<>@,;:".\\\[\]\000-\037\x8 0-\xff])|\[(?:[^\\\x80-\xff\n\015\[\]]|\\[^\x80-\xff])*\])[\040\t]*(?: \([^\\\x80-\xff\n\015()]*(?:(?:\\[^\x80-\xff]|\([^\\\x80-\xff\n\015()]
- (?:\\[^\x80-\xff][^\\\x80-\xff\n\015()]*)*\))[^\\\x80-\xff\n\015()]*)
- \)[\040\t]*)*)*>)
EOF
- ---------------------------------------------------------------------
open(LOG, ">>$logfile");
- my $user = $ENV{"USER"} || $ENV{"LOGNAME"} || getlogin || (getpwuid($>))[0];
my $user = (getpwuid($>))[0];
print LOG "\n$user -- @{[scalar localtime()]} ------------------------------\n"; print LOG "$user -- ARGV: @ARGV\n"; print LOG ">>>>>$ENV{'USER'} || $ENV{'LOGNAME'} || ", getlogin, " || ", (getpwuid($>))[0], "\n";
- -- Options
- Usage in .forward: "\mailaccount, |si_vacation -i <mail@addr.tld,mail@addr.tld,...>"
- ... or "\mailaccount, |si_vacation"
- With no arguments the hole mailaccount will be handled.
- -i <comma seperated list> processes only the given mailaddresses (aliases). mail@addr.tld must be rfc822 conform email address.
my %i_args = (); if ( $ARGV[0] eq "-i" ) {
shift (@ARGV);
map { $i_args{lc($_)} = "1" } split(",", shift(@ARGV));
print LOG "$user -- Handle addrs: ", join(",", keys %i_args), "\n";
} else {
$fwall = TRUE;
}
- -- read the local host names
open(LH, "<$local_hostnames") or die "error open $local_hostnames: $!\n"; my @lhn = <LH>; close LH;
- Process the message
my ($from, $to, $cc, $subject, $messageid, $lastheader, $email, $delivery_to, $header); $subject=""; while (<>) {
last if (/^$/);
$header .= $_;
if (/^\s+(.*)/ and $lastheader) { $$lastheader .= " $1"; }
elsif (/^from:\s+(.*)\n$/i) { $from = $1; $lastheader = \$from; }
elsif (/^to:\s+(.*)\n$/i) { $to = $1; $lastheader = \$to; }
elsif (/^cc:\s+(.*)\n$/i) { $cc = $1; $lastheader = \$cc; }
elsif (/^subject:\s+(.*)\n$/i) { $subject = $1; $lastheader = \$subject; }
elsif (/^message-id:\s+(.*)\n$/i) { $messageid = $1; $lastheader = \$messageid; }
elsif (/^x-spam-(flag|status):\s+yes/i) { exit; }
elsif (/^precedence:\s+(bulk|list|junk)/i) { exit; }
elsif (/^Auto-Submitted: auto\-.+/i) { exit; }
else {$lastheader = "" ; }
} close ARGV;
if (!$from || !$messageid) { exit; } exit if $from =~ /mailer-daemon/i; exit if $from =~ /postmaster/i;
- print LOG "HEADER:\n$header\n";
$RFC822PAT =~ s/\n//g; if ( $from !~ /^${RFC822PAT}$/o ) {
print LOG "$user -- Exit! Address check failed (not rfc822 conform): $from\n"; exit;
} my ($db_from) = ( $from =~ /([\w\-.%]+\@[\w.-]+)/ ); chomp($db_from); chomp($from);
print LOG "$user -- MAIL-TO: $to\n";
- exit if no need to send a mail
if ( check_vdb($db_from) == FALSE ) {
print LOG "$user -- Already sent a vacation message.\n"; exit;
}
- check for allowed domains
my ($dom) = ( $db_from =~ /.+\@(.+)$/ ); if ( !grep /^$dom$/i, @lhn ) {
#print LOG "$user -- Exit! Unallowed domainname ($dom). Won't reply.\n"; #exit;
}
- senseless mailaddr ?
($to) = ( $to =~ /([\w\-.%]+\@[\w.-]+)/ ); exit if $to eq ""; chomp($to); lc $to;
- -- exit if not in argument list
if ( ! $fwall ) {
if ( ! exists $i_args{$to} ) {
print LOG "$user -- Not handled address.($to / $i_args{$to}\n";
exit;
}
}
my $msg = ""; if (open(MSG,"$vacation_msg")) {
undef $/; $msg = <MSG>; close MSG;
} else {
print LOG "$user -- Can't open $vacation_msg: $!\n"; exit;
}
- Replace $SUBJECT with real subject text.
$msg =~ s/\$SUBJECT/$subject/gm;
print LOG "$user -- REPLAY-TO: $from\n";
- Send reply using sendmail.
if ( open(MAIL, "|$sendmail -t -f $to") ) {
print MAIL <<EOF_MAIL_HEADER;
To: $from Auto-Submitted: auto-replied Precedence: junk EOF_MAIL_HEADER
print MAIL $msg; close MAIL; upd_vdb($db_from);
} else {
print LOG "$user -- si_vacation: Can't run sendmail\n"; exit;
}
close LOG;
exit;
- -- check_vdb()
- returns false if mailaddr is found and timestamp is in span (don't send a mail)
- returns true if mailaddr is not found or timestamp is beyond span (send a mail)
- --
sub check_vdb {
my $mailaddr = shift;
my $ts_span;
my $db = BerkeleyDB::Hash->new( -Filename => "$vacation_db",
-Flags => DB_CREATE );
if ( ! $db ) {
print LOG "$user -- Cannot open vacation DB: $!";
exit FALSE;
}
my $lt = localtime();
if ( $db->db_get( "$mailaddr", $ts_span ) == 0 ) {
return FALSE if $span == 0; # infinite span. send only one message.
return TRUE if $lt - $ts_span > $span;
return FALSE;
} else {
return TRUE;
}
$db->db_close();
}
- -- upd_vdb()
- update/write vacation db with mailaddr and timestamp
- --
sub upd_vdb {
my $mailaddr = shift;
my $db = BerkeleyDB::Hash->new( -Filename => "$vacation_db",
-Flags => DB_CREATE );
if ( ! $db ) {
print LOG "$user -- Cannot open vacation DB: $!";
exit FALSE;
}
my $lt = localtime();
$db->db_put( "$mailaddr", $lt );
$db->db_close();
}