Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117906789
D215.1775390658.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
15 KB
Referenced Files
None
Subscribers
None
D215.1775390658.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 5, 12:04 PM (15 h, 59 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831377
Default Alt Text
D215.1775390658.diff (15 KB)
Attached To
Mode
D215: wait until we have at least a tag+command for filtering to commence
Attached
Detach File
Event Timeline