Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117878338
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
9 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rG guam
Attached
Detach File
Event Timeline