Compare commits
16 Commits
main
...
rcpt_mysql
Author | SHA1 | Date | |
---|---|---|---|
57402c47db | |||
ebe62e577d | |||
579e1ffce5 | |||
bb6ed367da | |||
a10a890f2c | |||
0cf9ea687d | |||
d4d6f74fcf | |||
5e58432d4f | |||
a53e7df40f | |||
03bbb91eb4 | |||
212329ebf6 | |||
2adb10a92a | |||
5d3658ccbb | |||
96150122a3 | |||
b57349c35b | |||
0b647b150b |
255
plugins/rcpt_mysql
Normal file
255
plugins/rcpt_mysql
Normal file
@ -0,0 +1,255 @@
|
||||
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} = "3306";
|
||||
$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:mysql:database=$database;host=$hostname;port=$port";
|
||||
my $dsn = "DBI:MariaDB:database=" . $self->{database} . ";host=" . $self->{host} . ";port=" . $self->{port};
|
||||
$self->{dsn} = $dsn;
|
||||
|
||||
# try to parse the dsn to ensure it is valid
|
||||
my @data = DBI->parse_dsn($self->{dsn});
|
||||
|
||||
if (@data == 0)
|
||||
{
|
||||
$self->log(LOGERROR, "DSN " . $self->{dsn} . " not valid");
|
||||
$self->{dsn}="";
|
||||
}
|
||||
|
||||
$self->log(LOGDEBUG, "created DSN " . $self->{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;
|
||||
|
||||
$self->log(LOGDEBUG, "created query " . $self->{sqlquery});
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$self->log(LOGDEBUG, "config line " . $line);
|
||||
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;
|
||||
|
||||
$self->log(LOGDEBUG, "asking database for: $recipient ");
|
||||
|
||||
if (length($self->{dsn}) == 0)
|
||||
{
|
||||
$self->log(LOGERROR, "DSN not valid not checking recipient in database");
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
my $dsn = $self->{dsn};
|
||||
my $dbh = DBI->connect($dsn, $self->{user}, $self->{pass}, { RaiseError => 0});
|
||||
|
||||
if ($dbh->err())
|
||||
{
|
||||
$self->log(LOGERROR, "error connecting to DB: " . $dbh->errstr());
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
my $sth = $dbh->prepare($self->{sqlquery});
|
||||
if ($sth->err())
|
||||
{
|
||||
$self->log(LOGERROR, "error preparing query: " . $sth->errstr());
|
||||
return DECLINED ;
|
||||
}
|
||||
|
||||
$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
|
||||
$self->log(LOGDEBUG, "missing recipient host or recipient user (" . $recipient->host . "/" . $recipient->user . ")") unless $recipient->host && $recipient->user;
|
||||
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_to");
|
||||
|
||||
}
|
@ -55,6 +55,8 @@
|
||||
605 rcpt_ok rok rcpok
|
||||
608 bogus_bounce bog bogus check_bogus_bounce
|
||||
609 greylisting gry greyl
|
||||
610 rcpt_mysql rmy rcmys
|
||||
|
||||
#
|
||||
# Content Filters
|
||||
#
|
||||
|
Loading…
Reference in New Issue
Block a user