quarantine.cgi is a perl script that permits authenticated users to selectivly delete, or force messages to be delivered out of sendmail's QUARANTINE queue. This script needs to be run setuid root to delete and to deliver mail from the quarantine queue.

NOTE: I havn't spent quite enough time making absolutely sure it's foolproof (users can delete or deliver other user's mail if they know the queue id), but it's close enough for me.
Questions? Comments? Shoot me an email at richard ((__NOSPAM__)) richardharman.com.

Here's the syntax highlighted version of quarantine.cgi. Click here to download the sourcecode.
#!/usr/bin/perl -T

BEGIN { use CGI::Carp qw(fatalsToBrowser carpout); }
$ENV{PATH} = "/usr/sbin";

use strict;
#use warnings;
use CGI;
use CGI::Carp qw(fatalsToBrowser carpout);
use AnyDBM_File;
use Fcntl;
use List::Util qw(first);


my %queue_to_user;
my $tie_status = tie(%queue_to_user, 'AnyDBM_File', "/var/www/data/queue.db", O_CREAT | O_RDWR,0666) or die "Couldn't tie! ($!)";

$|++;

my $q = new CGI;

# print out to browser mode (somewhat useful)
if ( $ENV{PATH_INFO} ) {
  my ($qid) = ( $ENV{PATH_INFO} =~ m,/(\w{14}), );
  open( QF, "<", "/var/spool/mqueue/hf$qid" ) or die "Couldn't open /var/spool/mqueue/hf$qid for reading ($!)\n";
  open( DF, "<", "/var/spool/mqueue/df$qid" ) or die "Couldn't open /var/spool/mqueue/df$qid for reading ($!)\n";
  print $q->header( -type => 'text/plain' );
  $/ = undef;
  print <QF>, <DF>;
  exit 0;
}

# all else, we're in html mode
print $q->header( -type => 'text/html' );

# checks to see if we're in admin mode or not
my $admin = 0;
if ($ENV{REMOTE_USER} eq "warewolf" ) { # && defined($q->param("noadmin")) && $q->param("noadmin") eq "1") {
  $admin++;
}

print STDERR "admin mode = $admin\n";
# set real to effective uid
$< = $>;

my $clearing = 0;

if ( $q->param("op") ) {

  if ( $q->param("op") eq "clear") { # {{{
    # clean slate mode, delete all messages owned by $ENV{REMOTE_USER}
    $clearing = 1;
  } # }}}
  elsif ( $q->param("op") eq "delete" ) { # {{{
    # we're deleting messsages now
    my ($qid) = ( $q->param("qid") =~ m/(\w{14})/ );
    unlink("/var/spool/mqueue/hf$qid") or die "Couldn't delete /var/spool/mqueue/hf$qid ($!)";
    unlink("/var/spool/mqueue/df$qid") or die "Couldn't delete /var/spool/mqueue/df$qid ($!)";
    print "Deleted $qid<br>\n";
  }  # }}}
  elsif ( $q->param("op") eq "run" ) { # {{{
    # we're telling sendmail to run a particular message in the quarantine queue so it gets delivered to somebody's mailbox
    my ($qid) = ( $q->param("qid") =~ m/(\w{14})/ );

    open( SENDMAIL, "-|", "sendmail -v -qQ -qf -qI$qid" );
    print "<pre>\n";
    # XXX 
    # XXX NO sanitation of sendmail's output
    # XXX 
    while (<SENDMAIL>) {
      my ($out) = ( $_ =~ m/(.+)/ );
      print $out;
    } 
    print "</pre>";
  } # }}}
}

# carry on as usual

# start finding quarantined messages
opendir( QUEUE, "/var/spool/mqueue" ) or die "Couldn't opendir /var/spool/mqueue ($!)";

# hf = quarantined messages
my @queue_ids = grep { m/hf(\w{14})$/; $_ = $1 } readdir QUEUE;

# this is a quick lookup table
# that maps email addresses to local unix account names
my %email_to_localuser;
print "Click <a href='/ssl-bin/quarantine.cgi'>here</a> to refresh this page.<br>\n";
print "<form method=post>Press <input type=submit name='op' value='clear'> to clear all mail from the queue.</form>\n";
print "<table border=1><th>Q ID</th><th>Size</th><th>Q Time</th><th>Sender/Recipient</th></tr>\n";
QID: foreach my $qid (@queue_ids) {
  open( QF, "<", "/var/spool/mqueue/hf$qid" ) or die "Couldn't open /var/spool/mqueue/hf$qid for reading! ($!)";
  my $info;
  while (<QF>) {
    $_ =~ m/^q"?(.+)"?$/     && do { $info->{quarantine} = $1; next };
    $_ =~ m/^T(\d+)$/        && do { $info->{epoch}      = $1; next };
    $_ =~ m/^S<(.+)\>$/      && do { $info->{sender}     = $1; next };
    $_ =~ m/^rRFC822; (.+)$/ && do { $info->{recipient}  = $1; last };
  }
  $info->{size}  = ( stat QF )[7];
  $info->{epoch} = scalar localtime( $info->{epoch} );
  close QF;

  # don't skip messages 
  # ..... when our email -> local user (unix name) cache gets a hit
  # ..... when we're an admin
  # ..... when our Queue ID -> local user (unix name) cache gets a hit
  if ( defined( $email_to_localuser{ $info->{recipient} } ) or defined( $queue_to_user{$qid} ) ) {
    print STDERR "email to localuser for $info->{recipient} is $email_to_localuser{$info->{recipient}}\n";
    next QID unless (
                      $ENV{REMOTE_USER} eq $email_to_localuser{ $info->{recipient} }
		      or $admin
		      or $queue_to_user{$qid} eq $ENV{REMOTE_USER}
                    );
  }

  # figure out if a message is to be delivered locally or not
  # XXX actually this wasn't implimented XXX
  my $local = 0;
  do { # if either cache $email_to_localuser or $queue_to_user is empty, figure out who it goes to
    #open( SENDMAIL, "-|", "sendmail -bv $info->{recipient}" ); # noisy
    open( SENDMAIL, "-|", "sendmail -OLogLevel=0 -bv $info->{recipient}" ); # quiet

#  REMOTE
#  # warewolf@xabean.com:/var/www/ssl-bin$ sudo sendmail -OLogLevel=0 -bv s2288@mail2000.com.tw
#  s2288@mail2000.com.tw... deliverable: mailer esmtp, host mail2000.com.tw., user s2288@mail2000.com.tw
#
#  LOCAL
#  # warewolf@xabean.com:/var/www/ssl-bin$ sudo sendmail -OLogLevel=0 -bv wa4usb@wa4usb.us
#  wa4usb@wa4usb.us... deliverable: mailer local, user dharman

    while (<SENDMAIL>) {
      # XXX local or not delivery detection XXX
      if ($_ !~ m/\Qmailer esmtp, host\E/) { $local++ };

      my ($found) = ($_ =~ m/\Qlocal, user $ENV{REMOTE_USER}\E/);
      #  skip unless it's for the logged in remote user, or we're god
      next QID unless ( $found or $admin );
      # stick it in our cache (unless we're an admin, otherwise it'd be a false cache)
      $email_to_localuser{ $info->{recipient} } = $ENV{REMOTE_USER} unless ($admin);
      if ($found) {
        # stick it in our cache (unless we're an admin, otherwise it'd be a false cache)
        $queue_to_user{$qid} = $ENV{REMOTE_USER} unless ($admin); 
   
      }
      if (! $local) {
        # non local mail, lets nuke it from the queue
        # map { unlink("/var/spool/mqueue/$_$qid") } qw(qf df hf) ;
      }
    } # end while (<SENDMAIL>) {
  } unless ( defined( $email_to_localuser{ $info->{recipient} } ) or defined( $queue_to_user{$qid} ) );
  my $style;
  if (! defined($queue_to_user{$qid})) {
    $style = $admin  ? ' style="background-color: #ccc;"' : "";
  } else {
    $style = ( $admin && $ENV{REMOTE_USER} ne $queue_to_user{$qid}) ? ' style="background-color: #ccc;"' : "";
  }
  if ($clearing) {
    unlink("/var/spool/mqueue/hf$qid") or die "Couldn't delete /var/spool/mqueue/hf$qid ($!)";
    unlink("/var/spool/mqueue/df$qid") or die "Couldn't delete /var/spool/mqueue/df$qid ($!)";
    print "<tr$style><td><b>DELETED</b><br></td>\n";
    print "<td>$info->{size}</td><td>$info->{epoch}</td><td>$info->{sender}</td></tr>\n";
    print "<tr$style><td colspan=4>$info->{quarantine}</td></tr>\n";
    print "<tr$style><td colspan=4 align=right>$info->{recipient}</td></tr>\n";
  } else {
    print "<tr$style><td><a href='$ENV{REQUEST_URI}/$qid'>$qid</a><br>\n";
    print "<form method=post><input type=hidden name=qid value=$qid>\n";
    if ($q->param("noadmin")) {
      print "<input type=hidden name=noadmin value=".$q->param("noadmin").">\n";
    }
    $info->{sender} = $info->{sender} || "";
    print "<input type=submit name=op value=run> | <input type=submit name=op value=delete></form>\n";
    print "</td><td>$info->{size}</td><td>$info->{epoch}</td><td>$info->{sender}</td></tr>\n";
    print "<tr$style><td colspan=4>$info->{quarantine}</td></tr>\n";
    print "<tr$style><td colspan=4 align=right>$info->{recipient}</td></tr>\n";
  }
}
print "</table>\n";

# size maintenance of cache
foreach my $id ( keys %queue_to_user ) {
  delete $queue_to_user{$id} unless first { defined( $queue_to_user{$_} ) } @queue_ids;
}
undef $tie_status;
untie %queue_to_user;

# vim: ft=perl foldmethod=marker commentstring=#\ %s bg=dark