#!perl -w

=head1 NAME

rcpt_ok

=head1 SYNOPSIS

Validate that we accept mail for a recipient using a qmail rcpthosts file

=head1 DESCRIPTION

Check the envelope recipient hostname and determine if we accept mail to that host.

This is functionally identical to qmail's rcpthosts implementation, consulting
both rcpthosts and morercpthosts.cdb.

=head1 CONFIGURATION

It should be configured as the _LAST_ recipient plugin!

=cut

use strict;
use warnings;

use Qpsmtpd::Constants;
use Qpsmtpd::DSN;

sub hook_rcpt {
    my ($self, $transaction, $recipient, %param) = @_;

    return OK if $self->is_immune();    # relay_client or whitelist

    # Allow 'no @' addresses for 'postmaster' and 'abuse'
    # qmail-smtpd will do this for all users without a domain, but we'll
    # be a bit more picky.  Maybe that's a bad idea.
    my $host = $self->get_rcpt_host($recipient) or return OK;

    return OK if $self->is_in_rcpthosts($host);
    return OK if $self->is_in_morercpthosts($host);

    # default of relaying_denied is obviously DENY,
    # we use the default "Relaying denied" message...
    return Qpsmtpd::DSN->relaying_denied();
}

sub is_in_rcpthosts {
    my ($self, $host) = @_;

    my @rcpt_hosts = ($self->qp->config('me'), $self->qp->config('rcpthosts'));

    # Check if this recipient host is allowed
    for my $allowed (@rcpt_hosts) {
        $allowed =~ s/^\s*(\S+)/$1/;
        if ($host eq lc $allowed) {
            $self->log(LOGINFO, "pass: $host in rcpthosts");
            return 1;
        }

        if (substr($allowed, 0, 1) eq '.' and $host =~ m/\Q$allowed\E$/i) {
            $self->log(LOGINFO, "pass: $host in rcpthosts as $allowed");
            return 1;
        }
    }

    return;
}

sub is_in_morercpthosts {
    my ($self, $host) = @_;

    my $more_rcpt_hosts = $self->qp->config('morercpthosts', 'map');

    if (exists $more_rcpt_hosts->{$host}) {
        $self->log(LOGINFO, "pass: $host found in morercpthosts");
        return 1;
    }

    $self->log(LOGINFO, "fail: $host not in morercpthosts");
    return;
}

sub get_rcpt_host {
    my ($self, $recipient) = @_;

    return if !$recipient;    # Qpsmtpd::Address couldn't parse the recipient

    if ($recipient->host) {
        return lc $recipient->host;
    }

    # no host portion exists
    my $user = $recipient->user or return;
    if (lc $user eq 'postmaster' || lc $user eq 'abuse') {
        return $self->qp->config('me');
    }
    return;
}