Si vacation

Aus Si:Wiki von Siegrist SystemLösungen - Informatik und Rezepte
Zur Navigation springen Zur Suche springen

<syntaxhighlight lang="perl">

  1. !/usr/bin/perl
  1. Copyright (c) 2002 by Siegrist(SystemLoesungen) (PSS @ ZweierNet.ch)
  2. http://pss.ZweierNet.ch
  3. All Rights reserved.
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License as
  6. published by the Free Software Foundation.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.


  1. NAME
  2. si_vacation - Email Vacation Auto-Responder.
  3. SYNOPSIS
  4. si_vacation -i <FQDA-mailaddr,FQDA-mailaddr,...>
  5. DESCRIPTION
  6. This is a replacement to the BSD vacation program.
  7. As BSD vacation does only deal with login resp. alias names
  8. si_vacation was made to deal with FQDA Mailaddresses.
  9. This may be usefull in relation to sendmails virtualtable
  10. mechanism. With other words it processes all Mailaddrs supported
  11. by the MTA.
  12. To enable si_vacation put a line of the following form into
  13. the .forwarw file of the users home directory:
  14. \username, "|/usr/local/bin/si_vacation [-i <FQDA-mailaddr,FQDA-mailaddr,...>]"
  15. Options:
  16. With no arguments the hole mailaccount will be handled.
  17. -i <comma seperated list> . processes only the given mailaddresses (aliases). FQDA-mailaddr must be rfc822 conform email address.
  18. This sends one copy of an incoming message to username while
  19. another copy is piped into si_vacation.
  20. si_acation will not respond to automatically generated mails
  21. (like mails generated by vacation progs). si_vacation will also not
  22. respond to mails from either postmaster or mailer-daemon.
  23. Further all messages with a precedence:(bulk|list|junk) header field
  24. will not be processed.
  25. To avoid multiple replies to the same sender the program uses a
  26. database file .vacation.bdb in the users home directory. You can
  27. simply delete this file to reset this mechanism.
  28. The '$span' variable in the program settings defines the interval
  29. in days we send such a reply.
  30. The database file holds an entry of each sender address with the appropriate
  31. timestamp to calculate the interval.
  32. FILES
  33. ~/.forward Contains e-mail adresses or programs
  34. to which mail must be forwarded.
  35. ~/.vacation.msg The reply message. The tag "$SUBJECT" in this message
  36. can be used tu substitute the subject of received mail.
  37. ~/.vacation.bdb Berkeley Database file.
  38. AUTHOR
  39. Peter Siegrist <PSS at ZweierNet.ch>
  40. NOTES
  41. This script has been tested with Gentoo Linux. Note that
  42. for other systems it can be necessary to modify some things.
  43. The script needs the BerkeleyDB Module vom CPAN.
  44. To be sure the program can be processed out of the .forward file
  45. consult the 'smrsh' manpage.


$VERSION = "0.9";

use strict; use BerkeleyDB;


    1. --- Program Settings -----------------------------
  1. path to sendmail

my $sendmail = "/usr/sbin/sendmail";

  1. name of logfile

my $logfile = "/var/log/si_vacation.log";

  1. the vacation message file

my $vacation_msg = ".vacation.msg";

  1. the database file

my $vacation_db = ".vacation.bdb";

  1. file contains all domains handled by the mailsystem

my $local_hostnames = "/etc/mail/local-host-names";

  1. (days): send a vacation message every $span days. 0 = never send more than one message.

my $span = 1;

    1. --------------------------------------------------

$span *= 86400; # convert days to secs sub TRUE { 1; } sub FALSE { 0; } my $fwall = FALSE;


  1. RFC822 check. Regular expression von Jeffrey Friedl's Beispiel in
  2. "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

  1. ---------------------------------------------------------------------


open(LOG, ">>$logfile");

  1. 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";

  1. -- Options
  1. Usage in .forward: "\mailaccount, |si_vacation -i <mail@addr.tld,mail@addr.tld,...>"
  2. ... or "\mailaccount, |si_vacation"
  3. With no arguments the hole mailaccount will be handled.
  4. -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;

}


  1. -- read the local host names

open(LH, "<$local_hostnames") or die "error open $local_hostnames: $!\n"; my @lhn = <LH>; close LH;


  1. 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;


  1. 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";

  1. exit if no need to send a mail

if ( check_vdb($db_from) == FALSE ) {

   print LOG "$user -- Already sent a vacation message.\n";
   exit;

}

  1. 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;

}

  1. senseless mailaddr ?

($to) = ( $to =~ /([\w\-.%]+\@[\w.-]+)/ ); exit if $to eq ""; chomp($to); lc $to;

  1. -- 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;

}

  1. Replace $SUBJECT with real subject text.

$msg =~ s/\$SUBJECT/$subject/gm;

print LOG "$user -- REPLAY-TO: $from\n";


  1. 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;


  1. -- check_vdb()
  2. returns false if mailaddr is found and timestamp is in span (don't send a mail)
  3. returns true if mailaddr is not found or timestamp is beyond span (send a mail)
  4. --

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();

}

  1. -- upd_vdb()
  2. update/write vacation db with mailaddr and timestamp
  3. --

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();

}