Benutzer-Werkzeuge


postfix-quota

postfix-quota ist ein kleines Perl Script welches einen Dienst zur Abfrage von Mailboxgrössen für Postfix zur Verfügung stellt. Dabei öffnet das Script TCP-Sockets und lauscht auf Anfragen von Postfix. Das Script beantwortet Anfragen mit einem ACTION Code für Postfix. Ist die Quota in Ordnung oder tritt ein Fehler auf, dann kommt als ACTION ein DUNNO. Ist der User overquota dann wird Postfix angewiesen die Mail zu verweigern. Bis zu 200% Overquota mittels temporärem 450-er Fehler und ab 200% mit einem harten 550-er Fehler.

Das Script setzt Perl und ein paar Module/Libraries voraus. Zudem mysql, da die erlaubte Quote pro User aus einer Mysql-Tabelle abgefragt wird

#!/usr/bin/perl
 
use strict;
use warnings;
use DBI;
use DBD::mysql;
use Sys::Syslog qw(:DEFAULT setlogsock);
use base qw(Net::Server::PreFork);
#
# Initalize and open syslog.
#
openlog('postfix-quota','pid','mail');
 
__PACKAGE__->run;
exit;
###
sub configure_hook {
        #Konfigution fuer den TCP Socket
        #User/Gruppe muessen auf basedir Leserechte haben
        my $self = shift;
        $self->{server}->{port}     = '127.0.0.1:20028';
        $self->{server}->{user}     = 'vmail';
        $self->{server}->{group}    = 'vmail';
        $self->{server}->{pid_file} = '/tmp/size.pid';
        $self->{server}->{setsid}   = 1;
        $self->{basedir}            = "/home/vmail/";
}
 
### process the request
sub process_request {
        my $self = shift;
        while(my $line = <STDIN>) {
                # Request zerlegen am Leerzeichen
                # den hinteren Teil (Mailadresse) an @ zerlegen
                chomp($line);
                my @parts = split(' ',$line);
                my @values = split('@',$parts[1]);
                # Fehlerhafter Request DUNNO zurueckgeben, damit postfix 'nichts macht'
                if(!defined $parts[0] || $parts[0] ne "get" || !defined $parts[1] || !defined $values[0] || !defined $values[1])        {
                        print STDOUT "200 DUNNO\n";
                        next;
                }
                # $user und $domain
                my $user = $values[0];
                my $domain = $values[1];
                trim($user);
                trim($domain);
                # Maximale Groesse der Mailbox
                my $sqlsize = quotaFromDB("$user\@$domain");
                # sofort "abbrechen" wenn quotaFromDB() einen Fehler oder den Wert 0 produziert
                # mit dem quota Wert 0 in der DB kann man also einen User von quotas ausnehmen
                if (defined $sqlsize && $sqlsize == 0) {
                        print STDOUT "200 DUNNO\n";
                        next;
                }
                # Pfad zur Usermailbox erstellen und an checksize uebergeben
                my $dirsize = checksize($self->{basedir} . $domain. '/'. $user);
                if (defined $dirsize && defined $sqlsize) {
                        # syslog Meldung in mail.log
                        # da jede Pruefung geloggt wird habe ich es aus
                        # Performance-Gruenden auskommentiert
                        # syslog("info","Checking %s maildir size: define=%s, diskusage=%s", "$user\@$domain", format_byte($sqlsize), format_byte($dirsize));
                        # Mailbox ist Overquota
                        if ( $dirsize > $sqlsize ) {
                                # Prozentuale Belegung der Mailbox berechnen
                                my $usage = (100 * $dirsize) / $sqlsize;
                                $usage = sprintf("%.1f",$usage);
                                # $sqlsize und $dirsize werden fuer die Textausgabe
                                # duch format_byte() formatiert
                                $sqlsize = format_byte($sqlsize);
                                $dirsize = format_byte($dirsize);
                                syslog("info","%s maildir overquota size: define=%s, diskusage=%s", "$user\@$domain", $sqlsize, $dirsize);
                                # Meldung an den Client (postfix) geben dass die Mailbox voll ist
                                # damit wird postfix eine eingehende Mail verweigern
                                # bis 200% overquota wird "nur" ein temporaerer Fehler ausgegeben
                                # damit der Client es spaeter nochmals probiert
                                # ab 200% overquota wird ein harter SMTP Fehler ausgegeben
                                # damit muss der Client diese Zustellung als gescheitert betrachten und eine Fehlermeldung an den Absender schicken
                                if ( $usage < 200 ) {
                                        print STDOUT "200 452 4.2.2 Mailbox full!! mailbox size: allowed=$sqlsize, used=$dirsize, usage=$usage%\n";
                                        next;
                                } else {
                                        print STDOUT "200 550 5.7.1 Mailbox full!! mailbox size: allowed=$sqlsize, used=$dirsize, usage=$usage%\n";
                                        next;
                                }
                        } else {
                                # Kein Overquota drum DUNNO
                                print STDOUT "200 DUNNO\n";
                                next;
                        }
                } else {
                        # Soll quasi als default Antwort dienen und
                        # sicherstellen das postfix nicht eine Anweisung zum Blockieren bekommt
                        print STDOUT "200 DUNNO\n";
                        next;
                }
        }
}
 
# liest die Quota des Users aus der mysql-DB
# gibt den Wert aus der DB an den Aufrufer zurueck
sub quotaFromDB {
        my $user = $_[0];
        my $sqlresult;
        trim($user);
        my $dbh = DBI->connect('DBI:mysql:postfix-quota:localhost', 'MYSQLUSER', 'MYSQLPASSWD', { RaiseError => 1 });
        my $sth = $dbh->prepare(qq{SELECT quota FROM mailbox WHERE username='$user'});
        $sth->execute();
        while (my @row = $sth->fetchrow_array) {
                $sqlresult = $row[0];
        }
        $sth->finish();
        $dbh->disconnect;
        if ($sqlresult >= 0 ) {
                return $sqlresult;
        } else {
                return undef;
        }
}
 
 
# Entfernt Leerzeichen vom Anfang umd vom Ende des Strings
sub trim{
        $_[0]=~s/^\s+//;
        $_[0]=~s/\s+$//;
        return;
}
# Prueft wieviel Platz eine Mailbox im Dateisystem belegt
sub checksize {
        my $diruser = $_[0];
        trim($diruser);
        # effektive Groesse des Verzeichnisses ermitteln
        # "du" ist wesentlich schneller als "find"
        # -b ist wichtig damit "du" die Groesse in bytes und nicht in kbytes zurueckgibt
        my @size = split(' ',`du -sb $diruser`);
        if (defined $size[0]) {
                my $size = sprintf("%u",$size[0]);
                return $size;
        }
        return undef;
}
# Formatiert eine Zahl
sub format_byte {
        my $number = shift;
        if($number >= 1000000000000){
                return sprintf("%.2f TB", $number / 1000000000000);
        }elsif($number >= 1000000000){
                return sprintf("%.2f GB", $number / 1000000000);
        }elsif($number >= 1000000){
                return sprintf("%.2f MB", $number / 1000000);
        }elsif($number >= 1000){
                return sprintf("%.2f KB", $number / 1000);
        }else{
                return sprintf("%.2f Bytes", $number);
        }
}
 
1;
Melden Sie sich an, um einen Kommentar zu erstellen.

Seiten-Werkzeuge