Skip to main content

9. Formal Syntax (正式语法)

以下语法规范使用 [ABNF] 中指定的增强巴科斯-诺尔范式 (Augmented Backus-Naur Form, ABNF) 表示法.

在存在替代或可选规则的情况下, 如果后面的规则与前面的规则重叠, 则前面列出的规则必须 (MUST) 优先. 例如, 当 "\Seen" 被解析为标志时, 它是 \Seen 标志名称而不是 flag-extension, 即使 "\Seen" 可以被解析为 flag-extension. 下面注明了此规则的一些 (但不是全部) 实例.

注意: 必须 (MUST) 严格遵循 [ABNF] 规则; 特别是:

(1) 除非另有说明, 所有字母字符都不区分大小写. 使用大写或小写字符来定义标记字符串仅用于编辑清晰. 实现必须 (MUST) 以不区分大小写的方式接受这些字符串.

(2) 在所有情况下, SP 指的是恰好一个空格. 不允许 (NOT permitted) 用 TAB 替换, 插入额外的空格, 或以其他方式将 SP 视为等同于 LWSP.

(3) ASCII NUL 字符 (%x00) 在任何时候都不得 (MUST NOT) 使用.


ABNF 语法定义

以下是 IMAP4rev1 协议的完整 ABNF 语法定义:

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









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









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







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







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 ">"]









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














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






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








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






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) ")"







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".












(第9章 Formal Syntax 翻译完成)

注: 本章包含完整的 IMAP4rev1 协议 ABNF 语法定义, 所有语法规则均保持原文不变, 以确保技术准确性.