diff --git a/apps/kolab_guam/src/kolab_guam_session.erl b/apps/kolab_guam/src/kolab_guam_session.erl --- a/apps/kolab_guam/src/kolab_guam_session.erl +++ b/apps/kolab_guam/src/kolab_guam_session.erl @@ -143,6 +143,18 @@ end end end; +update_split_command_state(Data, #state{ command_split_reset_trigger = reset_for_next_client_command, current_command_split = CurrentCommandSplit }) -> + %If we have a currently active command (CurrentCommandSplit), then we reset only if we find a new command (and not just on any new line). + %This is relevant for multi-line commands such as APPEND, which follow up with a continuation which we want to avoid buffering. + case CurrentCommandSplit of + undefined -> undefined; + _ -> + case eimap_utils:split_command_into_components(Data) of + { <<>>, <<>>, <<>> } -> CurrentCommandSplit; + { _Tag, <<>>, <<>> } -> CurrentCommandSplit; + { _Tag, Command, _Data } -> undefined + end + end; update_split_command_state(_Data, _State) -> undefined. @@ -184,15 +196,9 @@ end. -process_preprocessed_client_data(Socket, PreprocessData, #state{ command_split_reset_trigger = CurrentCommandSplitResetTrigger, current_command_split = CurrentCommandSplit } = State) -> - NewSplitCommand = case CurrentCommandSplitResetTrigger of - reset_for_next_client_command -> undefined; - _ -> CurrentCommandSplit - end, - - % TODO Maybe we should prepend buffered data here instead. +process_preprocessed_client_data(Socket, PreprocessData, State) -> {Line, Remainder} = read_line(PreprocessData), - NewState = process_client_line(Line, Socket, State#state{ current_command_split = NewSplitCommand }), + NewState = process_client_line(Line, Socket, State), case Remainder of <<>> -> NewState; @@ -223,7 +229,9 @@ eimap:compress(ImapSession), % TODO: make optional { TLS, Socket, NewInflator, NewDeflator, UndecidedRules, ActiveRules, <<>>, undefined, undefined }; nochange -> - { ModifiedData, NewSplitCommand, NewSplitResetTrigger, NewUndecidedRules, NewActiveRules, PostAction } = apply_ruleset_clientside(ImapSession, Socket, Data, CurrentCommandSplit, UndecidedRules, ActiveRules), + %We first check if we have to reset the split command before we process the new command in apply_ruleset_clientside + NewSplit = update_split_command_state(Data, State), + { ModifiedData, NewSplitCommand, NewSplitResetTrigger, NewUndecidedRules, NewActiveRules, PostAction } = apply_ruleset_clientside(ImapSession, Socket, Data, NewSplit, UndecidedRules, ActiveRules), BufferThisData = case PostAction of perform_passthrough -> diff --git a/apps/kolab_guam/test/kolab_guam_session_SUITE.erl b/apps/kolab_guam/test/kolab_guam_session_SUITE.erl --- a/apps/kolab_guam/test/kolab_guam_session_SUITE.erl +++ b/apps/kolab_guam/test/kolab_guam_session_SUITE.erl @@ -91,6 +91,29 @@ }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), + % Append + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {7}">>}, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState4} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 APPEND INBOX {7}\r\n123">>}, State#state{ rules_deciding = ActiveRules }), + + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, %This ensures we aren't buffering during the continuation + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {7}">>}, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState5} = kolab_guam_session:handle_info({tcp, Socket, <<"456">>}, IntermediateState4), + + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + current_command_split = {<<"y2">>,<<"ENABLE">>,<<"QRESYNC">>}, + command_split_reset_trigger = reset_for_next_client_command + }} = kolab_guam_session:handle_info({tcp, Socket, <<"7\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState5), + + % Don't activate filtering {noreply, #state{ server_config = ServerConfig,