Page MenuHomePhorge

D215.1775393811.diff
No OneTemporary

Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None

D215.1775393811.diff

diff --git a/apps/kolab_guam/src/kolab_guam_rule.erl b/apps/kolab_guam/src/kolab_guam_rule.erl
--- a/apps/kolab_guam/src/kolab_guam_rule.erl
+++ b/apps/kolab_guam/src/kolab_guam_rule.erl
@@ -18,9 +18,9 @@
-module(kolab_guam_rule).
-callback new(Args :: any()) -> any().
--callback applies(ConnectionDetails :: list(), Buffer :: binary(), State :: any()) -> { true, State :: any() } |
+-callback applies(ConnectionDetails :: list(), Buffer :: binary(), SplitBinary :: { Tag :: binary(), Command :: binary(), Data :: binary() }, State :: any()) -> { true, State :: any() } |
{ false, State :: any() } |
{ notyet, State :: any() }.
--callback apply_to_client_message(ImapSession :: pid(), Command :: binary(), State :: any()) -> { ProcessedCommand :: binary(), State :: any() }.
+-callback apply_to_client_message(ImapSession :: pid(), Command :: binary(), SplitBinary :: { Tag :: binary(), Command :: binary(), Data :: binary() }, State :: any()) -> { ProcessedCommand :: binary(), State :: any() }.
-callback apply_to_server_message(ImapSession :: pid(), Command :: binary(), State :: any()) -> { ProcessedCommand :: binary(), State :: any() }.
-callback imap_data(ResponseToken :: any(), Response :: any(), State :: any()) -> State ::any().
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
@@ -27,7 +27,8 @@
%% state record definition
-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, deflator }).
+ rules_active = [], rules_deciding = [], imap_session = undefined, inflator, deflator, buffered_client_data = <<>>,
+ current_command_split = undefined, command_split_reset_trigger = reset_for_next_client_command }).
%% public API
start_link(SupervisorPID, ListenSocket, ImapConfig, ImplicitTLS, TLSConfig, Rules) -> gen_server:start_link(?MODULE, [SupervisorPID, ListenSocket, ImapConfig, ImplicitTLS, TLSConfig, Rules], []).
@@ -101,7 +102,8 @@
%lager:debug("FROM SERVER: ~s", [Data]),
{ ModifiedData, CurrentlyActiveRules } = apply_ruleset_serverside(ImapSession, Data, ActiveRules),
relay_response(Socket, postprocess_server_data(Deflator, ModifiedData), TLS),
- { noreply, State#state{ rules_active = CurrentlyActiveRules } };
+ NewSplitCommand = update_split_command_state(ModifiedData, State),
+ { noreply, State#state{ rules_active = CurrentlyActiveRules, current_command_split = NewSplitCommand } };
handle_info({ 'EXIT', PID, _Reason }, #state { imap_session = PID } = State) ->
{ stop, normal, State#state{ imap_session = undefined } };
handle_info(Info, State) ->
@@ -122,6 +124,30 @@
{ ok, State }.
%% private API
+
+%% update_split_command_state updates the split_command being held on to when we get a server response
+%% in the case of "transactional" messages (such as authentication) where the client and server enter a bidirectional conversation
+%% that is goverened by rules outside the the usual IMAP call/response pattern, we need to wait for the end of the server response
+%% since this is relatively expensive due to having to scan the data for the tagged server response, and is not necessary for all other
+%% IMAP commands, we shortcircuit when the command does not trigger a "transactional" interaction between client and server, and instead
+%% just always reset the split data state at that point
+update_split_command_state(Data, #state{ command_split_reset_trigger = reset_on_server_response, current_command_split = CurrentCommandSplit }) ->
+ case CurrentCommandSplit of
+ undefined -> undefined;
+ { Tag, _Command, _Data } ->
+ case binary:match(Data, <<Tag/binary, " ">>) of
+ nomatch -> CurrentCommandSplit;
+ { 0, _ } -> undefined;
+ { Start, _ } ->
+ case binary:at(Data, Start - 1) of
+ $\n -> undefined;
+ _ -> CurrentCommandSplit
+ end
+ end
+ end;
+update_split_command_state(_Data, _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
@@ -150,12 +176,12 @@
close_socket(true, _TLS, Socket) -> ssl:close(Socket);
close_socket(_ImplicitTLS, _TLS, Socket) -> gen_tcp:close(Socket).
-process_client_data(Socket, Data, #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 } = State) ->
+process_client_data(Socket, Data, #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) ->
%%TODO: multipacket input from clients
% TODO: refactor so starttls and compress commands can be made into rules
- PreprocessData = preprocess_client_data(Inflator, Data),
+ PreprocessData = preprocess_client_data(Inflator, Data, State),
%lager:info("FROM CLIENT: ~s", [PreprocessData]),
- { TLSActive, CurrentSocket, CurrentInflator, CurrentDeflator, CurrentUndecidedRules, CurrentActiveRules } =
+ { TLSActive, CurrentSocket, CurrentInflator, CurrentDeflator, CurrentUndecidedRules, CurrentActiveRules, DataToBuffer, SplitCommand, SplitResetTrigger } =
case check_for_transmission_change_commands(TLS, TLSConfig, PreprocessData, Deflator, Socket) of
{ socket_upgraded, SSLSocket } ->
%% if we have upgraded our socket, then do so to the backend if that hasn't happened auomatically
@@ -163,27 +189,39 @@
false -> eimap:starttls(ImapSession, undefined, undefined);
_ -> ok
end,
- { true, SSLSocket, Inflator, Deflator, UndecidedRules, ActiveRules };
+ { true, SSLSocket, Inflator, Deflator, UndecidedRules, ActiveRules, <<>>, undefined, undefined };
{ compression, NewInflator, NewDeflator } ->
eimap:compress(ImapSession), % TODO: make optional
- { TLS, Socket, NewInflator, NewDeflator, UndecidedRules, ActiveRules };
+ { TLS, Socket, NewInflator, NewDeflator, UndecidedRules, ActiveRules, <<>>, undefined, undefined };
nochange ->
%%lager:debug("... now applying rules"),
- { ModifiedData, NewUndecidedRules, NewActiveRules } = apply_ruleset_clientside(ImapSession, Socket, PreprocessData, UndecidedRules, ActiveRules),
+ { ModifiedData, NewSplitCommand, NewSplitResetTrigger, NewUndecidedRules, NewActiveRules, PostAction } = apply_ruleset_clientside(ImapSession, Socket, PreprocessData, CurrentCommandSplit, UndecidedRules, ActiveRules),
%%lager:info("The modified data is: ~s", [ModifiedData]),
%lager:info("The post-processed data is: ~s", [PostProcessed]),
- eimap:passthrough_data(ImapSession, ModifiedData),
- { TLS, Socket, Inflator, Deflator, NewUndecidedRules, NewActiveRules}
+ BufferThisData =
+ case PostAction of
+ perform_passthrough ->
+ eimap:passthrough_data(ImapSession, ModifiedData),
+ <<>>;
+ buffer_data ->
+ Data
+ end,
+ { TLS, Socket, Inflator, Deflator, NewUndecidedRules, NewActiveRules, BufferThisData, NewSplitCommand, NewSplitResetTrigger }
end,
set_socket_active(TLSActive, CurrentSocket),
+ PrevBuffered = State#state.buffered_client_data,
{ noreply, State#state{ rules_deciding = CurrentUndecidedRules, rules_active = CurrentActiveRules,
socket = CurrentSocket, client_tls_active = TLSActive,
- inflator = CurrentInflator, deflator = CurrentDeflator } }.
+ inflator = CurrentInflator, deflator = CurrentDeflator,
+ buffered_client_data = <<PrevBuffered/binary, DataToBuffer/binary>>,
+ current_command_split = SplitCommand,
+ command_split_reset_trigger = SplitResetTrigger } }.
-preprocess_client_data(undefined, Data) ->
- Data;
-preprocess_client_data(Z, Data) ->
- joined(zlib:inflate(Z, Data), <<>>).
+preprocess_client_data(undefined, Data, #state{ buffered_client_data = Buffered }) ->
+ <<Buffered/binary, Data/binary>>;
+preprocess_client_data(Z, Data, #state{ buffered_client_data = Buffered }) ->
+ Inflated = joined(zlib:inflate(Z, Data), <<>>),
+ <<Buffered/binary, Inflated/binary>>.
postprocess_server_data(undefined, Data) ->
%% we aren't compressing so there is nothing to do
@@ -222,27 +260,47 @@
{ 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, UndecidedRules, CurrentlyActiveRules) ->
- { StillUndecided, NewlyActive } = check_undecided(Socket, ClientData, UndecidedRules),
+apply_ruleset_clientside(_ImapSession, _Socket, ClientData, _CurrentCommandSplit, [], []) ->
+ { ClientData, [], [], perform_passthrough, undefined };
+apply_ruleset_clientside(ImapSession, Socket, ClientData, CurrentCommandSplit, UndecidedRules, CurrentlyActiveRules) ->
+ { PostAction, SplitCommand, SplitResetTrigger } =
+ case CurrentCommandSplit of
+ undefined ->
+ case eimap_utils:split_command_into_components(ClientData) of
+ { _Tag, <<>>, <<>> } -> { buffer_data, undefined, reset_for_next_client_command };
+ { _Tag, Command, _Data } = Split -> { perform_passthrough, Split, when_to_reset_split(Command) }
+ end;
+ _ -> { perform_passthrough, CurrentCommandSplit, reset_for_next_client_command }
+ end,
+ { StillUndecided, NewlyActive } = check_undecided(Socket, ClientData, SplitCommand, UndecidedRules),
ActiveRules = CurrentlyActiveRules ++ NewlyActive,
- { ModifiedData, ActiveRulesRun } = apply_next_rule_clientside(ImapSession, ClientData, [], ActiveRules),
- { ModifiedData, StillUndecided, ActiveRulesRun }.
-
-check_undecided(Socket, ClientData, Rules) -> check_next_undecided_rule(Socket, ClientData, Rules, { [], [] }).
-check_next_undecided_rule(_Socket, _ClientData, [], Accs) -> Accs;
-check_next_undecided_rule(Socket, ClientData, [Rule|Rules], { UndecidedAcc, NewActiveAcc }) ->
+ %lager:info("Active Rules: ~p", [ActiveRules]),
+ { ModifiedData, ActiveRulesRun } = apply_next_rule_clientside(ImapSession, ClientData, SplitCommand, [], ActiveRules),
+ { ModifiedData, SplitCommand, SplitResetTrigger, StillUndecided, ActiveRulesRun, PostAction }.
+
+when_to_reset_split(<<"AUTHENTICATE">>) -> reset_on_server_response;
+when_to_reset_split(<<"authenticate">>) -> reset_on_server_response;
+when_to_reset_split(_) -> reset_for_next_client_command.
+
+check_undecided(Socket, ClientData, undefined, Rules) ->
+ %% if we do not have a properly split command ... do nothing!
+ { Rules, [] };
+check_undecided(Socket, ClientData, SplitCommand, Rules) -> check_next_undecided_rule(Socket, ClientData, SplitCommand, Rules, { [], [] }).
+check_next_undecided_rule(_Socket, _ClientData, _SplitCommand, [], Accs) -> Accs;
+check_next_undecided_rule(Socket, ClientData, SplitCommand, [Rule|Rules], { UndecidedAcc, NewActiveAcc }) ->
{ Module, RuleState } = Rule,
%%lager:debug("Does ~p apply with state ~p? let's find out!", [Module, RuleState]),
- check_next_undecided_rule(Socket, ClientData, Rules, applies(Module, Module:applies(Socket, ClientData, RuleState), UndecidedAcc, NewActiveAcc)).
+ Application = Module:applies(Socket, ClientData, SplitCommand, RuleState),
+ check_next_undecided_rule(Socket, ClientData, SplitCommand, Rules, applies(Module, Application, UndecidedAcc, NewActiveAcc)).
applies(Module, { true, RuleState }, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, [{ Module, RuleState }|NewActiveAcc] };
applies(_Module, { false, _RuleState }, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, NewActiveAcc };
applies(Module, { notyet, RuleState }, UndecidedAcc, NewActiveAcc) -> { [{ Module, RuleState }|UndecidedAcc], NewActiveAcc }.
-apply_next_rule_clientside(_ImapSession, ClientData, ActiveRulesAcc, []) -> { ClientData, lists:reverse(ActiveRulesAcc) };
-apply_next_rule_clientside(ImapSession, ClientData, ActiveRulesAcc, [{ Module, RuleState }|Rules]) ->
- { Data, NewState } = Module:apply_to_client_message(ImapSession, ClientData, RuleState),
- apply_next_rule_clientside(ImapSession, Data, [{ Module, NewState } | ActiveRulesAcc], Rules).
+apply_next_rule_clientside(_ImapSession, ClientData, _SplitCommand, ActiveRulesAcc, []) -> { ClientData, lists:reverse(ActiveRulesAcc) };
+apply_next_rule_clientside(ImapSession, ClientData, SplitCommand, ActiveRulesAcc, [{ Module, RuleState }|Rules]) ->
+ { Data, NewState } = Module:apply_to_client_message(ImapSession, ClientData, SplitCommand, RuleState),
+ apply_next_rule_clientside(ImapSession, Data, SplitCommand, [{ Module, NewState } | ActiveRulesAcc], Rules).
relay_response(Socket, Data, false) ->
%lager:debug("Sending over non-secure socket ..."),
diff --git a/apps/kolab_guam/src/rules/kolab_guam_rule_filter_groupware.erl b/apps/kolab_guam/src/rules/kolab_guam_rule_filter_groupware.erl
--- a/apps/kolab_guam/src/rules/kolab_guam_rule_filter_groupware.erl
+++ b/apps/kolab_guam/src/rules/kolab_guam_rule_filter_groupware.erl
@@ -16,7 +16,7 @@
%% along with this program. If not, see <http://www.gnu.org/licenses/>.
-module(kolab_guam_rule_filter_groupware).
--export([new/1, applies/3, imap_data/3, apply_to_client_message/3, apply_to_server_message/3]).
+-export([new/1, applies/4, imap_data/3, apply_to_client_message/4, apply_to_server_message/3]).
-behavior(kolab_guam_rule).
-record(state, { blacklist = [], tag = <<>>, active = false, last_chunk = <<>>,
@@ -24,14 +24,13 @@
new(_Config) -> #state { blacklist = undefined }.
-applies(_ConnectionDetails, Buffer, State) ->
- { _Tag, Command, Data } = eimap_utils:split_command_into_components(Buffer),
- %lager:debug("********** Checking ...~n Command: ~s ~s", [Command, Data]),
- { apply_if_id_matches(Command, Data, State), State }.
+applies(_ConnectionDetails, _Buffer, { _Tag, Command, Data }, State) ->
+ Applies = apply_if_id_matches(Command, Data, State),
+ %lager:debug("********** Checking ...~n Command: ~s ~s, Result ~p", [Command, Data, Applies]),
+ { Applies, State }.
-apply_to_client_message(ImapSession, Buffer, State) ->
- { Tag, Command, Data } = eimap_utils:split_command_into_components(Buffer),
- { Active, StateTag }=
+apply_to_client_message(ImapSession, Buffer, { Tag, Command, Data }, State) ->
+ { Active, StateTag } =
case is_triggering_command(Command, Data, State) of
true -> fetch_metadata(ImapSession, State), { true, Tag };
_ -> { false, <<>> }

File Metadata

Mime Type
text/plain
Expires
Sun, Apr 5, 12:56 PM (16 h, 51 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831377
Default Alt Text
D215.1775393811.diff (15 KB)

Event Timeline