2010. február 3., szerda

Virtual Vacation

Forrás: http://www200.pair.com/mecham/spam/virtualp1.html
There are a lot of good reasons not to use an autoresponder. One reason is: most of the mail we receive is spam and the senders use forged addresses. You will either be sending notices to senders who did not send you mail, or you will fill up your deferred queue with undeliverable mail. There are often other problems like multiple deliveries or the potential for infinite loops. The 'cache' field in the vacation table that stores email addresses of senders we have already sent notices to can fill up and cause errors. Use at your own risk. We will not use vacation.pl as supplied with PostfixAdmin. I just want to make sure you are using version 3.2, so get a copy from me. My copy also makes one small change: the envelope sender will be the null sender <>. Even though we have the latest version, there may be some lingering issues with vacation.pl; see http://high5.net/forum/

apt-get install libdbi-perl libdbd-mysql-perl

addgroup --gid 65501 vacation
useradd -c "Virtual Vacation" -d /nonexistent -u 65501 -g 65501 -s /sbin/false vacation
mkdir /var/spool/vacation
cd /var/spool/vacation/
wget http://www200.pair.com/mecham/spam/gv-vac.txt

mv gv-vac.txt vacation.pl
sed -i "s/db_pass = 'postfixadmin/db_pass = 'pfixadm_password/" vacation.pl
chown -R vacation:vacation /var/spool/vacation
chmod 750 vacation.pl
cd


We place an entry in /etc/postfix/transport that will send mail to the vacation script. This is a bogus (sub)domain name. It does not need to be set up in DNS:
vi /etc/postfix/transport

and insert:
autoreply.example.com vacation:

vi /etc/postfix/master.cf

and insert (just below the maildrop transport might be a good place):
vacation  unix  -       n       n       -       -       pipe
flags=Rq user=vacation argv=/var/spool/vacation/vacation.pl -f ${sender} -- ${recipient}
Then:
postmap /etc/postfix/transport
postconf -e "transport_maps = hash:/etc/postfix/transport"
LINUX2


Now we configure PostfixAdmin to use vacation:
sed -i "s/vacation'] = 'NO/vacation'] = 'YES/" /var/www/postFixadminx/config.inc.php

To change their Auto Response settings, a user could log into:
https://msa.example.com/postFixadminx/users

However, we can avoid users logging into PostfixAdmin by setting up a PostfixAdmin plugin in SquirrelMail (jump ahead to the next section to set it up - then return here) https://msa.example.com/mail. Test by logging into your own account and setting yourself up. Remember that the database records the senders it has already seen (so only one notice is sent per sender). To clear out the cache and start over, return from your vacation by choosing Auto Response->Coming Back and then Auto Response->Going Away again. Use phpMyAdmin to browse the database as you make changes.

Now that vacation is working, consider turning it off so no one can use it. First use https://msa.example.com/phpmyadmiNx to make sure there are no entries in the vacation table and no aliases that send mail to @autoreply.example.com, then:

sed -i "s/vacation'] = 'YES/vacation'] = 'NO/" /var/www/postFixadminx/config.inc.php
sed -i "s/Vacation = true/Vacation = false/" /usr/share/squirrelmail/plugins/postfixadmin/config.php



###

#!/usr/bin/perl -w
#
# Virtual Vacation 3.2
# by Mischa Peters
# Copyright (c) 2002 - 2005 High5!
# Licensed under GPL for more info check GPL-LICENSE.TXT
#
# Additions:
# 2004/07/13 David Osborn
# Uses strict, processes domain level aliases, more
# subroutines, send reply from original to address
# 2004/11/09 David Osborn
# Added syslog support
# Slightly better logging which includes messageid
# Avoid infinite loops with domain aliases
#
# 2005/07/20 David Osborn
# Added threading headers to reply
# Fixed problem of sending reply to wrong To:
#
# 2005/07/21 Brian Taber
# Added error handling in case of database errors
# to prevent bounced email
#
# 2007/06/17 Gary V
# Changed sender address to '<>'
# See RFC3834 Section 3.3

use DBI;
use strict;
use Sys::Syslog;

my $db_type = 'mysql';
my $db_host = 'localhost';
my $db_user = 'postfixadmin';
my $db_pass = 'postfixadmin';
my $db_name = 'postfix';
my $sendmail = "/usr/sbin/sendmail";
my $logfile = ""; # specify a file name here for example: vacation.log
my $debugfile = ""; # sepcify a file name here for example: vacation.debug
my $syslog = 1; # 1 if log entries should be sent to syslog


my $dbh = DBI->connect("DBI:$db_type:$db_name:$db_host", "$db_user", "$db_pass", { RaiseError => 1 }) or &exit_error($DBI::errstr);

# make sure vacation.pl exists cleanly when it encounters an error
sub exit_error {
syslog('mail|warning', 'virtual vacation failure: %s', shift);
exit (0);
}

# used to detect infinite address lookup loops
my $loopcount=0;

sub do_query {
my ($query) = @_;
my $sth = $dbh->prepare($query) or &exit_error("Can't prepare $query: $dbh->errstr");
$sth->execute or die "Can't execute the query: $sth->errstr";
return $sth;
}

sub do_debug {
my ($in1, $in2, $in3, $in4, $in5, $in6) = @_;
if ( $debugfile ) {
my $date;
open (DEBUG, ">> $debugfile") or &exit_error("Unable to open debug file");
chop ($date = `date "+%Y/%m/%d %H:%M:%S"`);
print DEBUG "====== $date ======\n";
printf DEBUG "%s | %s | %s | %s | %s | %s\n", $in1, $in2, $in3, $in4, $in5, $in6;
close (DEBUG);
}
}

sub do_cache {
my ($to, $from) = @_;
my $query = qq{SELECT cache FROM vacation WHERE email='$to' AND FIND_IN_SET('$from',cache)};
my $sth = do_query ($query);
my $rv = $sth->rows;
if ($rv == 0) {
$query = qq{UPDATE vacation SET cache=CONCAT(cache,',','$from') WHERE email='$to'};
$sth = do_query ($query);
}
return $rv;
}

sub do_log {
my ($messageid, $to, $from, $subject) = @_;
my $date;
if ( $syslog ) {
open (SYSLOG, "|/usr/bin/logger -p mail.info -t Vacation") or die ("Unable to open logger");
printf SYSLOG "Orig-To: %s From: %s MessageID: %s Subject: %s", $to, $from, $messageid, $subject;
close (SYSLOG);
}
if ( $logfile ) {
open (LOG, ">> $logfile") or die ("Unable to open log file");
chop ($date = `date "+%Y/%m/%d %H:%M:%S"`);
print LOG "$date: To: $to From: $from Subject: $subject MessageID: $messageid \n";
close (LOG);
}
}

sub do_mail {
my ($from, $to, $subject, $body, $orig_msgID) = @_;
open (MAIL, "| $sendmail -t -f \"<>\"") or die ("Unable to open sendmail");
print MAIL "From: $from\n";
print MAIL "To: $to\n";
print MAIL "Subject: $subject\n";
if ( $orig_msgID ) {
print MAIL "References: $orig_msgID\n";
print MAIL "In-Reply-To: $orig_msgID\n";
}
print MAIL "X-Loop: Postfix Admin Virtual Vacation\n\n";
print MAIL "$body";
close (MAIL) or die ("Unable to close sendmail");
}

sub find_real_address {
my ($email) = @_;
if (++$loopcount > 20) {
do_log ("find_real_address loop!", "currently: $email", "ERROR", "ERROR");
print ("possible infinite loop in find_real_address for <$email>. Check for alias loop\n");
exit 1;
}
my $realemail;
my $query = qq{SELECT email FROM vacation WHERE email='$email' and active=1};
my $sth = do_query ($query);
my $rv = $sth->rows;

# Recipient has vacation
if ($rv == 1) {
$realemail = $email;

} else {
$query = qq{SELECT goto FROM alias WHERE address='$email'};
$sth = do_query ($query);
$rv = $sth->rows;

# Recipient is an alias, check if mailbox has vacation
if ($rv == 1) {
my @row = $sth->fetchrow_array;
my $alias = $row[0];
$query = qq{SELECT email FROM vacation WHERE email='$alias' and active=1};
$sth = do_query ($query);
$rv = $sth->rows;

# Alias has vacation
if ($rv == 1) {
$realemail = $alias;
}

# We still have to look for domain level aliases...
} else {
my ($user, $domain) = split(/@/, $email);
$query = qq{SELECT goto FROM alias WHERE address='\@$domain'};
$sth = do_query ($query);
$rv = $sth->rows;

# The receipient has a domain level alias
if ($rv == 1) {
my @row = $sth->fetchrow_array;
my $wildcard_dest = $row[0];
my ($wilduser, $wilddomain) = split(/@/, $wildcard_dest);

# Check domain alias
if ($wilduser) {
($rv, $realemail) = find_real_address ($wildcard_dest);
} else {
my $new_email = $user . '@' . $wilddomain;
($rv, $realemail) = find_real_address ($new_email);
}
}
}
}
return ($rv, $realemail);
}

sub send_vacation_email {
my ($email, $orig_subject, $orig_from, $orig_to, $orig_messageid) = @_;
my $query = qq{SELECT subject,body FROM vacation WHERE email='$email'};
my $sth = do_query ($query);
my $rv = $sth->rows;
if ($rv == 1) {
my @row = $sth->fetchrow_array;
if (do_cache ($email, $orig_from)) { return; }
$row[0] =~ s/\$SUBJECT/$orig_subject/g;
$row[1] =~ s/\$SUBJECT/$orig_subject/g;
do_debug ("[SEND RESPONSE] for $orig_messageid:\n", "FROM: $email (orig_to: $orig_to)\n", "TO: $orig_from\n", "SUBJECT: $orig_subject\n", "VACATION SUBJECT: $row[0]\n", "VACATION BODY: $row[1]\n");
do_mail ($orig_to, $orig_from, $row[0], $row[1], $orig_messageid);
do_log ($orig_messageid, $orig_to, $orig_from, $orig_subject);
}
}

########################### main #################################

my ($from, $to, $cc, $subject, $messageid);

# Take headers apart
while () {
last if (/^$/);
if (/^from:\s+(.*)\n$/i) { $from = $1; }
if (/^to:\s+(.*)\n$/i) { $to = $1; }
if (/^cc:\s+(.*)\n$/i) { $cc = $1; }
if (/^subject:\s+(.*)\n$/i) { $subject = $1; }
if (/^message-id:\s+(.*)\n$/i) { $messageid = $1; }
if (/^precedence:\s+(bulk|list|junk)/i) { exit (0); }
if (/^x-loop:\s+postfix\ admin\ virtual\ vacation/i) { exit (0); }
}

# If either From: or To: are not set, exit
if (!$from || !$to) { exit (0); }

$from = lc ($from);

# Check if it's an obvious sender, exit
if ($from =~ /([\w\-.%]+\@[\w.-]+)/) { $from = $1; }
if ($from eq "" || $from =~ /^owner-|-(request|owner)\@|^(mailer-daemon|postmaster)\@/i) { exit (0); }

# Strip To: and Cc: and push them in array
my @strip_cc_array;
my @strip_to_array = split(/, */, lc ($to) );
if (defined $cc) { @strip_cc_array = split(/, */, lc ($cc) ); }
push (@strip_to_array, @strip_cc_array);

my @search_array;

# Strip email address from headers
for (@strip_to_array) {
if ($_ =~ /([\w\-.%]+\@[\w.-]+)/) {
push (@search_array, $1);
#do_debug ("[STRIP RECIPIENTS]: ", $messageid, $1, "-", "-", "-");
}
}

# Search for email address which has vacation
for my $to (@search_array) {
my ($rv, $email) = find_real_address ($to);
if ($rv == 1) {
do_debug ("[FOUND VACATION]: ", $messageid, $from, $to, $email, $subject);
send_vacation_email( $email, $subject, $from, $to, $messageid);
}
}

0;