I have about 12 web sites hosted on my server. 4 of them are pretty
hefty web applications with multple users. There are a few community
sites with mailing lists. YOU know, how it is. Well, because many of
these applications have the capability of sending emails, bounce
messages have become a real issue.
Most mail servers don't send a bounced message back to whoever is in
the 'reply-to' field (for good reason!). All of those messages get
bounced back to the server itself. But it is useless to sift
through the 6000 or so double-bounces and spam bounces that come back
to the server for those 2 or 3 'real' bounce messages that happen per
week. I was just dropping them to /dev/null and explaining to my
frustrated customers that there was nothing I could do about it.
I stumbled across this post Detecting bounced mails and thought to myself, "Hey,
I already have some experience with POP3 handling
(Oakmailer) and that
Mail::DeliveryStatus::BounceParser module looks like just the thing I
need to make my customers happy campers."
So, without further ado, this is what I came up with:
- Dump
those bounces into a mailbox.
- Use Mail::POP3Client to
communicate with that mailbox.
- Use
Mail::DeliveryStatus::BounceParser to look at each of those messages
and find the 'real' bounces
- come up with a series of
subroutines relating to each of my hosted web sites to see if I can
find out where that message originally came from
- Alert the
sender about the bounce
- Anything that doesn't fit into one of
those tests are forwarded on to me for that personal touch
- All
of this is wrapped up into a cron job that runs on the hour
I went through each of my sites and created a bunch of messages that I
knew would bounce (phyllis_diller_is_my_hero@xxxxx.xxx) and sent them
to a wide variety of domains. This gave me a corpus to run the script
against, any messages that weren't detected meant I needed to do some
rewriting.
In the end, I spent about an hour and a half writing a script that
saves me about 10 times that amount of time per month in handling
complaints and trying to track down, "Why didn't my candidate get that
email?" questions.
use Mail::POP3Client;
use Mail::DeliveryStatus::BounceParser;
# the following module is general housekeeping stuff
use Oakbox::Main;
my $Main = Oakbox::Main->new("adminemail"=>"richard\@oakbox.com",
"logfile" => "Site_Log.txt",
"scratch_dir"=> "/tmp/bouncing/");
# connect to the mailbox
my $pop = new Mail::POP3Client( USER => "doublebounce",
PASSWORD => "password",
HOST=> "my.mailserver.com",
DEBUG => 0 ,
AUTH_MODE => 'PASS' );
my $count = $pop->Count;
if ($count == -1) { die("Unable to read Mail box! Something went horr
+ibly wrong here.");}
&listletters;
exit;
####
sub listletters{
my $nummessages=$pop->Count();
foreach my $i (1 ... $nummessages) {
# read this message
my $mailpiece = $pop->HeadAndBody($i);
# Read the message, if it can't be parsed, delete it
my $bounce = eval { Mail::DeliveryStatus::BounceParser->new( $mailpi
+ece ) };
if ($@) { $pop->Delete( $i ); next; } # couldn't parse.
# If it's not a bounce, delete it
if(! $bounce->is_bounce()){ $pop->Delete( $i ); next; }
# After a bunch of testing, this is the consistent
# test for a 'real' bounce
if($bounce->orig_message_id() =~ /qmail\@my.mailserver.com/){
my @reports = $bounce->reports();
foreach my $report (@reports){
my $email_address = $report->get('email');
my $reason = $report->get('std_reason');
my $orig = $bounce->orig_text();
if($email_address eq ""){ next; }
### This is a series of tests related to different
# sites that I'm hosting. If I find the original
# message, I email the correct person and return
# true, so I know I can delete it.
my $ret_code = &check_otm($email_address,$reason,$orig);
if($ret_code eq "1"){
$pop->Delete( $i );
next;
}
# several other tests
# It's a bounce, I don't know what to do,
# let Richard figure it out
my $rawmessage = $report->get('raw');
&default_handler($email_address,$reason,$orig,$rawmessage);
$pop->Delete( $i );
}
# This message isn't from my server, drop
# it
}else{ $pop->Delete( $i ); }
}
# You have to State and Close the POP3 connection
# for the messages to actually BE deleted.
$pop->State();
$pop->Close();
}
This script processes roughly 1000 messages in 30 seconds on a 1.8 Ghz machine. I was thinking it might be useful to others in a similar situation.