From f2003943db82f35bf714cf3d50a8de5763a7197f Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sun, 20 Jul 2014 22:43:16 +0400 Subject: [PATCH] Add tests for mod_carboncopy --- test/ejabberd_SUITE.erl | 193 ++++++++++++++++++++++---- test/ejabberd_SUITE_data/ejabberd.yml | 2 + test/suite.erl | 2 + tools/xmpp_codec.erl | 189 +++++++++++++++++++++++++ tools/xmpp_codec.hrl | 13 ++ tools/xmpp_codec.spec | 36 +++++ 6 files changed, 405 insertions(+), 30 deletions(-) diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 5151f728d..33107040e 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -105,18 +105,34 @@ 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), Test = atom_to_list(TestCase), IsMaster = lists:suffix("_master", Test), IsSlave = lists:suffix("_slave", Test), - User = if IsMaster -> <<"test_master">>; + IsCarbons = lists:prefix("carbons_", Test), + User = if IsMaster or IsCarbons -> <<"test_master">>; IsSlave -> <<"test_slave">>; true -> <<"test_single">> end, - Slave = jlib:make_jid(<<"test_slave">>, Server, Resource), - Master = jlib:make_jid(<<"test_master">>, Server, Resource), + MyResource = if IsMaster and IsCarbons -> MasterResource; + IsSlave and IsCarbons -> SlaveResource; + true -> Resource + end, + Slave = if IsCarbons -> + jlib:make_jid(<<"test_master">>, Server, SlaveResource); + true -> + jlib:make_jid(<<"test_slave">>, Server, Resource) + end, + Master = if IsCarbons -> + jlib:make_jid(<<"test_master">>, Server, MasterResource); + true -> + jlib:make_jid(<<"test_master">>, Server, Resource) + end, Config = set_opt(user, User, set_opt(slave, Slave, - set_opt(master, Master, OrigConfig))), + set_opt(master, Master, + set_opt(resource, MyResource, OrigConfig)))), case TestCase of test_connect -> Config; @@ -164,32 +180,6 @@ no_db_tests() -> {test_proxy65, [parallel], [proxy65_master, proxy65_slave]}]. -db_tests() -> - [{single_user, [sequence], - [test_register, - auth_plain, - auth_md5, - presence_broadcast, - last, - roster_get, - roster_ver, - private, - privacy, - blocking, - vcard, - pubsub, - test_unregister]}, - {test_roster_subscribe, [parallel], - [roster_subscribe_master, - roster_subscribe_slave]}, - {test_offline, [sequence], - [offline_master, offline_slave]}, - {test_muc, [parallel], - [muc_master, muc_slave]}, - {test_roster_remove, [parallel], - [roster_remove_master, - roster_remove_slave]}]. - db_tests(riak) -> %% No support for mod_pubsub [{single_user, [sequence], @@ -214,7 +204,35 @@ db_tests(riak) -> {test_roster_remove, [parallel], [roster_remove_master, roster_remove_slave]}]; +db_tests(mnesia) -> + [{single_user, [sequence], + [test_register, + auth_plain, + auth_md5, + presence_broadcast, + last, + roster_get, + roster_ver, + private, + privacy, + blocking, + vcard, + pubsub, + test_unregister]}, + {test_roster_subscribe, [parallel], + [roster_subscribe_master, + roster_subscribe_slave]}, + {test_offline, [sequence], + [offline_master, offline_slave]}, + {test_carbons, [parallel], + [carbons_master, carbons_slave]}, + {test_muc, [parallel], + [muc_master, muc_slave]}, + {test_roster_remove, [parallel], + [roster_remove_master, + roster_remove_slave]}]; db_tests(_) -> + %% No support for carboncopy [{single_user, [sequence], [test_register, auth_plain, @@ -1233,6 +1251,121 @@ offline_slave(Config) -> true = lists:keymember(legacy_delay, 1, SubEls), disconnect(Config). +carbons_master(Config) -> + MyJID = my_jid(Config), + MyBareJID = jlib: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}), + #presence{from = MyJID} = recv(), + wait_for_slave(Config), + #presence{from = Peer} = recv(), + %% 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 = jlib: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 + #presence{from = Peer, type = unavailable} = recv(), + disconnect(Config). + %%%=================================================================== %%% Aux functions %%%=================================================================== diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index f0cad2023..50b6540bb 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -138,6 +138,8 @@ Welcome to this XMPP server." db_type: internal mod_vcard: db_type: internal + mod_carboncopy: + db_type: internal mod_adhoc: [] mod_configure: [] mod_disco: [] diff --git a/test/suite.erl b/test/suite.erl index 3f3432159..180be40fb 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -45,6 +45,8 @@ init_config(Config) -> {certfile, CertFile}, {base_dir, BaseDir}, {resource, <<"resource">>}, + {master_resource, <<"master_resource">>}, + {slave_resource, <<"slave_resource">>}, {password, <<"password">>} |Config]. diff --git a/tools/xmpp_codec.erl b/tools/xmpp_codec.erl index 9ffa16354..9758ae49d 100644 --- a/tools/xmpp_codec.erl +++ b/tools/xmpp_codec.erl @@ -12,6 +12,18 @@ decode({xmlel, _name, _attrs, _} = _el) -> case {_name, get_attr(<<"xmlns">>, _attrs)} of + {<<"sent">>, <<"urn:xmpp:carbons:2">>} -> + decode_carbons_sent(_el); + {<<"received">>, <<"urn:xmpp:carbons:2">>} -> + decode_carbons_received(_el); + {<<"private">>, <<"urn:xmpp:carbons:2">>} -> + decode_carbons_private(_el); + {<<"enable">>, <<"urn:xmpp:carbons:2">>} -> + decode_carbons_enable(_el); + {<<"disable">>, <<"urn:xmpp:carbons:2">>} -> + decode_carbons_disable(_el); + {<<"forwarded">>, <<"urn:xmpp:forward:0">>} -> + decode_forwarded(_el); {<<"x">>, <<"http://jabber.org/protocol/muc">>} -> decode_muc(_el); {<<"query">>, @@ -705,6 +717,12 @@ decode({xmlel, _name, _attrs, _} = _el) -> is_known_tag({xmlel, _name, _attrs, _} = _el) -> case {_name, get_attr(<<"xmlns">>, _attrs)} of + {<<"sent">>, <<"urn:xmpp:carbons:2">>} -> true; + {<<"received">>, <<"urn:xmpp:carbons:2">>} -> true; + {<<"private">>, <<"urn:xmpp:carbons:2">>} -> true; + {<<"enable">>, <<"urn:xmpp:carbons:2">>} -> true; + {<<"disable">>, <<"urn:xmpp:carbons:2">>} -> true; + {<<"forwarded">>, <<"urn:xmpp:forward:0">>} -> true; {<<"x">>, <<"http://jabber.org/protocol/muc">>} -> true; {<<"query">>, <<"http://jabber.org/protocol/muc#admin">>} -> @@ -1267,6 +1285,24 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) -> _ -> false end. +encode({carbons_sent, _} = Sent) -> + encode_carbons_sent(Sent, + [{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]); +encode({carbons_received, _} = Received) -> + encode_carbons_received(Received, + [{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]); +encode({carbons_private} = Private) -> + encode_carbons_private(Private, + [{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]); +encode({carbons_enable} = Enable) -> + encode_carbons_enable(Enable, + [{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]); +encode({carbons_disable} = Disable) -> + encode_carbons_disable(Disable, + [{<<"xmlns">>, <<"urn:xmpp:carbons:2">>}]); +encode({forwarded, _, _} = Forwarded) -> + encode_forwarded(Forwarded, + [{<<"xmlns">>, <<"urn:xmpp:forward:0">>}]); encode({muc, _, _} = X) -> encode_muc(X, [{<<"xmlns">>, <<"http://jabber.org/protocol/muc">>}]); @@ -1778,6 +1814,12 @@ pp(muc_item, 7) -> pp(muc_actor, 2) -> [jid, nick]; pp(muc_admin, 1) -> [items]; pp(muc, 2) -> [history, password]; +pp(forwarded, 2) -> [delay, sub_els]; +pp(carbons_disable, 0) -> []; +pp(carbons_enable, 0) -> []; +pp(carbons_private, 0) -> []; +pp(carbons_received, 1) -> [forwarded]; +pp(carbons_sent, 1) -> [forwarded]; pp(_, _) -> no. enc_bool(false) -> <<"false">>; @@ -1818,6 +1860,153 @@ dec_tzo(Val) -> M = jlib:binary_to_integer(M1), if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end. +decode_carbons_sent({xmlel, <<"sent">>, _attrs, + _els}) -> + Forwarded = decode_carbons_sent_els(_els, error), + {carbons_sent, Forwarded}. + +decode_carbons_sent_els([], Forwarded) -> + case Forwarded of + error -> + erlang:error({xmpp_codec, + {missing_tag, <<"forwarded">>, + <<"urn:xmpp:forward:0">>}}); + {value, Forwarded1} -> Forwarded1 + end; +decode_carbons_sent_els([{xmlel, <<"forwarded">>, + _attrs, _} = + _el + | _els], + Forwarded) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == <<"urn:xmpp:forward:0">> -> + decode_carbons_sent_els(_els, + {value, decode_forwarded(_el)}); + true -> decode_carbons_sent_els(_els, Forwarded) + end; +decode_carbons_sent_els([_ | _els], Forwarded) -> + decode_carbons_sent_els(_els, Forwarded). + +encode_carbons_sent({carbons_sent, Forwarded}, + _xmlns_attrs) -> + _els = 'encode_carbons_sent_$forwarded'(Forwarded, []), + _attrs = _xmlns_attrs, + {xmlel, <<"sent">>, _attrs, _els}. + +'encode_carbons_sent_$forwarded'(Forwarded, _acc) -> + [encode_forwarded(Forwarded, + [{<<"xmlns">>, <<"urn:xmpp:forward:0">>}]) + | _acc]. + +decode_carbons_received({xmlel, <<"received">>, _attrs, + _els}) -> + Forwarded = decode_carbons_received_els(_els, error), + {carbons_received, Forwarded}. + +decode_carbons_received_els([], Forwarded) -> + case Forwarded of + error -> + erlang:error({xmpp_codec, + {missing_tag, <<"forwarded">>, + <<"urn:xmpp:forward:0">>}}); + {value, Forwarded1} -> Forwarded1 + end; +decode_carbons_received_els([{xmlel, <<"forwarded">>, + _attrs, _} = + _el + | _els], + Forwarded) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == <<"urn:xmpp:forward:0">> -> + decode_carbons_received_els(_els, + {value, decode_forwarded(_el)}); + true -> decode_carbons_received_els(_els, Forwarded) + end; +decode_carbons_received_els([_ | _els], Forwarded) -> + decode_carbons_received_els(_els, Forwarded). + +encode_carbons_received({carbons_received, Forwarded}, + _xmlns_attrs) -> + _els = 'encode_carbons_received_$forwarded'(Forwarded, + []), + _attrs = _xmlns_attrs, + {xmlel, <<"received">>, _attrs, _els}. + +'encode_carbons_received_$forwarded'(Forwarded, _acc) -> + [encode_forwarded(Forwarded, + [{<<"xmlns">>, <<"urn:xmpp:forward:0">>}]) + | _acc]. + +decode_carbons_private({xmlel, <<"private">>, _attrs, + _els}) -> + {carbons_private}. + +encode_carbons_private({carbons_private}, + _xmlns_attrs) -> + _els = [], + _attrs = _xmlns_attrs, + {xmlel, <<"private">>, _attrs, _els}. + +decode_carbons_enable({xmlel, <<"enable">>, _attrs, + _els}) -> + {carbons_enable}. + +encode_carbons_enable({carbons_enable}, _xmlns_attrs) -> + _els = [], + _attrs = _xmlns_attrs, + {xmlel, <<"enable">>, _attrs, _els}. + +decode_carbons_disable({xmlel, <<"disable">>, _attrs, + _els}) -> + {carbons_disable}. + +encode_carbons_disable({carbons_disable}, + _xmlns_attrs) -> + _els = [], + _attrs = _xmlns_attrs, + {xmlel, <<"disable">>, _attrs, _els}. + +decode_forwarded({xmlel, <<"forwarded">>, _attrs, + _els}) -> + {Delay, __Els} = decode_forwarded_els(_els, undefined, + []), + {forwarded, Delay, __Els}. + +decode_forwarded_els([], Delay, __Els) -> + {Delay, lists:reverse(__Els)}; +decode_forwarded_els([{xmlel, <<"delay">>, _attrs, _} = + _el + | _els], + Delay, __Els) -> + _xmlns = get_attr(<<"xmlns">>, _attrs), + if _xmlns == <<"urn:xmpp:delay">> -> + decode_forwarded_els(_els, decode_delay(_el), __Els); + true -> decode_forwarded_els(_els, Delay, __Els) + end; +decode_forwarded_els([{xmlel, _, _, _} = _el | _els], + Delay, __Els) -> + case is_known_tag(_el) of + true -> + decode_forwarded_els(_els, Delay, + [decode(_el) | __Els]); + false -> decode_forwarded_els(_els, Delay, __Els) + end; +decode_forwarded_els([_ | _els], Delay, __Els) -> + decode_forwarded_els(_els, Delay, __Els). + +encode_forwarded({forwarded, Delay, __Els}, + _xmlns_attrs) -> + _els = 'encode_forwarded_$delay'(Delay, + [encode(_el) || _el <- __Els]), + _attrs = _xmlns_attrs, + {xmlel, <<"forwarded">>, _attrs, _els}. + +'encode_forwarded_$delay'(undefined, _acc) -> _acc; +'encode_forwarded_$delay'(Delay, _acc) -> + [encode_delay(Delay, + [{<<"xmlns">>, <<"urn:xmpp:delay">>}]) + | _acc]. + decode_muc({xmlel, <<"x">>, _attrs, _els}) -> History = decode_muc_els(_els, undefined), Password = decode_muc_attrs(_attrs, undefined), diff --git a/tools/xmpp_codec.hrl b/tools/xmpp_codec.hrl index b9b6aa9eb..d8516201a 100644 --- a/tools/xmpp_codec.hrl +++ b/tools/xmpp_codec.hrl @@ -9,6 +9,10 @@ host :: binary(), port = 1080 :: non_neg_integer()}). +-record(carbons_enable, {}). + +-record(carbons_private, {}). + -record(pubsub_unsubscribe, {node :: binary(), jid :: any(), subid :: binary()}). @@ -32,6 +36,9 @@ -record(starttls_proceed, {}). +-record(forwarded, {delay :: #delay{}, + sub_els = [] :: [any()]}). + -record(starttls_failure, {}). -record(sasl_challenge, {text :: any()}). @@ -144,6 +151,8 @@ subid :: binary(), items = [] :: [#pubsub_item{}]}). +-record(carbons_sent, {forwarded :: #forwarded{}}). + -record(p1_rebind, {}). -record(compress_failure, {reason :: 'processing-failed' | 'setup-failed' | 'unsupported-method'}). @@ -157,6 +166,8 @@ x400 = false :: boolean(), userid :: binary()}). +-record(carbons_received, {forwarded :: #forwarded{}}). + -record(pubsub_retract, {node :: binary(), notify = false :: any(), items = [] :: [#pubsub_item{}]}). @@ -210,6 +221,8 @@ -record(muc_admin, {items = [] :: [#muc_item{}]}). +-record(carbons_disable, {}). + -record(bytestreams, {hosts = [] :: [#streamhost{}], used :: any(), activate :: any(), diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index 62f661381..ad1e24f98 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -2032,6 +2032,42 @@ refs = [#ref{name = muc_history, min = 0, max = 1, label = '$history'}]}). +-xml(forwarded, + #elem{name = <<"forwarded">>, + xmlns = <<"urn:xmpp:forward:0">>, + result = {forwarded, '$delay', '$_els'}, + refs = [#ref{name = delay, min = 0, + max = 1, label = '$delay'}]}). + +-xml(carbons_disable, + #elem{name = <<"disable">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_disable}}). + +-xml(carbons_enable, + #elem{name = <<"enable">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_enable}}). + +-xml(carbons_private, + #elem{name = <<"private">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_private}}). + +-xml(carbons_received, + #elem{name = <<"received">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_received, '$forwarded'}, + refs = [#ref{name = forwarded, min = 1, + max = 1, label = '$forwarded'}]}). + +-xml(carbons_sent, + #elem{name = <<"sent">>, + xmlns = <<"urn:xmpp:carbons:2">>, + result = {carbons_sent, '$forwarded'}, + refs = [#ref{name = forwarded, min = 1, + max = 1, label = '$forwarded'}]}). + dec_tzo(Val) -> [H1, M1] = str:tokens(Val, <<":">>), H = jlib:binary_to_integer(H1),