非官方中文译本声明:本页为 IETF RFC 2046《Multipurpose Internet Mail Extensions (MIME) Part Two: Media Types(多用途互联网邮件扩展 第二部分:媒体类型)》中文译本,由 ztpop.net 整理翻译,仅供学习参考。RFC 文档由 IETF 发布,受 BCP 78 与 IETF 信托法律条款约束;本译本保留原文编号与结构,权威性以英文原文为准。英文原文见 rfc-editor.org/rfc/rfc2046

RFC 2046:MIME 第二部分(媒体类型)

本备忘录的状态

本文档为互联网团体规定了一种互联网标准跟踪协议,并征求讨论与改进建议。有关该协议的标准化状态与状况,请参考当前版本的《互联网官方协议标准》(STD 1)。本备忘录的分发不受限制。

摘要

STD 11、RFC 822 定义了一种报文表示协议,其中对 US-ASCII 报文头作了相当详尽的规定,但将报文内容(即报文主体)留作平坦的 US-ASCII 文本。这套统称为多用途互联网邮件扩展(Multipurpose Internet Mail Extensions,MIME)的文档,重新定义了报文的格式,以允许:

  1. 使用 US-ASCII 之外的字符集表示文本报文主体;
  2. 为非负文本报文主体提供一套可扩展的不同格式;
  3. 多部分(multi-part)报文主体;以及
  4. 使用 US-ASCII 之外的字符集表示文本报文头信息。

这些文档基于 RFC 934、STD 11 与 RFC 1049 中记录的早期工作,但对其进行了扩展与修订。由于 RFC 822 对报文主体所言甚少,这些文档在很大程度上与 RFC 822 正交(而非对其的修订)。

本系列的第一篇文档 RFC 2045 规定了用于描述 MIME 报文结构的各类头字段。本第二篇文档定义了 MIME 媒体类型系统的总体结构,并定义了一组初始的媒体类型。第三篇文档 RFC 2047 描述了对 RFC 822 的扩展,以允许互联网邮件头字段中出现非 US-ASCII 文本数据。第四篇文档 RFC 2048 规定了 MIME 相关设施的各类 IANA 注册流程。第五篇亦为最后一篇文档 RFC 2049 描述了 MIME 一致性准则,并提供了一些 MIME 报文格式的示例、致谢与参考文献。

这些文档是 RFC 1521 与 1522 的修订版,而后者本身又是 RFC 1341 与 1342 的修订版。RFC 2049 中的一个附录描述了与先前版本的差异与变更。

本 RFC 由 IETF 发布,受 BCP 78 及 IETF 信托相关条款约束。本中文译本由 ztpop.net 整理翻译,仅用于学习参考,不得作为权威依据;如中英文表述存在分歧,一律以英文原文为准。引用本译文时请注明出处并链接至英文原文。

目录

1. 引言

本系列的第一篇文档 RFC 2045 定义了一组头字段,其中包括 Content-Type。Content-Type 头字段用于说明 MIME 实体主体中数据的性质,做法是给出媒体类型与子类型标识符,并提供某些媒体类型可能需要的辅助信息。在类型与子类型名称之后,该头字段的其余部分只是一组以"属性/值"记号指定的参数。参数的先后顺序无关紧要。

一般而言,顶层媒体类型用于声明数据的通用类型,而子类型则指定该类型数据的具体格式。因此,媒体类型 "image/xyz" 足以告诉用户代理数据是一幅图像,即使用户代理并不了解具体的图像格式 "xyz"。此类信息可用于例如决定是否向用户显示来自未识别子类型的原始数据——对于 "text" 的未识别子类型,这样做或许是合理的,但对于 "image" 或 "audio" 的未识别子类型则不然。出于这一原因,已注册的 "text"、"image"、"audio" 与 "video" 子类型不应包含实质属于另一种类型的嵌入信息。此类复合格式应使用 "multipart" 或 "application" 类型来表示。

参数是媒体子类型的修饰符,因此从根本上不影响内容的性质。有意义的参数集合取决于媒体类型与子类型。大多数参数与某一个特定子类型相关联。然而,给定的顶层媒体类型可以定义适用于该类型任意子类型的参数。参数可能由其定义的媒体类型或子类型要求为必选,也可能是可选的。MIME 实现还必须忽略任何它们不认识的参数名称。

MIME 的 Content-Type 头字段与媒体类型机制经过精心设计,具有良好的可扩展性,预计媒体类型/子类型对及其关联参数集合将随时间显著增长。其他若干 MIME 设施,例如传输编码与 "message/external-body" 访问类型,也可能随时间定义新取值。为确保此类取值集合以有序、规范且公开的方式发展,MIME 建立了一套注册流程,该流程使用互联网号码分配局(IANA)作为 MIME 各个可扩展领域的中央注册机构。这些领域的注册流程在配套文档 RFC 2048 中描述。

本文档的其余部分定义并描述了初始的七个标准顶层媒体类型。

2. 顶层媒体类型的定义

一个顶层媒体类型的定义包括:

  1. 该类型的名称与描述,包括用于判断某一特定类型是否符合该类型的标准;
  2. 为该类型的所有子类型定义的参数(若有)的名称与定义(包括此类参数是必选还是可选);
  3. 用户代理和/或网关应如何处理该类型的未知子类型;
  4. 关于该顶层类型实体的网关转换的一般性考量(若有);以及
  5. 对该顶层类型实体的内容传输编码(content-transfer-encoding)的任何限制。

3. 初始顶层媒体类型概述

五个离散顶层媒体类型为:

  1. text(文本)——文本信息。其中子类型 "plain" 尤其表示不含任何格式命令或指令的纯文本。纯文本意在"原样"显示,除支持所指示的字符集外,无需任何特殊软件即可获取文本的完整含义。其他子类型用于经过排版的富文本,此时应用软件可增强文本的呈现效果,但要理解内容大意则不应要求此类软件。因此 "text" 的可能子类型包括任何不需要借助理解该格式的软件即可读取的的字处理格式。特别地,采用嵌入二进制格式信息的格式不被认为是可直接读取的。RFC 1341 中定义了一种非常简单且可移植的子类型 "richtext",后又在 RFC 1896 中以名称 "enriched" 作了进一步修订。
  2. image(图像)——图像数据。"image" 需要显示设备(如图形显示器、图形打印机或传真机)才能查看信息。本文档为广泛使用的 JPEG 图像格式定义了一个初始子类型;此外还为两种广泛使用的图像格式 jpeg 与 gif 定义了子类型。
  3. audio(音频)——音频数据。"audio" 需要音频输出设备(如扬声器或电话)来"显示"内容。本文档定义了一个初始子类型 "basic"。
  4. video(视频)——视频数据。"video" 需要显示动态影像的能力,通常包含专门的硬件与软件。本文档定义了一个初始子类型 "mpeg"。
  5. application(应用)——某种其他类型的数据,通常是未经解释的二进制数据,或供应用程序处理的信息。子类型 "octet-stream" 用于未经解释的二进制数据情形,此时最简单的推荐动作是将信息写入文件提供给用户。"PostScript" 子类型也为 PostScript 材料的传送而定义。"application" 的其他预期用途包括电子表格、基于邮件的日程安排系统的数据,以及用于"活动"(计算型)消息的语言,还有不可直接读取的字处理格式。注意,某些类型的应用数据可能存在安全考量,最显著的是 "application/PostScript" 以及任何形式的活动消息。这些问题将在本文档后面讨论。

两个复合顶层媒体类型为:

  1. multipart(多部分)——由多个独立数据类型实体组成的数据。本文档初始定义了四个子类型,包括基本的 "mixed" 子类型(指定一组通用的混合部分)、"alternative"(用于以多种格式表示同一数据)、"parallel"(用于意在同时查看的部分),以及 "digest"(用于每个部分默认类型为 "message/rfc822" 的多部分实体)。
  2. message(报文)——一个被封装的报文。媒体类型为 "message" 的主体本身是某个报文对象的全部或一部分。此类对象可能包含、也可能不包含其他实体。"rfc822" 子类型用于被封装内容本身是一个 RFC 822 报文的情形。"partial" 子类型用于不完整的 RFC 822 报文,以便允许将那些被认为过大、无法一次性通过传输设施传递的主体进行分片传输。另一个子类型 "external-body" 用于通过引用外部数据源来指定大型主体。

需要注意的是,此处给出的媒体类型取值列表可能会随时间通过上述机制得到扩充,并且子类型的集合预计会大幅增长。

4. 离散媒体类型取值

七个初始媒体类型取值中有五个指向离散主体。这些类型的内容必须由非 MIME 机制处理;它们对 MIME 处理器而言是不透明的。

4.1. 文本(text)媒体类型

"text" 媒体类型用于发送本质上以文本形式呈现的材料。可以使用 "charset" 参数来指明 "text" 子类型主体文本的字符集,尤其是包括子类型 "text/plain"——它是纯文本的通用子类型。纯文本不提供也不允许格式命令、字体属性规范、处理指令、解释指令或内容标记。纯文本被简单地视为字符的线性序列,其间可能被换行符或分页符打断。纯文本允许在同一文本位置堆叠多个字符。阿拉伯文、希伯来文等文字中的纯文本还可以包含允许任意混排书写方向相反的文本片段的设施。

除纯文本外,还有许多用于表示所谓"富文本"的格式。此类表示的许多都有一个有趣特征:即便没有解释它们的软件,它们在某种程度上也是可读的。因此,在最高层次上将其与图像、音频或以不可读形式表示的文本等不可读数据区分开来是有用的。在缺乏适当的解释软件时,向用户显示 "text" 的子类型是合理的,而对大多数非文本数据这样做则不合理。此类经过格式化的文本数据应当使用 "text" 的子类型来表示。

4.1.1. 换行符的表示

任何 MIME "text" 子类型的规范形式都必须始终将换行符表示为 CRLF 序列。类似地,MIME "text" 中出现的任何 CRLF 都必须表示一个换行符。除换行序列之外,单独使用 CR 与 LF 也是被禁止的。

无论所涉及的格式或字符集是什么,本规则均适用。

注意:当主体被显示时,换行符的正确解释取决于媒体类型。特别是,虽然将换行符视为换到新行对于显示 "text/plain" 主体是恰当的,但对于 "text" 的其他子类型(如 "text/enriched" [RFC-1896])而言,这种处理实际上是不正确的。类似地,在显示操作中是否应当添加换行符同样取决于媒体类型。正确显示 "text/plain" 应当无需添加任何换行符,而正确显示 "text/enriched" 则需要适当地添加换行符。

注意:某些协议规定了最大行长度。例如 SMTP [RFC-821] 在下一个 CRLF 序列之前最多允许 998 个八位组。要通过此类协议传输,包含过长且无 CRLF 序列的段的数据必须用合适的 content-transfer-encoding 进行编码。

4.1.2. 字符集(charset)参数

可以在 "text/plain" 数据的 Content-Type 字段中指定的一个关键参数是字符集。这通过 "charset" 参数来指定,例如:

Content-type: text/plain; charset=iso-8859-1

与某些其他参数值不同,charset 参数的值不区分大小写。在缺少 charset 参数时必须假定的默认字符集是 US-ASCII。

任何未来 "text" 子类型的规定都必须说明它们是否也会使用 "charset" 参数,并且可能还会限制其取值。对于 "text/plain" 之外的其他 "text" 子类型,"charset" 参数的语义应定义为与此处为 "text/plain" 指定的相同,即主体完全由给定字符集中的字符组成。特别地,未来 "text" 子类型的定义者应密切关注多八位组字符集对其子类型定义的影响。

"text" 子类型的 charset 参数给出的是一个字符集的名称,正如 RFC 2045 中"字符集"的定义。上一节详述的关于换行符的规则也必须遵守——其定义不符合这些规则的字符集不能在 MIME "text" 子类型中使用。

本节的末尾可找到一组预定义字符集名称的初步列表。额外的字符集可向 IANA 注册。

除 "text" 子类型之外的其他媒体类型也可以选择采用此处定义的 charset 参数,但要去掉 CRLF/换行符的限制。因此,所有符合 RFC 2045 中"字符集"一般定义的字符集都可以注册供 MIME 使用。

注意,如果指定的字符集包含 8 位字符,且此类字符用于主体中,则为了通过某些邮件传输协议(如 SMTP [RFC-821])传输主体,需要一个 Content-Transfer-Encoding 头字段以及数据上相应的编码。

默认字符集 US-ASCII 过去一直是一些混淆与歧义的来源。不仅其定义中存在一些歧义,实践中也存在广泛差异。为了在未来消除此类歧义与差异,强烈建议新的用户代理在 Content-Type 头字段中显式地将字符集指定为媒体类型参数。"US-ASCII" 并非指任意 7 位字符集,而是指定主体中的所有八位组都必须按照 US-ASCII 字符集解释为字符。ISO 646 [ISO-646] 的各国版本与应用导向版本通常与 US-ASCII 并不相同,在此情况下,互联网邮件中明确不鼓励使用它们。本文档刻意省略了 ISO 646 字符集,原因正在于此。"US-ASCII" 这一字符集名称明确指代 ANSI X3.4-1986 [US-ASCII] 中定义的字符集。ISO 646 的 1991 版新国际参考版本(IRV)与 US-ASCII 相同。字符集名称 "ASCII" 被保留,不得用于任何目的。

注意:RFC 821 明确规定了 "ASCII",并引用了美国标准的早期版本。鉴于指定媒体类型与字符集的目的之一,是让接收方能够无歧义地确定发送方意图如何解释被编码的报文,假设除"严格 ASCII"之外的任何默认值,都会给当前正在传输的报文语义带来无意且不兼容的改变风险。这也意味着,包含按照 US-ASCII 与 1991 IRV 之外的其他 ISO 646 版本编码的字符、或使用代码切换过程(如 ISO 2022 的那些)以及 8 位或多八位组字符编码的报文,必须使用适当的字符集规范才能与 MIME 保持一致。

完整的 US-ASCII 字符集列于 ANSI X3.4-1986。注意,控制字符(包括 DEL,值 0-31 与 127)除 CRLF 组合(US-ASCII 值 13 与 10)表示新行外没有定义的含义。其中两个字符在广泛使用中具有事实上的含义:FF(12)通常表示"后续文本从新页开头开始";而 TAB 或 HT(9)通常(尽管不总是)表示"将光标移动到当前位置之后的下一个可用列,该列号为 8 的倍数(第一列计为列 0)"。除这些约定外,在主体中对控制字符或 DEL 的任何使用必须属于以下情形:

  1. 因为 "plain" 之外的某个 text 子类型专门赋予其某些附加含义;或
  2. 在发送方与接收方之间的私下约定背景下。此类私下约定不受鼓励,应当用本文档的其他能力来取代。

注意:除 US-ASCII 外还存在数量庞大的字符集。大量部分或完全重叠的字符集并非好事。一个可普遍用于在互联网邮件中表示全世界所有语言的单一字符集将是更可取的。遗憾的是,若干群体中的现有实践似乎指向近期内继续使用多个字符集。因此,本文档为互联网使用定义了少量标准字符集。

所定义的 charset 取值为:

  1. US-ASCII——如 ANSI X3.4-1986 [US-ASCII] 中所定义。
  2. ISO-8859-X——其中 "X" 根据需要替换为 ISO-8859 [ISO-8859] 的各个部分。注意,ISO 646 字符集已被刻意省略,转而采用其 8859 替代版本,后者是互联网邮件指定的字符集。截至本文档发布之时,"X" 的合法取值为数字 1 至 10。

在 ISO-8859-X 中,值 128-159 的字符没有指定含义。ISO-8859-X 中值小于 128 的字符与它们在 US-ASCII 中具有相同的指定含义。

ISO 8859 的第 6 部分(拉丁/阿拉伯字母表)与第 8 部分(拉丁/希伯来字母表)既包含正常书写方向为从右到左的字符,也包含从左到右的字符,但并未定义表示双向文本的规范排序方法。而字符集值 "ISO-8859-6" 与 "ISO-8859-8" 指定使用视觉法(visual method)[RFC-1556]。

所有这些字符集都作为纯 7 位或 8 位集合使用,没有任何移位或转义功能。这些字符集中移位与转义序列的含义未作定义。

上述字符集是 MIME 起草过程中相对无争议的一些。本文档不认可除 US-ASCII 之外的任何特定字符集的使用,并认识到世界字符集的未来演进仍不明确。

注意,所使用的字符集若非 US-ASCII,则必须始终在 Content-Type 字段中显式指定。

除以上定义的名称外,未经正式规范的发布并向 IANA 注册,或未经私下约定(在此情形下字符集名称必须以 "X-" 开头),不得在互联网邮件中使用任何其他字符集名称。

除非绝对必要,实现者不应定义新的字符集。

"charset" 参数主要是为文本数据的目的而定义,并因此在本节中予以描述。然而,非文本数据也可能出于某种目的希望指定 charset 值,在此情形下应使用相同的语法与取值。

一般而言,组合软件应始终使用尽可能"最低公共分母"的字符集。例如,如果一个主体仅包含 US-ASCII 字符,则应将其标记为属于 US-ASCII 字符集,而不是 ISO-8859-1(它与整个 ISO-8859 系列字符集一样,是 US-ASCII 的超集)。更一般地,如果一个广泛使用的字符集是另一个字符集的子集,且主体仅包含该广泛使用的子集中的字符,则应将其标记为属于该子集。这将增加接收方能够正确查看所生成实体的机会。

4.1.3. 纯文本(plain)子类型

"text" 最简单也是最重要的子类型是 "plain"。它表示不含任何格式命令或指令的纯文本。纯文本意在"原样"显示,即为了正确显示,无需解释嵌入的格式命令、字体属性规范、处理指令、解释指令或内容标记。互联网邮件中 "text/plain; charset=us-ascii" 的默认媒体类型描述了现有的互联网实践。也就是说,它是 RFC 822 所定义的主体类型。

本文档未定义其他 "text" 子类型。

4.1.4. 未识别的子类型

只要 MIME 实现知道如何处理其 charset,未识别的 "text" 子类型就应当作为 "plain" 子类型来处理。同时指定了未识别 charset 的未识别子类型则应作为 "application/octet-stream" 处理。

4.2. 图像(image)媒体类型

媒体类型 "image" 表示主体包含一幅图像。子类型命名具体的图像格式。这些名称不区分大小写。一个初始子类型是采用 JFIF 编码 [JPEG] 的 JPEG 格式 "jpeg"。

此处给出的 "image" 子类型列表既非排他也非穷尽,预计将随更多类型按 RFC 2048 所述向 IANA 注册而增长。

未识别的 "image" 子类型至少应作为 "application/octet-stream" 处理。实现可以选择性地将它们未具体识别的 "image" 子类型传递给一个安全且健壮的通用图像查看应用程序(若此类应用程序可用)。

注意:以这种方式使用通用图像查看应用程序,会继承该应用程序所支持的最危险类型的安全问题。

4.3. 音频(audio)媒体类型

媒体类型 "audio" 表示主体包含音频数据。尽管对于适合计算机使用的"理想"音频格式尚未达成共识,但对一种能够提供互操作行为的格式存在迫切需求。

为此规定了一个初始子类型 "basic",它通过提供一个绝对最低限度的公共分母音频格式来满足该需求。预计更高质量和/或更低带宽音频的更丰富格式将由后续文档定义。

"audio/basic" 子类型的内容是以 8000 Hz 采样率、采用 8 位 ISDN mu-law [PCM] 编码的单声道音频。

未识别的 "audio" 子类型至少应作为 "application/octet-stream" 处理。实现可以选择性地将它们未具体识别的 "audio" 子类型传递给一个健壮的通用音频播放应用程序(若此类应用程序可用)。

4.4. 视频(video)媒体类型

媒体类型 "video" 表示主体包含随时间变化的图像,可能带有颜色与协调的声音。术语"video"取其最广义,而非指任何特定技术或格式,也无意排除诸如紧凑编码的动画绘制等子类型。子类型 "mpeg" 指按照 MPEG 标准 [MPEG] 编码的视频。

注意,尽管本文档总体上强烈反对在单一主体中混合多种媒体,但承认许多所谓的视频格式包含对同步音频的表示,而这对于 "video" 的子类型是明确允许的。

未识别的 "video" 子类型至少应作为 "application/octet-stream" 处理。实现可以选择性地将它们未具体识别的 "video" 子类型传递给一个健壮的通用视频显示应用程序(若此类应用程序可用)。

4.5. 应用(application)媒体类型

"application" 媒体类型用于不适合归入任何其他类别的离散数据,尤其是供某类应用程序处理的数据。这类信息必须先由应用程序处理,才能被用户查看或使用。预计 "application" 媒体类型的用途包括文件传输、电子表格、基于邮件的日程安排系统的数据,以及用于"活动"(计算型)材料的语言。(后者尤其可能带来安全问题,实现者必须理解这些问题,并在对 "application/PostScript" 媒体类型的讨论中详细考量。)

例如,一个会议日程安排器可能为提议的会议日期信息定义一种标准表示。一个智能用户代理将利用该信息与用户进行对话,并可能基于该对话发送额外材料。更一般地,已经开发了若干"活动"消息语言,其中用某种适当专门化的语言编写的程序被传送到远程位置并在接收方的环境中自动运行。

此类应用可被定义为 "application" 媒体类型的子类型。本文档定义了两个子类型:

octet-stream 与 PostScript。

"application" 的子类型通常是为其数据所面向的应用程序的名称,或包含该名称的一部分。然而,这并不意味着任何应用程序名称都可以自由用作 "application" 的子类型。

4.5.1. 八位字节流(octet-stream)子类型

"octet-stream" 子类型用于表明主体包含任意二进制数据。当前已定义的参数集合为:

  1. TYPE——二进制数据的一般类型或类别。这是供人类接收方参考的信息,而非用于任何自动处理。
  2. PADDING——为构成实际内容的位流附加、以产生所封装的面向 8 位字节的数据而填充的位数。当总位数不是 8 的倍数时,这用于将一个位流封装进主体中,十分有用。

这两个参数都是可选的。

一个名为 "CONVERSIONS" 的额外参数曾在 RFC 1341 中定义,但后来被移除。RFC 1341 还定义了使用 "NAME" 参数来给出若数据写入文件时所建议的文件名。这已被废弃,以待后续 RFC 中定义的独立 Content-Disposition 头字段。

对于收到一个 "application/octet-stream" 实体的实现,推荐的动作是简单地提议将数据放入文件(同时撤销任何 Content-Transfer-Encoding),或者或许将其用作用户指定进程的输入。

为降低传输恶意程序的危险,强烈建议实现不要实现一种路径搜索机制,即根据 Content-Type 参数(例如 "interpreter=" 参数)中命名的任意程序来查找并以报文主体为输入执行它。

4.5.2. PostScript 子类型

媒体类型 "application/postscript" 表示一个 PostScript 程序。当前允许 PostScript 语言的两种变体;原始的 1 级变体在 [POSTSCRIPT] 中描述,较新的 2 级变体在 [POSTSCRIPT2] 中描述。

PostScript 是 Adobe Systems, Inc. 的注册商标。使用 MIME 媒体类型 "application/postscript" 即意味着承认该商标及其所带来的一切权利。

PostScript 语言定义提供了对给定程序所使用的特定语言特性进行内部标记的能力。这种标记称为 PostScript 文档结构约定(PostScript document structuring conventions,DSC),非常通用,提供的信息远不止语言级别。使用文档结构约定虽然不是必需的,但作为互操作性的辅助手段被强烈推荐。缺少适当结构约定的文档无法被测试以判断其是否能在给定环境中正常工作。因此,某些系统可能会做最坏的假设并拒绝处理非结构化文档。

运行通用 PostScript 解释器带来严重的安全风险,实现者不应简单地将 PostScript 主体发送给"现成"的解释器。虽然将 PostScript 发送给打印机通常是安全的(其潜在危害被典型的打印机环境大大限制),但实现者在将 PostScript 主体的交互式显示加入其 MIME 阅读器之前,应考虑以下所有内容。

本节其余部分概述了传输 PostScript 实体可能存在的部分(尽管或许并非全部)问题。

  1. PostScript 语言中的危险操作包括但不限于 PostScript 运算符 "deletefile"、"renamefile"、"filenameforall" 与 "file"。"file" 仅在应用于标准输入或输出之外的对象时是危险的。实现也可能定义额外的非标准文件运算符;这些也可能对安全构成威胁。"filenameforall"(通配符文件搜索运算符)乍看似乎无害。然而请注意,该运算符有可能泄露接收方有权访问哪些文件的信息,而该信息本身可能就是敏感的。报文发送方应避免使用潜在危险的文件运算符,因为这些运算符在安全的 PostScript 实现中很可能不可用。报文接收与显示软件应当要么完全禁用所有潜在危险的文件运算符,要么特别小心不要向它们的操作委托任何特殊权限。这些运算符在解释 PostScript 文档时应被视为由外部机构执行。此类禁用和/或检查应当完全在 PostScript 语言自身触及范围之外进行;应注意确保不存在任何重新启用这些运算符全功能版本的方法。
  2. PostScript 语言提供了退出正常解释器或服务器循环(server loop)的设施。在此"外部"环境中所做的更改通常跨文档保留,并且在某些情况下以非易失性存储器半永久性地保留。与退出解释器循环相关的运算符有可能干扰后续文档处理。因此,它们的无限制使用构成拒绝服务的威胁。退出解释器循环的 PostScript 运算符包括但不限于 exitserver 与 startjob 运算符。报文发送软件不应生成依赖退出解释器循环才能运行的 PostScript,因为退出能力在安全的 PostScript 实现中可能不可用。报文接收与显示软件应当通过消除或禁用 "startjob" 与 "exitserver" 操作,完全禁用对 PostScript 环境做保留性更改的能力。如果这些操作无法被消除或完全禁用,则至少应将其关联口令设置为难以猜出的值。
  3. PostScript 提供用于设置系统级与设备级参数的运算符。这些参数设置可能跨作业保留,并可能对解释器的正确运行构成潜在威胁。设置系统与设备参数的 PostScript 运算符包括但不限于 "setsystemparams" 与 "setdevparams" 运算符。报文发送软件不应生成依赖设置系统或设备参数才能正确运行的 PostScript。设置这些参数的能力在安全的 PostScript 实现中很可能不可用。报文接收与显示软件应当禁用更改系统与设备参数的能力。如果这些运算符无法被完全禁用,则至少应将其关联口令设置为难以猜出的值。
  4. 某些 PostScript 实现提供用于直接加载与执行机器码的非标准设施。此类设施显然容易被大量滥用。报文发送软件不应使用此类特性。除了完全依赖于硬件之外,它们在安全的 PostScript 实现中也很可能不可用。报文接收与显示软件不应允许此类运算符(若其存在)被使用。
  5. PostScript 是一种可扩展语言,其许多(即使不是大多数)实现都提供大量自身扩展。本文档不明确处理此类扩展,因为它们构成一个未知因素。报文发送软件不应使用非标准扩展;它们很可能在一些实现中缺失。报文接收与显示软件应确保任何非标准 PostScript 运算符都是安全的,且不构成任何形式的威胁。
  6. 有可能编写消耗大量各类系统资源的 PostScript。也有可能编写无限循环的 PostScript 程序。这两类程序若发送给不知情的接收方,都有可能造成损害。报文发送软件应避免构造与传播此类程序,这是不道德的。报文接收与显示软件应提供适当机制,在经过了合理时间后中止处理。此外,PostScript 解释器对给定系统资源的消耗应被限制在合理数量内。
  7. 有可能以各种形式在 PostScript 中嵌入原始二进制信息。这不推荐用于互联网邮件,既因为它不被所有 PostScript 解释器支持,也因为它会显著复杂化 MIME Content-Transfer-Encoding 的使用。(若没有此类二进制,PostScript 通常可被视为面向行的数据。如果二进制与面向行数据混合在单个 PostScript 数据流中,CRLF 序列的处理会变得极其麻烦。)
  8. 最后,某些 PostScript 解释器中可能存在可被利用来获取对接收方系统未授权访问的缺陷。除指出这种可能性外,除了及时纠正发现的此类缺陷外,没有具体的预防措施可采。

4.5.3. 其他应用子类型

预计将来会定义许多其他 "application" 子类型。MIME 实现至少必须将任何未识别的子类型当作等同于 "application/octet-stream" 来处理。

5. 复合媒体类型取值

七个初始 Content-Type 取值中剩余的两个指向复合实体。复合实体使用 MIME 机制处理——MIME 处理器通常直接处理主体。

5.1. 多部分(multipart)媒体类型

对于多部分实体,即在一个主体中组合了一组或多组不同数据的情况,实体的头中必须出现一个 "multipart" 媒体类型字段。主体随后必须包含一个或多个主体部分(body part),每个部分之前有一个边界分隔符行(boundary delimiter line),最后一个部分之后跟一个结束边界分隔符行(closing boundary delimiter line)。在每个边界分隔符行之后,每个主体部分由头区、一个空行与一个主体区组成。因此,主体部分在语法上类似于 RFC 822 报文,但含义不同。

主体部分是一个实体,因此不应被解释为实际上就是一个 RFC 822 报文。首先,主体部分中实际上并不要求任何头字段。因此,以一个空行开始的主体部分是允许的,它是一个所有默认值都将被假定的主体部分。在此情形下,缺少 Content-Type 头通常意味着相应主体具有 "text/plain; charset=US-ASCII" 的内容类型。

对于主体部分,仅有那些名称以 "Content-" 开头的头字段具有定义的含义。所有其他头字段在主体部分中可被忽略。尽管一般应尽可能保留它们,但在必要时网关可以丢弃它们。此类其他字段允许出现在主体部分中,但不可依赖。"X-" 字段可为实验或私有目的而创建,但应认识到其所含信息可能在某个网关处丢失。

注意:RFC 822 报文与主体部分之间的区别微妙但重要。例如,互联网与 X.400 邮件之间的网关必须能够区分包含一幅图像的主体部分,与包含一个被封装报文(其主体是一幅 JPEG 图像)的主体部分。为了表示后者,主体部分必须具有 "Content-Type: message/rfc822",并且其主体(在空行之后)必须是被封装的报文,带有其自身的 "Content-Type: image/jpeg" 头字段。使用相似的语法便于将报文转换为主体部分,反之亦然,但实现者必须理解二者之间的区别。(对于部分实际就是报文的特殊情况,还定义了一个 "digest" 子类型。)

如前所述,每个主体部分之前都有一个包含边界分隔符的边界分隔符行。边界分隔符绝不可出现在任何被封装部分的内部,无论是单独成行还是作为任何行的前缀。这意味着,组合代理能够选择并指定一个唯一的边界参数值、且该值不以某个外层 multipart 的边界参数值为前缀,这一点至关重要。

"multipart" 类型的所有现存与未来子类型都必须使用相同的语法。子类型在语义上可能不同,并可能施加额外的语法限制,但必须符合 "multipart" 类型所需的语法。该要求确保所有合规的用户代理至少能够识别并分离任何 multipart 实体的各个部分,即便是未识别子类型的部分。

如 RFC 2045 中 Content-Transfer-Encoding 字段的定义,对于 "multipart" 类型的实体,除 "7bit"、"8bit" 或 "binary" 外不允许任何其他编码。"multipart" 边界分隔符与头字段在任何情况下都始终表示为 7 位 US-ASCII(尽管头字段可按照 RFC 2047 编码非 US-ASCII 头文本),而主体部分内部的数据可以按部分逐个编码,每个适当的主体部分带有 Content-Transfer-Encoding 字段。

5.1.1. 通用语法

本节为 "multipart" 的子类型定义通用语法。所有 "multipart" 子类型都必须使用此语法。本节还给出一个多部分报文的简单示例。一个更复杂的多部分报文示例在 RFC 2049 中给出。

multipart 实体的 Content-Type 字段需要一个参数 "boundary"。边界分隔符行于是定义为完全由连字符("-",十进制值 45)后跟 Content-Type 头字段中的 boundary 参数值、可选的线性空白(linear whitespace)以及一个结束的 CRLF 所组成的行。

注意:这些连字符是为了与早期的 RFC 934 报文封装方法大致兼容,并便于在某些实现中搜索边界。但应注意,多部分报文与 RFC 934 封装并不完全兼容;特别是,它们不遵守 RFC 934 对以连字符开头的嵌入行的引用约定。之所以选择此机制而非 RFC 934 机制,是因为后者会导致行随每一级引用而增长。这种增长,加上 SMTP 实现有时会折行长行,使得 RFC 934 机制在人们希望深度嵌套多部分结构时不适合使用。

对实现者的警告:Content-Type 字段上参数的文法常常要求将边界参数值用引号括在 Content-type 行中。这并非总是必要,但绝无坏处。实现者应务必仔细研究该文法,以避免产生无效的 Content-type 字段。因此,一个典型的 "multipart" Content-Type 头字段可能如下所示:

Content-Type: multipart/mixed; boundary=gc0p4Jq0M2Yt08j34c0p

但以下写法无效:

Content-Type: multipart/mixed; boundary=gc0pJq0M:08jU534c0p

(因为其中的冒号),而必须表示为:

Content-Type: multipart/mixed; boundary="gc0pJq0M:08jU534c0p"

该 Content-Type 值表明内容由一个或多个部分组成,每个部分具有与 RFC 822 报文语法相同的结构,例外在于头区允许完全为空,并且各部分之前各有一个如下行:

--gc0pJq0M:08jU534c0p

边界分隔符必须出现在一行的开头,即跟随在一个 CRLF 之后,并且初始的 CRLF 被视为附加在边界分隔符行上,而非前一部分的一部分。边界之后可跟零个或多个线性空白字符。然后它由一个 CRLF 加下一部分的头字段终止,或由两个 CRLF 终止,在后一种情形下下一部分没有头字段。若不存在 Content-Type 字段,则在 "multipart/digest" 中假定为 "message/rfc822",否则假定为 "text/plain"。

注意:边界分隔符行之前的 CRLF 在概念上附加在边界上,这样才有可能存在不以 CRLF(换行)结尾的部分。因此,必须被认为以换行结尾的主体部分,在边界分隔符行之前必须有两个 CRLF,其中第一个属于前一个主体部分,第二个属于封装边界。

边界分隔符不得出现在被封装的材料内部,且长度(不计两个前导连字符)不得超过 70 个字符。

最后一个主体部分之后的边界分隔符行是一个特殊的分隔符,表明其后不再有主体部分。该分隔符行与此前的分隔符行相同,只是在 boundary 参数值之后附加了两个连字符。

--gc0pJq0M:08jU534c0p--

对实现者的注意:边界字符串比较必须将边界值与每个候选行的开头进行比较。不要求候选整行精确匹配;只要边界在 CRLF 之后完整出现即已足够。

在第一个边界分隔符行之前与最后一个边界分隔符行之后,似乎有放置额外信息的空间。这些区域通常应留空,实现必须忽略出现在第一个边界分隔符行之前或最后一个之后的任何内容。

注意:这些"前导区"(preamble)与"尾声区"(epilogue)一般不被使用,原因是这些部分缺乏适当的类型标注,以及在网关(尤其是 X.400 网关)处处理这些区域的语义不明确。然而,许多 MIME 实现并未将前导区留空,而是发现它是向使用 MIME 之前软件阅读报文的接收方插入说明性注释的便利位置,因为此类注释会被符合 MIME 的软件忽略。

注意:由于边界分隔符不得出现在被封装的主体部分中,用户代理必须谨慎选择唯一的边界参数值。上面示例中的边界参数值可能是某种算法的结果,该算法旨在以极低的概率生成在待封装数据中已经存在的边界分隔符,而无需预扫描数据。其他算法可能产生对使用旧用户代理的接收方而言更"可读"的边界分隔符,但会更需要注意边界分隔符可能出现在被封装部分中某行开头的可能性。可能的最简单边界分隔符行类似于 "---",其结束边界分隔符行为 "-----"。

作为一个非常简单的示例,以下多部分报文有两个部分,均为纯文本,其中一个显式指定了类型,另一个隐式指定了类型:

From: Nathaniel Borenstein <nsb@bellcore.com>
To: Ned Freed <ned@innosoft.com>
Date: Sun, 21 Mar 1993 23:56:48 -0800 (PST)
Subject: Sample message
MIME-Version: 1.0
Content-type: multipart/mixed; boundary="simple boundary"

This is the preamble.  It is to be ignored, though it
is a handy place for composition agents to include an
explanatory note to non-MIME conformant readers.

--simple boundary

This is implicitly typed plain US-ASCII text.
It does NOT end with a linebreak.
--simple boundary
Content-type: text/plain; charset=us-ascii

This is explicitly typed plain US-ASCII text.
It DOES end with a linebreak.

--simple boundary--

This is the epilogue.  It is also to be ignored.

在一个 "multipart" 实体内将 "multipart" 媒体类型用于另一个 "multipart" 实体内的主体部分,是明确允许的。在此类情形下,出于显而易见的原因,必须注意确保每个嵌套的 "multipart" 实体使用不同的边界分隔符。关于嵌套 "multipart" 实体的示例见 RFC 2049。

在仅含单个主体部分的情况下使用 "multipart" 媒体类型,在某些上下文中可能很有用,并明确允许。

注意:经验表明,仅含单个主体部分的 "multipart" 媒体类型对于发送非文本媒体类型很有用。它的优点在于提供前导区作为放置解码说明的位置。此外,一些 SMTP 网关会移动或移除 MIME 头,而一个聪明的 MIME 解码器即使在没有 Content-Type 头的情况下,也能很好地猜出多部分边界并成功解码报文。

"multipart" 媒体类型唯一强制的全局参数是 boundary 参数,它由一组已知可通过邮件网关稳健传输、且不以空白结尾的 1 至 70 个字符组成。(如果某个边界分隔符行看似以空白结尾,则该空白必须被假定为由某个网关添加,并必须被删除。)其形式化定义如下 BNF:

boundary := 0*69<bchars> bcharsnospace

bchars := bcharsnospace / " "

bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
                 "+" / "_" / "," / "-" / "." /
                 "/" / ":" / "=" / "?"

总体而言,"multipart" 实体的主体可规定如下:

dash-boundary := "--" boundary
                 ; boundary taken from the value of
                 ; boundary parameter of the
                 ; Content-Type field.

multipart-body := [preamble CRLF]
                  dash-boundary transport-padding CRLF
                  body-part *encapsulation
                  close-delimiter transport-padding
                  [CRLF epilogue]

transport-padding := *LWSP-char
                     ; Composers MUST NOT generate
                     ; non-zero length transport
                     ; padding, but receivers MUST
                     ; be able to handle padding
                     ; added by message transports.

encapsulation := delimiter transport-padding
                 CRLF body-part

delimiter := CRLF dash-boundary

close-delimiter := delimiter "--"

preamble := discard-text

epilogue := discard-text

discard-text := *(*text CRLF) *text
              ; May be ignored or discarded.

body-part := MIME-part-headers [CRLF *OCTET]
             ; Lines in a body-part must not start
             ; with the specified dash-boundary and
             ; the delimiter must not appear anywhere
             ; in the body part.  Note that the
             ; semantics of a body-part differ from
             ; the semantics of a message, as
             ; described in the text.

OCTET := <any 0-255 octet value>

重要:由于此 BNF 并未规定一个结构化的头字段,因此不允许在本 BNF 所示元素之间自由插入线性空白与 RFC 822 注释。

注意:在某些传输区域内,RFC 822 的限制(如将主体限制为可打印 US-ASCII 字符)可能并未生效。(也就是说,可能存在一些传输域,它们类似于 RFC 821 所规定、RFC 822 所假定的标准互联网邮件传输,但缺少某些限制。)这些限制的放宽应被理解为在本地扩展了主体的定义,例如将 US-ASCII 范围之外的八位组包括进来,只要这些扩展得到传输支持并在 Content-Transfer-Encoding 头字段中得到充分说明。然而,在任何情况下,头(无论是报文头还是主体部分头)都不得包含 US-ASCII 字符之外的任何内容。

注意:"multipart" 类型明显缺少结构化、相互关联的主体部分这一概念。建议那些希望提供更结构化或集成化多部分消息设施的人,定义语法相同但规定了各部分之间关系的 multipart 子类型。例如,可以定义包含某个特殊部分的 multipart 子类型,该部分反过来用于指定其他部分之间的关系,大概是通过它们的 Content-ID 字段来引用。若采用此种方法,旧实现将不识别新的子类型,但会将其当作 multipart/mixed 处理,从而能够向用户显示那些被识别的部分。

5.1.2. 嵌套报文与多部分的处理

本文档后续章节定义的 "message/rfc822" 子类型,除了数据耗尽外没有其他终止条件。类似地,一个被不当截断的 "multipart" 实体可能没有终止边界标记,并且可能因邮件系统故障而在运行中浮现。

当此类实体自身被嵌入另一个 "multipart" 结构内部时,正确处理它们至关重要。因此要求 MIME 实现在任何层次的嵌套处都能识别外层边界标记。仅检查下一个预期标记或其他终止条件是不够的。

5.1.3. mixed 子类型

"multipart" 的 "mixed" 子类型用于主体部分相互独立、且需要按特定顺序捆绑的情形。任何实现不能识别的 "multipart" 子类型都必须作为 "mixed" 子类型处理。

5.1.4. alternative 子类型

"multipart/alternative" 类型在语法上与 "multipart/mixed" 相同,但语义不同。特别是,每个主体部分都是同一信息的"替代"版本。

系统应当认识到各个部分的内容是可互换的。系统应基于本地环境与引用选择"最佳"类型,某些情况下甚至通过用户交互来选择。与 "multipart/mixed" 一样,主体部分的顺序是重要的。在此情形下,各替代项按对原始内容忠实程度递增的顺序出现。一般而言,最佳选择是接收方系统本地环境所支持的类型的最后一个部分。

例如,"multipart/alternative" 可用于以如下方式发送采用花哨文本格式、且能随处轻松显示的报文:

From: Nathaniel Borenstein <nsb@bellcore.com>
To: Ned Freed <ned@innosoft.com>
Date: Mon, 22 Mar 1993 09:41:09 -0800 (PST)
Subject: Formatted text mail
MIME-Version: 1.0
Content-Type: multipart/alternative; boundary=boundary42

--boundary42
Content-Type: text/plain; charset=us-ascii

  ... plain text version of message goes here ...

--boundary42
Content-Type: text/enriched

  ... RFC 1896 text/enriched version of same message
      goes here ...

--boundary42
Content-Type: application/x-whatever

  ... fanciest version of same message goes here ...

--boundary42--

在此示例中,邮件系统理解 "application/x-whatever" 格式的用户将只看到花哨版本,而其他用户将只看到 enriched 或纯文本版本,取决于其系统的能力。

一般而言,组合 "multipart/alternative" 实体的用户代理必须按偏好递增的顺序放置主体部分,即偏好的格式放在最后。对于花哨文本,发送用户代理应当将最朴素的格式放在最前,最丰富的格式放在最后。接收用户代理应当选择并显示它们能够显示的最后一个格式。当某个替代项本身属于 "multipart" 类型并包含未识别的子部分时,用户代理可以选择显示该替代项、一个较早的替代项,或两者都显示。

注意:从实现者角度看,反转此顺序、将最朴素的替代项放在最后似乎更合理。然而,当使用非 MIME 兼容的查看器查看 "multipart/alternative" 实体时,将最朴素的替代项放在最前是尽可能友好的选择。尽管这种做法给合规的 MIME 查看器带来了一些负担,但在此情形下与较旧邮件阅读器的互操作性被认为更为重要。

某些用户代理,如果它们能识别多种格式中的不止一种,可能倾向于向用户提供选择查看哪种格式的机会。例如,当一条报文同时包含排版美观的图像版本与易于编辑的文本版本时,这就说得通。然而,最关键的是用户不应被自动显示同一数据的多个版本。要么应向用户显示最后一个被识别的版本,要么应向用户提供选择。

multipart/alternative 中 Content-ID 的语义:一个 "multipart/alternative" 实体的每个部分都表示相同的数据,但两者之间的映射未必没有信息损失。例如,将 ODA 转换为 PostScript 或纯文本时会丢失信息。建议当两部分的信息内容不同时,各部分应具有不同的 Content-ID 值。而当信息内容相同时——例如当几个 "message/external-body" 类型的部分指定了访问相同数据的不同方式时——应当使用相同的 Content-ID 字段值,以优化接收方可能存在的任何缓存机制。然而,各部分使用的 Content-ID 值不应与描述整个 "multipart/alternative" 的 Content-ID 值(若设有此类 Content-ID 字段)相同。也就是说,一个 Content-ID 值将指向 "multipart/alternative" 实体,而一个或多个其他 Content-ID 值将指向其中的部分。

5.1.5. digest 子类型

本文档定义了 "multipart" Content-Type 的一个 "digest" 子类型。该类型在语法上与 "multipart/mixed" 相同,但语义不同。特别是,在 digest 中,主体部分的默认 Content-Type 值从 "text/plain" 改为 "message/rfc822"。这样做是为了允许一种大体上与 RFC 934 兼容(引用约定除外)的、可读性更强的 digest 格式。

注意:虽然在 digest 中可以为一个主体部分指定 "message/rfc822" 之外的 Content-Type 值(例如一个包含 digest 中材料描述的 "text/plain" 部分),但实际这样做并不可取。"multipart/digest" Content-Type 意在用于发送报文集合。如果需要 "text/plain" 部分,应将其作为 "multipart/mixed" 报文的独立部分包含。

此种格式的 digest 可能类似如下:

From: Moderator-Address
To: Recipient-List
Date: Mon, 22 Mar 1994 13:34:51 +0000
Subject: Internet Digest, volume 42
MIME-Version: 1.0
Content-Type: multipart/mixed;
              boundary="---- main boundary ----"

------ main boundary ----

  ...Introductory text or table of contents...

------ main boundary ----
Content-Type: multipart/digest;
              boundary="---- next message ----"

------ next message ----

From: someone-else
Date: Fri, 26 Mar 1993 11:13:32 +0200
Subject: my opinion

  ...body goes here ...

------ next message ----

From: someone-else-again
Date: Fri, 26 Mar 1993 10:07:13 -0500
Subject: my different opinion

  ... another body goes here ...

------ next message ------

------ main boundary ------

5.1.6. parallel 子类型

本文档定义了 "multipart" Content-Type 的一个 "parallel" 子类型。该类型在语法上与 "multipart/mixed" 相同,但语义不同。特别是,在 parallel 实体中,主体部分的顺序无关紧要。

该类型的一种常见呈现方式是在能够这样做的硬件与软件上同时显示所有部分。然而,组合代理应意识到许多邮件阅读器将缺少此能力,并将无论如何串行地显示各部分。

5.1.7. 其他 multipart 子类型

预计未来会出现其他 "multipart" 子类型。MIME 实现通常必须将未识别的 "multipart" 子类型当作等同于 "multipart/mixed" 来处理。

5.2. 报文(message)媒体类型

在发送邮件时,封装另一封邮件常常是可取的。为此定义了一个特殊媒体类型 "message"。特别是,"message" 的 "rfc822" 子类型用于封装 RFC 822 报文。

注意:曾有人建议为转发或被拒报文定义 "message" 的子类型。然而,转发与被拒报文可以作为多部分报文处理,其中第一部分包含任何控制或描述信息,第二部分(类型为 "message/rfc822")是被转发或被拒的报文。以这种方式组合拒绝与转发报文将保留原始报文的类型信息,并允许其被正确呈现给接收方,因此被强烈鼓励。

"message" 的子类型常常对允许的编码施加限制。这些限制随每个具体子类型一并描述。

邮件网关、中继与其他邮件处理代理通常会改变 RFC 822 报文的顶层头。特别是,它们经常添加、移除或重排头字段。对于嵌入在 "message" 类型报文主体中的被封装头,这些操作被明确禁止。

5.2.1. rfc822 子类型

媒体类型 "message/rfc822" 表示主体包含一个具有 RFC 822 报文语法的被封装报文。然而,与顶层 RFC 822 报文不同,去除了每个 "message/rfc822" 主体必须包含 "From"、"Date" 以及至少一个目的地头的要求,代之以要求至少存在 "From"、"Subject" 或 "Date" 之一。

应注意,尽管使用了数字"822","message/rfc822" 实体并不限于严格符合 RFC822 的材料,并且 "message/rfc822" 对象的语义也不限于 RFC822 中定义的语义。更具体地说,"message/rfc822" 报文很可能是一篇新闻文章或一个 MIME 报文。

对于 "message/rfc822" 实体的主体,除 "7bit"、"8bit" 或 "binary" 外不允许其他编码。报文头字段在任何情况下始终是 US-ASCII,而主体内部的数据仍可被编码,在此情形下被封装报文中的 Content-Transfer-Encoding 头字段将反映这一点。被封装报文头中的非 US-ASCII 文本可使用 RFC 2047 描述的机制来指定。

5.2.2. partial 子类型

"partial" 子类型用于允许将大型实体作为若干独立的邮件投递,并由接收用户代理自动重组。(该概念类似于基本互联网协议中的 IP 分片与重组。)当中间传输代理限制可发送的单个报文大小时,可使用此机制。因此媒体类型 "message/partial" 表示主体包含某个更大实体的一个片段。

由于 "message" 类型的数据绝不能以 base64 或 quoted-printable 编码,若 "message/partial" 实体是在支持 binary 或 8bit 传输的环境中构造的,就可能出现问题。问题在于二进制数据会被拆分成多个 "message/partial" 报文,而每个都需要 binary 传输。如果此类报文在某个进入 7bit 传输环境的网关处遇到,除了等待所有片段、重组内部报文、然后将重组后的数据以 base64 或 quoted-printable 编码外,将没有办法为 7bit 世界正确编码它们。由于不同片段可能经过不同网关,即便这样也非可接受的解决方案。因此规定,类型为 "message/partial" 的实体必须始终具有 "7bit"(默认值)的 content-transfer-encoding。特别是,即使在支持 binary 或 8bit 传输的环境中,也明确禁止对 "message/partial" 类型的 MIME 实体使用 "8bit" 或 "binary" 的 content-transfer-encoding。这转而意味着内部报文不得使用 "8bit" 或 "binary" 编码。

由于某些报文传输代理可能选择自动分片大型报文,并且此类代理可能使用非常不同的分片阈值,一个部分报文在重组后其各部分可能自身又构成一个部分报文。这是明确允许的。

在 "message/partial" 类型的 Content-Type 字段中必须指定三个参数:第一个 "id" 是一个唯一标识符,尽可能接近世界唯一的标识符,用于将片段匹配在一起。(一般而言,该标识符本质上是一个 message-id;若置于双引号中,则它可以是任意 message-id,符合 RFC 2045 中 "parameter" 的 BNF。)第二个 "number" 是一个整数,即片段编号,指示此片段在片段序列中的位置。第三个 "total" 是另一个整数,即片段总数。第三个子字段在最后一个片段上是必需的,在前面的片段上是可选(尽管鼓励)的。还应注意,这些参数可按任意顺序给出。

因此,一个 3 片段报文的第二个片段可能具有如下两个头字段之一:

Content-Type: Message/Partial; number=2; total=3;
              id="oc=jpbe0M2Yt4s@thumper.bellcore.com"

Content-Type: Message/Partial;
              id="oc=jpbe0M2Yt4s@thumper.bellcore.com";
              number=2

但第三个片段必须指定片段总数:

Content-Type: Message/Partial; number=3; total=3;
              id="oc=jpbe0M2Yt4s@thumper.bellcore.com"

注意,片段编号从 1 开始,而非 0。

当以此方式拆分的实体的各个片段被组合在一起时,结果总是一个完整的 MIME 实体,它可能有其自身的 Content-Type 头字段,从而可包含任何其他数据类型。

5.2.2.1. 报文分片与重组

重组后的部分报文的语义必须是"内部"报文的语义,而不是包含该内部报文的报文的语义。这就使得例如可以将一个大型音频报文作为几个部分报文发送,而对接收方仍呈现为一个简单的音频报文,而非一个包含音频报文的被封装报文。也就是说,报文的封装被认为是"透明的"。

在生成与重组一个 "message/partial" 报文的各个片段时,被封装报文的头必须与外层实体的头合并。在此过程中必须遵守以下规则:

  1. 分片代理只能在行边界处拆分报文。施加此限制是因为,在非行尾处拆分又转而依赖于报文传输能够保留不以 CRLF 序列结尾的报文的语义。许多传输无法保留此类语义。
  2. 初始外层报文的所有头字段,除那些以 "Content-" 开头的字段以及特定的 "Subject"、"Message-ID"、"Encrypted" 与 "MIME-Version" 头字段外,必须按顺序复制到新报文中。
  3. 被封装报文中以 "Content-" 开头的头字段,加上 "Subject"、"Message-ID"、"Encrypted" 与 "MIME-Version" 字段,必须按顺序追加到新报文的头字段之后。被封装报文中任何不以 "Content-" 开头的头字段("Subject"、"Message-ID"、"Encrypted" 与 "MIME-Version" 字段除外)将被忽略并丢弃。
  4. 来自第二个及任何后续外层报文的所有头字段都被重组过程丢弃。
5.2.2.2. 分片与重组示例

如果一个音频报文被拆成两片,第一片可能类似如下:

X-Weird-Header-1: Foo
From: Bill@host.com
To: joe@otherhost.com
Date: Fri, 26 Mar 1993 12:59:38 -0500 (EST)
Subject: Audio mail (part 1 of 2)
Message-ID: <id1@host.com>
MIME-Version: 1.0
Content-type: message/partial; id="ABC@host.com";
              number=1; total=2

X-Weird-Header-1: Bar
X-Weird-Header-2: Hello
Message-ID: <anotherid@foo.com>
Subject: Audio mail
MIME-Version: 1.0
Content-type: audio/basic
Content-transfer-encoding: base64

  ... first half of encoded audio data goes here ...

而第二片可能类似如下:

From: Bill@host.com
To: joe@otherhost.com
Date: Fri, 26 Mar 1993 12:59:38 -0500 (EST)
Subject: Audio mail (part 2 of 2)
MIME-Version: 1.0
Message-ID: <id2@host.com>
Content-type: message/partial;
              id="ABC@host.com"; number=2; total=2

  ... second half of encoded audio data goes here ...

然后,当被分片的报文被重组时,呈现给用户的所得报文应类似如下:

X-Weird-Header-1: Foo
From: Bill@host.com
To: joe@otherhost.com
Date: Fri, 26 Mar 1993 12:59:38 -0500 (EST)
Subject: Audio mail
Message-ID: <anotherid@foo.com>
MIME-Version: 1.0
Content-type: audio/basic
Content-transfer-encoding: base64

  ... first half of encoded audio data goes here ...
  ... second half of encoded audio data goes here ...

在部分报文第二片及后续片的头中包含一个引用前一片 Message-Id 的 "References" 字段,可能对理解并跟踪引用的邮件阅读器有益。然而,生成此类 "References" 字段完全是可选的。

最后,应注意 "Encrypted" 头字段已被隐私增强邮件(PEM)[RFC-1421、RFC-1422、RFC-1423、RFC-1424] 废弃,但上述规则仍被认为描述了在转换为 "message/partial" 片段及从其转换时若遇到它所应采用的正确处理方式。

5.2.3. external-body 子类型

external-body 子类型表示实际的主体数据并未包含在内,而仅仅是被引用。在此情形下,参数描述了一个访问外部数据的机制。

当一个 MIME 实体类型为 "message/external-body" 时,它由头、两个连续的 CRLF,以及被封装报文的报文头组成。如果出现另一对连续的 CRLF,这当然会结束被封装报文的报文头。然而,由于被封装报文的主体本身在外部,它并不出现在随后的区域中。例如,考虑以下报文:

Content-type: message/external-body;
              access-type=local-file;
              name="/u/nsb/Me.jpeg"

Content-type: image/jpeg
Content-ID: <id42@guppylake.bellcore.com>
Content-Transfer-Encoding: binary

THIS IS NOT REALLY THE BODY!

末尾的区域(可称为"幻影主体" phantom body)对大多数 external-body 报文被忽略。然而,它可用于包含某些此类报文的辅助信息,当 access-type 为 "mail-server" 时确实如此。本文档中唯一使用幻影主体的 access-type 是 "mail-server",但未来可能在其他规范中定义使用此区域的其他 access-type。

所有 "message/external-body" 实体中的被封装头必须包含一个 Content-ID 头字段,以给出一个用于引用该数据的唯一标识符。该标识符可用于缓存机制,以及在 access-type 为 "mail-server" 时识别数据的接收。

注意,如此处所规定,描述 external-body 数据的记号(如文件名与邮件服务器命令)要求处于 US-ASCII 字符集中。

如果这在实践中被证明有问题,可能需要一种新的机制作为 MIME 的未来扩展,无论是新定义的 "message/external-body" access-type,还是通过其他某种机制。

与 "message/partial" 一样,类型为 "message/external-body" 的 MIME 实体必须具有 "7bit"(默认值)的 content-transfer-encoding。特别是,即使在支持 binary 或 8bit 传输的环境中,也明确禁止对 "message/external-body" 类型的实体使用 "8bit" 或 "binary" 的 content-transfer-encoding。

5.2.3.1. 通用 external-body 参数

可与任何 "message/external-body" 一起使用的参数为:

  1. ACCESS-TYPE——一个指明可用于获取文件或数据的受支持访问机制的词。该词不区分大小写。取值包括但不限于 "FTP"、"ANON-FTP"、"TFTP"、"LOCAL-FILE" 与 "MAIL-SERVER"。未来的取值(以 "X-" 开头的实验性取值除外)必须按 RFC 2048 所述向 IANA 注册。该参数无条件强制,必须出现在每个 "message/external-body" 上。
  2. EXPIRATION——日期(采用 RFC 822 的 "date-time" 语法,如 RFC 1123 所扩展,允许年份字段为 4 位数字),超过该日期后外部数据的存在不再得到保证。该参数可与任何 access-type 一起使用,且始终可选。
  3. SIZE——数据的大小(以八位组计)。该参数的意图是帮助接收方决定是否要耗费必要资源去检索外部数据。注意,这描述的是数据在其规范形式下的大小,即在任何 Content-Transfer-Encoding 应用之前或数据被解码之后的大小。该参数可与任何 access-type 一起使用,且始终可选。
  4. PERMISSION——一个不区分大小写的字段,指示是否预期客户端也可能尝试覆盖该数据。默认情况下,或者若 permission 为 "read",则假设它们不会,并且一旦数据被检索一次就再也不需要。若 PERMISSION 为 "read-write",该假设不成立,任何本地副本必须仅被视为缓存。"Read" 与 "Read-write" 是 permission 仅有的两个定义取值。该参数可与任何 access-type 一起使用,且始终可选。

此处定义的各 access-type 的精确语义在后续小节中描述。

5.2.3.2. "ftp" 与 "tftp" 访问类型

access-type 为 FTP 或 TFTP 表示报文主体可作为文件通过 FTP [RFC-959] 或 TFTP [RFC-783] 协议访问。对于这些 access-type,以下附加参数是强制的:

  1. NAME——包含实际主体数据的文件名。
  2. SITE——可从中使用给定协议获取该文件的机器。这必须是一个完全限定的域名,而非昵称。
  3. 在使用 FTP 检索任何数据之前,通常需请求用户提供该 site 参数所命名机器的登录标识与口令。出于安全原因,此类标识与口令不作为 content-type 参数指定,而必须从用户处获取。

此外,以下参数是可选的:

  1. DIRECTORY——应从中检索由 NAME 命名的数据的目录。
  2. MODE——一个不区分大小写的字符串,指示检索信息时要使用的模式。access-type "TFTP" 的有效取值为 "NETASCII"、"OCTET" 与 "MAIL",如 TFTP 协议 [RFC-783] 所规定。access-type "FTP" 的有效取值为 "ASCII"、"EBCDIC"、"IMAGE" 与 "LOCALn"(其中 "n" 为十进制整数,通常为 8)。它们对应于 FTP 协议 [RFC-959] 所规定的表示类型 "A"、"E"、"I" 与 "L n"。注意,"BINARY" 与 "TENEX" 不是 MODE 的有效取值,应改用 "OCTET"、"IMAGE" 或 "LOCAL8"。若未指定 MODE,则 TFTP 的默认值为 "NETASCII",否则为 "ASCII"。
5.2.3.3. "anon-ftp" 访问类型

"anon-ftp" 访问类型与 "ftp" 访问类型相同,只是无需请求用户提供该指定站点的名称与口令。相反,将使用登录名 "anonymous" 以及与用户邮件地址对应的口令来使用 ftp 协议。

5.2.3.4. "local-file" 访问类型

access-type 为 "local-file" 表示实际主体可作为本地机器上的文件访问。为此访问类型定义了两个附加参数:

  1. NAME——包含实际主体数据的文件名。该参数对 "local-file" 访问类型是强制的。
  2. SITE——对已知能够访问该数据文件的一台机器或一组机器的域说明符。该可选参数用于描述数据的引用局部性,即预期该文件可见的站点或站点集合。星号可用于对域名的一部分进行通配匹配,如 "*.bellcore.com",以指示一组数据应直接可见的机器,而单个星号可用于指示预期普遍可用(例如通过全局文件系统)的文件。
5.2.3.5. "mail-server" 访问类型

"mail-server" 访问类型表示实际主体可从邮件服务器获取。为此访问类型定义了两个附加参数:

  1. SERVER——可从中获取实际主体数据的邮件服务器的 addr-spec。该参数对 "mail-server" 访问类型是强制的。
  2. SUBJECT——在发送给邮件服务器以获取数据的邮件中所要使用的主题。注意,基于 Subject 行来引导邮件服务器并不推荐,但已知存在此类邮件服务器。这是一个可选参数。

由于邮件服务器接受多种语法(其中一些是多行的),要发送给邮件服务器的完整命令并未作为 content-type 头字段中的参数包含。相反,当媒体类型为 "message/external-body" 且 access-type 为 mail-server 时,它作为"幻影主体"提供。

注意,MIME 并未定义邮件服务器语法。相反,它允许在幻影主体中包含任意邮件服务器命令。实现必须将幻影主体包含在它发送给邮件服务器地址以检索相关数据的报文主体中。

与其他访问类型不同,mail-server 访问是异步的,将在未来不可预测的时间发生。因此,必须存在一种机制,使返回的数据能够与原 "message/external-body" 实体相匹配。MIME 邮件服务器必须在返回报文上使用与原 "message/external-body" 实体相同的 Content-ID 字段,以方便此类匹配。

5.2.3.6. external-body 安全问题

"message/external-body" 实体引发两个重要的安全问题:

  1. 通过 "message/external-body" 引用访问数据,实际上导致报文接收方执行了一个由报文发起方指定的操作。因此报文发起方有可能诱使接收方去做他们原本不会做的事。例如,发起方可以指定一个试图检索接收方无权获取的材料的动作,使接收方在不知情的情况下违反某些安全策略。为此,能够解析外部引用的用户代理必须始终采取步骤,向接收方描述它们将要采取的动作,并在执行之前请求明确许可。
  2. 对于提供某种报文完整性与真实性保证的环境,MIME 有时会被使用。若存在此类保证,它们可能仅适用于报文实际的直接内容——它们可能适用、也可能不适用于通过 MIME 的 "message/external-body" 机制访问的数据。特别是,即使消息系统本身是安全的,也有可能破坏某些访问机制。

应注意,无论是否借助 MIME 机制,此问题都存在。在安全报文文本中随便引用一个包含某文档的 FTP 站点也会带来类似问题——唯一的区别是 MIME 提供了对此类材料的自动检索,而用户可能对这种自动检索机制给予不应有的信任。

5.2.3.7. 示例与进一步说明

当 external-body 机制与 "multipart/alternative" 媒体类型结合使用时,它将 "multipart/alternative" 的功能扩展到包含以下情形:同一实体以相同格式但通过不同访问机制提供。这样做时,报文发起方必须首先将各部分按偏好格式排序,然后按偏好访问机制排序。接收方的查看器随后应同时就格式与访问机制两方面评估该列表。

随着极广域文件系统出现的可能性,很难预先知道一组机器中文件将可直接从文件系统访问、或在何处不可访问。因此,提供既可直接尝试、又给出已知可访问该文件的一个或多个站点名称的文件名,可能是有意义的。一个实现可以尝试使用 FTP 或任何其他协议检索远程文件,使用匿名文件检索或提示用户提供必要的名称与口令。如果一个外部主体可通过多种机制访问,发送方可以在一个外层 "multipart/alternative" 实体的主体部分中包含多个 "message/external-body" 类型的实体。

然而,external-body 机制并不局限于文件检索,mail-server 访问类型即说明了这一点。除此之外,还可以设想例如使用视频服务器来引用视频片段。

出现在 "message/external-body" 数据主体中的被嵌入报文头字段必须用于声明外部主体的媒体类型(若其非纯 US-ASCII 文本),因为外部主体没有用于声明其类型的头区。类似地,除 "7bit" 外的任何 Content-transfer-encoding 也必须在此声明。因此一个完整的、引用 PostScript 格式对象的 "message/external-body" 报文可能类似如下:

From: Whomever
To: Someone
Date: Whenever
Subject: whatever
MIME-Version: 1.0
Message-ID: <id1@host.com>
Content-Type: multipart/alternative; boundary=42
Content-ID: <id001@guppylake.bellcore.com>

--42
Content-Type: message/external-body; name="BodyFormats.ps";
              site="thumper.bellcore.com"; mode="image";
              access-type=ANON-FTP; directory="pub";
              expiration="Fri, 14 Jun 1991 19:13:14 -0400 (EDT)"

Content-type: application/postscript
Content-ID: <id42@guppylake.bellcore.com>

--42
Content-Type: message/external-body; access-type=local-file;
              name="/u/nsb/writing/rfcs/RFC-MIME.ps";
              site="thumper.bellcore.com";
              expiration="Fri, 14 Jun 1991 19:13:14 -0400 (EDT)"

Content-type: application/postscript
Content-ID: <id42@guppylake.bellcore.com>

--42
Content-Type: message/external-body;
              access-type=mail-server
              server="listserv@bogus.bitnet";
              expiration="Fri, 14 Jun 1991 19:13:14 -0400 (EDT)"

Content-type: application/postscript
Content-ID: <id42@guppylake.bellcore.com>

get RFC-MIME.DOC

--42--

注意,在上述示例中,外部 postscript 数据的默认 Content-transfer-encoding "7bit" 被假定。

与 "message/partial" 类型一样,"message/external-body" 媒体类型意在是透明的,即传达外部主体中的数据类型,而非传达一个具有该类型主体的报文。因此外层与内层部分的头必须使用与 "message/partial" 相同的规则合并。特别是,这意味着 Content-type 与 Subject 字段被覆盖,但 From 字段被保留。

注意,由于外部主体不与外部主体引用一同传输,它们无需符合适用于引用本身的传输限制。特别是,互联网邮件传输可能施加 7bit 与行长度限制,但这些并不自动适用于二进制外部主体引用。因此 Content-Transfer-Encoding 一般不是必需的,尽管它是允许的。

注意,类型为 "message/external-body" 的报文主体受 RFC 822 报文的基语法支配。特别是,第一个连续 CRLF 对之前的任何内容都是头信息,而其后的任何内容都是主体信息,对大多数访问类型而言被忽略。

5.2.4. 其他 message 子类型

MIME 实现通常必须将未识别的 "message" 子类型当作等同于 "application/octet-stream" 来处理。

打算用于电子邮件的 "message" 未来子类型应限制为 "7bit" 编码。若无法限制为 "7bit",则应使用 "message" 之外的类型。

6. 实验性媒体类型取值

以字符 "X-" 开头的媒体类型取值是私有取值,供达成一致的系统在相互同意下使用。任何没有严格且公开定义的格式都必须用 "X-" 前缀命名,而公开规定的取值绝不得以 "X-" 开头。(广泛使用的 Andrew 系统的旧版本使用 "X-BE2" 名称,因此新系统可能应选择不同的名称。)

一般而言,强烈不鼓励使用 "X-" 顶层类型。实现者应在可能时发明现有类型的子类型。在许多情况下,"application" 的子类型会比新的顶层类型更合适。

7. 摘要

五个离散媒体类型为将实体标记为 "audio"、"image" 或其他几种数据提供了标准化机制。复合的 "multipart" 与 "message" 媒体类型允许在单一报文中混合与分层结构化不同类型的实体。一种特殊的参数语法允许进一步规定数据格式细节,特别是规定替代字符集。额外的可选头字段为许多实现者认为可取的某些扩展提供了机制。最后,定义了一些有用的媒体类型供达成一致的客户端代理普遍使用,尤其是 "message/partial" 与 "message/external-body"。

9. 安全考量

安全问题在 "application/postscript" 类型、"message/external-body" 类型的上下文中,以及 RFC 2048 中进行了讨论。实现者应特别注意任何可能在接收方环境中导致远程执行任何动作的媒体类型的安全影响。在此类情形下,对 "application/postscript" 类型的讨论可作为考量其他具有远程执行能力的媒体类型的范本。

9. 作者地址

如需更多信息,最好通过互联网邮件联系本文档的作者:

Ned Freed
Innosoft International, Inc.
1050 East Garvey Avenue South
West Covina, CA 91790
USA
电话:+1 818 919 3600
传真:+1 818 919 3614
电子邮件:ned@innosoft.com

Nathaniel S. Borenstein
First Virtual Holdings
25 Washington Avenue
Morristown, NJ 07960
USA
电话:+1 201 540 8967
传真:+1 201 993 3032
电子邮件:nsb@nsb.fv.com

MIME 是互联网工程任务组(IETF)RFC 822 扩展工作组工作的成果。该组主席 Greg Vaudreuil 可通过以下方式联系:

Gregory M. Vaudreuil
Octel Network Services
17080 Dallas Parkway
Dallas, TX 75248-1905
USA
电子邮件:Greg.Vaudreuil@Octel.Com

附录 A. 收集文法

本附录包含本文档规定的所有语法的完整 BNF 文法。

然而,仅凭本文档,此文法并不完整。它通过名称引用了若干由 RFC 822 定义的语法规则。本文档不在此重复那些定义(以免无意间造成两者之间的差异),而是指引读者到 RFC 822 查阅其余定义。凡术语未定义之处,均指 RFC 822 的定义。

boundary := 0*69<bchars> bcharsnospace

bchars := bcharsnospace / " "

bcharsnospace := DIGIT / ALPHA / "'" / "(" / ")" /
                 "+" / "_" / "," / "-" / "." /
                 "/" / ":" / "=" / "?"

body-part := <"message" as defined in RFC 822, with all
              header fields optional, not starting with the
              specified dash-boundary, and with the
              delimiter not occurring anywhere in the
              body part.  Note that the semantics of a
              part differ from the semantics of a message,
              as described in the text.>

close-delimiter := delimiter "--"

dash-boundary := "--" boundary
                 ; boundary taken from the value of
                 ; boundary parameter of the
                 ; Content-Type field.

delimiter := CRLF dash-boundary

discard-text := *(*text CRLF)
                ; May be ignored or discarded.

encapsulation := delimiter transport-padding
                 CRLF body-part

epilogue := discard-text

multipart-body := [preamble CRLF]
                 dash-boundary transport-padding CRLF
                 body-part *encapsulation
                 close-delimiter transport-padding
                 [CRLF epilogue]

preamble := discard-text

transport-padding := *LWSP-char
                    ; Composers MUST NOT generate
                    ; non-zero length transport
                    ; padding, but receivers MUST
                    ; be able to handle padding
                    ; added by message transports.