Class: Net::IMAP::ResponseParser
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Inherits: | Object |
Defined in: | lib/net/imap/response_parser.rb |
Overview
Parses an IMAP server response.
Constant Summary
-
ADDRESS_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 1216/\G\ (?# 1: NAME )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ (?# 2: ROUTE )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ (?# 3: MAILBOX )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)") \ (?# 4: HOST )(?:NIL|"((?:[^\x80-\xff\x00\r\n"\\]|\\["\\])*)")\ \)/ni
-
ASTRING_CHARS_TOKENS =
Internal use only
ASTRING-CHAR = ATOM-CHAR / resp-specials resp-specials = “]”
[*ATOM_TOKENS, T_RBRA]
-
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
# File 'lib/net/imap/response_parser.rb', line 60/\G(?:\ (?# 1: SPACE )( +)|\ (?# 2: NIL )(NIL)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\ (?# 3: NUMBER )(\d+)(?=[\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]+])|\ (?# 4: ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\\[\]])|\ (?# 5: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\ (?# 6: LPAR )(\()|\ (?# 7: RPAR )(\))|\ (?# 8: BSLASH )(\\)|\ (?# 9: STAR )(\*)|\ (?# 10: LBRA )(\[)|\ (?# 11: RBRA )(\])|\ (?# 12: LITERAL )\{(\d+)\}\r\n|\ (?# 13: PLUS )(\+)|\ (?# 14: PERCENT )(%)|\ (?# 15: CRLF )(\r\n)|\ (?# 16: EOF )(\z))/ni
-
CTEXT_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 94/\G(?:\ (?# 1: TEXT )([^\x00\r\n\]]*))/ni
-
DATA_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 78/\G(?:\ (?# 1: SPACE )( )|\ (?# 2: NIL )(NIL)|\ (?# 3: NUMBER )(\d+)|\ (?# 4: QUOTED )"((?:[^\x00\r\n"\\]|\\["\\])*)"|\ (?# 5: LITERAL )\{(\d+)\}\r\n|\ (?# 6: LPAR )(\()|\ (?# 7: RPAR )(\)))/ni
-
EXPR_BEG =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 36:EXPR_BEG
-
EXPR_CTEXT =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 40:EXPR_CTEXT
-
EXPR_DATA =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 37:EXPR_DATA
-
EXPR_RTEXT =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 39:EXPR_RTEXT
-
EXPR_TEXT =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 38:EXPR_TEXT
-
FLAG_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 1250/\ (?# FLAG )\\([^\x80-\xff(){ \x00-\x1f\x7f%"\\]+)|\ (?# ATOM )([^\x80-\xff(){ \x00-\x1f\x7f%*"\\]+)/n
-
RTEXT_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 90/\G(?:\ (?# 1: LBRA )(\[)|\ (?# 2: TEXT )([^\x00\r\n]*))/ni
-
SPACES_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 1394/\G */n
-
STRING_TOKENS =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 1298[T_QUOTED, T_LITERAL, T_NIL]
-
TEXT_REGEXP =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 87/\G(?:\ (?# 1: TEXT )([^\x00\r\n]*))/ni
-
T_ATOM =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 45:ATOM
-
T_BSLASH =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 49:BSLASH
-
T_CRLF =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 56:CRLF
-
T_EOF =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 57:EOF
-
T_LBRA =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 51:LBRA
-
T_LITERAL =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 53:LITERAL
-
T_LPAR =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 47:LPAR
-
T_NIL =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 43:NIL
-
T_NUMBER =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 44:NUMBER
-
T_PERCENT =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 55:PERCENT
-
T_PLUS =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 54:PLUS
-
T_QUOTED =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 46:QUOTED
-
T_RBRA =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 52:RBRA
-
T_RPAR =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 48:RPAR
-
T_SPACE =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 42:SPACE
-
T_STAR =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 50:STAR
-
T_TEXT =
Internal use only
# File 'lib/net/imap/response_parser.rb', line 58:TEXT
Class Method Summary
- .new ⇒ ResponseParser constructor
Instance Method Summary
-
#parse(str) ⇒ ContinuationRequest
Raises ResponseParseError for unparsable strings.
-
#accept(*args)
private
Internal use only
like match, but does not raise error on failure.
-
#accept_space
private
Internal use only
This advances @pos directly so it’s safe before changing @lex_state.
-
#accept_spaces
private
Internal use only
The RFC is very strict about this and usually we should be too.
- #address private Internal use only
- #address_list private Internal use only
- #astring private Internal use only
- #astring_chars private Internal use only
- #atom private Internal use only
- #body private Internal use only
- #body_data private Internal use only
- #body_ext_1part private Internal use only
- #body_ext_mpart private Internal use only
- #body_extension private Internal use only
- #body_extensions private Internal use only
- #body_fields private Internal use only
- #body_fld_dsp private Internal use only
- #body_fld_lang private Internal use only
- #body_fld_param private Internal use only
- #body_type_1part private Internal use only
- #body_type_attachment private Internal use only
- #body_type_basic private Internal use only
- #body_type_mixed private Internal use only
- #body_type_mpart private Internal use only
- #body_type_msg private Internal use only
- #body_type_text private Internal use only
- #capability_data private Internal use only
- #capability_response private Internal use only
- #case_insensitive_string private Internal use only
- #charset private Internal use only
- #charset_list private Internal use only
- #combine_adjacent(*tokens) private Internal use only
- #continue_req private Internal use only
- #envelope private Internal use only
- #envelope_data private Internal use only
- #flag_list private Internal use only
- #flags_data private Internal use only
- #flags_response private Internal use only
- #format_string(str) private Internal use only
- #getacl_response private Internal use only
- #getquota_response private Internal use only
- #getquotaroot_response private Internal use only
- #id_response private Internal use only
- #ignored_response private Internal use only
- #internaldate_data private Internal use only
- #list_response private Internal use only
- #lookahead private Internal use only
- #mailbox_list private Internal use only
- #match(*args, lex_state: @lex_state) private Internal use only
- #media_type private Internal use only
- #modseq_data private Internal use only
- #msg_att(n) private Internal use only
- #namespace private Internal use only
- #namespace_response private Internal use only
- #namespace_response_extensions private Internal use only
- #namespaces private Internal use only
- #next_token private Internal use only
- #nil_atom private Internal use only
- #nstring private Internal use only
- #number private Internal use only
- #numeric_response private Internal use only
- #parse_error(fmt, *args) private Internal use only
-
#resp_code_apnd__data
private
Internal use only
already matched: “APPENDUID”.
-
#resp_code_copy__data
private
Internal use only
already matched: “COPYUID”.
-
#resp_text
private
Internal use only
resp-text = [“[” resp-text-code “]” SP] text.
- #resp_text_code private Internal use only
- #response private Internal use only
- #response_cond private Internal use only
- #response_tagged private Internal use only
- #response_untagged private Internal use only
- #rfc822_size private Internal use only
- #rfc822_text private Internal use only
- #search_response private Internal use only
- #section private Internal use only
- #shift_token private Internal use only
- #status_response private Internal use only
- #string private Internal use only
- #string_token?(token) ⇒ Boolean private Internal use only
-
#text
private
Internal use only
text = 1*TEXT-CHAR TEXT-CHAR = <any CHAR except CR and LF>.
- #text_response private Internal use only
- #thread_branch(token) private Internal use only
- #thread_response private Internal use only
- #uid_data private Internal use only
-
#uid_set
private
Internal use only
RFC-4315 (UIDPLUS) or RFC9051 (IMAP4rev2):
Constructor Details
.new ⇒ ResponseParser
# File 'lib/net/imap/response_parser.rb', line 11
def initialize @str = nil @pos = nil @lex_state = nil @token = nil end
Instance Method Details
#accept(*args) (private)
like match, but does not raise error on failure.
returns and shifts token on successful match returns nil and leaves @token unshifted on no match
# File 'lib/net/imap/response_parser.rb', line 1440
def accept(*args) token = lookahead if args.include?(token.symbol) shift_token token end end
#accept_space (private)
This advances @pos directly so it’s safe before changing @lex_state.
# File 'lib/net/imap/response_parser.rb', line 1397
def accept_space if @token shift_token if @token.symbol == T_SPACE elsif @str[@pos] == " " @pos += 1 end end
#accept_spaces (private)
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 1409
def accept_spaces shift_token if @token&.symbol == T_SPACE if @str.index(SPACES_REGEXP, @pos) @pos = $~.end(0) end end
#address (private)
# File 'lib/net/imap/response_parser.rb', line 1223
def address match(T_LPAR) if @str.index(ADDRESS_REGEXP, @pos) # address does not include literal. @pos = $~.end(0) name = $1 route = $2 mailbox = $3 host = $4 for s in [name, route, mailbox, host] if s s.gsub!(/\\(["\\])/n, "\\1") end end else name = nstring match(T_SPACE) route = nstring match(T_SPACE) mailbox = nstring match(T_SPACE) host = nstring match(T_RPAR) end return Address.new(name, route, mailbox, host) end
#address_list (private)
# File 'lib/net/imap/response_parser.rb', line 1193
def address_list token = lookahead if token.symbol == T_NIL shift_token return nil else result = [] match(T_LPAR) while true token = lookahead case token.symbol when T_RPAR shift_token break when T_SPACE shift_token end result.push(address) end return result end end
#astring (private)
# File 'lib/net/imap/response_parser.rb', line 1279
def astring token = lookahead if string_token?(token) return string else return astring_chars end end
#astring_chars (private)
# File 'lib/net/imap/response_parser.rb', line 1332
def astring_chars combine_adjacent(*ASTRING_CHARS_TOKENS) end
#atom (private)
# File 'lib/net/imap/response_parser.rb', line 1324
def atom -combine_adjacent(*ATOM_TOKENS) end
#body (private)
# File 'lib/net/imap/response_parser.rb', line 336
def body @lex_state = EXPR_DATA token = lookahead if token.symbol == T_NIL shift_token result = nil else match(T_LPAR) token = lookahead if token.symbol == T_LPAR result = body_type_mpart else result = body_type_1part end match(T_RPAR) end @lex_state = EXPR_BEG return result end
#body_data (private)
# File 'lib/net/imap/response_parser.rb', line 317
def body_data token = match(T_ATOM) name = token.value.upcase token = lookahead if token.symbol == T_SPACE shift_token return name, body end name.concat(section) token = lookahead if token.symbol == T_ATOM name.concat(token.value) shift_token end match(T_SPACE) data = nstring return name, data end
#body_ext_1part (private)
# File 'lib/net/imap/response_parser.rb', line 523
def body_ext_1part token = lookahead if token.symbol == T_SPACE shift_token else return nil end md5 = nstring token = lookahead if token.symbol == T_SPACE shift_token else return md5 end disposition = body_fld_dsp token = lookahead if token.symbol == T_SPACE shift_token else return md5, disposition end language = body_fld_lang token = lookahead if token.symbol == T_SPACE shift_token else return md5, disposition, language end extension = body_extensions return md5, disposition, language, extension end
#body_ext_mpart (private)
# File 'lib/net/imap/response_parser.rb', line 559
def body_ext_mpart token = lookahead if token.symbol == T_SPACE shift_token else return nil end param = body_fld_param token = lookahead if token.symbol == T_SPACE shift_token else return param end disposition = body_fld_dsp token = lookahead if token.symbol == T_SPACE shift_token else return param, disposition end language = body_fld_lang token = lookahead if token.symbol == T_SPACE shift_token else return param, disposition, language end extension = body_extensions return param, disposition, language, extension end
#body_extension (private)
# File 'lib/net/imap/response_parser.rb', line 649
def body_extension token = lookahead case token.symbol when T_LPAR shift_token result = body_extensions match(T_RPAR) return result when T_NUMBER return number else return nstring end end
#body_extensions (private)
# File 'lib/net/imap/response_parser.rb', line 635
def body_extensions result = [] while true token = lookahead case token.symbol when T_RPAR return result when T_SPACE shift_token end result.push(body_extension) end end
#body_fields (private)
#body_fld_dsp (private)
# File 'lib/net/imap/response_parser.rb', line 595
def body_fld_dsp token = lookahead if token.symbol == T_NIL shift_token return nil end match(T_LPAR) dsp_type = case_insensitive_string match(T_SPACE) param = body_fld_param match(T_RPAR) return ContentDisposition.new(dsp_type, param) end
#body_fld_lang (private)
# File 'lib/net/imap/response_parser.rb', line 609
def body_fld_lang token = lookahead if token.symbol == T_LPAR shift_token result = [] while true token = lookahead case token.symbol when T_RPAR shift_token return result when T_SPACE shift_token end result.push(case_insensitive_string) end else lang = nstring if lang return lang.upcase else return lang end end end
#body_fld_param (private)
# File 'lib/net/imap/response_parser.rb', line 498
def body_fld_param token = lookahead if token.symbol == T_NIL shift_token return nil end match(T_LPAR) param = {} while true token = lookahead case token.symbol when T_RPAR shift_token break when T_SPACE shift_token end name = case_insensitive_string match(T_SPACE) val = string param[name] = val end return param end
#body_type_1part (private)
# File 'lib/net/imap/response_parser.rb', line 356
def body_type_1part token = lookahead case token.value when /\A(?:TEXT)\z/ni return body_type_text when /\A(?:MESSAGE)\z/ni return body_type_msg when /\A(?:ATTACHMENT)\z/ni return when /\A(?:MIXED)\z/ni return body_type_mixed else return body_type_basic end end
#body_type_attachment (private)
# File 'lib/net/imap/response_parser.rb', line 442
def mtype = case_insensitive_string match(T_SPACE) param = body_fld_param return BodyTypeAttachment.new(mtype, nil, param) end
#body_type_basic (private)
# File 'lib/net/imap/response_parser.rb', line 372
def body_type_basic mtype, msubtype = media_type token = lookahead if token.symbol == T_RPAR return BodyTypeBasic.new(mtype, msubtype) end match(T_SPACE) param, content_id, desc, enc, size = body_fields md5, disposition, language, extension = body_ext_1part return BodyTypeBasic.new(mtype, msubtype, param, content_id, desc, enc, size, md5, disposition, language, extension) end
#body_type_mixed (private)
# File 'lib/net/imap/response_parser.rb', line 449
def body_type_mixed mtype = "MULTIPART" msubtype = case_insensitive_string param, disposition, language, extension = body_ext_mpart return BodyTypeBasic.new(mtype, msubtype, param, nil, nil, nil, nil, nil, disposition, language, extension) end
#body_type_mpart (private)
# File 'lib/net/imap/response_parser.rb', line 456
def body_type_mpart parts = [] while true token = lookahead if token.symbol == T_SPACE shift_token break end parts.push(body) end mtype = "MULTIPART" msubtype = case_insensitive_string param, disposition, language, extension = body_ext_mpart return BodyTypeMultipart.new(mtype, msubtype, parts, param, disposition, language, extension) end
#body_type_msg (private)
# File 'lib/net/imap/response_parser.rb', line 401
def body_type_msg mtype, msubtype = media_type match(T_SPACE) param, content_id, desc, enc, size = body_fields token = lookahead if token.symbol == T_RPAR # If this is not message/rfc822, we shouldn't apply the RFC822 # spec to it. We should handle anything other than # message/rfc822 using multipart extension data [rfc3501] (i.e. # the data itself won't be returned, we would have to retrieve it # with BODYSTRUCTURE instead of with BODY # Also, sometimes a message/rfc822 is included as a large # attachment instead of having all of the other details # (e.g. attaching a .eml file to an email) if msubtype == "RFC822" return BodyTypeMessage.new(mtype, msubtype, param, content_id, desc, enc, size, nil, nil, nil, nil, nil, nil, nil) else return BodyTypeExtension.new(mtype, msubtype, param, content_id, desc, enc, size) end end match(T_SPACE) env = envelope match(T_SPACE) b = body match(T_SPACE) lines = number md5, disposition, language, extension = body_ext_1part return BodyTypeMessage.new(mtype, msubtype, param, content_id, desc, enc, size, env, b, lines, md5, disposition, language, extension) end
#body_type_text (private)
# File 'lib/net/imap/response_parser.rb', line 387
def body_type_text mtype, msubtype = media_type match(T_SPACE) param, content_id, desc, enc, size = body_fields match(T_SPACE) lines = number md5, disposition, language, extension = body_ext_1part return BodyTypeText.new(mtype, msubtype, param, content_id, desc, enc, size, lines, md5, disposition, language, extension) end
#capability_data (private)
#capability_response (private)
# File 'lib/net/imap/response_parser.rb', line 970
def capability_response token = match(T_ATOM) name = token.value.upcase match(T_SPACE) UntaggedResponse.new(name, capability_data, @str) end
#case_insensitive_string (private)
#charset (private)
See www.rfc-editor.org/errata/rfc3501
charset = atom / quoted
#charset_list (private)
#combine_adjacent(*tokens) (private)
# File 'lib/net/imap/response_parser.rb', line 1336
def combine_adjacent(*tokens) result = "".b while token = accept(*tokens) result << token.value end if result.empty? parse_error('unexpected token %s (expected %s)', lookahead.symbol, args.join(" or ")) end result end
#continue_req (private)
# File 'lib/net/imap/response_parser.rb', line 118
def continue_req match(T_PLUS) token = lookahead if token.symbol == T_SPACE shift_token return ContinuationRequest.new(resp_text, @str) else return ContinuationRequest.new(ResponseText.new(nil, ""), @str) end end
#envelope (private)
# File 'lib/net/imap/response_parser.rb', line 248
def envelope @lex_state = EXPR_DATA token = lookahead if token.symbol == T_NIL shift_token result = nil else match(T_LPAR) date = nstring match(T_SPACE) subject = nstring match(T_SPACE) from = address_list match(T_SPACE) sender = address_list match(T_SPACE) reply_to = address_list match(T_SPACE) to = address_list match(T_SPACE) cc = address_list match(T_SPACE) bcc = address_list match(T_SPACE) in_reply_to = nstring match(T_SPACE) = nstring match(T_RPAR) result = Envelope.new(date, subject, from, sender, reply_to, to, cc, bcc, in_reply_to, ) end @lex_state = EXPR_BEG return result end
#envelope_data (private)
#flag_list (private)
# File 'lib/net/imap/response_parser.rb', line 1254
def flag_list if @str.index(/\(([^)]*)\)/ni, @pos) @pos = $~.end(0) return $1.scan(FLAG_REGEXP).collect { |flag, atom| if atom atom else flag.capitalize.intern end } else parse_error("invalid flag list") end end
#flags_data (private)
#flags_response (private)
#format_string(str) (private)
# File 'lib/net/imap/response_parser.rb', line 699
def format_string(str) case str when "" return '""' when /[\x80-\xff\r\n]/n # literal return "{" + str.bytesize.to_s + "}" + CRLF + str when /[(){ \x00-\x1f\x7f%*"\\]/n # quoted string return '"' + str.gsub(/["\\]/n, "\\\\\\&") + '"' else # atom return str end end
#getacl_response (private)
# File 'lib/net/imap/response_parser.rb', line 824
def getacl_response 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
#getquota_response (private)
# File 'lib/net/imap/response_parser.rb', line 774
def getquota_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
#getquotaroot_response (private)
# File 'lib/net/imap/response_parser.rb', line 807
def getquotaroot_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
#id_response (private)
# File 'lib/net/imap/response_parser.rb', line 993
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
#ignored_response (private)
# File 'lib/net/imap/response_parser.rb', line 732
def ignored_response while lookahead.symbol != T_CRLF shift_token end return IgnoredResponse.new(@str) end
#internaldate_data (private)
#list_response (private)
# File 'lib/net/imap/response_parser.rb', line 753
def list_response token = match(T_ATOM) name = token.value.upcase match(T_SPACE) return UntaggedResponse.new(name, mailbox_list, @str) end
#lookahead (private)
# File 'lib/net/imap/response_parser.rb', line 1448
def lookahead @token ||= next_token end
#mailbox_list (private)
#match(*args, lex_state: @lex_state) (private)
# File 'lib/net/imap/response_parser.rb', line 1416
def match(*args, lex_state: @lex_state) if @token && lex_state != @lex_state parse_error("invalid lex_state change to %s with unconsumed token", lex_state) end begin @lex_state, original_lex_state = lex_state, @lex_state token = lookahead unless args.include?(token.symbol) parse_error('unexpected token %s (expected %s)', token.symbol.id2name, args.collect {|i| i.id2name}.join(" or ")) end shift_token return token ensure @lex_state = original_lex_state end end
#media_type (private)
# File 'lib/net/imap/response_parser.rb', line 474
def media_type mtype = case_insensitive_string token = lookahead if token.symbol != T_SPACE return mtype, nil end match(T_SPACE) msubtype = case_insensitive_string return mtype, msubtype end
#modseq_data (private)
#msg_att(n) (private)
# File 'lib/net/imap/response_parser.rb', line 203
def msg_att(n) match(T_LPAR) attr = {} while true token = lookahead case token.symbol when T_RPAR shift_token break when T_SPACE shift_token next end case token.value when /\A(?:ENVELOPE)\z/ni name, val = envelope_data when /\A(?:FLAGS)\z/ni name, val = flags_data when /\A(?:INTERNALDATE)\z/ni name, val = internaldate_data when /\A(?:RFC822(?:\.HEADER|\.TEXT)?)\z/ni name, val = rfc822_text when /\A(?:RFC822\.SIZE)\z/ni name, val = rfc822_size when /\A(?:BODY(?:STRUCTURE)?)\z/ni name, val = body_data when /\A(?:UID)\z/ni name, val = uid_data when /\A(?:MODSEQ)\z/ni name, val = modseq_data else parse_error("unknown attribute `%s' for {%d}", token.value, n) end attr[name] = val end return attr end
#namespace (private)
#namespace_response (private)
# File 'lib/net/imap/response_parser.rb', line 1022
def namespace_response @lex_state = EXPR_DATA token = lookahead token = match(T_ATOM) name = token.value.upcase match(T_SPACE) personal = namespaces match(T_SPACE) other = namespaces match(T_SPACE) shared = namespaces @lex_state = EXPR_BEG data = Namespaces.new(personal, other, shared) return UntaggedResponse.new(name, data, @str) end
#namespace_response_extensions (private)
# File 'lib/net/imap/response_parser.rb', line 1066
def namespace_response_extensions data = {} token = lookahead if token.symbol == T_SPACE shift_token name = match(T_QUOTED, T_LITERAL).value data[name] ||= [] match(T_SPACE) match(T_LPAR) loop do data[name].push match(T_QUOTED, T_LITERAL).value break unless lookahead.symbol == T_SPACE shift_token end match(T_RPAR) end data end
#namespaces (private)
# File 'lib/net/imap/response_parser.rb', line 1038
def namespaces token = lookahead # empty () is not allowed, so nil is functionally identical to empty. data = [] if token.symbol == T_NIL shift_token else match(T_LPAR) loop do data << namespace break unless lookahead.symbol == T_SPACE shift_token end match(T_RPAR) end data end
#next_token (private)
# File 'lib/net/imap/response_parser.rb', line 1456
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 return Token.new(T_NIL, $+) elsif $3 return Token.new(T_NUMBER, $+) elsif $4 return Token.new(T_ATOM, $+) elsif $5 return Token.new(T_QUOTED, $+.gsub(/\\(["\\])/n, "\\1")) elsif $6 return Token.new(T_LPAR, $+) elsif $7 return Token.new(T_RPAR, $+) elsif $8 return Token.new(T_BSLASH, $+) elsif $9 return Token.new(T_STAR, $+) elsif $10 return Token.new(T_LBRA, $+) elsif $11 return Token.new(T_RBRA, $+) elsif $12 len = $+.to_i val = @str[@pos, len] @pos += len return Token.new(T_LITERAL, val) elsif $13 return Token.new(T_PLUS, $+) elsif $14 return Token.new(T_PERCENT, $+) elsif $15 return Token.new(T_CRLF, $+) elsif $16 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, $+.gsub(/\\(["\\])/n, "\\1")) 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 when EXPR_TEXT if @str.index(TEXT_REGEXP, @pos) @pos = $~.end(0) if $1 return Token.new(T_TEXT, $+) else parse_error("[Net::IMAP BUG] TEXT_REGEXP is invalid") end else @str.index(/\S*/n, @pos) parse_error("unknown token - %s", $&.dump) end when EXPR_RTEXT if @str.index(RTEXT_REGEXP, @pos) @pos = $~.end(0) if $1 return Token.new(T_LBRA, $+) elsif $2 return Token.new(T_TEXT, $+) else parse_error("[Net::IMAP BUG] RTEXT_REGEXP is invalid") end else @str.index(/\S*/n, @pos) parse_error("unknown token - %s", $&.dump) end when EXPR_CTEXT if @str.index(CTEXT_REGEXP, @pos) @pos = $~.end(0) if $1 return Token.new(T_TEXT, $+) else parse_error("[Net::IMAP BUG] CTEXT_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 (private)
#nstring (private)
# File 'lib/net/imap/response_parser.rb', line 1269
def nstring token = lookahead if token.symbol == T_NIL shift_token return nil else return string end end
#number (private)
# File 'lib/net/imap/response_parser.rb', line 1359
def number token = lookahead if token.symbol == T_NIL shift_token return nil end token = match(T_NUMBER) return token.value.to_i end
#numeric_response (private)
# File 'lib/net/imap/response_parser.rb', line 187
def numeric_response n = number match(T_SPACE) token = match(T_ATOM) name = token.value.upcase case name when "EXISTS", "RECENT", "EXPUNGE" return UntaggedResponse.new(name, n, @str) when "FETCH" shift_token match(T_SPACE) data = FetchData.new(n, msg_att(n)) return UntaggedResponse.new(name, data, @str) end end
Raises ResponseParseError for unparsable strings.
#parse_error(fmt, *args) (private)
# File 'lib/net/imap/response_parser.rb', line 1575
def parse_error(fmt, *args) if IMAP.debug $stderr.printf("@str: %s\n", @str.dump) $stderr.printf("@pos: %d\n", @pos) $stderr.printf("@lex_state: %s\n", @lex_state) if @token $stderr.printf("@token.symbol: %s\n", @token.symbol) $stderr.printf("@token.value: %s\n", @token.value.inspect) end end raise ResponseParseError, format(fmt, *args) end
#resp_code_apnd__data (private)
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.
#resp_code_copy__data (private)
already matched: “COPYUID”
resp-code-copy = “COPYUID” SP nz-number SP uid-set SP uid-set
#resp_text (private)
resp-text = [“[” resp-text-code “]” SP] text
# File 'lib/net/imap/response_parser.rb', line 1092
def resp_text token = match(T_LBRA, T_TEXT, lex_state: EXPR_RTEXT) case token.symbol when T_LBRA code = resp_text_code match(T_RBRA) accept_space # violating RFC ResponseText.new(code, text) when T_TEXT ResponseText.new(nil, token.value) end end
#resp_text_code (private)
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 "]">]
UIDPLUS
ABNF
resp-text-code =/ resp-code-apnd / resp-code-copy / "UIDNOTSTICKY"
# File 'lib/net/imap/response_parser.rb', line 1119
def resp_text_code token = match(T_ATOM) name = token.value.upcase case name when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n result = ResponseCode.new(name, nil) when /\A(?:BADCHARSET)\z/n result = ResponseCode.new(name, charset_list) when /\A(?:CAPABILITY)\z/ni result = ResponseCode.new(name, capability_data) when /\A(?:PERMANENTFLAGS)\z/n match(T_SPACE) result = ResponseCode.new(name, flag_list) when /\A(?:UIDVALIDITY|UIDNEXT|UNSEEN)\z/n match(T_SPACE) result = ResponseCode.new(name, number) when /\A(?:APPENDUID)\z/n result = ResponseCode.new(name, resp_code_apnd__data) when /\A(?:COPYUID)\z/n result = ResponseCode.new(name, resp_code_copy__data) else token = lookahead if token.symbol == T_SPACE shift_token token = match(T_TEXT, lex_state: EXPR_CTEXT) result = ResponseCode.new(name, token.value) else result = ResponseCode.new(name, nil) end end return result end
#response (private)
# File 'lib/net/imap/response_parser.rb', line 99
def response token = lookahead case token.symbol when T_PLUS result = continue_req when T_STAR result = response_untagged else result = response_tagged end while lookahead.symbol == T_SPACE # Ignore trailing space for Microsoft Exchange Server shift_token end match(T_CRLF) match(T_EOF) return result end
#response_cond (private)
#response_tagged (private)
# File 'lib/net/imap/response_parser.rb', line 171
def response_tagged tag = astring_chars match(T_SPACE) token = match(T_ATOM) name = token.value.upcase match(T_SPACE) return TaggedResponse.new(tag, name, resp_text, @str) end
#response_untagged (private)
# File 'lib/net/imap/response_parser.rb', line 129
def response_untagged match(T_STAR) match(T_SPACE) token = lookahead if token.symbol == T_NUMBER return numeric_response elsif token.symbol == T_ATOM case token.value when /\A(?:OK|NO|BAD|BYE|PREAUTH)\z/ni return response_cond when /\A(?:FLAGS)\z/ni return flags_response when /\A(?:ID)\z/ni return id_response when /\A(?:LIST|LSUB|XLIST)\z/ni return list_response when /\A(?:NAMESPACE)\z/ni return namespace_response when /\A(?:QUOTA)\z/ni return getquota_response when /\A(?:QUOTAROOT)\z/ni return getquotaroot_response when /\A(?:ACL)\z/ni return getacl_response when /\A(?:SEARCH|SORT)\z/ni return search_response when /\A(?:THREAD)\z/ni return thread_response when /\A(?:STATUS)\z/ni return status_response when /\A(?:CAPABILITY)\z/ni return capability_response when /\A(?:NOOP)\z/ni return ignored_response else return text_response end else parse_error("unexpected token %s", token.symbol) end end
#rfc822_size (private)
#rfc822_text (private)
#search_response (private)
# File 'lib/net/imap/response_parser.rb', line 850
def search_response token = match(T_ATOM) name = token.value.upcase token = lookahead if token.symbol == T_SPACE shift_token data = [] while true token = lookahead case token.symbol when T_CRLF break when T_SPACE shift_token when T_NUMBER data.push(number) when T_LPAR # TODO: include the MODSEQ value in a response shift_token match(T_ATOM) match(T_SPACE) match(T_NUMBER) match(T_RPAR) end end else data = [] end return UntaggedResponse.new(name, data, @str) end
#section (private)
# File 'lib/net/imap/response_parser.rb', line 664
def section str = String.new token = match(T_LBRA) str.concat(token.value) token = match(T_ATOM, T_NUMBER, T_RBRA) if token.symbol == T_RBRA str.concat(token.value) return str end str.concat(token.value) token = lookahead if token.symbol == T_SPACE shift_token str.concat(token.value) token = match(T_LPAR) str.concat(token.value) while true token = lookahead case token.symbol when T_RPAR str.concat(token.value) shift_token break when T_SPACE shift_token str.concat(token.value) end str.concat(format_string(astring)) end end token = match(T_RBRA) str.concat(token.value) return str end
#shift_token (private)
# File 'lib/net/imap/response_parser.rb', line 1452
def shift_token @token = nil end
#status_response (private)
# File 'lib/net/imap/response_parser.rb', line 943
def status_response token = match(T_ATOM) name = token.value.upcase match(T_SPACE) mailbox = astring match(T_SPACE) match(T_LPAR) attr = {} while true token = lookahead case token.symbol when T_RPAR shift_token break when T_SPACE shift_token end token = match(T_ATOM) key = token.value.upcase match(T_SPACE) val = number attr[key] = val end data = StatusData.new(mailbox, attr) return UntaggedResponse.new(name, data, @str) end
#string (private)
#string_token?(token) ⇒ Boolean
(private)
# File 'lib/net/imap/response_parser.rb', line 1300
def string_token?(token) return STRING_TOKENS.include?(token.symbol) end
#text (private)
text = 1*TEXT-CHAR TEXT-CHAR = <any CHAR except CR and LF>
#text_response (private)
#thread_branch(token) (private)
# File 'lib/net/imap/response_parser.rb', line 908
def thread_branch(token) rootmember = nil lastmember = nil while true shift_token # ignore first T_LPAR token = lookahead case token.symbol when T_NUMBER # new member newmember = ThreadMember.new(number, []) if rootmember.nil? rootmember = newmember else lastmember.children << newmember end lastmember = newmember when T_SPACE # do nothing when T_LPAR if rootmember.nil? # dummy member lastmember = rootmember = ThreadMember.new(nil, []) end lastmember.children << thread_branch(token) when T_RPAR break end end return rootmember end
#thread_response (private)
# File 'lib/net/imap/response_parser.rb', line 881
def thread_response token = match(T_ATOM) name = token.value.upcase token = lookahead if token.symbol == T_SPACE threads = [] while true shift_token token = lookahead case token.symbol when T_LPAR threads << thread_branch(token) when T_CRLF break end end else # no member threads = [] end return UntaggedResponse.new(name, threads, @str) end
#uid_data (private)
#uid_set (private)
RFC-4315 (UIDPLUS) or RFC9051 (IMAP4rev2):
uid-set = (uniqueid / uid-range) *("," uid-set)
uid-range = (uniqueid ":" uniqueid)
; two uniqueid values and all values
; between these two regardless of order.
; Example: 2:4 and 4:2 are equivalent.
uniqueid = nz-number
; Strictly ascending
# File 'lib/net/imap/response_parser.rb', line 1377
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