package emile

  1. Overview
  2. Docs
Legend:
Library
Module
Module type
Parameter
Class
Class type

Emile module, parser of e-mail address.

type raw =
  1. | Quoted_printable of (string, [ `Msg of string ]) result
  2. | Base64 of (string, [ `Msg of string ]) result

An e-mail address can contain as a part of a phrase (identifier) an encoded string. Standards describe 2 kinds of encoding:

  • Quoted Printable: used to insert hexadecimal value with the = operator.
  • Base 64: string encoded in MIME's Base64

Parser already decodes encoded raw, the client can use it as is.

type word = [
  1. | `Atom of string
  2. | `String of string
]

The local part of an e-mail address is composed by two kinds of words:

  • `Atom is string as is.
  • `String is a string surrounded by double-quote to allow white-space.

The second kind is sanitized — we deleted double-quote which surround string.

type local = word list

Local part of e-mail address.

type addr =
  1. | IPv4 of Ipaddr.V4.t
  2. | IPv6 of Ipaddr.V6.t
  3. | Ext of string * string

Subset of domain described by RFC5321 which contains 3 kinds of address:

  • IPv4: a valid IPv4 address
  • IPv6: a valid IPv6 address
  • Ext (ldh, value): an extended kind of domain recognized by ldh identifier which valus is value

Parser of IPv4 and IPv6 was done by Ipaddr. An extended kind Ext needs to be resolved by the client.

type domain = [
  1. | `Domain of string list
  2. | `Addr of addr
  3. | `Literal of string
]

Domain part of e-mail address. A domain integrate kinds from RFC5321 (see addr), a domain described by RFC5322 and a `Literal which is the last best-effort value possible as a domain.

Emile does not resolve domain.

type phrase = [ `Dot | `Word of word | `Encoded of string * raw ] list

A phrase is a sentence to associate a name with an e-mail address or a group of e-mail addresses. `Encoded value is not normalized on the charset specified. The encoded's string is decoded as is only. For example, `Encoded can inform to use KOI-8 encoding (cyrillic charset). However, Emile does not check if value is a valid KOI-8 string, nor normalizes to unicode. Emile just decodes it as is.

type mailbox = {
  1. name : phrase option;
  2. local : local;
  3. domain : domain * domain list;
}

A mailbox is an e-mail address. It contains an optional name (see phrase), a local-part (see local) and one or more domain(s).

type group = {
  1. group : phrase;
  2. mailboxes : mailbox list;
}

A group is a named set of mailbox.

type address = local * (domain * domain list)

A basic e-mail address.

type t = [
  1. | `Mailbox of mailbox
  2. | `Group of group
]

The Emile's t type which is a singleton (only one mailbox) or a list of e-mail addresses (a group).

Pretty-printers.

val pp_addr : addr Fmt.t
val pp_domain : domain Fmt.t
val pp_word : word Fmt.t
val pp_local : local Fmt.t
val pp_raw : raw Fmt.t
val pp_phrase : phrase Fmt.t
val pp_mailbox : mailbox Fmt.t
val pp_group : group Fmt.t
val pp_address : address Fmt.t
val pp : t Fmt.t

Equal & Compare.

type 'a equal = 'a -> 'a -> bool
type 'a compare = 'a -> 'a -> int
val case_sensitive : string -> string -> int

Alias of String.compare.

val case_insensitive : string -> string -> int

case_insensitive a b maps values with lowercase_ascii and compare them with String.compare. We do not map UTF8 value.

val equal_word : compare:string compare -> word equal

equal ~compare a b tests if word a and word b are semantically equal. compare specifies implementation to compare two string (i.e. to be case-sensitive or not).

val compare_word : ?case_sensitive:bool -> word compare

compare_word ?case_sensitive a b compares word a and word b semantically. From standards, word SHOULD be case-sensitive, the client can notice this behaviour by ?case_sensitive (default is true).

val equal_raw : compare:string compare -> raw equal

equal_raw a b tests if raw a and raw b are semantically equal. Semantically equal means we compare raw's content, by this way, a Base64 raw could be equal to a Quoted_printable raw if and only if string are equal.

val compare_raw : compare:string compare -> raw compare

compare_raw a b compares raw a and raw b semantically.

val equal_phrase : phrase equal

equal_phrase a b tests if phrase a and phrase b are semantically equal. In this case, the comparison is case-insensitive between elements in phrase. The order of elements is important.

val compare_phrase : phrase compare

compare_phrase a b compares phrase a and phrase b semantically.

val equal_addr : addr equal

equal_addr a b tests if addr a and addr b are semantically equal. An IPv4 should be equal with an IPv6 address. Then, for extended kind, we strictly compare (Pervasives.compare) kind and value.

val compare_addr : addr compare

compare_addr a b compares addr a and addr b, we prioritize IPv6, IPv4 and finally Ext.

val equal_domain : domain equal

equal_addr a b tests if domain a and domain b are semantically equal. We do not resolve domain, a `Domain could be semantically equal to another `Domain if they point to the same IPv4/IPv6.

val compare_domain : domain compare

comapre_domain a b compares domain a and domain b, we prioritize `Domain, `Literal and finally `Addr. The comparison between two `Literal and between part of `Domain are case-insensitive.

val equal_domains : (domain * domain list) equal

equal_domains a b apply equal_domain to ordered domains (see compare_domain) between a and b.

val compare_domains : (domain * domain list) compare

compare_domains a b compares ordered list of domain a and ordered list of domain b.

val equal_local : ?case_sensitive:bool -> local equal

equal_local ?case_sensitive a b tests if local a and local b are semantically equal. Standards notices local-part SHOULD be case-sensitive, the client can choose this behaviour with case_sensitive.

val compare_local : ?case_sensitive:bool -> local compare

compare_local ?case_sensitive a b compares local a and local b semantically. The user can decide if the comparison is case-sensitive or not (with case_sensitive).

val equal_mailbox : ?case_sensitive:bool -> mailbox equal

equal_mailbox ?case_sensitive a b tests if mailbox a and mailbox b are semantically equal. The user can define if the local-part need to be case-sensitive or not (by case_sensitive). If a xor b has a name, we consider a = b if we have the same local-part and same domain(s). Otherwise, we compare identifier/phrase between them.

val compare_mailbox : ?case_sensitive:bool -> mailbox compare

compare ?case_sensitive a b compares mailbox a and mailbxo b semantically. We prioritize local-part, domain-part and finally optionnal name.

val compare_group : group compare

comapre_group a b compares group a and group b. We compare the group name first and compare ordered mailboxes list then.

val equal_group : group equal

equal_group a b tests if group a and group b are semantically equal. We compare first group name and ordered mailboxes list then.

val compare_address : address compare

compare_address a b compares semantically address a* and address b.

val equal_address : address equal

equal_address a b tests semantically address a and address b.

val equal_set : t equal

equal a b tests semantically set a and set b.

val compare_set : t compare

compare a b compares set a and set b.

Parsers

If you don't want a headache, you should move on.

module Parser : sig ... end

This is an aggregation of rules used to parse an e-mail address. The goal of this documentation is to show relations between RFCs, updates, and final description of parts needed to parse an e-mail address.

type error = [
  1. | `Invalid
]

The error type.

val pp_error : error Fmt.t

pp_error ppf err is pretty-printer of error.

module List : sig ... end
val address_of_string_with_crlf : string -> (address, error) result

address_of_string_with_crlf s parses s which have the form: local@domain. Named email or multiple-domain email are not handle by this parser. s must terminate with CRLF as the delimiter. If the parser fails, it return an error error.

val address_of_string : string -> (address, error) result

address_of_string s parses s which have the form: local@domain. Named email or multiple-domain email are not handle by this function. If the parser fails, it return an error error. It's possible that address_of_string did not consume all s.

val address_of_string_raw : off:int -> len:int -> ?tmp:Bigstringaf.t -> string -> (int * address, error) result

address_of_string_raw s off len parses a sub-part of s starting at off and it computes at most len bytes. It returns the email and how many bytes it consumes. Named email or multiple-domain are not handle by this parser. If the parser fails, it return an error error.

If the user has an already allocated Bigstringaf.t, it can use it as an internal buffer to parse given input s.

val set_of_string_with_crlf : string -> (t, error) result
val set_of_string : string -> (t, error) result
val set_of_string_raw : off:int -> len:int -> ?tmp:Bigstringaf.t -> string -> (int * t, error) result
val of_string_with_crlf : string -> (mailbox, error) result

of_string_with_crlf s parses s which can have multiple form:

  • Named email Bobby <bobby@mail.net>
  • Multiple-domain email <@laposte.net:bobby@mail.net
  • Usual form bobby@mail.net
  • Surrounded form <bobby@mail.net>

About named email, the parser handles encoded-word (according RFC 2047) to be able to use a special charset (like UTF-8) to show the name. Parser decodes encoded-word as is and do not do any translation from charset specified to any encoding (eg. translation from latin1 to UTF-8).

s must terminates with CRLF as the delimiter. If the parser fails, it return an error error.

val of_string : string -> (mailbox, error) result

of_string s is of_string_with_crlf but did not need CRLF at the end. It's possible that of_string did not consume all s.

val of_string_raw : off:int -> len:int -> ?tmp:Bigstringaf.t -> string -> (int * mailbox, error) result

of_string_raw s off len is of_string_with_crlf but did not need CRLF at the end. It parses only a sub-part of s starting at off and computes at most len bytes. It returns how many bytes it consumed.

If the user has an already allocated Bigstringaf.t, it can use it as an internal buffer to parse given input s.

OCaml

Innovation. Community. Security.