Postfix Quota Notification Script

If you are running Postfix with quota support, email reporting for users that are nearing their quota limit would be highly beneficial in your environment. If aren’t running Postfix with quota support and would like to, see our articles Postfix VDA 2.11.3 Available  for Debian 8 or Postfix VDA 2.11.2 Available for Debian 7. Below we are providing a Perl script that will notify mail administrators of current quota usages and email users if they reach a preset percent of quota used. This script is loosely based on this script at How to Forge. In order to run the script below, you will need to place it with your Postfix configuration at /etc/postfix/quota-notify and make it executable. Once that’s complete, simply edit the variables on lines 4-11 and add an entry to your crontab to run the script as seen below.
postfix-quota-crontab

#!/usr/bin/perl -w
use strict;

my $POSTFIX_CF = "/etc/postfix/main.cf";
my $MAILPROG = "/usr/sbin/sendmail -t";
my $WARNPERCENT = 85;
my @POSTMASTERS = ('tech@oitibs.com');
my $COADDR = 'OITIBS Automation <tech@oitibs.com>';
my $SUADDR = 'OITIBS Automation <tech@oitibs.com>';
my $MAIL_REPORT = 1;
my $MAIL_WARNING = 1;

# get virtual mailbox base from postfix config
open(PCF, "< $POSTFIX_CF") or die $!;
my $mboxBase;
while (<PCF>) {
	next unless /virtual_mailbox_base\s*=\s*(.*)\s*/;
	$mboxBase = $1;
}
close(PCF);

# assume one level of subdirectories for domain names
my @domains;
opendir(DIR, $mboxBase) or die $!;
while (defined(my $name = readdir(DIR))) {
	next if $name =~ /^\.\.?$/;        #skip '.' and '..'
	next unless (-d "$mboxBase/$name");
	push(@domains, $name);
}
closedir(DIR);

# iterate through domains for username/maildirsize files
my @users;
chdir($mboxBase);
foreach my $domain (@domains) {
	opendir(DIR, $domain) or die $!;
	while (defined(my $name = readdir(DIR))) {
		next if $name =~ /^\.\.?$/;        #skip '.' and '..'
		next unless (-d "$domain/$name");
		push(@users, {"$name\@$domain" => "$mboxBase/$domain/$name"});
	}
}
closedir(DIR);

# get user quotas and percent used
my (%lusers, %uquota, $report);
foreach my $href (@users) {
	foreach my $user (keys %$href) {
		my $quotafile = "$href->{$user}/maildirsize";
		next unless (-f $quotafile);
		open(QF, "< $quotafile") or die $!;
		my ($firstln, $quota, $used);
		while (<QF>) {
			my $line = $_;
			if (! $firstln) {
				$firstln = 1;
				die "Error: corrupt quotafile $quotafile"
				unless ($line =~ /^(\d+)S/);
				$quota = $1;
				last if (! $quota);
				next;
			}
			die "Error: corrupt quotafile $quotafile"
			unless ($line =~ /\s*(-?\d+)/);
			$used += $1;
		}
	close(QF);
	next if (! $used);
	my $percent = int($used / $quota * 100);
	$uquota{$user} = "(" . int($quota / 1048576) . "MB)";
	$lusers{$user} = $percent unless not $percent;
	}
}

# send a report to the postmasters
if ($MAIL_REPORT) {
	open(MAIL, "| $MAILPROG");
	select(MAIL);
	map {print "To: $_\n"} @POSTMASTERS;
	print "From: $COADDR\n";
	print "Subject: Postfix Daily Quota Report\n";
	print "Content-Type: text/html\n\n";
	print "<html><style> table tbody { border-collapse: collapse; border: 2px solid #C0C0C0; font-family: calibri; } tr td { border: 2px solid #C0C0C0; padding: 8px; } </style><table border=2><tbody>";
	print "<tr bgcolor='#333333'><td colspan='3'><b style='color: #ffffff'>Postfix Daily Quota Report By Mailbox</b></td></tr>";
	foreach my $luser ( sort { $lusers{$b} <=> $lusers{$a} } keys %lusers ) {
		printf("<tr><td> %d%%\n </td><td> %s </td><td> %s </td></tr>", $lusers{$luser}, $luser, $uquota{$luser});	}
	print "</tbody></table></html>";
	close(MAIL);
}

# email a warning to people over quota
if ($MAIL_WARNING) {
	foreach my $luser (keys (%lusers)) {
		next unless $lusers{$luser} >= $WARNPERCENT; # skip those under quota
		open(MAIL, "| $MAILPROG");
		select(MAIL);
		print "To: $luser\n";
		map {print "BCC: $_\n"} @POSTMASTERS;
		print "From: $SUADDR\n";
		print "Subject: Mailbox Quota Warning: $lusers{$luser}% full.\n";
		print "X-Priority: 1 (Highest)\n";
		print "X-MSMail-Priority: High\n";
		print "Reply-to: $SUADDR\n";
		print "Your mailbox: $luser is $lusers{$luser}% full.\n\n";
		print "Once your storage quota has been exceeded your mailbox will no longer accept email.\n";
		print "Please consider deleting e-mail and emptying your trash folder to free some space.\n\n";
		print "If further assistance is required, please reply to this email.\n\n";
		print "Open IT Integrated Business Solutions";
		close(MAIL);
	}
}

In the end the admin report will look like the image below.postfix-quota-report

download-icon Postfix Quota Notify Download

Did you find this article useful? Why not share it with your friends?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.