跳到主要内容

3. 语法

3.1. 引言​

本节给出的语法定义了互联网消息的合法语法. 符合本规范的消息必须符合本节的语法. 如果本节中的某个选项应当被优先生成, 则会在正文中或语法旁边的注释中指明.

对于所定义的各个表达式, 先给出语法及其用法的简要说明, 然后给出 ABNF 形式的语法, 最后给出语义分析. 以下被使用但未另行规定的原语记号取自 [RFC5234] 附录 B.1 的 "Core Rules": CR、LF、CRLF、HTAB、SP、WSP、DQUOTE、DIGIT、ALPHA 和 VCHAR.

在某些定义中会出现名称以 "obs-" 开头的非终结符. 这些 "obs-" 元素指第 4 节过时语法中定义的记号. 在所有情况下, 为生成合法的互联网消息, 这些产生式都应当被忽略, 并且不得作为此类消息的一部分使用. 然而, 在解释消息时, 这些记号必须作为合法语法的一部分被遵循. 在这个意义上, 第 3 节定义了用于生成消息的文法 (其中 "obs-" 元素应被忽略), 而第 4 节为解释消息增加了文法.

3.2. 词法记号​

以下规则用于定义一个底层词法分析器, 它向更高层的解析器提供记号. 本节定义结构化头部字段主体中使用的记号.

注意: 本规范的读者需要特别注意这些词法记号在本文档后文低层语法和高层语法中的使用方式. 尤其是, 第 3.2.2 节定义的空白记号和注释记号会被用于此处定义的低层记号, 而这些低层记号又依次用作后文定义的高层记号的一部分. 因此, 即使某个定义中没有显式出现空白和注释, 高层记号中也可能允许它们.

3.2.1. 带引号的字符​

有些字符被保留用于特殊解释, 例如分隔词法记号. 为允许把这些字符用作不解释的数据, 提供了一种引用机制.

quoted-pair     =   ("\" (VCHAR / WSP)) / obs-qp

凡出现 quoted-pair 的地方, 它都被解释为该字符本身. 也就是说, 作为 quoted-pair 一部分出现的 "" 字符在语义上是"不可见"的.

注意: "" 字符可能出现在消息中而不属于 quoted-pair. 不属于 quoted-pair 的 "" 字符在语义上并非不可见. 在本规范中, 目前出现 quoted-pair 的位置只有 ccontent、qcontent 以及第 4 节中的 obs-dtext.

3.2.2. 折叠空白与注释​

空白字符, 包括用于折叠的空白 (第 2.2.3 节所述), 可以出现在头部字段主体中的许多元素之间. 此外, 被当作注释处理的字符串也可以以括在圆括号中的字符形式包含在结构化字段主体中. 下面定义折叠空白 (FWS) 和注释构造.

只要括在圆括号中的字符串不出现在第 3.2.4 节定义的 "quoted-string" 内部, 它们就被视为注释. 注释可以嵌套.

本规范中有若干位置可以自由插入注释和 FWS. 为适应这种语法, 另外定义了一个 "CFWS" 记号, 用于可以出现注释和/或 FWS 的位置. 但是, 在本规范中出现 CFWS 的地方, 不得以这样的方式插入它: 使折叠头部字段的任何一行完全由 WSP 字符组成而没有其他内容.

FWS             =   ([*WSP CRLF] 1*WSP) /  obs-FWS
; Folding white space

ctext = %d33-39 / ; Printable US-ASCII
%d42-91 / ; characters not including
%d93-126 / ; "(", ")", or "\"
obs-ctext

ccontent = ctext / quoted-pair / comment

comment = "(" *([FWS] ccontent) [FWS] ")"

CFWS = (1*([FWS] comment) [FWS]) / FWS

在本规范全文中, 凡出现 FWS (折叠空白记号) 的地方, 都表示第 2.2.3 节所讨论的折叠可以在此发生. 凡消息中出现折叠 (即头部字段主体中包含后跟任意 WSP 的 CRLF), 在按照本规范对该头部字段进行任何进一步的语义分析之前, 都要先执行展开 (删除 CRLF). 也就是说, FWS 中出现的任何 CRLF 在语义上都是"不可见"的.

注释通常用于结构化字段主体中, 以提供某种人类可读的信息性文本. 由于注释允许包含 FWS, 因此注释内部允许折叠. 另请注意, 由于注释中允许 quoted-pair, 圆括号和反斜杠字符可以出现在注释中, 只要它们以 quoted-pair 的形式出现. 从语义上看, 外层圆括号不是注释的一部分; 注释是这两个圆括号之间包含的内容. 如前所述, 任何 quoted-pair 中的 "" 以及注释中出现的任何 FWS 中的 CRLF 在语义上都是"不可见"的, 因此也不是注释的一部分.

结构化头部字段中词法记号之间出现的连续 FWS、注释或 CFWS, 在语义上被解释为单个空格字符.

3.2.3. 原子​

结构化头部字段主体中的若干产生式只是某些基本字符组成的字符串. 这类产生式称为原子 (atom).

某些结构化头部字段主体还允许在连续的 atext 中出现句点字符 (".", ASCII 值 46). 为此定义了另一个 "dot-atom" 记号.

注意: "specials" 记号在本规范的其他任何地方都没有出现. 它只是那些不出现在 atext 中的可见 (即非控制、非空白) 字符. 提供它仅仅是因为它对使用词法分析消息的工具的实现者有用. specials 中的每个字符都可以用来指示词法分析中的切分点.

atext           =   ALPHA / DIGIT /    ; Printable US-ASCII
"!" / "#" / ; characters not including
"$" / "%" / ; specials. Used for atoms.
"&" / "'" /
"*" / "+" /
"-" / "/" /
"=" / "?" /
"^" / "_" /
"`" / "{" /
"|" / "}" /
"~"

atom = [CFWS] 1*atext [CFWS]

dot-atom-text = 1*atext *("." 1*atext)

dot-atom = [CFWS] dot-atom-text [CFWS]

specials = "(" / ")" / ; Special characters that do
"<" / ">" / ; not appear in atext
"[" / "]" /
":" / ";" /
"@" / "\" /
"," / "." /
DQUOTE

atom 和 dot-atom 都被解释为单个单元, 即由组成它们的字符串构成. 从语义上看, 围绕其余字符的可选注释和 FWS 不是原子的一部分; 原子只是 atom 中连续的一段 atext 字符, 或 dot-atom 中的 atext 与 "." 字符.

3.2.4. 带引号的字符串​

包含原子所允许字符之外的其他字符的字符串, 可以用带引号的字符串 (quoted string) 格式表示, 其中这些字符被引号 (DQUOTE, ASCII 值 34) 字符包围.

qtext           =   %d33 /             ; Printable US-ASCII
%d35-91 / ; characters not including
%d93-126 / ; "\" or the quote character
obs-qtext

qcontent = qtext / quoted-pair

quoted-string = [CFWS]
DQUOTE *([FWS] qcontent) [FWS] DQUOTE
[CFWS]

quoted-string 被视为一个单元. 也就是说, 从语义上看, quoted-string 与 atom 完全相同. 由于 quoted-string 允许包含 FWS, 因此允许折叠. 另请注意, 由于 quoted-string 中允许 quoted-pair, 引号和反斜杠字符可以出现在 quoted-string 中, 只要它们以 quoted-pair 的形式出现.

从语义上看, 引号字符之外的可选 CFWS 以及引号字符本身都不是 quoted-string 的一部分; quoted-string 是两个引号字符之间包含的内容. 如前所述, 任何 quoted-pair 中的 "" 以及 quoted-string 内出现的任何 FWS/CFWS 中的 CRLF 在语义上都是"不可见"的, 因此也不是 quoted-string 的一部分.

3.2.5. 其他记号​

另外定义了三个记号: 用于原子和/或带引号字符串组合的 word 和 phrase, 以及用于非结构化头部字段及结构化头部字段中某些位置的 unstructured.

word            =   atom / quoted-string

phrase = 1*word / obs-phrase

unstructured = (*([FWS] VCHAR) *WSP) / obs-unstruct

3.3. 日期与时间规范​

日期和时间值出现在若干头部字段中. 本节规定完整日期和时间规范的语法. 尽管整个日期时间规范中都允许折叠空白, 但推荐在出现 FWS 的每个位置都使用单个空格 (无论它是必需的还是可选的); 某些较旧的实现无法正确解释较长的连续折叠空白.

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] 1*2DIGIT FWS) / obs-day

month = "Jan" / "Feb" / "Mar" / "Apr" /
"May" / "Jun" / "Jul" / "Aug" /
"Sep" / "Oct" / "Nov" / "Dec"

year = (FWS 4*DIGIT FWS) / obs-year

time = time-of-day zone

time-of-day = hour ":" minute [ ":" second ]

hour = 2DIGIT / obs-hour

minute = 2DIGIT / obs-minute

second = 2DIGIT / obs-second

zone = (FWS ( "+" / "-" ) 4DIGIT) / obs-zone

日 (day) 是月份中的数字日. 年 (year) 是 1900 或之后的任意数字年份.

time-of-day 规定自所示日期午夜以来经过的小时数、分钟数以及可选的秒数.

日期和 time-of-day 应当表示当地时间.

zone 规定日期和 time-of-day 所表示的时间相对于 Coordinated Universal Time (UTC, 旧称 "Greenwich Mean Time") 的偏移量. "+" 或 "-" 表示 time-of-day 是超前于 (即位于 Universal Time 以东) 还是落后于 (即位于 Universal Time 以西) Universal Time. 前两位数字表示与 Universal Time 相差的小时数, 后两位数字表示与 Universal Time 相差的额外分钟数. (因此, +hhmm 表示 +(hh * 60 + mm) 分钟, -hhmm 表示 -(hh * 60 + mm) 分钟). 形式 "+0000" 应当用于表示处于 Universal Time 的时区. 尽管 "-0000" 也表示 Universal Time, 但它用于表示该时间是在一个本地时区可能不是 Universal Time 的系统上生成的, 且该 date-time 不包含有关本地时区的任何信息.

date-time 规范必须在语义上有效. 也就是说, day-of-week (如果包含) 必须是该日期所对应的星期几; 数字形式的 day-of-month 必须在 1 到指定月份 (在指定年份中) 所允许的天数之间; time-of-day 必须在 00:00:00 到 23:59:60 的范围内 (秒数允许闰秒; 见 [RFC1305]); zone 的最后两位数字必须在 00 到 59 范围内.

3.4. 地址规范​

若干消息头部字段中出现地址, 用于指明消息的发送者和接收者. 地址可以是单个邮箱, 也可以是一组邮箱.

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

邮箱接收邮件. 它是一个概念性实体, 不一定与文件存储相关. 例如, 某些站点可能选择把邮件打印到打印机上, 并把打印输出投递到收件人的办公桌上.

通常, 邮箱由两部分组成: (1) 可选的显示名 (display name), 用于指明可向邮件应用程序用户显示的收件人名称 (可以是人或系统); (2) 括在尖括号 ("<" 和 ">") 中的 addr-spec 地址. 邮箱还有一种更简单的替代形式, 其中只出现 addr-spec 地址, 不带收件人名称和尖括号. 互联网 addr-spec 地址在第 3.4.1 节描述.

注意: 某些遗留实现使用这种简单形式: addr-spec 不带尖括号出现, 但在 addr-spec 之后把收件人名称作为注释括在圆括号中. 由于注释中信息的含义没有规定, 实现应当使用完整的 name-addr 邮箱形式, 而不是遗留形式, 来指明与邮箱关联的显示名. 另外, 由于某些遗留实现会解释注释, 一般应当避免在地址字段中使用注释, 以免使此类实现产生混淆.

当希望把若干邮箱当作单个单元处理时 (即在分发列表中), 可以使用 group 构造. group 构造允许发送者指明一个命名的收件人组. 其做法是: 给出该组的显示名, 后跟冒号, 再后跟由任意数量 (包括零个和一个) 邮箱组成的逗号分隔列表, 最后以分号结尾. 由于邮箱列表可以为空, 使用 group 构造也是一种简单的方式, 用来告知收件人消息被发送给一个或多个命名的收件人集合, 而不实际提供其中任何收件人的单个邮箱地址.

3.4.1. Addr-Spec 规范​

addr-spec 是一种特定的互联网标识符, 它包含一个本地解释的字符串, 后跟 at 符号 ("@", ASCII 值 64), 再后跟一个互联网域. 本地解释的字符串要么是 quoted-string, 要么是 dot-atom. 如果该字符串可以表示为 dot-atom (即它除 atext 字符或由 atext 字符包围的 "." 之外不含其他字符), 那么应当使用 dot-atom 形式, 不应当使用 quoted-string 形式. 在 addr-spec 中, 不应当在 "@" 周围使用注释和折叠空白.

注意: 此处为 addr-spec 的域部分给出了一种宽松语法. 然而, 域部分包含由其他协议 (例如 [RFC1034], [RFC1035], [RFC1123], [RFC5321]) 规定并使用的寻址信息. 因此, 实现有责任符合其使用语境下地址的语法.

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 = %d33-90 / ; Printable US-ASCII
%d94-126 / ; characters not including
obs-dtext ; "[", "]", or "\"

域部分标识邮件投递的目的点. 在 dot-atom 形式中, 它被解释为 [RFC1034]、[RFC1035] 和 [RFC1123] 所述的一种互联网域名 (主机名或邮件交换器名). 在 domain-literal 形式中, 域被解释为特定主机的字面互联网地址. 在这两种情况下, 寻址如何使用以及消息如何传输到特定主机, 都在单独的文档 (例如 [RFC5321]) 中论述. 这些机制超出本文档的范围.

local-part 部分是与域相关的字符串. 在地址中, 它只是在特定主机上被解释为某个特定邮箱的名称.

3.5. 整体消息语法​

消息由头部字段组成, 其后可选地跟随消息正文. 消息中的行不得超过 998 个字符 (不包括 CRLF), 但推荐把行限制为 78 个字符 (不包括 CRLF). (解释见第 2.1.1 节.) 在消息正文中, 尽管 text 规则中列出的所有字符都可以使用, 但不鼓励使用 US-ASCII 控制字符 (值 1 到 8、11、12 以及 14 到 31), 因为无法保证接收者显示时能正确解释它们.

message         =   (fields / obs-fields)
[CRLF body]

body = (*(*998text CRLF) *998text) / obs-body

text = %d1-9 / ; Characters excluding CR
%d11 / ; and LF
%d12 /
%d14-127

头部字段承载大部分语义信息, 在第 3.6 节中定义. 正文只是若干行文本, 就本规范而言不作解释.

3.6. 字段定义​

此处定义消息的头部字段. 所有头部字段都具有相同的一般语法结构: 字段名, 后跟冒号, 再后跟字段主体. 每个头部字段的具体语法在随后的各节中定义.

注意: 在随后各节中每个字段的 ABNF 语法里, 每个字段名后面都跟有必需的冒号. 然而, 为简洁起见, 有时在语法的文字说明中未提及冒号. 尽管如此, 它仍是必需的.

需要注意, 并不保证头部字段处于特定顺序. 它们可以以任意顺序出现, 而且已知它们经由互联网传输时偶尔会被重新排序. 然而, 就本规范而言, 在消息被传输或转换时, 不应当对头部字段重新排序. 更重要的是, 跟踪头部字段和重发头部字段不得重新排序, 并且应当保持为前置到消息的块. 更多信息见第 3.6.6 节和第 3.6.7 节.

唯一必需的头部字段是创建日期字段和发起者地址字段. 所有其他头部字段在语法上都是可选的. 更多信息见本定义之后的表格.

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)

下表指明每个字段在消息头部段中可以出现的次数限制, 以及对这些字段使用的任何特殊限制. 最小数量或最大数量列中某个值旁边的星号 ("*") 表示 "Notes" 列中有特殊限制.

+----------------+--------+------------+----------------------------+
| Field          | Min    | Max number | Notes                      |
|                | number |            |                            |
+----------------+--------+------------+----------------------------+
| trace          | 0      | unlimited  | Block prepended - see      |
|                |        |            | 3.6.7                      |
| resent-date    | 0*     | unlimited* | One per block, required if |
|                |        |            | other resent fields are    |
|                |        |            | present - see 3.6.6        |
| resent-from    | 0      | unlimited* | One per block - see 3.6.6  |
| resent-sender  | 0*     | unlimited* | One per block, MUST occur  |
|                |        |            | with multi-address         |
|                |        |            | resent-from - see 3.6.6    |
| resent-to      | 0      | unlimited* | One per block - see 3.6.6  |
| resent-cc      | 0      | unlimited* | One per block - see 3.6.6  |
| resent-bcc     | 0      | unlimited* | One per block - see 3.6.6  |
| resent-msg-id  | 0      | unlimited* | One per block - see 3.6.6  |
| orig-date      | 1      | 1          |                            |
| from           | 1      | 1          | See sender and 3.6.2       |
| sender         | 0*     | 1          | MUST occur with            |
|                |        |            | multi-address from - see   |
|                |        |            | 3.6.2                      |
| reply-to       | 0      | 1          |                            |
| to             | 0      | 1          |                            |
| cc             | 0      | 1          |                            |
| bcc            | 0      | 1          |                            |
| message-id     | 0*     | 1          | SHOULD be present - see    |
|                |        |            | 3.6.4                      |
| in-reply-to    | 0*     | 1          | SHOULD occur in some       |
|                |        |            | replies - see 3.6.4        |
| references     | 0*     | 1          | SHOULD occur in some       |
|                |        |            | replies - see 3.6.4        |
| subject        | 0      | 1          |                            |
| comments       | 0      | unlimited  |                            |
| keywords       | 0      | unlimited  |                            |
| optional-field | 0      | unlimited  |                            |
+----------------+--------+------------+----------------------------+

每个字段的确切解释在随后的各节中描述.

3.6.1. 创建日期字段​

创建日期字段由字段名 "Date" 后跟一个 date-time 规范组成.

orig-date       =   "Date:" date-time CRLF

创建日期指明消息创建者所指示的消息完成并可以进入邮件投递系统的日期和时间. 例如, 这可能是用户在应用程序中按下 "send" 或 "submit" 按钮的时间. 无论如何, 它特别不旨在传达消息实际被传输的时间, 而是人或消息的其他创建者把消息整理为最终形式、可以传输的时间. (例如, 未连接网络的便携式计算机用户可能会把消息排入队列等待投递. 创建日期旨在包含用户把消息排入队列的日期和时间, 而不是用户连接网络发送消息的时间.)

3.6.2. 发起者字段​

消息的发起者字段由 from 字段、sender 字段 (适用时) 以及可选的 reply-to 字段组成. from 字段由字段名 "From" 和一个或多个邮箱规范的逗号分隔列表组成. 如果 from 字段在 mailbox-list 中包含多个邮箱规范, 那么消息中必须出现 sender 字段, 它包含字段名 "Sender" 和一个邮箱规范. 在两种情况下, 还可以包含可选的 reply-to 字段, 它包含字段名 "Reply-To" 和一个或多个地址的逗号分隔列表.

from            =   "From:" mailbox-list CRLF

sender = "Sender:" mailbox CRLF

reply-to = "Reply-To:" address-list CRLF

发起者字段指明消息来源的邮箱. "From:" 字段指明消息的作者, 即负责撰写消息的人或系统的邮箱. "Sender:" 字段指明负责实际传输消息的代理的邮箱. 例如, 如果秘书替另一个人发送消息, 那么秘书的邮箱会出现在 "Sender:" 字段中, 而实际作者的邮箱会出现在 "From:" 字段中. 如果消息的发起者可以用单个邮箱表明, 且作者与传输者相同, 则不应当使用 "Sender:" 字段. 否则, 两个字段都应当出现.

注意: 传输者信息始终存在. "Sender:" 字段的缺失有时被错误地理解为没有指明负责传输消息的代理. 这种缺失仅仅意味着传输者与作者相同, 因此没有冗余地放入 "Sender:" 字段.

发起者字段还提供回复消息时所需的信息. 当 "Reply-To:" 字段存在时, 它指明消息作者建议把回复发送到的地址. 在没有 "Reply-To:" 字段时, 除非撰写回复的人另有规定, 否则默认应当把回复发送到 "From:" 字段中指定的邮箱.

在所有情况下, "From:" 字段都不应当包含不属于消息作者的任何邮箱. 关于为回复组织目标地址的更多信息, 另见第 3.6.3 节.

3.6.3. 目标地址字段​

消息的目标字段由三个可能的字段组成, 它们的形式相同: 字段名 ("To"、"Cc" 或 "Bcc"), 后跟一个或多个地址 (邮箱语法或组语法) 的逗号分隔列表.

to              =   "To:" address-list CRLF

cc = "Cc:" address-list CRLF

bcc = "Bcc:" [address-list / CFWS] CRLF

目标字段指明消息的收件人. 每个目标字段可以有一个或多个地址, 这些地址指明消息的预期收件人. 这三个字段的唯一区别在于各自的用途.

"To:" 字段包含消息主要收件人的地址.

"Cc:" 字段 (其中 "Cc" 意为 "Carbon Copy", 即用复写纸在打字机上制作副本) 包含要接收消息的其他人的地址, 尽管消息内容可能并非针对他们.

"Bcc:" 字段 (其中 "Bcc" 意为 "Blind Carbon Copy") 包含其地址不向消息其他收件人透露的收件人地址. "Bcc:" 字段有三种使用方式. 第一种情况: 当包含 "Bcc:" 字段的消息准备发送时, 删除 "Bcc:" 行, 但仍向所有收件人 (包括 "Bcc:" 字段中指定的收件人) 发送一份消息副本. 第二种情况: "To:" 和 "Cc:" 行中指定的收件人各自收到一份按上述方式删除了 "Bcc:" 行的消息副本, 但 "Bcc:" 行上的收件人会收到一份包含 "Bcc:" 行的单独消息副本. (当 "Bcc:" 字段中有多个收件人地址时, 某些实现实际上会向每个收件人发送一份单独的消息副本, 其中 "Bcc:" 只包含该特定收件人的地址.) 最后, 由于 "Bcc:" 字段可以不含任何地址, 因此可以发送一个不带任何地址的 "Bcc:" 字段, 向收件人表明曾向某人发送了密送副本. "Bcc:" 字段使用哪种方法取决于实现, 但关于每种方法的讨论请参阅本文档的 "Security Considerations" 一节.

当消息是对另一消息的回复时, 原消息作者的邮箱 ("From:" 字段中的邮箱) 或 "Reply-To:" 字段 (如果存在) 中指定的邮箱可以出现在回复的 "To:" 字段中, 因为它们通常是回复的主要收件人. 如果对具有目标字段的消息发送回复, 通常希望把回复副本发送给消息的所有收件人以及作者. 组成此类回复时, 原消息 "To:" 和 "Cc:" 字段中的地址可以出现在回复的 "Cc:" 字段中, 因为它们通常是回复的次要收件人. 如果原消息中存在 "Bcc:" 字段, 该字段中的地址可以出现在回复的 "Bcc:" 字段中, 但它们不应当出现在 "To:" 或 "Cc:" 字段中.

注意: 某些邮件应用程序具有自动回复命令, 会把原消息的目标地址包含在回复的目标地址中. 这些回复命令的行为取决于实现, 超出本文档的范围. 特别是, 当原消息具有 "Reply-To:" 字段时是否包含原目标地址, 本文档不予讨论.

3.6.4. 标识字段​

尽管在第 3.6 节的表格中列为可选, 但每条消息都应当具有 "Message-ID:" 字段. 此外, 回复消息应当酌情并按以下所述具有 "In-Reply-To:" 和 "References:" 字段.

"Message-ID:" 字段包含单个唯一的消息标识符. "References:" 和 "In-Reply-To:" 字段各包含一个或多个唯一的消息标识符, 可选地以 CFWS 分隔.

消息标识符 (msg-id) 语法是括在尖括号字符 "<" 和 ">" 中的 addr-spec 构造的受限版本. 与 addr-spec 不同, 此语法只允许在 "@" 左侧使用 dot-atom-text 形式, 并且消息标识符中的任何位置都没有内部 CFWS.

注意: 与 addr-spec 一样, 此处为 msg-id 中 "@" 的右侧给出了一种宽松语法. 然而, 本节后文推荐对 "@" 的右侧使用域. 再次说明, 域构造的语法由其他协议 (例如 [RFC1034], [RFC1035], [RFC1123], [RFC5321]) 规定并使用. 因此, 实现有责任符合其使用语境下地址的语法.

message-id      =   "Message-ID:" msg-id CRLF

in-reply-to = "In-Reply-To:" 1*msg-id CRLF

references = "References:" 1*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 "]"

"Message-ID:" 字段提供唯一标识特定消息的特定版本的消息标识符. 消息标识符的唯一性由生成它的主机保证 (见下文). 此消息标识符旨在机器可读, 对人不一定有含义. 消息标识符只对应于特定消息的一个版本; 消息的后续修订各自会获得新的消息标识符.

注意: 在许多情况下消息被"更改", 但这些更改并不构成该消息的新实例, 因此该消息不会获得新的消息标识符. 例如, 当消息被引入传输系统时, 通常会在其前面添加附加头部字段, 例如跟踪字段 (第 3.6.7 节所述) 和重发字段 (第 3.6.6 节所述). 添加此类头部字段不会改变消息的身份, 因此保留原来的 "Message-ID:" 字段. 在所有情况下, 决定 "Message-ID:" 字段是否改变的是消息发送者希望传达的含义 (即这是同一条消息还是不同的消息), 而不是消息中出现的 (或未出现的) 任何特定语法差异.

"References:" 和 "In-Reply-To:" 字段用于创建对消息的回复. 它们保存原消息的消息标识符以及其他消息的消息标识符 (例如, 在回复本身也是对某条消息的回复的情况下). "In-Reply-To:" 字段可用于标识新消息所回复的消息, 而 "References:" 字段可用于标识一个会话"线程".

创建对消息的回复时, 所生成消息的 "In-Reply-To:" 和 "References:" 字段按如下方式构造:

"In-Reply-To:" 字段将包含本条消息所回复的消息 ("父消息") 的 "Message-ID:" 字段的内容. 如果存在多个父消息, 那么 "In-Reply-To:" 字段将包含所有父消息的 "Message-ID:" 字段的内容. 如果任何父消息中都没有 "Message-ID:" 字段, 那么新消息将没有 "In-Reply-To:" 字段.

"References:" 字段将包含父消息 "References:" 字段 (如有) 的内容, 后跟父消息 "Message-ID:" 字段 (如有) 的内容. 如果父消息不包含 "References:" 字段, 但具有一个包含单个消息标识符的 "In-Reply-To:" 字段, 那么 "References:" 字段将包含父消息 "In-Reply-To:" 字段的内容, 后跟父消息 "Message-ID:" 字段 (如有) 的内容. 如果父消息没有 "References:"、"In-Reply-To:" 或 "Message-ID:" 中的任何一个字段, 那么新消息将没有 "References:" 字段.

注意: 某些实现会解析 "References:" 字段以显示"讨论线程". 这些实现假定每条新消息都是对单个父消息的回复, 因而它们可以沿 "References:" 字段向后追溯, 找出其中列出的每条消息的父消息. 因此, 不鼓励为具有多个父消息的回复构造 "References:" 字段; 本文档不定义如何这样做.

消息标识符 (msg-id) 本身必须是消息的全局唯一标识符. 消息标识符的生成者必须保证该 msg-id 唯一. 有若干算法可用于实现这一点. 由于 msg-id 与 addr-spec 语法相似 (除了不允许带引号的字符串、注释和折叠空白之外完全相同), 一种好方法是在 "@" 右侧放入生成该消息标识符的主机的域名 (或域字面量 IP 地址) (因为域名和 IP 地址通常是唯一的), 并在左侧放入当前绝对日期和时间与系统上某个当前唯一的 (也许是顺序的) 标识符 (例如进程 id 号) 的组合. 尽管其他算法也能奏效, 但推荐右侧包含某种域标识符 (主机本身的或其他域标识符), 使消息标识符的生成者能够保证左侧在该域范围内唯一.

从语义上看, 尖括号字符不是 msg-id 的一部分; msg-id 是两个尖括号字符之间包含的内容.

3.6.5. 信息性字段​

信息性字段全部是可选的. "Subject:" 和 "Comments:" 字段是第 2.2.1 节定义的非结构化字段, 因此可以包含文本或折叠空白. "Keywords:" 字段包含一个或多个 word 或 quoted-string 的逗号分隔列表.

subject         =   "Subject:" unstructured CRLF

comments = "Comments:" unstructured CRLF

keywords = "Keywords:" phrase *("," phrase) CRLF

这三个字段旨在只包含有关消息的人类可读内容. "Subject:" 字段最常见, 它包含标识消息主题的短字符串. 在用于回复时, 该字段主体可以以字符串 "Re: " (拉丁语 "in re" 的缩写, 意为"关于此事") 开头, 后跟原消息 "Subject:" 字段主体的内容. 如果这样做, 只应当使用一个字面字符串 "Re: ", 因为使用其他字符串或多于一个实例可能导致不良后果. "Comments:" 字段包含对消息正文文本的任何附加评论. "Keywords:" 字段包含对收件人可能有用的重要词和短语的逗号分隔列表.

3.6.6. 重发字段​

对于被用户重新引入传输系统的任何消息, 都应当添加重发字段. 每次这样做时, 都应当添加一组单独的重发字段. 对应于某次特定重发的所有重发字段应当归为一组. 每一组新的重发字段都前置到消息; 也就是说, 最近的一组重发字段出现在消息中更靠前的位置. 添加重发字段时, 消息中其他字段都不改变.

每个重发字段都对应于语法中其他位置的某个特定字段. 例如, "Resent-Date:" 字段对应于 "Date:" 字段, "Resent-To:" 字段对应于 "To:" 字段. 在每种情况下, 字段主体的语法都与先前为对应字段给出的语法相同.

使用重发字段时, 必须发送 "Resent-From:" 和 "Resent-Date:" 字段. 应当发送 "Resent-Message-ID:" 字段. 如果 "Resent-Sender:" 与 "Resent-From:" 相同, 则不应当使用 "Resent-Sender:".

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

重发字段用于标识某条消息已被用户重新引入传输系统. 使用重发字段的目的是让消息在最终收件人看来就好像是原发送者直接发送的, 且所有原字段保持不变. 每一组重发字段对应一次特定的重发事件. 也就是说, 如果一条消息被多次重发, 那么每一组重发字段给出每一次重发的标识信息. 重发字段严格来说是信息性的. 它们不得用于对消息的正常回复处理或其他此类自动操作.

注意: 把消息重新引入传输系统并使用重发字段, 与"转发"是不同的操作. "转发"有两种含义: 一种含义是, 用户可以指示邮件阅读程序把消息副本转发给另一个人, 使被转发的消息成为新消息的正文. 这种意义上的被转发消息看起来并非来自原发送者, 而是转发者发出的一条全新消息. 转发也可能指邮件传输程序收到消息后把它转发到另一个目的地进行最终投递. 重发头部字段不用于这两种转发.

重发发起者字段指明重发消息的人或系统的邮箱. 与常规发起者字段一样, 有两种形式: 一种是简单的 "Resent-From:" 形式, 包含执行重发的个人的邮箱; 另一种是更复杂的形式, 即一个个人 (在 "Resent-Sender:" 字段中标明) 代表一个或多个其他人 (在 "Resent-From:" 字段中标明) 重发消息.

注意: 回复重发的消息时, 回复的行为与回复任何其他消息一样, 使用原来的 "From:"、"Reply-To:"、"Message-ID:" 及其他字段. 重发字段只是信息性的, 不得用于正常的回复处理.

"Resent-Date:" 指明重发者发出该重发消息的日期和时间. 与 "Date:" 字段一样, 它不是消息实际被传输的日期和时间.

"Resent-To:"、"Resent-Cc:" 和 "Resent-Bcc:" 字段的功能分别与 "To:"、"Cc:" 和 "Bcc:" 字段相同, 只是它们指明重发消息的收件人, 而不是原消息的收件人.

"Resent-Message-ID:" 字段为重发的消息提供唯一标识符.

3.6.7. 跟踪字段​

跟踪字段是一组头部字段, 由一个可选的 "Return-Path:" 字段和一个或多个 "Received:" 字段组成. "Return-Path:" 头部字段包含一对尖括号, 其中括有一个可选的 addr-spec. "Received:" 字段包含一个 (可能为空的) 记号列表, 后跟一个分号和一个 date-time 规范. 每个记号必须是 word、angle-addr、addr-spec 或 domain. 规定其使用的规范 (例如 [RFC5321]) 会对跟踪字段的语法施加进一步的限制.

trace           =   [return]
1*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

关于跟踪字段在互联网邮件中的使用的完整讨论见 [RFC5321]. 就本规范而言, 跟踪字段严格来说是信息性的, 对它们的任何形式化解释都超出本文档的范围.

3.6.8. 可选字段​

消息中可以出现本文档未另作规定的字段. 它们必须符合 optional-field 的语法. 这是一个字段名 (由除 SP 和冒号之外的可打印 US-ASCII 字符组成), 后跟冒号, 再后跟符合 unstructured 语法的任意文本.

任何可选字段的字段名都不得与本文档其他任何地方规定的任何字段名相同.

optional-field  =   field-name ":" unstructured CRLF

field-name = 1*ftext

ftext = %d33-57 / ; Printable US-ASCII
%d59-126 ; characters not including
; ":".

就本规范而言, 任何可选字段都不作解释.