Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None
diff --git a/apps/kolab_guam/src/kolab_guam_session.erl b/apps/kolab_guam/src/kolab_guam_session.erl
index 08bf55b..a73e4f5 100644
--- a/apps/kolab_guam/src/kolab_guam_session.erl
+++ b/apps/kolab_guam/src/kolab_guam_session.erl
@@ -1,125 +1,189 @@
%% Copyright 2015 Kolab Systems AG (http://www.kolabsys.com)
%%
%% Aaron Seigo (Kolab Systems) <seigo a kolabsys.com>
%%
%% This program is free software: you can redistribute it and/or modify
%% it under the terms of the GNU General Public License as published by
%% the Free Software Foundation, either version 3 of the License, or
%% (at your option) any later version.
%%
%% This program is distributed in the hope that it will be useful,
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
%% GNU General Public License for more details.
%%
%% You should have received a copy of the GNU General Public License
%% along with this program. If not, see <http://www.gnu.org/licenses/>.
-module(kolab_guam_session).
-behaviour(gen_server).
-include("kolab_guam_tls.hrl").
-include_lib("eimap/src/eimap.hrl").
%% API
-export([ start_link/4 ]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%% state record definition
-record(state, { socket, super_pid, tls_data = #kolab_guam_tls_data{}, tls_active = false, server_config = #eimap_server_config{},
- rules_active = [], rules_deciding = [], imap_session }).
+ rules_active = [], rules_deciding = [], imap_session, inflator, deflator, server_inflator, server_deflator }).
%% public API
start_link(SupervisorPID, ListenSocket, TLSConfig, Rules) -> gen_server:start_link(?MODULE, [SupervisorPID, ListenSocket, TLSConfig, Rules], []).
%% gen_server API
init([SupervisorPID, ListenSocket, TLSConfig, Rules]) ->
%% accepting a connection is blocking .. so do it async
lager:debug("Starting a session handler on socket ~p for listener ~p", [ListenSocket, SupervisorPID]),
gen_server:cast(self(), accept),
ServerConfig = kolab_guam_sup:default_imap_server_config(),
%%lager:debug("Server config is ~p", [ServerConfig]),
{ ok, #state{ socket = ListenSocket, super_pid = SupervisorPID, tls_data = TLSConfig, server_config = ServerConfig, rules_deciding = init_rules(Rules) } }.
handle_call(_Request, _From, State) ->
{ reply, ok, State }.
handle_cast(accept, State = #state{ socket = ListenSocket, super_pid = SupervisorPID, server_config = ServerConfig }) ->
{ ok, AcceptSocket } = gen_tcp:accept(ListenSocket),
ok = inet:setopts(AcceptSocket, [{ active, once }, { mode, binary }]),
%% start a new accepting process to replace this one, which is now i use
supervisor:start_child(SupervisorPID, []),
ok = inet:setopts(ListenSocket, [{ active, once }, { mode, binary }]),
{ ok, ImapSession } = eimap:start_link(ServerConfig),
eimap:start_passthrough(ImapSession, self()),
eimap:connect(ImapSession),
{ noreply, State#state{ socket = AcceptSocket, imap_session = ImapSession } };
handle_cast(_Msg, State) ->
{ noreply, State }.
-handle_info({ tcp, Socket, Data }, #state{ rules_deciding = UndecidedRules, rules_active = ActiveRules, socket = Socket, imap_session = ImapSession } = State) ->
- lager:info("FROM CLIENT: ~p", [Data]),
+handle_info({ tcp, Socket, Data }, #state{ rules_deciding = UndecidedRules, rules_active = ActiveRules, socket = Socket, imap_session = ImapSession, inflator = Inflator, deflator = Deflator } = State) ->
%%TODO: multipacket input from clients
- { ModifiedData, NewUndecidedRules, NewActiveRules } = apply_ruleset(Socket, Data, UndecidedRules, ActiveRules),
- eimap:passthrough_data(ImapSession, ModifiedData),
+ PreprocessData = preprocess_client_data(Inflator, Data),
+ %%lager:info("FROM CLIENT: ~s", [Data]),
+ { ModifiedData, NewUndecidedRules, NewActiveRules } = apply_ruleset(Socket, PreprocessData, UndecidedRules, ActiveRules),
+ %%lager:info("The modified data is: ~s", [ModifiedData]),
+ PostProcessed = postprocess_client_data(Deflator, ModifiedData),
+ %%lager:info("The post-processed data is: ~s", [PostProcessed]),
+ eimap:passthrough_data(ImapSession, PostProcessed),
inet:setopts(Socket, [{ active, once }]),
{ noreply, State#state{ rules_deciding = NewUndecidedRules, rules_active = NewActiveRules } };
-handle_info({ imap_server_response, Data }, #state{ socket = Socket, tls_active = TLS } = State) ->
- lager:info("FROM SERVER: ~p", [Data]),
- relay_response(Socket, Data, TLS),
- { noreply, State };
+handle_info({ imap_server_response, Data }, #state{ socket = Socket, tls_active = TLS, server_inflator = Inflator, server_deflator = Deflator } = State) ->
+ { ProcessedData, NewState } = preprocess_server_data(Inflator, Data, State),
+ %%TODO: send through rules
+ %%lager:info("FROM SERVER: ~s", [Data]),
+ relay_response(Socket, postprocess_server_data(Deflator, ProcessedData), TLS),
+ { noreply, NewState };
handle_info(_Info, State) ->
{ noreply, State }.
-terminate(_Reason, _State) ->
+terminate(_Reason, #state{ inflator = Inflator, deflator = Deflator, server_inflator = ServerInflator, server_deflator = ServerDeflator }) ->
+ close_zlib_handle(Inflator),
+ close_zlib_handle(Deflator),
+ close_zlib_handle(ServerInflator),
+ close_zlib_handle(ServerDeflator),
ok.
code_change(_OldVsn, State, _Extra) ->
{ ok, State }.
%% private API
+close_zlib_handle(undefined) -> ok;
+close_zlib_handle(Z) -> zlib:close(Z).
+
+preprocess_client_data(undefined, Data) ->
+ Data;
+preprocess_client_data(Z, Data) ->
+ inflate(Z, Data).
+
+postprocess_client_data(undefined, Data) ->
+ %% we aren't compressing so there is nothing to do
+ Data;
+postprocess_client_data(Z, Data) ->
+ %% compression is happening .. but eimap does not yet support that <-- TODO
+ deflate(Z, Data).
+
+preprocess_server_data(undefined, Data, State) ->
+ case binary:match(Data, <<"OK DEFLATE active">>, [ { scope, { 0, min(byte_size(Data), 30) } } ]) of
+ nomatch ->
+ %% we aren't compressing so there is nothing to do
+ { Data, State };
+ _ ->
+ %% create an inflate/deflate pair for use with the client
+ Inflator = zlib:open(),
+ ok = zlib:inflateInit(Inflator, -15),
+ Deflator = zlib:open(),
+ ok = zlib:deflateInit(Deflator, 1, deflated, -15, 8, default),
+ ServerInflator = zlib:open(),
+
+ %% create an inflate/defate pair for use with the server
+ ok = zlib:inflateInit(ServerInflator, -15),
+ ServerDeflator = zlib:open(),
+ ok = zlib:deflateInit(ServerDeflator, 1, deflated, -15, 8, default),
+
+ %% return data with State modified to include the zlib sessions
+ { Data, State#state{ inflator = Inflator, deflator = Deflator, server_inflator = ServerInflator, server_deflator = ServerDeflator } }
+ end;
+preprocess_server_data(Z, Data, State) ->
+ { inflate(Z, Data), State }.
+
+postprocess_server_data(undefined, Data) ->
+ %% we aren't compressing so there is nothing to do
+ Data;
+postprocess_server_data(Z, Data) ->
+ %% compression is happening .. but eimap does not yet support that <-- TODO
+ deflate(Z, Data).
+
+deflate(Z, Data) ->
+ [Deflated] = zlib:deflate(Z, Data, sync),
+ Deflated.
+
+inflate(Z, Data) ->
+ Inflated = zlib:inflate(Z, Data),
+ Inflated.
+
init_rules(RuleConfig) -> init_rule(RuleConfig, []).
init_rule([], Acc) -> Acc;
init_rule([{ RuleName, Config }|RuleConfig], Acc) ->
Module = full_rule_name(RuleName),
%% we try to new the module, but if something goes wrong, e.g. it does not exist,
%% then we skip this config block because it is BROKEN
try Module:new(Config) of
ModuleState -> init_rule(RuleConfig, [{ Module, ModuleState }|Acc])
catch
Type:Error ->
lager:warning("Could not create rule for ~p due to failure: ~p ~p", [RuleName, Type, Error]),
init_rule(RuleConfig, Acc)
end;
init_rule([_|RuleConfig], Acc) ->
init_rule(RuleConfig, Acc).
full_rule_name(Module) when is_atom(Module) -> list_to_atom("kolab_guam_rule_" ++ atom_to_list(Module)).
apply_ruleset(Socket, ClientData, UndecidedRules, CurrentlyActiveRules) ->
{ StillUndecided, NewlyActive } = check_undecided(Socket, ClientData, UndecidedRules),
ActiveRules = CurrentlyActiveRules ++ NewlyActive,
ModifiedData = apply_next_rule(ClientData, ActiveRules),
{ ModifiedData, StillUndecided, ActiveRules }.
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 }) ->
{ Module, RuleState } = Rule,
check_next_undecided_rule(Socket, ClientData, Rules, applies(Rule, Module:applies(Socket, ClientData, RuleState), UndecidedAcc, NewActiveAcc)).
applies(Rule, true, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, [Rule|NewActiveAcc] };
applies(_Rule, false, UndecidedAcc, NewActiveAcc) -> { UndecidedAcc, NewActiveAcc };
applies(Rule, notyet, UndecidedAcc, NewActiveAcc) -> { [Rule|UndecidedAcc], NewActiveAcc }.
apply_next_rule(ClientData, []) -> ClientData;
apply_next_rule(ClientData, [{ Module, RuleState }|Rules]) ->
{ Data, NewState } = Module:apply(ClientData, RuleState), %%FIXME: new state (if any) is not being applied
apply_next_rule(Data, Rules).
relay_response(Socket, Data, false) ->
lager:info("Sending over non-secure socket ..."),
gen_tcp:send(Socket, Data);
relay_response(Socket, Data, _TLS) ->
ssl:send(Socket, Data).

File Metadata

Mime Type
text/x-diff
Expires
Sun, Apr 5, 10:07 PM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18831374
Default Alt Text
(9 KB)

Event Timeline