diff --git a/apps/kolab_guam/test/kolab_guam_session_SUITE.erl b/apps/kolab_guam/test/kolab_guam_session_SUITE.erl new file mode 100644 index 0000000..c91cc0c --- /dev/null +++ b/apps/kolab_guam/test/kolab_guam_session_SUITE.erl @@ -0,0 +1,172 @@ +%% Copyright 2015 Kolab Systems AG (http://www.kolabsys.com) +%% +%% Christian Mollekopf (Kolab Systems) +%% +%% 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 . + +-module(kolab_guam_session_SUITE). + +% easier than exporting by name +-compile(export_all). + +% required for common_test to work +-include_lib("common_test/include/ct.hrl"). +-include("../src/kolab_guam_session.hrl"). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% common test callbacks %% +%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% Specify a list of all unit test functions +all() -> [ + kolab_guam_session_test + ]. + +% required, but can just return Config. this is a suite level setup function. +init_per_suite(Config) -> + Config. + +% required, but can just return Config. this is a suite level tear down function. +end_per_suite(Config) -> + Config. + +% optional, can do function level setup for all functions, +% or for individual functions by matching on TestCase. +init_per_testcase(_TestCase, Config) -> + Config. + +% optional, can do function level tear down for all functions, +% or for individual functions by matching on TestCase. +end_per_testcase(_TestCase, Config) -> + Config. + + +initState() -> + { ok, #state{ listen_socket = undefined, super_pid = undefined, client_implicit_tls = false, tls_config = [], server_config = [], rules_deciding = [] } }. + + +kolab_guam_session_test(_TestConfig) -> + %% setup boilerplate + ServerConfig = kolab_guam_sup:default_imap_server_config(), + { ok, ImapSession } = eimap:start_link(ServerConfig), + { ok, Socket } = gen_tcp:listen(9964, [ { reuseaddr, true }, {active, false}, inet6 ]), + + lager:start(), + lager:set_loglevel(lager_console_backend, debug), + + ActiveRules = [{ kolab_guam_rule_filter_groupware, kolab_guam_rule_filter_groupware:new({}) }], + + State = #state{ + socket = Socket, + super_pid = undefined, + tls_config = [], + client_implicit_tls = false, + client_tls_active= false, + server_config = ServerConfig, + rules_active = [], + rules_deciding = ActiveRules, + imap_session = ImapSession, + inflator = undefined, + deflator = undefined, + buffered_client_data = <<>>, + current_command_split = undefined, + command_split_reset_trigger = undefined + }, + + + % Run without rules + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = undefined, + command_split_reset_trigger = [] %FIXME ? + }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = [] }), + + + % Activate filtering + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = undefined, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), + + + % Don't activate filtering + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = undefined, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [] + }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test/KOLAB\")\r\n">>}, State#state{ rules_deciding = ActiveRules }), + + + % Lone tag in a packet. Can/Could happen with iPhone apparently. (See commit 89f9dc93757c68032ed17f42838858bdfaefa408) + {noreply, #state{ buffered_client_data = <<"y1">>, rules_active = [] } = IntermediateState1} = kolab_guam_session:handle_info({tcp, Socket, <<"y1">>}, State#state{ rules_deciding = ActiveRules }), + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\" \"os\"">>}, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + }} = kolab_guam_session:handle_info({tcp, Socket, <<" ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState1), + + + % Lone tag in a packet, but with a \r\n before. See guam-0.9.2-stalling-client-buffer-and-split-command-handling.patch (This triggers the odd List buffering case) + {noreply, #state{ buffered_client_data = <<"y1 ">>, rules_active = [] } = IntermediateState2} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\ny1 ">>}, State#state{ rules_deciding = ActiveRules }), + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\"">>}, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + }} = kolab_guam_session:handle_info({tcp, Socket, <<"ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\n">>}, IntermediateState2), + + + % Empty line (should be considered a complete command and should not be buffered) + {noreply, #state{ buffered_client_data = <<>>, rules_active = [] } = IntermediateState3} = kolab_guam_session:handle_info({tcp, Socket, <<"\r\n">>}, State#state{ rules_deciding = ActiveRules }), + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = {<<"y1">>,<<"ID">>,<<"(\"name\" \"Test\"">>}, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + rules_active = [{kolab_guam_rule_filter_groupware, _}] + }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\n">>}, IntermediateState3), + + + + lager:info("Command split ~p", [eimap_utils:split_command_into_components(<<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>)]), + + % Activate with multi-line packet + {noreply, #state{ + server_config = ServerConfig, + buffered_client_data = <<>>, + % current_command_split = undefined, + command_split_reset_trigger = reset_for_next_client_command, + rules_deciding = [], + % Make sure that we have processed y3 in here. + rules_active = [{kolab_guam_rule_filter_groupware, {state,undefined,<<"y3">>,true,<<>>,[<<"LIST">>,<<"list">>,<<"XLIST">>,<<"xlist">>,<<"LSUB">>,<<"lsub">>]}}] + % rules_active = [{kolab_guam_rule_filter_groupware, _}] + }} = kolab_guam_session:handle_info({tcp, Socket, <<"y1 ID (\"name\" \"Test\")\r\ny2 ENABLE QRESYNC\r\ny3 LIST \"\" \"%\" RETURN (SUBSCRIBED))\r\n">>}, State#state{ rules_deciding = ActiveRules }), + + lager:info("Done"). +