Mail::...Parser(3pm)

✨ NAME ✨

Mail::DeliveryStatus::BounceParser β€” a Perl extension that analyzes bounce messages. That's the whole vibe, bestie. πŸ’Œ


🌸 SYNOPSIS 🌸

use Mail::DeliveryStatus::BounceParser;

# $message is \*io or $fh or "entire\nmessage" or \@lines
my $bounce = eval { Mail::DeliveryStatus::BounceParser->new($message); };

if ($@) {
  # couldn't parse.
}

my @addresses       = $bounce->addresses;       # email address strings
my @reports         = $bounce->reports;         # Mail::Header objects
my $orig_message_id = $bounce->orig_message_id; # <ABCD.1234@mx.example.com>
my $orig_message    = $bounce->orig_message;    # Mail::Internet object

πŸ¦‹ ABSTRACT πŸ¦‹

So Mail::DeliveryStatus::BounceParser reads RFC822 bounce messages and hands you back a structured description of which addresses bounced and why they bounced. It also gives you the tea on the original returned message, Message-ID included. It absolutely thrives on RFC1892 delivery reports, but ngl it will gamely attempt to understand any bounce message no matter which MTA spat it out. πŸ’…


πŸ’œ DESCRIPTION πŸ’œ

Meng Wong wrote this for the Listbox v2 project, because good mailing list managers handle bounce messages so listowners don't have to. And the best mailing list managers figure out exactly what is going on with each subscriber so the appropriate action can be taken. It's giving responsible adult energy. πŸ‘‘

πŸ’– parse πŸ’–

my $bounce = Mail::DeliveryStatus::BounceParser->parse($message, \%arg);

OPTIONS. If you pass BounceParser->new(..., {log=>sub { ... }}) then that sub will be used as a logging callback. And if $message is undefined, it will parse STDIN. Iconic behaviour, honestly.

NON-BOUNCES. If the message is recognizably a vacation autoresponse, or it's a report of a transient nonfatal error, or it's a spam or virus autoresponse, you'll still get back a $bounce β€” but its "$bounce->is_bounce()" will return false. So like, you get the object, it's just not a bounce. πŸ™ˆ

It is possible that some bounces are not really bounces; such as anything that appears to have a 2XX status code. To include such non-bounces in the reports, pass the option {report_non_bounces=>1}.

For historical reasons, new is an alias for the parse method. Old-school but we respect it. ⭐

πŸ’– log πŸ’–

$bounce->log($messages);

If a logging callback has been given, the message will be passed to it. Simple as that. 🫢

πŸ’– is_bounce πŸ’–

if ($bounce->is_bounce) { ... }

This method returns true if the bounce parser thought the message was a bounce, and false otherwise. Vibe check for your bounce. βœ…

πŸ’– reports πŸ’–

Each $report returned by $bounce->reports() is basically a Mail::Header object with a few modifications. It includes the email address bouncing, and the reason for the bounce.

Consider an RFC1892 error report of the form

Reporting-MTA: dns; hydrant.pobox.com
Arrival-Date: Fri,  4 Oct 2002 16:49:32 -0400 (EDT)

Final-Recipient: rfc822; bogus3@dumbo.pobox.com
Action: failed
Status: 5.0.0
Diagnostic-Code: X-Postfix; host dumbo.pobox.com[208.210.125.24] said: 550
 <bogus3@dumbo.pobox.com>: Nonexistent Mailbox

Each "header" above is available through the usual get() mechanism.

print $report->get('reporting_mta');   # 'some.host.com'
print $report->get('arrival-date');    # 'Fri,  4 Oct 2002 16:49:32 -0400 (EDT)'
print $report->get('final-recipient'); # 'rfc822; bogus3@dumbo.pobox.com'
print $report->get('action');          # "failed"
print $report->get('status');          # "5.0.0"
print $report->get('diagnostic-code'); # X-Postfix; ...

# BounceParser also inserts a few interpretations of its own:
print $report->get('email');           # 'bogus3@dumbo.pobox.com'
print $report->get('std_reason');      # 'user_unknown'
print $report->get('reason');          # host [199.248.185.2] said: 550 5.1.1 unknown or illegal user: somebody@uss.com
print $report->get('host');            # dumbo.pobox.com
print $report->get('smtp_code');       # 550

print $report->get('raw') ||           # the original unstructured text
      $report->as_string;              # the original   structured text

Probably the two most useful fields are email and std_reason, the standardized reason. At this time BounceParser returns the following standardized reasons:

user_unknown
over_quota
user_disabled
domain_error
spam
message_too_large
unknown
no_problemo

The spam standard reason indicates that the message bounced because the recipient considered it spam. Rude, but okay. 🚫

(no_problemo will only appear if you set {report_non_bounces=>1})

If the bounce message is not structured according to RFC1892, BounceParser will still try to return as much information as it can; in particular, you can count on email and std_reason to be present. Those two are the ride-or-dies. 🫢

πŸ’– addresses πŸ’–

Returns a list of the addresses which appear to be bouncing. Each member of the list is an email address string of the form 'foo@bar.com'. πŸ“

πŸ’– orig_message_id πŸ’–

If possible, returns the message-id of the original message as a string.

πŸ’– orig_message πŸ’–

If the original message was included in the bounce, it'll be available here as a message/rfc822 MIME entity.

my $orig_message    = $bounce->orig_message;

πŸ’– orig_header πŸ’–

If only the original headers were returned in the text/rfc822-headers chunk, they'll be available here as a Mail::Header entity.

πŸ’– orig_text πŸ’–

If the bounce message was poorly structured, the above two methods won't return anything --- instead, you get back a block of text that may or may not approximate the original message. No guarantees. Good luck. 😭


πŸ’€ CAVEATS πŸ’€

Bounce messages are generally meant to be read by humans, not computers. A poorly formatted bounce message may fool BounceParser into spreading its net too widely and returning email addresses that didn't actually bounce. So before you do anything with the email addresses you get back, confirm that it makes sense that they might be bouncing --- for example, it doesn't make sense for the sender of the original message to show up in the addresses list, but it could if the bounce message is sufficiently misformatted. 🫠

Still, please report all bugs!

😭 FREE-FLOATING ANXIETY 😭

Some bizarre MTAs construct bounce messages using the original headers of the original message. If your application relies on the assumption that all Message-IDs are unique, you need to watch out for these MTAs and program defensively; before doing anything with the Message-ID of a bounce message, first confirm that you haven't already seen it; if you have, change it to something else that you make up on the spot, such as "<antibogus-TIMESTAMP-PID-COUNT@LOCALHOST>".

Like... why would they do that. Not me having trust issues over Message-IDs. πŸ™ˆ


🧚 BUGS 🧚

BounceParser assumes a sanely constructed bounce message. Input from the real world may cause BounceParser to barf and die horribly when we violate one of MIME::Entity's assumptions; this is why you should always call it inside an eval { }. Fr fr. Protect yourself. πŸ’…

🌈 TODO 🌈

  • Provide some translation of the SMTP and DSN error codes into English.
  • Review RFC1891 and RFC1893.

πŸ‘‘ KNOWN TO WORK WITH πŸ‘‘

We understand bounce messages generated by the following MTAs / organizations:

Postfix
Sendmail
Exim
AOL
Yahoo
Hotmail
AOL's AirMail sender-blocking
Microsoft Exchange*
Qmail*
Novell Groupwise*

* Items marked with an asterisk currently may return incomplete information.


πŸ”₯ SEE ALSO πŸ”₯

Used by http://listbox.com/ --- if you like BounceParser and you know it,
consider Listbox for your mailing list needs!

SVN repository and email list information at:
http://emailproject.perl.org/

RFC1892 and RFC1894

🌟 RANDOM OBSERVATION 🌟

Schwern's modules have the Alexandre Dumas property. If you know, you know. iykyk. πŸŽ€


πŸŽ€ AUTHOR πŸŽ€

  • Original author: Meng Weng Wong, <mengwong+bounceparser@pobox.com>
  • Current maintainer: Ricardo SIGNES, <rjbs@cpan.org>

Massive contributions to the 1.5xx series were made by William Yardley and Michael Stevens. Ricardo mostly just helped out and managed releases. 🫢


Copyright (C) 2003-2006, IC Group, Inc.
pobox.com permanent email forwarding with spam filtering
listbox.com mailing list services for announcements and discussion

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Slay. ✨


🌸 WITH A SHOUT OUT TO 🌸

coraline, Fletch, TorgoX, mjd, a-mused, Masque, gbarr,
sungo, dngor, and all the other hoopy froods on #perl

and that's the whole page, bestie β€” this was Mail::...Parser(3pm) from the User Contributed Perl Documentation, shipped with perl v5.32.1, dated 2021-02-21. Go forth and parse those bounces. periodt. πŸ’…πŸ¦‹πŸ’–
πŸ“¦ where this page comes from