Skip to content

Commit

Permalink
Merge pull request #57 from jtmoulia/fix/unterm-paren-parse
Browse files Browse the repository at this point in the history
Retry parse when an IMAP list is unterminated
  • Loading branch information
jtmoulia committed Jan 24, 2016
2 parents d2afa65 + 8a4d161 commit 01fa7d7
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 16 deletions.
25 changes: 10 additions & 15 deletions src/imap.erl
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@
clean_body/1,
cmd_to_list/1,
seqset_to_list/1,
pop_token/1,
decode_line/1,
pop_token/1, pop_token/2,
decode_line/1, decode_line/2,
tokenize/1,
parse/1]).
-endif.
Expand Down Expand Up @@ -604,6 +604,8 @@ churn_buffer(#state{tokenize_state=TokenizeState, parse_state=ParseState} = Stat
#state{}.
churn_buffer(State, none) ->
State;
churn_buffer(State, []) ->
State;
churn_buffer(#state{cmds=Cmds} = State, [<<"*">> | Response]) ->
% lager:debug("Untagged: ~p", [Response]),
ok = lists:foreach(
Expand Down Expand Up @@ -990,7 +992,7 @@ pop_token(<<$], Rest/binary>>, none) ->

%% Numbers
pop_token(<<C, _/binary>> = Data, {number, NumberAcc}) when
C =:= 32; C =:= 40; C =:= 41; C =:= 91; C =:= 93 ->
C =:= 32; C =:= 40; C =:= 41; C =:= $(; C =:= $); C =:= 91; C =:= 93 ->
{binary_to_integer(NumberAcc), Data, none};
pop_token(<<$\r, $\n, _/binary>> = Data, {number, NumberAcc}) ->
{binary_to_integer(NumberAcc), Data, none};
Expand All @@ -1005,7 +1007,7 @@ pop_token(<<C, Rest/binary>>, {number, NumberAcc}) when C >= 35, C < 123 ->

%% Atom
pop_token(<<C, _/binary>> = Data, {atom, AtomAcc}) when
C =:= 32; C =:= 40; C =:= 41; C =:= 91; C =:= 93 ->
C =:= 32; C =:= 40; C =:= 41; C =:= $(; C =:= $); C =:= 91; C =:= 93 ->
{AtomAcc, Data, none};
pop_token(<<$\r, $\n, _/binary>> = Data, {atom, AtomAcc}) ->
{AtomAcc, Data, none};
Expand Down Expand Up @@ -1048,15 +1050,8 @@ pop_token(Binary, _) ->


%% @private
%% @doc Parse the flat list of tokens into a data structurej
%% @doc Parse the flat list of tokens into a syntax tree and remainder.
%% @end

%% todo
%% 21:46:48.402 [info] Received: <<"* 12 FETCH (UID 12)\r\n* 13 FETCH (U">>
%% 21:46:48.403 [info] Result: [<<"*">>,12,<<"FETCH">>,[<<"UID">>,12]]
%% 21:46:48.403 [info] Result: none
%% 21:46:48.403 [info] Received: <<"ID 13)\r\n* 14 FETCH (UID 14)\r\n">>
%% 21:46:48.403 [info] Result: [<<"*">>,13,<<"FETCH">>,none,<<"UID">>,13]
-spec parse([token()]) ->
{[imap_term()] | none, [token()], [imap_term()]}.
parse(Tokens) ->
Expand All @@ -1072,10 +1067,10 @@ parse([crlf | Rest], ParseAcc) ->

parse(['(' | Rest] = Tokens, ParseAcc) ->
case parse(Rest) of
{List, Rest2, []} ->
parse(Rest2, [List | ParseAcc]);
{none, _, _} ->
{none, Tokens, ParseAcc}
{none, Tokens, ParseAcc};
{List, Rest2, []} ->
parse(Rest2, [List | ParseAcc])
end;
parse([')' | Rest], ParseAcc) ->
{lists:reverse(ParseAcc), Rest, []};
Expand Down
20 changes: 19 additions & 1 deletion src/test/eunit/imap_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -217,14 +217,32 @@ parse_lists() ->

%% decode_line testing.
decode_line_test_() ->
[decode_line_default()].
[decode_line_default(),
decode_line_paren_partial()].


decode_line_default() ->
[?_assertEqual({[1, 2, 3], {<<>>, none}, {[], []}},
imap:decode_line(<<"1 2 3\r\n">>))].


%% 21:46:48.402 [info] Received: <<"* 12 FETCH (UID 12)\r\n* 13 FETCH (U">>
%% 21:46:48.403 [info] Result: [<<"*">>,12,<<"FETCH">>,[<<"UID">>,12]]
%% 21:46:48.403 [info] Result: none
%% 21:46:48.403 [info] Received: <<"ID 13)\r\n* 14 FETCH (UID 14)\r\n">>
%% 21:46:48.403 [info] Result: [<<"*">>,13,<<"FETCH">>,none,<<"UID">>,13]
decode_line_paren_partial() ->
{Result, {<<>>, TokenizeCurrent}, ParseState} =
imap:decode_line(<<"* 12 FETCH (UID 12)\r\n* 13 FETCH (U">>),
{Result2, TokenizeState2, ParseState2} =
imap:decode_line({<<"ID 13)\r\n* 14 FETCH (UID 14)\r\n">>, TokenizeCurrent},
ParseState),
[?_assertEqual([<<"*">>,12,<<"FETCH">>,[<<"UID">>,12]], Result),
?_assertEqual([<<"*">>,13,<<"FETCH">>,[<<"UID">>,13]], Result2),
?_assertEqual({<<>>, none}, TokenizeState2),
?_assertEqual({[<<"*">>,14,<<"FETCH">>,'(',<<"UID">>,14,')',crlf],[]},
ParseState2)].

auth_to_test_() ->
[auth_to_props()].

Expand Down

0 comments on commit 01fa7d7

Please sign in to comment.