ADD: initial version of rcpt_mysql plugin

This commit is contained in:
Dominik Meyer 2023-12-26 15:34:34 +01:00
parent d3cf60d2c8
commit c4f5490abd
Signed by: byterazor
GPG Key ID: EABDA0FD5981BC97

232
plugins/rcpt_mysql Normal file
View File

@ -0,0 +1,232 @@
use Qpsmtpd::Constants;
use DBI;
#
# called when initializing the plugin
#
sub init {
my ($self, $qp, %args) = @_;
# some default values
$self->{database} = "mail";
$self->{host} = "localhost";
$self->{port} = "3301";
$self->{user} = undef;
$self->{pass} = undef;
$self->{rawquery}= undef;
# the cache timeout in seconds
$self->{cacheTimeout} = 500; #seconds
# a map for caching database repsonses
# done on a per email basis
$self->{cache} = {};
# parse the configuration file
$self->parseConfig();
$self->createQuery();
$self->createDSN();
}
sub createDSN
{
my $self = shift;
my $dsn = "DBI:MariaDB:database=" . $self->{database} . ";host=" . $self->{host} . ";port=" . $self->{port};
$self->{dsn} = $dsn;
}
sub createQuery
{
my $self = shift;
#
# !u the user part of the rcpt
# !d the domain part of the rcpt
my @params;
my @variables = split /!/,$self->{rawquery};
for my $v (@variables)
{
if ($v eq "u")
{
push(@params,"user");
}
elsif ($v eq "d")
{
push(@params,"domain")
}
}
$self->{sqlparams}=\@params;
my $query = $self->{rawquery};
$query =~s/!u/?/g;
$query =~s/!d/?/g;
$self->{sqlquery}=$query;
}
sub prepareParams
{
my $self = shift;
my $recipient = shift;
my @params;
for my $p (@params)
{
if ($p eq "user")
{
push(@params,$recipient->user);
}
elsif($p eq "domain")
{
push(@params,$recipient->user);
}
}
return @params;
}
sub parseConfig
{
my $self = shift;
# parse the configuration file of this plugin line by line
my @config = $self->qp->config('rcpt_mysql');
for my $line (@config)
{
my @value = split /:=/, $line;
my $key = lc($value[0]);
if ( $key eq "database")
{
$self->{database} = $value[1];
}
elsif ( $key eq "host")
{
$self->{host} = $value[1];
}
elsif ( $key eq "user")
{
$self->{user} = $value[1];
}
elsif ( $key eq "pass")
{
$self->{pass} = $value[1];
}
elsif ( $key eq "port")
{
$self->{port} = $value[1];
}
elsif ( $key eq "cachetimeout")
{
$self->{cacheTimeout} = $value[1];
}
elsif ( $key eq "pass")
{
$self->{pass} = $value[1];
}
elsif ( $key eq "query")
{
$self->{rawquery} = $value[1];
}
else
{
warn("Key \"" . $key . "\" is an unknown configuration option");
}
}
}
sub askCache
{
my $self = shift;
my $recipient = shift;
# combine to a normal recipient
my $rcpt = lc $recipient->user . '@' . lc $recipient->host;
# check if there is something in the cache for this recipient
if ($self->{cache}->{$rcpt} && $self->{cache}->{$rcpt}->{time}-time() <= $self->{cacheTimeout}) {
if ($self->{cache}->{$rcpt}->{hit} == 0 )
{
return DECLINED;
}
else
{
return OK;
}
}
return undef;
}
sub askDatabase
{
my $self = shift;
my $recipient = shift;
my $dbh = DBI::connect($self->{dsn}, $self->{user}, $self->{pass},{ RaiseError => 0, PrintError => 0 });
if ($dbh->err())
{
warn("error connecting to DB: " . $dbh->errstr());
return DENYSOFT;
}
my $sth = $dbh->prepare($self->{sqlquery});
if ($sth->err())
{
warn("error preparing query: " . $sth->errstr());
return DENYSOFT;
}
$sth->execute($self->prepareParams($recipient));
my $ret = DECLINED;
if ($sth->rows > 0)
{
$ret = OK;
}
return $ret;
}
#
# plugin hook called if rcpt_to is issued within the smtp session
#
sub rcpt_to {
my ($self, $transaction, $recipient) = @_;
# some basic validations
return DECLINED unless $recipient->host && $recipient->user;
# ask the cache for results
my $cacheResult = $self->askCache($recipient);
return $cacheResult if defined($cacheResult);
# ask the database for results
return $self->askDatabase($recipient);
}
#
# called for registering all the hooks
#
sub register {
my ($self, $qp, %args) = @_;
$self->register_hook("rcpt", "rcpt_handler");
}