Autoresponders to accompany Virtual Users and Domains with Postfix, Courier and MySQL

Want to support HowtoForge? Become a subscriber!
 
Submitted by todgerme (Contact Author) (Forums) on Mon, 2006-12-04 14:30. :: Postfix

Autoresponders to accompany Virtual Users and Domains with Postfix, Courier and MySQL

This guide focuses on making Yaa! (Yet Another Autoresponder!) work with Debian Sarge and the virtual users on Postfix tutorial (see http://www.howtoforge.com/virtual_postfix_mysql_quota_courier).

The first thing to understand about the guide is that it uses the Virtual  Delivery Agent (VDA) and as a result any of the auto responders that you could use by passing them to a pipe via a .forward file simply won't work as the VDA does not support .forward expansions.  This limitation can be overcome via Procmail/Maildrop delivery but that is messy and slower especially for mail systems that are heavily used.  The simple answer is Yaa!

 Lets download Yaa and get the party started:

mkdir -p /usr/local/postfix-tools 

cd  /usr/local/postfix-tools

wget http://frost.ath.cx/software/yaa/dist/yaa-0.3.tar.bz2

tar jxvf yaa-0.3.tar.bz2

Next we have to get the required Perl modules to make Yaa work.  Yaa works in both daemon mode however in this guide I am only going to concern myself with the non-daemonised mode of the program.

apt-get install libmldbm-perl libio-lockedfile-perl  libnet-perl libcarp-clan-perl libdbi-perl libdbd-mysql-perl libnet-server-perl libio-stringy-perl

Lets setup the database structure so that Yaa will work:

mysql -uroot -p

use mail;

CREATE TABLE `autoresponder` (
`active` tinyint(1) NOT NULL default '0',
`message` mediumtext NOT NULL,
`subject` varchar(250) NOT NULL default '',
`charset` varchar(250) NOT NULL default '',
`forward` varchar(250) NOT NULL default '',
`address` varchar(250) NOT NULL default '',
`local_domains` varchar(250) NOT NULL default '',
`tstart` int(32) NOT NULL default '0',
`tfinish` int(32) NOT NULL default '0',
PRIMARY KEY (`address`),
KEY `active` (`active`),
KEY `tstart` (`tstart`,`tfinish`)
)

quit;

Next we want to configure Postfix to invoke Yaa! via a transport so we need to edit /etc/postfix/master.cf to add the following:

 # yaa autoresponder
yaa     unix    -       n       n       -       -       pipe
                                                        user=vmail
                                                        argv=/usr/local/postfix-tools/yaa-0.3/bin/yaa.pl

Lets restart Postfix to enable the changes:

/etc/init.d/postfix restart

Finally we need to configure Yaa!  To make things easier, I have provided my yaa.conf

######################################################
# !!!!WARNING!!!! #
# DO NOT REMOVE OR COMMENT OUT THE FOLLOWING LINE #
use strict; #
######################################################

# NOTICE:
# If you're having trouble running yaa.pl and you're
# not shure, what's going wrong, set environment varibale
#
# YAA_DEBUG
#
# and run yaa.pl again with the same arguments/configuration file.
#
# Yaa debug output will be written to stderr if running in single
# message processing mode or in daemon mode with $daemon_background = 0.
#
# If you're running in daemon mode with $daemon_background = 1, debug messages
# will be sent to logger subsystem if it's enabled.
#
# example for csh:
# setenv YAA_DEBUG 1
#
# example for sh/ksh/bash:
# export YAA_DEBUG=1
#


# GENERAL NOTES
#
# excerpt taken from configuration file of excellent opensource project
# called amavisd-new <http://www.ijs.si/software/amavisd/>
# by Mark Martinec <http://www.ijs.si/people/mark/>.
#
# This file is a normal Perl code, interpreted by Perl itself.
# - make sure this file (or directory where it resides) is NOT WRITABLE
# by mere mortals, otherwise it represents a severe security risk!
# - for values which are interpreted as booleans, it is recommended
# to use 1 for true, and 0 or undef or '' for false.
# - Perl syntax applies. Most notably: strings in "" may include variables
# (which start with $ or @); to include characters @ and $ in double
# quoted strings, precede them by a backslash; in single-quoted strings
# the $ and @ lose their special meaning, so it is usually easier to use
# single quoted strings. Still, in both cases backslash needs to be doubled.


######################################################
# LOGGING SUBSYSTEM SETTINGS #
######################################################

# enable logging?
# type: boolean
# default: 1
# $logging = 1;

# enable logging to syslog?
# type: boolean
# default: 1
$log_syslog = 1;

# syslog logging facility
# type: string
# default: "mail"
# this setting applies only when logging to syslog is enabled
# $log_syslog_facility = "mail";

# syslog logging priority
# type: string
# default: "info"
# this setting applies only when logging to syslog is enabled
# $log_syslog_priority = "info";

# log to plaintext file?
# type: boolean
# default: 0
#$log_file = 1;

# log filename
# type: string
# default: undef
# this setting applies only when logging to file is enabled
#$log_file_filename = "/var/log/yaa.log";

# log to stderr?
# type: boolean
# default: 0
# !!!! WARNING !!!!
# when yaa debugging is turned on (environment variable YAA_DEBUG is set),
# STDERR output is mapped to logger subsystem and nothing actually doesn't show up
# on real stderr, so make shure to set up syslog or file based logging.
# !!!! WARNING !!!!
# $log_stderr = 0;

# log time format when logging in file
# type: string
# default: "[%a, %b %e %T %Y]: "
# see strftime(3) for more details
# $log_time_format = undef;


######################################################
# DAEMON MODE SETTINGS #
######################################################

# run as daemon?
# type: boolean
# default: 0, do not run as daemon
# this setting enables daemon mode operation.
# $daemon = 1;

$daemon = 0;
$daemon_lockfile = "/tmp/yaa.lock";
$daemon_pidfile = "/tmp/yaa.pid";
$daemon_tcpserver_loglevel = 4;

# fork into background when running as daemon?
# default: 1
# this setting applies only when operating in daemon mode
# $daemon_background = 1;

# daemon accept lock file
# type: string
# default: "/var/lock/yaa.lock"
# this setting applies only when operating in daemon mode
# WARNING: if running in chroot jail, this option should be
# set relative to chroot directory
# $daemon_lockfile = "/tmp/yaa.lock";

# daemon pid file
# type: string
# default: "/var/run/yaa.lock"
# this setting applies only when operating in daemon mode
# $daemon_pidfile = "/tmp/yaa.pid";

# minimum number of yaa child processes
# type: integer
# default: 2
# this setting applies only when operating in daemon mode
# $daemon_min_servers = 2;

# maximum number of yaa child processes
# type: integer
# default: 3
# this setting applies only when operating in daemon mode
# $daemon_max_servers = 3;

# minimum number of yaa spare child processes
# type: integer
# default: 1
# this setting applies only when operating in daemon mode
# $daemon_min_spare_servers = 0;

# maximum number of yaa spare child processes
# type: integer
# default: 1
# this setting applies only when operating in daemon mode
#$daemon_max_spare_servers = 1;

# daemon communication protocol
# type: string
# protocol which your MTA uses for communication with yaa
#
# NOTICE: for possible values for this configuration parameter, RUN
# yaa.pl --list-transport-protocols
#
# default: "SMTP"
# this setting applies only when operating in daemon mode
# $daemon_protocol = "LMTP";

# tcp listen port or unix domain socket on which yaa should listen
# type: integer/string
# default: 40000
# to specify unix domain socket set value to: '/path/to/socket|unix'
#
# see also: perldoc Net::Server::Proto
#
# this setting applies only when operating in daemon mode
# $daemon_listen_port = 40000;

# hostname which yaa should bind to.
# type: string
# default: "localhost"
# this setting applies only when operating in daemon mode
# $daemon_listen_host = "127.0.0.1";

# daemon tcpserver (Net::Server) log level
# type: integer
# default: 0
# 'O' => disable logging
# 0 => 'err'
# 1 => 'warning'
# 2 => 'notice'
# 3 => 'info'
# 4 => 'debug'
$daemon_tcpserver_loglevel = 4;


######################################################
# YAA OBJECT SETTINGS #
######################################################

# directory used to store autoresponse message sent time database.
# type: string
# default: "/tmp"
# !!!! WARNING !!!!
#
# - if you're running yaa chrooted (see configuration variable $chroot),
# then you need to set this variable to value RELATIVE to chroot directory
#
# - directory must be writeable for uid/gid which yaa uses for message processing
# see also $user and $group variables
#
# !!!! WARNING !!!!
# $db_dir = "/db";

# time in seconds between to autoresponses will be sent to the
# same message sender from message recipient which has autoresponder
# turned on.
#
# !!!! WARNING !!!!
#
# For testing purposes, set to -1 (turn off time checking),
# BUT DO NOT SET THIS VALUE LOWER THAN 3600 (1 hour) ON
# PRODUCTION SYSTEM !!!!
#
# !!!! WARNING !!!!
# type: integer
# default: 7200
# $duration_interval = 24 * 60 * 60;
$duration_interval = "-1";

######################################################
# AUTORESPONSE SETTINGS #
######################################################

# method used for sending autoresponses and forwaring messages
# type: string
# possible values: "smtp", "sendmail"
# - "smtp" uses smtp server to send mail
# - "sendmail" invokes sendmail binary to send mail
#
# !!!!WARNING!!!!: weird things happen when using sendmail sending
# method and running in daemon mode!
#
# default: "smtp"
# $mail_sending_method = "smtp";

# sendmail program path
# type string
# default: automaticaly searched in $PATH environmental variable;
# undef if not found in $PATH.
#$sendmail_path = undef;

# SMTP server setting
# type: string
#
# see also perldoc Net::SMTP
#
# default: localhost
# $smtp_server = "localhost";

# Use SMTP auth?
# type: boolean
# default: 0
# $smtp_auth = 0;

# SMTP auth username
# type: string
# default: undef
# $smtp_username = undef;

# SMTP auth password
# type: string
# default: undef
# $smtp_password = undef;


######################################################
# LOOKUP MAP SETTINGS #
######################################################

# List of ALL lookup maps
# You need to define lookup maps here and
# then set lookup_map_query_order, where you reference to
# the lookup name
#
# NOTICE:
# All lookup maps are initialized BEFORE Yaa! enters chroot jail (if any)
# and BEFORE starts processing emails.
#
#
# NOTICE: To obtain list of all lookup drivers, RUN
# yaa.pl --list-lookup-map-drivers
#
# type: hash of hashes
# default: empty hash (no defined lookup maps)
$lookup_maps = {
#
# !!!WARNING!!!!
#
# FOR COMPLETE LIST OF DRIVER CONFIGURATION ARGUMENTS
# RUN yaa.pl --show-lookup-map-doc <DRIVER>
#

# Lookup map configuration format
#
# 'map_name' => {
# 'driver' => 'DRIVER_NAME',
# 'driver_param1' => 'value1'.
# 'driver_param2' => 'value2',
# 'driver_param3' => 'value3',
# },

# SQL lookup map example
#
# (used sql database: mysql)
# (for other types see perldoc DBD::<yourdb>)
#
'my_sql_map' => {
'driver' => 'SQL',
'sql_dsn' => 'dbi:mysql:database=mail;host=localhost',
'sql_username' => "mail_admin",
'sql_password' => "mail_admin_password",
'sql_select' => "select active,message,subject,charset,forward from autoresponder where address = %m and active='1'",
},

# PCRE lookup map example
#
# 'my_pcre_map' => {
# 'driver' => 'PCRE',
# 'file' => "file.pcre",
# 'replacement_num' => 0
#},

# STATIC lookup map example
# 'my_static_map' => {
# 'driver' => 'STATIC',
# 'result_key1' => 'result_value1',
# 'result_key2' => 'result_value2',
# 'result' => 'sth'
#},

# LDAP lookup map sample
# 'my_ldap_map' => {
# 'driver' => 'LDAP',
# 'ldap_host' => 'ldap.example.org',
#
# 'ldap_bind' => 1,
# 'ldap_bind_dn' => "cn=Manager,dc=example,dc=org",
# 'ldap_bind_pw' => "secret",
#
# 'ldap_search_base' => "ou=MyOU,dc=example,dc=org",
# 'ldap_search_filter' => "(&(objectClass=rfc822Recipient)(mail=%m)(accountActive=1)",
# 'ldap_search_attrs' => ['autoResponseActive', 'autoResponseSubject', 'autoresponseMessage', 'autoResponseCharset', 'autoresponseForward'],
#},

# DB_File lookup map sample
# 'my_dbf_map' => {
# 'driver' => 'DB_File',
# 'file' => "/path/to/mydb",
#},

# BerkeleyDB lookup map sample
#'my_dbd_map' => {
# 'driver' => 'BerkeleyDB',
# 'type' => 'Btree',
# 'file' => "/path/to/mydb"
#},
};

# lookup map query order by attribute
# for each autoresponse except 'rewrite_recipient' and 'rewrite_sender' must
# be defined lookup query order
#
# Each item can be specified as:
# + string (example: 'domain.tld')
#
# OR
#
# lookup_map_name:result_value (example: 'my_pcre_map:result')
#
# Lookup map is recognized by ':' character in string.
#
# Lookup map 'lookup_map_name' MUST BE specified in $lookup_maps configuration
# parameter in yaa.conf
#
# type: hash of hashes
# default: empty hash (no lookups order lists defined)
#
#
$lookup_map_query_order = {
active => [
'my_sql_map:active'
],
subject => [
'my_sql_map:subject'
],
message => [
'my_sql_map:message'
],
charset => [
# 'my_sql_map:charset'
],
forward => [
'my_sql_map:forward'
],
rewrite_sender => [
#empty
],
rewrite_recipient => [
#empty
],
'local_domains' => [
'my_sql_map:local_domains'
],
};

######################################################
# OTHER SETTINGS #
######################################################

# chroot to some directory?
# type: string
# default: undef, do not chroot
# Warning: yaa must be started as superuser to enable this feature.
# this applies to daemon and single message processing mode
# $chroot = undef;

# change uid/gid before processing?
# type: string
# default: undef, do not change uid/gid
# Warning: yaa must be started as superuser to enable this feature.
# this applies to daemon and single message processing mode
# $user = undef;
# $group = undef;

# Load additional perl modules before processing any message
# Modules in this list will be loaded before Yaa! will process any
# message in single process mode or become daemon, when running
# in daemon mode.
#
# This configuration parameter is very handy when
# when running in chroot jail
#
# type: array
# default: empty array (don't load any additional modules)
@extra_modules = (
# !!!!WARNING!!!!
# when as chrooted daemon, uncomment the following line
# 'Net::Server::Mail::ESMTP::PIPELINING',

# if running chrooted, and using mysql version
# of SQL lookup map, uncomment the following line
#'DBD::mysql',

# if running chrooted, and using postgres version
# of SQL lookup map, uncomment the following line
#'DBD::Pg',
);

######################################################
# !!!!WARNING!!!! #
# COMMENT OUT THE FOLLOWING LINE TO MAKE YAA WORK! #
# !!!!WARNING!!!! #
######################################################
#die "You haven't edit configuration file, have you?:))";

######################################################
# !!!!WARNING!!!! #
# DO NOT REMOVE OR COMMENT OUT THE FOLLOWING LINE #
1; #
######################################################

You will need to find

 'my_sql_map' => {
'driver' => 'SQL',
'sql_dsn' => 'dbi:mysql:database=mail;host=localhost',
'sql_username' => "mail_admin",
'sql_password' => "mail_admin_password",
'sql_select' => "select active,message,subject,charset,forward from autoresponder where address = %m and active='1'",
and edit for your own username and password.

Next we need to add a few editional database entries.

INSERT INTO `forwardings` ( `source` , `destination` ) VALUES ('user@domain.com', 'user@domain.com, user@autoreply.domain.com');

INSERT INTO `transport` ( `domain` , `transport` ) VALUES ('autoreply.domain.com', 'yaa');

INSERT INTO `autoresponder` ( `active` , `message` , `subject` , `charset` , `forward` , `address` , `local_domains` , `tstart` , `tfinish` ) VALUES ('1', 'Message Body', 'Message Subject', '', '', 'user@domain.com', 'autoreply.domain.com', '0', '0');

In addition you can populate tstart/tfinish values, these are done in Unix timestamp and allow you to enable the autoresponder for a particular time period - great for people who always forget to turn them off after they return to the office for example!

INSERT INTO `autoresponder` ( `active` , `message` , `subject` , `charset` , `forward` , `address` , `local_domains` , `tstart` , `tfinish` ) VALUES ('1', 'Message Body', 'Message Subject', '', '', 'user@domain.com', 'autoreply.domain.com', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + 14400);

Under Thunderbird I noticed some very quirky behaviour regarding the Subject of the reply so I googled and found a small patch to fix the problem.

Create a file called email_subject.patch:

--- Autoresponse.pm     2004-08-19 20:35:28.000000000 +0200
+++ Autoresponse.pm 2005-07-14 13:16:03.000000000 +0200
@@ -182,7 +182,11 @@
push(@headers, "Precedence: bulk");

# Subject
- push(@headers, "Subject: =?" . (($self->{charset}) ? $self->{charset} : $self->{default_charset}) . "?Q?" . encode_qp($subject) . "?=");
+ #
+ # Fixes the strange subjects that email clients don't understand
+ # (second argument on encode_qp should be "")
+ push(@headers, "Subject: =?" . (($self->{charset}) ? $self->{charset} : $self->{default_charset}) . "?Q?" . encode_qp($subject,"") . "?=");


push(@headers, "MIME-Version: 1.0"); 

 Next we apply the patch:

cd /usr/local/postfix-tools/yaa-0.3/lib/

patch -p0 <email_subject.patch

This guide does not concern itself with the daemonised version of Yaa! which would probably be more efficient on extremely high volumes of mail but it should only be a matter of tweaking the yaa.conf file if the daemon version is required.

Yaa has some great features such as not replying the same person within a certain time period so it can be very intelligent especially as you can get two auto responder programs responding to themselves and creating a loop.  This does not happen with Yaa! 


Please do not use the comment function to ask for help! If you need help, please use our forum.
Comments will be published after administrator approval.
Submitted by fabs (not registered) on Thu, 2009-08-27 13:27.

Hi all,

in order to exclude SPAM messages to be responded, i've added a single line in a yaa! file.

This work with spammassassin because it add X-Spam-Flag to the mail.

And use the MailingList detection runtime.

In 'yaa-0.3.1/lib/Yaa/Message.pm'

near line 461, add this line :

$self->getHeader('X-Spam-Flag', 1)


dont forget the || on precedent line.

Result block :

# is this a mailing list post?
if (
lc($self->getHeader('Precedence', 1)) eq 'list' ||
$self->getHeader('List-Id', 1) ||
$self->getHeader('List-Subscribe', 1) ||
$self->getHeader('List-Unsubscribe', 1) ||
$self->getHeader('List-Archive', 1) ||
$self->getHeader('List-Post', 1) ||
$self->getHeader('X-Mailing-List', 1) ||
$self->getHeader('Mailing-List', 1) ||
$self->getHeader('X-List', 1) ||
$self->getHeader('X-Spam-Flag', 1)
) {
$self->{_msg_flag_mailinglist} = 1;
}

that's all.

There is a better way ?

Submitted by Kimzu (not registered) on Mon, 2010-08-09 12:48.

At least for me the above method happily ignored all messages because Spamassassin sets the header
X-Spam-Flag:
to all messages.
The value is either YES or NO

so the correct way for me is
$self->getHeader('X-Spam-Flag', 1) eq 'YES'

The whole if:


if (
                        lc($self->getHeader('Precedence', 1)) eq 'list' ||
                        $self->getHeader('List-Id', 1) ||
                        $self->getHeader('List-Subscribe', 1) ||
                        $self->getHeader('List-Unsubscribe', 1) ||
                        $self->getHeader('List-Archive', 1) ||
                        $self->getHeader('List-Post', 1) ||
                        $self->getHeader('X-Mailing-List', 1) ||
                        $self->getHeader('Mailing-List', 1) ||
                        $self->getHeader('X-List', 1) ||
                        $self->getHeader('X-Spam-Flag', 1) eq 'YES'
                ) {
                $self->{_msg_flag_mailinglist} = 1;
        }


Submitted by mac (not registered) on Tue, 2008-11-18 09:09.

hi,

after a  few minutes my autoresponder was working as it have to.

the link to the YAA!-scripts isn't up to date

here's the working link to YAA!0.3.1: http://www.sourcefiles.org/Internet/Mail/Utilities/Autoresponders/yaa-0.3.1.tar.bz2

thx&bye
mac 

Submitted by Dee-Lee (not registered) on Mon, 2009-05-25 11:05.
Submitted by Luke (not registered) on Thu, 2009-05-21 12:03.

That link is broken too now - I managed to find it via archive.org though:

http://web.archive.org/web/20070612112516/http://frost.ath.cx/software/yaa/dist/yaa-0.3.1.tar.bz2