#!/usr/bin/perl -w
#
# autoreply.pl - Automatic email reply.
#
# All messages are logged to your mail log. Check the
# log after executing the script to see the results.
#
# Set $UID to the uid of the process that runs the script.
# Check the entry in master.cf that calls this script. Use
# the uid of the account you assign to the user= attribute.
# If you want to test the script from the command line,
# set $UID to your own uid.
#
# Set the %mysql_config hash values for each predefined
# key in order to connect to the database holding
# autoreply subject and message.
#
# If necessary, change the path to sendmail in $MAILBIN.
#
# @MAILOPTS contains options to sendmail. Make changes if
# necessary. The default options should work in most
# situations.
#
# The calls to syslog require that your Perl installation
# converted the necessary header files. See h2ph in your
# Perl distribution.
#

require 5.004;	# for setlogsock in Sys::Syslog module

use strict;
use Sys::Syslog qw(:DEFAULT setlogsock);
use DBI;
use DB_File;

#
# Config options. Set these according to your needs.
#
my $UID = 5000;

my %mysql_config = (
	'server'   => 'localhost',
	'port'     => '3306',
	'database' => 'DATABASE',
	'table'	   => 'TABLE',
	'username' => 'USERNAME',
	'password' => 'PASSWORD'
);

my $MAILBIN               = "/usr/sbin/sendmail";
my @MAILOPTS              = ("-oi", "-tr", "");
my $SELF                  = "autoreply.pl";
my $DB_AUTOREPLY          = "/var/cache/autoreply/autoreply.db";
my $NO_AUTOREPLY_INTERVAL = 172800; # 48h in seconds
#
# end of config options

my $EX_TEMPFAIL = 75;
my $EX_UNAVAILABLE = 69;
my $EX_OK = 0;
my $sender;
my $addressee;
my $subject;
my $euid = $>;

$SIG{PIPE} = \&PipeHandler;
$ENV{PATH} = "/bin:/usr/bin:/sbin:/usr/sbin";

setlogsock('unix');
openlog($SELF, 'ndelay,pid', 'user');

#
# Check our environment.
#
if ( $euid != $UID ) {
	syslog('mail|err', "error: invalid uid: $> (expecting: $UID)");
	exit($EX_TEMPFAIL);
}
if ( @ARGV != 2 ) {
	syslog('mail|err', "error: invalid invocation (expecting 2 arguments: sender and addressee)");
	exit($EX_TEMPFAIL);
} else {
	$sender = $ARGV[0];
	$addressee = $ARGV[1];

	if ( $sender =~ /([\w\-.%]+\@[\w.-]+)/ ) { # scrub address
		$sender = $1;
	} else {
		syslog('mail|err', "error: illegal sender address: $sender");
		exit($EX_UNAVAILABLE);
	}
	if ( $addressee =~ /([\w\-.%]+\@[\w.-]+)/ ) { # scrub address
		$addressee = $1;
	} else {
		syslog('mail|err', "error: illegal addressee address: $addressee");
		exit($EX_UNAVAILABLE);
	}
}
if (! -x $MAILBIN ) {
	syslog('mail|err', "error: $MAILBIN not found or not executable");
	exit($EX_TEMPFAIL);
}

#
# Check sender exception.
#
if ( $sender eq ""
	|| $sender =~ /^owner-|-(request|owner)\@|^(mailer-daemon|postmaster)\@/i) {
	exit($EX_OK);
}

#
# Check message contents for Precedence header.
#
while ( <STDIN> ) {
	last if (/^$/);
	if ( /^subject:\s+(.*)$/i ) {
		$subject = $1;
	}
	exit($EX_OK) if ( /^precedence:\s+(bulk|list|junk)/i );

	# See http://tools.ietf.org/html/rfc3834
	exit($EX_OK) if ( /^auto-submitted:\s+(auto-generated|auto-replied)/i ); 
}

#
# Check no autoreply interval
#
my %db_handle;
my $key = "$sender->$addressee";
my $need_autoreply = 1;

tie %db_handle, "DB_File", $DB_AUTOREPLY, O_RDWR|O_CREAT, 0640, $DB_HASH
	|| syslog('mail|err', "error: cannot open file " . $DB_AUTOREPLY . ": $!");

# Show all key/values
#while ( my ($k, $v) = each %db_handle ) {
#	syslog('mail|info', "$k = $v");
#}

if ( $db_handle{$key} ) {
	my $last_sent = $db_handle{$key};
	if ( time - $last_sent < $NO_AUTOREPLY_INTERVAL ) {
		$need_autoreply = 0;
	}
}

if ( ! $need_autoreply ) {
	untie %db_handle;
	exit($EX_OK);
}

delete $db_handle{$key};
$db_handle{$key} = time;
untie %db_handle;

#
# Connect to the database
#
my $dsn = 'DBI:mysql:' . $mysql_config{database} . ':'
	. $mysql_config{server} . ':' . $mysql_config{port};
my $dbh = DBI->connect($dsn, $mysql_config{username},
	$mysql_config{password}) || syslog('mail|err', "error: cannot connect to database: $DBI::errstr");

my $sth = $dbh->prepare(
	'SELECT autoreply_message FROM ' .$mysql_config{table}
	. ' WHERE autoreply_mailbox_email = \'' . $addressee . '\''
);
$sth->execute();

my @row = $sth->fetchrow_array();
if ($sth->err()) {
	syslog('mail|err', "error: query failed: $DBI::errstr");

	$dbh->disconnect;
	exit($EX_TEMPFAIL);
}

$sth->finish();

my $message = $row[0];

$dbh->disconnect || syslog('mail|err', "warning: disconnection failed: $DBI::errstr");

#
# Open pipe to mailer.
#
my $pid = open(MAIL, "|-") || exec("$MAILBIN", @MAILOPTS);

#
# Send reply.
#
print MAIL "To: $sender\n";
print MAIL "From: $addressee\n";
print MAIL "Return-Path:\n";
print MAIL "Auto-Submitted: auto-replied\n";
print MAIL "Content-Type: text/plain; charset=UTF-8; format=flowed\n";
print MAIL "Subject: Auto: $subject\n\n";
print MAIL $message;

if (! close(MAIL) ) {
	syslog('mail|err', "error: failure invoking $MAILBIN: %m");
	exit($EX_UNAVAILABLE);
}

syslog('mail|info', "sent reply to $sender");
exit($EX_OK);

sub PipeHandler {
	syslog('mail|err', "error: broken pipe to mailer.");
}

