#!/usr/bin/perl
#
# quotacheck.pl
#
# Written by Mark Borrie, ITS, University of Otago
# Dunedin, New Zealand
# mark at gandalf.otago.ac.nz
#
# Version : March 1 1999.
#
# Time zone and message priority fixes 20030603 by
# Karsten Behrens <kbe at tigernet.de>
#
# Additional logfile to be used as Postfix regexp table (to reject
# mails to full mailboxes) and some indention added 20030720 by
# Karsten Behrens <kbe at tigernet.de>
#
# A utility to check cyrus quotas and mail users who have used more than
# 90% of their quota. In addition users who have used all of their quota
# are also notified.
#
# Users who are over 90% are warned every 3 days, while user with full
# quotas are warned every five days.
#
# By using the delivery program directly we can deliver mail directly to
# the users mailbox even when the mailbox is full.
#
# In addition, users who have orgainised extended quotas have their extra
# space accounted for. At Otago we give users 5Mb of quota for standard
# use. Users can contract to pay for additional space. We record that
# additional space usage.
#

# DECLARATIONS

# Some debugging things
$debug=0;
$verbose=0;
$dohash=0;
$threshold=100;
$total=0;

# A temp file with the quota data
$quotadata="/usr/lib/cyrus/quota/quotas.tmp";
# The program that generates the quota data
$doquota="/usr/lib/cyrus/bin/quota";
# Log file of all the mailboxes we are interested in
$log="/usr/lib/cyrus/quota/quota.log";
# Log file of all the full mailboxes found (kbe)
$fulllog="/etc/postfix/mailboxfull";
$rejecttext="554 User mailbox is over quota";
# The cyrus delivery program
$mail="/usr/lib/cyrus/bin/deliver";
# The 2 files that contain the email message to the clients.
# mailwarn is sent to users whose quota is nearly full and mailfull
# to users with a full mailbox.
$mailwarn="/usr/lib/cyrus/quota/mailwarn.message";
$mailfull="/usr/lib/cyrus/quota/mailfull.message";
# Store the accounts that have been sent mail in a data file (we send e-mail to these accounts about full or almost full mailboxes)
$quotafull = "/usr/lib/cyrus/quota/quotafull.dat";
# Store the clients who are using more than the std quota
$accountlog="/usr/lib/cyrus/quota/account.log";


# INCLUDES

# we need this to do time things. If the ENVIRONMENT variable TZ is NOT
# set this doesn't work too well.
require "ctime.pl";
#require "ptime.pl";
use POSIX qw(strftime);

# better actually check if TZ was set before we go off and do things
# I got this from someone else so I'm not sure what happens if TZ is not
# set. The following line will need changed elsewhere.
# $TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'CEST' ) : '';
$TZ = `date "+%Z"`;
print "Timezone is set to : ",$TZ,"\n" if $debug;

open(LOGFILE,">>".$log);
open(FULLLOG,">".$fulllog); # (kbe)

# Preload the previous full account data
# The format of the file is a space separated field file with
# <lastwarningdate> <lastfulldate> <popid>
# lastwarningdate and lastfulldate are set to zero if the other
# value is non-zero.
# the values lastwarningdate and lastfulldate are more or less the
# number of days since Jan 1 1999.

print "Preloading previous full account data," if $debug;
$counter=0;
open(TABLE,"<".$quotafull);
while(<TABLE>) {
  chop;
  @fields = split(/[ \t]+/,$_);# white space separated fields
  $lastwarndate{$fields[2]} = $fields[0];
  $lastfulldate{$fields[2]} = $fields[1];
  $counter++;
}
close(TABLE);

print "Read $counter records from previous data\n" if $debug;


# MAIN LOOP

# This is not leap year compliant. Jan 1 the year following a leap year
# will have the same index as Dec 31 (the day before). This will simply
# increase the message sending interval by one day arround this time.

$year=strftime("%Y",localtime);
$day=strftime("%j",localtime);
$offset= (($year-1999)*365) + $day; # days since 1/1/99,approx.
$date = strftime("%a, %d %b %Y %H:%M:%S %z (%Z)",localtime);

# Create the 2 warning message files with appropriate dates
# These can then be delivered directly to the cyrus mailbox
# The From: line will need customizing.

open (TMPFILE,">".$mailwarn.".tmp");
print TMPFILE <<ENDOFTEXT;
From: "Postmaster" <postmaster\@mydomain.ac.il>
Subject: Important message - you mailbox is almost full
Date: $date
Importance: high
X-Priority: 1

ENDOFTEXT

open (MAILMESSAGE,"<".$mailwarn) or print "Could not open $mailwarn\n";

# Read the main message which is non variable text
while (<MAILMESSAGE>) {
  print TMPFILE $_;
}
close (MAILMESSAGE);
close (TMPFILE);

open (TMPFILE,">".$mailfull.".tmp");
print TMPFILE <<ENDOFTEXT;
From: "Postmaster" <postmaster\@mydomain.ac.il>
Subject: Important message - you mailbox is full
Date: $date
Importance: high
X-Priority: 1

ENDOFTEXT

open (MAILMESSAGE,"<".$mailfull) or print "Could not open $mailfull\n";

# Read the main message which is non variable text
while (<MAILMESSAGE>) {
  print TMPFILE $_;
}
close (MAILMESSAGE);
close (TMPFILE);

# Now change the date variable to something else
#$date = strftime("%d %b %y",localtime);
$date = &'ctime(time);
print "Entering main processing loop" if $debug;

# Generate quota file - remember to remove first line.
system("su - cyrus -c \"$doquota > $quotadata\"");

open(QUOTAFILE,"<".$quotadata);
open(ACCOUNTING,">".$accountlog); # original open(ACCOUNTING,">>".$accountlog); used for listing the accounts with more quota then the default

# Discard the first line of the quota file
$firstline = <QUOTAFILE>;

while (<QUOTAFILE>) {
  # print "Main loop\n" if $debug;
  if ($dohash) {
    $total++;
    if ( $cntr++ > $threshold ) {
      $cntr=0;
      print STDERR "Processed ", $total," records\n" if $verbose;
    }
  }
  # split input line into useful fields
  ($quota,$percent,$kbytes,$tmp)=split(" ",$_);
  $user=$bbs="";
  print "tmp=$tmp\n"if $debug;
  $user=$tmp if $tmp=~/^user\//i;
  $bbs=$tmp if $tmp=~/^public\//i;
  $user=~s/^\S*\///;
  $bbs=~s/^\S*\///;
  # $user=substr($tmp,5);
  print "user=$user, bbs=$bbs\n" if $debug;
  $lastfulloffset=$lastfulldate{$user};
  $lastwarnoffset=$lastwarndate{$user};
  if (!defined($lastfulloffset) ) {
    $lastfulloffset=0;
  }
  if (!defined($lastwarnoffset) ) {
    $lastwarnoffset=0;
  }
  if ($percent >= 100 && !$bbs) {
    if (($offset - $lastfulloffset) > 5) {
      $lastwarndate{$user}=0;
      $lastfulldate{$user}=$offset;
      print LOGFILE "$user quota is full : $date\n";
      system("su - cyrus -c \"$mail -e -q $user < $mailfull.tmp\"");
      system("mail -s \"Quota full for $user\" postmaster\@mydomain\.ac\.il < $mailfull.tmp");
    }
    $moduser=$user; # (kbe)
    $moduser=~s/\./[@.]/g; # (kbe)
    print FULLLOG "/$moduser/ $rejecttext\n"; # (kbe)
  } elsif ($percent >= 90 && !$bbs) {
    if (($offset - $lastwarnoffset) > 3) {
      $lastfulldate{$user}=0;
      $lastwarndate{$user}=$offset;
      print LOGFILE "$user quota is nearly full : $date\n";
      system("su - cyrus -c \"$mail -e -q $user < $mailwarn.tmp\"");
      system("mail postmaster\@mydomain\.ac\.il -s \"Quota warn for $user\" < $mailwarn.tmp");
    }
  }
  if (($quota > 102400)) { # check the Kb non std. accounts have above the default quota
    $chargable=$quota - 102400;
    print ACCOUNTING "$user, $chargable, $date\n";
  }
}

close(LOGFILE);
close(FULLLOG);
system("postmap $fulllog");
close(ACCOUNTING);

# Write out the quota full data again
open(TABLE,">".$quotafull);
$counter=0;
foreach ( keys %lastwarndate ) {
  print TABLE "$lastwarndate{$_} $lastfulldate{$_} $_\n";
  $counter++;
}
close(TABLE);

print "Wrote $counter records to login data\n" if $debug;