Standalone, never-raising RFC 5322 MIME parser.
parse/1 turns a canonical raw MIME body into a stable internal
representation, or returns a structured MailglassInbound.MIMEError. It
never raises: the underlying gen_smtp mimemail decoder
escapes through three mechanisms (erlang:error, throw, and
:exit/:undef from iconv), all of which are absorbed by the
Mailglass.OptionalDeps.GenSmtp gateway seam and translated into
{:error, %MailglassInbound.MIMEError{}}.
Contract
@spec parse(binary()) :: {:ok, repr} | {:error, MailglassInbound.MIMEError.t()}{:ok, repr}—repris%{headers: ..., parts: ..., attachments: ..., inline: ...}(see Internal representation below).{:error, %MIMEError{type: :inbound_mime_invalid}}— the raw source could not be parsed (any of the three escape mechanisms, or the representation exceeded:max_depth).:causecarries the tagged gateway failure;:contextcarries%{byte_size: byte_size(raw)}.{:error, %MIMEError{type: :gen_smtp_unavailable}}— the optionalgen_smtpdependency is not loaded; MIME parsing is unavailable (degraded fallback).
Note
The :max_depth option bounds the depth of the internal representation
walk — collect_leaves/3 re-walking the already-decoded tree — and gives
a deterministic structured ceiling on what the pipeline iterates. It does
not limit the underlying :mimemail decoder recursion: decode_and_build/2
calls the decoder first, which fully parses to any depth before the guard
ever runs. So :max_depth does not by itself defend against
provider-fed deep-nesting (boundary-bomb) DoS.
Provider-fed DoS hardening — a real decoder-level recursion limit — is a A future concern that will plug into this same seam. The guard is kept because it is that seam and because it bounds the representation the pipeline iterates.
Internal representation
parse/1 returns a map with four keys:
:headers— the top-level decoded header proplist ([{binary, binary}]).:parts— a flattened list of non-attachment, non-inline leaf parts. Each part is%{type, subtype, headers, params, body}wherebodyis the raw (untranscoded) leaf bytes.:attachments— leaf parts whoseContent-Dispositionisattachment. Each carries a resolved:filename(disposition_params["filename"] || content_type_params["name"]).:inline— leaf parts whoseContent-Dispositionis explicitlyinlineand which carry a filename (typicallyContent-IDimages). Inline text leaves without a filename land in:parts.
Standalone — not wired into any provider path
This parser is the producer only. It is NOT wired into the working JSON-based Postmark/SendGrid normalize paths in this version; Mailgun/SES raw-MIME ingress is the first consumer.
Encoding note
The gateway passes {:encoding, :none} to the decoder (gen_smtp does not
bundle iconv), so leaf :body bytes are not transcoded to UTF-8. A consumer
that needs UTF-8 text must transcode using the part's declared charset
(content_type_params["charset"]). Flagged as a future improvement.
Summary
Types
A single decoded leaf or container part in the internal representation.
Functions
Parses a raw RFC 5322 MIME body. See the moduledoc for the full contract.
Parses a raw RFC 5322 MIME body with options.
Types
@type part() :: %{ type: binary(), subtype: binary(), headers: [{binary(), binary()}], params: map(), body: term() }
A single decoded leaf or container part in the internal representation.
@type repr() :: %{ headers: [{binary(), binary()}], parts: [part()], attachments: [part()], inline: [part()] }
The stable internal representation returned by parse/1.
Functions
@spec parse(binary()) :: {:ok, repr()} | {:error, MailglassInbound.MIMEError.t()}
Parses a raw RFC 5322 MIME body. See the moduledoc for the full contract.
Equivalent to parse(raw, []).
@spec parse( binary(), keyword() ) :: {:ok, repr()} | {:error, MailglassInbound.MIMEError.t()}
Parses a raw RFC 5322 MIME body with options.
Options
:max_depth— maximum multipart nesting depth before the representation-depth guard trips and returns:inbound_mime_invalid(default100). See the moduledoc note on what this guard does and does not bound.:gen_smtp_available?— overrides the gateway availability check (testing seam for the degraded path). Defaults toMailglass.OptionalDeps.GenSmtp.available?/0.