2022-08-18 14:01:52 +02:00
|
|
|
#!/usr/bin/env perl
|
|
|
|
#
|
|
|
|
# Copyright (c) 2022 Mischa Peters <mischa @ high5.nl>
|
|
|
|
#
|
|
|
|
# Permission to use, copy, modify, and distribute this software for any
|
|
|
|
# purpose with or without fee is hereby granted, provided that the above
|
|
|
|
# copyright notice and this permission notice appear in all copies.
|
|
|
|
#
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
|
|
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
|
|
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
|
|
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
|
|
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
|
|
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
|
|
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
#
|
|
|
|
use 5.024;
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use autodie;
|
|
|
|
use Getopt::Std;
|
|
|
|
use DBI;
|
2022-08-20 07:59:10 +02:00
|
|
|
use POSIX qw(strftime);
|
2022-08-18 14:01:52 +02:00
|
|
|
|
2022-08-22 21:22:56 +02:00
|
|
|
# -c = name SQL config file for OpenSMTPD, located in /etc/mail
|
2022-08-22 18:22:27 +02:00
|
|
|
# -l = logging of virtual vacation parsed report/filter streams and decisions
|
|
|
|
# -v = verbose (extra flag) logging of report stream
|
|
|
|
# -d = debug (extra flag) logging of filter stream
|
|
|
|
getopts('c:lvd');
|
|
|
|
our($opt_c, $opt_l, $opt_v, $opt_d);
|
|
|
|
|
2022-08-18 14:01:52 +02:00
|
|
|
my $db_type = 'MariaDB';
|
|
|
|
my $db_host = '';
|
|
|
|
my $db_user = '';
|
|
|
|
my $db_pass = '';
|
|
|
|
my $db_name = '';
|
|
|
|
|
2022-08-22 21:22:56 +02:00
|
|
|
if ($opt_c && -e "/etc/mail/$opt_c") {
|
|
|
|
open (my $fh_config, '<', "/etc/mail/$opt_c");
|
2022-08-22 18:22:27 +02:00
|
|
|
while (my $line = <$fh_config>) {
|
|
|
|
chomp $line;
|
|
|
|
if ($line =~ /^host\s+(.*)$/) { $db_host = $1; }
|
|
|
|
if ($line =~ m/^username\s+(.*)$/) { $db_user = $1; }
|
|
|
|
if ($line =~ m/^password\s+(.*)$/) { $db_pass = $1; }
|
|
|
|
if ($line =~ m/^database\s+(.*)$/) { $db_name = $1; }
|
|
|
|
}
|
|
|
|
close $fh_config;
|
|
|
|
} else {
|
|
|
|
print "ERROR: UNABLE TO LOCATE CONFIG FILE!\n";
|
|
|
|
exit 1;
|
|
|
|
}
|
2022-08-18 14:01:52 +02:00
|
|
|
|
|
|
|
my %ooo;
|
2022-08-22 18:22:27 +02:00
|
|
|
my $email = '';
|
|
|
|
my $from = '';
|
2022-08-18 14:01:52 +02:00
|
|
|
my $dbh = DBI->connect("DBI:$db_type:$db_name:$db_host", "$db_user", "$db_pass", {RaiseError => 1});
|
2022-08-22 18:22:27 +02:00
|
|
|
my $selvacation = $dbh->prepare("SELECT subject,body FROM vacation WHERE email=? and active='1'");
|
|
|
|
my $selcache = $dbh->prepare("SELECT cache FROM vacation WHERE email=? AND FIND_IN_SET(?, cache)");
|
|
|
|
my $upcache = $dbh->prepare("UPDATE vacation SET cache=CONCAT(cache,',',?) WHERE email=?");
|
|
|
|
|
2022-08-20 11:41:02 +02:00
|
|
|
sub dolog {
|
|
|
|
my ($fh, $msg, $opt) = @_;
|
|
|
|
print $fh (POSIX::strftime("%h %d %H:%M:%S ", localtime) . "Virtual Vacation: $msg\n") if ($opt);
|
2022-08-20 08:13:12 +02:00
|
|
|
}
|
2022-08-18 14:01:52 +02:00
|
|
|
|
2022-08-20 10:31:15 +02:00
|
|
|
open (my $fh, '>', "/tmp/virtualvacation.log") if ($opt_l || $opt_v || $opt_d);
|
2022-08-18 14:01:52 +02:00
|
|
|
select(STDOUT);
|
|
|
|
$|++;
|
|
|
|
select($fh);
|
|
|
|
$|++;
|
2022-08-18 16:57:25 +02:00
|
|
|
print STDOUT "register|report|smtp-in|tx-mail\n";
|
|
|
|
print STDOUT "register|report|smtp-in|tx-rcpt\n";
|
2022-08-18 14:01:52 +02:00
|
|
|
print STDOUT "register|filter|smtp-in|data-line\n";
|
|
|
|
print STDOUT "register|ready\n";
|
|
|
|
|
|
|
|
while (my $line = <>) {
|
|
|
|
next if ($line =~ m/^config/);
|
|
|
|
chomp $line;
|
2022-08-18 16:57:25 +02:00
|
|
|
if ($line =~ m/^report/) {
|
2022-08-20 11:41:02 +02:00
|
|
|
dolog($fh, "$line", $opt_v);
|
2022-08-18 16:57:25 +02:00
|
|
|
my ($stream, $version, $timestamp, $subsystem, $event, $sid, $token, $code, $address) = split /\|/, $line;
|
|
|
|
if ($event eq "tx-mail" && $code eq "ok") {
|
2022-08-18 14:01:52 +02:00
|
|
|
$ooo{$sid} = 1;
|
2022-08-18 16:57:25 +02:00
|
|
|
$from = $address;
|
2022-08-20 11:41:02 +02:00
|
|
|
dolog($fh, "$sid created session", $opt_l);
|
2022-08-21 13:07:17 +02:00
|
|
|
if ($from =~ m/^(postmaster|hostmaster)@|.*(noreply|no-reply).*@|.*bounce.*/i) {
|
2022-08-20 12:10:48 +02:00
|
|
|
$ooo{$sid} = 0;
|
2022-08-20 12:26:20 +02:00
|
|
|
dolog($fh, "$sid from skip $from", $opt_l);
|
2022-08-20 12:10:48 +02:00
|
|
|
}
|
2022-08-18 16:57:25 +02:00
|
|
|
} elsif ($event eq "tx-mail" && $code ne "ok") {
|
|
|
|
$ooo{$sid} = 0;
|
|
|
|
}
|
|
|
|
if ($event eq "tx-rcpt" && $code eq "ok") {
|
|
|
|
$email = $address;
|
|
|
|
} elsif ($event eq "tx-rcpt" && $code ne "ok") {
|
|
|
|
delete $ooo{$sid};
|
2022-08-20 12:26:20 +02:00
|
|
|
dolog($fh, "$sid removed session - rcpt doesn't exist", $opt_l);
|
2022-08-18 14:01:52 +02:00
|
|
|
}
|
2022-08-18 16:57:25 +02:00
|
|
|
}
|
|
|
|
if ($line =~ m/^filter/) {
|
2022-08-20 11:41:02 +02:00
|
|
|
dolog($fh, "$line", $opt_d);
|
2022-08-18 16:57:25 +02:00
|
|
|
my ($stream, $version, $timestamp, $subsystem, $event, $sid, $token, $data) = split /\|/, $line;
|
2022-08-18 14:01:52 +02:00
|
|
|
if ($line =~ m/data-line/) {
|
|
|
|
if (!$data) { $data = ""; }
|
2022-08-20 20:25:28 +02:00
|
|
|
if ($data =~ m/^(precedence:\s+(bulk|list|junk)|list-(help|id|owner|post|subscribe|unsubscribe)|x-loop:\s+opensmtpd\ admin)/i) {
|
2022-08-20 12:10:48 +02:00
|
|
|
$ooo{$sid} = 0;
|
2022-08-20 12:26:20 +02:00
|
|
|
dolog($fh, "$sid header skip $data", $opt_l);
|
2022-08-20 12:10:48 +02:00
|
|
|
}
|
2022-08-18 14:01:52 +02:00
|
|
|
print STDOUT "filter-dataline|$sid|$token|$data\n";
|
|
|
|
}
|
|
|
|
if ($line =~ m/data-line/ && $data eq '.' && $ooo{$sid} == 1) {
|
2022-08-20 11:41:02 +02:00
|
|
|
dolog($fh, "$sid to: $email, from: $from", $opt_l);
|
2022-08-22 18:22:27 +02:00
|
|
|
$selvacation->bind_param(1, $email);
|
|
|
|
$selvacation->execute;
|
|
|
|
if ($selvacation->rows == 1) {
|
2022-08-20 11:41:02 +02:00
|
|
|
dolog($fh, "$sid found OOO for $email", $opt_l);
|
2022-08-22 18:22:27 +02:00
|
|
|
my @vacation_msg = $selvacation->fetchrow_array;
|
|
|
|
$selcache->bind_param(1, $email);
|
|
|
|
$selcache->bind_param(2, $from);
|
|
|
|
$selcache->execute;
|
|
|
|
if ($selcache->rows == 0) {
|
2022-08-20 11:41:02 +02:00
|
|
|
dolog($fh, "$sid sending OOO to $from", $opt_l);
|
2022-08-22 18:22:27 +02:00
|
|
|
$upcache->bind_param(1, $from);
|
|
|
|
$upcache->bind_param(2, $email);
|
|
|
|
$upcache->execute;
|
2022-08-18 14:01:52 +02:00
|
|
|
open my $fh_email, "|-", "/usr/sbin/sendmail -t";
|
|
|
|
print $fh_email "From: $email\n";
|
|
|
|
print $fh_email "To: $from\n";;
|
2022-08-18 16:57:25 +02:00
|
|
|
print $fh_email "Subject: $vacation_msg[0]\n";
|
2022-08-18 14:01:52 +02:00
|
|
|
print $fh_email "X-Loop: OpenSMTPD Admin Virtual Vacation\n";
|
|
|
|
print $fh_email "Content-Type: text/plain; charset=utf-8\n\n";
|
2022-08-18 16:57:25 +02:00
|
|
|
print $fh_email "$vacation_msg[1]\n";
|
2022-08-18 14:01:52 +02:00
|
|
|
close $fh_email;
|
2022-08-20 12:39:13 +02:00
|
|
|
delete $ooo{$sid};
|
|
|
|
dolog($fh, "$sid removed session - OOO done", $opt_l);
|
2022-08-20 12:31:47 +02:00
|
|
|
} else {
|
|
|
|
delete $ooo{$sid};
|
2022-08-20 12:39:13 +02:00
|
|
|
dolog($fh, "$sid removed session - OOO cache hit", $opt_l);
|
2022-08-18 14:01:52 +02:00
|
|
|
}
|
2022-08-20 10:52:13 +02:00
|
|
|
} else {
|
|
|
|
delete $ooo{$sid};
|
2022-08-20 12:31:47 +02:00
|
|
|
dolog($fh, "$sid removed session - OOO not found for $email", $opt_l);
|
2022-08-18 14:01:52 +02:00
|
|
|
}
|
|
|
|
} elsif ($line =~ m/data-line/ && $data eq '.' && $ooo{$sid} == 0) {
|
|
|
|
delete $ooo{$sid};
|
2022-08-20 12:26:20 +02:00
|
|
|
dolog($fh, "$sid removed session - OOO skip", $opt_l);
|
2022-08-18 14:01:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close $fh;
|
2022-08-22 18:22:27 +02:00
|
|
|
$dbh->disconnect();
|
2022-08-18 14:01:52 +02:00
|
|
|
0;
|