Typed views over the MIME entity tree (RecursiveAI.OneMail.Mime.Reports, .Mhtml, .Authentication). The tree stays the
lossless store: a view reads it, never changes it, so an untouched message still writes back byte for byte. Every repair is a
typed defect with the byte offset. The ambiguity entries (RP, AR, RS, MH) are in ambiguities.md.
multipart/report (RFC 6522)
MimeReport.Find(tree) returns the first multipart/report (searching nested message/rfc822 as well); TryCreate(entity)
tests one entity. The view exposes the human-readable part, the machine-readable part, and the original (a message, or
headers only for text/rfc822-headers and message/rfc822-headers). GetDeliveryStatus, GetDispositionNotification and
GetFeedbackReport parse the machine part and add their defects to a caller's list.
| Report | Media type | Spec | Class |
|---|---|---|---|
| Delivery status | message/delivery-status, message/global-delivery-status | RFC 3464, 6533 | DeliveryStatusReport (per-message block, per-recipient RecipientStatus) |
| Disposition notification | message/disposition-notification | RFC 8098 (3798) | DispositionNotification, MdnDisposition |
| Feedback | message/feedback-report | RFC 5965, 6591, 6650 | FeedbackReport |
- Field syntax (
ReportFieldParser): RFC 5322 header syntax, a block ends at an empty line. A line of only blanks is a fold, not an empty line. Values are not RFC 2047-decoded (the RFCs do not use encoded words here); bytes are read as UTF-8 if valid, else windows-1252. A lone CR stays in the value (BareCarriageReturnInHeader). - Typed values:
DsnTypedValue(type; value),EnhancedStatusCode(RFC 3463; trailing comments tolerated),DsnAction, dates asMimeDate. A value that does not fit stays readable as text and raisesReportInvalidFieldValue. - Kind: the machine part's own media type decides; a
report-typethat disagrees or is missing raisesReportStructureInvalid. - Limits: a report body over 16 MiB or more than 100,000 fields raises
ReportLimitExceeded; the rest is not parsed. - Writing:
MimeReport.CreateDeliveryStatus / CreateDispositionNotification / CreateFeedbackReportbuild themultipart/report(boundary overridable for reproducible output);ToByteswrites fields folded at 78 columns, UTF-8 for non-ASCII (the caller picks theglobalmedia type withRequiresGlobal).
MHTML (RFC 2557)
MhtmlDocument.Find / TryCreate / Open / Load view a multipart/related (or a lone HTML part) as a root resource plus
resources. Resolve(reference, htmlBase) returns the resource a reference in the HTML names; ResolveToAbsolute gives the
URI. MhtmlBuilder writes a .mht. MhtmlUri is RFC 3986 §5.2 resolution, with thismessage:/ as the base of last resort.
Base order (RFC 2557 §5): the HTML's own base argument, the legacy Content-Base, an absolute Content-Location of the
part and then of its enclosing entities, else thismessage:/. Lookup is exact first, then after Normalize (scheme and host
case, percent-hex case, space as %20, default ports, dot segments). cid: matches Content-ID only (RFC 2557 §8.3: a
Content-Location holding a cid: URL never satisfies it), with angle brackets stripped and percent-decoding; mid: needs
both a content id and the message id. A lookup falls through to enclosing multipart/related entities. Duplicates: the first
wins, and the ambiguity is reported.
Authentication-Results and Received-SPF
MimeEntity.GetAuthenticationResults() and GetReceivedSpf() parse every such field (RFC 8601; RFC 7208 §9.1) into
AuthenticationResults (authserv-id, version, AuthenticationMethodResult with method, version, result, comment, reason and
AuthenticationProperty ptype.name=value) and ReceivedSpf. This is parsing only: nothing is evaluated or trusted, and
callers must still check the authserv-id against their own boundary. MimeEntity.GetAuthenticationResults takes the header
text as the tree stores it (comments and folds kept in the raw value).
Method
tools/oracles/reports runs ours, MimeKit 4.18.2 (public API) and Python 3.13 email over testdata/reports (119 messages,
generated by tools/reports/make_fixtures.py, with RFC 3464, 8601, 7208 and 2557 examples) and over the corpus messages that
carry the headers (corpus.sh). Python has no Authentication-Results parser; Received-SPF has no oracle (RFC examples only).
MHTML reference resolution is also run against Python urllib.parse.urljoin on 218 base/reference pairs. A difference must
match a line of explained.tsv (naming an ambiguity entry or a documented oracle limit) or the run fails.