Projects
Kolab:Winterfell
guam
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 75
View file
guam.spec
Changed
@@ -18,7 +18,7 @@ %define lock_version() %{1}%{?_isa} = %(rpm -q --queryformat "%%{VERSION}" %{1}) Name: guam -Version: 0.9.7 +Version: 0.9.8 Release: 1%{?dist} Summary: A Smart Reverse IMAP Proxy @@ -225,8 +225,11 @@ %changelog +* Mon Oct 26 2020 Christian Mollekopf <mollekopf@kolabsys.com> - 0.9.8-1 +- Release of version 0.9.8 + * Wed Apr 15 2020 Christian Mollekopf <mollekopf@kolabsys.com> - 0.9.7-1 -- Release of version 0.9.6 +- Release of version 0.9.7 * Tue Mar 31 2020 Christian Mollekopf <mollekopf@kolabsys.com> - 0.9.6-1 - Release of version 0.9.6
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +guam (0.9.8-1) unstable; urgency=medium + + * Release of version 0.9.8 + + -- Christian Mollekopf (Kolab Systems) <mollekopf@kolabsys.com> Mon, 26 Oct 2020 09:31:31 +0200 + guam (0.9.7-3) unstable; urgency=medium * Updated debian/postinst script
View file
guam-0.9.7.tar.gz/CHANGELOG.md -> guam-0.9.8.tar.gz/CHANGELOG.md
Changed
@@ -11,10 +11,18 @@ ### Fixed ### Security +## [0.9.8] - 2020-10-26 +### Added +### Fixed +- Fixed performance regression with literals containing CRLFs. +- Avoid buffering entire literal continuations + ## [0.9.7] - 2020-04-15 ### Added ### Fixed - Fixed processing of multiple commands arriving in the same packet. +- Enable keepalive on incoming socket +- Enabled full IMAP traffic logging on debug log level ## [0.9.6] - 2020-03-31 ### Added
View file
guam-0.9.7.tar.gz/apps/kolab_guam/src/kolab_guam.app.src -> guam-0.9.8.tar.gz/apps/kolab_guam/src/kolab_guam.app.src
Changed
@@ -2,7 +2,7 @@ {application, kolab_guam, [ {description, "IMAP session proxy"}, - {vsn, "0.9.7"}, + {vsn, "0.9.8"}, {registered, []}, {applications, [ kernel,
View file
guam-0.9.7.tar.gz/apps/kolab_guam/src/kolab_guam_session.erl -> guam-0.9.8.tar.gz/apps/kolab_guam/src/kolab_guam_session.erl
Changed
@@ -25,6 +25,8 @@ %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). +%% tests +-export([ process_client_data/3 ]). -include("kolab_guam_session.hrl"). @@ -67,11 +69,19 @@ %lager:debug("Socket error"), { stop, normal, State }; handle_info({ tcp, Socket, Data }, #state{ client_tls_active = false } = State) -> - %lager:debug("Data coming in from client over TCP ~s", [Data]), - process_client_data(Socket, Data, State); + % lager:debug("Data coming in from client over TCP ~s", [Data]), + {Acc, NewState} = process_client_data(Socket, Data, State), + forward_literal_data(Acc, NewState), + inet:setopts(Socket, [{ active, once }]), + { noreply, NewState }; + handle_info({ ssl, Socket, Data }, State) -> - %lager:debug("Data coming in from client over SSL, ~p", [Data]), - process_client_data(Socket, Data, State); + % lager:debug("Data coming in from client over SSL, ~p", [Data]), + {Acc, NewState} = process_client_data(Socket, Data, State), + forward_literal_data(Acc, NewState), + ssl:setopts(Socket, [{ active, once }]), + { noreply, NewState }; + handle_info({ server_hello, ServerHello }, #state{ imap_session = ImapSession, tls_config = TLSConfig, socket = Socket, client_implicit_tls = ImplicitTLS, client_tls_active = TLSActive, deflator = Deflator } = State) -> CorrectedHello = correct_hello(TLSActive, ImplicitTLS, TLSConfig, ServerHello), ServerIdent = proplists:get_value(server_id, ServerHello, <<>>), @@ -146,6 +156,22 @@ update_split_command_state(_Data, _State) -> undefined. +update_split_command_state_client(ClientDataComponents, #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 ClientDataComponents of + { <<>>, <<>>, <<>> } -> CurrentCommandSplit; + { _Tag, <<>>, <<>> } -> CurrentCommandSplit; + { <<>>, _Command, <<>> } -> CurrentCommandSplit; + { _Tag, _Command, _Data } -> undefined + end + end; +update_split_command_state_client(_ClientDataComponents, _State) -> + undefined. + accept_client(#state{ client_implicit_tls = true, tls_config = TLSConfig, listen_socket = ListenSocket, super_pid = SupervisorPID }) -> AcceptSocket = accept_socket(ListenSocket, SupervisorPID), %% prep for the next listen @@ -175,72 +201,84 @@ close_socket(_ImplicitTLS, _TLS, Socket) -> gen_tcp:close(Socket). -read_line(Data) -> - case binary:match(Data, <<"\r\n">>) of - nomatch -> - {Data, <<>>}; - {SplitPos, SplitSize} -> - {binary:part(Data, 0, SplitPos + SplitSize), binary:part(Data, SplitPos + SplitSize, size(Data)-(SplitPos + SplitSize))} - 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. - {Line, Remainder} = read_line(PreprocessData), - NewState = process_client_line(Line, Socket, State#state{ current_command_split = NewSplitCommand }), - +forward_literal_data(<<>>, _State) -> + {}; +forward_literal_data(Data, #state{imap_session = ImapSession} = _State) -> + eimap:passthrough_data(ImapSession, Data). + +process_lines(_Socket, State, [], ContinuationBytes, Acc) -> + {Acc, State#state{ continuation_bytes = ContinuationBytes }}; +process_lines(Socket, State, [Line|MoreLines], 0, Acc) -> + { _StrippedNextLine, NextContinuationBytes } = eimap_utils:num_literal_continuation_bytes(binary:part(Line, 0, byte_size(Line)-2)), + {NewAcc, NewState} = process_client_line(Line, Socket, State, Acc), + process_lines(Socket, NewState, MoreLines, NextContinuationBytes, NewAcc); +process_lines(Socket, State, [Line|MoreLines], ContinuationBytes, Acc) -> + {LiteralPartOfLine, Remainder} = split_binary(Line, min(ContinuationBytes, size(Line))), + Lines = case Remainder of - <<>> -> NewState; - _ -> process_preprocessed_client_data(Socket, Remainder, NewState) - end. - + <<>> -> MoreLines; + _ -> [Remainder|MoreLines] + end, + process_lines(Socket, State, Lines, ContinuationBytes - size(LiteralPartOfLine), <<Acc/binary, LiteralPartOfLine/binary>>). + +split(Lines, Data, Pattern) -> + case binary:match(Data, Pattern) of + nomatch -> {lists:reverse(Lines), Data}; + { Start, Length } -> + Num = Start + Length, + <<Line:Num/binary, Remainder/binary>> = Data, + split([Line|Lines], Remainder, Pattern) + end. +split(Lines, Data) -> + split(Lines, Data, binary:compile_pattern(<<"\r\n">>)). + +split_lines(Data) -> + split([], Data). % Client data is processed as it comes in, but split up by lines if more than one line comes in at a time. -% The processing interanly buffers data as necessary if it encounters incomplete lines. -process_client_data(Socket, Data, #state{ inflator = Inflator } = State) -> +% The processing internally buffers data as necessary if it encounters incomplete lines. +process_client_data(Socket, Data, #state{ inflator = Inflator, continuation_bytes = ContinuationBytes } = State) -> % TODO: refactor so starttls and compress commands can be made into rules PreprocessData = preprocess_client_data(Inflator, Data, State), lager:debug("FROM CLIENT: ~p", [PreprocessData]), - { noreply, process_preprocessed_client_data(Socket, PreprocessData, State) }. + {Lines, NewLastPartialLine} = split_lines(PreprocessData), + {Acc, NewState} = process_lines(Socket, State#state{ buffered_client_data = <<>> }, Lines, ContinuationBytes, <<>>), + #state{ buffered_client_data = DataToBuffer, continuation_bytes = NewContinuationBytes} = NewState, + + NewBuffer = <<DataToBuffer/binary, NewLastPartialLine/binary>>, + %If we are inside a continuation, forward up to NewContinuationBytes + {LiteralPartOfBuffer, NonLiteralPart} = split_binary(NewBuffer, min(NewContinuationBytes, size(NewBuffer))), -process_client_line(Data, Socket, #state{ rules_deciding = UndecidedRules, tls_config = TLSConfig, client_tls_active = TLS, rules_active = ActiveRules, socket = Socket, imap_session = ImapSession, inflator = Inflator, deflator = Deflator, server_config = ServerConfig, current_command_split = CurrentCommandSplit } = State) -> - { TLSActive, CurrentSocket, CurrentInflator, CurrentDeflator, CurrentUndecidedRules, CurrentActiveRules, DataToBuffer, SplitCommand, SplitResetTrigger } = - case check_for_transmission_change_commands(TLS, TLSConfig, Data, Deflator, Socket) of + {<<Acc/binary, LiteralPartOfBuffer/binary>>, NewState#state{ buffered_client_data = NonLiteralPart, continuation_bytes = NewContinuationBytes - size(LiteralPartOfBuffer)}}. + + +process_client_line(Data, Socket, #state{ rules_deciding = UndecidedRules, tls_config = TLSConfig, client_tls_active = TLS, rules_active = ActiveRules, socket = Socket, imap_session = ImapSession, deflator = Deflator, server_config = ServerConfig} = State, Acc) -> + ClientDataComponents = eimap_utils:split_command_into_components(Data), + case check_for_transmission_change_commands(TLS, TLSConfig, ClientDataComponents, Deflator, Socket) of { socket_upgraded, SSLSocket } -> %% if we have upgraded our socket, then do so to the backend if that hasn't happened auomatically case proplists:get_value(implicit_tls, ServerConfig, false) of false -> eimap:starttls(ImapSession, undefined, undefined); _ -> ok end, - { true, SSLSocket, Inflator, Deflator, UndecidedRules, ActiveRules, <<>>, undefined, undefined }; + { Acc, State#state{ client_tls_active = true, socket = SSLSocket, buffered_client_data = <<>>, current_command_split = undefined, command_split_reset_trigger = undefined } }; { compression, NewInflator, NewDeflator } -> eimap:compress(ImapSession), % TODO: make optional - { TLS, Socket, NewInflator, NewDeflator, UndecidedRules, ActiveRules, <<>>, undefined, undefined }; + { Acc, State#state{ inflator = NewInflator, deflator = NewDeflator, buffered_client_data = <<>>, current_command_split = undefined, command_split_reset_trigger = undefined } }; nochange -> - { ModifiedData, NewSplitCommand, NewSplitResetTrigger, NewUndecidedRules, NewActiveRules, PostAction } = apply_ruleset_clientside(ImapSession, Socket, Data, CurrentCommandSplit, UndecidedRules, ActiveRules), - BufferThisData = + %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_client(ClientDataComponents, State), + { ModifiedData, NewSplitCommand, NewSplitResetTrigger, NewUndecidedRules, NewActiveRules, PostAction } = apply_ruleset_clientside(ImapSession, Socket, Data, ClientDataComponents, NewSplit, UndecidedRules, ActiveRules), + { SendThisData, BufferThisData } = case PostAction of perform_passthrough -> - eimap:passthrough_data(ImapSession, ModifiedData), - <<>>; + {ModifiedData, <<>>}; buffer_data -> - Data + {<<>>, Data} end, - { TLS, Socket, Inflator, Deflator, NewUndecidedRules, NewActiveRules, BufferThisData, NewSplitCommand, NewSplitResetTrigger } - end, - set_socket_active(TLSActive, CurrentSocket), - State#state{ rules_deciding = CurrentUndecidedRules, rules_active = CurrentActiveRules, - socket = CurrentSocket, client_tls_active = TLSActive, - inflator = CurrentInflator, deflator = CurrentDeflator, - buffered_client_data = DataToBuffer, - current_command_split = SplitCommand, - command_split_reset_trigger = SplitResetTrigger }. + { <<Acc/binary, SendThisData/binary>>, State#state{ rules_deciding = NewUndecidedRules, rules_active = NewActiveRules, buffered_client_data = BufferThisData, current_command_split = NewSplitCommand, command_split_reset_trigger = NewSplitResetTrigger } } + end. preprocess_client_data(undefined, Data, #state{ buffered_client_data = Buffered }) -> <<Buffered/binary, Data/binary>>; @@ -282,13 +320,13 @@ { ModifiedData, ModifiedRuleState } = Module:apply_to_server_message(ImapSession, ServerData, RuleState), apply_next_rule_serverside(ImapSession, ModifiedData, [{ Module, ModifiedRuleState } | ActiveRulesAcc], ActiveRules). -apply_ruleset_clientside(_ImapSession, _Socket, ClientData, _CurrentCommandSplit, [], []) -> +apply_ruleset_clientside(_ImapSession, _Socket, ClientData, _ClientDataComponents, _CurrentCommandSplit, [], []) -> { ClientData, undefined, [], [], [], perform_passthrough }; -apply_ruleset_clientside(ImapSession, Socket, ClientData, CurrentCommandSplit, UndecidedRules, CurrentlyActiveRules) -> +apply_ruleset_clientside(ImapSession, Socket, ClientData, ClientDataComponents, CurrentCommandSplit, UndecidedRules, CurrentlyActiveRules) -> { PostAction, SplitCommand, SplitResetTrigger } = case CurrentCommandSplit of undefined -> - case eimap_utils:split_command_into_components(ClientData) of + case ClientDataComponents of % In case of an empty line ("\r\n") the line is complete (doesn't need to be buffered), but there is also no command or tag. { <<>>, <<>>, <<>> } -> { perform_passthrough, CurrentCommandSplit, reset_for_next_client_command }; { _Tag, <<>>, <<>> } -> { buffer_data, undefined, reset_for_next_client_command }; @@ -333,8 +371,8 @@ %lager:debug("Sending over TLS!"), ssl:send(Socket, Data). -check_for_transmission_change_commands(TLS, TLSConfig, Buffer, Deflator, Socket) -> - {Tag, Command, _Data } = eimap_utils:split_command_into_components(Buffer), +check_for_transmission_change_commands(TLS, TLSConfig, ClientDataComponents, Deflator, Socket) -> + {Tag, Command, _Data } = ClientDataComponents, case check_tls_state(TLS, TLSConfig, Command, Deflator, Socket, Tag) of nochange -> check_compress_request(Deflator, Command, Socket, TLS, Tag); Response -> Response @@ -366,9 +404,6 @@ { compression, Inflator, Deflator }. -set_socket_active(true, Socket) -> ssl:setopts(Socket, [{ active, once }]); -set_socket_active(_, Socket) -> inet:setopts(Socket, [{ active, once }]). - -spec correct_hello(TLSActive :: true | false, ImplicitTLS :: true | false, TlSConfig :: [] | list(), ServerHello :: binary()) -> CorrectedHello :: binary(). correct_hello(true, true, _TLSConfig, ServerResponse) -> % the connection is already secured, so don't advertise starttls to the client
View file
guam-0.9.7.tar.gz/apps/kolab_guam/src/kolab_guam_session.hrl -> guam-0.9.8.tar.gz/apps/kolab_guam/src/kolab_guam_session.hrl
Changed
@@ -1,4 +1,4 @@ -record(state, { listen_socket, socket = undefined, super_pid, tls_config = [], client_implicit_tls = false, client_tls_active = false, server_config = [], rules_active = [], rules_deciding = [], imap_session = undefined, inflator = undefined, deflator = undefined, buffered_client_data = <<>>, - current_command_split = undefined, command_split_reset_trigger = reset_for_next_client_command }). + current_command_split = undefined, command_split_reset_trigger = reset_for_next_client_command, continuation_bytes = 0}).
View file
guam-0.9.7.tar.gz/apps/kolab_guam/test/kolab_guam_session_SUITE.erl -> guam-0.9.8.tar.gz/apps/kolab_guam/test/kolab_guam_session_SUITE.erl
Changed
@@ -31,7 +31,9 @@ % Specify a list of all unit test functions all() -> [ - kolab_guam_session_test + kolab_guam_session_test_client, + kolab_guam_session_test_server, + kolab_guam_session_test_client_benchmark ]. % required, but can just return Config. this is a suite level setup function. @@ -53,7 +55,7 @@ Config. -kolab_guam_session_test(_TestConfig) -> +kolab_guam_session_test_client(_TestConfig) -> %% setup boilerplate ServerConfig = kolab_guam_sup:default_imap_server_config(), { ok, ImapSession } = eimap:start_link(ServerConfig), @@ -70,8 +72,7 @@ imap_session = ImapSession }, - - % Run without rules + %handle_info {noreply, #state{ server_config = ServerConfig, buffered_client_data = <<>>, @@ -79,67 +80,130 @@ command_split_reset_trigger = [] %FIXME ? }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = [] }), + % Run without rules + {<<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + current_command_split = undefined, + command_split_reset_trigger = [] %FIXME ? + }} = kolab_guam_session:process_client_data(Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>, State#state{ rules_deciding = [] }), + % Activate filtering - {noreply, #state{ + {<<"y1 ID (\"name\" \"Test\")\r\n">>, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] - }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), + }} = kolab_guam_session:process_client_data(Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>, State#state{ rules_deciding = ActiveRules }), + + + % Append + {<<"y1 APPEND INBOX {30}\r\n0123">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {30}">>}, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState4} = kolab_guam_session:process_client_data(Socket, <<"y1 APPEND INBOX {30}\r\n0123">>, State#state{ rules_deciding = ActiveRules }), + + {<<"456789">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, %This ensures we aren't buffering during the continuation + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {30}">>}, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState5} = kolab_guam_session:process_client_data(Socket, <<"456789">>, IntermediateState4), + + {<<"0123456789012345678">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, %This ensures we aren't buffering during the continuation + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {30}">>}, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState6} = kolab_guam_session:process_client_data(Socket, <<"0123456789012345678">>, IntermediateState5), + + {<<"9\r\ny2 ENABLE QRESYNC\r\n">>, #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:process_client_data(Socket, <<"9\r\ny2 ENABLE QRESYNC\r\n">>, IntermediateState6), + + % Append2 + {<<"y1 APPEND INBOX {36}\r\n0123">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {36}">>}, + command_split_reset_trigger = reset_for_next_client_command, + continuation_bytes = 32 + } = IntermediateState7} = kolab_guam_session:process_client_data(Socket, <<"y1 APPEND INBOX {36}\r\n0123">>, State#state{ rules_deciding = ActiveRules }), + + {<<"456789\r\n0123456789\r\n0123456789">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, %This ensures we aren't buffering during the continuation + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {36}">>}, + command_split_reset_trigger = reset_for_next_client_command, + continuation_bytes = 2 + } = IntermediateState8} = kolab_guam_session:process_client_data(Socket, <<"456789\r\n0123456789\r\n0123456789">>, IntermediateState7), + + {<<"\r\n">>, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, %This ensures we aren't buffering during the continuation + current_command_split = {<<"y1">>,<<"APPEND">>,<<"INBOX {36}">>}, + command_split_reset_trigger = reset_for_next_client_command, + continuation_bytes = 0 + }} = kolab_guam_session:process_client_data(Socket, <<"\r\n">>, IntermediateState8), % Don't activate filtering - {noreply, #state{ + {<<"y1 ID (\"name\" \"Test/KOLAB\")\r\n">>, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test/KOLAB\")">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [] - }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test/KOLAB\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), + }} = kolab_guam_session:process_client_data(Socket, <<"y1 ID (\"name\" \"Test/KOLAB\")\r\n">>, State#state{ rules_deciding = ActiveRules }), % Lone tag in a packet. Can/Could happen with iPhone apparently. (See commit 89f9dc93757c68032ed17f42838858bdfaefa408) - {noreply, #state{ buffered_client_data = <<"y1">>, rules_active = [] } = IntermediateState1} = kolab_guam_session:handle_info({tcp, Socket, <<"y1">>}, State#state{ rules_deciding = ActiveRules }), - {noreply, #state{ + {<<>>, #state{ buffered_client_data = <<"y1">>, rules_active = [] } = IntermediateState1} = kolab_guam_session:process_client_data(Socket, <<"y1">>, State#state{ rules_deciding = ActiveRules }), + {<<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y2">>,<<"ENABLE">>,<<"QRESYNC">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] - }} = kolab_guam_session:handle_info({tcp, Socket, <<" ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState1), + }} = kolab_guam_session:process_client_data(Socket, <<" ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>, IntermediateState1), % Lone tag in a packet, but with a \r\n before. See guam-0.9.2-stalling-client-buffer-and-split-command-handling.patch (This triggers the odd List buffering case) - {noreply, #state{ buffered_client_data = <<"y1 ">>, rules_active = [] } = IntermediateState2} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\ny1 ">>}, State#state{ rules_deciding = ActiveRules }), - {noreply, #state{ + {<<"\r\n">>, #state{ buffered_client_data = <<"y1 ">>, rules_active = [] } = IntermediateState2} = kolab_guam_session:process_client_data(Socket, <<"\r\ny1 ">>, State#state{ rules_deciding = ActiveRules }), + {<<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y2">>,<<"ENABLE">>,<<"QRESYNC">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] - }} = kolab_guam_session:handle_info({tcp, Socket, <<"ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState2), + }} = kolab_guam_session:process_client_data(Socket, <<"ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>, IntermediateState2), % Empty line (should be considered a complete command and should not be buffered) - {noreply, #state{ buffered_client_data = <<>>, rules_active = [] } = IntermediateState3} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\n">>}, State#state{ rules_deciding = ActiveRules }), - {noreply, #state{ + {<<"\r\n">>, #state{ buffered_client_data = <<>>, rules_active = [] } = IntermediateState3} = kolab_guam_session:process_client_data(Socket, <<"\r\n">>, State#state{ rules_deciding = ActiveRules }), + {<<"y1 ID (\"name\" \"Test\")\r\n">>, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, command_split_reset_trigger = reset_for_next_client_command, rules_deciding = [], rules_active = [{kolab_guam_rule_filter_groupware, _}] - }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, IntermediateState3), + }} = kolab_guam_session:process_client_data(Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>, IntermediateState3), % Activate with multi-line packet - {noreply, #state{ + {<<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>, #state{ server_config = ServerConfig, buffered_client_data = <<>>, current_command_split = {<<"y3">>,<<"LIST">>,<<"\"\" \"%\" RETURN (SUBSCRIBED))">>}, @@ -148,7 +212,7 @@ % Make sure that we have processed y3 in here (and don't stop processing at y1) rules_active = [{kolab_guam_rule_filter_groupware, {state,undefined,<<"y3">>,true,<<>>,[<<"LIST">>,<<"list">>,<<"XLIST">>,<<"xlist">>,<<"LSUB">>,<<"lsub">>]}}] % rules_active = [{kolab_guam_rule_filter_groupware, _}] - }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = ActiveRules }), + }} = kolab_guam_session:process_client_data(Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>, State#state{ rules_deciding = ActiveRules }), % Test various packet splits @@ -185,3 +249,122 @@ end, State, Packets). + + +kolab_guam_session_test_server(_TestConfig) -> + %% setup boilerplate + % gen_tcp:connect + ServerConfig = kolab_guam_sup:default_imap_server_config(), + { ok, ImapSession } = eimap:start_link(ServerConfig), + { ok, Socket } = gen_tcp:listen(9964, [ { reuseaddr, true }, {active, false}, inet6 ]), + + lager:start(), + lager:set_loglevel(lager_console_backend, debug), + + ActiveRules = [{ kolab_guam_rule_filter_groupware, kolab_guam_rule_filter_groupware:new({}) }], + + State = #state{ + socket = Socket, + server_config = ServerConfig, + imap_session = ImapSession + }, + + + % This should trigger a metadata request + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, + current_command_split = {<<"y3">>,<<"LIST">>,<<"\"\" \"%\" RETURN (SUBSCRIBED))">>}, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + } = IntermediateState1} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = ActiveRules }), + + + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, + current_command_split = undefined, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + } = IntermediateState2} = kolab_guam_session:handle_info({imap_server_response, <<"* ID (\"name\" \"Cyrus IMAPD\")\n\ry1 OK Completed\r\n">>}, IntermediateState1), + + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\")">>}, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + }} = kolab_guam_session:handle_info({imap_server_response, <<"* METADATA INBOX (/shared/vendor/kolab/folder-type \"mail\")\r\n">>}, IntermediateState2), + +% <<"* METADATA Archive (/shared/vendor/kolab/folder-type NIL)\r\n"> +% <<"* METADATA Calendar (/shared/vendor/kolab/folder-type \"event\")\r\n"> +% <<"EG0001 OK Completed\r\n">> + + lager:info("Done"). + + + +kolab_guam_session_test_client_benchmark(_TestConfig) -> + %% setup boilerplate + ServerConfig = kolab_guam_sup:default_imap_server_config(), + { ok, ImapSession } = eimap:start_link(ServerConfig), + { ok, Socket } = gen_tcp:listen(9964, [ { reuseaddr, true }, {active, false}, inet6 ]), + + lager:start(), + lager:set_loglevel(lager_console_backend, debug), + + ActiveRules = [{ kolab_guam_rule_filter_groupware, kolab_guam_rule_filter_groupware:new({}) }], + + State = #state{ + socket = Socket, + server_config = ServerConfig, + imap_session = ImapSession + }, + + Count = 70, + % We used to have a factor of 10 in runtime between \r\n (causing line splits), and just \n (not causing line splits) + Payload = <<"0123456789\r\n">>, + Data = list_to_binary(lists:duplicate(Count, Payload)), + BinaryCount = list_to_binary(integer_to_list(Count * size(Payload) + 36)), + ExpectedCommandSplit = {<<"y1">>, <<"APPEND">>, list_to_binary([<<"INBOX {">>, BinaryCount, <<"}">>])}, + + S = os:timestamp(), + + % Append + {noreply, #state{ + server_config = ServerConfig, + current_command_split = ExpectedCommandSplit, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState4} = kolab_guam_session:handle_info({tcp, Socket, list_to_binary([<<"y1 APPEND INBOX {">>, BinaryCount, <<"}\r\n0123">>])}, State#state{ rules_deciding = ActiveRules }), + + {noreply, #state{ + server_config = ServerConfig, + current_command_split = ExpectedCommandSplit, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState5} = kolab_guam_session:handle_info({tcp, Socket, <<"456789\r\n0123456789\r\n0123456789">>}, IntermediateState4), + + + {noreply, #state{ + server_config = ServerConfig, + current_command_split = ExpectedCommandSplit, + command_split_reset_trigger = reset_for_next_client_command + } = IntermediateState6} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\n">>}, IntermediateState5), + + {noreply, #state{ + server_config = ServerConfig, + current_command_split = ExpectedCommandSplit, + command_split_reset_trigger = reset_for_next_client_command + }} = kolab_guam_session:handle_info({tcp, Socket, Data}, IntermediateState6), + + + E = os:timestamp(), + X = timer:now_diff(E,S), + lager:info("Runtime: ~p usecs", [X]), + + + lager:info("Done").
View file
guam-0.9.7.tar.gz/rebar.config -> guam-0.9.8.tar.gz/rebar.config
Changed
@@ -23,7 +23,7 @@ { eunit_opts, [verbose, {skip_deps, true }] }. { eunit_exclude_deps, true }. { cover_enabled, true }. -{ relx, [ { release, { guam, "0.9.7" }, [kolab_guam]}, +{ relx, [ { release, { guam, "0.9.8" }, [kolab_guam]}, { dev_mode, false }, { include_erts, false }, { extended_start_script, true },
View file
guam.dsc
Changed
@@ -2,7 +2,7 @@ Source: guam Binary: guam Architecture: any -Version: 0.9.7-3 +Version: 0.9.8-1 Maintainer: Christoph Erhardt <kolab@sicherha.de> Homepage: https://kolab.org/about/guam Standards-Version: 3.9.6 @@ -10,5 +10,5 @@ Package-List: guam deb mail extra Files: - 00000000000000000000000000000000 0 guam-0.9.7.tar.gz + 00000000000000000000000000000000 0 guam-0.9.8.tar.gz 00000000000000000000000000000000 0 debian.tar.gz
View file
plesk.sys.config
Changed
@@ -160,7 +160,7 @@ lager, [ { handlers, [ - { lager_console_backend, debug }, + { lager_console_backend, info }, { lager_file_backend, [ { file, "log/error.log"}, { level, error } ] }, { lager_file_backend, [ { file, "log/console.log"}, { level, info } ] } ]
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.