---
title: "onemail"
url: https://recursiveai.org/onemail/
description: "An email library for .NET in pure C#. Read EML, mbox, Maildir and Outlook MSG, PST, OLM and winmail.dat files, convert Outlook mail to EML and mbox and appointments to ICS, and read ICS calendars and vCards, with no Outlook and no native code. A preview. MIT or Apache-2.0."
---

# onemail

An email library for .NET in pure C#. Read EML, mbox and Outlook MSG, PST, OLM and winmail.dat files, convert mail and calendars between formats, and read calendars and contacts, with no Outlook and no native code.

A preview, 0.3.0-preview.2: the API isn’t stable yet. Outlook writing, protocols and cryptography come in later previews.

```sh
dotnet add package RecursiveAI.OneMail --prerelease
```

- [Getting started](https://recursiveai.org/onemail/docs/getting-started/)
- [Docs](https://recursiveai.org/onemail/docs/)
- [Cookbook](https://recursiveai.org/onemail/cookbook/)
- [GitHub](https://github.com/recursive-ai-oss/onemail-csharp)
- [NuGet](https://www.nuget.org/packages/RecursiveAI.OneMail)

## A quick look

One call parses a message; one opens any file. The format comes from the content, so an mbox, an `.msg` and a PST store all read the same way.

[Getting started](https://recursiveai.org/onemail/docs/getting-started/)

*Program.cs · C#*

```
using RecursiveAI.OneMail;
IMailItem message = Mail.Parse(File.ReadAllBytes("message.eml"));
Console.WriteLine($"{message.Subject} from {message.From[0].Address}");
using MailContainer pst = Mail.Open("archive.pst");
foreach (IMailItem item in pst.Enumerate())
    Console.WriteLine(item.Subject);
```

### Internet mail

MIME and EML, MHTML, delivery and read reports, mbox in every variant, Maildir, Apple Mail and Outlook Express files. Untouched messages write back byte for byte.

### Outlook files, on read

MSG and OFT, PST and OST, Outlook for Mac OLM, winmail.dat and RTF, with no Outlook installed. Large stores are read a message at a time.

### Any store to any format

PST, OLM or mbox to EML, mbox, Maildir, ICS or vCard, following Microsoft’s published rules. A loss report names everything that didn’t carry over.

### Calendars and contacts

ICS and vCard 2.1 to 4.0, plus their JSON and XML forms, and Outlook appointments, meetings and tasks to ICS. Recurring events expand with exceptions and time zones applied.

### Repairs, not exceptions

Broken mail is read anyway. Each repair is a typed defect with its byte offset, and resource limits are on by default.

### Every layer public

One-line `Mail.Parse` and `Mail.Open` on top; the MIME reader and writer, the stores, the PST layers and the MAPI model underneath.

## Compared with other readers, message by message

MIME reading was compared with MimeKit, Python and Go on 517,401 Enron messages and three smaller sets, with no unexplained difference, and every untouched message wrote back byte-identical. Outlook files were compared with extract-msg, libpff and three TNEF readers. Converting 70 Outlook items to MIME and back lost no property silently.

Speed: against MimeKit 4.18.2 on the same bytes in memory, OneMail’s median time is 0.091 of MimeKit’s across 18 MIME rows (geometric mean, x64; 0.093 on arm64). The parse and write rows reward OneMail’s lazy design; the like-for-like body-decoding rows range from 0.104 to 0.479 on x64. Calendars against Ical.Net 5.2.3: ICS parsing at 0.087 of its time on x64. Contacts against FolkerKinzel.VCards 8.2.0: vCard parsing at 0.959 on x64 and 1.093 on arm64, where it misses the project’s own bar.

- [Fidelity and known gaps](https://recursiveai.org/onemail/docs/compatibility/)
- [Benchmarks](https://recursiveai.org/onemail/docs/benchmarks/)

## The packages

- **OneMail**: `RecursiveAI.OneMail`: the mail formats, the read view, conversion, replies and forwards, and display HTML, whose sanitiser hasn’t had its independent security review yet: don’t rely on it to make untrusted mail safe. Preview, 0.3.0-preview.2.
- **Calendar**: `RecursiveAI.OneMail.Calendar`: ICS and vCard, jCal, jCard, xCal and xCard, recurrence and time zones. Depends only on .NET. Preview, 0.3.0-preview.2.
- **Cli**: `RecursiveAI.OneMail.Cli`: the `onemail` tool. `onemail inspect` lists the messages, attachments and defects of any file it reads; `convert` and `export` write it in another format. Preview, 0.3.0-preview.2.
- **Not yet**: Writing MSG and PST; SMTP, IMAP, POP3 and SASL; DKIM, ARC, S/MIME and OpenPGP; a message to PDF or an image.
- **.NET**: .NET 10, pure C#. NativeAOT, trimming and browser WebAssembly are supported targets. MIT or Apache-2.0.

## Known gaps

As the project’s fidelity table states them for 0.3.0-preview.2. No Outlook has been run yet: the Outlook formats are checked against public files and other readers.

[The full table](https://recursiveai.org/onemail/docs/compatibility/)

- **MSG / OFT**: “two fuzz-minimised files from the corpus are refused by the compound file reader (typed defect); writing comes later”
- **PST, OST**: “no public ANSI or 512-byte-page Unicode PST from Outlook exists; those layouts and 4 KiB multi-map stores are checked only on files we generate. libpff cannot read 4 KiB tables whose rows sit in a data tree, so those have no differential yet. Associated (hidden) items are read but libpff does not export them”
- **TNEF**: “no TNEF-in-MIME message in the public corpora; checked on generated wrappers (64 messages)”
- **RTF**: Compressed RTF: “`\objattph` positions become placeholders”
- **OLM**: “no Outlook 2011 layout sample; no archive with a raw MIME source, attendees or exceptions seen; a damaged zip is refused (recovery comes later); the working set is not constant in the entry count”
- **Calendar items**: “the corpus holds 2 distinct appointment and 2 task items and no meeting or recurring item: those are covered by generated items following the MS-OXOCAL examples”; “no Outlook-written recurring item yet”; “Outlook-written meetings and exceptions unseen”
- **MAPI to MIME**: “EX addresses with no SMTP form in the item are left out and reported (30 `AddressUnresolved` entries over the 70 items); Bcc is not written by default”; “`PidTagTransportMessageHeaders` is rewritten (reported)”; the view: “no directory lookup”
- **MIME to MAPI**: “Bcc dropped on the way out (reported); display-name spelling of a kept received header”
- **MIME / EML**: “ambiguity resolutions are provisional until mail clients are observed”. Reports: “no real-world reports in the corpora; checked on generated and RFC-derived fixtures”. “`Received-SPF` has no oracle”
- **Mailboxes**: mbox: “MMDF not supported”. Maildir: “Courier keyword file line format unverified against a real store; one directory listing held in memory”. EMLX: “flag bits unverified against a real Apple Mail store”. DBX: “one real store (a Windows XP laptop image, 28 files); OE4 files and deleted messages are not read”
- **Contacts**: “no Outlook-produced contact PST or MSG with the full property set, and no Outlook import/export of vCards to compare against”. “The slot order of telephone numbers and email addresses is approximated by a vCard and reported”
- **Conversions**: “flags and keywords from Outlook items (`PidTagFlagStatus`, last verb) are mapped by rule and not yet observed in Outlook; MSG and PST are not destinations until 0.5”
- **Display HTML**: “the display sanitiser has not had its independent review”. Tested in “one browser engine”

## Go further

- [GitHub](https://github.com/recursive-ai-oss/onemail-csharp)
- [NuGet](https://www.nuget.org/packages/RecursiveAI.OneMail)
- [Getting started](https://recursiveai.org/onemail/docs/getting-started/)
- [All docs](https://recursiveai.org/onemail/docs/)
- [Cookbook](https://recursiveai.org/onemail/cookbook/)
- [Read an MSG file](https://recursiveai.org/onemail/cookbook/read-msg-file/)
- [MSG to EML](https://recursiveai.org/onemail/cookbook/convert-msg-to-eml/)
- [PST to mbox](https://recursiveai.org/onemail/cookbook/convert-pst-to-mbox/)
- [Changelog](https://github.com/recursive-ai-oss/onemail-csharp/blob/v0.3.0-preview.2/CHANGELOG.md)

This project is unaffiliated with other products named OneMail.
