poste.io

complete mail server built in docker
Open source plugins for QPSMTPD:

Dovecot quota


Download
#!perl -w

=head1 NAME

check_dovecot_quota - dovecot quota integration for qpsmtpd

=head1 DESCRIPTION

Plugin that checks if the receiver mailbox have enough free space (quota)
to deliver mail. If not, it will DENYSOFT after data input.

=head1 CONFIG

To enable quota plugin you have to add this to your Dovecot configuration:

service quota-status {
    executable = quota-status -p postfix
    inet_listener {
        port = 13001
    }
}

example communication between plugin and dovecot:
>
request=smtpd_access_policy
sender=ahoj@omg.cz
recipient=sh@isecure.cz
size=100000
<
OK

=head1 AUTHOR

Copyright (c) 2015 Stanislav Humplik (stanislav@analogic.cz)
http://poste.io

This plugin is free software; you can distribute it and/or modify it
under the terms of the General Public License v3.

=cut

use strict;
use warnings;

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

use IO::Socket::INET;

sub hook_data_post {
    my ($self, $transaction) = @_;

    return (DECLINED) if $self->is_immune(); # relay client or whitelist
    $self->log(LOGDEBUG, "starting quota status plugin");

    my $username = ($transaction->recipients)[0]->address;
    my $sender = $transaction->sender->address;

    if(($transaction->recipients) > 1) {
        $self->log(LOGINFO, "pass, more recipients - will handle LDA");
        return (DECLINED); # more recipients will handle LDA
    }

    my $socket = new IO::Socket::INET (
        PeerHost => 'localhost',
        PeerPort => '13001',
        Proto => 'tcp',
    );

    if(!$socket) {
        $self->log(LOGERROR, "connection error");
        return (DECLINED, 'pass, quota connection error');
    }

    my $req = "request=smtpd_access_policy\n";
    $req .= "sender=" . $sender . "\n";
    $req .= "recipient=" . $username . "\n";
    $req .= "size=" . $transaction->data_size() . "\n";
    $req .= "\n";

    my $size = $socket->send($req);

    shutdown($socket, 1);

    my $line = "";
    $socket->recv($line, 1024);
    $socket->close();

    if ($line !~ /^action/) {
        $self->log(LOGERROR, "invalid response from quota-status: $line");
        return (DECLINED);
    }

    $line =~ s/^\s+|\s+$//g; # trim
    if ($line =~ /OK/) {
        $self->log(LOGINFO, "ok, quota-status returned: $line");
        return (DECLINED, 'quota ok');
    } else {
        $self->log(LOGINFO, "quota-status returned: $line");
        return (DENYSOFT, $line);
    }
}