mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Let a MUC room to route presences from its bare JID
The goal for this is to provide entity capabilities (XEP-0115) and vCard-based avatar hash (XEP-0153)
This commit is contained in:
parent
66fc1bf3b6
commit
ffe02c46e4
@ -63,6 +63,7 @@
|
|||||||
max_users = ?MAX_USERS_DEFAULT :: non_neg_integer() | none,
|
max_users = ?MAX_USERS_DEFAULT :: non_neg_integer() | none,
|
||||||
logging = false :: boolean(),
|
logging = false :: boolean(),
|
||||||
vcard = <<"">> :: binary(),
|
vcard = <<"">> :: binary(),
|
||||||
|
vcard_xupdate = undefined :: undefined | external | binary(),
|
||||||
captcha_whitelist = (?SETS):empty() :: ?TGB_SET,
|
captcha_whitelist = (?SETS):empty() :: ?TGB_SET,
|
||||||
mam = false :: boolean(),
|
mam = false :: boolean(),
|
||||||
pubsub = <<"">> :: binary()
|
pubsub = <<"">> :: binary()
|
||||||
|
@ -38,7 +38,8 @@
|
|||||||
-export([read_caps/1, list_features/1, caps_stream_features/2,
|
-export([read_caps/1, list_features/1, caps_stream_features/2,
|
||||||
disco_features/5, disco_identity/5, disco_info/5,
|
disco_features/5, disco_identity/5, disco_info/5,
|
||||||
get_features/2, export/1, import_info/0, import/5,
|
get_features/2, export/1, import_info/0, import/5,
|
||||||
get_user_caps/2, import_start/2, import_stop/2]).
|
get_user_caps/2, import_start/2, import_stop/2,
|
||||||
|
compute_disco_hash/2, is_valid_node/1]).
|
||||||
|
|
||||||
%% gen_mod callbacks
|
%% gen_mod callbacks
|
||||||
-export([start/2, stop/1, reload/3, depends/2]).
|
-export([start/2, stop/1, reload/3, depends/2]).
|
||||||
@ -412,13 +413,13 @@ make_my_disco_hash(Host) ->
|
|||||||
DiscoInfo = #disco_info{identities = Identities,
|
DiscoInfo = #disco_info{identities = Identities,
|
||||||
features = Feats,
|
features = Feats,
|
||||||
xdata = Info},
|
xdata = Info},
|
||||||
make_disco_hash(DiscoInfo, sha);
|
compute_disco_hash(DiscoInfo, sha);
|
||||||
_Err -> <<"">>
|
_Err -> <<"">>
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
|
-type digest_type() :: md5 | sha | sha224 | sha256 | sha384 | sha512.
|
||||||
-spec make_disco_hash(disco_info(), digest_type()) -> binary().
|
-spec compute_disco_hash(disco_info(), digest_type()) -> binary().
|
||||||
make_disco_hash(DiscoInfo, Algo) ->
|
compute_disco_hash(DiscoInfo, Algo) ->
|
||||||
Concat = list_to_binary([concat_identities(DiscoInfo),
|
Concat = list_to_binary([concat_identities(DiscoInfo),
|
||||||
concat_features(DiscoInfo), concat_info(DiscoInfo)]),
|
concat_features(DiscoInfo), concat_info(DiscoInfo)]),
|
||||||
base64:encode(case Algo of
|
base64:encode(case Algo of
|
||||||
@ -434,17 +435,17 @@ make_disco_hash(DiscoInfo, Algo) ->
|
|||||||
check_hash(Caps, DiscoInfo) ->
|
check_hash(Caps, DiscoInfo) ->
|
||||||
case Caps#caps.hash of
|
case Caps#caps.hash of
|
||||||
<<"md5">> ->
|
<<"md5">> ->
|
||||||
Caps#caps.version == make_disco_hash(DiscoInfo, md5);
|
Caps#caps.version == compute_disco_hash(DiscoInfo, md5);
|
||||||
<<"sha-1">> ->
|
<<"sha-1">> ->
|
||||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha);
|
Caps#caps.version == compute_disco_hash(DiscoInfo, sha);
|
||||||
<<"sha-224">> ->
|
<<"sha-224">> ->
|
||||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha224);
|
Caps#caps.version == compute_disco_hash(DiscoInfo, sha224);
|
||||||
<<"sha-256">> ->
|
<<"sha-256">> ->
|
||||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha256);
|
Caps#caps.version == compute_disco_hash(DiscoInfo, sha256);
|
||||||
<<"sha-384">> ->
|
<<"sha-384">> ->
|
||||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha384);
|
Caps#caps.version == compute_disco_hash(DiscoInfo, sha384);
|
||||||
<<"sha-512">> ->
|
<<"sha-512">> ->
|
||||||
Caps#caps.version == make_disco_hash(DiscoInfo, sha512);
|
Caps#caps.version == compute_disco_hash(DiscoInfo, sha512);
|
||||||
_ -> true
|
_ -> true
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ normal_state({route, <<"">>,
|
|||||||
process_iq_admin(From, IQ, StateData);
|
process_iq_admin(From, IQ, StateData);
|
||||||
?NS_MUC_OWNER ->
|
?NS_MUC_OWNER ->
|
||||||
process_iq_owner(From, IQ, StateData);
|
process_iq_owner(From, IQ, StateData);
|
||||||
?NS_DISCO_INFO when SubEl#disco_info.node == <<>> ->
|
?NS_DISCO_INFO ->
|
||||||
process_iq_disco_info(From, IQ, StateData);
|
process_iq_disco_info(From, IQ, StateData);
|
||||||
?NS_DISCO_ITEMS ->
|
?NS_DISCO_ITEMS ->
|
||||||
process_iq_disco_items(From, IQ, StateData);
|
process_iq_disco_items(From, IQ, StateData);
|
||||||
@ -2066,12 +2066,30 @@ presence_broadcast_allowed(JID, StateData) ->
|
|||||||
-spec send_initial_presences_and_messages(
|
-spec send_initial_presences_and_messages(
|
||||||
jid(), binary(), presence(), state(), state()) -> ok.
|
jid(), binary(), presence(), state(), state()) -> ok.
|
||||||
send_initial_presences_and_messages(From, Nick, Presence, NewState, OldState) ->
|
send_initial_presences_and_messages(From, Nick, Presence, NewState, OldState) ->
|
||||||
|
send_self_presence(From, NewState),
|
||||||
send_existing_presences(From, NewState),
|
send_existing_presences(From, NewState),
|
||||||
send_initial_presence(From, NewState, OldState),
|
send_initial_presence(From, NewState, OldState),
|
||||||
History = get_history(Nick, Presence, NewState),
|
History = get_history(Nick, Presence, NewState),
|
||||||
send_history(From, History, NewState),
|
send_history(From, History, NewState),
|
||||||
send_subject(From, OldState).
|
send_subject(From, OldState).
|
||||||
|
|
||||||
|
-spec send_self_presence(jid(), state()) -> ok.
|
||||||
|
send_self_presence(JID, State) ->
|
||||||
|
AvatarHash = (State#state.config)#config.vcard_xupdate,
|
||||||
|
DiscoInfo = make_disco_info(JID, State),
|
||||||
|
DiscoHash = mod_caps:compute_disco_hash(DiscoInfo, sha),
|
||||||
|
Els1 = [#caps{hash = <<"sha-1">>,
|
||||||
|
node = ?EJABBERD_URI,
|
||||||
|
version = DiscoHash}],
|
||||||
|
Els2 = if is_binary(AvatarHash) ->
|
||||||
|
[#vcard_xupdate{hash = AvatarHash}|Els1];
|
||||||
|
true ->
|
||||||
|
Els1
|
||||||
|
end,
|
||||||
|
ejabberd_router:route(#presence{from = State#state.jid, to = JID,
|
||||||
|
id = randoms:get_string(),
|
||||||
|
sub_els = Els2}).
|
||||||
|
|
||||||
-spec send_initial_presence(jid(), state(), state()) -> ok.
|
-spec send_initial_presence(jid(), state(), state()) -> ok.
|
||||||
send_initial_presence(NJID, StateData, OldStateData) ->
|
send_initial_presence(NJID, StateData, OldStateData) ->
|
||||||
send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
|
send_new_presence1(NJID, <<"">>, true, StateData, OldStateData).
|
||||||
@ -3342,12 +3360,15 @@ send_config_change_info(New, #state{config = Old} = StateData) ->
|
|||||||
end
|
end
|
||||||
++
|
++
|
||||||
case Old#config{anonymous = New#config.anonymous,
|
case Old#config{anonymous = New#config.anonymous,
|
||||||
vcard = New#config.vcard,
|
|
||||||
logging = New#config.logging} of
|
logging = New#config.logging} of
|
||||||
New -> [];
|
New -> [];
|
||||||
_ -> [104]
|
_ -> [104]
|
||||||
end,
|
end,
|
||||||
if Codes /= [] ->
|
if Codes /= [] ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({_LJID, #user{jid = JID}}) ->
|
||||||
|
send_self_presence(JID, StateData#state{config = New})
|
||||||
|
end, ?DICT:to_list(StateData#state.users)),
|
||||||
Message = #message{type = groupchat,
|
Message = #message{type = groupchat,
|
||||||
id = randoms:get_string(),
|
id = randoms:get_string(),
|
||||||
sub_els = [#muc_user{status_codes = Codes}]},
|
sub_els = [#muc_user{status_codes = Codes}]},
|
||||||
@ -3375,7 +3396,8 @@ remove_nonmembers(StateData) ->
|
|||||||
StateData, (?DICT):to_list(get_users_and_subscribers(StateData))).
|
StateData, (?DICT):to_list(get_users_and_subscribers(StateData))).
|
||||||
|
|
||||||
-spec set_opts([{atom(), any()}], state()) -> state().
|
-spec set_opts([{atom(), any()}], state()) -> state().
|
||||||
set_opts([], StateData) -> StateData;
|
set_opts([], StateData) ->
|
||||||
|
set_vcard_xupdate(StateData);
|
||||||
set_opts([{Opt, Val} | Opts], StateData) ->
|
set_opts([{Opt, Val} | Opts], StateData) ->
|
||||||
NSD = case Opt of
|
NSD = case Opt of
|
||||||
title ->
|
title ->
|
||||||
@ -3490,6 +3512,10 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
|||||||
StateData#state{config =
|
StateData#state{config =
|
||||||
(StateData#state.config)#config{vcard =
|
(StateData#state.config)#config{vcard =
|
||||||
Val}};
|
Val}};
|
||||||
|
vcard_xupdate ->
|
||||||
|
StateData#state{config =
|
||||||
|
(StateData#state.config)#config{vcard_xupdate =
|
||||||
|
Val}};
|
||||||
pubsub ->
|
pubsub ->
|
||||||
StateData#state{config =
|
StateData#state{config =
|
||||||
(StateData#state.config)#config{pubsub = Val}};
|
(StateData#state.config)#config{pubsub = Val}};
|
||||||
@ -3524,6 +3550,20 @@ set_opts([{Opt, Val} | Opts], StateData) ->
|
|||||||
end,
|
end,
|
||||||
set_opts(Opts, NSD).
|
set_opts(Opts, NSD).
|
||||||
|
|
||||||
|
set_vcard_xupdate(#state{config =
|
||||||
|
#config{vcard = VCardRaw,
|
||||||
|
vcard_xupdate = undefined} = Config} = State)
|
||||||
|
when VCardRaw /= <<"">> ->
|
||||||
|
case fxml_stream:parse_element(VCardRaw) of
|
||||||
|
{error, _} ->
|
||||||
|
State;
|
||||||
|
El ->
|
||||||
|
Hash = mod_vcard_xupdate:compute_hash(El),
|
||||||
|
State#state{config = Config#config{vcard_xupdate = Hash}}
|
||||||
|
end;
|
||||||
|
set_vcard_xupdate(State) ->
|
||||||
|
State.
|
||||||
|
|
||||||
-define(MAKE_CONFIG_OPT(Opt),
|
-define(MAKE_CONFIG_OPT(Opt),
|
||||||
{get_config_opt_name(Opt), element(Opt, Config)}).
|
{get_config_opt_name(Opt), element(Opt, Config)}).
|
||||||
|
|
||||||
@ -3559,6 +3599,7 @@ make_opts(StateData) ->
|
|||||||
?MAKE_CONFIG_OPT(#config.presence_broadcast),
|
?MAKE_CONFIG_OPT(#config.presence_broadcast),
|
||||||
?MAKE_CONFIG_OPT(#config.voice_request_min_interval),
|
?MAKE_CONFIG_OPT(#config.voice_request_min_interval),
|
||||||
?MAKE_CONFIG_OPT(#config.vcard),
|
?MAKE_CONFIG_OPT(#config.vcard),
|
||||||
|
?MAKE_CONFIG_OPT(#config.vcard_xupdate),
|
||||||
?MAKE_CONFIG_OPT(#config.pubsub),
|
?MAKE_CONFIG_OPT(#config.pubsub),
|
||||||
{captcha_whitelist,
|
{captcha_whitelist,
|
||||||
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
|
(?SETS):to_list((StateData#state.config)#config.captcha_whitelist)},
|
||||||
@ -3602,12 +3643,8 @@ destroy_room(DEl, StateData) ->
|
|||||||
false -> Fiffalse
|
false -> Fiffalse
|
||||||
end).
|
end).
|
||||||
|
|
||||||
-spec process_iq_disco_info(jid(), iq(), state()) ->
|
-spec make_disco_info(jid(), state()) -> disco_info().
|
||||||
{result, disco_info()} | {error, stanza_error()}.
|
make_disco_info(_From, StateData) ->
|
||||||
process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
|
|
||||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
|
||||||
{error, xmpp:err_not_allowed(Txt, Lang)};
|
|
||||||
process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
|
|
||||||
Config = StateData#state.config,
|
Config = StateData#state.config,
|
||||||
Feats = [?NS_VCARD, ?NS_MUC,
|
Feats = [?NS_VCARD, ?NS_MUC,
|
||||||
?CONFIG_OPT_TO_FEATURE((Config#config.public),
|
?CONFIG_OPT_TO_FEATURE((Config#config.public),
|
||||||
@ -3633,11 +3670,35 @@ process_iq_disco_info(_From, #iq{type = get, lang = Lang}, StateData) ->
|
|||||||
_ ->
|
_ ->
|
||||||
[]
|
[]
|
||||||
end,
|
end,
|
||||||
{result, #disco_info{xdata = [iq_disco_info_extras(Lang, StateData)],
|
#disco_info{identities = [#identity{category = <<"conference">>,
|
||||||
identities = [#identity{category = <<"conference">>,
|
|
||||||
type = <<"text">>,
|
type = <<"text">>,
|
||||||
name = get_title(StateData)}],
|
name = get_title(StateData)}],
|
||||||
features = Feats}}.
|
features = Feats}.
|
||||||
|
|
||||||
|
-spec process_iq_disco_info(jid(), iq(), state()) ->
|
||||||
|
{result, disco_info()} | {error, stanza_error()}.
|
||||||
|
process_iq_disco_info(_From, #iq{type = set, lang = Lang}, _StateData) ->
|
||||||
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
|
{error, xmpp:err_not_allowed(Txt, Lang)};
|
||||||
|
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||||
|
sub_els = [#disco_info{node = <<>>}]},
|
||||||
|
StateData) ->
|
||||||
|
DiscoInfo = make_disco_info(From, StateData),
|
||||||
|
Extras = iq_disco_info_extras(Lang, StateData),
|
||||||
|
{result, DiscoInfo#disco_info{xdata = [Extras]}};
|
||||||
|
process_iq_disco_info(From, #iq{type = get, lang = Lang,
|
||||||
|
sub_els = [#disco_info{node = Node}]},
|
||||||
|
StateData) ->
|
||||||
|
try
|
||||||
|
true = mod_caps:is_valid_node(Node),
|
||||||
|
DiscoInfo = make_disco_info(From, StateData),
|
||||||
|
Hash = mod_caps:compute_disco_hash(DiscoInfo, sha),
|
||||||
|
Node = <<(?EJABBERD_URI)/binary, $#, Hash/binary>>,
|
||||||
|
{result, DiscoInfo#disco_info{node = Node}}
|
||||||
|
catch _:{badmatch, _} ->
|
||||||
|
Txt = <<"Invalid node name">>,
|
||||||
|
{error, xmpp:err_item_not_found(Txt, Lang)}
|
||||||
|
end.
|
||||||
|
|
||||||
-spec iq_disco_info_extras(binary(), state()) -> xdata().
|
-spec iq_disco_info_extras(binary(), state()) -> xdata().
|
||||||
iq_disco_info_extras(Lang, StateData) ->
|
iq_disco_info_extras(Lang, StateData) ->
|
||||||
@ -3703,13 +3764,15 @@ process_iq_vcard(_From, #iq{type = get}, StateData) ->
|
|||||||
{error, _} ->
|
{error, _} ->
|
||||||
{error, xmpp:err_item_not_found()}
|
{error, xmpp:err_item_not_found()}
|
||||||
end;
|
end;
|
||||||
process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [SubEl]},
|
process_iq_vcard(From, #iq{type = set, lang = Lang, sub_els = [Pkt]},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
case get_affiliation(From, StateData) of
|
case get_affiliation(From, StateData) of
|
||||||
owner ->
|
owner ->
|
||||||
VCardRaw = fxml:element_to_binary(xmpp:encode(SubEl)),
|
SubEl = xmpp:encode(Pkt),
|
||||||
|
VCardRaw = fxml:element_to_binary(SubEl),
|
||||||
|
Hash = mod_vcard_xupdate:compute_hash(SubEl),
|
||||||
Config = StateData#state.config,
|
Config = StateData#state.config,
|
||||||
NewConfig = Config#config{vcard = VCardRaw},
|
NewConfig = Config#config{vcard = VCardRaw, vcard_xupdate = Hash},
|
||||||
change_config(NewConfig, StateData);
|
change_config(NewConfig, StateData);
|
||||||
_ ->
|
_ ->
|
||||||
ErrText = <<"Owner privileges required">>,
|
ErrText = <<"Owner privileges required">>,
|
||||||
@ -4133,6 +4196,28 @@ send_wrapped(From, To, Packet, Node, State) ->
|
|||||||
ok
|
ok
|
||||||
end;
|
end;
|
||||||
true ->
|
true ->
|
||||||
|
case Packet of
|
||||||
|
#presence{type = unavailable} ->
|
||||||
|
case xmpp:get_subtag(Packet, #muc_user{}) of
|
||||||
|
#muc_user{destroy = Destroy,
|
||||||
|
status_codes = Codes} ->
|
||||||
|
case Destroy /= undefined orelse
|
||||||
|
(lists:member(110,Codes) andalso
|
||||||
|
not lists:member(303, Codes)) of
|
||||||
|
true ->
|
||||||
|
ejabberd_router:route(
|
||||||
|
#presence{from = State#state.jid, to = To,
|
||||||
|
id = randoms:get_string(),
|
||||||
|
type = unavailable});
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
ejabberd_router:route(xmpp:set_from_to(Packet, From, To))
|
ejabberd_router:route(xmpp:set_from_to(Packet, From, To))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
-export([update_presence/1, vcard_set/1, remove_user/2,
|
-export([update_presence/1, vcard_set/1, remove_user/2,
|
||||||
user_send_packet/1, mod_opt_type/1, mod_options/1, depends/2]).
|
user_send_packet/1, mod_opt_type/1, mod_options/1, depends/2]).
|
||||||
|
%% API
|
||||||
|
-export([compute_hash/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
@ -800,6 +800,11 @@ change_affiliation_slave(Config, {Aff, Role, Status, Reason}) ->
|
|||||||
MyNick = ?config(nick, Config),
|
MyNick = ?config(nick, Config),
|
||||||
MyNickJID = jid:replace_resource(Room, MyNick),
|
MyNickJID = jid:replace_resource(Room, MyNick),
|
||||||
ct:comment("Receiving affiliation change to ~s", [Aff]),
|
ct:comment("Receiving affiliation change to ~s", [Aff]),
|
||||||
|
if Aff == outcast ->
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config);
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end,
|
||||||
#muc_user{status_codes = Codes,
|
#muc_user{status_codes = Codes,
|
||||||
items = [#muc_item{role = Role,
|
items = [#muc_item{role = Role,
|
||||||
actor = Actor,
|
actor = Actor,
|
||||||
@ -858,6 +863,7 @@ kick_slave(Config) ->
|
|||||||
wait_for_master(Config),
|
wait_for_master(Config),
|
||||||
{[], _, _} = join(Config),
|
{[], _, _} = join(Config),
|
||||||
ct:comment("Receiving role change to 'none'"),
|
ct:comment("Receiving role change to 'none'"),
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config),
|
||||||
#muc_user{status_codes = Codes,
|
#muc_user{status_codes = Codes,
|
||||||
items = [#muc_item{role = none,
|
items = [#muc_item{role = none,
|
||||||
affiliation = none,
|
affiliation = none,
|
||||||
@ -889,6 +895,7 @@ destroy_master(Config) ->
|
|||||||
wait_for_slave(Config),
|
wait_for_slave(Config),
|
||||||
ok = destroy(Config, Reason),
|
ok = destroy(Config, Reason),
|
||||||
ct:comment("Receiving destruction presence"),
|
ct:comment("Receiving destruction presence"),
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config),
|
||||||
#muc_user{items = [#muc_item{role = none,
|
#muc_user{items = [#muc_item{role = none,
|
||||||
affiliation = none}],
|
affiliation = none}],
|
||||||
destroy = #muc_destroy{jid = AltRoom,
|
destroy = #muc_destroy{jid = AltRoom,
|
||||||
@ -907,6 +914,7 @@ destroy_slave(Config) ->
|
|||||||
#stanza_error{reason = 'forbidden'} = destroy(Config, Reason),
|
#stanza_error{reason = 'forbidden'} = destroy(Config, Reason),
|
||||||
wait_for_master(Config),
|
wait_for_master(Config),
|
||||||
ct:comment("Receiving destruction presence"),
|
ct:comment("Receiving destruction presence"),
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config),
|
||||||
#muc_user{items = [#muc_item{role = none,
|
#muc_user{items = [#muc_item{role = none,
|
||||||
affiliation = none}],
|
affiliation = none}],
|
||||||
destroy = #muc_destroy{jid = AltRoom,
|
destroy = #muc_destroy{jid = AltRoom,
|
||||||
@ -938,6 +946,7 @@ vcard_master(Config) ->
|
|||||||
vcard_slave(Config) ->
|
vcard_slave(Config) ->
|
||||||
wait_for_master(Config),
|
wait_for_master(Config),
|
||||||
{[], _, _} = join(Config),
|
{[], _, _} = join(Config),
|
||||||
|
[104] = recv_config_change_message(Config),
|
||||||
VCard = get_event(Config),
|
VCard = get_event(Config),
|
||||||
VCard = get_vcard(Config),
|
VCard = get_vcard(Config),
|
||||||
#stanza_error{reason = 'forbidden'} = set_vcard(Config, VCard),
|
#stanza_error{reason = 'forbidden'} = set_vcard(Config, VCard),
|
||||||
@ -1150,11 +1159,13 @@ config_members_only_master(Config) ->
|
|||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
config_members_only_slave(Config) ->
|
config_members_only_slave(Config) ->
|
||||||
|
Room = muc_room_jid(Config),
|
||||||
MyJID = my_jid(Config),
|
MyJID = my_jid(Config),
|
||||||
MyNickJID = my_muc_jid(Config),
|
MyNickJID = my_muc_jid(Config),
|
||||||
{[], _, _} = slave_join(Config),
|
{[], _, _} = slave_join(Config),
|
||||||
[104] = recv_config_change_message(Config),
|
[104] = recv_config_change_message(Config),
|
||||||
ct:comment("Getting kicked because the room has become members-only"),
|
ct:comment("Getting kicked because the room has become members-only"),
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config),
|
||||||
#muc_user{status_codes = Codes,
|
#muc_user{status_codes = Codes,
|
||||||
items = [#muc_item{jid = MyJID,
|
items = [#muc_item{jid = MyJID,
|
||||||
role = none,
|
role = none,
|
||||||
@ -1171,6 +1182,7 @@ config_members_only_slave(Config) ->
|
|||||||
ct:comment("Waiting for the peer to ask for join"),
|
ct:comment("Waiting for the peer to ask for join"),
|
||||||
join = get_event(Config),
|
join = get_event(Config),
|
||||||
{[], _, _} = join(Config, participant, member),
|
{[], _, _} = join(Config, participant, member),
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config),
|
||||||
#muc_user{status_codes = NewCodes,
|
#muc_user{status_codes = NewCodes,
|
||||||
items = [#muc_item{jid = MyJID,
|
items = [#muc_item{jid = MyJID,
|
||||||
role = none,
|
role = none,
|
||||||
@ -1555,8 +1567,9 @@ join_new(Config, Room) ->
|
|||||||
MyJID = my_jid(Config),
|
MyJID = my_jid(Config),
|
||||||
MyNick = ?config(nick, Config),
|
MyNick = ?config(nick, Config),
|
||||||
MyNickJID = jid:replace_resource(Room, MyNick),
|
MyNickJID = jid:replace_resource(Room, MyNick),
|
||||||
ct:comment("Joining new room"),
|
ct:comment("Joining new room ~p", [Room]),
|
||||||
send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
|
send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
|
||||||
|
#presence{from = Room, type = available} = recv_presence(Config),
|
||||||
%% As per XEP-0045 we MUST receive stanzas in the following order:
|
%% As per XEP-0045 we MUST receive stanzas in the following order:
|
||||||
%% 1. In-room presence from other occupants
|
%% 1. In-room presence from other occupants
|
||||||
%% 2. In-room presence from the joining entity itself (so-called "self-presence")
|
%% 2. In-room presence from the joining entity itself (so-called "self-presence")
|
||||||
@ -1625,6 +1638,8 @@ join(Config, Role, Aff, SubEl) ->
|
|||||||
case recv_presence(Config) of
|
case recv_presence(Config) of
|
||||||
#presence{type = error, from = MyNickJID} = Err ->
|
#presence{type = error, from = MyNickJID} = Err ->
|
||||||
xmpp:get_subtag(Err, #stanza_error{});
|
xmpp:get_subtag(Err, #stanza_error{});
|
||||||
|
#presence{from = Room, type = available} ->
|
||||||
|
case recv_presence(Config) of
|
||||||
#presence{type = available, from = PeerNickJID} = Pres ->
|
#presence{type = available, from = PeerNickJID} = Pres ->
|
||||||
#muc_user{items = [#muc_item{role = moderator,
|
#muc_user{items = [#muc_item{role = moderator,
|
||||||
affiliation = owner}]} =
|
affiliation = owner}]} =
|
||||||
@ -1649,6 +1664,7 @@ join(Config, Role, Aff, SubEl) ->
|
|||||||
true = lists:member(110, Codes),
|
true = lists:member(110, Codes),
|
||||||
{History, Subj} = recv_history_and_subject(Config),
|
{History, Subj} = recv_history_and_subject(Config),
|
||||||
{empty, History, Subj, Codes}
|
{empty, History, Subj, Codes}
|
||||||
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
leave(Config) ->
|
leave(Config) ->
|
||||||
@ -1667,6 +1683,7 @@ leave(Config, Room) ->
|
|||||||
end,
|
end,
|
||||||
ct:comment("Leaving the room"),
|
ct:comment("Leaving the room"),
|
||||||
send(Config, #presence{to = MyNickJID, type = unavailable}),
|
send(Config, #presence{to = MyNickJID, type = unavailable}),
|
||||||
|
#presence{from = Room, type = unavailable} = recv_presence(Config),
|
||||||
#muc_user{
|
#muc_user{
|
||||||
status_codes = Codes,
|
status_codes = Codes,
|
||||||
items = [#muc_item{role = none, jid = MyJID}]} =
|
items = [#muc_item{role = none, jid = MyJID}]} =
|
||||||
@ -1702,6 +1719,7 @@ set_config(Config, RoomConfig, Room) ->
|
|||||||
sub_els = [#muc_owner{config = #xdata{type = submit,
|
sub_els = [#muc_owner{config = #xdata{type = submit,
|
||||||
fields = Fs}}]}) of
|
fields = Fs}}]}) of
|
||||||
#iq{type = result, sub_els = []} ->
|
#iq{type = result, sub_els = []} ->
|
||||||
|
#presence{from = Room, type = available} = recv_presence(Config),
|
||||||
#message{from = Room, type = groupchat} = Msg = recv_message(Config),
|
#message{from = Room, type = groupchat} = Msg = recv_message(Config),
|
||||||
#muc_user{status_codes = Codes} = xmpp:get_subtag(Msg, #muc_user{}),
|
#muc_user{status_codes = Codes} = xmpp:get_subtag(Msg, #muc_user{}),
|
||||||
lists:sort(Codes);
|
lists:sort(Codes);
|
||||||
@ -1846,6 +1864,7 @@ set_vcard(Config, VCard) ->
|
|||||||
case send_recv(Config, #iq{type = set, to = Room,
|
case send_recv(Config, #iq{type = set, to = Room,
|
||||||
sub_els = [VCard]}) of
|
sub_els = [VCard]}) of
|
||||||
#iq{type = result, sub_els = []} ->
|
#iq{type = result, sub_els = []} ->
|
||||||
|
[104] = recv_config_change_message(Config),
|
||||||
ok;
|
ok;
|
||||||
#iq{type = error} = Err ->
|
#iq{type = error} = Err ->
|
||||||
xmpp:get_subtag(Err, #stanza_error{})
|
xmpp:get_subtag(Err, #stanza_error{})
|
||||||
@ -1865,6 +1884,7 @@ get_vcard(Config) ->
|
|||||||
recv_config_change_message(Config) ->
|
recv_config_change_message(Config) ->
|
||||||
ct:comment("Receiving configuration change notification message"),
|
ct:comment("Receiving configuration change notification message"),
|
||||||
Room = muc_room_jid(Config),
|
Room = muc_room_jid(Config),
|
||||||
|
#presence{from = Room, type = available} = recv_presence(Config),
|
||||||
#message{type = groupchat, from = Room} = Msg = recv_message(Config),
|
#message{type = groupchat, from = Room} = Msg = recv_message(Config),
|
||||||
#muc_user{status_codes = Codes} = xmpp:get_subtag(Msg, #muc_user{}),
|
#muc_user{status_codes = Codes} = xmpp:get_subtag(Msg, #muc_user{}),
|
||||||
lists:sort(Codes).
|
lists:sort(Codes).
|
||||||
|
Loading…
Reference in New Issue
Block a user