Class: Net::IMAP::ResponseParser
Relationships & Source Files | |
Namespace Children | |
Modules:
| |
Classes:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
|
|
Instance Chain:
self,
ResponseConditions ,
ParserUtils
|
|
Inherits: | Object |
Defined in: | lib/net/imap/response_parser.rb, lib/net/imap/response_parser/parser_utils.rb |
Overview
Parses an IMAP server response.
Constant Summary
-
ADDRESS_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 1871/\G \( (?: NIL | #{Patterns::QUOTED_rev2} ) # 1: NAME \s (?: NIL | #{Patterns::QUOTED_rev2} ) # 2: ROUTE \s (?: NIL | #{Patterns::QUOTED_rev2} ) # 3: MAILBOX \s (?: NIL | #{Patterns::QUOTED_rev2} ) # 4: HOST \) /nix
-
ASTRING_CHARS_TOKENS =
Internal use only
ASTRING-CHAR = ATOM-CHAR / resp-specials resp-specials = “]”
[*ATOM_TOKENS, T_RBRA].freeze
-
ASTRING_TOKENS =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 484[T_QUOTED, *ASTRING_CHARS_TOKENS, T_LITERAL].freeze
-
ATOM_TOKENS =
Internal use only
atom = 1*ATOM-CHAR ATOM-CHAR = <any CHAR except atom-specials>
[T_ATOM, T_NUMBER, T_NIL, T_LBRA, T_PLUS]
-
BEG_REGEXP =
Internal use only
the default, used in most places
/\G(?:\ (?# 1: SPACE )( )|\ (?# 2: LITERAL8)#{Patterns::LITERAL8}|\ (?# 3: ATOM prefixed with a compatible subtype)\ ((?:\ (?# 4: NIL )(NIL)|\ (?# 5: NUMBER )(\d+)|\ (?# 6: PLUS )(\+))\ (?# 7: ATOM remaining after prefix )(#{Patterns::ATOMISH})?\ (?# This enables greedy alternation without lookahead, in linear time.)\ )|\ (?# Also need to check for ATOM without a subtype prefix.)\ (?# 8: ATOM )(#{Patterns::ATOMISH})|\ (?# 9: QUOTED )#{Patterns::QUOTED_rev2}|\ (?# 10: LPAR )(\()|\ (?# 11: RPAR )(\))|\ (?# 12: BSLASH )(\\)|\ (?# 13: STAR )(\*)|\ (?# 14: LBRA )(\[)|\ (?# 15: RBRA )(\])|\ (?# 16: LITERAL )#{Patterns::LITERAL}|\ (?# 17: PERCENT )(%)|\ (?# 18: CRLF )(\r\n)|\ (?# 19: EOF )(\z))/ni
-
CTEXT_REGEXP =
Internal use only
resp-text-code, after ‘atom SP’
/\G(#{Patterns::CODE_TEXT})/n
-
DATA_REGEXP =
Internal use only
envelope, body(structure), namespaces
/\G(?:\ (?# 1: SPACE )( )|\ (?# 2: NIL )(NIL)|\ (?# 3: NUMBER )(\d+)|\ (?# 4: QUOTED )#{Patterns::QUOTED_rev2}|\ (?# 5: LITERAL )#{Patterns::LITERAL}|\ (?# 6: LPAR )(\()|\ (?# 7: RPAR )(\)))/ni
-
EXPR_BEG =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 43:EXPR_BEG
-
EXPR_DATA =
Internal use only
the default, used in most places
:EXPR_DATA
-
RE_RESPONSE_TYPE =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 681/\G(?:\d+ )?(?<type>#{Patterns::TAGGED_EXT_LABEL})/n
-
SEQUENCE_SET_TOKENS =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 462[T_ATOM, T_NUMBER, T_STAR]
-
SPACES_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 2010/\G */n
-
TAG_TOKENS =
Internal use only
tag = 1*<any ASTRING-CHAR except “+”>
(ASTRING_CHARS_TOKENS - [T_PLUS]).freeze
-
TEXT_REGEXP =
Internal use only
text, after ‘resp-text-code “]”’
/\G(#{Patterns::TEXT_rev2})/n
-
T_ATOM =
Internal use only
atom special
:ATOM
-
T_BSLASH =
Internal use only
starts/end with atom special
:BSLASH
-
T_CRLF =
Internal use only
starts with atom char “~”
:CRLF
-
T_EOF =
Internal use only
any char except CRLF
:EOF
-
T_LBRA =
Internal use only
subset of atom
:LBRA
-
T_LITERAL =
Internal use only
atom special; list wildcard
:LITERAL
-
T_LITERAL8 =
Internal use only
starts with atom special
:LITERAL8
-
T_LPAR =
Internal use only
atom special; quoted special
:LPAR
-
T_NIL =
Internal use only
atom (subset of astring chars)
:NIL
-
T_NUMBER =
Internal use only
subset of atom and label
:NUMBER
-
T_PERCENT =
Internal use only
atom special; list wildcard
:PERCENT
-
T_PLUS =
Internal use only
subset of atom
:PLUS
-
T_QUOTED =
Internal use only
atom special; resp_special; valid astring char
:QUOTED
-
T_RBRA =
Internal use only
subset of atom; tag special
:RBRA
-
T_RPAR =
Internal use only
atom special; paren list delimiter
:RPAR
-
T_SPACE =
Internal use only
envelope, body(structure), namespaces
:SPACE
-
T_STAR =
Internal use only
atom special; paren list delimiter
:STAR
-
T_TEXT =
Internal use only
atom special; text special; quoted special
:TEXT
ResponseConditions
- Included
AUTH_CONDS, BAD, BYE, GREETING_CONDS, NO, OK, PREAUTH, RESP_CONDS, RESP_COND_STATES, RESP_DATA_CONDS
Class Method Summary
- .new ⇒ ResponseParser constructor
ResponseParser::ParserUtils::Generator
- Extended
def_char_matchers | we can skip lexer for single character matches, as a shortcut. |
def_token_matchers | TODO: move coersion to the token.value method? |
Instance Attribute Summary
- #config readonly
-
#astring
(also: #mailbox, #header_fld_name)
readonly
Internal use only
astring = 1*ASTRING-CHAR / string.
- #astring? ⇒ Boolean readonly Internal use only
-
#atom
(also: #gt__number__lt, #section_part, #objectid)
readonly
Internal use only
TODO: handle atom, astring_chars, and tag entirely inside the lexer.
-
#atom? ⇒ Boolean
readonly
Internal use only
the
#accept
version of #atom -
#capability
readonly
Internal use only
Alias for #case_insensitive__atom.
-
#capability?
readonly
Internal use only
Alias for #case_insensitive__atom?.
-
#case_insensitive__atom
(also: #capability, #resp_text_code__name)
readonly
Internal use only
Returns
atom.upcase
. -
#case_insensitive__atom? ⇒ Boolean
(also: #capability?)
readonly
Internal use only
Returns
atom?&.upcase
. -
#gt__number__lt
readonly
Internal use only
Alias for #atom.
-
#header_fld_name
readonly
Internal use only
Alias for #astring.
-
#lookahead_thread_nested?
readonly
Internal use only
Alias for #lookahead_thread_list?.
-
#mailbox
readonly
Internal use only
Alias for #astring.
-
#objectid
readonly
Internal use only
Alias for #atom.
-
#resp_text_code__name
readonly
Internal use only
Alias for #case_insensitive__atom.
-
#section_part
readonly
Internal use only
Alias for #atom.
-
#text
readonly
Internal use only
TEXT-CHAR = <any CHAR except CR and LF> RFC3501:
-
#text? ⇒ Boolean
readonly
Internal use only
an “accept” versiun of #text
Instance Method Summary
-
#parse(str) ⇒ ContinuationRequest
Raises ResponseParseError for unparsable strings.
-
#accept_spaces
Internal use only
The RFC is very strict about this and usually we should be too.
-
#acl_data
Internal use only
acl-data = “ACL” SP mailbox *(SP identifier SP rights).
-
#addr_adl
Internal use only
Alias for #nstring.
-
#addr_host
Internal use only
Alias for #nstring.
-
#addr_mailbox
Internal use only
Alias for #nstring.
-
#addr_name
Internal use only
Alias for #nstring.
-
#address
Internal use only
address = “(” addr-name SP addr-adl SP addr-mailbox SP.
- #astring_chars Internal use only
-
#body
Internal use only
RFC-3501 & RFC-9051:
-
#body_ext_1part
Internal use only
RFC2060.
-
#body_ext_mpart
Internal use only
RFC-2060:
-
#body_extension
Internal use only
body-extension = nstring / number / number64 /.
-
#body_extensions
Internal use only
body-extension *(SP body-extension).
-
#body_fields
Internal use only
RFC-3501 & RFC-9051:
-
#body_fld_desc
Internal use only
Alias for #nstring.
-
#body_fld_dsp
Internal use only
body-fld-dsp = “(” string SP body-fld-param “)” / nil.
-
#body_fld_enc
Internal use only
RFC-3501 & RFC-9051:
-
#body_fld_id
Internal use only
Alias for #nstring.
-
#body_fld_lang
Internal use only
body-fld-lang = nstring / “(” string *(SP string) “)”.
-
#body_fld_lines
Internal use only
Alias for #number64.
-
#body_fld_loc
Internal use only
Alias for #nstring.
-
#body_fld_md5
Internal use only
Alias for #nstring.
- #body_fld_octets Internal use only
-
#body_fld_param
Internal use only
RFC3501, RFC9051: body-fld-param = “(” string SP string *(SP string SP string) “)” / nil.
-
#body_type_1part
Internal use only
RFC-3501 & RFC9051:
-
#body_type_basic
Internal use only
RFC-3501 & RFC9051:
-
#body_type_mixed
Internal use only
This is a malformed body-type-mpart with no subparts.
-
#body_type_mpart
Internal use only
RFC-3501 & RFC-9051:
-
#body_type_msg
Internal use only
RFC-3501 & RFC-9051:
-
#body_type_text
Internal use only
RFC-3501 & RFC-9051:
-
#capability__list
(also: #resp_code__capability)
Internal use only
As a workaround for buggy servers, allow a trailing SP:
-
#capability_data__untagged
Internal use only
The presence of “IMAP4rev1” or “IMAP4rev2” is unenforced here.
-
#case_insensitive__nstring
Internal use only
use where nstring represents “LABEL” values.
- #charset Internal use only
-
#charset__list
Internal use only
“(” charset *(SP charset) “)”.
-
#comparator_data(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#continue_req
Internal use only
RFC3501 & RFC9051:
-
#date_time
Internal use only
date-time = DQUOTE date-day-fixed “-” date-month “-” date-year.
-
#enable_data
Internal use only
enable-data = “ENABLED” *(SP capability).
-
#env_bcc
Internal use only
Alias for #nlist__address.
-
#env_cc
Internal use only
Alias for #nlist__address.
-
#env_date
Internal use only
Alias for #nstring.
-
#env_from
Internal use only
Alias for #nlist__address.
-
#env_in_reply_to
Internal use only
Alias for #nstring.
-
#env_message_id
Internal use only
Alias for #nstring.
-
#env_reply_to
Internal use only
Alias for #nlist__address.
-
#env_sender
Internal use only
Alias for #nlist__address.
-
#env_subject
Internal use only
Alias for #nstring.
-
#env_to
Internal use only
Alias for #nlist__address.
-
#envelope
Internal use only
RFC3501 & RFC9051:
-
#esearch_response(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#expunged_resp(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#flag_list
Internal use only
flag-list = “(” [flag *(SP flag)] “)”.
-
#flag_perm__list
Internal use only
“(” [flag-perm *(SP flag-perm)] “)”.
-
#header_list
Internal use only
header-list = “(” header-fld-name *(SP header-fld-name) “)”.
- #id_response Internal use only
- #label(word) Internal use only
- #label_in(*labels) Internal use only
-
#language_data(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#listrights_data(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
- #lookahead_body? Internal use only
- #lookahead_thread_list? (also: #lookahead_thread_nested?) Internal use only
-
#mailbox_data__exists
Internal use only
Alias for #response_data__simple_numeric.
-
#mailbox_data__flags
Internal use only
mailbox-data = “FLAGS” SP flag-list / “LIST” SP mailbox-list /.
- #mailbox_data__list (also: #mailbox_data__lsub, #mailbox_data__xlist) Internal use only
-
#mailbox_data__lsub
Internal use only
Alias for #mailbox_data__list.
-
#mailbox_data__recent
Internal use only
Alias for #response_data__simple_numeric.
-
#mailbox_data__search
(also: #sort_data)
Internal use only
RFC3501:
-
#mailbox_data__status
Internal use only
mailbox-data =/ “STATUS” SP mailbox SP “(” [status-att-list] “)”.
-
#mailbox_data__xlist
Internal use only
Alias for #mailbox_data__list.
-
#mailbox_list
Internal use only
mailbox-list = “(” [mbx-list-flags] “)” SP.
- #mbx_list_flags Internal use only
-
#media_basic
Internal use only
Alias for #media_type.
-
#media_message
Internal use only
Alias for #media_type.
-
#media_subtype
Internal use only
text/*.
-
#media_text
Internal use only
Alias for #media_type.
-
#media_type
(also: #media_basic, #media_message, #media_text)
Internal use only
n.b. this handles both type and subtype.
-
#message_data__converted(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#message_data__expunge
Internal use only
Alias for #response_data__simple_numeric.
-
#message_data__fetch
Internal use only
message-data = nz-number SP (“EXPUNGE” / (“FETCH” SP msg-att)).
-
#metadata_resp(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#mod_sequence_value
(also: #permsg_modsequence)
Internal use only
Alias for #nz_number64.
-
#mod_sequence_valzer
Internal use only
Alias for #number64.
-
#msg_att(n)
Internal use only
RFC3501 & RFC9051:
-
#msg_att__label
Internal use only
appends “[section]” and “<partial>” to the base label.
-
#myrights_data(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#namespace
Internal use only
namespace = nil / “(” 1*namespace-descr “)”.
-
#namespace_descr
Internal use only
namespace-descr = “(” string SP.
-
#namespace_response
Internal use only
namespace-response = “NAMESPACE” SP namespace.
-
#namespace_response_extensions
Internal use only
namespace-response-extensions = *namespace-response-extension namespace-response-extension = SP string SP.
-
#ndatetime
Internal use only
Alias for #nquoted.
- #next_token Internal use only
- #nil_atom Internal use only
-
#nlist__address
(also: #env_from, #env_sender, #env_reply_to, #env_to, #env_cc, #env_bcc)
Internal use only
env-from = “(” 1*address “)” / nil env-sender = “(” 1*address “)” / nil env-reply-to = “(” 1*address “)” / nil env-to = “(” 1*address “)” / nil env-cc = “(” 1*address “)” / nil env-bcc = “(” 1*address “)” / nil.
- #nparens__objectid Internal use only
- #nquoted (also: #ndatetime) Internal use only
-
#nstring
(also: #env_date, #env_subject, #env_in_reply_to, #env_message_id, #body_fld_desc, #body_fld_id, #body_fld_loc, #body_fld_md5, #addr_adl, #addr_host, #addr_mailbox, #addr_name)
Internal use only
nstring = string / nil.
- #nstring8 Internal use only
-
#number64
(also: #body_fld_lines, #mod_sequence_valzer)
Internal use only
valid number ranges are not enforced by parser.
- #number64? Internal use only
-
#nz_number
(also: #nz_number64, #uniqueid)
Internal use only
valid number ranges are not enforced by parser.
-
#nz_number64
(also: #mod_sequence_value)
Internal use only
Alias for #nz_number.
- #nz_number? Internal use only
- #parens__modseq Internal use only
- #parens__objectid Internal use only
-
#permsg_modsequence
Internal use only
Alias for #mod_sequence_value.
-
#quirky__flag_list(name)
Internal use only
This allows illegal “]” in flag names (Gmail), or “*” in a FLAGS response (greenmail).
-
#quirky_SP?
Internal use only
Used when servers erroneously send an extra SP.
- #quota_response Internal use only
- #quotaroot_response Internal use only
-
#remaining_unparsed
Internal use only
reads all the way up until CRLF.
-
#resp_code__capability
Internal use only
Alias for #capability__list.
-
#resp_code_apnd__data
Internal use only
already matched: “APPENDUID”.
-
#resp_code_copy__data
Internal use only
already matched: “COPYUID”.
-
#resp_cond_auth
Internal use only
resp-cond-auth = (“OK” / “PREAUTH”) SP resp-text.
-
#resp_cond_auth__name
Internal use only
expects “OK” or “PREAUTH” and raises
InvalidResponseError
on failure. -
#resp_cond_bye
Internal use only
resp-cond-bye = “BYE” SP resp-text.
-
#resp_cond_state
Internal use only
RFC3501 & RFC9051:
-
#resp_cond_state__name
Internal use only
expects “OK” or “NO” or “BAD” and raises
InvalidResponseError
on failure. - #resp_cond_state__untagged Internal use only
-
#resp_text
Internal use only
RFC3501:
-
#resp_text_code
Internal use only
RFC3501 (See www.rfc-editor.org/errata/rfc3501):
-
#response
Internal use only
[RFC3501 & RFC9051:].
-
#response_data
Internal use only
[RFC3501:].
- #response_data__ignored (also: #response_data__noop) Internal use only
-
#response_data__noop
Internal use only
Alias for #response_data__ignored.
- #response_data__simple_numeric (also: #message_data__expunge, #mailbox_data__exists, #mailbox_data__recent) Internal use only
- #response_data__unhandled(klass = UntaggedResponse) (also: #esearch_response, #expunged_resp, #uidfetch_resp, #listrights_data, #myrights_data, #metadata_resp, #language_data, #comparator_data, #message_data__converted) Internal use only
-
#response_tagged
Internal use only
RFC3501 & RFC9051:
-
#section
Internal use only
section = “[” [section-spec] “]”.
-
#section_binary
Internal use only
section-binary = “[” [section-part] “]”.
-
#section_spec
Internal use only
section-spec = section-msgtext / (section-part [“.” section-text]) section-msgtext = “HEADER” /.
-
#sequence_set
Internal use only
sequence-set = (seq-number / seq-range) [“,” sequence-set].
-
#sort_data
Internal use only
Alias for #mailbox_data__search.
-
#status_att_list
Internal use only
RFC3501.
-
#status_att_val
Internal use only
RFC3501 Errata: status-att-val = (“MESSAGES” SP number) / (“RECENT” SP number) /.
- #tag Internal use only
-
#tagged_ext_comp
Internal use only
tagged-ext-comp = astring /.
-
#tagged_ext_simple
Internal use only
tagged-ext-simple is a subset of atom TODO: recognize sequence-set in the lexer.
-
#tagged_ext_val
Internal use only
tagged-ext-val = tagged-ext-simple /.
-
#text_chars_except_rbra
Internal use only
1*<any TEXT-CHAR except “]”>.
-
#thread_data
Internal use only
RFC5256: THREAD.
-
#thread_list
Internal use only
RFC5256: THREAD.
-
#thread_members
Internal use only
RFC5256: THREAD.
-
#thread_nested
Internal use only
RFC5256: THREAD.
-
#uid_set
Internal use only
RFC-4315 (UIDPLUS) or RFC9051 (IMAP4rev2):
-
#uidfetch_resp(klass = UntaggedResponse)
Internal use only
Alias for #response_data__unhandled.
-
#uniqueid
Internal use only
Alias for #nz_number.
-
#x_gm_id
Internal use only
valid number ranges are not enforced by parser.
- #x_gm_label Internal use only
- #x_gm_labels Internal use only
ParserUtils
- Included
#accept | like match, but does not raise error on failure. |
#accept_re, | |
#assert_no_lookahead | To be used conditionally: |
#combine_adjacent | TODO: after checking the lookahead, use a regexp for remaining chars. |
#lookahead, | |
#lookahead! | like match, without consuming the token. |
#lookahead? | like accept, without consuming the token. |
#match, #match_re, #parse_error, #peek_re, #peek_str?, #shift_token |
Constructor Details
.new ⇒ ResponseParser
# File 'lib/net/imap/response_parser.rb', line 17
def initialize(config: Config.global) @str = nil @pos = nil @lex_state = nil @token = nil @config = Config[config] end
Instance Attribute Details
#astring (readonly) Also known as: #mailbox, #header_fld_name
astring = 1*ASTRING-CHAR / string
# File 'lib/net/imap/response_parser.rb', line 506
def astring lookahead?(*ASTRING_CHARS_TOKENS) ? astring_chars : string end
#astring? ⇒ Boolean
(readonly)
# File 'lib/net/imap/response_parser.rb', line 510
def astring? lookahead?(*ASTRING_CHARS_TOKENS) ? astring_chars : string? end
#atom (readonly) Also known as: #gt__number__lt, #section_part, #objectid
TODO: handle atom, astring_chars, and tag entirely inside the lexer
# File 'lib/net/imap/response_parser.rb', line 490
def atom; combine_adjacent(*ATOM_TOKENS) end
#atom? ⇒ Boolean
(readonly)
the #accept
version of #atom
# File 'lib/net/imap/response_parser.rb', line 495
def atom?; -combine_adjacent(*ATOM_TOKENS) if lookahead?(*ATOM_TOKENS) end
#capability (readonly)
Alias for #case_insensitive__atom.
# File 'lib/net/imap/response_parser.rb', line 1630
alias capability case_insensitive__atom
#capability? (readonly)
Alias for #case_insensitive__atom?.
# File 'lib/net/imap/response_parser.rb', line 1631
alias capability? case_insensitive__atom?
#case_insensitive__atom (readonly) Also known as: #capability, #resp_text_code__name
Returns atom.upcase
# File 'lib/net/imap/response_parser.rb', line 498
def case_insensitive__atom; -combine_adjacent(*ATOM_TOKENS).upcase end
#case_insensitive__atom? ⇒ Boolean
(readonly)
Also known as: #capability?
Returns atom?&.upcase
# File 'lib/net/imap/response_parser.rb', line 501
def case_insensitive__atom? -combine_adjacent(*ATOM_TOKENS).upcase if lookahead?(*ATOM_TOKENS) end
#config (readonly)
[ GitHub ]# File 'lib/net/imap/response_parser.rb', line 14
attr_reader :config
#gt__number__lt (readonly)
Alias for #atom.
# File 'lib/net/imap/response_parser.rb', line 946
alias gt__number__lt atom
#header_fld_name (readonly)
Alias for #astring.
# File 'lib/net/imap/response_parser.rb', line 1332
alias header_fld_name astring
#lookahead_thread_nested? (readonly)
Alias for #lookahead_thread_list?.
# File 'lib/net/imap/response_parser.rb', line 1483
alias lookahead_thread_nested? lookahead_thread_list?
#mailbox (readonly)
Alias for #astring.
# File 'lib/net/imap/response_parser.rb', line 615
alias mailbox astring
#objectid (readonly)
Alias for #atom.
# File 'lib/net/imap/response_parser.rb', line 1980
alias objectid atom
#resp_text_code__name (readonly)
Alias for #case_insensitive__atom.
# File 'lib/net/imap/response_parser.rb', line 1831
alias resp_text_code__name case_insensitive__atom
#section_part (readonly)
Alias for #atom.
# File 'lib/net/imap/response_parser.rb', line 1314
alias section_part atom
#text (readonly)
TEXT-CHAR = <any CHAR except CR and LF> RFC3501:
text = 1*TEXT-CHAR
RFC9051:
text = 1*(TEXT-CHAR / UTF8-2 / UTF8-3 / UTF8-4)
; Non-ASCII text can only be returned
; after ENABLE IMAP4rev2 command
# File 'lib/net/imap/response_parser.rb', line 1723
def text match_re(TEXT_REGEXP, "text")[0].force_encoding("UTF-8") end
#text? ⇒ Boolean
(readonly)
an “accept” versiun of #text
# File 'lib/net/imap/response_parser.rb', line 1728
def text? accept_re(TEXT_REGEXP)&.[](0)&.force_encoding("UTF-8") end
Instance Method Details
#accept_spaces
The RFC is very strict about this and usually we should be too. But skipping spaces is usually a safe workaround for buggy servers.
This advances @pos directly so it’s safe before changing @lex_state.
# File 'lib/net/imap/response_parser.rb', line 2016
def accept_spaces return false unless SP? @str.index(SPACES_REGEXP, @pos) and @pos = $~.end(0) true end
#acl_data
acl-data = “ACL” SP mailbox *(SP identifier SP rights)
# File 'lib/net/imap/response_parser.rb', line 1417
def acl_data token = match(T_ATOM) name = token.value.upcase match(T_SPACE) mailbox = astring data = [] token = lookahead if token.symbol == T_SPACE shift_token while true token = lookahead case token.symbol when T_CRLF break when T_SPACE shift_token end user = astring match(T_SPACE) rights = astring data.push(MailboxACLItem.new(user, rights, mailbox)) end end return UntaggedResponse.new(name, data, @str) end
#addr_adl
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1900
alias addr_adl nstring
#addr_host
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1901
alias addr_host nstring
#addr_mailbox
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1902
alias addr_mailbox nstring
#addr_name
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1903
alias addr_name nstring
#address
address = “(” addr-name SP addr-adl SP addr-mailbox SP
addr-host ")"
addr-adl = nstring addr-host = nstring addr-mailbox = nstring addr-name = nstring
# File 'lib/net/imap/response_parser.rb', line 1885
def address if (match = accept_re(ADDRESS_REGEXP)) # note that "NIL" isn't captured by the regexp name, route, mailbox, host = match.captures .map { Patterns.unescape_quoted _1 } else # address may include literals lpar; name = addr_name SP!; route = addr_adl SP!; mailbox = addr_mailbox SP!; host = addr_host rpar end Address.new(name, route, mailbox, host) end
#astring_chars
# File 'lib/net/imap/response_parser.rb', line 491
def astring_chars; combine_adjacent(*ASTRING_CHARS_TOKENS) end
#body
RFC-3501 & RFC-9051:
body = "(" (body-type-1part / body-type-mpart) ")"
# File 'lib/net/imap/response_parser.rb', line 1012
def body @lex_state = EXPR_DATA lpar; result = peek_lpar? ? body_type_mpart : body_type_1part; rpar result ensure @lex_state = EXPR_BEG end
#body_ext_1part
RFC2060
body_ext_1part ::= body_fld_md5 [SPACE body_fld_dsp
[SPACE body_fld_lang
[SPACE 1#body_extension]]]
;; MUST NOT be returned on non-extensible
;; "BODY" fetch
RFC3501 & RFC9051
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
# File 'lib/net/imap/response_parser.rb', line 1184
def body_ext_1part fields = []; fields << body_fld_md5 SP? or return fields; fields << body_fld_dsp SP? or return fields; fields << body_fld_lang SP? or return fields; fields << body_fld_loc SP? or return fields; fields << body_extensions fields end
#body_ext_mpart
RFC-2060:
body_ext_mpart = body_fld_param [SP body_fld_dsp SP body_fld_lang
[SP 1#body_extension]]
;; MUST NOT be returned on non-extensible
;; "BODY" fetch
RFC-3501 & RFC-9051:
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
# File 'lib/net/imap/response_parser.rb', line 1203
def body_ext_mpart fields = []; fields << body_fld_param SP? or return fields; fields << body_fld_dsp SP? or return fields; fields << body_fld_lang SP? or return fields; fields << body_fld_loc SP? or return fields; fields << body_extensions fields end
#body_extension
body-extension = nstring / number / number64 /
"(" 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.
# File 'lib/net/imap/response_parser.rb', line 1260
def body_extension if (uint = number64?) then uint elsif lpar? then exts = body_extensions; rpar; exts else nstring end end
#body_extensions
body-extension *(SP body-extension)
# File 'lib/net/imap/response_parser.rb', line 1246
def body_extensions result = [] result << body_extension; while SP? do result << body_extension end result end
#body_fields
# File 'lib/net/imap/response_parser.rb', line 1148
def body_fields fields = [] fields << body_fld_param; SP! fields << body_fld_id; SP! fields << body_fld_desc; SP! fields << body_fld_enc; SP! fields << body_fld_octets fields end
#body_fld_desc
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1212
alias body_fld_desc nstring
#body_fld_dsp
body-fld-dsp = “(” string SP body-fld-param “)” / nil
# File 'lib/net/imap/response_parser.rb', line 1225
def body_fld_dsp return if NIL? lpar; dsp_type = case_insensitive__string SP!; param = body_fld_param rpar ContentDisposition.new(dsp_type, param) end
#body_fld_enc
RFC-3501 & RFC-9051:
body-fld-enc = (DQUOTE ("7BIT" / "8BIT" / "BINARY" / "BASE64"/
"QUOTED-PRINTABLE") DQUOTE) / string
# File 'lib/net/imap/response_parser.rb', line 1222
alias body_fld_enc case_insensitive__string
#body_fld_id
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1213
alias body_fld_id nstring
#body_fld_lang
body-fld-lang = nstring / “(” string *(SP string) “)”
# File 'lib/net/imap/response_parser.rb', line 1234
def body_fld_lang if lpar? result = [case_insensitive__string] result << case_insensitive__string while SP? rpar result else case_insensitive__nstring end end
#body_fld_lines
Alias for #number64.
# File 'lib/net/imap/response_parser.rb', line 1215
alias body_fld_lines number64 # number in 3501, number64 in 9051
#body_fld_loc
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1214
alias body_fld_loc nstring
#body_fld_md5
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 1216
alias body_fld_md5 nstring
#body_fld_octets
# File 'lib/net/imap/response_parser.rb', line 1217
alias body_fld_octets number
#body_fld_param
RFC3501, RFC9051: body-fld-param = “(” string SP string *(SP string SP string) “)” / nil
# File 'lib/net/imap/response_parser.rb', line 1160
def body_fld_param quirky_SP? # See comments on test_bodystructure_extra_space return if NIL? param = {} lpar name = case_insensitive__string; SP!; param[name] = string while SP? name = case_insensitive__string; SP!; param[name] = string end rpar param end
#body_type_1part
# File 'lib/net/imap/response_parser.rb', line 1024
def body_type_1part # This regexp peek is a performance optimization. # The lookahead fallback would work fine too. m = peek_re(/\G(?: (?<TEXT> "TEXT" \s "[^"]+" ) |(?<MESSAGE> "MESSAGE" \s "(?:RFC822|GLOBAL)" ) |(?<BASIC> "[^"]+" \s "[^"]+" ) |(?<MIXED> "MIXED" ) )/nix) choice = m&.named_captures&.compact&.keys&.first # In practice, the following line should never be used. But the ABNF # *does* allow literals, and this will handle them. choice ||= lookahead_case_insensitive__string! case choice when "BASIC" then body_type_basic # => BodyTypeBasic when "MESSAGE" then body_type_msg # => BodyTypeMessage | BodyTypeBasic when "TEXT" then body_type_text # => BodyTypeText when "MIXED" then body_type_mixed # => BodyTypeMultipart (server bug) else body_type_basic # might be a bug; server's or ours? end end
#body_type_basic
# File 'lib/net/imap/response_parser.rb', line 1048
def body_type_basic type = media_basic # n.b. "basic" type isn't enforced here if lookahead_rpar? then return BodyTypeBasic.new(*type) end # invalid SP!; flds = body_fields SP? and exts = body_ext_1part BodyTypeBasic.new(*type, *flds, *exts) end
#body_type_mixed
This is a malformed body-type-mpart with no subparts.
# File 'lib/net/imap/response_parser.rb', line 1097
def body_type_mixed # warn "malformed body-type-mpart: multipart/mixed with no parts." type = media_subtype # => "MIXED" SP? and exts = body_ext_mpart BodyTypeMultipart.new("MULTIPART", type, nil, *exts) end
#body_type_mpart
# File 'lib/net/imap/response_parser.rb', line 1107
def body_type_mpart parts = [body]; parts << body until SP?; msubtype = media_subtype SP? and exts = body_ext_mpart BodyTypeMultipart.new("MULTIPART", msubtype, parts, *exts) end
#body_type_msg
# File 'lib/net/imap/response_parser.rb', line 1069
def body_type_msg # n.b. "message/rfc822" type isn't enforced here type = SP!; flds = body_fields # Sometimes servers send body-type-basic when body-type-msg should be. # E.g: when a message/rfc822 part has "Content-Disposition: attachment". # # * SP "(" --> SP envelope --> continue as body-type-msg # * ")" --> no body-ext-1part --> completed body-type-basic # * SP nstring --> SP body-fld-md5 # --> SP body-ext-1part --> continue as body-type-basic # # It's probably better to return BodyTypeBasic---even for # "message/rfc822"---than BodyTypeMessage with invalid fields. unless peek_str?(" (") SP? and exts = body_ext_1part return BodyTypeBasic.new(*type, *flds, *exts) end SP!; env = envelope SP!; bdy = body SP!; lines = body_fld_lines SP? and exts = body_ext_1part BodyTypeMessage.new(*type, *flds, env, bdy, lines, *exts) end
#body_type_text
# File 'lib/net/imap/response_parser.rb', line 1058
def body_type_text type = media_text SP!; flds = body_fields SP!; lines = body_fld_lines SP? and exts = body_ext_1part BodyTypeText.new(*type, *flds, lines, *exts) end
#capability__list Also known as: #resp_code__capability
As a workaround for buggy servers, allow a trailing SP:
*(SP capability) [SP]
# File 'lib/net/imap/response_parser.rb', line 1620
def capability__list list = []; while SP? && (capa = capability?) do list << capa end; list end
#capability_data__untagged
The presence of “IMAP4rev1” or “IMAP4rev2” is unenforced here. The grammar rule is used by both response-data and resp-text-code. But this method only returns UntaggedResponse
(response-data).
RFC3501:
capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1"
*(SP capability)
RFC9051:
capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev2"
*(SP capability)
# File 'lib/net/imap/response_parser.rb', line 1609
def capability_data__untagged UntaggedResponse.new label("CAPABILITY"), capability__list, @str end
#case_insensitive__nstring
use where nstring represents “LABEL” values
# File 'lib/net/imap/response_parser.rb', line 559
def case_insensitive__nstring NIL? ? nil : case_insensitive__string end
#charset
See www.rfc-editor.org/errata/rfc3501
charset = atom / quoted
# File 'lib/net/imap/response_parser.rb', line 1956
def charset; quoted? || atom end
#charset__list
“(” charset *(SP charset) “)”
#comparator_data(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 779
alias comparator_data response_data__unhandled
#continue_req
# File 'lib/net/imap/response_parser.rb', line 676
def continue_req PLUS! ContinuationRequest.new(SP? ? resp_text : ResponseText::EMPTY, @str) end
#date_time
date-time = DQUOTE date-day-fixed “-” date-month “-” date-year
SP time SP zone DQUOTE
# File 'lib/net/imap/response_parser.rb', line 1007
alias date_time quoted
#enable_data
enable-data = “ENABLED” *(SP capability)
# File 'lib/net/imap/response_parser.rb', line 1614
def enable_data UntaggedResponse.new label("ENABLED"), capability__list, @str end
#env_bcc
Alias for #nlist__address.
# File 'lib/net/imap/response_parser.rb', line 997
alias env_bcc nlist__address
#env_cc
Alias for #nlist__address.
# File 'lib/net/imap/response_parser.rb', line 996
alias env_cc nlist__address
#env_date
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 975
alias env_date nstring
#env_from
Alias for #nlist__address.
# File 'lib/net/imap/response_parser.rb', line 992
alias env_from nlist__address
#env_in_reply_to
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 977
alias env_in_reply_to nstring
#env_message_id
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 978
alias nstring
#env_reply_to
Alias for #nlist__address.
# File 'lib/net/imap/response_parser.rb', line 994
alias env_reply_to nlist__address
#env_sender
Alias for #nlist__address.
# File 'lib/net/imap/response_parser.rb', line 993
alias env_sender nlist__address
#env_subject
Alias for #nstring.
# File 'lib/net/imap/response_parser.rb', line 976
alias env_subject nstring
#env_to
Alias for #nlist__address.
# File 'lib/net/imap/response_parser.rb', line 995
alias env_to nlist__address
#envelope
RFC3501 & RFC9051:
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--id ")"
# File 'lib/net/imap/response_parser.rb', line 952
def envelope @lex_state = EXPR_DATA lpar; date = env_date SP!; subject = env_subject SP!; from = env_from SP!; sender = env_sender SP!; reply_to = env_reply_to SP!; to = env_to SP!; cc = env_cc SP!; bcc = env_bcc SP!; in_reply_to = env_in_reply_to SP!; = rpar Envelope.new(date, subject, from, sender, reply_to, to, cc, bcc, in_reply_to, ) ensure @lex_state = EXPR_BEG end
#esearch_response(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 772
alias esearch_response response_data__unhandled
#expunged_resp(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 773
alias expunged_resp response_data__unhandled
#flag_list
flag-list = “(” [flag *(SP flag)] “)”
# File 'lib/net/imap/response_parser.rb', line 1906
def flag_list if (match = accept_re(Patterns::FLAG_LIST)) match[1].split(nil) .map! { _1.delete_prefix!("\\") ? _1.capitalize.to_sym : _1 } else quirky__flag_list "flags-list" end end
#flag_perm__list
“(” [flag-perm *(SP flag-perm)] “)”
# File 'lib/net/imap/response_parser.rb', line 1916
def flag_perm__list if (match = accept_re(Patterns::FLAG_PERM_LIST)) match[1].split(nil) .map! { _1.delete_prefix!("\\") ? _1.capitalize.to_sym : _1 } else quirky__flag_list "PERMANENTFLAGS flag-perm list" end end
#header_list
header-list = “(” header-fld-name *(SP header-fld-name) “)”
# File 'lib/net/imap/response_parser.rb', line 1304
def header_list str = +"" str << lpar << header_fld_name str << " " << header_fld_name while SP? str << rpar end
#id_response
# File 'lib/net/imap/response_parser.rb', line 1633
def id_response token = match(T_ATOM) name = token.value.upcase match(T_SPACE) token = match(T_LPAR, T_NIL) if token.symbol == T_NIL return UntaggedResponse.new(name, nil, @str) else data = {} while true token = lookahead case token.symbol when T_RPAR shift_token break when T_SPACE shift_token next else key = string match(T_SPACE) val = nstring data[key] = val end end return UntaggedResponse.new(name, data, @str) end end
#label(word)
# File 'lib/net/imap/response_parser.rb', line 516
def label(word) (val = tagged_ext_label) == word and return val parse_error("unexpected atom %p, expected %p instead", val, word) end
#label_in(*labels)
# File 'lib/net/imap/response_parser.rb', line 523
def label_in(*labels) lbl = tagged_ext_label and labels.include?(lbl) and return lbl parse_error("unexpected atom %p, expected one of %s instead", lbl, labels.join(" or ")) end
#language_data(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 778
alias language_data response_data__unhandled
#listrights_data(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 775
alias listrights_data response_data__unhandled
#lookahead_body?
# File 'lib/net/imap/response_parser.rb', line 1019
alias lookahead_body? lookahead_lpar?
#lookahead_thread_list? Also known as: #lookahead_thread_nested?
# File 'lib/net/imap/response_parser.rb', line 1482
alias lookahead_thread_list? lookahead_lpar?
#mailbox_data__exists
Alias for #response_data__simple_numeric.
# File 'lib/net/imap/response_parser.rb', line 842
alias mailbox_data__exists response_data__simple_numeric
#mailbox_data__flags
# File 'lib/net/imap/response_parser.rb', line 1339
def mailbox_data__flags name = label("FLAGS") SP! UntaggedResponse.new(name, flag_list, @str) end
#mailbox_data__list Also known as: #mailbox_data__lsub, #mailbox_data__xlist
# File 'lib/net/imap/response_parser.rb', line 1345
def mailbox_data__list name = label_in("LIST", "LSUB", "XLIST") SP! UntaggedResponse.new(name, mailbox_list, @str) end
#mailbox_data__lsub
Alias for #mailbox_data__list.
# File 'lib/net/imap/response_parser.rb', line 1350
alias mailbox_data__lsub mailbox_data__list
#mailbox_data__recent
Alias for #response_data__simple_numeric.
# File 'lib/net/imap/response_parser.rb', line 843
alias mailbox_data__recent response_data__simple_numeric
#mailbox_data__search Also known as: #sort_data
RFC3501:
mailbox-data = "SEARCH" *(SP nz-number) / ...
RFC5256: SORT
sort-data = "SORT" *(SP nz-number)
RFC7162: CONDSTORE, QRESYNC
mailbox-data =/ "SEARCH" [1*(SP nz-number) SP
search-sort-mod-seq]
sort-data = "SORT" [1*(SP nz-number) SP
search-sort-mod-seq]
; Updates the SORT response from RFC 5256.
search-sort-mod-seq = "(" "MODSEQ" SP mod-sequence-value ")"
RFC9051:
mailbox-data = obsolete-search-response / ...
obsolete-search-response = "SEARCH" *(SP nz-number)
# File 'lib/net/imap/response_parser.rb', line 1457
def mailbox_data__search name = label_in("SEARCH", "SORT") data = [] while _ = SP? && nz_number? do data << _ end if lpar? label("MODSEQ"); SP! modseq = mod_sequence_value rpar end data = SearchResult.new(data, modseq: modseq) UntaggedResponse.new(name, data, @str) end
#mailbox_data__status
mailbox-data =/ “STATUS” SP mailbox SP “(” [status-att-list] “)”
# File 'lib/net/imap/response_parser.rb', line 1523
def mailbox_data__status resp_name = label("STATUS"); SP! mbox_name = mailbox; SP! lpar; attr = status_att_list; rpar UntaggedResponse.new(resp_name, StatusData.new(mbox_name, attr), @str) end
#mailbox_data__xlist
Alias for #mailbox_data__list.
# File 'lib/net/imap/response_parser.rb', line 1351
alias mailbox_data__xlist mailbox_data__list
#mailbox_list
# File 'lib/net/imap/response_parser.rb', line 1358
def mailbox_list lpar; attr = peek_rpar? ? [] : mbx_list_flags; rpar SP!; delim = nquoted SP!; name = mailbox # TODO: mbox-list-extended MailboxList.new(attr, delim, name) end
#mbx_list_flags
# File 'lib/net/imap/response_parser.rb', line 1934
def mbx_list_flags match_re(Patterns::MBX_LIST_FLAGS, "mbx-list-flags")[1] .split(nil) .map! { _1.delete_prefix!("\\"); _1.capitalize.to_sym } end
#media_basic
Alias for #media_type.
# File 'lib/net/imap/response_parser.rb', line 1139
alias media_basic media_type # */* --- catchall
#media_message
Alias for #media_type.
# File 'lib/net/imap/response_parser.rb', line 1140
alias media_type # message/rfc822, message/global
#media_subtype
text/*
# File 'lib/net/imap/response_parser.rb', line 1143
alias media_subtype case_insensitive__string
#media_text
Alias for #media_type.
# File 'lib/net/imap/response_parser.rb', line 1141
alias media_text media_type # text/*
#media_type Also known as: #media_basic, #media_message, #media_text
n.b. this handles both type and subtype
RFC-3501 vs RFC-9051:
media-basic = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" /
"MESSAGE" /
"VIDEO") DQUOTE) / string) SP media-subtype
media-basic = ((DQUOTE ("APPLICATION" / "AUDIO" / "IMAGE" /
"FONT" / "MESSAGE" / "MODEL" /
"VIDEO") DQUOTE) / string) SP media-subtype
media- = DQUOTE "MESSAGE" DQUOTE SP
DQUOTE "RFC822" DQUOTE
media- = DQUOTE "MESSAGE" DQUOTE SP
DQUOTE ("RFC822" / "GLOBAL") DQUOTE
RFC-3501 & RFC-9051:
media-text = DQUOTE "TEXT" DQUOTE SP media-subtype
media-subtype = string
# File 'lib/net/imap/response_parser.rb', line 1131
def media_type mtype = case_insensitive__string SP? or return mtype, nil # ??? quirky! msubtype = media_subtype return mtype, msubtype end
#message_data__converted(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 780
alias response_data__unhandled
#message_data__expunge
Alias for #response_data__simple_numeric.
# File 'lib/net/imap/response_parser.rb', line 841
alias response_data__simple_numeric
#message_data__fetch
message-data = nz-number SP (“EXPUNGE” / (“FETCH” SP msg-att))
#metadata_resp(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 777
alias response_data__unhandled
#mod_sequence_value Also known as: #permsg_modsequence
Alias for #nz_number64.
# File 'lib/net/imap/response_parser.rb', line 1963
alias mod_sequence_value nz_number64
#mod_sequence_valzer
Alias for #number64.
# File 'lib/net/imap/response_parser.rb', line 1972
alias mod_sequence_valzer number64
#msg_att(n)
RFC3501 & RFC9051:
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)] ")"
RFC5257 (ANNOTATE extension):
msg-att-dynamic =/ "ANNOTATION" SP
( "(" entry-att *(SP entry-att) ")" /
"(" entry *(SP entry) ")" )
RFC7162 (CONDSTORE extension):
msg-att-dynamic =/ fetch-mod-resp
fetch-mod-resp = "MODSEQ" SP "(" permsg-modsequence ")"
RFC8970 (PREVIEW extension):
msg-att-dynamic =/ "PREVIEW" SP nstring
RFC3501:
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
RFC3516 (BINARY extension):
msg-att-static =/ "BINARY" section-binary SP (nstring / literal8)
/ "BINARY.SIZE" section-binary SP number
RFC8514 (SAVEDATE extension):
msg-att-static =/ "SAVEDATE" SP (date-time / nil)
RFC8474 (OBJECTID extension):
msg-att-static =/ fetch-emailid-resp / fetch-threadid-resp
fetch-emailid-resp = "EMAILID" SP "(" objectid ")"
fetch-threadid-resp = "THREADID" SP ( "(" objectid ")" / nil )
RFC9051:
msg-att-static = "ENVELOPE" SP envelope /
"INTERNALDATE" SP date-time /
"RFC822.SIZE" SP number64 /
"BODY" ["STRUCTURE"] SP body /
"BODY" section ["<" number ">"] SP nstring /
"BINARY" section-binary SP (nstring / literal8) /
"BINARY.SIZE" section-binary SP number /
"UID" SP uniqueid
Re www.rfc-editor.org/errata/eid7246, I’m adding “offset” to the official “BINARY” ABNF, like so:
msg-att-static =/ "BINARY" section-binary ["<" number ">"] SP
(nstring / literal8)
# File 'lib/net/imap/response_parser.rb', line 892
def msg_att(n) lpar attr = {} while true name = msg_att__label; SP! val = case name when "UID" then uniqueid when "FLAGS" then flag_list when "BODY" then body when /\ABODY\[/ni then nstring when "BODYSTRUCTURE" then body when "ENVELOPE" then envelope when "INTERNALDATE" then date_time when "RFC822.SIZE" then number64 when /\ABINARY\[/ni then nstring8 # BINARY, IMAP4rev2 when /\ABINARY\.SIZE\[/ni then number # BINARY, IMAP4rev2 when "RFC822" then nstring # not in rev2 when "RFC822.HEADER" then nstring # not in rev2 when "RFC822.TEXT" then nstring # not in rev2 when "MODSEQ" then parens__modseq # CONDSTORE when "EMAILID" then parens__objectid # OBJECTID when "THREADID" then nparens__objectid # OBJECTID when "X-GM-MSGID" then x_gm_id # GMail when "X-GM-THRID" then x_gm_id # GMail when "X-GM-LABELS" then x_gm_labels # GMail else parse_error("unknown attribute `%s' for {%d}", name, n) end attr[name] = val break unless SP? break if lookahead_rpar? end rpar attr end
#msg_att__label
appends “[section]” and “<partial>” to the base label
# File 'lib/net/imap/response_parser.rb', line 929
def msg_att__label case (name = tagged_ext_label) when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni # ignoring "[]" fixes https://bugs.ruby-lang.org/issues/5620 lbra? and rbra when "BODY" peek_lbra? and name << section and peek_str?("<") and name << gt__number__lt # partial when "BINARY", "BINARY.SIZE" name << section_binary # see https://www.rfc-editor.org/errata/eid7246 and the note above peek_str?("<") and name << gt__number__lt # partial end name end
#myrights_data(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 776
alias myrights_data response_data__unhandled
#namespace
namespace = nil / “(” 1*namespace-descr “)”
# File 'lib/net/imap/response_parser.rb', line 1680
def namespace NIL? and return [] lpar list = [namespace_descr] list << namespace_descr until rpar? list end
#namespace_descr
# File 'lib/net/imap/response_parser.rb', line 1691
def namespace_descr lpar prefix = string; SP! delimiter = nquoted # n.b: should only accept single char extensions = namespace_response_extensions rpar Namespace.new(prefix, delimiter, extensions) end
#namespace_response
# File 'lib/net/imap/response_parser.rb', line 1668
def namespace_response name = label("NAMESPACE") @lex_state = EXPR_DATA data = Namespaces.new((SP!; namespace), (SP!; namespace), (SP!; namespace)) UntaggedResponse.new(name, data, @str) ensure @lex_state = EXPR_BEG end
#namespace_response_extensions
namespace-response-extensions = *namespace-response-extension namespace-response-extension = SP string SP
"(" string *(SP string) ")"
# File 'lib/net/imap/response_parser.rb', line 1703
def namespace_response_extensions data = {} while SP? name = string; SP! lpar data[name] ||= [] data[name] << string data[name] << string while SP? rpar end data end
#ndatetime
Alias for #nquoted.
# File 'lib/net/imap/response_parser.rb', line 1008
alias ndatetime nquoted
#next_token
# File 'lib/net/imap/response_parser.rb', line 2023
def next_token case @lex_state when EXPR_BEG if @str.index(BEG_REGEXP, @pos) @pos = $~.end(0) if $1 return Token.new(T_SPACE, $+) elsif $2 len = $+.to_i val = @str[@pos, len] @pos += len return Token.new(T_LITERAL8, val) elsif $3 && $7 # greedily match ATOM, prefixed with NUMBER, NIL, or PLUS. return Token.new(T_ATOM, $3) elsif $4 return Token.new(T_NIL, $+) elsif $5 return Token.new(T_NUMBER, $+) elsif $6 return Token.new(T_PLUS, $+) elsif $8 # match ATOM, without a NUMBER, NIL, or PLUS prefix return Token.new(T_ATOM, $+) elsif $9 return Token.new(T_QUOTED, Patterns.unescape_quoted($+)) elsif $10 return Token.new(T_LPAR, $+) elsif $11 return Token.new(T_RPAR, $+) elsif $12 return Token.new(T_BSLASH, $+) elsif $13 return Token.new(T_STAR, $+) elsif $14 return Token.new(T_LBRA, $+) elsif $15 return Token.new(T_RBRA, $+) elsif $16 len = $+.to_i val = @str[@pos, len] @pos += len return Token.new(T_LITERAL, val) elsif $17 return Token.new(T_PERCENT, $+) elsif $18 return Token.new(T_CRLF, $+) elsif $19 return Token.new(T_EOF, $+) else parse_error("[Net::IMAP BUG] BEG_REGEXP is invalid") end else @str.index(/\S*/n, @pos) parse_error("unknown token - %s", $&.dump) end when EXPR_DATA if @str.index(DATA_REGEXP, @pos) @pos = $~.end(0) if $1 return Token.new(T_SPACE, $+) elsif $2 return Token.new(T_NIL, $+) elsif $3 return Token.new(T_NUMBER, $+) elsif $4 return Token.new(T_QUOTED, Patterns.unescape_quoted($+)) elsif $5 len = $+.to_i val = @str[@pos, len] @pos += len return Token.new(T_LITERAL, val) elsif $6 return Token.new(T_LPAR, $+) elsif $7 return Token.new(T_RPAR, $+) else parse_error("[Net::IMAP BUG] DATA_REGEXP is invalid") end else @str.index(/\S*/n, @pos) parse_error("unknown token - %s", $&.dump) end else parse_error("invalid @lex_state - %s", @lex_state.inspect) end end
#nil_atom
# File 'lib/net/imap/response_parser.rb', line 2005
def nil_atom match(T_NIL) return nil end
#nlist__address Also known as: #env_from, #env_sender, #env_reply_to, #env_to, #env_cc, #env_bcc
env-from = “(” 1*address “)” / nil env-sender = “(” 1*address “)” / nil env-reply-to = “(” 1*address “)” / nil env-to = “(” 1*address “)” / nil env-cc = “(” 1*address “)” / nil env-bcc = “(” 1*address “)” / nil
#nparens__objectid
# File 'lib/net/imap/response_parser.rb', line 1983
def nparens__objectid; NIL? ? nil : parens__objectid end
#nquoted Also known as: #ndatetime
# File 'lib/net/imap/response_parser.rb', line 554
def nquoted NIL? ? nil : quoted end
#nstring Also known as: #env_date, #env_subject, #env_in_reply_to, #env_message_id, #body_fld_desc, #body_fld_id, #body_fld_loc, #body_fld_md5, #addr_adl, #addr_host, #addr_mailbox, #addr_name
nstring = string / nil
# File 'lib/net/imap/response_parser.rb', line 546
def nstring NIL? ? nil : string end
#nstring8
# File 'lib/net/imap/response_parser.rb', line 550
def nstring8 NIL? ? nil : string8 end
#number64 Also known as: #body_fld_lines, #mod_sequence_valzer
valid number ranges are not enforced by parser
number64 = 1*DIGIT
; Unsigned 63-bit integer
; (0 <= n <= 9,223,372,036,854,775,807)
# File 'lib/net/imap/response_parser.rb', line 621
alias number64 number
#number64?
# File 'lib/net/imap/response_parser.rb', line 622
alias number64? number?
#nz_number Also known as: #nz_number64, #uniqueid
valid number ranges are not enforced by parser
nz-number = digit-nz *DIGIT
; Non-zero unsigned 32-bit integer
; (0 < n < 4,294,967,296)
# File 'lib/net/imap/response_parser.rb', line 628
alias nz_number number
#nz_number64 Also known as: #mod_sequence_value
Alias for #nz_number.
# File 'lib/net/imap/response_parser.rb', line 635
alias nz_number64 nz_number
#nz_number?
# File 'lib/net/imap/response_parser.rb', line 629
alias nz_number? number?
#parens__modseq
# File 'lib/net/imap/response_parser.rb', line 1974
def parens__modseq; lpar; _ = permsg_modsequence; rpar; _ end
#parens__objectid
# File 'lib/net/imap/response_parser.rb', line 1982
def parens__objectid; lpar; _ = objectid; rpar; _ end
Raises ResponseParseError for unparsable strings.
#permsg_modsequence
Alias for #mod_sequence_value.
# File 'lib/net/imap/response_parser.rb', line 1968
alias permsg_modsequence mod_sequence_value
#quirky__flag_list(name)
This allows illegal “]” in flag names (Gmail), or “*” in a FLAGS response (greenmail).
# File 'lib/net/imap/response_parser.rb', line 1927
def quirky__flag_list(name) match_re(Patterns::QUIRKY_FLAGS_LIST, "quirks mode #{name}")[1] .scan(Patterns::QUIRKY_FLAG) .map! { _1.delete_prefix!("\\") ? _1.capitalize.to_sym : _1 } end
#quirky_SP?
Used when servers erroneously send an extra SP.
As of 2023-11-28, Outlook.com
(still) sends SP
between {address} in <tt>env-*</tt> lists.
# File 'lib/net/imap/response_parser.rb', line 1003
alias quirky_SP? SP?
#quota_response
# File 'lib/net/imap/response_parser.rb', line 1366
def quota_response # If quota never established, get back # `NO Quota root does not exist'. # If quota removed, get `()' after the # folder spec with no mention of `STORAGE'. token = match(T_ATOM) name = token.value.upcase match(T_SPACE) mailbox = astring match(T_SPACE) match(T_LPAR) token = lookahead case token.symbol when T_RPAR shift_token data = MailboxQuota.new(mailbox, nil, nil) return UntaggedResponse.new(name, data, @str) when T_ATOM shift_token match(T_SPACE) token = match(T_NUMBER) usage = token.value match(T_SPACE) token = match(T_NUMBER) quota = token.value match(T_RPAR) data = MailboxQuota.new(mailbox, usage, quota) return UntaggedResponse.new(name, data, @str) else parse_error("unexpected token %s", token.symbol) end end
#quotaroot_response
# File 'lib/net/imap/response_parser.rb', line 1399
def quotaroot_response # Similar to getquota, but only admin can use getquota. token = match(T_ATOM) name = token.value.upcase match(T_SPACE) mailbox = astring quotaroots = [] while true token = lookahead break unless token.symbol == T_SPACE shift_token quotaroots.push(astring) end data = MailboxQuotaRoot.new(mailbox, quotaroots) return UntaggedResponse.new(name, data, @str) end
#remaining_unparsed
reads all the way up until CRLF
# File 'lib/net/imap/response_parser.rb', line 764
def remaining_unparsed str = @str[@pos...-2] and @pos += str.bytesize str&.empty? ? nil : str end
#resp_code__capability
Alias for #capability__list.
# File 'lib/net/imap/response_parser.rb', line 1624
alias resp_code__capability capability__list
#resp_code_apnd__data
already matched: “APPENDUID”
UIDPLUS
ABNF
resp-code-apnd = "APPENDUID" SP nz-number SP append-uid
append-uid = uniqueid
append-uid =/ uid-set
; only permitted if client uses [MULTIAPPEND]
; to append multiple messages.
n.b, uniqueid ⊂ uid-set. To avoid inconsistent return types, we always match uid_set even if that returns a single-member array.
# File 'lib/net/imap/response_parser.rb', line 1855
def resp_code_apnd__data validity = number; SP! dst_uids = uid_set # uniqueid ⊂ uid-set UIDPlusData.new(validity, nil, dst_uids) end
#resp_code_copy__data
already matched: “COPYUID”
resp-code-copy = “COPYUID” SP nz-number SP uid-set SP uid-set
# File 'lib/net/imap/response_parser.rb', line 1864
def resp_code_copy__data validity = number; SP! src_uids = uid_set; SP! dst_uids = uid_set UIDPlusData.new(validity, src_uids, dst_uids) end
#resp_cond_auth
resp-cond-auth = (“OK” / “PREAUTH”) SP resp-text
NOTE: In the spirit of RFC9051 Appx E 23 (and to workaround existing servers), we don’t require a final SP and instead parse this as:
resp-cond-auth = ("OK" / "PREAUTH") [SP resp-text]
# File 'lib/net/imap/response_parser.rb', line 809
def resp_cond_auth UntaggedResponse.new(resp_cond_auth__name, SP? ? resp_text : ResponseText::EMPTY, @str) end
#resp_cond_auth__name
expects “OK” or “PREAUTH” and raises InvalidResponseError
on failure
# File 'lib/net/imap/response_parser.rb', line 530
def resp_cond_auth__name lbl = tagged_ext_label and AUTH_CONDS.include? lbl and return lbl raise InvalidResponseError, "bad response type %p, expected %s" % [ lbl, AUTH_CONDS.join(" or ") ] end
#resp_cond_bye
resp-cond-bye = “BYE” SP resp-text
NOTE: In the spirit of RFC9051 Appx E 23 (and to workaround existing servers), we don’t require a final SP and instead parse this as:
resp-cond-bye = "BYE" [SP resp-text]
# File 'lib/net/imap/response_parser.rb', line 821
def resp_cond_bye UntaggedResponse.new(label(BYE), SP? ? resp_text : ResponseText::EMPTY, @str) end
#resp_cond_state
# File 'lib/net/imap/response_parser.rb', line 795
def resp_cond_state [resp_cond_state__name, SP? ? resp_text : ResponseText::EMPTY] end
#resp_cond_state__name
expects “OK” or “NO” or “BAD” and raises InvalidResponseError
on failure
# File 'lib/net/imap/response_parser.rb', line 538
def resp_cond_state__name lbl = tagged_ext_label and RESP_COND_STATES.include? lbl and return lbl raise InvalidResponseError, "bad response type %p, expected %s" % [ lbl, RESP_COND_STATES.join(" or ") ] end
#resp_cond_state__untagged
# File 'lib/net/imap/response_parser.rb', line 799
def resp_cond_state__untagged UntaggedResponse.new(*resp_cond_state, @str) end
#resp_text
# File 'lib/net/imap/response_parser.rb', line 1739
def resp_text if lbra? code = resp_text_code; rbra ResponseText.new(code, SP? && text? || "") else ResponseText.new(nil, text? || "") end end
#resp_text_code
RFC3501 (See www.rfc-editor.org/errata/rfc3501):
resp-text-code = "ALERT" /
"BADCHARSET" [SP "(" charset *(SP charset) ")" ] /
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 "]">]
capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev1"
*(SP capability)
RFC5530:
resp-text-code =/ "UNAVAILABLE" / "AUTHENTICATIONFAILED" /
"AUTHORIZATIONFAILED" / "EXPIRED" /
"PRIVACYREQUIRED" / "CONTACTADMIN" / "NOPERM" /
"INUSE" / "EXPUNGEISSUED" / "CORRUPTION" /
"SERVERBUG" / "CLIENTBUG" / "CANNOT" /
"LIMIT" / "OVERQUOTA" / "ALREADYEXISTS" /
"NONEXISTENT"
RFC9051:
resp-text-code = "ALERT" /
"BADCHARSET" [SP "(" charset *(SP charset) ")" ] /
capability-data / "PARSE" /
"PERMANENTFLAGS" SP "(" [flag-perm *(SP flag-perm)] ")" /
"READ-ONLY" / "READ-WRITE" / "TRYCREATE" /
"UIDNEXT" SP nz-number / "UIDVALIDITY" SP nz-number /
resp-code-apnd / resp-code-copy / "UIDNOTSTICKY" /
"UNAVAILABLE" / "AUTHENTICATIONFAILED" /
"AUTHORIZATIONFAILED" / "EXPIRED" /
"PRIVACYREQUIRED" / "CONTACTADMIN" / "NOPERM" /
"INUSE" / "EXPUNGEISSUED" / "CORRUPTION" /
"SERVERBUG" / "CLIENTBUG" / "CANNOT" /
"LIMIT" / "OVERQUOTA" / "ALREADYEXISTS" /
"NONEXISTENT" / "NOTSAVED" / "HASCHILDREN" /
"CLOSED" /
"UNKNOWN-CTE" /
atom [SP 1*<any TEXT-CHAR except "]">]
capability-data = "CAPABILITY" *(SP capability) SP "IMAP4rev2"
*(SP capability)
RFC4315 (UIDPLUS), RFC9051 (IMAP4rev2):
resp-code-apnd = "APPENDUID" SP nz-number SP append-uid
resp-code-copy = "COPYUID" SP nz-number SP uid-set SP uid-set
resp-text-code =/ resp-code-apnd / resp-code-copy / "UIDNOTSTICKY"
RFC7162 (CONDSTORE):
resp-text-code =/ "HIGHESTMODSEQ" SP mod-sequence-value /
"NOMODSEQ" /
"MODIFIED" SP sequence-set
RFC7162 (QRESYNC):
resp-text-code =/ "CLOSED"
RFC8474: OBJECTID
resp-text-code =/ "MAILBOXID" SP "(" objectid ")"
# File 'lib/net/imap/response_parser.rb', line 1803
def resp_text_code name = resp_text_code__name data = case name when "CAPABILITY" then resp_code__capability when "PERMANENTFLAGS" then SP? ? flag_perm__list : [] when "UIDNEXT" then SP!; nz_number when "UIDVALIDITY" then SP!; nz_number when "UNSEEN" then SP!; nz_number # rev1 only when "APPENDUID" then SP!; resp_code_apnd__data # rev2, UIDPLUS when "COPYUID" then SP!; resp_code_copy__data # rev2, UIDPLUS when "BADCHARSET" then SP? ? charset__list : [] when "ALERT", "PARSE", "READ-ONLY", "READ-WRITE", "TRYCREATE", "UNAVAILABLE", "AUTHENTICATIONFAILED", "AUTHORIZATIONFAILED", "EXPIRED", "PRIVACYREQUIRED", "CONTACTADMIN", "NOPERM", "INUSE", "EXPUNGEISSUED", "CORRUPTION", "SERVERBUG", "CLIENTBUG", "CANNOT", "LIMIT", "OVERQUOTA", "ALREADYEXISTS", "NONEXISTENT", "CLOSED", "NOTSAVED", "UIDNOTSTICKY", "UNKNOWN-CTE", "HASCHILDREN" when "NOMODSEQ" then nil # CONDSTORE when "HIGHESTMODSEQ" then SP!; mod_sequence_value # CONDSTORE when "MODIFIED" then SP!; sequence_set # CONDSTORE when "MAILBOXID" then SP!; parens__objectid # RFC8474: OBJECTID else SP? and text_chars_except_rbra end ResponseCode.new(name, data) end
#response
- RFC3501 & RFC9051:
-
response = *(continue-req / response-data) response-done
For simplicity, response isn’t interpreted as the combination of the three response types, but instead represents any individual server response. Our simplified interpretation is defined as:
response = continue-req | response_data | response-tagged
n.b: our “response-tagged” definition parses “greeting” too.
# File 'lib/net/imap/response_parser.rb', line 657
def response resp = case lookahead!(T_PLUS, T_STAR, *TAG_TOKENS).symbol when T_PLUS then continue_req when T_STAR then response_data else response_tagged end accept_spaces # QUIRKY: Ignore trailing space (MS Exchange Server?) CRLF! EOF! resp end
#response_data
- RFC3501:
-
response-data = “*” SP (resp-cond-state / resp-cond-bye /
mailbox-data / -data / capability-data) CRLF
- RFC4466:
-
response-data = “*” SP response-payload CRLF response-payload = resp-cond-state / resp-cond-bye /
mailbox-data / -data / capability-data
RFC5161 (ENABLE capability):
response-data =/ "*" SP enable-data CRLF
RFC5255 (LANGUAGE capability)
response-payload =/ language-data
RFC5255 (I18NLEVEL=1 and I18NLEVEL=2 capabilities)
response-payload =/ comparator-data
- RFC9051:
-
response-data = “*” SP (resp-cond-state / resp-cond-bye /
mailbox-data / -data / capability-data / enable-data) CRLF
- merging in greeting and response-fatal:
-
greeting = “*” SP (resp-cond-auth / resp-cond-bye) CRLF response-fatal = “*” SP resp-cond-bye CRLF response-data =/ “*” SP (resp-cond-auth / resp-cond-bye) CRLF
- removing duplicates, this is simply
-
response-payload =/ resp-cond-auth
TODO: remove resp-cond-auth and handle greeting separately
# File 'lib/net/imap/response_parser.rb', line 709
def response_data STAR!; SP! m = peek_re(RE_RESPONSE_TYPE) or parse_error("unparsable response") case m["type"].upcase when "OK" then resp_cond_state__untagged # RFC3501, RFC9051 when "FETCH" then # RFC3501, RFC9051 when "EXPUNGE" then # RFC3501, RFC9051 when "EXISTS" then mailbox_data__exists # RFC3501, RFC9051 when "ESEARCH" then esearch_response # RFC4731, RFC9051, etc when "VANISHED" then expunged_resp # RFC7162 when "UIDFETCH" then uidfetch_resp # (draft) UIDONLY when "SEARCH" then mailbox_data__search # RFC3501 (obsolete) when "CAPABILITY" then capability_data__untagged # RFC3501, RFC9051 when "FLAGS" then mailbox_data__flags # RFC3501, RFC9051 when "LIST" then mailbox_data__list # RFC3501, RFC9051 when "STATUS" then mailbox_data__status # RFC3501, RFC9051 when "NAMESPACE" then namespace_response # RFC2342, RFC9051 when "ENABLED" then enable_data # RFC5161, RFC9051 when "BAD" then resp_cond_state__untagged # RFC3501, RFC9051 when "NO" then resp_cond_state__untagged # RFC3501, RFC9051 when "PREAUTH" then resp_cond_auth # RFC3501, RFC9051 when "BYE" then resp_cond_bye # RFC3501, RFC9051 when "RECENT" then mailbox_data__recent # RFC3501 (obsolete) when "SORT" then sort_data # RFC5256, RFC7162 when "THREAD" then thread_data # RFC5256 when "QUOTA" then quota_response # RFC2087, RFC9208 when "QUOTAROOT" then quotaroot_response # RFC2087, RFC9208 when "ID" then id_response # RFC2971 when "ACL" then acl_data # RFC4314 when "LISTRIGHTS" then listrights_data # RFC4314 when "MYRIGHTS" then myrights_data # RFC4314 when "METADATA" then # RFC5464 when "LANGUAGE" then language_data # RFC5255 when "COMPARATOR" then comparator_data # RFC5255 when "CONVERTED" then # RFC5259 when "LSUB" then mailbox_data__lsub # RFC3501 (obsolete) when "XLIST" then mailbox_data__xlist # deprecated when "NOOP" then response_data__noop else response_data__unhandled end end
#response_data__ignored Also known as: #response_data__noop
# File 'lib/net/imap/response_parser.rb', line 769
def response_data__ignored; response_data__unhandled(IgnoredResponse) end
#response_data__noop
Alias for #response_data__ignored.
# File 'lib/net/imap/response_parser.rb', line 770
alias response_data__noop response_data__ignored
#response_data__simple_numeric Also known as: #message_data__expunge, #mailbox_data__exists, #mailbox_data__recent
# File 'lib/net/imap/response_parser.rb', line 835
def response_data__simple_numeric data = nz_number; SP! name = tagged_ext_label UntaggedResponse.new(name, data, @str) end
#response_data__unhandled(klass = UntaggedResponse) Also known as: #esearch_response, #expunged_resp, #uidfetch_resp, #listrights_data, #myrights_data, #metadata_resp, #language_data, #comparator_data, #message_data__converted
# File 'lib/net/imap/response_parser.rb', line 751
def response_data__unhandled(klass = UntaggedResponse) num = number?; SP? type = tagged_ext_label; SP? text = remaining_unparsed data = if num && text then UnparsedNumericResponseData.new(num, text) elsif text then UnparsedData.new(text) else num end klass.new(type, data, @str) end
#response_tagged
# File 'lib/net/imap/response_parser.rb', line 784
def response_tagged TaggedResponse.new(tag, *(SP!; resp_cond_state), @str) end
#section
section = “[” [section-spec] “]”
# File 'lib/net/imap/response_parser.rb', line 1268
def section str = +lbra str << section_spec unless peek_rbra? str << rbra end
#section_binary
section-binary = “[” [section-part] “]”
# File 'lib/net/imap/response_parser.rb', line 1275
def section_binary str = +lbra str << section_part unless peek_rbra? str << rbra end
#section_spec
section-spec = section-msgtext / (section-part [“.” section-text]) section-msgtext = “HEADER” /
"HEADER.FIELDS" [".NOT"] SP header-list /
"TEXT"
; top-level or MESSAGE/RFC822 or
; MESSAGE/GLOBAL part
section-part = nz-number *(“.” nz-number)
; body part reference.
; Allows for accessing nested body parts.
section-text = section-msgtext / “MIME”
; text other than actual body part (headers,
; etc.)
n.b: we could “cheat” here and just grab all text inside the brackets, but literals would need special treatment.
# File 'lib/net/imap/response_parser.rb', line 1296
def section_spec str = "".b str << atom # grabs everything up to "SP header-list" or "]" str << " " << header_list if SP? str end
#sequence_set
sequence-set = (seq-number / seq-range) [“,” sequence-set]
sequence-set =/ seq-last-command
; Allow for "result of the last command"
; indicator.
seq-last-command = "$"
note: doesn’t match seq-last-command
# File 'lib/net/imap/response_parser.rb', line 471
def sequence_set str = combine_adjacent(*SEQUENCE_SET_TOKENS) if Patterns::SEQUENCE_SET_STR.match?(str) SequenceSet[str] else parse_error("unexpected atom %p, expected sequence-set", str) end end
#sort_data
Alias for #mailbox_data__search.
# File 'lib/net/imap/response_parser.rb', line 1469
alias sort_data mailbox_data__search
#status_att_list
# File 'lib/net/imap/response_parser.rb', line 1534
def status_att_list attrs = [status_att_val] while SP? do attrs << status_att_val end attrs.to_h end
#status_att_val
RFC3501 Errata: status-att-val = (“MESSAGES” SP number) / (“RECENT” SP number) /
("UIDNEXT" SP nz-number) / ("UIDVALIDITY" SP nz-number) /
("UNSEEN" SP number)
RFC4466: status-att-val = (“MESSAGES” SP number) /
("RECENT" SP number) /
("UIDNEXT" SP nz-number) /
("UIDVALIDITY" SP nz-number) /
("UNSEEN" SP number)
;; Extensions to the STATUS responses
;; should extend this production.
;; Extensions should use the generic
;; syntax defined by tagged-ext.
RFC9051: status-att-val = (“MESSAGES” SP number) /
("UIDNEXT" SP nz-number) /
("UIDVALIDITY" SP nz-number) /
("UNSEEN" SP number) /
("DELETED" SP number) /
("SIZE" SP number64)
; Extensions to the STATUS responses
; should extend this production.
; Extensions should use the generic
; syntax defined by tagged-ext.
RFC7162: status-att-val =/ “HIGHESTMODSEQ” SP mod-sequence-valzer
;; Extends non-terminal defined in [RFC4466].
;; Value 0 denotes that the mailbox doesn't
;; support persistent mod-sequences
;; as described in Section 3.1.2.2.
RFC7889: status-att-val =/ “APPENDLIMIT” SP (number / nil)
;; status-att-val is defined in RFC 4466
RFC8438: status-att-val =/ “SIZE” SP number64 RFC8474: status-att-val =/ “MAILBOXID” SP “(” objectid “)”
; follows tagged-ext production from [RFC4466]
# File 'lib/net/imap/response_parser.rb', line 1579
def status_att_val key = tagged_ext_label SP! val = case key when "MESSAGES" then number # RFC3501, RFC9051 when "UNSEEN" then number # RFC3501, RFC9051 when "DELETED" then number # RFC3501, RFC9051 when "UIDNEXT" then nz_number # RFC3501, RFC9051 when "UIDVALIDITY" then nz_number # RFC3501, RFC9051 when "RECENT" then number # RFC3501 (obsolete) when "SIZE" then number64 # RFC8483, RFC9051 when "HIGHESTMODSEQ" then mod_sequence_valzer # RFC7162 when "MAILBOXID" then parens__objectid # RFC8474 else number? || ExtensionData.new(tagged_ext_val) end [key, val] end
#tag
# File 'lib/net/imap/response_parser.rb', line 492
def tag; combine_adjacent(*TAG_TOKENS) end
#tagged_ext_comp
tagged-ext-comp = astring /
tagged-ext-comp *(SP tagged-ext-comp) /
"(" tagged-ext-comp ")"
; Extensions that follow this general
; syntax should use nstring instead of
; astring when appropriate in the context
; of the extension.
; Note that a set or a "number"
; can always be represented as an "atom".
; A URL should be represented as
; a "quoted" string.
# File 'lib/net/imap/response_parser.rb', line 574
def tagged_ext_comp vals = [] while true vals << case lookahead!(*ASTRING_TOKENS, T_LPAR).symbol when T_LPAR then lpar; ary = tagged_ext_comp; rpar; ary when T_NUMBER then number else astring end SP? or break end vals end
#tagged_ext_simple
tagged-ext-simple is a subset of atom TODO: recognize sequence-set in the lexer
tagged-ext-simple = sequence-set / number / number64
# File 'lib/net/imap/response_parser.rb', line 591
def tagged_ext_simple number? || sequence_set end
#tagged_ext_val
tagged-ext-val = tagged-ext-simple /
"(" [tagged-ext-comp] ")"
# File 'lib/net/imap/response_parser.rb', line 597
def tagged_ext_val if lpar? _ = peek_rpar? ? [] : tagged_ext_comp rpar _ else tagged_ext_simple end end
#text_chars_except_rbra
1*<any TEXT-CHAR except “]”>
# File 'lib/net/imap/response_parser.rb', line 1834
def text_chars_except_rbra match_re(CTEXT_REGEXP, '1*<any TEXT-CHAR except "]">')[0] end
#thread_data
# File 'lib/net/imap/response_parser.rb', line 1473
def thread_data name = label("THREAD") threads = [] if SP? threads << thread_list while lookahead_thread_list? end UntaggedResponse.new(name, threads, @str) end
#thread_list
# File 'lib/net/imap/response_parser.rb', line 1487
def thread_list lpar thread = if lookahead_thread_nested? ThreadMember.new(nil, thread_nested) else thread_members end rpar thread end
#thread_members
# File 'lib/net/imap/response_parser.rb', line 1500
def thread_members members = [] members << nz_number # thread root while SP? case lookahead!(T_NUMBER, T_LPAR).symbol when T_NUMBER then members << nz_number else nested = thread_nested; break end end members.reverse.inject(nested || []) {|subthreads, number| [ThreadMember.new(number, subthreads)] }.first end
#thread_nested
# File 'lib/net/imap/response_parser.rb', line 1516
def thread_nested nested = [thread_list, thread_list] while lookahead_thread_list? do nested << thread_list end nested end
#uid_set
# File 'lib/net/imap/response_parser.rb', line 1993
def uid_set token = match(T_NUMBER, T_ATOM) case token.symbol when T_NUMBER then [Integer(token.value)] when T_ATOM token.value.split(",").flat_map {|range| range = range.split(":").map {|uniqueid| Integer(uniqueid) } range.size == 1 ? range : Range.new(range.min, range.max).to_a } end end
#uidfetch_resp(klass = UntaggedResponse)
Alias for #response_data__unhandled.
# File 'lib/net/imap/response_parser.rb', line 774
alias uidfetch_resp response_data__unhandled
#uniqueid
Alias for #nz_number.
# File 'lib/net/imap/response_parser.rb', line 640
alias uniqueid nz_number
#x_gm_id
valid number ranges are not enforced by parser
a 64-bit unsigned integer and is the decimal equivalent for the ID hex string used in the web interface and the Gmail API.
# File 'lib/net/imap/response_parser.rb', line 646
alias x_gm_id number
#x_gm_label
#x_gm_labels
# File 'lib/net/imap/response_parser.rb', line 1944
def x_gm_labels lpar; return [] if rpar? labels = [] labels << x_gm_label labels << x_gm_label while SP? rpar labels end