Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 47 additions & 24 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2316,7 +2316,7 @@ def response_untagged
end

def response_tagged
tag = atom
tag = astring_chars
match(T_SPACE)
token = match(T_ATOM)
name = token.value.upcase
Expand Down Expand Up @@ -3114,19 +3114,23 @@ def capability_response
token = match(T_ATOM)
name = token.value.upcase
match(T_SPACE)
UntaggedResponse.new(name, capability_data, @str)
end

def capability_data
data = []
while true
token = lookahead
case token.symbol
when T_CRLF
when T_CRLF, T_RBRA
break
when T_SPACE
shift_token
next
end
data.push(atom.upcase)
end
return UntaggedResponse.new(name, data, @str)
data
end

def resp_text
Expand All @@ -3150,6 +3154,8 @@ def resp_text_code
case name
when /\A(?:ALERT|PARSE|READ-ONLY|READ-WRITE|TRYCREATE|NOMODSEQ)\z/n
result = ResponseCode.new(name, nil)
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)
Expand All @@ -3169,6 +3175,7 @@ def resp_text_code
end
end
match(T_RBRA)
@pos += 1 if @str[@pos] == " "
@lex_state = EXPR_RTEXT
return result
end
Expand Down Expand Up @@ -3269,7 +3276,7 @@ def astring
if string_token?(token)
return string
else
return atom
return astring_chars
end
end

Expand Down Expand Up @@ -3299,34 +3306,38 @@ def case_insensitive_string
return token.value.upcase
end

def atom
result = String.new
while true
token = lookahead
if atom_token?(token)
result.concat(token.value)
shift_token
else
if result.empty?
parse_error("unexpected token %s", token.symbol)
else
return result
end
end
end
end

# atom = 1*ATOM-CHAR
# ATOM-CHAR = <any CHAR except atom-specials>
ATOM_TOKENS = [
T_ATOM,
T_NUMBER,
T_NIL,
T_LBRA,
T_RBRA,
T_PLUS
]

def atom_token?(token)
return ATOM_TOKENS.include?(token.symbol)
def atom
-combine_adjacent(*ATOM_TOKENS)
end

# ASTRING-CHAR = ATOM-CHAR / resp-specials
# resp-specials = "]"
ASTRING_CHARS_TOKENS = [*ATOM_TOKENS, T_RBRA]

def astring_chars
combine_adjacent(*ASTRING_CHARS_TOKENS)
end

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

def number
Expand Down Expand Up @@ -3355,6 +3366,18 @@ def match(*args)
return token
end

# 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
def accept(*args)
token = lookahead
if args.include?(token.symbol)
shift_token
token
end
end

def lookahead
unless @token
@token = next_token
Expand Down
9 changes: 9 additions & 0 deletions test/net/imap/test_imap_response_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,15 @@ def test_capability
response = parser.parse("* CAPABILITY st11p00mm-iscream009 1Q49 XAPPLEPUSHSERVICE IMAP4 IMAP4rev1 SASL-IR AUTH=ATOKEN AUTH=PLAIN \r\n")
assert_equal("CAPABILITY", response.name)
assert_equal("AUTH=PLAIN", response.data.last)
response = parser.parse("* OK [CAPABILITY IMAP4rev1 SASL-IR 1234 NIL THIS+THAT + AUTH=PLAIN ID] IMAP4rev1 Hello\r\n")
assert_equal("OK", response.name)
assert_equal("IMAP4rev1 Hello", response.data.text)
code = response.data.code
assert_equal("CAPABILITY", code.name)
assert_equal(
["IMAP4REV1", "SASL-IR", "1234", "NIL", "THIS+THAT", "+", "AUTH=PLAIN", "ID"],
code.data
)
end

def test_mixed_boundary
Expand Down