Recursive AI

Parse an EML file in C#

All onemail recipes

Pass the bytes to Mail.Parse. Headers come back decoded (encoded words, charsets) and the bodies as .NET strings, with base64 and quoted-printable undone. The program below parses a public test message from Apache Tika with two attachments.

Terminal
dotnet new console -o ParseEml && cd ParseEml
dotnet add package RecursiveAI.OneMail --version 0.2.0-preview.2
curl -LO https://raw.githubusercontent.com/apache/tika/4.2.0/tika-parsers/tika-parsers-standard/tika-parsers-standard-modules/tika-parser-mail-module/src/test/resources/test-documents/testRFC822_multipart_attachments.eml

Put this in Program.cs and run dotnet run:

C#
using RecursiveAI.OneMail;
 
IMailItem msg = Mail.Parse(File.ReadAllBytes("testRFC822_multipart_attachments.eml"));
 
Console.WriteLine($"Subject: {msg.Subject}");
Console.WriteLine($"From:    {msg.From.FirstOrDefault()}");
Console.WriteLine($"To:      {string.Join(", ", msg.ToRecipients)}");
Console.WriteLine($"Date:    {(msg.Sent is { } d ? d.Value.ToString("u") : "none")}");
 
// Bodies come decoded (base64, quoted-printable, charset) as .NET strings
string? text = msg.TextBody?.ReadText();
string? html = msg.HtmlBody?.ReadText();
Console.WriteLine($"Text:    {text?.ReplaceLineEndings(" ").Trim()}");
Console.WriteLine($"HTML:    {html?.Length} characters");
 
foreach (IMailAttachment a in msg.Attachments)
    Console.WriteLine($"Attachment: {a.FileName}, {a.ContentType}, {a.Size} bytes");
 
// Malformed input is repaired, never thrown; each repair is listed
Console.WriteLine($"Defects: {msg.Defects.Count()}");
Text
Subject: EML 1
From:    Bugs Bunny <[email protected]>
To:      Bugs Bunny <[email protected]>
Date:    none
Text:    Hi,  This is test email for EML1  Thanks, Chetan
HTML:    151 characters
Attachment: Test TxtA.txt, text/plain, 33 bytes
Attachment: Test TxtB.txt, text/plain, 33 bytes
Defects: 0

This message has no Date header, so Sent is null. For a file that might be an .eml, an .msg or a mailbox, use Mail.Open(path) instead: it decides from the content.

What you get

  • Addresses as MailAddress values (Name, Address), with groups flattened: From, ToRecipients, Cc, Bcc, ReplyTo, Sender.
  • Ids and threading: MessageId, InReplyTo, References, without angle brackets.
  • Bodies: TextBody and HtmlBody are the candidates the message carries; neither is converted into the other.
  • Repairs, not exceptions. Accessors never throw on malformed content. Every repair is a MailDefect with its byte offset.
  • Lossless. Parsing doesn’t copy the bodies, and an untouched message writes back byte for byte. The lower layer, MimeEntity.Parse, gives the full part tree and every header.

Limits

  • A preview. 0.2.0-preview.2: the API can change before 1.0.
  • Ambiguous mail. Where mail clients disagree on how to read broken input, OneMail picks one reading and marks the defect as an ambiguity. The project calls these choices “provisional until mail clients are observed”.
  • No signatures or encryption. S/MIME and OpenPGP parts are read as parts; checking or decrypting them isn’t in this preview.

Everything checked, and every known gap: Fidelity and known gaps. The reader: Reading and writing MIME.