Per-recipient email diagnostics for the mailto: positional recipient list
Source:R/email-diagnostics.R
get_mailto_recipients.RdCompanion helper (ADR 0006) that reports structural, per-recipient
facts about the recipients in the positional to of a
mailto: URL — the comma-separated addr-spec list before the
? (RFC 6068 section 2). Recipients carried in to/cc/
bcc hfields are RFC 5322 address-lists and are deliberately
out of scope; only the positional list is analysed.
Arguments
- url
A character vector of URLs. Non-
mailto:URLs (and, underscheme_acceptance = "web", allmailto:URLs, which the web allowlist does not accept) contribute no rows.- url_standard
Standard profile passed to the general parser. A
mailto:path is opaque, so this does not affect the classification; it defaults to"rfc3986"because the general parser requires a selector.- scheme_policy
Controls whether scheme-less, host-shaped input is accepted (an input-acceptance axis, distinct from
protocol_handling, which only controls how the scheme is presented, and fromurl_standard, which controls interpretation). Defaults to "infer"."infer": (Default) Fabricate
http://for scheme-less host-shaped input (e.g.example.comparses ashttp://example.com), a browser-omnibox-style affordance. This is the historical behavior."require": Reject scheme-less input — a scheme-less host-shaped value becomes
parse_status = "error"rather than gaining a fabricated scheme. Use this for a strict, pure-parser posture. Note this governs only bare host input; scheme-relative//hostinput is governed separately byscheme_relative_handling.
- scheme_acceptance
Which scheme tokens may enter parsing (a scheme-acceptance axis, distinct from
scheme_policy, which governs scheme-less input, and fromurl_standard, which governs interpretation). Defaults to "web"."web": (Default) Only the curated web-scheme allowlist (
http/https/ftp/ftps/file) is admitted; a scheme-bearing input outside it isparse_status = "error". This is the historical, byte-for-byte compatible behavior."general": Admit any syntactically valid scheme token and parse opaque (
mailto:x), non-special (foo://host), and RFC-generic URLs. Requires an expliciturl_standard("rfc3986"or"whatwg"), which decides the interpretation;generalwithurl_standard = NULLis an error. Non-special / opaque hosts receive no www-stripping, no domain/TLD derivation, and are never run through the IDNA/punycode helpers. A non-special scheme with no//is an opaque path: it has no authority, sohost,user,portand thedomain/tldcolumns are allNAand the entire remainder is thepath(query/fragmentare still split off). This includesmailto:— the recipient's@never re-triggers authority parsing. To decompose amailto:recipient, use the accessors (get_host()/get_domain()/get_user(), ADR 0012 D7) orget_mailto_recipients(); those deliberately return a recipient's parts where this table presentsNA, because a recipient domain is extraction metadata, not the URL's authority.
- smtp_wire
Logical; when
TRUE, compute the opt-in SMTP wire-projection columns (see the corresponding section). Defaults toFALSE.
Value
A data.frame (always, including for length-1 or all-empty
input) with one row per positional-to recipient and columns:
urlthe source URL the recipient came from.
recipient_index1-based index of the recipient within that URL's positional list.
mailto_local_part_formRFC 6068 local-part form:
"dot-atom-text","quoted-string","invalid", or"indeterminate".mailto_domain_formRFC 6068 domain form:
"ascii-dot-atom-text","idna2008-domain","bracketed-domain","invalid", or"indeterminate".smtp_mailbox_rhs_syntax_formRFC 5321 mailbox RHS form, independent of the mailto grammar and of DNS:
"domain","address-literal","invalid", or"indeterminate".public_suffix_knownTRUE/FALSEwhether the domain's public suffix is known to the PSL (non-validating);NAwhen the RHS is not a domain form.smtp_domain_wire_form(opt-in)
"ascii-domain","a-label-domain","u-label-domain","address-literal", or"unavailable".smtp_envelope_wire_mode(opt-in)
"ascii","smtputf8", or"unavailable".smtp_envelope_address_requires_smtputf8(opt-in) logical;
NAwhen no wire projection could be made.smtp_local_part_length_ok(opt-in) logical, serialized local-part at most 64 octets;
NAwhen unavailable.smtp_direct_forward_path_fits(opt-in) logical, the octet length of
"<" + Mailbox + ">"is at most 256;NAwhen unavailable. The familiar 254 is the RFC 3696 EID 1690 derivation of this path limit, not a standalone production.
Details
Each fact names the grammar it was judged against. The left of the
addr-spec is classified as an RFC 6068 local-part and,
independently, the right is classified both as RFC 6068 mailto domain
vocabulary and as an SMTP (RFC 5321) mailbox right-hand side — these are
distinct grammars, so no single column spans them. Public-suffix knowledge is
reported separately and is explicitly non-validating: a known suffix
is not mailbox validity.
Facts, not a gate
These are selected structural facts, not
a conformance oracle and never a validator. A recipient's classification
never turns a parse into an error, and the absence of an invalid
value does not imply the address is deliverable or fully RFC-conformant.
No DNS resolution or deliverability check is performed.
SMTP wire-projection facts (opt-in)
Set smtp_wire = TRUE to
additionally compute the SMTP transport facts that require an actual
serialized wire projection of the address (octet-length limits per RFC 5321
section 4.5.3.1, and the SMTPUTF8 envelope mode per RFC 6531/6530). These
are octet facts on the UTF-8 wire bytes, distinct from the syntax
classifications above and from DNS. When smtp_wire = FALSE (the
default) the five smtp_* wire columns are still present but carry
the "unavailable"/NA sentinel; the same sentinel is used for
a recipient whose address cannot be projected (an invalid mailbox).
Provenance-preserving parse
The positional list is tokenized on the
raw (still percent-encoded) source before decoding, so an encoded
comma (%2C) is never a recipient separator and an encoded quote or
bracket (%22, %5B/%5D) still protects a raw comma;
each field is then percent-decoded exactly once and classified.
Examples
get_mailto_recipients("mailto:jane@example.com",
scheme_acceptance = "general")
#> url recipient_index mailto_local_part_form
#> 1 mailto:jane@example.com 1 dot-atom-text
#> mailto_domain_form smtp_mailbox_rhs_syntax_form public_suffix_known
#> 1 ascii-dot-atom-text domain TRUE
#> smtp_domain_wire_form smtp_envelope_wire_mode
#> 1 unavailable unavailable
#> smtp_envelope_address_requires_smtputf8 smtp_local_part_length_ok
#> 1 NA NA
#> smtp_direct_forward_path_fits
#> 1 NA
get_mailto_recipients(
"mailto:a@example.com,\"b,c\"@example.org",
scheme_acceptance = "general"
)
#> url recipient_index mailto_local_part_form
#> 1 mailto:a@example.com,"b,c"@example.org 1 dot-atom-text
#> 2 mailto:a@example.com,"b,c"@example.org 2 quoted-string
#> mailto_domain_form smtp_mailbox_rhs_syntax_form public_suffix_known
#> 1 ascii-dot-atom-text domain TRUE
#> 2 ascii-dot-atom-text domain TRUE
#> smtp_domain_wire_form smtp_envelope_wire_mode
#> 1 unavailable unavailable
#> 2 unavailable unavailable
#> smtp_envelope_address_requires_smtputf8 smtp_local_part_length_ok
#> 1 NA NA
#> 2 NA NA
#> smtp_direct_forward_path_fits
#> 1 NA
#> 2 NA
# opt-in SMTP wire-projection facts
get_mailto_recipients("mailto:a@xn--mnchen-3ya.de",
scheme_acceptance = "general", smtp_wire = TRUE)
#> url recipient_index mailto_local_part_form
#> 1 mailto:a@xn--mnchen-3ya.de 1 dot-atom-text
#> mailto_domain_form smtp_mailbox_rhs_syntax_form public_suffix_known
#> 1 ascii-dot-atom-text domain TRUE
#> smtp_domain_wire_form smtp_envelope_wire_mode
#> 1 a-label-domain ascii
#> smtp_envelope_address_requires_smtputf8 smtp_local_part_length_ok
#> 1 FALSE TRUE
#> smtp_direct_forward_path_fits
#> 1 TRUE