Postfix, virtual_alias and vacation

Posted by Roy de Boer on 3 February 2006

Tag(s): Artikel

When using local(8) delivery in postfix, sending automatic vacation messages is easy; just run 'vacation'.

 How does it work? Incomming mail will be delivered by the local(8) mail delivery program. It reads the file .forward in the users maildirectory. If the .forward file contains a pipe (|) the mail is processed by a program, in this case /usr/bin/vacation. So when .forwards contains

\john, "| /usr/local/bin/vacation username"

all mail directed to john will be handled by vacation. Vacation will respond with a nice email response.

So what's the problem? When using virtual_alias there is no 1:1 relation between users and email adresses. In fact, all email on the server may be stored under the same uid!

I will explore two ways to pipe a mail message through a program. Finaly I will give an example of a vacation script capable of handling multiple emailadresses.

Method 1

Make sure you have a virtual_alias_maps entry in main.cf, for example:

virtual_alias_maps = hash:/etc/postfix/virtual_alias

further more, make sure a dummy user exists, e.g. 'vacation'. 

Say we want to pipe john's mail to a program, we can just redirect it to vacation@localhost by adding the following line to virtual_alias:

john@infi.nl			vacation@localhost,john@infi.nl

Don't forget to run

postmap /etc/postfix/virtual_alias. 

The beautyfull thing about virtual_alias is, it won't change the 'To:' field. If it would, all messages would be directed to vacation@localhost and a per-user vacation messages would be impossible. 

Now, when you send a message to john@infi.nl, it will be delivered to both his virtual mailbox and to the local vacation user. Depending on your configuration, the message will be delivered to /home/vacation/Maildir or /var/spool/mail/vacation.

Finaly we can pipe the contents to a vacation script:

echo "| /usr/bin/vacation" > /home/vacation/.forward 

Method 2

The second method is somewhat more sophisticated. The local(8) delivery program seems a little bit unnecessary. We can use postfix transports to send a message through a program. First we create a mail delivery service in master.cf:

cat >> master.cf
vacation	
	unix -        n       n       -       -       pipe
	flags=F user=vacation argv=/usr/bin/perl /usr/bin/vacation.pl ${sender} ${recipient}
^D

Now we have to redirect mail through this delivery service, this can be done by adding the following line to transport:

vacation.infi vacation:

vacation.infi is a domain, vacation: is the previously defined mail delivery service. So this line means 'all mail to vacation.infi must be directed to the vacation mail delivery service'.

Now we redirect johns to john#infi.nl@vacation.infi by adding the following line to virtual_alias:

john@infi.nl			john#infi.nl@vacation.infi,john@infi.nl
Note I wrote john#infi.nl, we can instead do something like
john@infi.nl			something@vacation.infi,john@infi.nl

however, the mail script cannot trust parameter 2, ${recipient}, but has to do some kind of regexp on the mail itself. No big deal, ofcourse. 

The mail program

Create the following mysql tabel:

CREATE TABLE `vacation` (
`id` int(11) NOT NULL auto_increment,
`email` varchar(255) NOT NULL default '',
`subject` varchar(255) NOT NULL default '',
`body` text NOT NULL,
PRIMARY KEY  (`id`)
) TYPE=MyISAM COMMENT='Virtual Vacation - Vacation Daemon Table';
And store the following script at /usr/bin/vacation.pl:
#! /usr/bin/perl -w
#
# Vitual Vacation
# Version 1.0
# 2003 (c) High5!
# Created by: Mischa Peters 
# tiny modifications by Roy de Boer
#
use DBI;

$db_name = "";
$db_user = "";
$db_pass = "";
$sendmail = "/usr/sbin/sendmail -t";
$logfile = "logfile";

@input = <>;

for ($i = 0; $i <= $#input; $i++) {
	if ($input[$i] =~ /From: (.*)n$/) { $from = $1; }
	if ($input[$i] =~ /X-Original-To: (.*)n$/) { $to = $1; }
	if ($input[$i] =~ /Subject: (.*)n$/) { $subject = $1; }
}

$to =~ m/(S*@S*)/;
$email = $1;

if ($logfile) {
	open (FILE, ">> $logfile") or die ("Unable to open log file");
	chop ($date = `date "+%Y/%m/%d %H:%M:%S"`);
	print FILE "$date: $from - $to - $subjectn";
	close (FILE);
}

$query = qq{
	SELECT subject,body
	FROM vacation
	WHERE email='$email'
};

$dbh = DBI->connect("DBI:mysql:$db_name:127.0.0.1", "$db_user", "$db_pass", { RaiseError => 1});
$sth = $dbh->prepare($query) or die "Can't prepare $query: $dbh->errstrn";
$sth->execute or die "Can't execute the query: $sth->errstr";
$rv = $sth->rows;

if ($rv == 1) {
	@row = $sth->fetchrow_array;
	$new_subject = $row[0];
	if ($new_subject == "") {
		$new_subject = $subject;
	}
	open (MAIL, "| $sendmail") or die ("Unable to open sendmail");
	print MAIL "From: $ton";
	print MAIL "To: $fromn";
	print MAIL "Subject: $new_subjectn";
	print MAIL "X-Loop: Postfix Vacationnn";
	$row[1] =~ s/rn/n/g;
	print MAIL "$row[1]";
	close (MAIL);
}
1;

 

Post a comment:

Name:
E-mail*:
Comment:
*optional, will not be published.