# Syntax of an e-mail message. This set of EBNF rules defines the syntax of # an e-mail as specified by RFC 5322 available at: # https://www.rfc-editor.org/info/rfc5322 # The overall rule defined is "message". # The rule "specials" is not used but it is cited in the specification. # # Adapted from the cited source for the BNF_CHK program by Umberto Salsi. # Rules are listed in the same order of the specification, with the title # of the paragraph intermixed. Changes made: # - Hypens in rule names replaced by underscore. # - *RULE --> {RULE} # - 1*RULE --> RULE {RULE} # - 1*2RULE --> RULE [RULE] # - Arbitrary ASCII control codes %d12 replaced with prose "ASCII code 12". # - Printable ASCII codes %d61 replaced with verbatim string "A". # - Missing rules from RFC 5234 added. # Created: 2018-07-08 # Network Working Group P. Resnick, Ed. # Request for Comments: 5322 Qualcomm Incorporated # Obsoletes: 2822 October 2008 # Updates: 4021 # Category: Standards Track # # Internet Message Format # # NUL rule added by me: NUL = "ASCII code zero"; # As explained in the text, the following rules come from RFC 5234: HTAB = "ASCII code 9"; LF = "ASCII code 10"; CR = "ASCII code 13"; CRLF = CR LF; SP = "ASCII code 32"; WSP = SP | HTAB; ALPHA = "A".."Z" | "a".."z"; DIGIT = "0".."9"; DQUOTE = "\""; VCHAR = "!".."~"; # visible (printing) characters, ASCII codes 21-126 # 3.2.1. Quoted characters quoted_pair = ("\\" (VCHAR | WSP)) | obs_qp; # 3.2.2. Folding White Space and Comments FWS = ([{WSP} CRLF] WSP {WSP}) | obs_FWS; # Folding white space ctext = "!".."'" | # Printable US-ASCII "*".."[" | # characters not including "]".."~" | # "(", ")", or "\" obs_ctext; ccontent = ctext | quoted_pair | comment; comment = "(" {[FWS] ccontent} [FWS] ")"; CFWS = ([FWS] comment {[FWS] comment} [FWS]) | FWS; # 3.2.3. Atom atext = ALPHA | DIGIT | # Printable US-ASCII "!" | "#" | # characters not including "$" | "%" | # specials. Used for atoms. "&" | "'" | "*" | "+" | "-" | "/" | "=" | "?" | "^" | "_" | "`" | "{" | "|" | "}" | "~"; atom = [CFWS] atext {atext} [CFWS]; dot_atom_text = atext {atext} {"." atext {atext}}; dot_atom = [CFWS] dot_atom_text [CFWS]; specials = "(" | ")" | # Special characters that do "<" | ">" | # not appear in atext "[" | "]" | ":" | ";" | "@" | "\\" | "," | "." | DQUOTE; # 3.2.4. Quoted Strings qtext = "!" | # Printable US-ASCII "#".."[" | # characters not including "]".."~" | # "\" or the quote character obs_qtext; qcontent = qtext | quoted_pair; quoted_string = [CFWS] DQUOTE {[FWS] qcontent} [FWS] DQUOTE [CFWS]; # 3.2.5. Miscellaneous Tokens word = atom | quoted_string; phrase = word {word} | obs_phrase; unstructured = ({[FWS] VCHAR} {WSP}) | obs_unstruct; # 3.3. Date and Time Specification date_time = [ day_of_week "," ] date time [CFWS]; day_of_week = ([FWS] day_name) | obs_day_of_week; day_name = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"; date = day month year; day = ([FWS] DIGIT [DIGIT] FWS) | obs_day; month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"; year = (FWS DIGIT DIGIT DIGIT DIGIT FWS) | obs_year; time = time_of_day zone; time_of_day = hour ":" minute [ ":" second ]; hour = DIGIT DIGIT | obs_hour; minute = DIGIT DIGIT | obs_minute; second = DIGIT DIGIT | obs_second; zone = (FWS ( "+" | "-" ) DIGIT DIGIT DIGIT DIGIT) | obs_zone; # 3.4. Address Specification address = mailbox | group; mailbox = name_addr | addr_spec; name_addr = [display_name] angle_addr; angle_addr = [CFWS] "<" addr_spec ">" [CFWS] | obs_angle_addr; group = display_name ":" [group_list] ";" [CFWS]; display_name = phrase; mailbox_list = (mailbox {"," mailbox}) | obs_mbox_list; address_list = (address {"," address}) | obs_addr_list; group_list = mailbox_list | CFWS | obs_group_list; # 3.4.1. Addr_Spec Specification addr_spec = local_part "@" domain; local_part = dot_atom | quoted_string | obs_local_part; domain = dot_atom | domain_literal | obs_domain; domain_literal = [CFWS] "[" {[FWS] dtext} [FWS] "]" [CFWS]; dtext = "!".."Z" | # Printable US-ASCII "^".."~" | # characters not including obs_dtext; # "[", "]", or "\" # 3.5. Overall Message Syntax message = (fields | obs_fields) [CRLF body]; body = ({{text} CRLF} {text}) | obs_body; # Max line length 998 text = "ASCII codes: 1-9, 11, 12, 14-127"; # text = %d1-9 | ; Characters excluding CR # %d11 | ; and LF # %d12 | # %d14-127; # 3.6. Field Definitions fields = {trace {optional_field} | {resent_date | resent_from | resent_sender | resent_to | resent_cc | resent_bcc | resent_msg_id}} {orig_date | from | sender | reply_to | to | cc | bcc | message_id | in_reply_to | references | subject | comments | keywords | optional_field}; # 3.6.1. The Origination Date Field orig_date = "Date:" date_time CRLF; # 3.6.2. Originator Fields from = "From:" mailbox_list CRLF; sender = "Sender:" mailbox CRLF; reply_to = "Reply_To:" address_list CRLF; # 3.6.3. Destination Address Fields to = "To:" address_list CRLF; cc = "Cc:" address_list CRLF; bcc = "Bcc:" [address_list | CFWS] CRLF; # 3.6.4. Identification Fields message_id = "Message_ID:" msg_id CRLF; in_reply_to = "In_Reply_To:" msg_id {msg_id} CRLF; references = "References:" msg_id {msg_id} CRLF; msg_id = [CFWS] "<" id_left "@" id_right ">" [CFWS]; id_left = dot_atom_text | obs_id_left; id_right = dot_atom_text | no_fold_literal | obs_id_right; no_fold_literal = "[" {dtext} "]"; # 3.6.5. Informational Fields subject = "Subject:" unstructured CRLF; comments = "Comments:" unstructured CRLF; keywords = "Keywords:" phrase {"," phrase} CRLF; # 3.6.6. Resent Fields resent_date = "Resent_Date:" date_time CRLF; resent_from = "Resent_From:" mailbox_list CRLF; resent_sender = "Resent_Sender:" mailbox CRLF; resent_to = "Resent_To:" address_list CRLF; resent_cc = "Resent_Cc:" address_list CRLF; resent_bcc = "Resent_Bcc:" [address_list | CFWS] CRLF; resent_msg_id = "Resent_Message_ID:" msg_id CRLF; # 3.6.7. Trace Fields trace = [return] received {received}; return = "Return_Path:" path CRLF; path = angle_addr | ([CFWS] "<" [CFWS] ">" [CFWS]); received = "Received:" {received_token} ";" date_time CRLF; received_token = word | angle_addr | addr_spec | domain; # 3.6.8. Optional Fields optional_field = field_name ":" unstructured CRLF; field_name = ftext {ftext}; ftext = "!".."9" | # Printable US-ASCII ";".."~"; # characters not including # ":". # 4. Obsolete Syntax # 4.1. Miscellaneous Obsolete Tokens obs_NO_WS_CTL = "ASCII codes 1-8, 11, 12, 14-31, 127"; # obs_NO_WS_CTL = %d1-8 | ; US-ASCII control # %d11 | ; characters that do not # %d12 | ; include the carriage # %d14-31 | ; return, line feed, and # %d127 ; white space characters obs_ctext = obs_NO_WS_CTL; obs_qtext = obs_NO_WS_CTL; obs_utext = NUL | obs_NO_WS_CTL | VCHAR; obs_qp = "\\" (NUL | obs_NO_WS_CTL | LF | CR); obs_body = {({LF} {CR} {(NUL | text) {LF} {CR}}) | CRLF}; obs_unstruct = {({LF} {CR} {obs_utext {LF} {CR}}) | FWS}; obs_phrase = word {word | "." | CFWS}; obs_phrase_list = [phrase | CFWS] {"," [phrase | CFWS]}; # 4.2. Obsolete Folding White Space obs_FWS = WSP {WSP} {CRLF WSP {WSP}}; # 4.3. Obsolete Date and Time obs_day_of_week = [CFWS] day_name [CFWS]; obs_day = [CFWS] DIGIT [DIGIT] [CFWS]; obs_year = [CFWS] DIGIT DIGIT {DIGIT} [CFWS]; obs_hour = [CFWS] DIGIT DIGIT [CFWS]; obs_minute = [CFWS] DIGIT DIGIT [CFWS]; obs_second = [CFWS] DIGIT DIGIT [CFWS]; obs_zone = "UT" | "GMT" | # Universal Time # North American UT # offsets "EST" | "EDT" | # Eastern: - 5/ - 4 "CST" | "CDT" | # Central: - 6/ - 5 "MST" | "MDT" | # Mountain: - 7/ - 6 "PST" | "PDT" | # Pacific: - 8/ - 7 # "A".."I" | # Military zones - "A" "K".."Z" | # through "I" and "K" "a".."i" | # through "Z", both "k".."z"; # upper and lower case # 4.4. Obsolete Addressing obs_angle_addr = [CFWS] "<" obs_route addr_spec ">" [CFWS]; obs_route = obs_domain_list ":"; obs_domain_list = {CFWS | ","} "@" domain {"," [CFWS] ["@" domain]}; obs_mbox_list = {[CFWS] ","} mailbox {"," [mailbox | CFWS]}; obs_addr_list = {[CFWS] ","} address {"," [address | CFWS]}; obs_group_list = [CFWS] "," {[CFWS] ","} [CFWS]; obs_local_part = word {"." word}; obs_domain = atom {"." atom}; obs_dtext = obs_NO_WS_CTL | quoted_pair; # 4.5. Obsolete Header Fields obs_fields = {obs_return | obs_received | obs_orig_date | obs_from | obs_sender | obs_reply_to | obs_to | obs_cc | obs_bcc | obs_message_id | obs_in_reply_to | obs_references | obs_subject | obs_comments | obs_keywords | obs_resent_date | obs_resent_from | obs_resent_send | obs_resent_rply | obs_resent_to | obs_resent_cc | obs_resent_bcc | obs_resent_mid | obs_optional}; # 4.5.1. Obsolete Origination Date Field obs_orig_date = "Date" {WSP} ":" date_time CRLF; # 4.5.2. Obsolete Originator Fields obs_from = "From" {WSP} ":" mailbox_list CRLF; obs_sender = "Sender" {WSP} ":" mailbox CRLF; obs_reply_to = "Reply_To" {WSP} ":" address_list CRLF; # 4.5.3. Obsolete Destination Address Fields obs_to = "To" {WSP} ":" address_list CRLF; obs_cc = "Cc" {WSP} ":" address_list CRLF; obs_bcc = "Bcc" {WSP} ":" (address_list | ({[CFWS] ","} [CFWS])) CRLF; # 4.5.4. Obsolete Identification Fields obs_message_id = "Message_ID" {WSP} ":" msg_id CRLF; obs_in_reply_to = "In_Reply_To" {WSP} ":" {phrase | msg_id} CRLF; obs_references = "References" {WSP} ":" {phrase | msg_id} CRLF; obs_id_left = local_part; obs_id_right = domain; # 4.5.5. Obsolete Informational Fields obs_subject = "Subject" {WSP} ":" unstructured CRLF; obs_comments = "Comments" {WSP} ":" unstructured CRLF; obs_keywords = "Keywords" {WSP} ":" obs_phrase_list CRLF; # 4.5.6. Obsolete Resent Fields obs_resent_from = "Resent_From" {WSP} ":" mailbox_list CRLF; obs_resent_send = "Resent_Sender" {WSP} ":" mailbox CRLF; obs_resent_date = "Resent_Date" {WSP} ":" date_time CRLF; obs_resent_to = "Resent_To" {WSP} ":" address_list CRLF; obs_resent_cc = "Resent_Cc" {WSP} ":" address_list CRLF; obs_resent_bcc = "Resent_Bcc" {WSP} ":" (address_list | ({[CFWS] ","} [CFWS])) CRLF; obs_resent_mid = "Resent_Message_ID" {WSP} ":" msg_id CRLF; obs_resent_rply = "Resent_Reply_To" {WSP} ":" address_list CRLF; # 4.5.7. Obsolete Trace Fields obs_return = "Return_Path" {WSP} ":" path CRLF; obs_received = "Received" {WSP} ":" {received_token} CRLF; # 4.5.8. Obsolete optional fields obs_optional = field_name {WSP} ":" unstructured CRLF; # THE END