9. 正式语法
RFC 3501 IMAPv4 March 2003
-
IMAP4rev1 连接示例以下是一个 IMAP4rev1 连接的交互记录. 为便于编辑阅读, 本示例中的长行被折行.
S: * OK IMAP4rev1 Service Ready
C: a001 login mrc secret
S: a001 OK LOGIN completed
C: a002 select inbox
S: * 18 EXISTS
S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
S: * 2 RECENT
S: * OK [UNSEEN 17] Message 17 is the first unseen message
S: * OK [UIDVALIDITY 3857529045] UIDs valid
S: a002 OK [READ-WRITE] SELECT completed
C: a003 fetch 12 full
S: * 12 FETCH (FLAGS (\Seen) INTERNALDATE "17-Jul-1996 02:44:25 -0700"
RFC822.SIZE 4286 ENVELOPE ("Wed, 17 Jul 1996 02:23:25 -0700 (PDT)"
"IMAP4rev1 WG mtg summary and minutes"
(("Terry Gray" NIL "gray" "cac.washington.edu"))
(("Terry Gray" NIL "gray" "cac.washington.edu"))
(("Terry Gray" NIL "gray" "cac.washington.edu"))
((NIL NIL "imap" "cac.washington.edu"))
((NIL NIL "minutes" "CNRI.Reston.VA.US")
("John Klensin" NIL "KLENSIN" "MIT.EDU")) NIL NIL
"<[email protected]>")
BODY ("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 3028
92))
S: a003 OK FETCH completed
C: a004 fetch 12 body[header]
S: * 12 FETCH (BODY[HEADER] \{342\}
S: Date: Wed, 17 Jul 1996 02:23:25 -0700 (PDT)
S: From: Terry Gray <[email protected]>
S: Subject: IMAP4rev1 WG mtg summary and minutes
S: To: [email protected]
S: cc: [email protected], John Klensin <[email protected]>
S: Message-Id: <[email protected]>
S: MIME-Version: 1.0
S: Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
S:
S: )
S: a004 OK FETCH completed
C: a005 store 12 +flags \deleted
S: * 12 FETCH (FLAGS (\Seen \Deleted))
S: a005 OK +FLAGS completed
C: a006 logout
S: * BYE IMAP4rev1 server terminating connection
S: a006 OK LOGOUT completed
RFC 3501 IMAPv4 March 2003
-
正式语法以下语法规范使用 [ABNF] 中规定的扩展巴科斯-瑙尔范式 (Augmented Backus-Naur Form, ABNF) 表示法.
对于备选规则或可选规则, 如果后面的规则与前面的规则重叠, 则较早列出的规则 MUST 优先. 例如, "\Seen" 在作为 flag 解析时是 \Seen 标志名, 而不是 flag-extension, 即使 "\Seen" 也可以解析为 flag-extension. 下面标注了此规则的一些实例, 但并非全部实例.
注意: [ABNF] 规则 MUST 被严格遵守; 特别是:
(1) 除非另有说明, 所有字母字符均不区分大小写. 使用大写或小写字符定义 token 字符串仅为编辑清晰性. 实现 MUST 以不区分大小写的方式接受这些字符串.
(2) 在所有情况下, SP 均指恰好一个空格. 不允许用 TAB 替代, 插入额外空格, 或以其他方式将 SP 视为等同于 LWSP.
(3) ASCII NUL 字符 %x00 在任何时候都 MUST NOT 使用.
address = "(" addr-name SP addr-adl SP addr-mailbox SP addr-host ")"
addr-adl = nstring ; Holds route from [RFC-2822] route-addr if ; non-NIL
addr-host = nstring ; NIL indicates [RFC-2822] group syntax. ; Otherwise, holds [RFC-2822] domain name
addr-mailbox = nstring ; NIL indicates end of [RFC-2822] group; if ; non-NIL and addr-host is NIL, holds ; [RFC-2822] group name. ; Otherwise, holds [RFC-2822] local-part ; after removing [RFC-2822] quoting
RFC 3501 IMAPv4 March 2003
addr-name = nstring ; If non-NIL, holds phrase from [RFC-2822] ; mailbox after removing [RFC-2822] quoting
append = "APPEND" SP mailbox [SP flag-list] [SP date-time] SP literal
astring = 1*ASTRING-CHAR / string
ASTRING-CHAR = ATOM-CHAR / resp-specials
atom = 1*ATOM-CHAR
ATOM-CHAR = <any CHAR except atom-specials>
atom-specials = "(" / ")" / "\{" / SP / CTL / list-wildcards / quoted-specials / resp-specials
authenticate = "AUTHENTICATE" SP auth-type *(CRLF base64)
auth-type = atom ; Defined by [SASL]
base64 = *(4base64-char) [base64-terminal]
base64-char = ALPHA / DIGIT / "+" / "/" ; Case-sensitive
base64-terminal = (2base64-char "==") / (3base64-char "=")
body = "(" (body-type-1part / body-type-mpart) ")"
body-extension = nstring / number / "(" body-extension *(SP body-extension) ")" ; Future expansion. Client implementations ; MUST accept body-extension fields. Server ; implementations MUST NOT generate ; body-extension fields except as defined by ; future standard or standards-track ; revisions of this specification.
body-ext-1part = body-fld-md5 [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]] ; MUST NOT be returned on non-extensible ; "BODY" fetch
RFC 3501 IMAPv4 March 2003
body-ext-mpart = body-fld-param [SP body-fld-dsp [SP body-fld-lang [SP body-fld-loc *(SP body-extension)]]] ; MUST NOT be returned on non-extensible ; "BODY" fetch
body-fields = body-fld-param SP body-fld-id SP body-fld-desc SP body-fld-enc SP body-fld-octets
body-fld-desc = nstring
body-fld-dsp = "(" string SP body-fld-param ")" / nil
body-fld-enc = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/ "QUOTED-PRINTABLE") DQUOTE) / string
body-fld-id = nstring
body-fld-lang = nstring / "(" string *(SP string) ")"
body-fld-loc = nstring
body-fld-lines = number
body-fld-md5 = nstring
body-fld-octets = number
body-fld-param = "(" string SP string *(SP string SP string) ")" / nil
body-type-1part = (body-type-basic / body-type-msg / body-type-text) [SP body-ext-1part]
body-type-basic = media-basic SP body-fields ; MESSAGE subtype MUST NOT be "RFC822"
body-type-mpart = 1*body SP media-subtype [SP body-ext-mpart]
body-type-msg = media-message SP body-fields SP envelope SP body SP body-fld-lines
body-type-text = media-text SP body-fields SP body-fld-lines
capability = ("AUTH=" auth-type) / atom ; New capabilities MUST begin with "X" or be ; registered with IANA as standard or ; standards-track
RFC 3501 IMAPv4 March 2003
capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1" *(SP capability) ; Servers MUST implement the STARTTLS, AUTH=PLAIN, ; and LOGINDISABLED capabilities ; Servers which offer RFC 1730 compatibility MUST ; list "IMAP4" as the first capability.
CHAR8 = %x01-ff ; any OCTET except NUL, %x00
command = tag SP (command-any / command-auth / command-nonauth / command-select) CRLF ; Modal based on state
command-any = "CAPABILITY" / "LOGOUT" / "NOOP" / x-command ; Valid in all states
command-auth = append / create / delete / examine / list / lsub / rename / select / status / subscribe / unsubscribe ; Valid only in Authenticated or Selected state
command-nonauth = login / authenticate / "STARTTLS" ; Valid only when in Not Authenticated state
command-select = "CHECK" / "CLOSE" / "EXPUNGE" / copy / fetch / store / uid / search ; Valid only when in Selected state
continue-req = "+" SP (resp-text / base64) CRLF
copy = "COPY" SP sequence-set SP mailbox
create = "CREATE" SP mailbox ; Use of INBOX gives a NO error
date = date-text / DQUOTE date-text DQUOTE
date-day = 1*2DIGIT ; Day of month
date-day-fixed = (SP DIGIT) / 2DIGIT ; Fixed-format version of date-day
date-month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec"
date-text = date-day "-" date-month "-" date-year
RFC 3501 IMAPv4 March 2003
date-year = 4DIGIT
date-time = DQUOTE date-day-fixed "-" date-month "-" date-year SP time SP zone DQUOTE
delete = "DELETE" SP mailbox ; Use of INBOX gives a NO error
digit-nz = %x31-39 ; 1-9
envelope = "(" env-date SP env-subject SP env-from SP env-sender SP env-reply-to SP env-to SP env-cc SP env-bcc SP env-in-reply-to SP env-message-id ")"
env-bcc = "(" 1*address ")" / nil
env-cc = "(" 1*address ")" / nil
env-date = nstring
env-from = "(" 1*address ")" / nil
env-in-reply-to = nstring
env-message-id = nstring
env-reply-to = "(" 1*address ")" / nil
env-sender = "(" 1*address ")" / nil
env-subject = nstring
env-to = "(" 1*address ")" / nil
examine = "EXAMINE" SP mailbox
fetch = "FETCH" SP sequence-set SP ("ALL" / "FULL" / "FAST" / fetch-att / "(" fetch-att *(SP fetch-att) ")")
fetch-att = "ENVELOPE" / "FLAGS" / "INTERNALDATE" /
"RFC822" [".HEADER" / ".SIZE" / ".TEXT"] /
"BODY" ["STRUCTURE"] / "UID" /
"BODY" section ["<" number "." nz-number ">"] /
"BODY.PEEK" section ["<" number "." nz-number ">"]
RFC 3501 IMAPv4 March 2003
flag = "\Answered" / "\Flagged" / "\Deleted" / "\Seen" / "\Draft" / flag-keyword / flag-extension ; Does not include "\Recent"
flag-extension = "" atom ; Future expansion. Client implementations ; MUST accept flag-extension flags. Server ; implementations MUST NOT generate ; flag-extension flags except as defined by ; future standard or standards-track ; revisions of this specification.
flag-fetch = flag / "\Recent"
flag-keyword = atom
flag-list = "(" [flag *(SP flag)] ")"
flag-perm = flag / "*"
greeting = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
header-fld-name = astring
header-list = "(" header-fld-name *(SP header-fld-name) ")"
list = "LIST" SP mailbox SP list-mailbox
list-mailbox = 1*list-char / string
list-char = ATOM-CHAR / list-wildcards / resp-specials
list-wildcards = "%" / "*"
literal = "\{" number "\}" CRLF *CHAR8 ; Number represents the number of CHAR8s
login = "LOGIN" SP userid SP password
lsub = "LSUB" SP mailbox SP list-mailbox
RFC 3501 IMAPv4 March 2003
mailbox = "INBOX" / astring ; INBOX is case-insensitive. All case variants of ; INBOX (e.g., "iNbOx") MUST be interpreted as INBOX ; not as an astring. An astring which consists of ; the case-insensitive sequence "I" "N" "B" "O" "X" ; is considered to be INBOX and not an astring. ; Refer to section 5.1 for further ; semantic details of mailbox names.
mailbox-data = "FLAGS" SP flag-list / "LIST" SP mailbox-list / "LSUB" SP mailbox-list / "SEARCH" *(SP nz-number) / "STATUS" SP mailbox SP "(" [status-att-list] ")" / number SP "EXISTS" / number SP "RECENT"
mailbox-list = "(" [mbx-list-flags] ")" SP (DQUOTE QUOTED-CHAR DQUOTE / nil) SP mailbox
mbx-list-flags = *(mbx-list-oflag SP) mbx-list-sflag *(SP mbx-list-oflag) / mbx-list-oflag *(SP mbx-list-oflag)
mbx-list-oflag = "\Noinferiors" / flag-extension ; Other flags; multiple possible per LIST response
mbx-list-sflag = "\Noselect" / "\Marked" / "\Unmarked" ; Selectability flags; only one per LIST response
media-basic = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" / "MESSAGE" / "VIDEO") DQUOTE) / string) SP media-subtype ; Defined in [MIME-IMT]
media-message = DQUOTE "MESSAGE" DQUOTE SP DQUOTE "RFC822" DQUOTE ; Defined in [MIME-IMT]
media-subtype = string ; Defined in [MIME-IMT]
media-text = DQUOTE "TEXT" DQUOTE SP media-subtype ; Defined in [MIME-IMT]
message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
msg-att = "(" (msg-att-dynamic / msg-att-static) *(SP (msg-att-dynamic / msg-att-static)) ")"
msg-att-dynamic = "FLAGS" SP "(" [flag-fetch *(SP flag-fetch)] ")" ; MAY change for a message
RFC 3501 IMAPv4 March 2003
msg-att-static = "ENVELOPE" SP envelope / "INTERNALDATE" SP date-time /
"RFC822" [".HEADER" / ".TEXT"] SP nstring /
"RFC822.SIZE" SP number /
"BODY" ["STRUCTURE"] SP body /
"BODY" section ["<" number ">"] SP nstring /
"UID" SP uniqueid
; MUST NOT change for a message
nil = "NIL"
nstring = string / nil
number = 1*DIGIT
; Unsigned 32-bit integer
; (0 <= n < 4,294,967,296)
nz-number = digit-nz *DIGIT
; Non-zero unsigned 32-bit integer
; (0 < n < 4,294,967,296)
password = astring
quoted = DQUOTE *QUOTED-CHAR DQUOTE
QUOTED-CHAR = <any TEXT-CHAR except quoted-specials> /
"" quoted-specials
quoted-specials = DQUOTE / ""
rename = "RENAME" SP mailbox SP mailbox ; Use of INBOX as a destination gives a NO error
response = *(continue-req / response-data) response-done
response-data = "*" SP (resp-cond-state / resp-cond-bye / mailbox-data / message-data / capability-data) CRLF
response-done = response-tagged / response-fatal
response-fatal = "*" SP resp-cond-bye CRLF ; Server closes connection immediately
response-tagged = tag SP resp-cond-state CRLF
resp-cond-auth = ("OK" / "PREAUTH") SP resp-text ; Authentication condition
RFC 3501 IMAPv4 March 2003
resp-cond-bye = "BYE" SP resp-text
resp-cond-state = ("OK" / "NO" / "BAD") SP resp-text ; Status condition
resp-specials = "]"
resp-text = ["[" resp-text-code "]" SP] text
resp-text-code = "ALERT" /
"BADCHARSET" [SP "(" astring *(SP astring) ")" ] /
capability-data / "PARSE" /
"PERMANENTFLAGS" SP "("
[flag-perm (SP flag-perm)] ")" /
"READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
"UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number /
"UNSEEN" SP nz-number /
atom [SP 1<any TEXT-CHAR except "]">]
search = "SEARCH" [SP "CHARSET" SP astring] 1*(SP search-key) ; CHARSET argument to MUST be registered with IANA
search-key = "ALL" / "ANSWERED" / "BCC" SP astring / "BEFORE" SP date / "BODY" SP astring / "CC" SP astring / "DELETED" / "FLAGGED" / "FROM" SP astring / "KEYWORD" SP flag-keyword / "NEW" / "OLD" / "ON" SP date / "RECENT" / "SEEN" / "SINCE" SP date / "SUBJECT" SP astring / "TEXT" SP astring / "TO" SP astring / "UNANSWERED" / "UNDELETED" / "UNFLAGGED" / "UNKEYWORD" SP flag-keyword / "UNSEEN" / ; Above this line were in [IMAP2] "DRAFT" / "HEADER" SP header-fld-name SP astring / "LARGER" SP number / "NOT" SP search-key / "OR" SP search-key SP search-key / "SENTBEFORE" SP date / "SENTON" SP date / "SENTSINCE" SP date / "SMALLER" SP number / "UID" SP sequence-set / "UNDRAFT" / sequence-set / "(" search-key *(SP search-key) ")"
section = "[" [section-spec] "]"
section-msgtext = "HEADER" / "HEADER.FIELDS" [".NOT"] SP header-list / "TEXT" ; top-level or MESSAGE/RFC822 part
section-part = nz-number *("." nz-number) ; body part nesting
RFC 3501 IMAPv4 March 2003
section-spec = section-msgtext / (section-part ["." section-text])
section-text = section-msgtext / "MIME" ; text other than actual body part (headers, etc.)
select = "SELECT" SP mailbox
seq-number = nz-number / "" ; message sequence number (COPY, FETCH, STORE ; commands) or unique identifier (UID COPY, ; UID FETCH, UID STORE commands). ; * represents the largest number in use. In ; the case of message sequence numbers, it is ; the number of messages in a non-empty mailbox. ; In the case of unique identifiers, it is the ; unique identifier of the last message in the ; mailbox or, if the mailbox is empty, the ; mailbox's current UIDNEXT value. ; The server should respond with a tagged BAD ; response to a command that uses a message ; sequence number greater than the number of ; messages in the selected mailbox. This ; includes "" if the selected mailbox is empty.
seq-range = seq-number ":" seq-number ; two seq-number values and all values between ; these two regardless of order. ; Example: 2:4 and 4:2 are equivalent and indicate ; values 2, 3, and 4. ; Example: a unique identifier sequence range of ; 3291:* includes the UID of the last message in ; the mailbox, even if that value is less than 3291.
sequence-set = (seq-number / seq-range) ("," sequence-set) ; set of seq-number values, regardless of order. ; Servers MAY coalesce overlaps and/or execute the ; sequence in any order. ; Example: a message sequence number set of ; 2,4:7,9,12: for a mailbox with 15 messages is ; equivalent to 2,4,5,6,7,9,12,13,14,15 ; Example: a message sequence number set of *:4,5:7 ; for a mailbox with 10 messages is equivalent to ; 10,9,8,7,6,5,4,5,6,7 and MAY be reordered and ; overlap coalesced to be 4,5,6,7,8,9,10.
status = "STATUS" SP mailbox SP "(" status-att *(SP status-att) ")"
RFC 3501 IMAPv4 March 2003
status-att = "MESSAGES" / "RECENT" / "UIDNEXT" / "UIDVALIDITY" / "UNSEEN"
status-att-list = status-att SP number *(SP status-att SP number)
store = "STORE" SP sequence-set SP store-att-flags
store-att-flags = (["+" / "-"] "FLAGS" [".SILENT"]) SP (flag-list / (flag *(SP flag)))
string = quoted / literal
subscribe = "SUBSCRIBE" SP mailbox
tag = 1*<any ASTRING-CHAR except "+">
text = 1*TEXT-CHAR
TEXT-CHAR = <any CHAR except CR and LF>
time = 2DIGIT ":" 2DIGIT ":" 2DIGIT ; Hours minutes seconds
uid = "UID" SP (copy / fetch / search / store) ; Unique identifiers used instead of message ; sequence numbers
uniqueid = nz-number ; Strictly ascending
unsubscribe = "UNSUBSCRIBE" SP mailbox
userid = astring
x-command = "X" atom <experimental command arguments>
zone = ("+" / "-") 4DIGIT ; Signed four-digit value of hhmm representing ; hours and minutes east of Greenwich (that is, ; the amount that the given time differs from ; Universal Time). Subtracting the timezone ; from the given time will give the UT form. ; The Universal Time zone is "+0000".
RFC 3501 IMAPv4 March 2003
-
作者说明
本文档是对早期文档的修订或重写, 并取代这些文档中的协议规范: RFC 2060, RFC 1730, 未发布的 IMAP2bis.TXT 文档, RFC 1176, RFC 1064.
-
安全考虑事项
除非协商了防窥探保护, 否则 IMAP4rev1 协议事务 (包括电子邮件数据) 会以明文在网络上传输. 这可以通过使用 STARTTLS, 在 AUTHENTICATE 命令中协商隐私保护, 或使用其他保护机制来实现.
11.1. STARTTLS 安全考虑事项
本文档中 STARTTLS 命令和 LOGINDISABLED 能力的规范取代 [IMAP-TLS] 中的对应内容. [IMAP-TLS] 对 PLAIN [SASL] 认证器仍具有规范性.
IMAP 客户端和服务器实现 MUST 实现 TLS_RSA_WITH_RC4_128_MD5 [TLS] 密码套件, 并 SHOULD 实现 TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA [TLS] 密码套件. 这一点很重要, 因为它确保任意两个符合规范的实现都可以配置为互操作. 所有其他密码套件都是 OPTIONAL. 注意, 这是相对于 [IMAP-TLS] 第 2.1 节的变更.
在 [TLS] 协商期间, 客户端 MUST 将其理解的服务器主机名与服务器在 Certificate 消息中呈现的身份进行检查, 以防止中间人攻击. 如果匹配失败, 客户端 SHOULD 请求用户明确确认, 或终止连接并指示服务器身份可疑. 匹配按以下规则执行:
客户端 MUST 使用其用于打开连接的服务器主机名, 作为与服务器证书中表示的服务器名称进行比较的值. 客户端 MUST NOT 使用从不安全远程来源 (例如不安全的 DNS 查询) 派生的任何形式的服务器主机名. 不执行 CNAME 规范化.
如果证书中存在 dNSName 类型的 subjectAltName 扩展, 则 SHOULD 使用它作为服务器身份的来源.
匹配不区分大小写.
RFC 3501 IMAPv4 March 2003
"*" 通配符字符 MAY 用作证书中最左侧的名称组件. 例如, *.example.com 会匹配 a.example.com, foo.example.com 等, 但不会匹配 example.com.
如果证书包含多个名称 (例如多个 dNSName 字段), 则与其中任一字段匹配即视为可接受.
客户端和服务器都 MUST 检查 STARTTLS 命令以及后续 [TLS] 协商的结果, 以确定是否达到了可接受的认证或隐私保护.
11.2. 其他安全考虑事项
对于因凭据无效而失败的 AUTHENTICATE 命令, 服务器错误消息 SHOULD NOT 详细说明凭据无效的原因.
使用 LOGIN 命令会以明文发送密码. 可以通过以下方式避免这一点: 使用带有不使用明文密码的 [SASL] 机制的 AUTHENTICATE 命令, 或先通过 STARTTLS 或其他保护机制协商加密.
服务器实现 MUST 实现一种配置, 要求在认证时满足以下条件: (1) STARTTLS 命令已经协商. OR (2) 已提供某种其他机制, 用于保护会话免受密码窥探. OR (3) 以下措施已经到位: (a) 通告 LOGINDISABLED 能力, 并且 CAPABILITY 列表中 NOT 通告使用明文密码的 [SASL] 机制 (如 PLAIN). AND (b) 即使密码正确, LOGIN 命令也返回错误. AND (c) 对所有使用明文密码的 [SASL] 机制, 即使密码正确, AUTHENTICATE 命令也返回错误.
对于失败的 LOGIN 命令, 服务器错误消息 SHOULD NOT 指明无效的是用户名而不是密码.