#!/usr/bin/perl # $Id: clean-mqueue.pl,v 1.8 2005/11/16 02:47:52 jik Exp $ # The current version of this script is available from # http://www.mit.edu/~jik/clean-mqueue.pl. # Send comment, corrections, fixes to jik@kamens.brookline.ma.us. # This script is in the public domain - do whatever you want with it. # This script scans the queue files in your sendmail queue directory, # shows you a summary of each one, and asks you what to do about it. # You can delete it ('d'), quarantine it ('q'), view the body ('b'), # or move on to the next message (anything else). # Configure the script by modifying $qdir below to point at your queue # directory, if it isn't /var/spool/mqueue, and $hide_headers to be a # regexp matching the headers you don't want to be displayed (mostly # just to make the output of the script shorter and more manageable). # Thanks to jonenetworks.co.za for submitting changes to clean # automatically based on domain, sender or TLD. I modified Jon's # changes extensively, so if there are any bugs in them, it's my # fault, not his. use strict; use warnings; use Term::Cap; use File::Basename; use Getopt::Long; my $qdir = "/var/spool/mqueue"; my $hide_headers = '(?i:Received|Return-Path|Full-Name|X-Moderate-For|Organization|Received-SPF|X-Mailer|X-Bogosity|References|In-Reply-To|Precedence|X-Authentication-Warning|Content-Transfer-Encoding|DomainKey-Signature|MIME-Version|Content-Type|X-IronPort-AV|X-Accept-Language|User-Agent|X-Originating-IP|X-Originating-Email|X-Sender|X-Priority|X-MSMail-Priority|Seal-Send-Time|X-MimeOLE|X-OriginalArrivalTime|Errors-To|X-Maintainer|X-Mailing-List|X-Loop|Old-To|Importance|X-Rcpt-To):'; use vars qw($clean_recipient $clean_sender $clean_batch $terse $go); my $whoami = basename $0; my $usage = "Usage: $whoami [--queue-directory dir] [--dryrun] [--batch [--terse]] \t[--go] [--recipient regexp] [--sender regexp]\n"; my(@qfiles) = glob "$qdir/qf*"; my $term = Tgetent Term::Cap; my $bold = $term->Tputs('md') || ''; my $endmode = $term->Tputs('me') || ''; my $dryrun; die $usage if (! GetOptions("queue-directory=s" => \$qdir, "dryrun" => \$dryrun, "terse" => \$terse, "batch" => \$clean_batch, "recipient=s" => \$clean_recipient, "sender=s" => \$clean_sender, "go" => \$go)); die "$whoami: $qdir does not exist\n" if (! -d $qdir); die "$whoami: Can't read any messages in $qdir\n" if (! @qfiles); die "$whoami: Must specify --recipient or --sender with --batch\n$usage" if ($clean_batch && ! ($clean_recipient || $clean_sender)); die "$whoami: --terse doesn't make sense without --batch\n$usage" if ($terse && ! $clean_batch); print "\n"; if ($clean_batch) { print "Running in batch mode\n"; } if ($clean_recipient) { print "Auto-deleting recipients matching \"$clean_recipient\"\n"; } if ($clean_sender) { print "Auto-deleting senders matching \"$clean_sender\"\n"; } print "Looking at queue in $qdir\n"; print "There are " . scalar(@qfiles) . " messages in your queue\n"; if (! $go && ($clean_recipient || $clean_sender)) { print "\n"; print "You must type \"go\" here to proceed: "; my $answer = ; if ($answer !~ /^go\s*$/i) { die "Wrong answer! Aborting.\n"; } } print "\n"; my %sender_actions; foreach my $qfile (@qfiles) { my(%qfile, $answer); my $id = $qfile; $id =~ s,.*/..,,; (%qfile = &parse_qfile($qfile)) or die "$whoami: Error parsing $qfile\n"; if (! $terse) { print $bold, "Id: ", $endmode, $id, "\n"; print $bold, "From: ", $endmode, $qfile{'sender'}, "\n"; print $bold, "To: ", $endmode, $qfile{'recipient'}, "\n"; print $bold, "Message: ", $endmode, $qfile{'message'}, "\n"; print $bold, "Header:", $endmode, "\n"; $_ = $qfile{'header'}; s/^/ /mg; print $_; } my $auto_delete; if (! $auto_delete && $clean_sender) { my $sender = $qfile{'sender'}; $sender =~ s/<(.*)>/$1/; if ($sender =~ /$clean_sender/o) { print "Auto-deleting sender $sender\n"; $auto_delete = 1; } } if (! $auto_delete && $clean_recipient) { my $recipient = $qfile{'recipient'}; $recipient =~ s/<(.*)>/$1/; if ($recipient =~ /$clean_recipient/o) { print "Auto deleting recipient $recipient\n"; $auto_delete = 1; } } prompt: if ($auto_delete) { $answer = "d"; } else { if (! $clean_batch) { print($bold, "Enter 'd' to delete, 'q' to quarantine, 'b' to see body.\n", "Add 's' to 'd' or 'q' to act on all messages with this sender: ", $endmode); } if ($sender_actions{$qfile{'sender'}}) { $answer = $sender_actions{$qfile{'sender'}}; print "$answer\n"; } elsif ($clean_batch) { $answer = "\n"; } else { $answer = ; exit if (! $answer); } } if ($answer =~ /^(d)(s?)/) { $sender_actions{$qfile{'sender'}} = $1 if ($2); if (! $dryrun) { unlink(glob("$qdir/*$id")) || die "$whoami: Removing $qdir/*$id: $!\n"; } } elsif ($answer =~ /^(q)(s?)/) { $sender_actions{$qfile{'sender'}} = $1 if ($2); my(@cmd) = ("sendmail", "-qI$id", "-Q$whoami"); if (! $dryrun) { system(@cmd) && die "$whoami: @cmd failed\n"; } } elsif ($answer =~ /^b/) { print "\n"; system("cat", "$qdir/df$id"); print "\n"; goto prompt; } if (! $terse) { print "\n"; } } sub parse_qfile { my($qfile) = @_; my(%qfile, $qtext); local($_); open(QFILE, $qfile) or die "$whoami: $qfile: $!\n"; { local($/) = undef; $qtext = ; close(QFILE); } $qtext =~ s/^\.\n//m; my(@qlines) = split(/\n\b/, $qtext); $qfile{'header'} = ''; foreach (@qlines) { if (s/^H\?[^\?]*\?//) { next if /^$hide_headers/o; $qfile{'header'} .= $_ . "\n"; } elsif (s/^R[^:]+://) { $qfile{'recipient'} = $_; } elsif (s/^S//) { $qfile{'sender'} = $_; } elsif (s/^M//) { $qfile{'message'} = $_; } } %qfile; }