Changeset View
Changeset View
Standalone View
Standalone View
apps/kolab_guam/src/kolab_guam_session.erl
Show All 21 Lines | |||||
%% API | %% API | ||||
-export([ start_link/6 ]). | -export([ start_link/6 ]). | ||||
%% gen_server callbacks | %% gen_server callbacks | ||||
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). | ||||
%% state record definition | %% state record definition | ||||
-record(state, { listen_socket, socket = undefined, super_pid, tls_config = [], client_implicit_tls = false, client_tls_active = false, server_config = [], | -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 }). | ||||
%% public API | %% public API | ||||
start_link(SupervisorPID, ListenSocket, ImapConfig, ImplicitTLS, TLSConfig, Rules) -> gen_server:start_link(?MODULE, [SupervisorPID, ListenSocket, ImapConfig, ImplicitTLS, TLSConfig, Rules], []). | start_link(SupervisorPID, ListenSocket, ImapConfig, ImplicitTLS, TLSConfig, Rules) -> gen_server:start_link(?MODULE, [SupervisorPID, ListenSocket, ImapConfig, ImplicitTLS, TLSConfig, Rules], []). | ||||
%% gen_server API | %% gen_server API | ||||
init([SupervisorPID, ListenSocket, ServerConfig, ImplicitTLS, TLSConfig, Rules]) -> | init([SupervisorPID, ListenSocket, ServerConfig, ImplicitTLS, TLSConfig, Rules]) -> | ||||
%% accepting a connection is blocking .. so do it async | %% accepting a connection is blocking .. so do it async | ||||
%% lager:debug("Starting a session handler on socket ~p for listener ~p", [ListenSocket, SupervisorPID]), | %% lager:debug("Starting a session handler on socket ~p for listener ~p", [ListenSocket, SupervisorPID]), | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | case proplists:get_value(Module, ActiveRules) of | ||||
[], ActiveRules)) | [], ActiveRules)) | ||||
end, | end, | ||||
%TODO: should we also support non-active rules doing imapy things here? | %TODO: should we also support non-active rules doing imapy things here? | ||||
{ noreply, State#state{ rules_active = NewActiveRules } }; | { noreply, State#state{ rules_active = NewActiveRules } }; | ||||
handle_info({ imap_server_response, Data }, #state{ socket = Socket, imap_session = ImapSession, client_tls_active = TLS, deflator = Deflator, rules_active = ActiveRules } = State) -> | handle_info({ imap_server_response, Data }, #state{ socket = Socket, imap_session = ImapSession, client_tls_active = TLS, deflator = Deflator, rules_active = ActiveRules } = State) -> | ||||
%lager:debug("FROM SERVER: ~s", [Data]), | %lager:debug("FROM SERVER: ~s", [Data]), | ||||
{ ModifiedData, CurrentlyActiveRules } = apply_ruleset_serverside(ImapSession, Data, ActiveRules), | { ModifiedData, CurrentlyActiveRules } = apply_ruleset_serverside(ImapSession, Data, ActiveRules), | ||||
relay_response(Socket, postprocess_server_data(Deflator, ModifiedData), TLS), | relay_response(Socket, postprocess_server_data(Deflator, ModifiedData), TLS), | ||||
{ noreply, State#state{ rules_active = CurrentlyActiveRules } }; | { noreply, State#state{ rules_active = CurrentlyActiveRules, current_command_split = undefined } }; | ||||
handle_info({ 'EXIT', PID, _Reason }, #state { imap_session = PID } = State) -> | handle_info({ 'EXIT', PID, _Reason }, #state { imap_session = PID } = State) -> | ||||
{ stop, normal, State#state{ imap_session = undefined } }; | { stop, normal, State#state{ imap_session = undefined } }; | ||||
handle_info(Info, State) -> | handle_info(Info, State) -> | ||||
lager:debug("Received unexpected info... ~p", [Info]), | lager:debug("Received unexpected info... ~p", [Info]), | ||||
{ noreply, State }. | { noreply, State }. | ||||
terminate(_Reason, #state{ inflator = Inflator, imap_session = ImapSession, deflator = Deflator, socket = Socket, client_implicit_tls = ImplicitTLS, client_tls_active = TLS }) -> | terminate(_Reason, #state{ inflator = Inflator, imap_session = ImapSession, deflator = Deflator, socket = Socket, client_implicit_tls = ImplicitTLS, client_tls_active = TLS }) -> | ||||
close_zlib_handle(Inflator), | close_zlib_handle(Inflator), | ||||
Show All 32 Lines | |||||
close_zlib_handle(undefined) -> ok; | close_zlib_handle(undefined) -> ok; | ||||
close_zlib_handle(Z) -> zlib:close(Z). | close_zlib_handle(Z) -> zlib:close(Z). | ||||
close_socket(_ImplicitTLS, _TLS, undefined) -> ok; | close_socket(_ImplicitTLS, _TLS, undefined) -> ok; | ||||
close_socket(_ImplicitTLS, true, Socket) -> ssl:close(Socket); | close_socket(_ImplicitTLS, true, Socket) -> ssl:close(Socket); | ||||
close_socket(true, _TLS, Socket) -> ssl:close(Socket); | close_socket(true, _TLS, Socket) -> ssl:close(Socket); | ||||
close_socket(_ImplicitTLS, _TLS, Socket) -> gen_tcp: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: multipacket input from clients | ||||
% TODO: refactor so starttls and compress commands can be made into rules | % 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]), | %lager:info("FROM CLIENT: ~s", [PreprocessData]), | ||||
{ TLSActive, CurrentSocket, CurrentInflator, CurrentDeflator, CurrentUndecidedRules, CurrentActiveRules } = | { TLSActive, CurrentSocket, CurrentInflator, CurrentDeflator, CurrentUndecidedRules, CurrentActiveRules, DataToBuffer, SplitCommand } = | ||||
case check_for_transmission_change_commands(TLS, TLSConfig, PreprocessData, Deflator, Socket) of | case check_for_transmission_change_commands(TLS, TLSConfig, PreprocessData, Deflator, Socket) of | ||||
{ socket_upgraded, SSLSocket } -> | { socket_upgraded, SSLSocket } -> | ||||
%% if we have upgraded our socket, then do so to the backend if that hasn't happened auomatically | %% 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 | case proplists:get_value(implicit_tls, ServerConfig, false) of | ||||
false -> eimap:starttls(ImapSession, undefined, undefined); | false -> eimap:starttls(ImapSession, undefined, undefined); | ||||
_ -> ok | _ -> ok | ||||
end, | end, | ||||
{ true, SSLSocket, Inflator, Deflator, UndecidedRules, ActiveRules }; | { true, SSLSocket, Inflator, Deflator, UndecidedRules, ActiveRules, <<>>, undefined }; | ||||
{ compression, NewInflator, NewDeflator } -> | { compression, NewInflator, NewDeflator } -> | ||||
eimap:compress(ImapSession), % TODO: make optional | eimap:compress(ImapSession), % TODO: make optional | ||||
{ TLS, Socket, NewInflator, NewDeflator, UndecidedRules, ActiveRules }; | { TLS, Socket, NewInflator, NewDeflator, UndecidedRules, ActiveRules, <<>>, undefined }; | ||||
nochange -> | nochange -> | ||||
%%lager:debug("... now applying rules"), | %%lager:debug("... now applying rules"), | ||||
{ ModifiedData, NewUndecidedRules, NewActiveRules } = apply_ruleset_clientside(ImapSession, Socket, PreprocessData, UndecidedRules, ActiveRules), | { ModifiedData, NewUndecidedRules, NewActiveRules, PostAction, NewSplitCommand } = apply_ruleset_clientside(ImapSession, Socket, PreprocessData, CurrentCommandSplit, UndecidedRules, ActiveRules), | ||||
%%lager:info("The modified data is: ~s", [ModifiedData]), | %%lager:info("The modified data is: ~s", [ModifiedData]), | ||||
%lager:info("The post-processed data is: ~s", [PostProcessed]), | %lager:info("The post-processed data is: ~s", [PostProcessed]), | ||||
BufferThisData = | |||||
case PostAction of | |||||
perform_passthrough -> | |||||
eimap:passthrough_data(ImapSession, ModifiedData), | eimap:passthrough_data(ImapSession, ModifiedData), | ||||
{ TLS, Socket, Inflator, Deflator, NewUndecidedRules, NewActiveRules} | <<>>; | ||||
buffer_data -> | |||||
Data | |||||
end, | |||||
{ TLS, Socket, Inflator, Deflator, NewUndecidedRules, NewActiveRules, BufferThisData, NewSplitCommand } | |||||
end, | end, | ||||
set_socket_active(TLSActive, CurrentSocket), | set_socket_active(TLSActive, CurrentSocket), | ||||
PrevBuffered = State#state.buffered_client_data, | |||||
{ noreply, State#state{ rules_deciding = CurrentUndecidedRules, rules_active = CurrentActiveRules, | { noreply, State#state{ rules_deciding = CurrentUndecidedRules, rules_active = CurrentActiveRules, | ||||
socket = CurrentSocket, client_tls_active = TLSActive, | socket = CurrentSocket, client_tls_active = TLSActive, | ||||
inflator = CurrentInflator, deflator = CurrentDeflator } }. | inflator = CurrentInflator, deflator = CurrentDeflator, | ||||
buffered_client_data = <<PrevBuffered/binary, DataToBuffer/binary>>, | |||||
preprocess_client_data(undefined, Data) -> | current_command_split = SplitCommand } }. | ||||
Data; | |||||
preprocess_client_data(Z, Data) -> | preprocess_client_data(undefined, Data, #state{ buffered_client_data = Buffered }) -> | ||||
joined(zlib:inflate(Z, Data), <<>>). | <<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) -> | postprocess_server_data(undefined, Data) -> | ||||
%% we aren't compressing so there is nothing to do | %% we aren't compressing so there is nothing to do | ||||
Data; | Data; | ||||
postprocess_server_data(Z, Data) -> | postprocess_server_data(Z, Data) -> | ||||
joined(zlib:deflate(Z, Data, sync), <<>>). | joined(zlib:deflate(Z, Data, sync), <<>>). | ||||
joined([], Binary) -> Binary; | joined([], Binary) -> Binary; | ||||
Show All 22 Lines | apply_ruleset_serverside(ImapSession, ServerData, CurrentlyActiveRules) -> | ||||
apply_next_rule_serverside(ImapSession, ServerData, [], CurrentlyActiveRules). | apply_next_rule_serverside(ImapSession, ServerData, [], CurrentlyActiveRules). | ||||
apply_next_rule_serverside(_ImapSession, ServerData, ActiveRulesAcc, []) -> { ServerData, lists:reverse(ActiveRulesAcc) }; | apply_next_rule_serverside(_ImapSession, ServerData, ActiveRulesAcc, []) -> { ServerData, lists:reverse(ActiveRulesAcc) }; | ||||
apply_next_rule_serverside(ImapSession, ServerData, ActiveRulesAcc, [{ Module, RuleState } | ActiveRules]) -> | apply_next_rule_serverside(ImapSession, ServerData, ActiveRulesAcc, [{ Module, RuleState } | ActiveRules]) -> | ||||
%TODO: allow rules to remove themselves from the action during serverside processing? | %TODO: allow rules to remove themselves from the action during serverside processing? | ||||
{ ModifiedData, ModifiedRuleState } = Module:apply_to_server_message(ImapSession, ServerData, RuleState), | { ModifiedData, ModifiedRuleState } = Module:apply_to_server_message(ImapSession, ServerData, RuleState), | ||||
apply_next_rule_serverside(ImapSession, ModifiedData, [{ Module, ModifiedRuleState } | ActiveRulesAcc], ActiveRules). | apply_next_rule_serverside(ImapSession, ModifiedData, [{ Module, ModifiedRuleState } | ActiveRulesAcc], ActiveRules). | ||||
apply_ruleset_clientside(ImapSession, Socket, ClientData, UndecidedRules, CurrentlyActiveRules) -> | apply_ruleset_clientside(_ImapSession, _Socket, ClientData, _CurrentCommandSplit, [], []) -> | ||||
{ StillUndecided, NewlyActive } = check_undecided(Socket, ClientData, UndecidedRules), | { ClientData, [], [], perform_passthrough, undefined }; | ||||
apply_ruleset_clientside(ImapSession, Socket, ClientData, CurrentCommandSplit, UndecidedRules, CurrentlyActiveRules) -> | |||||
{ PostAction, SplitCommand } = | |||||
case CurrentCommandSplit of | |||||
undefined -> | |||||
case eimap_utils:split_command_into_components(ClientData) of | |||||
{ _Tag, <<>>, <<>> } -> { buffer_data, undefined }; | |||||
Split -> { perform_passthrough, Split } | |||||
end; | |||||
_ -> { perform_passthrough, CurrentCommandSplit } | |||||
end, | |||||
{ StillUndecided, NewlyActive } = check_undecided(Socket, ClientData, SplitCommand, UndecidedRules), | |||||
ActiveRules = CurrentlyActiveRules ++ NewlyActive, | ActiveRules = CurrentlyActiveRules ++ NewlyActive, | ||||
{ ModifiedData, ActiveRulesRun } = apply_next_rule_clientside(ImapSession, ClientData, [], ActiveRules), | { ModifiedData, ActiveRulesRun } = apply_next_rule_clientside(ImapSession, ClientData, SplitCommand, [], ActiveRules), | ||||
{ ModifiedData, StillUndecided, ActiveRulesRun }. | { ModifiedData, SplitCommand, StillUndecided, ActiveRulesRun, PostAction, SplitCommand }. | ||||
check_undecided(Socket, ClientData, Rules) -> check_next_undecided_rule(Socket, ClientData, Rules, { [], [] }). | check_undecided(Socket, ClientData, undefined, Rules) -> | ||||
check_next_undecided_rule(_Socket, _ClientData, [], Accs) -> Accs; | %% if we do not have a properly split command ... do nothing! | ||||
check_next_undecided_rule(Socket, ClientData, [Rule|Rules], { UndecidedAcc, NewActiveAcc }) -> | 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, | { Module, RuleState } = Rule, | ||||
%%lager:debug("Does ~p apply with state ~p? let's find out!", [Module, RuleState]), | %%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, { true, RuleState }, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, [{ Module, RuleState }|NewActiveAcc] }; | ||||
applies(_Module, { false, _RuleState }, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, NewActiveAcc }; | applies(_Module, { false, _RuleState }, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, NewActiveAcc }; | ||||
applies(Module, { notyet, RuleState }, UndecidedAcc, NewActiveAcc) -> { [{ Module, RuleState }|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, _SplitCommand, ActiveRulesAcc, []) -> { ClientData, lists:reverse(ActiveRulesAcc) }; | ||||
apply_next_rule_clientside(ImapSession, ClientData, ActiveRulesAcc, [{ Module, RuleState }|Rules]) -> | apply_next_rule_clientside(ImapSession, ClientData, SplitCommand, ActiveRulesAcc, [{ Module, RuleState }|Rules]) -> | ||||
{ Data, NewState } = Module:apply_to_client_message(ImapSession, ClientData, RuleState), | { Data, NewState } = Module:apply_to_client_message(ImapSession, ClientData, SplitCommand, RuleState), | ||||
apply_next_rule_clientside(ImapSession, Data, [{ Module, NewState } | ActiveRulesAcc], Rules). | apply_next_rule_clientside(ImapSession, Data, SplitCommand, [{ Module, NewState } | ActiveRulesAcc], Rules). | ||||
relay_response(Socket, Data, false) -> | relay_response(Socket, Data, false) -> | ||||
%lager:debug("Sending over non-secure socket ..."), | %lager:debug("Sending over non-secure socket ..."), | ||||
gen_tcp:send(Socket, Data); | gen_tcp:send(Socket, Data); | ||||
relay_response(Socket, Data, _TLS) -> | relay_response(Socket, Data, _TLS) -> | ||||
%lager:debug("Sending over TLS!"), | %lager:debug("Sending over TLS!"), | ||||
ssl:send(Socket, Data). | ssl:send(Socket, Data). | ||||
▲ Show 20 Lines • Show All 109 Lines • Show Last 20 Lines |