Congfigure vacation

written by Thomas Haas.

As basic installation I used Howto: ISP-style Email Server with Debian-Etch and Postfix 2.3 written by Christoph Haas.

The basic idea of my vacation setup came from Postfix, Courier, MySQL, Vacation mini-HOWTO by Fred.

Contents

  1. Database changes
  2. The vacation-script
  3. A new mapping-file
  4. Deliver vacation-messages
  5. Finish
  6. Configuration

1. Database changes

I did not add another table. That would be only needed if you want to make vacation-rules for the aliases, not only for the users.

We add two columns to the virtual_users-Table.

ALTER TABLE virtual_users
 ADD vacation_subject VARCHAR( 255 ) NULL ,
 ADD vacation_text TEXT NULL ;

The view view_users needs to be changed. Be careful when dropping the old one! Did you change it? Or is it still the same as Christoph described?

DROP VIEW view_users;

CREATE VIEW view_users AS
SELECT CONCAT(virtual_users.user, '@', virtual_domains.name) AS email,
    virtual_users.password,
    virtual_users.vacation_subject,
    virtual_users.vacation_text
FROM virtual_users
LEFT JOIN virtual_domains ON virtual_users.domain_id=virtual_domains.id;

2. The vacation-script

I prefer php, that's why I used it. Fell free to make your own changes here!

#!/usr/bin/php5
<?php

    $mysql_host = 'localhost';
    $mysql_user = 'mailuser';
    $mysql_pass = 'mailuser2007';
    $mysql_db   = 'mailserver';

    $subject = 'Autoresponder';
    $text = 'Ich bin momentan abwesend. Die Mail wurde in meinem Postfach gespeichert';
    $text .= "\n\n";
    $text .= 'I\'m on vacation. Your mail was saved in my mailbox.';

    //get mailheader - content of mail is passed via stdin (piped in master.cf)
    //
    $mailheader = '';
    $i = 0;

    if ($fp = fopen("php://stdin","r")){
        while (!feof($fp)) {
            $line = fgets($fp,4096);
            if(trim($line)==''){
                break;
            }
            $mailheader .= $line;
            $i++;
            if($i>555550){
                break;
            }
        }
        fclose($fp);
    }

    //if Mail is marked as spam: return
    //
    $spam_match = preg_match('/^X-Spam-Flag/m',$mailheader);
    if($spam_match){
        return;
    }

    //get orignal-subject
    //
    $subject_match = preg_match('/^subject: (.*)$/im',$mailheader,$out);
    $original_subject = '';
    if($subject_match){
        $original_subject = $out[1];
        $subject = 'Re: '.$original_subject;
    }

    $original_sender = $_SERVER['argv'][1];
    $original_recipient = $_SERVER['argv'][2];  //with ".vacation" added!^

    $len = strlen('.vacation');
    $len1 = strlen($original_recipient);
    $recipient = substr($original_recipient, 0, $len1 - $len);

    $headers = 'From: '.$recipient;

    //Get real vacation-subject and text from database
    //
    $conn = mysql_connect($mysql_host, $mysql_user, $mysql_pass);
    mysql_select_db($mysql_db, $conn);

    $fp = mysql_query('SELECT vacation_subject, vacation_text FROM view_users WHERE email = "'.mysql_real_escape_string($recipient, $conn).'"', $conn);
    $buf = mysql_fetch_object($fp);

    if(!empty($buf->vacation_subject)){
        $subject = $buf->vacation_subject;
    }

    if(!empty($buf->vacation_text)){
        $text = $buf->vacation_text;

        $text = str_replace('$subject', $original_subject, $text);
    }

    mysql_close($conn);

    mail($original_sender, $subject, $text, $headers);

?>

I placed the script to /home/vmail/vacation.php and changed the rights to (chmod 700, chown vmail:vmail).

3. A new mapping-file

/etc/postfix/mysql-transport.cf

Neither I want to a add a "transport"-field to the virtual_domains-Table nor I want to handle "vacation-domains".
Thats's why that new file ist created. It maps only existing domains an tells Postfix to deliver mail via dovecot. But if we sent mail to domain.tld.vacation, it works also fine, but tells Postfix to deliver mail via vacation.

user = mailuser
password = mailuser2007
hosts = 127.0.0.1
dbname = mailserver
query = SELECT if("%s" LIKE "%%.vacation", "vacation", "dovecot") FROM `view_domains` WHERE name LIKE "%s" OR CONCAT(name,".vacation") LIKE "%s"

And we need to make Postfix use this mapping:

postconf -e transport_maps=mysql:/etc/postfix/mysql-transport.cf

4. Deliver vacation-messages

Add a new service to /etc/postfix/master.cf

vacation  unix -        n       n       -       -       pipe
 flags=Rq user=vmail:vmail argv=/var/vmail/vacation.php ${sender} ${recipient}

5. Finish

Restart postfix now: /etc/init.d/postfix restart

6. Configuration

To configure vacation you only need to work with the database (or you use your own administration-script).

If you want to set up vacation for john@example.com add the following to the virtual_aliases-table:
Source: john
Destination: john@example.com.vacation

But now john will never receive the mail!. That's why add another line if it doesn't exist yet:
Source: john
Destination: john@example.com

You may want to setup your own message (with your own subject) in the virtual_users-table. In the text you can place the word $subject, which will be replaced by the subject of the original-message.

My administration script does this automatically, but it's not in a stable state, so I will publish it later...