aboutsummaryrefslogtreecommitdiff
path: root/test/ejabberd_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'test/ejabberd_SUITE.erl')
-rw-r--r--test/ejabberd_SUITE.erl2599
1 files changed, 592 insertions, 2007 deletions
diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl
index d3e7ec668..5739fa780 100644
--- a/test/ejabberd_SUITE.erl
+++ b/test/ejabberd_SUITE.erl
@@ -1,31 +1,47 @@
%%%-------------------------------------------------------------------
-%%% @author Evgeniy Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2002-2016, ProcessOne
-%%% @doc
-%%%
-%%% @end
+%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 2 Jun 2013 by Evgeniy Khramtsov <ekhramtsov@process-one.net>
-%%%-------------------------------------------------------------------
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%%
+%%% 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 2 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, write to the Free Software Foundation, Inc.,
+%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+%%%
+%%%----------------------------------------------------------------------
-module(ejabberd_SUITE).
-
-compile(export_all).
--import(suite, [init_config/1, connect/1, disconnect/1,
- recv/0, send/2, send_recv/2, my_jid/1, server_jid/1,
- pubsub_jid/1, proxy_jid/1, muc_jid/1, muc_room_jid/1,
- mix_jid/1, mix_room_jid/1, get_features/2, re_register/1,
- is_feature_advertised/2, subscribe_to_events/1,
- is_feature_advertised/3, set_opt/3, auth_SASL/2,
- wait_for_master/1, wait_for_slave/1,
- make_iq_result/1, start_event_relay/0,
+-import(suite, [init_config/1, connect/1, disconnect/1, recv_message/1,
+ recv/1, recv_presence/1, send/2, send_recv/2, my_jid/1,
+ server_jid/1, pubsub_jid/1, proxy_jid/1, muc_jid/1,
+ muc_room_jid/1, my_muc_jid/1, peer_muc_jid/1,
+ mix_jid/1, mix_room_jid/1, get_features/2, recv_iq/1,
+ re_register/1, is_feature_advertised/2, subscribe_to_events/1,
+ is_feature_advertised/3, set_opt/3,
+ auth_SASL/2, auth_SASL/3, auth_SASL/4,
+ wait_for_master/1, wait_for_slave/1, flush/1,
+ make_iq_result/1, start_event_relay/0, alt_room_jid/1,
stop_event_relay/1, put_event/2, get_event/1,
- bind/1, auth/1, open_session/1, zlib/1, starttls/1,
- close_socket/1]).
-
+ bind/1, auth/1, auth/2, open_session/1, open_session/2,
+ zlib/1, starttls/1, starttls/2, close_socket/1, init_stream/1,
+ auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2,
+ set_roster/3, del_roster/1]).
-include("suite.hrl").
suite() ->
- [{timetrap, {seconds,120}}].
+ [{timetrap, {seconds, 120}}].
init_per_suite(Config) ->
NewConfig = init_config(Config),
@@ -35,26 +51,20 @@ init_per_suite(Config) ->
LDIFFile = filename:join([DataDir, "ejabberd.ldif"]),
{ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])),
{ok, _} = ldap_srv:start(LDIFFile),
+ inet_db:add_host({127,0,0,1}, [binary_to_list(?S2S_VHOST),
+ binary_to_list(?MNESIA_VHOST),
+ binary_to_list(?UPLOAD_VHOST)]),
+ inet_db:set_domain(binary_to_list(p1_rand:get_string())),
+ inet_db:set_lookup([file, native]),
start_ejabberd(NewConfig),
NewConfig.
-start_ejabberd(Config) ->
- case proplists:get_value(backends, Config) of
- all ->
- ok = application:start(ejabberd, transient);
- Backends when is_list(Backends) ->
- Hosts = lists:map(fun(Backend) -> Backend ++ ".localhost" end, Backends),
- application:load(ejabberd),
- AllHosts = Hosts ++ ["localhost"], %% We always need localhost for the generic no_db tests
- application:set_env(ejabberd, hosts, AllHosts),
- ok = application:start(ejabberd, transient)
- end.
+start_ejabberd(_) ->
+ {ok, _} = application:ensure_all_started(ejabberd, transient).
end_per_suite(_Config) ->
application:stop(ejabberd).
--define(BACKENDS, [mnesia,redis,mysql,pgsql,sqlite,ldap,extauth,riak]).
-
init_per_group(Group, Config) ->
case lists:member(Group, ?BACKENDS) of
false ->
@@ -67,7 +77,7 @@ init_per_group(Group, Config) ->
do_init_per_group(Group, Config);
Backends ->
%% Skipped backends that were not explicitely enabled
- case lists:member(atom_to_list(Group), Backends) of
+ case lists:member(Group, Backends) of
true ->
do_init_per_group(Group, Config);
false ->
@@ -78,7 +88,7 @@ init_per_group(Group, Config) ->
do_init_per_group(no_db, Config) ->
re_register(Config),
- Config;
+ set_opt(persistent_room, false, Config);
do_init_per_group(mnesia, Config) ->
mod_muc:shutdown_rooms(?MNESIA_VHOST),
set_opt(server, ?MNESIA_VHOST, Config);
@@ -89,7 +99,7 @@ do_init_per_group(mysql, Config) ->
case catch ejabberd_sql:sql_query(?MYSQL_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?MYSQL_VHOST),
- create_sql_tables(mysql, ?config(base_dir, Config)),
+ clear_sql_tables(mysql, ?config(base_dir, Config)),
set_opt(server, ?MYSQL_VHOST, Config);
Err ->
{skip, {mysql_not_available, Err}}
@@ -98,7 +108,7 @@ do_init_per_group(pgsql, Config) ->
case catch ejabberd_sql:sql_query(?PGSQL_VHOST, [<<"select 1;">>]) of
{selected, _, _} ->
mod_muc:shutdown_rooms(?PGSQL_VHOST),
- create_sql_tables(pgsql, ?config(base_dir, Config)),
+ clear_sql_tables(pgsql, ?config(base_dir, Config)),
set_opt(server, ?PGSQL_VHOST, Config);
Err ->
{skip, {pgsql_not_available, Err}}
@@ -115,18 +125,32 @@ do_init_per_group(ldap, Config) ->
set_opt(server, ?LDAP_VHOST, Config);
do_init_per_group(extauth, Config) ->
set_opt(server, ?EXTAUTH_VHOST, Config);
-do_init_per_group(riak, Config) ->
- case ejabberd_riak:is_connected() of
- true ->
- mod_muc:shutdown_rooms(?RIAK_VHOST),
- NewConfig = set_opt(server, ?RIAK_VHOST, Config),
- clear_riak_tables(NewConfig);
- Err ->
- {skip, {riak_not_available, Err}}
- end;
-do_init_per_group(_GroupName, Config) ->
+do_init_per_group(s2s, Config) ->
+ ejabberd_config:set_option({s2s_use_starttls, ?COMMON_VHOST}, required),
+ ejabberd_config:set_option(ca_file, "ca.pem"),
+ Port = ?config(s2s_port, Config),
+ set_opt(server, ?COMMON_VHOST,
+ set_opt(xmlns, ?NS_SERVER,
+ set_opt(type, server,
+ set_opt(server_port, Port,
+ set_opt(stream_from, ?S2S_VHOST,
+ set_opt(lang, <<"">>, Config))))));
+do_init_per_group(component, Config) ->
+ Server = ?config(server, Config),
+ Port = ?config(component_port, Config),
+ set_opt(xmlns, ?NS_COMPONENT,
+ set_opt(server, <<"component.", Server/binary>>,
+ set_opt(type, component,
+ set_opt(server_port, Port,
+ set_opt(stream_version, undefined,
+ set_opt(lang, <<"">>, Config))))));
+do_init_per_group(GroupName, Config) ->
Pid = start_event_relay(),
- set_opt(event_relay, Pid, Config).
+ NewConfig = set_opt(event_relay, Pid, Config),
+ case GroupName of
+ anonymous -> set_opt(anonymous, true, NewConfig);
+ _ -> NewConfig
+ end.
end_per_group(mnesia, _Config) ->
ok;
@@ -144,73 +168,133 @@ end_per_group(ldap, _Config) ->
ok;
end_per_group(extauth, _Config) ->
ok;
-end_per_group(riak, _Config) ->
+end_per_group(component, _Config) ->
ok;
+end_per_group(s2s, Config) ->
+ Server = ?config(server, Config),
+ ejabberd_config:set_option({s2s_use_starttls, Server}, false);
end_per_group(_GroupName, Config) ->
stop_event_relay(Config),
- ok.
+ set_opt(anonymous, false, Config).
init_per_testcase(stop_ejabberd, Config) ->
- open_session(bind(auth(connect(Config))));
+ NewConfig = set_opt(resource, <<"">>,
+ set_opt(anonymous, true, Config)),
+ open_session(bind(auth(connect(NewConfig))));
init_per_testcase(TestCase, OrigConfig) ->
- subscribe_to_events(OrigConfig),
- Server = ?config(server, OrigConfig),
- Resource = ?config(resource, OrigConfig),
- MasterResource = ?config(master_resource, OrigConfig),
- SlaveResource = ?config(slave_resource, OrigConfig),
+ ct:print(80, "Testcase '~p' starting", [TestCase]),
Test = atom_to_list(TestCase),
IsMaster = lists:suffix("_master", Test),
IsSlave = lists:suffix("_slave", Test),
+ if IsMaster or IsSlave ->
+ subscribe_to_events(OrigConfig);
+ true ->
+ ok
+ end,
+ TestGroup = proplists:get_value(
+ name, ?config(tc_group_properties, OrigConfig)),
+ Server = ?config(server, OrigConfig),
+ Resource = case TestGroup of
+ anonymous ->
+ <<"">>;
+ legacy_auth ->
+ p1_rand:get_string();
+ _ ->
+ ?config(resource, OrigConfig)
+ end,
+ MasterResource = ?config(master_resource, OrigConfig),
+ SlaveResource = ?config(slave_resource, OrigConfig),
+ Mode = if IsSlave -> slave;
+ IsMaster -> master;
+ true -> single
+ end,
IsCarbons = lists:prefix("carbons_", Test),
- User = if IsMaster or IsCarbons -> <<"test_master!#$%^*()`~+-;_=[]{}|\\">>;
+ IsReplaced = lists:prefix("replaced_", Test),
+ User = if IsReplaced -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>;
+ IsCarbons and not (IsMaster or IsSlave) ->
+ <<"test_single!#$%^*()`~+-;_=[]{}|\\">>;
+ IsMaster or IsCarbons -> <<"test_master!#$%^*()`~+-;_=[]{}|\\">>;
IsSlave -> <<"test_slave!#$%^*()`~+-;_=[]{}|\\">>;
true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>
end,
+ Nick = if IsSlave -> ?config(slave_nick, OrigConfig);
+ IsMaster -> ?config(master_nick, OrigConfig);
+ true -> ?config(nick, OrigConfig)
+ end,
MyResource = if IsMaster and IsCarbons -> MasterResource;
IsSlave and IsCarbons -> SlaveResource;
true -> Resource
end,
Slave = if IsCarbons ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, SlaveResource);
+ IsReplaced ->
+ jid:make(User, Server, Resource);
true ->
jid:make(<<"test_slave!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
end,
Master = if IsCarbons ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, MasterResource);
+ IsReplaced ->
+ jid:make(User, Server, Resource);
true ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
end,
- Config = set_opt(user, User,
- set_opt(slave, Slave,
- set_opt(master, Master,
- set_opt(resource, MyResource, OrigConfig)))),
- case TestCase of
- test_connect ->
+ Config1 = set_opt(user, User,
+ set_opt(slave, Slave,
+ set_opt(master, Master,
+ set_opt(resource, MyResource,
+ set_opt(nick, Nick,
+ set_opt(mode, Mode, OrigConfig)))))),
+ Config2 = if IsSlave ->
+ set_opt(peer_nick, ?config(master_nick, Config1), Config1);
+ IsMaster ->
+ set_opt(peer_nick, ?config(slave_nick, Config1), Config1);
+ true ->
+ Config1
+ end,
+ Config = if IsSlave -> set_opt(peer, Master, Config2);
+ IsMaster -> set_opt(peer, Slave, Config2);
+ true -> Config2
+ end,
+ case Test of
+ "test_connect" ++ _ ->
Config;
- test_auth ->
+ "test_legacy_auth_feature" ->
+ connect(Config);
+ "test_legacy_auth" ++ _ ->
+ init_stream(set_opt(stream_version, undefined, Config));
+ "test_auth" ++ _ ->
connect(Config);
- test_starttls ->
+ "test_starttls" ++ _ ->
connect(Config);
- test_zlib ->
+ "test_zlib" ->
+ auth(connect(starttls(connect(Config))));
+ "test_register" ->
connect(Config);
- test_register ->
+ "auth_md5" ->
connect(Config);
- auth_md5 ->
+ "auth_plain" ->
connect(Config);
- auth_plain ->
- connect(Config);
- test_bind ->
+ "auth_external" ++ _ ->
+ connect(Config);
+ "unauthenticated_" ++ _ ->
+ connect(Config);
+ "test_bind" ->
auth(connect(Config));
- sm_resume ->
+ "sm_resume" ->
auth(connect(Config));
- sm_resume_failed ->
+ "sm_resume_failed" ->
auth(connect(Config));
- test_open_session ->
+ "test_open_session" ->
bind(auth(connect(Config)));
+ "replaced" ++ _ ->
+ auth(connect(Config));
_ when IsMaster or IsSlave ->
Password = ?config(password, Config),
ejabberd_auth:try_register(User, Server, Password),
open_session(bind(auth(connect(Config))));
+ _ when TestGroup == s2s_tests ->
+ auth(connect(starttls(connect(Config))));
_ ->
open_session(bind(auth(connect(Config))))
end.
@@ -218,167 +302,187 @@ init_per_testcase(TestCase, OrigConfig) ->
end_per_testcase(_TestCase, _Config) ->
ok.
+legacy_auth_tests() ->
+ {legacy_auth, [parallel],
+ [test_legacy_auth_feature,
+ test_legacy_auth,
+ test_legacy_auth_digest,
+ test_legacy_auth_no_resource,
+ test_legacy_auth_bad_jid,
+ test_legacy_auth_fail]}.
+
no_db_tests() ->
- [{generic, [sequence],
- [test_connect,
+ [{anonymous, [parallel],
+ [test_connect_bad_xml,
+ test_connect_unexpected_xml,
+ test_connect_unknown_ns,
+ test_connect_bad_xmlns,
+ test_connect_bad_ns_stream,
+ test_connect_bad_lang,
+ test_connect_bad_to,
+ test_connect_missing_to,
+ test_connect,
+ unauthenticated_iq,
+ unauthenticated_message,
+ unauthenticated_presence,
test_starttls,
- test_zlib,
test_auth,
+ test_zlib,
test_bind,
test_open_session,
- presence,
+ codec_failure,
+ unsupported_query,
+ bad_nonza,
+ invalid_from,
ping,
version,
time,
stats,
- sm,
- sm_resume,
- sm_resume_failed,
disco]},
- {test_proxy65, [parallel],
- [proxy65_master, proxy65_slave]}].
+ {presence_and_s2s, [sequence],
+ [test_auth_fail,
+ presence,
+ s2s_dialback,
+ s2s_optional,
+ s2s_required]},
+ auth_external,
+ auth_external_no_jid,
+ auth_external_no_user,
+ auth_external_malformed_jid,
+ auth_external_wrong_jid,
+ auth_external_wrong_server,
+ auth_external_invalid_cert,
+ jidprep_tests:single_cases(),
+ sm_tests:single_cases(),
+ sm_tests:master_slave_cases(),
+ muc_tests:single_cases(),
+ muc_tests:master_slave_cases(),
+ proxy65_tests:single_cases(),
+ proxy65_tests:master_slave_cases(),
+ replaced_tests:master_slave_cases(),
+ upload_tests:single_cases(),
+ carbons_tests:single_cases(),
+ carbons_tests:master_slave_cases()].
-db_tests(riak) ->
- %% No support for mod_pubsub
- [{single_user, [sequence],
- [test_register,
- auth_plain,
- auth_md5,
- presence_broadcast,
- last,
- roster_get,
- private,
- privacy,
- blocking,
- vcard,
- test_unregister]},
- {test_muc_register, [sequence],
- [muc_register_master, muc_register_slave]},
- {test_roster_subscribe, [parallel],
- [roster_subscribe_master,
- roster_subscribe_slave]},
- {test_flex_offline, [sequence],
- [flex_offline_master, flex_offline_slave]},
- {test_offline, [sequence],
- [offline_master, offline_slave]},
- {test_muc, [parallel],
- [muc_master, muc_slave]},
- {test_announce, [sequence],
- [announce_master, announce_slave]},
- {test_vcard_xupdate, [parallel],
- [vcard_xupdate_master, vcard_xupdate_slave]},
- {test_roster_remove, [parallel],
- [roster_remove_master,
- roster_remove_slave]}];
db_tests(DB) when DB == mnesia; DB == redis ->
[{single_user, [sequence],
[test_register,
+ legacy_auth_tests(),
auth_plain,
auth_md5,
presence_broadcast,
last,
- roster_get,
- roster_ver,
- private,
- privacy,
- blocking,
- vcard,
- pubsub,
+ roster_tests:single_cases(),
+ private_tests:single_cases(),
+ privacy_tests:single_cases(),
+ vcard_tests:single_cases(),
+ pubsub_tests:single_cases(),
+ muc_tests:single_cases(),
+ offline_tests:single_cases(),
+ mam_tests:single_cases(),
+ csi_tests:single_cases(),
+ push_tests:single_cases(),
test_unregister]},
- {test_muc_register, [sequence],
- [muc_register_master, muc_register_slave]},
- {test_mix, [parallel],
- [mix_master, mix_slave]},
- {test_roster_subscribe, [parallel],
- [roster_subscribe_master,
- roster_subscribe_slave]},
- {test_flex_offline, [sequence],
- [flex_offline_master, flex_offline_slave]},
- {test_offline, [sequence],
- [offline_master, offline_slave]},
- {test_old_mam, [parallel],
- [mam_old_master, mam_old_slave]},
- {test_new_mam, [parallel],
- [mam_new_master, mam_new_slave]},
- {test_carbons, [parallel],
- [carbons_master, carbons_slave]},
- {test_client_state, [parallel],
- [client_state_master, client_state_slave]},
- {test_muc, [parallel],
- [muc_master, muc_slave]},
- {test_muc_mam, [parallel],
- [muc_mam_master, muc_mam_slave]},
- {test_announce, [sequence],
- [announce_master, announce_slave]},
- {test_vcard_xupdate, [parallel],
- [vcard_xupdate_master, vcard_xupdate_slave]},
- {test_roster_remove, [parallel],
- [roster_remove_master,
- roster_remove_slave]}];
-db_tests(_) ->
- %% No support for carboncopy
+ muc_tests:master_slave_cases(),
+ privacy_tests:master_slave_cases(),
+ pubsub_tests:master_slave_cases(),
+ roster_tests:master_slave_cases(),
+ offline_tests:master_slave_cases(DB),
+ mam_tests:master_slave_cases(),
+ vcard_tests:master_slave_cases(),
+ announce_tests:master_slave_cases(),
+ csi_tests:master_slave_cases(),
+ push_tests:master_slave_cases()];
+db_tests(DB) ->
[{single_user, [sequence],
[test_register,
+ legacy_auth_tests(),
auth_plain,
auth_md5,
presence_broadcast,
last,
- roster_get,
- roster_ver,
- private,
- privacy,
- blocking,
- vcard,
- pubsub,
+ roster_tests:single_cases(),
+ private_tests:single_cases(),
+ privacy_tests:single_cases(),
+ vcard_tests:single_cases(),
+ pubsub_tests:single_cases(),
+ muc_tests:single_cases(),
+ offline_tests:single_cases(),
+ mam_tests:single_cases(),
+ push_tests:single_cases(),
test_unregister]},
- {test_muc_register, [sequence],
- [muc_register_master, muc_register_slave]},
- {test_mix, [parallel],
- [mix_master, mix_slave]},
- {test_roster_subscribe, [parallel],
- [roster_subscribe_master,
- roster_subscribe_slave]},
- {test_flex_offline, [sequence],
- [flex_offline_master, flex_offline_slave]},
- {test_offline, [sequence],
- [offline_master, offline_slave]},
- {test_old_mam, [parallel],
- [mam_old_master, mam_old_slave]},
- {test_new_mam, [parallel],
- [mam_new_master, mam_new_slave]},
- {test_muc, [parallel],
- [muc_master, muc_slave]},
- {test_muc_mam, [parallel],
- [muc_mam_master, muc_mam_slave]},
- {test_announce, [sequence],
- [announce_master, announce_slave]},
- {test_vcard_xupdate, [parallel],
- [vcard_xupdate_master, vcard_xupdate_slave]},
- {test_roster_remove, [parallel],
- [roster_remove_master,
- roster_remove_slave]}].
+ muc_tests:master_slave_cases(),
+ privacy_tests:master_slave_cases(),
+ pubsub_tests:master_slave_cases(),
+ roster_tests:master_slave_cases(),
+ offline_tests:master_slave_cases(DB),
+ mam_tests:master_slave_cases(),
+ vcard_tests:master_slave_cases(),
+ announce_tests:master_slave_cases(),
+ push_tests:master_slave_cases()].
ldap_tests() ->
[{ldap_tests, [sequence],
[test_auth,
+ test_auth_fail,
vcard_get,
ldap_shared_roster_get]}].
extauth_tests() ->
[{extauth_tests, [sequence],
[test_auth,
+ test_auth_fail,
test_unregister]}].
+component_tests() ->
+ [{component_connect, [parallel],
+ [test_connect_bad_xml,
+ test_connect_unexpected_xml,
+ test_connect_unknown_ns,
+ test_connect_bad_xmlns,
+ test_connect_bad_ns_stream,
+ test_connect_missing_to,
+ test_connect,
+ test_auth,
+ test_auth_fail]},
+ {component_tests, [sequence],
+ [test_missing_from,
+ test_missing_to,
+ test_invalid_from,
+ test_component_send,
+ bad_nonza,
+ codec_failure]}].
+
+s2s_tests() ->
+ [{s2s_connect, [parallel],
+ [test_connect_bad_xml,
+ test_connect_unexpected_xml,
+ test_connect_unknown_ns,
+ test_connect_bad_xmlns,
+ test_connect_bad_ns_stream,
+ test_connect,
+ test_connect_s2s_starttls_required,
+ test_starttls,
+ test_connect_s2s_unauthenticated_iq,
+ test_auth_starttls]},
+ {s2s_tests, [sequence],
+ [test_missing_from,
+ test_missing_to,
+ test_invalid_from,
+ bad_nonza,
+ codec_failure]}].
+
groups() ->
[{ldap, [sequence], ldap_tests()},
{extauth, [sequence], extauth_tests()},
{no_db, [sequence], no_db_tests()},
+ {component, [sequence], component_tests()},
+ {s2s, [sequence], s2s_tests()},
{mnesia, [sequence], db_tests(mnesia)},
{redis, [sequence], db_tests(redis)},
{mysql, [sequence], db_tests(mysql)},
{pgsql, [sequence], db_tests(pgsql)},
- {sqlite, [sequence], db_tests(sqlite)},
- {riak, [sequence], db_tests(riak)}].
+ {sqlite, [sequence], db_tests(sqlite)}].
all() ->
[{group, ldap},
@@ -389,7 +493,8 @@ all() ->
{group, pgsql},
{group, sqlite},
{group, extauth},
- {group, riak},
+ {group, component},
+ {group, s2s},
stop_ejabberd].
stop_ejabberd(Config) ->
@@ -398,13 +503,83 @@ stop_ejabberd(Config) ->
?recv1({xmlstreamend, <<"stream:stream">>}),
Config.
+test_connect_bad_xml(Config) ->
+ Config0 = tcp_connect(Config),
+ send_text(Config0, <<"<'/>">>),
+ Version = ?config(stream_version, Config0),
+ ?recv1(#stream_start{version = Version}),
+ ?recv1(#stream_error{reason = 'not-well-formed'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_unexpected_xml(Config) ->
+ Config0 = tcp_connect(Config),
+ send(Config0, #caps{}),
+ Version = ?config(stream_version, Config0),
+ ?recv1(#stream_start{version = Version}),
+ ?recv1(#stream_error{reason = 'invalid-xml'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_unknown_ns(Config) ->
+ Config0 = init_stream(set_opt(xmlns, <<"wrong">>, Config)),
+ ?recv1(#stream_error{reason = 'invalid-xml'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_xmlns(Config) ->
+ NS = case ?config(type, Config) of
+ client -> ?NS_SERVER;
+ _ -> ?NS_CLIENT
+ end,
+ Config0 = init_stream(set_opt(xmlns, NS, Config)),
+ ?recv1(#stream_error{reason = 'invalid-namespace'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_ns_stream(Config) ->
+ Config0 = init_stream(set_opt(ns_stream, <<"wrong">>, Config)),
+ ?recv1(#stream_error{reason = 'invalid-namespace'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_lang(Config) ->
+ Lang = iolist_to_binary(lists:duplicate(36, $x)),
+ Config0 = init_stream(set_opt(lang, Lang, Config)),
+ ?recv1(#stream_error{reason = 'invalid-xml'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_to(Config) ->
+ Config0 = init_stream(set_opt(server, <<"wrong.com">>, Config)),
+ ?recv1(#stream_error{reason = 'host-unknown'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_missing_to(Config) ->
+ Config0 = init_stream(set_opt(server, <<"">>, Config)),
+ ?recv1(#stream_error{reason = 'improper-addressing'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
test_connect(Config) ->
disconnect(connect(Config)).
+test_connect_s2s_starttls_required(Config) ->
+ Config1 = connect(Config),
+ send(Config1, #presence{}),
+ ?recv1(#stream_error{reason = 'policy-violation'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config1).
+
+test_connect_s2s_unauthenticated_iq(Config) ->
+ Config1 = connect(starttls(connect(Config))),
+ unauthenticated_iq(Config1).
+
test_starttls(Config) ->
case ?config(starttls, Config) of
true ->
- disconnect(starttls(Config));
+ disconnect(connect(starttls(Config)));
_ ->
{skipped, 'starttls_not_available'}
end.
@@ -432,8 +607,8 @@ test_register(Config) ->
register(Config) ->
#iq{type = result,
- sub_els = [#register{username = none,
- password = none}]} =
+ sub_els = [#register{username = <<>>,
+ password = <<>>}]} =
send_recv(Config, #iq{type = get, to = server_jid(Config),
sub_els = [#register{}]}),
#iq{type = result, sub_els = []} =
@@ -462,6 +637,101 @@ try_unregister(Config) ->
?recv1(#stream_error{reason = conflict}),
Config.
+unauthenticated_presence(Config) ->
+ unauthenticated_packet(Config, #presence{}).
+
+unauthenticated_message(Config) ->
+ unauthenticated_packet(Config, #message{}).
+
+unauthenticated_iq(Config) ->
+ IQ = #iq{type = get, sub_els = [#disco_info{}]},
+ unauthenticated_packet(Config, IQ).
+
+unauthenticated_packet(Config, Pkt) ->
+ From = my_jid(Config),
+ To = server_jid(Config),
+ send(Config, xmpp:set_from_to(Pkt, From, To)),
+ #stream_error{reason = 'not-authorized'} = recv(Config),
+ {xmlstreamend, <<"stream:stream">>} = recv(Config),
+ close_socket(Config).
+
+bad_nonza(Config) ->
+ %% Unsupported and invalid nonza should be silently dropped.
+ send(Config, #caps{}),
+ send(Config, #stanza_error{type = wrong}),
+ disconnect(Config).
+
+invalid_from(Config) ->
+ send(Config, #message{from = jid:make(p1_rand:get_string())}),
+ ?recv1(#stream_error{reason = 'invalid-from'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config).
+
+test_missing_from(Config) ->
+ Server = server_jid(Config),
+ send(Config, #message{to = Server}),
+ ?recv1(#stream_error{reason = 'improper-addressing'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config).
+
+test_missing_to(Config) ->
+ Server = server_jid(Config),
+ send(Config, #message{from = Server}),
+ ?recv1(#stream_error{reason = 'improper-addressing'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config).
+
+test_invalid_from(Config) ->
+ From = jid:make(p1_rand:get_string()),
+ To = jid:make(p1_rand:get_string()),
+ send(Config, #message{from = From, to = To}),
+ ?recv1(#stream_error{reason = 'invalid-from'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config).
+
+test_component_send(Config) ->
+ To = jid:make(?COMMON_VHOST),
+ From = server_jid(Config),
+ #iq{type = result, from = To, to = From} =
+ send_recv(Config, #iq{type = get, to = To, from = From,
+ sub_els = [#ping{}]}),
+ disconnect(Config).
+
+s2s_dialback(Config) ->
+ Server = ?config(server, Config),
+ ejabberd_s2s:stop_s2s_connections(),
+ ejabberd_config:set_option({s2s_use_starttls, Server}, false),
+ ejabberd_config:set_option({s2s_use_starttls, ?MNESIA_VHOST}, false),
+ ejabberd_config:set_option(ca_file, pkix:get_cafile()),
+ s2s_ping(Config).
+
+s2s_optional(Config) ->
+ Server = ?config(server, Config),
+ ejabberd_s2s:stop_s2s_connections(),
+ ejabberd_config:set_option({s2s_use_starttls, Server}, optional),
+ ejabberd_config:set_option({s2s_use_starttls, ?MNESIA_VHOST}, optional),
+ ejabberd_config:set_option(ca_file, pkix:get_cafile()),
+ s2s_ping(Config).
+
+s2s_required(Config) ->
+ Server = ?config(server, Config),
+ ejabberd_s2s:stop_s2s_connections(),
+ gen_mod:stop_module(Server, mod_s2s_dialback),
+ gen_mod:stop_module(?MNESIA_VHOST, mod_s2s_dialback),
+ ejabberd_config:set_option({s2s_use_starttls, Server}, required),
+ ejabberd_config:set_option({s2s_use_starttls, ?MNESIA_VHOST}, required),
+ ejabberd_config:set_option(ca_file, "ca.pem"),
+ s2s_ping(Config).
+
+s2s_ping(Config) ->
+ From = my_jid(Config),
+ To = jid:make(?MNESIA_VHOST),
+ ID = p1_rand:get_string(),
+ ejabberd_s2s:route(#iq{from = From, to = To, id = ID,
+ type = get, sub_els = [#ping{}]}),
+ #iq{type = result, id = ID, sub_els = []} = recv_iq(Config),
+ disconnect(Config).
+
auth_md5(Config) ->
Mechs = ?config(mechs, Config),
case lists:member(<<"DIGEST-MD5">>, Mechs) of
@@ -482,87 +752,134 @@ auth_plain(Config) ->
{skipped, 'PLAIN_not_available'}
end.
+auth_external(Config0) ->
+ Config = connect(starttls(Config0)),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config)).
+
+auth_external_no_jid(Config0) ->
+ Config = connect(starttls(Config0)),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config, _ShoudFail = false,
+ {<<"">>, <<"">>, <<"">>})).
+
+auth_external_no_user(Config0) ->
+ Config = set_opt(user, <<"">>, connect(starttls(Config0))),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config)).
+
+auth_external_malformed_jid(Config0) ->
+ Config = connect(starttls(Config0)),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config, _ShouldFail = true,
+ {<<"">>, <<"@">>, <<"">>})).
+
+auth_external_wrong_jid(Config0) ->
+ Config = set_opt(user, <<"wrong">>,
+ connect(starttls(Config0))),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config, _ShouldFail = true)).
+
+auth_external_wrong_server(Config0) ->
+ Config = connect(starttls(Config0)),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config, _ShouldFail = true,
+ {<<"">>, <<"wrong.com">>, <<"">>})).
+
+auth_external_invalid_cert(Config0) ->
+ Config = connect(starttls(
+ set_opt(certfile, "self-signed-cert.pem", Config0))),
+ disconnect(auth_SASL(<<"EXTERNAL">>, Config, _ShouldFail = true)).
+
+test_legacy_auth_feature(Config) ->
+ true = ?config(legacy_auth, Config),
+ disconnect(Config).
+
+test_legacy_auth(Config) ->
+ disconnect(auth_legacy(Config, _Digest = false)).
+
+test_legacy_auth_digest(Config) ->
+ disconnect(auth_legacy(Config, _Digest = true)).
+
+test_legacy_auth_no_resource(Config0) ->
+ Config = set_opt(resource, <<"">>, Config0),
+ disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
+
+test_legacy_auth_bad_jid(Config0) ->
+ Config = set_opt(user, <<"@">>, Config0),
+ disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
+
+test_legacy_auth_fail(Config0) ->
+ Config = set_opt(user, <<"wrong">>, Config0),
+ disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
+
test_auth(Config) ->
disconnect(auth(Config)).
+test_auth_starttls(Config) ->
+ disconnect(auth(connect(starttls(Config)))).
+
+test_auth_fail(Config0) ->
+ Config = set_opt(user, <<"wrong">>,
+ set_opt(password, <<"wrong">>, Config0)),
+ disconnect(auth(Config, _ShouldFail = true)).
+
test_bind(Config) ->
disconnect(bind(Config)).
test_open_session(Config) ->
- disconnect(open_session(Config)).
+ disconnect(open_session(Config, true)).
-roster_get(Config) ->
- #iq{type = result, sub_els = [#roster{items = []}]} =
- send_recv(Config, #iq{type = get, sub_els = [#roster{}]}),
+codec_failure(Config) ->
+ JID = my_jid(Config),
+ #iq{type = error} =
+ send_recv(Config, #iq{type = wrong, from = JID, to = JID}),
disconnect(Config).
-roster_ver(Config) ->
- %% Get initial "ver"
- #iq{type = result, sub_els = [#roster{ver = Ver1, items = []}]} =
- send_recv(Config, #iq{type = get,
- sub_els = [#roster{ver = <<"">>}]}),
- %% Should receive empty IQ-result
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = get,
- sub_els = [#roster{ver = Ver1}]}),
- %% Attempting to subscribe to server's JID
- send(Config, #presence{type = subscribe, to = server_jid(Config)}),
- %% Receive a single roster push with the new "ver"
- ?recv1(#iq{type = set, sub_els = [#roster{ver = Ver2}]}),
- %% Requesting roster with the previous "ver". Should receive Ver2 again
- #iq{type = result, sub_els = [#roster{ver = Ver2}]} =
- send_recv(Config, #iq{type = get,
- sub_els = [#roster{ver = Ver1}]}),
- %% Now requesting roster with the newest "ver". Should receive empty IQ.
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = get,
- sub_els = [#roster{ver = Ver2}]}),
+unsupported_query(Config) ->
+ ServerJID = server_jid(Config),
+ #iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID}),
+ #iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID,
+ sub_els = [#caps{}]}),
+ #iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID,
+ sub_els = [#roster_query{},
+ #disco_info{},
+ #privacy_query{}]}),
disconnect(Config).
presence(Config) ->
- send(Config, #presence{}),
JID = my_jid(Config),
- ?recv1(#presence{from = JID, to = JID}),
+ #presence{from = JID, to = JID} = send_recv(Config, #presence{}),
disconnect(Config).
presence_broadcast(Config) ->
- Feature = <<"p1:tmp:", (randoms:get_string())/binary>>,
+ Feature = <<"p1:tmp:", (p1_rand:get_string())/binary>>,
Ver = crypto:hash(sha, ["client", $/, "bot", $/, "en", $/,
"ejabberd_ct", $<, Feature, $<]),
B64Ver = base64:encode(Ver),
Node = <<(?EJABBERD_CT_URI)/binary, $#, B64Ver/binary>>,
Server = ?config(server, Config),
- ServerJID = server_jid(Config),
Info = #disco_info{identities =
[#identity{category = <<"client">>,
type = <<"bot">>,
lang = <<"en">>,
name = <<"ejabberd_ct">>}],
node = Node, features = [Feature]},
- Caps = #caps{hash = <<"sha-1">>, node = ?EJABBERD_CT_URI, ver = Ver},
+ Caps = #caps{hash = <<"sha-1">>, node = ?EJABBERD_CT_URI, version = B64Ver},
send(Config, #presence{sub_els = [Caps]}),
JID = my_jid(Config),
%% We receive:
%% 1) disco#info iq request for CAPS
%% 2) welcome message
%% 3) presence broadcast
- {IQ, _, _} = ?recv3(#iq{type = get,
- from = ServerJID,
- sub_els = [#disco_info{node = Node}]},
- #message{type = normal},
- #presence{from = JID, to = JID}),
+ IQ = #iq{type = get,
+ from = JID,
+ sub_els = [#disco_info{node = Node}]} = recv_iq(Config),
+ #message{type = normal} = recv_message(Config),
+ #presence{from = JID, to = JID} = recv_presence(Config),
send(Config, #iq{type = result, id = IQ#iq.id,
- to = ServerJID, sub_els = [Info]}),
+ to = JID, sub_els = [Info]}),
%% We're trying to read our feature from ejabberd database
%% with exponential back-off as our IQ response may be delayed.
[Feature] =
lists:foldl(
fun(Time, []) ->
timer:sleep(Time),
- mod_caps:get_features(
- Server,
- mod_caps:read_caps(
- [xmpp_codec:encode(Caps)]));
+ mod_caps:get_features(Server, Caps);
(_, Acc) ->
Acc
end, [], [0, 100, 200, 2000, 5000, 10000]),
@@ -607,82 +924,6 @@ disco(Config) ->
end, Items),
disconnect(Config).
-sm(Config) ->
- Server = ?config(server, Config),
- ServerJID = jid:make(<<"">>, Server, <<"">>),
- %% Send messages of type 'headline' so the server discards them silently
- Msg = #message{to = ServerJID, type = headline,
- body = [#text{data = <<"body">>}]},
- true = ?config(sm, Config),
- %% Enable the session management with resumption enabled
- send(Config, #sm_enable{resume = true, xmlns = ?NS_STREAM_MGMT_3}),
- ?recv1(#sm_enabled{id = ID, resume = true}),
- %% Initial request; 'h' should be 0.
- send(Config, #sm_r{xmlns = ?NS_STREAM_MGMT_3}),
- ?recv1(#sm_a{h = 0}),
- %% sending two messages and requesting again; 'h' should be 3.
- send(Config, Msg),
- send(Config, Msg),
- send(Config, Msg),
- send(Config, #sm_r{xmlns = ?NS_STREAM_MGMT_3}),
- ?recv1(#sm_a{h = 3}),
- close_socket(Config),
- {save_config, set_opt(sm_previd, ID, Config)}.
-
-sm_resume(Config) ->
- {sm, SMConfig} = ?config(saved_config, Config),
- ID = ?config(sm_previd, SMConfig),
- Server = ?config(server, Config),
- ServerJID = jid:make(<<"">>, Server, <<"">>),
- MyJID = my_jid(Config),
- Txt = #text{data = <<"body">>},
- Msg = #message{from = ServerJID, to = MyJID, body = [Txt]},
- %% Route message. The message should be queued by the C2S process.
- ejabberd_router:route(ServerJID, MyJID, xmpp_codec:encode(Msg)),
- send(Config, #sm_resume{previd = ID, h = 0, xmlns = ?NS_STREAM_MGMT_3}),
- ?recv1(#sm_resumed{previd = ID, h = 3}),
- ?recv1(#message{from = ServerJID, to = MyJID, body = [Txt]}),
- ?recv1(#sm_r{}),
- send(Config, #sm_a{h = 1, xmlns = ?NS_STREAM_MGMT_3}),
- %% Send another stanza to increment the server's 'h' for sm_resume_failed.
- send(Config, #presence{to = ServerJID}),
- close_socket(Config),
- {save_config, set_opt(sm_previd, ID, Config)}.
-
-sm_resume_failed(Config) ->
- {sm_resume, SMConfig} = ?config(saved_config, Config),
- ID = ?config(sm_previd, SMConfig),
- ct:sleep(5000), % Wait for session to time out.
- send(Config, #sm_resume{previd = ID, h = 1, xmlns = ?NS_STREAM_MGMT_3}),
- ?recv1(#sm_failed{reason = 'item-not-found', h = 4}),
- disconnect(Config).
-
-private(Config) ->
- Conference = #bookmark_conference{name = <<"Some name">>,
- autojoin = true,
- jid = jid:make(
- <<"some">>,
- <<"some.conference.org">>,
- <<>>)},
- Storage = #bookmark_storage{conference = [Conference]},
- StorageXMLOut = xmpp_codec:encode(Storage),
- #iq{type = error} =
- send_recv(Config, #iq{type = get, sub_els = [#private{}],
- to = server_jid(Config)}),
- #iq{type = result, sub_els = []} =
- send_recv(
- Config, #iq{type = set,
- sub_els = [#private{xml_els = [StorageXMLOut]}]}),
- #iq{type = result,
- sub_els = [#private{xml_els = [StorageXMLIn]}]} =
- send_recv(
- Config,
- #iq{type = get,
- sub_els = [#private{xml_els = [xmpp_codec:encode(
- #bookmark_storage{})]}]}),
- Storage = xmpp_codec:decode(StorageXMLIn),
- disconnect(Config).
-
last(Config) ->
true = is_feature_advertised(Config, ?NS_LAST),
#iq{type = result, sub_els = [#last{}]} =
@@ -690,1685 +931,36 @@ last(Config) ->
to = server_jid(Config)}),
disconnect(Config).
-privacy(Config) ->
- true = is_feature_advertised(Config, ?NS_PRIVACY),
- #iq{type = result, sub_els = [#privacy{}]} =
- send_recv(Config, #iq{type = get, sub_els = [#privacy{}]}),
- JID = <<"tybalt@example.com">>,
- I1 = send(Config,
- #iq{type = set,
- sub_els = [#privacy{
- lists = [#privacy_list{
- name = <<"public">>,
- items =
- [#privacy_item{
- type = jid,
- order = 3,
- action = deny,
- kinds = ['presence-in'],
- value = JID}]}]}]}),
- {Push1, _} =
- ?recv2(
- #iq{type = set,
- sub_els = [#privacy{
- lists = [#privacy_list{
- name = <<"public">>}]}]},
- #iq{type = result, id = I1, sub_els = []}),
- send(Config, make_iq_result(Push1)),
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = set,
- sub_els = [#privacy{active = <<"public">>}]}),
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = set,
- sub_els = [#privacy{default = <<"public">>}]}),
- #iq{type = result,
- sub_els = [#privacy{default = <<"public">>,
- active = <<"public">>,
- lists = [#privacy_list{name = <<"public">>}]}]} =
- send_recv(Config, #iq{type = get, sub_els = [#privacy{}]}),
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set, sub_els = [#privacy{default = none}]}),
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = set, sub_els = [#privacy{active = none}]}),
- I2 = send(Config, #iq{type = set,
- sub_els = [#privacy{
- lists =
- [#privacy_list{
- name = <<"public">>}]}]}),
- {Push2, _} =
- ?recv2(
- #iq{type = set,
- sub_els = [#privacy{
- lists = [#privacy_list{
- name = <<"public">>}]}]},
- #iq{type = result, id = I2, sub_els = []}),
- send(Config, make_iq_result(Push2)),
- disconnect(Config).
-
-blocking(Config) ->
- true = is_feature_advertised(Config, ?NS_BLOCKING),
- JID = jid:make(<<"romeo">>, <<"montague.net">>, <<>>),
- #iq{type = result, sub_els = [#block_list{}]} =
- send_recv(Config, #iq{type = get, sub_els = [#block_list{}]}),
- I1 = send(Config, #iq{type = set,
- sub_els = [#block{items = [JID]}]}),
- {Push1, Push2, _} =
- ?recv3(
- #iq{type = set,
- sub_els = [#privacy{lists = [#privacy_list{}]}]},
- #iq{type = set,
- sub_els = [#block{items = [JID]}]},
- #iq{type = result, id = I1, sub_els = []}),
- send(Config, make_iq_result(Push1)),
- send(Config, make_iq_result(Push2)),
- I2 = send(Config, #iq{type = set,
- sub_els = [#unblock{items = [JID]}]}),
- {Push3, Push4, _} =
- ?recv3(
- #iq{type = set,
- sub_els = [#privacy{lists = [#privacy_list{}]}]},
- #iq{type = set,
- sub_els = [#unblock{items = [JID]}]},
- #iq{type = result, id = I2, sub_els = []}),
- send(Config, make_iq_result(Push3)),
- send(Config, make_iq_result(Push4)),
- disconnect(Config).
-
-vcard(Config) ->
- true = is_feature_advertised(Config, ?NS_VCARD),
- VCard =
- #vcard{fn = <<"Peter Saint-Andre">>,
- n = #vcard_name{family = <<"Saint-Andre">>,
- given = <<"Peter">>},
- nickname = <<"stpeter">>,
- bday = <<"1966-08-06">>,
- adr = [#vcard_adr{work = true,
- extadd = <<"Suite 600">>,
- street = <<"1899 Wynkoop Street">>,
- locality = <<"Denver">>,
- region = <<"CO">>,
- pcode = <<"80202">>,
- ctry = <<"USA">>},
- #vcard_adr{home = true,
- locality = <<"Denver">>,
- region = <<"CO">>,
- pcode = <<"80209">>,
- ctry = <<"USA">>}],
- tel = [#vcard_tel{work = true,voice = true,
- number = <<"303-308-3282">>},
- #vcard_tel{home = true,voice = true,
- number = <<"303-555-1212">>}],
- email = [#vcard_email{internet = true,pref = true,
- userid = <<"stpeter@jabber.org">>}],
- jabberid = <<"stpeter@jabber.org">>,
- title = <<"Executive Director">>,role = <<"Patron Saint">>,
- org = #vcard_org{name = <<"XMPP Standards Foundation">>},
- url = <<"http://www.xmpp.org/xsf/people/stpeter.shtml">>,
- desc = <<"More information about me is located on my "
- "personal website: http://www.saint-andre.com/">>},
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = set, sub_els = [VCard]}),
- %% TODO: check if VCard == VCard1.
- #iq{type = result, sub_els = [_VCard1]} =
- send_recv(Config, #iq{type = get, sub_els = [#vcard{}]}),
- disconnect(Config).
-
vcard_get(Config) ->
true = is_feature_advertised(Config, ?NS_VCARD),
%% TODO: check if VCard corresponds to LDIF data from ejabberd.ldif
#iq{type = result, sub_els = [_VCard]} =
- send_recv(Config, #iq{type = get, sub_els = [#vcard{}]}),
+ send_recv(Config, #iq{type = get, sub_els = [#vcard_temp{}]}),
disconnect(Config).
ldap_shared_roster_get(Config) ->
- Item = #roster_item{jid = jid:from_string(<<"user2@ldap.localhost">>), name = <<"Test User 2">>,
+ Item = #roster_item{jid = jid:decode(<<"user2@ldap.localhost">>), name = <<"Test User 2">>,
groups = [<<"group1">>], subscription = both},
- #iq{type = result, sub_els = [#roster{items = [Item]}]} =
- send_recv(Config, #iq{type = get, sub_els = [#roster{}]}),
- disconnect(Config).
-
-vcard_xupdate_master(Config) ->
- Img = <<137, "PNG\r\n", 26, $\n>>,
- ImgHash = p1_sha:sha(Img),
- MyJID = my_jid(Config),
- Peer = ?config(slave, Config),
- wait_for_slave(Config),
- send(Config, #presence{}),
- ?recv2(#presence{from = MyJID, type = undefined},
- #presence{from = Peer, type = undefined}),
- VCard = #vcard{photo = #vcard_photo{type = <<"image/png">>, binval = Img}},
- I1 = send(Config, #iq{type = set, sub_els = [VCard]}),
- ?recv2(#iq{type = result, sub_els = [], id = I1},
- #presence{from = MyJID, type = undefined,
- sub_els = [#vcard_xupdate{photo = ImgHash}]}),
- I2 = send(Config, #iq{type = set, sub_els = [#vcard{}]}),
- ?recv3(#iq{type = result, sub_els = [], id = I2},
- #presence{from = MyJID, type = undefined,
- sub_els = [#vcard_xupdate{photo = undefined}]},
- #presence{from = Peer, type = unavailable}),
- disconnect(Config).
-
-vcard_xupdate_slave(Config) ->
- Img = <<137, "PNG\r\n", 26, $\n>>,
- ImgHash = p1_sha:sha(Img),
- MyJID = my_jid(Config),
- Peer = ?config(master, Config),
- send(Config, #presence{}),
- ?recv1(#presence{from = MyJID, type = undefined}),
- wait_for_master(Config),
- ?recv1(#presence{from = Peer, type = undefined}),
- ?recv1(#presence{from = Peer, type = undefined,
- sub_els = [#vcard_xupdate{photo = ImgHash}]}),
- ?recv1(#presence{from = Peer, type = undefined,
- sub_els = [#vcard_xupdate{photo = undefined}]}),
+ #iq{type = result, sub_els = [#roster_query{items = [Item]}]} =
+ send_recv(Config, #iq{type = get, sub_els = [#roster_query{}]}),
disconnect(Config).
stats(Config) ->
- #iq{type = result, sub_els = [#stats{stat = Stats}]} =
+ #iq{type = result, sub_els = [#stats{list = Stats}]} =
send_recv(Config, #iq{type = get, sub_els = [#stats{}],
to = server_jid(Config)}),
lists:foreach(
fun(#stat{} = Stat) ->
#iq{type = result, sub_els = [_|_]} =
send_recv(Config, #iq{type = get,
- sub_els = [#stats{stat = [Stat]}],
+ sub_els = [#stats{list = [Stat]}],
to = server_jid(Config)})
end, Stats),
disconnect(Config).
-pubsub(Config) ->
- Features = get_features(Config, pubsub_jid(Config)),
- true = lists:member(?NS_PUBSUB, Features),
- %% Publish <presence/> element within node "presence"
- ItemID = randoms:get_string(),
- Node = <<"presence!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>,
- Item = #pubsub_item{id = ItemID,
- xml_els = [xmpp_codec:encode(#presence{})]},
- #iq{type = result,
- sub_els = [#pubsub{publish = #pubsub_publish{
- node = Node,
- items = [#pubsub_item{id = ItemID}]}}]} =
- send_recv(Config,
- #iq{type = set, to = pubsub_jid(Config),
- sub_els = [#pubsub{publish = #pubsub_publish{
- node = Node,
- items = [Item]}}]}),
- %% Subscribe to node "presence"
- I1 = send(Config,
- #iq{type = set, to = pubsub_jid(Config),
- sub_els = [#pubsub{subscribe = #pubsub_subscribe{
- node = Node,
- jid = my_jid(Config)}}]}),
- ?recv2(
- #message{sub_els = [#pubsub_event{}, #delay{}]},
- #iq{type = result, id = I1}),
- %% Get subscriptions
- true = lists:member(?PUBSUB("retrieve-subscriptions"), Features),
- #iq{type = result,
- sub_els =
- [#pubsub{subscriptions =
- {none, [#pubsub_subscription{node = Node}]}}]} =
- send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
- sub_els = [#pubsub{subscriptions = {none, []}}]}),
- %% Get affiliations
- true = lists:member(?PUBSUB("retrieve-affiliations"), Features),
- #iq{type = result,
- sub_els = [#pubsub{
- affiliations =
- [#pubsub_affiliation{node = Node, type = owner}]}]} =
- send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
- sub_els = [#pubsub{affiliations = []}]}),
- %% Fetching published items from node "presence"
- #iq{type = result,
- sub_els = [#pubsub{items = #pubsub_items{
- node = Node,
- items = [Item]}}]} =
- send_recv(Config,
- #iq{type = get, to = pubsub_jid(Config),
- sub_els = [#pubsub{items = #pubsub_items{node = Node}}]}),
- %% Deleting the item from the node
- true = lists:member(?PUBSUB("delete-items"), Features),
- I2 = send(Config,
- #iq{type = set, to = pubsub_jid(Config),
- sub_els = [#pubsub{retract = #pubsub_retract{
- node = Node,
- items = [#pubsub_item{id = ItemID}]}}]}),
- ?recv2(
- #iq{type = result, id = I2, sub_els = []},
- #message{sub_els = [#pubsub_event{
- items = [#pubsub_event_items{
- node = Node,
- retract = [ItemID]}]}]}),
- %% Unsubscribe from node "presence"
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set, to = pubsub_jid(Config),
- sub_els = [#pubsub{unsubscribe = #pubsub_unsubscribe{
- node = Node,
- jid = my_jid(Config)}}]}),
- disconnect(Config).
-
-mix_master(Config) ->
- MIX = mix_jid(Config),
- Room = mix_room_jid(Config),
- MyJID = my_jid(Config),
- MyBareJID = jid:remove_resource(MyJID),
- true = is_feature_advertised(Config, ?NS_MIX_0, MIX),
- #iq{type = result,
- sub_els =
- [#disco_info{
- identities = [#identity{category = <<"conference">>,
- type = <<"text">>}],
- xdata = [#xdata{type = result, fields = XFields}]}]} =
- send_recv(Config, #iq{type = get, to = MIX, sub_els = [#disco_info{}]}),
- true = lists:any(
- fun(#xdata_field{var = <<"FORM_TYPE">>,
- values = [?NS_MIX_SERVICEINFO_0]}) -> true;
- (_) -> false
- end, XFields),
- %% Joining
- Nodes = [?NS_MIX_NODES_MESSAGES, ?NS_MIX_NODES_PRESENCE,
- ?NS_MIX_NODES_PARTICIPANTS, ?NS_MIX_NODES_SUBJECT,
- ?NS_MIX_NODES_CONFIG],
- I0 = send(Config, #iq{type = set, to = Room,
- sub_els = [#mix_join{subscribe = Nodes}]}),
- {_, #message{sub_els =
- [#pubsub_event{
- items = [#pubsub_event_items{
- node = ?NS_MIX_NODES_PARTICIPANTS,
- items = [#pubsub_event_item{
- id = ParticipantID,
- xml_els = [PXML]}]}]}]}} =
- ?recv2(#iq{type = result, id = I0,
- sub_els = [#mix_join{subscribe = Nodes, jid = MyBareJID}]},
- #message{from = Room}),
- #mix_participant{jid = MyBareJID} = xmpp_codec:decode(PXML),
- %% Coming online
- PresenceID = randoms:get_string(),
- Presence = xmpp_codec:encode(#presence{}),
- I1 = send(
- Config,
- #iq{type = set, to = Room,
- sub_els =
- [#pubsub{
- publish = #pubsub_publish{
- node = ?NS_MIX_NODES_PRESENCE,
- items = [#pubsub_item{
- id = PresenceID,
- xml_els = [Presence]}]}}]}),
- ?recv2(#iq{type = result, id = I1,
- sub_els =
- [#pubsub{
- publish = #pubsub_publish{
- node = ?NS_MIX_NODES_PRESENCE,
- items = [#pubsub_item{id = PresenceID}]}}]},
- #message{from = Room,
- sub_els =
- [#pubsub_event{
- items = [#pubsub_event_items{
- node = ?NS_MIX_NODES_PRESENCE,
- items = [#pubsub_event_item{
- id = PresenceID,
- xml_els = [Presence]}]}]}]}),
- %% Coming offline
- send(Config, #presence{type = unavailable, to = Room}),
- %% Receiving presence retract event
- #message{from = Room,
- sub_els = [#pubsub_event{
- items = [#pubsub_event_items{
- node = ?NS_MIX_NODES_PRESENCE,
- retract = [PresenceID]}]}]} = recv(),
- %% Leaving
- I2 = send(Config, #iq{type = set, to = Room, sub_els = [#mix_leave{}]}),
- ?recv2(#iq{type = result, id = I2, sub_els = []},
- #message{from = Room,
- sub_els =
- [#pubsub_event{
- items = [#pubsub_event_items{
- node = ?NS_MIX_NODES_PARTICIPANTS,
- retract = [ParticipantID]}]}]}),
- disconnect(Config).
-
-mix_slave(Config) ->
- disconnect(Config).
-
-roster_subscribe_master(Config) ->
- send(Config, #presence{}),
- ?recv1(#presence{}),
- wait_for_slave(Config),
- Peer = ?config(slave, Config),
- LPeer = jid:remove_resource(Peer),
- send(Config, #presence{type = subscribe, to = LPeer}),
- Push1 = ?recv1(#iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- ask = subscribe,
- subscription = none,
- jid = LPeer}]}]}),
- send(Config, make_iq_result(Push1)),
- {Push2, _} = ?recv2(
- #iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- subscription = to,
- jid = LPeer}]}]},
- #presence{type = subscribed, from = LPeer}),
- send(Config, make_iq_result(Push2)),
- ?recv1(#presence{type = undefined, from = Peer}),
- %% BUG: ejabberd sends previous push again. Is it ok?
- Push3 = ?recv1(#iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- subscription = to,
- jid = LPeer}]}]}),
- send(Config, make_iq_result(Push3)),
- ?recv1(#presence{type = subscribe, from = LPeer}),
- send(Config, #presence{type = subscribed, to = LPeer}),
- Push4 = ?recv1(#iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- subscription = both,
- jid = LPeer}]}]}),
- send(Config, make_iq_result(Push4)),
- %% Move into a group
- Groups = [<<"A">>, <<"B">>],
- Item = #roster_item{jid = LPeer, groups = Groups},
- I1 = send(Config, #iq{type = set, sub_els = [#roster{items = [Item]}]}),
- {Push5, _} = ?recv2(
- #iq{type = set,
- sub_els =
- [#roster{items = [#roster_item{
- jid = LPeer,
- subscription = both}]}]},
- #iq{type = result, id = I1, sub_els = []}),
- send(Config, make_iq_result(Push5)),
- #iq{sub_els = [#roster{items = [#roster_item{groups = G1}]}]} = Push5,
- Groups = lists:sort(G1),
- wait_for_slave(Config),
- ?recv1(#presence{type = unavailable, from = Peer}),
- disconnect(Config).
-
-roster_subscribe_slave(Config) ->
- send(Config, #presence{}),
- ?recv1(#presence{}),
- wait_for_master(Config),
- Peer = ?config(master, Config),
- LPeer = jid:remove_resource(Peer),
- ?recv1(#presence{type = subscribe, from = LPeer}),
- send(Config, #presence{type = subscribed, to = LPeer}),
- Push1 = ?recv1(#iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- subscription = from,
- jid = LPeer}]}]}),
- send(Config, make_iq_result(Push1)),
- send(Config, #presence{type = subscribe, to = LPeer}),
- Push2 = ?recv1(#iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- ask = subscribe,
- subscription = from,
- jid = LPeer}]}]}),
- send(Config, make_iq_result(Push2)),
- {Push3, _} = ?recv2(
- #iq{type = set,
- sub_els = [#roster{items = [#roster_item{
- subscription = both,
- jid = LPeer}]}]},
- #presence{type = subscribed, from = LPeer}),
- send(Config, make_iq_result(Push3)),
- ?recv1(#presence{type = undefined, from = Peer}),
- wait_for_master(Config),
- disconnect(Config).
-
-roster_remove_master(Config) ->
- MyJID = my_jid(Config),
- Peer = ?config(slave, Config),
- LPeer = jid:remove_resource(Peer),
- Groups = [<<"A">>, <<"B">>],
- wait_for_slave(Config),
- send(Config, #presence{}),
- ?recv2(#presence{from = MyJID, type = undefined},
- #presence{from = Peer, type = undefined}),
- %% The peer removed us from its roster.
- {Push1, Push2, _, _, _} =
- ?recv5(
- %% TODO: I guess this can be optimized, we don't need
- %% to send transient roster push with subscription = 'to'.
- #iq{type = set,
- sub_els =
- [#roster{items = [#roster_item{
- jid = LPeer,
- subscription = to}]}]},
- #iq{type = set,
- sub_els =
- [#roster{items = [#roster_item{
- jid = LPeer,
- subscription = none}]}]},
- #presence{type = unsubscribe, from = LPeer},
- #presence{type = unsubscribed, from = LPeer},
- #presence{type = unavailable, from = Peer}),
- send(Config, make_iq_result(Push1)),
- send(Config, make_iq_result(Push2)),
- #iq{sub_els = [#roster{items = [#roster_item{groups = G1}]}]} = Push1,
- #iq{sub_els = [#roster{items = [#roster_item{groups = G2}]}]} = Push2,
- Groups = lists:sort(G1), Groups = lists:sort(G2),
- disconnect(Config).
-
-roster_remove_slave(Config) ->
- MyJID = my_jid(Config),
- Peer = ?config(master, Config),
- LPeer = jid:remove_resource(Peer),
- send(Config, #presence{}),
- ?recv1(#presence{from = MyJID, type = undefined}),
- wait_for_master(Config),
- ?recv1(#presence{from = Peer, type = undefined}),
- %% Remove the peer from roster.
- Item = #roster_item{jid = LPeer, subscription = remove},
- I = send(Config, #iq{type = set, sub_els = [#roster{items = [Item]}]}),
- {Push, _, _} = ?recv3(
- #iq{type = set,
- sub_els =
- [#roster{items = [#roster_item{
- jid = LPeer,
- subscription = remove}]}]},
- #iq{type = result, id = I, sub_els = []},
- #presence{type = unavailable, from = Peer}),
- send(Config, make_iq_result(Push)),
- disconnect(Config).
-
-proxy65_master(Config) ->
- Proxy = proxy_jid(Config),
- MyJID = my_jid(Config),
- Peer = ?config(slave, Config),
- wait_for_slave(Config),
- send(Config, #presence{}),
- ?recv1(#presence{from = MyJID, type = undefined}),
- true = is_feature_advertised(Config, ?NS_BYTESTREAMS, Proxy),
- #iq{type = result, sub_els = [#bytestreams{hosts = [StreamHost]}]} =
- send_recv(
- Config,
- #iq{type = get, sub_els = [#bytestreams{}], to = Proxy}),
- SID = randoms:get_string(),
- Data = crypto:rand_bytes(1024),
- put_event(Config, {StreamHost, SID, Data}),
- Socks5 = socks5_connect(StreamHost, {SID, MyJID, Peer}),
- wait_for_slave(Config),
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set, to = Proxy,
- sub_els = [#bytestreams{activate = Peer, sid = SID}]}),
- socks5_send(Socks5, Data),
- %%?recv1(#presence{type = unavailable, from = Peer}),
- disconnect(Config).
-
-proxy65_slave(Config) ->
- MyJID = my_jid(Config),
- Peer = ?config(master, Config),
- send(Config, #presence{}),
- ?recv1(#presence{from = MyJID, type = undefined}),
- wait_for_master(Config),
- {StreamHost, SID, Data} = get_event(Config),
- Socks5 = socks5_connect(StreamHost, {SID, Peer, MyJID}),
- wait_for_master(Config),
- socks5_recv(Socks5, Data),
- disconnect(Config).
-
-send_messages_to_room(Config, Range) ->
- MyNick = ?config(master_nick, Config),
- Room = muc_room_jid(Config),
- MyNickJID = jid:replace_resource(Room, MyNick),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- I = send(Config, #message{to = Room, body = [Text],
- type = groupchat}),
- ?recv1(#message{from = MyNickJID, id = I,
- type = groupchat,
- body = [Text]})
- end, Range).
-
-retrieve_messages_from_room_via_mam(Config, Range) ->
- MyNick = ?config(master_nick, Config),
- Room = muc_room_jid(Config),
- MyNickJID = jid:replace_resource(Room, MyNick),
- QID = randoms:get_string(),
- I = send(Config, #iq{type = set, to = Room,
- sub_els = [#mam_query{xmlns = ?NS_MAM_1, id = QID}]}),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{
- to = MyJID, from = Room,
- sub_els =
- [#mam_result{
- xmlns = ?NS_MAM_1,
- queryid = QID,
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els = [#message{
- from = MyNickJID,
- type = groupchat,
- body = [Text]}]}]}]})
- end, Range),
- ?recv1(#iq{from = Room, id = I, type = result, sub_els = []}).
-
-muc_mam_master(Config) ->
- MyJID = my_jid(Config),
- MyNick = ?config(master_nick, Config),
- Room = muc_room_jid(Config),
- MyNickJID = jid:replace_resource(Room, MyNick),
- %% Joining
- send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
- %% Receive self-presence
- ?recv1(#presence{from = MyNickJID}),
- %% MAM feature should not be advertised at this point,
- %% because MAM is not enabled so far
- false = is_feature_advertised(Config, ?NS_MAM_1, Room),
- %% Fill in some history
- send_messages_to_room(Config, lists:seq(1, 21)),
- %% We now should be able to retrieve those via MAM, even though
- %% MAM is disabled. However, only last 20 messages should be received.
- retrieve_messages_from_room_via_mam(Config, lists:seq(2, 21)),
- %% Now enable MAM for the conference
- %% Retrieve config first
- #iq{type = result, sub_els = [#muc_owner{config = #xdata{} = RoomCfg}]} =
- send_recv(Config, #iq{type = get, sub_els = [#muc_owner{}],
- to = Room}),
- %% Find the MAM field in the config and enable it
- NewFields = lists:flatmap(
- fun(#xdata_field{var = <<"muc#roomconfig_mam">> = Var}) ->
- [#xdata_field{var = Var, values = [<<"1">>]}];
- (_) ->
- []
- end, RoomCfg#xdata.fields),
- NewRoomCfg = #xdata{type = submit, fields = NewFields},
- I1 = send(Config, #iq{type = set, to = Room,
- sub_els = [#muc_owner{config = NewRoomCfg}]}),
- ?recv2(#iq{type = result, id = I1},
- #message{from = Room, type = groupchat,
- sub_els = [#muc_user{status_codes = [104]}]}),
- %% Check if MAM has been enabled
- true = is_feature_advertised(Config, ?NS_MAM_1, Room),
- %% We now sending some messages again
- send_messages_to_room(Config, lists:seq(1, 5)),
- %% And retrieve them via MAM again.
- retrieve_messages_from_room_via_mam(Config, lists:seq(1, 5)),
- disconnect(Config).
-
-muc_mam_slave(Config) ->
- disconnect(Config).
-
-muc_master(Config) ->
- MyJID = my_jid(Config),
- PeerJID = ?config(slave, Config),
- PeerBareJID = jid:remove_resource(PeerJID),
- PeerJIDStr = jid:to_string(PeerJID),
- MUC = muc_jid(Config),
- Room = muc_room_jid(Config),
- MyNick = ?config(master_nick, Config),
- MyNickJID = jid:replace_resource(Room, MyNick),
- PeerNick = ?config(slave_nick, Config),
- PeerNickJID = jid:replace_resource(Room, PeerNick),
- Subject = ?config(room_subject, Config),
- Localhost = jid:make(<<"">>, <<"localhost">>, <<"">>),
- true = is_feature_advertised(Config, ?NS_MUC, MUC),
- %% Joining
- send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
- %% As per XEP-0045 we MUST receive stanzas in the following order:
- %% 1. In-room presence from other occupants
- %% 2. In-room presence from the joining entity itself (so-called "self-presence")
- %% 3. Room history (if any)
- %% 4. The room subject
- %% 5. Live messages, presence updates, new user joins, etc.
- %% As this is the newly created room, we receive only the 2nd stanza.
- ?recv1(#presence{
- from = MyNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- status_codes = Codes,
- items = [#muc_item{role = moderator,
- jid = MyJID,
- affiliation = owner}]}]}),
- %% 110 -> Inform user that presence refers to itself
- %% 201 -> Inform user that a new room has been created
- [110, 201] = lists:sort(Codes),
- %% Request the configuration
- #iq{type = result, sub_els = [#muc_owner{config = #xdata{} = RoomCfg}]} =
- send_recv(Config, #iq{type = get, sub_els = [#muc_owner{}],
- to = Room}),
- NewFields =
- lists:flatmap(
- fun(#xdata_field{var = Var, values = OrigVals}) ->
- Vals = case Var of
- <<"FORM_TYPE">> ->
- OrigVals;
- <<"muc#roomconfig_roomname">> ->
- [<<"Test room">>];
- <<"muc#roomconfig_roomdesc">> ->
- [<<"Trying to break the server">>];
- <<"muc#roomconfig_persistentroom">> ->
- [<<"1">>];
- <<"members_by_default">> ->
- [<<"0">>];
- <<"muc#roomconfig_allowvoicerequests">> ->
- [<<"1">>];
- <<"public_list">> ->
- [<<"1">>];
- <<"muc#roomconfig_publicroom">> ->
- [<<"1">>];
- _ ->
- []
- end,
- if Vals /= [] ->
- [#xdata_field{values = Vals, var = Var}];
- true ->
- []
- end
- end, RoomCfg#xdata.fields),
- NewRoomCfg = #xdata{type = submit, fields = NewFields},
- ID = send(Config, #iq{type = set, to = Room,
- sub_els = [#muc_owner{config = NewRoomCfg}]}),
- ?recv2(#iq{type = result, id = ID},
- #message{from = Room, type = groupchat,
- sub_els = [#muc_user{status_codes = [104]}]}),
- %% Set subject
- send(Config, #message{to = Room, type = groupchat,
- body = [#text{data = Subject}]}),
- ?recv1(#message{from = MyNickJID, type = groupchat,
- body = [#text{data = Subject}]}),
- %% Sending messages (and thus, populating history for our peer)
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- I = send(Config, #message{to = Room, body = [Text],
- type = groupchat}),
- ?recv1(#message{from = MyNickJID, id = I,
- type = groupchat,
- body = [Text]})
- end, lists:seq(1, 5)),
- %% Inviting the peer
- send(Config, #message{to = Room, type = normal,
- sub_els =
- [#muc_user{
- invites =
- [#muc_invite{to = PeerJID}]}]}),
- %% Peer is joining
- ?recv1(#presence{from = PeerNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- items = [#muc_item{role = visitor,
- jid = PeerJID,
- affiliation = none}]}]}),
- %% Receiving a voice request
- ?recv1(#message{from = Room,
- sub_els = [#xdata{type = form,
- instructions = [_],
- fields = VoiceReqFs}]}),
- %% Approving the voice request
- ReplyVoiceReqFs =
- lists:map(
- fun(#xdata_field{var = Var, values = OrigVals}) ->
- Vals = case {Var, OrigVals} of
- {<<"FORM_TYPE">>,
- [<<"http://jabber.org/protocol/muc#request">>]} ->
- OrigVals;
- {<<"muc#role">>, [<<"participant">>]} ->
- [<<"participant">>];
- {<<"muc#jid">>, [PeerJIDStr]} ->
- [PeerJIDStr];
- {<<"muc#roomnick">>, [PeerNick]} ->
- [PeerNick];
- {<<"muc#request_allow">>, [<<"0">>]} ->
- [<<"1">>]
- end,
- #xdata_field{values = Vals, var = Var}
- end, VoiceReqFs),
- send(Config, #message{to = Room,
- sub_els = [#xdata{type = submit,
- fields = ReplyVoiceReqFs}]}),
- %% Peer is becoming a participant
- ?recv1(#presence{from = PeerNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- items = [#muc_item{role = participant,
- jid = PeerJID,
- affiliation = none}]}]}),
- %% Receive private message from the peer
- ?recv1(#message{from = PeerNickJID, body = [#text{data = Subject}]}),
- %% Granting membership to the peer and localhost server
- I1 = send(Config,
- #iq{type = set, to = Room,
- sub_els =
- [#muc_admin{
- items = [#muc_item{jid = Localhost,
- affiliation = member},
- #muc_item{nick = PeerNick,
- jid = PeerBareJID,
- affiliation = member}]}]}),
- %% Peer became a member
- ?recv1(#presence{from = PeerNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- items = [#muc_item{affiliation = member,
- jid = PeerJID,
- role = participant}]}]}),
- ?recv1(#message{from = Room,
- sub_els = [#muc_user{
- items = [#muc_item{affiliation = member,
- jid = Localhost,
- role = none}]}]}),
- %% BUG: We should not receive any sub_els!
- ?recv1(#iq{type = result, id = I1, sub_els = [_|_]}),
- %% Receive groupchat message from the peer
- ?recv1(#message{type = groupchat, from = PeerNickJID,
- body = [#text{data = Subject}]}),
- %% Retrieving a member list
- #iq{type = result, sub_els = [#muc_admin{items = MemberList}]} =
- send_recv(Config,
- #iq{type = get, to = Room,
- sub_els =
- [#muc_admin{items = [#muc_item{affiliation = member}]}]}),
- [#muc_item{affiliation = member,
- jid = Localhost},
- #muc_item{affiliation = member,
- jid = MyBareJID}] = lists:keysort(#muc_item.jid, MemberList),
- %% Kick the peer
- I2 = send(Config,
- #iq{type = set, to = Room,
- sub_els = [#muc_admin{
- items = [#muc_item{nick = PeerNick,
- role = none}]}]}),
- %% Got notification the peer is kicked
- %% 307 -> Inform user that he or she has been kicked from the room
- ?recv1(#presence{from = PeerNickJID, type = unavailable,
- sub_els = [#muc_user{
- status_codes = [307],
- items = [#muc_item{affiliation = member,
- jid = PeerJID,
- role = none}]}]}),
- %% BUG: We should not receive any sub_els!
- ?recv1(#iq{type = result, id = I2, sub_els = [_|_]}),
- %% Destroying the room
- I3 = send(Config,
- #iq{type = set, to = Room,
- sub_els = [#muc_owner{
- destroy = #muc_owner_destroy{
- reason = Subject}}]}),
- %% Kicked off
- ?recv1(#presence{from = MyNickJID, type = unavailable,
- sub_els = [#muc_user{items = [#muc_item{role = none,
- affiliation = none}],
- destroy = #muc_user_destroy{
- reason = Subject}}]}),
- %% BUG: We should not receive any sub_els!
- ?recv1(#iq{type = result, id = I3, sub_els = [_|_]}),
- disconnect(Config).
-
-muc_slave(Config) ->
- MyJID = my_jid(Config),
- MyBareJID = jid:remove_resource(MyJID),
- PeerJID = ?config(master, Config),
- MUC = muc_jid(Config),
- Room = muc_room_jid(Config),
- MyNick = ?config(slave_nick, Config),
- MyNickJID = jid:replace_resource(Room, MyNick),
- PeerNick = ?config(master_nick, Config),
- PeerNickJID = jid:replace_resource(Room, PeerNick),
- Subject = ?config(room_subject, Config),
- Localhost = jid:make(<<"">>, <<"localhost">>, <<"">>),
- %% Receive an invite from the peer
- ?recv1(#message{from = Room, type = normal,
- sub_els =
- [#muc_user{invites =
- [#muc_invite{from = PeerJID}]}]}),
- %% But before joining we discover the MUC service first
- %% to check if the room is in the disco list
- #iq{type = result,
- sub_els = [#disco_items{items = [#disco_item{jid = Room}]}]} =
- send_recv(Config, #iq{type = get, to = MUC,
- sub_els = [#disco_items{}]}),
- %% Now check if the peer is in the room. We check this via disco#items
- #iq{type = result,
- sub_els = [#disco_items{items = [#disco_item{jid = PeerNickJID,
- name = PeerNick}]}]} =
- send_recv(Config, #iq{type = get, to = Room,
- sub_els = [#disco_items{}]}),
- %% Now joining
- send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
- %% First presence is from the participant, i.e. from the peer
- ?recv1(#presence{
- from = PeerNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- status_codes = [],
- items = [#muc_item{role = moderator,
- affiliation = owner}]}]}),
- %% The next is the self-presence (code 110 means it)
- ?recv1(#presence{
- from = MyNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- status_codes = [110],
- items = [#muc_item{role = visitor,
- affiliation = none}]}]}),
- %% Receive the room subject
- ?recv1(#message{from = PeerNickJID, type = groupchat,
- body = [#text{data = Subject}],
- sub_els = [#delay{}]}),
- %% Receive MUC history
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{from = PeerNickJID,
- type = groupchat,
- body = [Text],
- sub_els = [#delay{}]})
- end, lists:seq(1, 5)),
- %% Sending a voice request
- VoiceReq = #xdata{
- type = submit,
- fields =
- [#xdata_field{
- var = <<"FORM_TYPE">>,
- values = [<<"http://jabber.org/protocol/muc#request">>]},
- #xdata_field{
- var = <<"muc#role">>,
- type = 'text-single',
- values = [<<"participant">>]}]},
- send(Config, #message{to = Room, sub_els = [VoiceReq]}),
- %% Becoming a participant
- ?recv1(#presence{from = MyNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- items = [#muc_item{role = participant,
- affiliation = none}]}]}),
- %% Sending private message to the peer
- send(Config, #message{to = PeerNickJID,
- body = [#text{data = Subject}]}),
- %% Becoming a member
- ?recv1(#presence{from = MyNickJID,
- sub_els = [#vcard_xupdate{},
- #muc_user{
- items = [#muc_item{role = participant,
- affiliation = member}]}]}),
- %% Sending groupchat message
- send(Config, #message{to = Room, type = groupchat,
- body = [#text{data = Subject}]}),
- %% Receive this message back
- ?recv1(#message{type = groupchat, from = MyNickJID,
- body = [#text{data = Subject}]}),
- %% We're kicked off
- %% 307 -> Inform user that he or she has been kicked from the room
- ?recv1(#presence{from = MyNickJID, type = unavailable,
- sub_els = [#muc_user{
- status_codes = [307],
- items = [#muc_item{affiliation = member,
- role = none}]}]}),
- disconnect(Config).
-
-muc_register_nick(Config, MUC, PrevNick, Nick) ->
- {Registered, PrevNickVals} = if PrevNick /= <<"">> ->
- {true, [PrevNick]};
- true ->
- {false, []}
- end,
- %% Request register form
- #iq{type = result,
- sub_els = [#register{registered = Registered,
- xdata = #xdata{type = form,
- fields = FsWithoutNick}}]} =
- send_recv(Config, #iq{type = get, to = MUC,
- sub_els = [#register{}]}),
- %% Check if 'nick' field presents
- #xdata_field{type = 'text-single',
- var = <<"nick">>,
- values = PrevNickVals} =
- lists:keyfind(<<"nick">>, #xdata_field.var, FsWithoutNick),
- X = #xdata{type = submit,
- fields = [#xdata_field{var = <<"nick">>, values = [Nick]}]},
- %% Submitting form
- #iq{type = result, sub_els = [_|_]} =
- send_recv(Config, #iq{type = set, to = MUC,
- sub_els = [#register{xdata = X}]}),
- %% Check if the nick was registered
- #iq{type = result,
- sub_els = [#register{registered = true,
- xdata = #xdata{type = form,
- fields = FsWithNick}}]} =
- send_recv(Config, #iq{type = get, to = MUC,
- sub_els = [#register{}]}),
- #xdata_field{type = 'text-single', var = <<"nick">>,
- values = [Nick]} =
- lists:keyfind(<<"nick">>, #xdata_field.var, FsWithNick).
-
-muc_register_master(Config) ->
- MUC = muc_jid(Config),
- %% Register nick "master1"
- muc_register_nick(Config, MUC, <<"">>, <<"master1">>),
- %% Unregister nick "master1" via jabber:register
- #iq{type = result, sub_els = [_|_]} =
- send_recv(Config, #iq{type = set, to = MUC,
- sub_els = [#register{remove = true}]}),
- %% Register nick "master2"
- muc_register_nick(Config, MUC, <<"">>, <<"master2">>),
- %% Now register nick "master"
- muc_register_nick(Config, MUC, <<"master2">>, <<"master">>),
- disconnect(Config).
-
-muc_register_slave(Config) ->
- MUC = muc_jid(Config),
- %% Trying to register occupied nick "master"
- X = #xdata{type = submit,
- fields = [#xdata_field{var = <<"nick">>,
- values = [<<"master">>]}]},
- #iq{type = error} =
- send_recv(Config, #iq{type = set, to = MUC,
- sub_els = [#register{xdata = X}]}),
- disconnect(Config).
-
-announce_master(Config) ->
- MyJID = my_jid(Config),
- ServerJID = server_jid(Config),
- MotdJID = jid:replace_resource(ServerJID, <<"announce/motd">>),
- MotdText = #text{data = <<"motd">>},
- send(Config, #presence{}),
- ?recv1(#presence{from = MyJID}),
- %% Set message of the day
- send(Config, #message{to = MotdJID, body = [MotdText]}),
- %% Receive this message back
- ?recv1(#message{from = ServerJID, body = [MotdText]}),
- disconnect(Config).
-
-announce_slave(Config) ->
- MyJID = my_jid(Config),
- ServerJID = server_jid(Config),
- MotdDelJID = jid:replace_resource(ServerJID, <<"announce/motd/delete">>),
- MotdText = #text{data = <<"motd">>},
- send(Config, #presence{}),
- ?recv2(#presence{from = MyJID},
- #message{from = ServerJID, body = [MotdText]}),
- %% Delete message of the day
- send(Config, #message{to = MotdDelJID}),
- disconnect(Config).
-
-flex_offline_master(Config) ->
- Peer = ?config(slave, Config),
- LPeer = jid:remove_resource(Peer),
- lists:foreach(
- fun(I) ->
- Body = integer_to_binary(I),
- send(Config, #message{to = LPeer,
- body = [#text{data = Body}],
- subject = [#text{data = <<"subject">>}]})
- end, lists:seq(1, 5)),
- disconnect(Config).
-
-flex_offline_slave(Config) ->
- MyJID = my_jid(Config),
- MyBareJID = jid:remove_resource(MyJID),
- Peer = ?config(master, Config),
- Peer_s = jid:to_string(Peer),
- true = is_feature_advertised(Config, ?NS_FLEX_OFFLINE),
- %% Request disco#info
- #iq{type = result,
- sub_els = [#disco_info{
- node = ?NS_FLEX_OFFLINE,
- identities = Ids,
- features = Fts,
- xdata = [X]}]} =
- send_recv(Config, #iq{type = get,
- sub_els = [#disco_info{
- node = ?NS_FLEX_OFFLINE}]}),
- %% Check if we have correct identities
- true = lists:any(
- fun(#identity{category = <<"automation">>,
- type = <<"message-list">>}) -> true;
- (_) -> false
- end, Ids),
- %% Check if we have needed feature
- true = lists:member(?NS_FLEX_OFFLINE, Fts),
- %% Check xdata, the 'number_of_messages' should be 5
- #xdata{type = result,
- fields = [#xdata_field{type = hidden,
- var = <<"FORM_TYPE">>},
- #xdata_field{var = <<"number_of_messages">>,
- values = [<<"5">>]}]} = X,
- %% Fetch headers,
- #iq{type = result,
- sub_els = [#disco_items{
- node = ?NS_FLEX_OFFLINE,
- items = DiscoItems}]} =
- send_recv(Config, #iq{type = get,
- sub_els = [#disco_items{
- node = ?NS_FLEX_OFFLINE}]}),
- %% Check if headers are correct
- Nodes = lists:sort(
- lists:map(
- fun(#disco_item{jid = J, name = P, node = N})
- when (J == MyBareJID) and (P == Peer_s) ->
- N
- end, DiscoItems)),
- %% Since headers are received we can send initial presence without a risk
- %% of getting offline messages flood
- send(Config, #presence{}),
- ?recv1(#presence{from = MyJID}),
- %% Check full fetch
- I0 = send(Config, #iq{type = get, sub_els = [#offline{fetch = true}]}),
- lists:foreach(
- fun({I, N}) ->
- Text = integer_to_binary(I),
- ?recv1(#message{body = Body, sub_els = SubEls}),
- [#text{data = Text}] = Body,
- #offline{items = [#offline_item{node = N}]} =
- lists:keyfind(offline, 1, SubEls),
- #delay{} = lists:keyfind(delay, 1, SubEls)
- end, lists:zip(lists:seq(1, 5), Nodes)),
- ?recv1(#iq{type = result, id = I0, sub_els = []}),
- %% Fetch 2nd and 4th message
- I1 = send(Config,
- #iq{type = get,
- sub_els = [#offline{
- items = [#offline_item{
- action = view,
- node = lists:nth(2, Nodes)},
- #offline_item{
- action = view,
- node = lists:nth(4, Nodes)}]}]}),
- lists:foreach(
- fun({I, N}) ->
- Text = integer_to_binary(I),
- ?recv1(#message{body = [#text{data = Text}], sub_els = SubEls}),
- #offline{items = [#offline_item{node = N}]} =
- lists:keyfind(offline, 1, SubEls)
- end, lists:zip([2, 4], [lists:nth(2, Nodes), lists:nth(4, Nodes)])),
- ?recv1(#iq{type = result, id = I1, sub_els = []}),
- %% Delete 2nd and 4th message
- #iq{type = result, sub_els = []} =
- send_recv(
- Config,
- #iq{type = set,
- sub_els = [#offline{
- items = [#offline_item{
- action = remove,
- node = lists:nth(2, Nodes)},
- #offline_item{
- action = remove,
- node = lists:nth(4, Nodes)}]}]}),
- %% Check if messages were deleted
- #iq{type = result,
- sub_els = [#disco_items{
- node = ?NS_FLEX_OFFLINE,
- items = RemainedItems}]} =
- send_recv(Config, #iq{type = get,
- sub_els = [#disco_items{
- node = ?NS_FLEX_OFFLINE}]}),
- RemainedNodes = [lists:nth(1, Nodes),
- lists:nth(3, Nodes),
- lists:nth(5, Nodes)],
- RemainedNodes = lists:sort(
- lists:map(
- fun(#disco_item{node = N}) -> N end,
- RemainedItems)),
- %% Purge everything left
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = set, sub_els = [#offline{purge = true}]}),
- %% Check if there is no offline messages
- #iq{type = result,
- sub_els = [#disco_items{node = ?NS_FLEX_OFFLINE, items = []}]} =
- send_recv(Config, #iq{type = get,
- sub_els = [#disco_items{
- node = ?NS_FLEX_OFFLINE}]}),
- disconnect(Config).
-
-offline_master(Config) ->
- Peer = ?config(slave, Config),
- LPeer = jid:remove_resource(Peer),
- send(Config, #message{to = LPeer,
- body = [#text{data = <<"body">>}],
- subject = [#text{data = <<"subject">>}]}),
- disconnect(Config).
-
-offline_slave(Config) ->
- Peer = ?config(master, Config),
- send(Config, #presence{}),
- {_, #message{sub_els = SubEls}} =
- ?recv2(#presence{},
- #message{from = Peer,
- body = [#text{data = <<"body">>}],
- subject = [#text{data = <<"subject">>}]}),
- true = lists:keymember(delay, 1, SubEls),
- disconnect(Config).
-
-carbons_master(Config) ->
- MyJID = my_jid(Config),
- MyBareJID = jid:remove_resource(MyJID),
- Peer = ?config(slave, Config),
- Txt = #text{data = <<"body">>},
- true = is_feature_advertised(Config, ?NS_CARBONS_2),
- send(Config, #presence{priority = 10}),
- ?recv1(#presence{from = MyJID}),
- wait_for_slave(Config),
- ?recv1(#presence{from = Peer}),
- %% Enable carbons
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set,
- sub_els = [#carbons_enable{}]}),
- %% Send a message to bare and full JID
- send(Config, #message{to = MyBareJID, type = chat, body = [Txt]}),
- send(Config, #message{to = MyJID, type = chat, body = [Txt]}),
- send(Config, #message{to = MyBareJID, type = chat, body = [Txt],
- sub_els = [#carbons_private{}]}),
- send(Config, #message{to = MyJID, type = chat, body = [Txt],
- sub_els = [#carbons_private{}]}),
- %% Receive the messages back
- ?recv4(#message{from = MyJID, to = MyBareJID, type = chat,
- body = [Txt], sub_els = []},
- #message{from = MyJID, to = MyJID, type = chat,
- body = [Txt], sub_els = []},
- #message{from = MyJID, to = MyBareJID, type = chat,
- body = [Txt], sub_els = [#carbons_private{}]},
- #message{from = MyJID, to = MyJID, type = chat,
- body = [Txt], sub_els = [#carbons_private{}]}),
- %% Disable carbons
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set,
- sub_els = [#carbons_disable{}]}),
- wait_for_slave(Config),
- %% Repeat the same and leave
- send(Config, #message{to = MyBareJID, type = chat, body = [Txt]}),
- send(Config, #message{to = MyJID, type = chat, body = [Txt]}),
- send(Config, #message{to = MyBareJID, type = chat, body = [Txt],
- sub_els = [#carbons_private{}]}),
- send(Config, #message{to = MyJID, type = chat, body = [Txt],
- sub_els = [#carbons_private{}]}),
- ?recv4(#message{from = MyJID, to = MyBareJID, type = chat,
- body = [Txt], sub_els = []},
- #message{from = MyJID, to = MyJID, type = chat,
- body = [Txt], sub_els = []},
- #message{from = MyJID, to = MyBareJID, type = chat,
- body = [Txt], sub_els = [#carbons_private{}]},
- #message{from = MyJID, to = MyJID, type = chat,
- body = [Txt], sub_els = [#carbons_private{}]}),
- disconnect(Config).
-
-carbons_slave(Config) ->
- MyJID = my_jid(Config),
- MyBareJID = jid:remove_resource(MyJID),
- Peer = ?config(master, Config),
- Txt = #text{data = <<"body">>},
- wait_for_master(Config),
- send(Config, #presence{priority = 5}),
- ?recv2(#presence{from = MyJID}, #presence{from = Peer}),
- %% Enable carbons
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set,
- sub_els = [#carbons_enable{}]}),
- %% Receive messages sent by the peer
- ?recv4(
- #message{from = MyBareJID, to = MyJID, type = chat,
- sub_els =
- [#carbons_sent{
- forwarded = #forwarded{
- sub_els =
- [#message{from = Peer,
- to = MyBareJID,
- type = chat,
- body = [Txt]}]}}]},
- #message{from = MyBareJID, to = MyJID, type = chat,
- sub_els =
- [#carbons_sent{
- forwarded = #forwarded{
- sub_els =
- [#message{from = Peer,
- to = Peer,
- type = chat,
- body = [Txt]}]}}]},
- #message{from = MyBareJID, to = MyJID, type = chat,
- sub_els =
- [#carbons_received{
- forwarded = #forwarded{
- sub_els =
- [#message{from = Peer,
- to = MyBareJID,
- type = chat,
- body = [Txt]}]}}]},
- #message{from = MyBareJID, to = MyJID, type = chat,
- sub_els =
- [#carbons_received{
- forwarded = #forwarded{
- sub_els =
- [#message{from = Peer,
- to = Peer,
- type = chat,
- body = [Txt]}]}}]}),
- %% Disable carbons
- #iq{type = result, sub_els = []} =
- send_recv(Config,
- #iq{type = set,
- sub_els = [#carbons_disable{}]}),
- wait_for_master(Config),
- %% Now we should receive nothing but presence unavailable from the peer
- ?recv1(#presence{from = Peer, type = unavailable}),
- disconnect(Config).
-
-mam_old_master(Config) ->
- mam_master(Config, ?NS_MAM_TMP).
-
-mam_new_master(Config) ->
- mam_master(Config, ?NS_MAM_0).
-
-mam_master(Config, NS) ->
- true = is_feature_advertised(Config, NS),
- MyJID = my_jid(Config),
- BareMyJID = jid:remove_resource(MyJID),
- Peer = ?config(slave, Config),
- send(Config, #presence{}),
- ?recv1(#presence{}),
- wait_for_slave(Config),
- ?recv1(#presence{from = Peer}),
- #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = roster}]} =
- send_recv(Config,
- #iq{type = set,
- sub_els = [#mam_prefs{xmlns = NS,
- default = roster,
- never = [MyJID]}]}),
- if NS == ?NS_MAM_TMP ->
- FakeArchived = #mam_archived{id = randoms:get_string(),
- by = server_jid(Config)},
- send(Config, #message{to = MyJID,
- sub_els = [FakeArchived],
- body = [#text{data = <<"a">>}]}),
- send(Config, #message{to = BareMyJID,
- sub_els = [FakeArchived],
- body = [#text{data = <<"b">>}]}),
- %% NOTE: The server should strip fake archived tags,
- %% i.e. the sub_els received should be [].
- ?recv2(#message{body = [#text{data = <<"a">>}], sub_els = []},
- #message{body = [#text{data = <<"b">>}], sub_els = []});
- true ->
- ok
- end,
- wait_for_slave(Config),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- send(Config,
- #message{to = Peer, body = [Text]})
- end, lists:seq(1, 5)),
- ?recv1(#presence{type = unavailable, from = Peer}),
- mam_query_all(Config, NS),
- mam_query_with(Config, Peer, NS),
- %% mam_query_with(Config, jid:remove_resource(Peer)),
- mam_query_rsm(Config, NS),
- #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = never}]} =
- send_recv(Config, #iq{type = set,
- sub_els = [#mam_prefs{xmlns = NS,
- default = never}]}),
- disconnect(Config).
-
-mam_old_slave(Config) ->
- mam_slave(Config, ?NS_MAM_TMP).
-
-mam_new_slave(Config) ->
- mam_slave(Config, ?NS_MAM_0).
-
-mam_slave(Config, NS) ->
- Peer = ?config(master, Config),
- ServerJID = server_jid(Config),
- wait_for_master(Config),
- send(Config, #presence{}),
- ?recv2(#presence{}, #presence{from = Peer}),
- #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = always}]} =
- send_recv(Config,
- #iq{type = set,
- sub_els = [#mam_prefs{xmlns = NS, default = always}]}),
- wait_for_master(Config),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{from = Peer, body = [Text],
- sub_els = [#mam_archived{by = ServerJID}]})
- end, lists:seq(1, 5)),
- #iq{type = result, sub_els = [#mam_prefs{xmlns = NS, default = never}]} =
- send_recv(Config, #iq{type = set,
- sub_els = [#mam_prefs{xmlns = NS, default = never}]}),
- disconnect(Config).
-
-mam_query_all(Config, NS) ->
- QID = randoms:get_string(),
- MyJID = my_jid(Config),
- Peer = ?config(slave, Config),
- Type = case NS of
- ?NS_MAM_TMP -> get;
- _ -> set
- end,
- I = send(Config, #iq{type = Type, sub_els = [#mam_query{xmlns = NS, id = QID}]}),
- maybe_recv_iq_result(NS, I),
- Iter = if NS == ?NS_MAM_TMP -> lists:seq(1, 5);
- true -> lists:seq(1, 5) ++ lists:seq(1, 5)
- end,
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
- sub_els =
- [#mam_result{
- queryid = QID,
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els =
- [#message{
- from = MyJID, to = Peer,
- body = [Text]}]}]}]})
- end, Iter),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I,
- sub_els = [#mam_query{xmlns = NS, id = QID}]});
- true ->
- ?recv1(#message{sub_els = [#mam_fin{complete = true, id = QID}]})
- end.
-
-mam_query_with(Config, JID, NS) ->
- MyJID = my_jid(Config),
- Peer = ?config(slave, Config),
- {Query, Type} = if NS == ?NS_MAM_TMP ->
- {#mam_query{xmlns = NS, with = JID}, get};
- true ->
- Fs = [#xdata_field{var = <<"jid">>,
- values = [jid:to_string(JID)]}],
- {#mam_query{xmlns = NS,
- xdata = #xdata{type = submit, fields = Fs}}, set}
- end,
- I = send(Config, #iq{type = Type, sub_els = [Query]}),
- Iter = if NS == ?NS_MAM_TMP -> lists:seq(1, 5);
- true -> lists:seq(1, 5) ++ lists:seq(1, 5)
- end,
- maybe_recv_iq_result(NS, I),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
- sub_els =
- [#mam_result{
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els =
- [#message{
- from = MyJID, to = Peer,
- body = [Text]}]}]}]})
- end, Iter),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I,
- sub_els = [#mam_query{xmlns = NS}]});
- true ->
- ?recv1(#message{sub_els = [#mam_fin{complete = true}]})
- end.
-
-maybe_recv_iq_result(?NS_MAM_0, I1) ->
- ?recv1(#iq{type = result, id = I1});
-maybe_recv_iq_result(_, _) ->
- ok.
-
-mam_query_rsm(Config, NS) ->
- MyJID = my_jid(Config),
- Peer = ?config(slave, Config),
- Type = case NS of
- ?NS_MAM_TMP -> get;
- _ -> set
- end,
- %% Get the first 3 items out of 5
- I1 = send(Config,
- #iq{type = Type,
- sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{max = 3}}]}),
- maybe_recv_iq_result(NS, I1),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
- sub_els =
- [#mam_result{
- xmlns = NS,
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els =
- [#message{
- from = MyJID, to = Peer,
- body = [Text]}]}]}]})
- end, lists:seq(1, 3)),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I1,
- sub_els = [#mam_query{xmlns = NS,
- rsm = #rsm_set{last = Last, count = 5}}]});
- true ->
- ?recv1(#message{sub_els = [#mam_fin{
- complete = false,
- rsm = #rsm_set{last = Last, count = 10}}]})
- end,
- %% Get the next items starting from the `Last`.
- %% Limit the response to 2 items.
- I2 = send(Config,
- #iq{type = Type,
- sub_els = [#mam_query{xmlns = NS,
- rsm = #rsm_set{max = 2,
- 'after' = Last}}]}),
- maybe_recv_iq_result(NS, I2),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
- sub_els =
- [#mam_result{
- xmlns = NS,
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els =
- [#message{
- from = MyJID, to = Peer,
- body = [Text]}]}]}]})
- end, lists:seq(4, 5)),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I2,
- sub_els = [#mam_query{
- xmlns = NS,
- rsm = #rsm_set{
- count = 5,
- first = #rsm_first{data = First}}}]});
- true ->
- ?recv1(#message{
- sub_els = [#mam_fin{
- complete = false,
- rsm = #rsm_set{
- count = 10,
- first = #rsm_first{data = First}}}]})
- end,
- %% Paging back. Should receive 3 elements: 1, 2, 3.
- I3 = send(Config,
- #iq{type = Type,
- sub_els = [#mam_query{xmlns = NS,
- rsm = #rsm_set{max = 3,
- before = First}}]}),
- maybe_recv_iq_result(NS, I3),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
- sub_els =
- [#mam_result{
- xmlns = NS,
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els =
- [#message{
- from = MyJID, to = Peer,
- body = [Text]}]}]}]})
- end, lists:seq(1, 3)),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I3,
- sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]});
- true ->
- ?recv1(#message{
- sub_els = [#mam_fin{complete = true,
- rsm = #rsm_set{count = 10}}]})
- end,
- %% Getting the item count. Should be 5 (or 10).
- I4 = send(Config,
- #iq{type = Type,
- sub_els = [#mam_query{xmlns = NS,
- rsm = #rsm_set{max = 0}}]}),
- maybe_recv_iq_result(NS, I4),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I4,
- sub_els = [#mam_query{
- xmlns = NS,
- rsm = #rsm_set{count = 5,
- first = undefined,
- last = undefined}}]});
- true ->
- ?recv1(#message{
- sub_els = [#mam_fin{
- complete = false,
- rsm = #rsm_set{count = 10,
- first = undefined,
- last = undefined}}]})
- end,
- %% Should receive 2 last messages
- I5 = send(Config,
- #iq{type = Type,
- sub_els = [#mam_query{xmlns = NS,
- rsm = #rsm_set{max = 2,
- before = none}}]}),
- maybe_recv_iq_result(NS, I5),
- lists:foreach(
- fun(N) ->
- Text = #text{data = integer_to_binary(N)},
- ?recv1(#message{to = MyJID,
- sub_els =
- [#mam_result{
- xmlns = NS,
- sub_els =
- [#forwarded{
- delay = #delay{},
- sub_els =
- [#message{
- from = MyJID, to = Peer,
- body = [Text]}]}]}]})
- end, lists:seq(4, 5)),
- if NS == ?NS_MAM_TMP ->
- ?recv1(#iq{type = result, id = I5,
- sub_els = [#mam_query{xmlns = NS, rsm = #rsm_set{count = 5}}]});
- true ->
- ?recv1(#message{
- sub_els = [#mam_fin{complete = false,
- rsm = #rsm_set{count = 10}}]})
- end.
-
-client_state_master(Config) ->
- true = ?config(csi, Config),
- Peer = ?config(slave, Config),
- Presence = #presence{to = Peer},
- ChatState = #message{to = Peer, thread = <<"1">>,
- sub_els = [#chatstate{type = active}]},
- Message = ChatState#message{body = [#text{data = <<"body">>}]},
- PepPayload = xmpp_codec:encode(#presence{}),
- PepOne = #message{
- to = Peer,
- sub_els =
- [#pubsub_event{
- items =
- [#pubsub_event_items{
- node = <<"foo-1">>,
- items =
- [#pubsub_event_item{
- id = <<"pep-1">>,
- xml_els = [PepPayload]}]}]}]},
- PepTwo = #message{
- to = Peer,
- sub_els =
- [#pubsub_event{
- items =
- [#pubsub_event_items{
- node = <<"foo-2">>,
- items =
- [#pubsub_event_item{
- id = <<"pep-2">>,
- xml_els = [PepPayload]}]}]}]},
- %% Wait for the slave to become inactive.
- wait_for_slave(Config),
- %% Should be queued (but see below):
- send(Config, Presence),
- %% Should replace the previous presence in the queue:
- send(Config, Presence#presence{type = unavailable}),
- %% The following two PEP stanzas should be queued (but see below):
- send(Config, PepOne),
- send(Config, PepTwo),
- %% The following two PEP stanzas should replace the previous two:
- send(Config, PepOne),
- send(Config, PepTwo),
- %% Should be queued (but see below):
- send(Config, ChatState),
- %% Should replace the previous chat state in the queue:
- send(Config, ChatState#message{sub_els = [#chatstate{type = composing}]}),
- %% Should be sent immediately, together with the queued stanzas:
- send(Config, Message),
- %% Wait for the slave to become active.
- wait_for_slave(Config),
- %% Should be delivered, as the client is active again:
- send(Config, ChatState),
- disconnect(Config).
-
-client_state_slave(Config) ->
- Peer = ?config(master, Config),
- change_client_state(Config, inactive),
- wait_for_master(Config),
- ?recv1(#presence{from = Peer, type = unavailable,
- sub_els = [#delay{}]}),
- #message{
- from = Peer,
- sub_els =
- [#pubsub_event{
- items =
- [#pubsub_event_items{
- node = <<"foo-1">>,
- items =
- [#pubsub_event_item{
- id = <<"pep-1">>}]}]},
- #delay{}]} = recv(),
- #message{
- from = Peer,
- sub_els =
- [#pubsub_event{
- items =
- [#pubsub_event_items{
- node = <<"foo-2">>,
- items =
- [#pubsub_event_item{
- id = <<"pep-2">>}]}]},
- #delay{}]} = recv(),
- ?recv1(#message{from = Peer, thread = <<"1">>,
- sub_els = [#chatstate{type = composing},
- #delay{}]}),
- ?recv1(#message{from = Peer, thread = <<"1">>,
- body = [#text{data = <<"body">>}],
- sub_els = [#chatstate{type = active}]}),
- change_client_state(Config, active),
- wait_for_master(Config),
- ?recv1(#message{from = Peer, thread = <<"1">>,
- sub_els = [#chatstate{type = active}]}),
- disconnect(Config).
-
%%%===================================================================
%%% Aux functions
%%%===================================================================
-change_client_state(Config, NewState) ->
- send(Config, #csi{type = NewState}),
- send_recv(Config, #iq{type = get, to = server_jid(Config),
- sub_els = [#ping{}]}).
-
bookmark_conference() ->
#bookmark_conference{name = <<"Some name">>,
autojoin = true,
@@ -2377,50 +969,56 @@ bookmark_conference() ->
<<"some.conference.org">>,
<<>>)}.
-socks5_connect(#streamhost{host = Host, port = Port},
- {SID, JID1, JID2}) ->
- Hash = p1_sha:sha([SID, jid:to_string(JID1), jid:to_string(JID2)]),
- {ok, Sock} = gen_tcp:connect(binary_to_list(Host), Port,
- [binary, {active, false}]),
- Init = <<?VERSION_5, 1, ?AUTH_ANONYMOUS>>,
- InitAck = <<?VERSION_5, ?AUTH_ANONYMOUS>>,
- Req = <<?VERSION_5, ?CMD_CONNECT, 0,
- ?ATYP_DOMAINNAME, 40, Hash:40/binary, 0, 0>>,
- Resp = <<?VERSION_5, ?SUCCESS, 0, ?ATYP_DOMAINNAME,
- 40, Hash:40/binary, 0, 0>>,
- gen_tcp:send(Sock, Init),
- {ok, InitAck} = gen_tcp:recv(Sock, size(InitAck)),
- gen_tcp:send(Sock, Req),
- {ok, Resp} = gen_tcp:recv(Sock, size(Resp)),
- Sock.
-
-socks5_send(Sock, Data) ->
- ok = gen_tcp:send(Sock, Data).
-
-socks5_recv(Sock, Data) ->
- {ok, Data} = gen_tcp:recv(Sock, size(Data)).
+'$handle_undefined_function'(F, [Config]) when is_list(Config) ->
+ case re:split(atom_to_list(F), "_", [{return, list}, {parts, 2}]) of
+ [M, T] ->
+ Module = list_to_atom(M ++ "_tests"),
+ Function = list_to_atom(T),
+ case erlang:function_exported(Module, Function, 1) of
+ true ->
+ Module:Function(Config);
+ false ->
+ erlang:error({undef, F})
+ end;
+ _ ->
+ erlang:error({undef, F})
+ end;
+'$handle_undefined_function'(_, _) ->
+ erlang:error(undef).
%%%===================================================================
%%% SQL stuff
%%%===================================================================
-create_sql_tables(sqlite, _BaseDir) ->
+clear_sql_tables(sqlite, _BaseDir) ->
ok;
-create_sql_tables(Type, BaseDir) ->
+clear_sql_tables(Type, BaseDir) ->
{VHost, File} = case Type of
mysql ->
- {?MYSQL_VHOST, "mysql.sql"};
+ Path = case ejabberd_sql:use_new_schema() of
+ true ->
+ "mysql.new.sql";
+ false ->
+ "mysql.sql"
+ end,
+ {?MYSQL_VHOST, Path};
pgsql ->
- {?PGSQL_VHOST, "pg.sql"}
+ Path = case ejabberd_sql:use_new_schema() of
+ true ->
+ "pg.new.sql";
+ false ->
+ "pg.sql"
+ end,
+ {?PGSQL_VHOST, Path}
end,
SQLFile = filename:join([BaseDir, "sql", File]),
CreationQueries = read_sql_queries(SQLFile),
- DropTableQueries = drop_table_queries(CreationQueries),
+ ClearTableQueries = clear_table_queries(CreationQueries),
case ejabberd_sql:sql_transaction(
- VHost, DropTableQueries ++ CreationQueries) of
+ VHost, ClearTableQueries) of
{atomic, ok} ->
ok;
Err ->
- ct:fail({failed_to_create_sql_tables, Type, Err})
+ ct:fail({failed_to_clear_sql_tables, Type, Err})
end.
read_sql_queries(File) ->
@@ -2431,12 +1029,12 @@ read_sql_queries(File) ->
ct:fail({open_file_failed, File, Err})
end.
-drop_table_queries(Queries) ->
+clear_table_queries(Queries) ->
lists:foldl(
fun(Query, Acc) ->
case split(str:to_lower(Query)) of
[<<"create">>, <<"table">>, Table|_] ->
- [<<"DROP TABLE IF EXISTS ", Table/binary, ";">>|Acc];
+ [<<"DELETE FROM ", Table/binary, ";">>|Acc];
_ ->
Acc
end
@@ -2476,16 +1074,3 @@ split(Data) ->
(_) ->
true
end, re:split(Data, <<"\s">>)).
-
-clear_riak_tables(Config) ->
- User = ?config(user, Config),
- Server = ?config(server, Config),
- Room = muc_room_jid(Config),
- {URoom, SRoom, _} = jid:tolower(Room),
- ejabberd_auth:remove_user(User, Server),
- ejabberd_auth:remove_user(<<"test_slave">>, Server),
- ejabberd_auth:remove_user(<<"test_master">>, Server),
- mod_muc:forget_room(Server, URoom, SRoom),
- ejabberd_riak:delete(muc_registered, {{<<"test_slave">>, Server}, SRoom}),
- ejabberd_riak:delete(muc_registered, {{<<"test_master">>, Server}, SRoom}),
- Config.