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;

    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 => 1, PrintError => 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
    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");
    
}