Parse Email Files Without a Mail Server
Nested MIME, four charsets in one mailbox, authentication headers that pass and fail, and a bounce that looks like a reply. Test your parser against real message shapes.

The messages you send yourself are all produced by one client, encoded one way, with one charset and one MIME structure. Real inboxes contain thirty years of accumulated convention from clients that no longer exist, and the parsing failures that matter come from the shapes you would never think to produce.
The email catalogue is 110 fixtures, all synthetic, all with fictional addresses, so you can commit a failing message to your own repository without putting anyone's real mail in version control.
plain-text-email-emlis the control.multipart-alternative-email-emlcarries the same message as text and as HTML, and the parser's job is to pick one rather than concatenate both.eml-html-only-no-plainis the case that breaks summarisers: there is no plain-text part at all, and code that reaches for one gets an empty string rather than an error.eml-nested-multipartnests amultipart/alternativeinside amultipart/mixed, which is what a message with both formatting and attachments actually looks like. Parsers that walk only one level deep find the attachment and lose the body, or the reverse.eml-cid-inline-imagereferences an image by content ID from the HTML part. Treating it as an attachment shows the user a mysterious extra file; failing to resolve it shows a broken image.
eml-p7-mbox-four-charsets is the single most useful fixture in this group: one mailbox, four
different character encodings across its messages. A parser that reads the mailbox with one
assumed encoding will produce mojibake for three quarters of it.
Underneath it, the transfer encodings are separated deliberately:
eml-p7-quoted-printable-accented-utf8 and eml-p7-base64-accented-utf8 carry the same accented
text through the two common encodings. Testing both matters because quoted-printable is where
soft line breaks live, and a parser that forgets to remove a trailing = produces text with
characters missing at line ends, roughly every 76 characters, which reads as random corruption.
eml-utf8-encoded-subject covers RFC 2047 header encoding, which is a separate mechanism from
body encoding and frequently implemented separately and worse. eml-utf8-filename-attachment is
the same problem on a filename, where the consequence is a saved file with an unusable name.
Six fixtures: eml-p7-auth-results-all-pass, -all-fail, -alignment-failure,
-multiple-hops, eml-p7-auth-two-dkim-signatures, and the annotated dkim-spf-annotated-eml.
Two of these are worth dwelling on.
eml-p7-auth-results-alignment-failure is the case where SPF and DKIM both pass and DMARC still
fails, because the domains they authenticated are not the domain in the From header. Code that
treats "SPF passed" as "this is legitimate" reports the opposite of the truth here.
eml-p7-auth-results-multiple-hops carries several Authentication-Results headers from
different relays. The only one you may trust is the one added by your own trusted boundary, and a
parser that reads the first or the last is trusting a header an attacker can add.
bounce-ndr-eml, eml-delivery-receipt-mdn, eml-delivery-out-of-office and
eml-p7-mbox-thread-with-bounce.
These matter to anything that threads conversations or counts replies. A bounce is a
multipart/report with machine-readable status inside; an out-of-office is an ordinary message
carrying Auto-Submitted. Treating either as a human reply corrupts response-rate metrics and, in
a support tool, reopens tickets nobody touched. eml-header-auto-submitted isolates the header
itself so you can test the check on its own.
eml-threaded-references and larger-mbox-thread exercise In-Reply-To and References.
Threading by subject line is the naive approach and it fails in both directions: it merges
unrelated messages that share a generic subject, and splits threads the moment someone edits the
subject. The references chain is the correct mechanism and these fixtures give you a real one to
walk.
eml-header-reply-to-different is the small trap where Reply-To and From disagree, which is
normal for mailing lists and is the difference between replying to a person and replying to
several thousand.
meeting-invite-ics-eml, eml-calendar-invite-request, eml-calendar-invite-cancel, plus the
bare ics-request-sample and ics-cancel-sample.
An invite is a message with a text/calendar part whose METHOD decides the meaning: REQUEST
creates, CANCEL withdraws. Having both the wrapped and bare forms lets you separate a MIME
problem from an iCalendar problem in one step, which is usually the slowest part of debugging
these.
eml-nested-multipart, because a lost body is the most common report.eml-p7-mbox-four-charsets, because encoding bugs look like data corruption.eml-html-only-no-plain, because empty-string handling is rarely written deliberately.bounce-ndr-eml, if you count replies for anything.eml-p7-auth-results-alignment-failure, if you make trust decisions from headers.
Every message here is synthetic and the addresses are fictional, so none of this is anyone's real correspondence.
Continue this workflow
Documentation and troubleshooting
Was this article helpful?
Found an error? Send a correction.