#!/usr/bin/perl
# Script to expire spam for $ARGV[0] older than $ARGV[1]
#     {$ARGV[2]=d "Days", $ARGV[1]=m "Months"}
# (Ugh)
# Last update Dec 11/2003 kdeugau@deepnet.cx
# Copyright 2003-2006 Kris Deugau <kdeugau@deepnet.cx>

# Some filesystem modules:
use Fcntl qw(:DEFAULT :flock);

# Time
use Time::Local;

# First, we set some defaults.
if ($ARGV[0] eq "") {
  die "No user specified!\n";
} else {
  if ($ARGV[1] == "") {
    $period = 30;
    $units = "d";
  } else {
    $period = $ARGV[1];
    if ($ARGV[2] == "") {
      $units = "d";
    } else {
      $units = $ARGV[2];
    }
  }
}
$nmsgs=0;

# how many seconds in 30 days?
if ($units == "d") {
  $timediff = $period*24*60*60;
} elsif ($units == "m") {
# nominal 30 days in a month;
  $timediff = $period*30*24*60*60;
}

# some hashes for turning "Mon" or "Oct" into a number usable by timegm()
%months = ('Jan',0,'Feb',1,'Mar',2,'Apr',3,'May',4,'Jun',5,'Jul',6,'Aug',7,'Sep',8,'Oct',9,'Nov',10,'Dec',11);

# Flow:
# -> Open user's spambox.  Lock it so procmail can't write new
#    data while we're busy expiring old data.  This process
#    *shouldn't* take long;  YMMV.

sysopen(SPAMFILE, "/home/$ARGV[0]/mail/spam", O_RDWR|O_CREAT)
  or die "Can't open /home/$ARGV[0]/mail/spam: $!";
flock(SPAMFILE, LOCK_EX)
  or die "Can't lock /home/$ARGV[0]/mail/spam: $!";

# We don't really need to lock this file;  
open CURRENTSPAM, ">/home/$ARGV[0]/mail/spam.lock";

# -> Start separating messages apart.  Be *very* careful about
#    parsing the "From " semantics;  make sure the *entire* line
#    is properly formed!  We're going to be a little more
#    restrictive than the RFCs on email address formatting;
#    while spaces are nominally technically *valid*, they're
#    usually *ignored*.  Among other things.

while (<SPAMFILE>) {
# read data
  if ($unexpired==1) {
# -> Once we reach a message that's "OK", we start stuffing
#    data into a temporary file (spam.lock, preferably).  Once
#    finished, we can close and reopen that file (ugh) and move
#    the data back to the original file.
    print CURRENTSPAM;
  } else {
    if (/^From /) {
      $nmsgs++;
      $fromline = $_;
# -> Compare the received date (on Filtermail) for each message
#    to the current date.  If the listed date is beyond the
#    boundary, drop the message and continue on to the next.
# We can do this quite neatly with the "From " line, as it's
# generated locally.  Heheheh.  :)

# A bunch of things have broken my parsing of "From " lines.  So we do this the hard way by
# splitting the whole thing, then counting from the end.  Ick.
      @frombits = split /\s+/;
      $fromnum = @frombits;
      $wday = $frombits[$fromnum-5];
      $month = $frombits[$fromnum-4];
      $mday = $frombits[$fromnum-3];
      $dtime = $frombits[$fromnum-2];
      $year = $frombits[$fromnum-1];
      @daybits = split /:/, $dtime[3];
      $msgtstamp = timegm($daybits[2], $daybits[1], $daybits[0], $mday, $months{$month}, $year-1900);
      $msgtstamp += $timediff;
      if ($msgtstamp > time()) {
	# Set a flag!  We've got unexpired spam.
        $unexpired = 1;
        $nmsgs--;
        print CURRENTSPAM $fromline;
      }
    }
  }
}

# Reopen the "unexpired" spam for reading...
close CURRENTSPAM;
open CURRENTSPAM, "</home/$ARGV[0]/mail/spam.lock";

# ... rewind to the beginning of the original file and truncate...
seek(SPAMFILE, 0, 0);
truncate(SPAMFILE,0)
  or die "Can't truncate original spambox: $!";

# ... and stream data from CURRENTSPAM
while (<CURRENTSPAM>) {
  print SPAMFILE;
}

close CURRENTSPAM;

# Remove the .lock file, as it's no longer needed.  We *might* be
# able to remove it while it's open... but I wouldn't count on
# being able to do so.
unlink "/home/$ARGV[0]/mail/spam.lock";

print "Expired $nmsgs messages from $ARGV[0]\'s spambox.\n";

# Let this happen implicitly, to force proper buffer flushes *before* unlocking files.
#close SPAMFILE;
