Si vacation

Aus Si:Wiki von Siegrist SystemLösungen - Informatik und Rezepte
Wechseln zu: Navigation, Suche
#!/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();
}