Add more MUC tests
This commit is contained in:
parent
0f67a8f98b
commit
67720c7713
|
@ -156,7 +156,7 @@ process_iq(Module, Function, #iq{lang = Lang, sub_els = [El]} = IQ) ->
|
||||||
%% TODO: move this 'conditional' decoding somewhere
|
%% TODO: move this 'conditional' decoding somewhere
|
||||||
%% IQ handler should know *nothing* about vCards.
|
%% IQ handler should know *nothing* about vCards.
|
||||||
Pkt = case xmpp:get_ns(El) of
|
Pkt = case xmpp:get_ns(El) of
|
||||||
?NS_VCARD -> El;
|
?NS_VCARD when Module == mod_vcard -> El;
|
||||||
_ -> xmpp:decode(El)
|
_ -> xmpp:decode(El)
|
||||||
end,
|
end,
|
||||||
Module:Function(IQ#iq{sub_els = [Pkt]})
|
Module:Function(IQ#iq{sub_els = [Pkt]})
|
||||||
|
|
|
@ -772,28 +772,25 @@ select(_LServer, JidRequestor, JidArchive,
|
||||||
{groupchat, _Role, #state{config = #config{mam = false},
|
{groupchat, _Role, #state{config = #config{mam = false},
|
||||||
history = History}} = MsgType) ->
|
history = History}} = MsgType) ->
|
||||||
#lqueue{len = L, queue = Q} = History,
|
#lqueue{len = L, queue = Q} = History,
|
||||||
{Msgs0, _} =
|
Msgs =
|
||||||
lists:mapfoldl(
|
lists:flatmap(
|
||||||
fun({Nick, Pkt, _HaveSubject, UTCDateTime, _Size}, I) ->
|
fun({Nick, Pkt, _HaveSubject, Now, _Size}) ->
|
||||||
Now = datetime_to_now(UTCDateTime, I),
|
|
||||||
TS = now_to_usec(Now),
|
TS = now_to_usec(Now),
|
||||||
case match_interval(Now, Start, End) and
|
case match_interval(Now, Start, End) and
|
||||||
match_rsm(Now, RSM) of
|
match_rsm(Now, RSM) of
|
||||||
true ->
|
true ->
|
||||||
{[{integer_to_binary(TS), TS,
|
[{integer_to_binary(TS), TS,
|
||||||
msg_to_el(#archive_msg{
|
msg_to_el(#archive_msg{
|
||||||
type = groupchat,
|
type = groupchat,
|
||||||
timestamp = Now,
|
timestamp = Now,
|
||||||
peer = undefined,
|
peer = undefined,
|
||||||
nick = Nick,
|
nick = Nick,
|
||||||
packet = Pkt},
|
packet = Pkt},
|
||||||
MsgType, JidRequestor, JidArchive)}],
|
MsgType, JidRequestor, JidArchive)}];
|
||||||
I+1};
|
|
||||||
false ->
|
false ->
|
||||||
{[], I+1}
|
[]
|
||||||
end
|
end
|
||||||
end, 0, queue:to_list(Q)),
|
end, queue:to_list(Q)),
|
||||||
Msgs = lists:flatten(Msgs0),
|
|
||||||
case RSM of
|
case RSM of
|
||||||
#rsm_set{max = Max, before = Before} when is_binary(Before) ->
|
#rsm_set{max = Max, before = Before} when is_binary(Before) ->
|
||||||
{NewMsgs, IsComplete} = filter_by_max(lists:reverse(Msgs), Max),
|
{NewMsgs, IsComplete} = filter_by_max(lists:reverse(Msgs), Max),
|
||||||
|
@ -960,11 +957,6 @@ usec_to_now(Int) ->
|
||||||
Sec = Secs rem 1000000,
|
Sec = Secs rem 1000000,
|
||||||
{MSec, Sec, USec}.
|
{MSec, Sec, USec}.
|
||||||
|
|
||||||
datetime_to_now(DateTime, USecs) ->
|
|
||||||
Seconds = calendar:datetime_to_gregorian_seconds(DateTime) -
|
|
||||||
calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
|
|
||||||
{Seconds div 1000000, Seconds rem 1000000, USecs}.
|
|
||||||
|
|
||||||
get_jids(undefined) ->
|
get_jids(undefined) ->
|
||||||
[];
|
[];
|
||||||
get_jids(Js) ->
|
get_jids(Js) ->
|
||||||
|
|
|
@ -377,7 +377,7 @@ do_route1(Host, ServerHost, Access, _HistorySize, _RoomShaper,
|
||||||
end;
|
end;
|
||||||
do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper,
|
do_route1(_Host, _ServerHost, _Access, _HistorySize, _RoomShaper,
|
||||||
From, #jid{luser = <<"">>} = To, Packet, _DefRoomOpts) ->
|
From, #jid{luser = <<"">>} = To, Packet, _DefRoomOpts) ->
|
||||||
Err = xmpp:err_item_not_found(),
|
Err = xmpp:err_service_unavailable(),
|
||||||
ejabberd_router:route_error(To, From, Packet, Err);
|
ejabberd_router:route_error(To, From, Packet, Err);
|
||||||
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||||
From, To, Packet, DefRoomOpts) ->
|
From, To, Packet, DefRoomOpts) ->
|
||||||
|
@ -419,7 +419,7 @@ do_route1(Host, ServerHost, Access, HistorySize, RoomShaper,
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_vcard(iq()) -> iq().
|
-spec process_vcard(iq()) -> iq().
|
||||||
process_vcard(#iq{type = get, lang = Lang} = IQ) ->
|
process_vcard(#iq{type = get, lang = Lang, sub_els = [#vcard_temp{}]} = IQ) ->
|
||||||
Desc = translate:translate(Lang, <<"ejabberd MUC module">>),
|
Desc = translate:translate(Lang, <<"ejabberd MUC module">>),
|
||||||
Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>,
|
Copyright = <<"Copyright (c) 2003-2016 ProcessOne">>,
|
||||||
xmpp:make_iq_result(
|
xmpp:make_iq_result(
|
||||||
|
@ -428,15 +428,19 @@ process_vcard(#iq{type = get, lang = Lang} = IQ) ->
|
||||||
desc = <<Desc/binary, $\n, Copyright/binary>>});
|
desc = <<Desc/binary, $\n, Copyright/binary>>});
|
||||||
process_vcard(#iq{type = set, lang = Lang} = IQ) ->
|
process_vcard(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang)).
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
|
process_vcard(#iq{lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"No module is handling this query">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||||
|
|
||||||
-spec process_register(iq()) -> iq().
|
-spec process_register(iq()) -> iq().
|
||||||
process_register(#iq{type = get, from = From, to = To, lang = Lang} = IQ) ->
|
process_register(#iq{type = get, from = From, to = To, lang = Lang,
|
||||||
|
sub_els = [#register{}]} = IQ) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
ServerHost = ejabberd_router:host_of_route(Host),
|
ServerHost = ejabberd_router:host_of_route(Host),
|
||||||
xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang));
|
xmpp:make_iq_result(IQ, iq_get_register_info(ServerHost, Host, From, Lang));
|
||||||
process_register(#iq{type = set, from = From, to = To,
|
process_register(#iq{type = set, from = From, to = To,
|
||||||
lang = Lang, sub_els = [El]} = IQ) ->
|
lang = Lang, sub_els = [El = #register{}]} = IQ) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
ServerHost = ejabberd_router:host_of_route(Host),
|
ServerHost = ejabberd_router:host_of_route(Host),
|
||||||
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
|
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
|
||||||
|
@ -469,8 +473,12 @@ process_disco_info(#iq{type = get, to = To, lang = Lang,
|
||||||
IQ, #disco_info{features = Features,
|
IQ, #disco_info{features = Features,
|
||||||
identities = [Identity],
|
identities = [Identity],
|
||||||
xdata = X});
|
xdata = X});
|
||||||
process_disco_info(#iq{type = get, lang = Lang} = IQ) ->
|
process_disco_info(#iq{type = get, lang = Lang,
|
||||||
xmpp:make_error(IQ, xmpp:err_item_not_found(<<"No info available">>, Lang)).
|
sub_els = [#disco_info{}]} = IQ) ->
|
||||||
|
xmpp:make_error(IQ, xmpp:err_item_not_found(<<"Node not found">>, Lang));
|
||||||
|
process_disco_info(#iq{lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"No module is handling this query">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||||
|
|
||||||
-spec process_disco_items(iq()) -> iq().
|
-spec process_disco_items(iq()) -> iq().
|
||||||
process_disco_items(#iq{type = set, lang = Lang} = IQ) ->
|
process_disco_items(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
|
@ -481,13 +489,17 @@ process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
xmpp:make_iq_result(
|
xmpp:make_iq_result(
|
||||||
IQ, #disco_items{node = Node,
|
IQ, #disco_items{node = Node,
|
||||||
items = iq_disco_items(Host, From, Lang, Node, RSM)}).
|
items = iq_disco_items(Host, From, Lang, Node, RSM)});
|
||||||
|
process_disco_items(#iq{lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"No module is handling this query">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||||
|
|
||||||
-spec process_muc_unique(iq()) -> iq().
|
-spec process_muc_unique(iq()) -> iq().
|
||||||
process_muc_unique(#iq{type = set, lang = Lang} = IQ) ->
|
process_muc_unique(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
process_muc_unique(#iq{from = From, type = get} = IQ) ->
|
process_muc_unique(#iq{from = From, type = get,
|
||||||
|
sub_els = [#muc_unique{}]} = IQ) ->
|
||||||
Name = p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(),
|
Name = p1_sha:sha(term_to_binary([From, p1_time_compat:timestamp(),
|
||||||
randoms:get_string()])),
|
randoms:get_string()])),
|
||||||
xmpp:make_iq_result(IQ, #muc_unique{name = Name}).
|
xmpp:make_iq_result(IQ, #muc_unique{name = Name}).
|
||||||
|
@ -496,19 +508,22 @@ process_muc_unique(#iq{from = From, type = get} = IQ) ->
|
||||||
process_mucsub(#iq{type = set, lang = Lang} = IQ) ->
|
process_mucsub(#iq{type = set, lang = Lang} = IQ) ->
|
||||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
|
||||||
process_mucsub(#iq{type = get, from = From, to = To} = IQ) ->
|
process_mucsub(#iq{type = get, from = From, to = To,
|
||||||
|
sub_els = [#muc_subscriptions{}]} = IQ) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
ServerHost = ejabberd_router:host_of_route(Host),
|
ServerHost = ejabberd_router:host_of_route(Host),
|
||||||
RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
|
RoomJIDs = get_subscribed_rooms(ServerHost, Host, From),
|
||||||
xmpp:make_iq_result(IQ, #muc_subscriptions{list = RoomJIDs}).
|
xmpp:make_iq_result(IQ, #muc_subscriptions{list = RoomJIDs});
|
||||||
|
process_mucsub(#iq{lang = Lang} = IQ) ->
|
||||||
|
Txt = <<"No module is handling this query">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||||
|
|
||||||
-spec is_create_request(stanza()) -> boolean().
|
-spec is_create_request(stanza()) -> boolean().
|
||||||
is_create_request(#presence{type = available}) ->
|
is_create_request(#presence{type = available}) ->
|
||||||
true;
|
true;
|
||||||
is_create_request(#iq{type = set} = IQ) ->
|
is_create_request(#iq{type = T} = IQ) when T == get; T == set ->
|
||||||
xmpp:has_subtag(IQ, #muc_subscribe{});
|
xmpp:has_subtag(IQ, #muc_subscribe{}) orelse
|
||||||
is_create_request(#iq{type = get} = IQ) ->
|
xmpp:has_subtag(IQ, #muc_owner{});
|
||||||
#muc_owner{} == xmpp:get_subtag(IQ, #muc_owner{});
|
|
||||||
is_create_request(_) ->
|
is_create_request(_) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
|
@ -645,13 +660,12 @@ get_vh_rooms(_, _) ->
|
||||||
%% index = NewIndex}}
|
%% index = NewIndex}}
|
||||||
%% end.
|
%% end.
|
||||||
|
|
||||||
get_subscribed_rooms(ServerHost, Host, From) ->
|
get_subscribed_rooms(_ServerHost, Host, From) ->
|
||||||
Rooms = get_rooms(ServerHost, Host),
|
Rooms = get_vh_rooms(Host),
|
||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun(#muc_room{name_host = {Name, _}, opts = Opts}) ->
|
fun(#muc_online_room{name_host = {Name, _}, pid = Pid}) ->
|
||||||
Subscribers = proplists:get_value(subscribers, Opts, []),
|
case gen_fsm:sync_send_all_state_event(Pid, {is_subscriber, From}) of
|
||||||
case lists:keymember(From, 1, Subscribers) of
|
true -> [jid:make(Name, Host)];
|
||||||
true -> [jid:make(Name, Host, <<>>)];
|
|
||||||
false -> []
|
false -> []
|
||||||
end;
|
end;
|
||||||
(_) ->
|
(_) ->
|
||||||
|
|
|
@ -76,11 +76,6 @@
|
||||||
-type fsm_stop() :: {stop, normal, state()}.
|
-type fsm_stop() :: {stop, normal, state()}.
|
||||||
-type fsm_next() :: {next_state, normal_state, state()}.
|
-type fsm_next() :: {next_state, normal_state, state()}.
|
||||||
-type fsm_transition() :: fsm_stop() | fsm_next().
|
-type fsm_transition() :: fsm_stop() | fsm_next().
|
||||||
-type history_element() :: {binary(), %% nick
|
|
||||||
message(), %% message itself
|
|
||||||
boolean(), %% have subject
|
|
||||||
erlang:timestamp(),
|
|
||||||
non_neg_integer()}.
|
|
||||||
|
|
||||||
-export_type([state/0]).
|
-export_type([state/0]).
|
||||||
|
|
||||||
|
@ -252,7 +247,8 @@ normal_state({route, From, <<"">>,
|
||||||
ejabberd_router:route_error(StateData#state.jid, From, Packet, Err),
|
ejabberd_router:route_error(StateData#state.jid, From, Packet, Err),
|
||||||
{next_state, normal_state, StateData};
|
{next_state, normal_state, StateData};
|
||||||
false when Type /= error ->
|
false when Type /= error ->
|
||||||
handle_roommessage_from_nonparticipant(Packet, StateData, From);
|
handle_roommessage_from_nonparticipant(Packet, StateData, From),
|
||||||
|
{next_state, normal_state, StateData};
|
||||||
false ->
|
false ->
|
||||||
{next_state, normal_state, StateData}
|
{next_state, normal_state, StateData}
|
||||||
end;
|
end;
|
||||||
|
@ -279,9 +275,6 @@ normal_state({route, From, <<"">>,
|
||||||
process_iq_owner(From, IQ, StateData);
|
process_iq_owner(From, IQ, StateData);
|
||||||
?NS_DISCO_INFO when SubEl#disco_info.node == <<>> ->
|
?NS_DISCO_INFO when SubEl#disco_info.node == <<>> ->
|
||||||
process_iq_disco_info(From, IQ, StateData);
|
process_iq_disco_info(From, IQ, StateData);
|
||||||
?NS_DISCO_INFO ->
|
|
||||||
Txt = <<"Disco info is not available for this node">>,
|
|
||||||
{error, xmpp:err_service_unavailable(Txt, Lang)};
|
|
||||||
?NS_DISCO_ITEMS ->
|
?NS_DISCO_ITEMS ->
|
||||||
process_iq_disco_items(From, IQ, StateData);
|
process_iq_disco_items(From, IQ, StateData);
|
||||||
?NS_VCARD ->
|
?NS_VCARD ->
|
||||||
|
@ -291,7 +284,9 @@ normal_state({route, From, <<"">>,
|
||||||
?NS_CAPTCHA ->
|
?NS_CAPTCHA ->
|
||||||
process_iq_captcha(From, IQ, StateData);
|
process_iq_captcha(From, IQ, StateData);
|
||||||
_ ->
|
_ ->
|
||||||
{error, xmpp:err_feature_not_implemented()}
|
Txt = <<"The feature requested is not "
|
||||||
|
"supported by the conference">>,
|
||||||
|
{error, xmpp:err_service_unavailable(Txt, Lang)}
|
||||||
end,
|
end,
|
||||||
{IQRes, NewStateData} =
|
{IQRes, NewStateData} =
|
||||||
case Res1 of
|
case Res1 of
|
||||||
|
@ -312,8 +307,12 @@ normal_state({route, From, <<"">>,
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
case NewStateData of
|
case NewStateData of
|
||||||
stop -> {stop, normal, StateData};
|
stop ->
|
||||||
_ -> {next_state, normal_state, NewStateData}
|
{stop, normal, StateData};
|
||||||
|
_ when NewStateData#state.just_created ->
|
||||||
|
close_room_if_temporary_and_empty(NewStateData);
|
||||||
|
_ ->
|
||||||
|
{next_state, normal_state, NewStateData}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
catch _:{xmpp_codec, Why} ->
|
catch _:{xmpp_codec, Why} ->
|
||||||
|
@ -324,7 +323,10 @@ normal_state({route, From, <<"">>,
|
||||||
normal_state({route, From, <<"">>, #iq{} = IQ}, StateData) ->
|
normal_state({route, From, <<"">>, #iq{} = IQ}, StateData) ->
|
||||||
Err = xmpp:err_bad_request(),
|
Err = xmpp:err_bad_request(),
|
||||||
ejabberd_router:route_error(StateData#state.jid, From, IQ, Err),
|
ejabberd_router:route_error(StateData#state.jid, From, IQ, Err),
|
||||||
{next_state, normal_state, StateData};
|
case StateData#state.just_created of
|
||||||
|
true -> {stop, normal, StateData};
|
||||||
|
false -> {next_state, normal_state, StateData}
|
||||||
|
end;
|
||||||
normal_state({route, From, Nick, #presence{} = Packet}, StateData) ->
|
normal_state({route, From, Nick, #presence{} = Packet}, StateData) ->
|
||||||
Activity = get_user_activity(From, StateData),
|
Activity = get_user_activity(From, StateData),
|
||||||
Now = p1_time_compat:system_time(micro_seconds),
|
Now = p1_time_compat:system_time(micro_seconds),
|
||||||
|
@ -530,8 +532,14 @@ handle_sync_event({change_state, NewStateData}, _From,
|
||||||
StateName, _StateData) ->
|
StateName, _StateData) ->
|
||||||
{reply, {ok, NewStateData}, StateName, NewStateData};
|
{reply, {ok, NewStateData}, StateName, NewStateData};
|
||||||
handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData) ->
|
handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData) ->
|
||||||
NSD = process_item_change(Item, StateData, UJID),
|
case process_item_change(Item, StateData, UJID) of
|
||||||
{reply, {ok, NSD}, StateName, NSD};
|
{error, _} = Err ->
|
||||||
|
{reply, Err, StateName, StateData};
|
||||||
|
NSD ->
|
||||||
|
{reply, {ok, NSD}, StateName, NSD}
|
||||||
|
end;
|
||||||
|
handle_sync_event({is_subscriber, From}, _From, StateName, StateData) ->
|
||||||
|
{reply, is_subscriber(From, StateData), StateName, StateData};
|
||||||
handle_sync_event(_Event, _From, StateName,
|
handle_sync_event(_Event, _From, StateName,
|
||||||
StateData) ->
|
StateData) ->
|
||||||
Reply = ok, {reply, Reply, StateName, StateData}.
|
Reply = ok, {reply, Reply, StateName, StateData}.
|
||||||
|
@ -763,7 +771,7 @@ process_normal_message(From, #message{lang = Lang} = Pkt, StateData) ->
|
||||||
(#muc_user{invites = [I]}, _) ->
|
(#muc_user{invites = [I]}, _) ->
|
||||||
{ok, I};
|
{ok, I};
|
||||||
(#muc_user{invites = [_|_]}, _) ->
|
(#muc_user{invites = [_|_]}, _) ->
|
||||||
Txt = <<"Multiple <invite/> elements are not allowed">>,
|
Txt = <<"Multiple invitations are not allowed">>,
|
||||||
{error, xmpp:err_resource_constraint(Txt, Lang)};
|
{error, xmpp:err_resource_constraint(Txt, Lang)};
|
||||||
(#xdata{type = submit, fields = Fs}, _) ->
|
(#xdata{type = submit, fields = Fs}, _) ->
|
||||||
try {ok, muc_request:decode(Fs)}
|
try {ok, muc_request:decode(Fs)}
|
||||||
|
@ -835,7 +843,7 @@ process_voice_request(From, Pkt, StateData) ->
|
||||||
{ok, _, _} ->
|
{ok, _, _} ->
|
||||||
ErrText = <<"Please, wait for a while before sending "
|
ErrText = <<"Please, wait for a while before sending "
|
||||||
"new voice request">>,
|
"new voice request">>,
|
||||||
Err = xmpp:err_not_acceptable(ErrText, Lang),
|
Err = xmpp:err_resource_constraint(ErrText, Lang),
|
||||||
ejabberd_router:route_error(
|
ejabberd_router:route_error(
|
||||||
StateData#state.jid, From, Pkt, Err),
|
StateData#state.jid, From, Pkt, Err),
|
||||||
StateData#state{last_voice_request_time = Times}
|
StateData#state{last_voice_request_time = Times}
|
||||||
|
@ -1147,10 +1155,9 @@ decide_fate_message(#message{type = error} = Msg,
|
||||||
PD = case check_error_kick(Err) of
|
PD = case check_error_kick(Err) of
|
||||||
%% If this is an error stanza and its condition matches a criteria
|
%% If this is an error stanza and its condition matches a criteria
|
||||||
true ->
|
true ->
|
||||||
Reason =
|
Reason = str:format("This participant is considered a ghost "
|
||||||
io_lib:format("This participant is considered a ghost "
|
"and is expulsed: ~s",
|
||||||
"and is expulsed: ~s",
|
[jid:to_string(From)]),
|
||||||
[jid:to_string(From)]),
|
|
||||||
{expulse_sender, Reason};
|
{expulse_sender, Reason};
|
||||||
false -> continue_delivery
|
false -> continue_delivery
|
||||||
end,
|
end,
|
||||||
|
@ -1198,7 +1205,7 @@ get_error_condition(undefined) ->
|
||||||
make_reason(Packet, From, StateData, Reason1) ->
|
make_reason(Packet, From, StateData, Reason1) ->
|
||||||
{ok, #user{nick = FromNick}} = (?DICT):find(jid:tolower(From), StateData#state.users),
|
{ok, #user{nick = FromNick}} = (?DICT):find(jid:tolower(From), StateData#state.users),
|
||||||
Condition = get_error_condition(xmpp:get_error(Packet)),
|
Condition = get_error_condition(xmpp:get_error(Packet)),
|
||||||
iolist_to_binary(io_lib:format(Reason1, [FromNick, Condition])).
|
str:format(Reason1, [FromNick, Condition]).
|
||||||
|
|
||||||
-spec expulse_participant(stanza(), jid(), state(), binary()) ->
|
-spec expulse_participant(stanza(), jid(), state(), binary()) ->
|
||||||
state().
|
state().
|
||||||
|
@ -1816,11 +1823,9 @@ add_new_user(From, Nick, Packet, StateData) ->
|
||||||
Nodes, StateData)),
|
Nodes, StateData)),
|
||||||
send_existing_presences(From, NewState),
|
send_existing_presences(From, NewState),
|
||||||
send_initial_presence(From, NewState, StateData),
|
send_initial_presence(From, NewState, StateData),
|
||||||
Shift = count_stanza_shift(Nick, Packet, NewState),
|
History = get_history(Nick, Packet, NewState),
|
||||||
case send_history(From, Shift, NewState) of
|
send_history(From, History, NewState),
|
||||||
true -> ok;
|
send_subject(From, StateData),
|
||||||
_ -> send_subject(From, StateData)
|
|
||||||
end,
|
|
||||||
NewState;
|
NewState;
|
||||||
true ->
|
true ->
|
||||||
add_online_user(From, Nick, none,
|
add_online_user(From, Nick, none,
|
||||||
|
@ -1963,84 +1968,43 @@ extract_password(Packet) ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec count_stanza_shift(binary(), stanza(), state()) -> non_neg_integer().
|
-spec get_history(binary(), stanza(), state()) -> lqueue().
|
||||||
count_stanza_shift(Nick, Packet, StateData) ->
|
get_history(Nick, Packet, #state{history = History}) ->
|
||||||
case xmpp:get_subtag(Packet, #muc_history{}) of
|
case xmpp:get_subtag(Packet, #muc{}) of
|
||||||
#muc_history{since = Since,
|
#muc{history = #muc_history{} = MUCHistory} ->
|
||||||
seconds = Seconds,
|
Now = p1_time_compat:timestamp(),
|
||||||
maxstanzas = MaxStanzas,
|
Q = History#lqueue.queue,
|
||||||
maxchars = MaxChars} ->
|
{NewQ, Len} = filter_history(Q, MUCHistory, Now, Nick, queue:new(), 0, 0),
|
||||||
HL = lqueue_to_list(StateData#state.history),
|
History#lqueue{queue = NewQ, len = Len};
|
||||||
Shift0 = case Since of
|
_ ->
|
||||||
undefined -> 0;
|
History
|
||||||
_ ->
|
|
||||||
Sin = calendar:datetime_to_gregorian_seconds(
|
|
||||||
calendar:now_to_datetime(Since)),
|
|
||||||
count_seconds_shift(Sin, HL)
|
|
||||||
end,
|
|
||||||
Shift1 = case Seconds of
|
|
||||||
undefined -> 0;
|
|
||||||
_ ->
|
|
||||||
Sec = calendar:datetime_to_gregorian_seconds(
|
|
||||||
calendar:universal_time()) - Seconds,
|
|
||||||
count_seconds_shift(Sec, HL)
|
|
||||||
end,
|
|
||||||
Shift2 = case MaxStanzas of
|
|
||||||
undefined -> 0;
|
|
||||||
_ -> count_maxstanzas_shift(MaxStanzas, HL)
|
|
||||||
end,
|
|
||||||
Shift3 = case MaxChars of
|
|
||||||
undefined -> 0;
|
|
||||||
_ -> count_maxchars_shift(Nick, MaxChars, HL)
|
|
||||||
end,
|
|
||||||
lists:max([Shift0, Shift1, Shift2, Shift3]);
|
|
||||||
false ->
|
|
||||||
0
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec count_seconds_shift(non_neg_integer(),
|
-spec filter_history(?TQUEUE, muc_history(), erlang:timestamp(), binary(),
|
||||||
[history_element()]) -> non_neg_integer().
|
?TQUEUE, non_neg_integer(), non_neg_integer()) ->
|
||||||
count_seconds_shift(Seconds, HistoryList) ->
|
{?TQUEUE, non_neg_integer()}.
|
||||||
lists:sum(lists:map(fun ({_Nick, _Packet, _HaveSubject,
|
filter_history(Queue, #muc_history{since = Since,
|
||||||
TimeStamp, _Size}) ->
|
seconds = Seconds,
|
||||||
T =
|
maxstanzas = MaxStanzas,
|
||||||
calendar:datetime_to_gregorian_seconds(TimeStamp),
|
maxchars = MaxChars} = MUC,
|
||||||
if T < Seconds -> 1;
|
Now, Nick, AccQueue, NumStanzas, NumChars) ->
|
||||||
true -> 0
|
case queue:out_r(Queue) of
|
||||||
end
|
{{value, {_, _, _, TimeStamp, Size} = Elem}, NewQueue} ->
|
||||||
end,
|
NowDiff = timer:now_diff(Now, TimeStamp) div 1000000,
|
||||||
HistoryList)).
|
Chars = Size + byte_size(Nick) + 1,
|
||||||
|
if (NumStanzas < MaxStanzas) andalso
|
||||||
-spec count_maxstanzas_shift(non_neg_integer(),
|
(TimeStamp > Since) andalso
|
||||||
[history_element()]) -> non_neg_integer().
|
(NowDiff =< Seconds) andalso
|
||||||
count_maxstanzas_shift(MaxStanzas, HistoryList) ->
|
(NumChars + Chars =< MaxChars) ->
|
||||||
S = length(HistoryList) - MaxStanzas,
|
filter_history(NewQueue, MUC, Now, Nick,
|
||||||
if S =< 0 -> 0;
|
queue:in_r(Elem, AccQueue),
|
||||||
true -> S
|
NumStanzas + 1,
|
||||||
end.
|
NumChars + Chars);
|
||||||
|
true ->
|
||||||
-spec count_maxchars_shift(binary(), non_neg_integer(),
|
{AccQueue, NumStanzas}
|
||||||
[history_element()]) -> integer().
|
end;
|
||||||
count_maxchars_shift(Nick, MaxSize, HistoryList) ->
|
{empty, _} ->
|
||||||
NLen = byte_size(Nick) + 1,
|
{AccQueue, NumStanzas}
|
||||||
Sizes = lists:map(fun ({_Nick, _Packet, _HaveSubject,
|
|
||||||
_TimeStamp, Size}) ->
|
|
||||||
Size + NLen
|
|
||||||
end,
|
|
||||||
HistoryList),
|
|
||||||
calc_shift(MaxSize, Sizes).
|
|
||||||
|
|
||||||
-spec calc_shift(non_neg_integer(), [non_neg_integer()]) -> integer().
|
|
||||||
calc_shift(MaxSize, Sizes) ->
|
|
||||||
Total = lists:sum(Sizes),
|
|
||||||
calc_shift(MaxSize, Total, 0, Sizes).
|
|
||||||
|
|
||||||
-spec calc_shift(non_neg_integer(), integer(), integer(),
|
|
||||||
[non_neg_integer()]) -> integer().
|
|
||||||
calc_shift(_MaxSize, _Size, Shift, []) -> Shift;
|
|
||||||
calc_shift(MaxSize, Size, Shift, [S | TSizes]) ->
|
|
||||||
if MaxSize >= Size -> Shift;
|
|
||||||
true -> calc_shift(MaxSize, Size - S, Shift + 1, TSizes)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec is_room_overcrowded(state()) -> boolean().
|
-spec is_room_overcrowded(state()) -> boolean().
|
||||||
|
@ -2166,19 +2130,20 @@ send_new_presence1(NJID, Reason, IsInitialPresence, StateData, OldStateData) ->
|
||||||
end,
|
end,
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({LUJID, Info}) ->
|
fun({LUJID, Info}) ->
|
||||||
{Role, Presence} = if LNJID == LUJID -> {Role0, Presence0};
|
IsSelfPresence = LNJID == LUJID,
|
||||||
|
{Role, Presence} = if IsSelfPresence -> {Role0, Presence0};
|
||||||
true -> {Role1, Presence1}
|
true -> {Role1, Presence1}
|
||||||
end,
|
end,
|
||||||
Item0 = #muc_item{affiliation = Affiliation,
|
Item0 = #muc_item{affiliation = Affiliation,
|
||||||
role = Role},
|
role = Role},
|
||||||
Item1 = case Info#user.role == moderator orelse
|
Item1 = case Info#user.role == moderator orelse
|
||||||
(StateData#state.config)#config.anonymous
|
(StateData#state.config)#config.anonymous
|
||||||
== false of
|
== false orelse IsSelfPresence of
|
||||||
true -> Item0#muc_item{jid = RealJID};
|
true -> Item0#muc_item{jid = RealJID};
|
||||||
false -> Item0
|
false -> Item0
|
||||||
end,
|
end,
|
||||||
Item = Item1#muc_item{reason = Reason},
|
Item = Item1#muc_item{reason = Reason},
|
||||||
StatusCodes = status_codes(IsInitialPresence, NJID, Info,
|
StatusCodes = status_codes(IsInitialPresence, IsSelfPresence,
|
||||||
StateData),
|
StateData),
|
||||||
Pres = if Presence == undefined -> #presence{};
|
Pres = if Presence == undefined -> #presence{};
|
||||||
true -> Presence
|
true -> Presence
|
||||||
|
@ -2303,30 +2268,26 @@ send_nick_changing(JID, OldNick, StateData,
|
||||||
StateData#state.users),
|
StateData#state.users),
|
||||||
Affiliation = get_affiliation(JID, StateData),
|
Affiliation = get_affiliation(JID, StateData),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({_LJID, Info}) when Presence /= undefined ->
|
fun({LJID, Info}) when Presence /= undefined ->
|
||||||
|
IsSelfPresence = LJID == jid:tolower(JID),
|
||||||
Item0 = #muc_item{affiliation = Affiliation, role = Role},
|
Item0 = #muc_item{affiliation = Affiliation, role = Role},
|
||||||
Item1 = case Info#user.role == moderator orelse
|
Item = case Info#user.role == moderator orelse
|
||||||
(StateData#state.config)#config.anonymous
|
(StateData#state.config)#config.anonymous
|
||||||
== false of
|
== false orelse IsSelfPresence of
|
||||||
true -> Item0#muc_item{jid = RealJID, nick = Nick};
|
true -> Item0#muc_item{jid = RealJID};
|
||||||
false -> Item0#muc_item{nick = Nick}
|
false -> Item0
|
||||||
end,
|
end,
|
||||||
Item2 = case Info#user.role == moderator orelse
|
Status110 = case IsSelfPresence of
|
||||||
(StateData#state.config)#config.anonymous
|
|
||||||
== false of
|
|
||||||
true -> Item0#muc_item{jid = RealJID};
|
|
||||||
false -> Item0
|
|
||||||
end,
|
|
||||||
Status110 = case JID == Info#user.jid of
|
|
||||||
true -> [110];
|
true -> [110];
|
||||||
false -> []
|
false -> []
|
||||||
end,
|
end,
|
||||||
Packet1 = #presence{type = unavailable,
|
Packet1 = #presence{
|
||||||
sub_els = [#muc_user{
|
type = unavailable,
|
||||||
items = [Item1],
|
sub_els = [#muc_user{
|
||||||
status_codes = [303|Status110]}]},
|
items = [Item#muc_item{nick = Nick}],
|
||||||
|
status_codes = [303|Status110]}]},
|
||||||
Packet2 = xmpp:set_subtag(Presence,
|
Packet2 = xmpp:set_subtag(Presence,
|
||||||
#muc_user{items = [Item2],
|
#muc_user{items = [Item],
|
||||||
status_codes = Status110}),
|
status_codes = Status110}),
|
||||||
if SendOldUnavailable ->
|
if SendOldUnavailable ->
|
||||||
send_wrapped(
|
send_wrapped(
|
||||||
|
@ -2364,12 +2325,12 @@ maybe_send_affiliation(JID, Affiliation, StateData) ->
|
||||||
true ->
|
true ->
|
||||||
ok; % The new affiliation is published via presence.
|
ok; % The new affiliation is published via presence.
|
||||||
false ->
|
false ->
|
||||||
send_affiliation(LJID, Affiliation, StateData)
|
send_affiliation(JID, Affiliation, StateData)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec send_affiliation(ljid(), affiliation(), state()) -> ok.
|
-spec send_affiliation(jid(), affiliation(), state()) -> ok.
|
||||||
send_affiliation(LJID, Affiliation, StateData) ->
|
send_affiliation(JID, Affiliation, StateData) ->
|
||||||
Item = #muc_item{jid = jid:make(LJID),
|
Item = #muc_item{jid = JID,
|
||||||
affiliation = Affiliation,
|
affiliation = Affiliation,
|
||||||
role = none},
|
role = none},
|
||||||
Message = #message{id = randoms:get_string(),
|
Message = #message{id = randoms:get_string(),
|
||||||
|
@ -2388,8 +2349,8 @@ send_affiliation(LJID, Affiliation, StateData) ->
|
||||||
StateData#state.server_host,
|
StateData#state.server_host,
|
||||||
Recipients, Message).
|
Recipients, Message).
|
||||||
|
|
||||||
-spec status_codes(boolean(), jid(), #user{}, state()) -> [pos_integer()].
|
-spec status_codes(boolean(), boolean(), state()) -> [pos_integer()].
|
||||||
status_codes(IsInitialPresence, JID, #user{jid = JID}, StateData) ->
|
status_codes(IsInitialPresence, _IsSelfPresence = true, StateData) ->
|
||||||
S0 = [110],
|
S0 = [110],
|
||||||
case IsInitialPresence of
|
case IsInitialPresence of
|
||||||
true ->
|
true ->
|
||||||
|
@ -2408,7 +2369,7 @@ status_codes(IsInitialPresence, JID, #user{jid = JID}, StateData) ->
|
||||||
S3;
|
S3;
|
||||||
false -> S0
|
false -> S0
|
||||||
end;
|
end;
|
||||||
status_codes(_IsInitialPresence, _JID, _Info, _StateData) -> [].
|
status_codes(_IsInitialPresence, _IsSelfPresence = false, _StateData) -> [].
|
||||||
|
|
||||||
-spec lqueue_new(non_neg_integer()) -> lqueue().
|
-spec lqueue_new(non_neg_integer()) -> lqueue().
|
||||||
lqueue_new(Max) ->
|
lqueue_new(Max) ->
|
||||||
|
@ -2438,54 +2399,58 @@ lqueue_to_list(#lqueue{queue = Q1}) ->
|
||||||
|
|
||||||
-spec add_message_to_history(binary(), jid(), message(), state()) -> state().
|
-spec add_message_to_history(binary(), jid(), message(), state()) -> state().
|
||||||
add_message_to_history(FromNick, FromJID, Packet, StateData) ->
|
add_message_to_history(FromNick, FromJID, Packet, StateData) ->
|
||||||
HaveSubject = Packet#message.subject /= [],
|
|
||||||
TimeStamp = p1_time_compat:timestamp(),
|
|
||||||
AddrPacket = case (StateData#state.config)#config.anonymous of
|
|
||||||
true -> Packet;
|
|
||||||
false ->
|
|
||||||
Addresses = #addresses{
|
|
||||||
list = [#address{type = ofrom,
|
|
||||||
jid = FromJID}]},
|
|
||||||
xmpp:set_subtag(Packet, Addresses)
|
|
||||||
end,
|
|
||||||
TSPacket = xmpp_util:add_delay_info(
|
|
||||||
AddrPacket, StateData#state.jid, TimeStamp),
|
|
||||||
SPacket = xmpp:set_from_to(
|
|
||||||
TSPacket,
|
|
||||||
jid:replace_resource(StateData#state.jid, FromNick),
|
|
||||||
StateData#state.jid),
|
|
||||||
Size = element_size(SPacket),
|
|
||||||
Q1 = lqueue_in({FromNick, TSPacket, HaveSubject,
|
|
||||||
calendar:now_to_universal_time(TimeStamp), Size},
|
|
||||||
StateData#state.history),
|
|
||||||
add_to_log(text, {FromNick, Packet}, StateData),
|
add_to_log(text, {FromNick, Packet}, StateData),
|
||||||
StateData#state{history = Q1}.
|
case check_subject(Packet) of
|
||||||
|
false ->
|
||||||
|
TimeStamp = p1_time_compat:timestamp(),
|
||||||
|
AddrPacket = case (StateData#state.config)#config.anonymous of
|
||||||
|
true -> Packet;
|
||||||
|
false ->
|
||||||
|
Addresses = #addresses{
|
||||||
|
list = [#address{type = ofrom,
|
||||||
|
jid = FromJID}]},
|
||||||
|
xmpp:set_subtag(Packet, Addresses)
|
||||||
|
end,
|
||||||
|
TSPacket = xmpp_util:add_delay_info(
|
||||||
|
AddrPacket, StateData#state.jid, TimeStamp),
|
||||||
|
SPacket = xmpp:set_from_to(
|
||||||
|
TSPacket,
|
||||||
|
jid:replace_resource(StateData#state.jid, FromNick),
|
||||||
|
StateData#state.jid),
|
||||||
|
Size = element_size(SPacket),
|
||||||
|
Q1 = lqueue_in({FromNick, TSPacket, false,
|
||||||
|
TimeStamp, Size},
|
||||||
|
StateData#state.history),
|
||||||
|
StateData#state{history = Q1};
|
||||||
|
_ ->
|
||||||
|
StateData
|
||||||
|
end.
|
||||||
|
|
||||||
-spec send_history(jid(), integer(), state()) -> boolean().
|
-spec send_history(jid(), lqueue(), state()) -> boolean().
|
||||||
send_history(JID, Shift, StateData) ->
|
send_history(JID, History, StateData) ->
|
||||||
lists:foldl(fun ({Nick, Packet, HaveSubject, _TimeStamp,
|
lists:foreach(
|
||||||
_Size},
|
fun({Nick, Packet, _HaveSubject, _TimeStamp, _Size}) ->
|
||||||
B) ->
|
ejabberd_router:route(
|
||||||
ejabberd_router:route(jid:replace_resource(StateData#state.jid,
|
jid:replace_resource(StateData#state.jid, Nick),
|
||||||
Nick),
|
JID, Packet)
|
||||||
JID, Packet),
|
end, lqueue_to_list(History)).
|
||||||
B or HaveSubject
|
|
||||||
end,
|
|
||||||
false,
|
|
||||||
lists:nthtail(Shift,
|
|
||||||
lqueue_to_list(StateData#state.history))).
|
|
||||||
|
|
||||||
-spec send_subject(jid(), state()) -> ok.
|
-spec send_subject(jid(), state()) -> ok.
|
||||||
send_subject(_JID, #state{subject_author = <<"">>}) -> ok;
|
|
||||||
send_subject(JID, #state{subject_author = Nick} = StateData) ->
|
send_subject(JID, #state{subject_author = Nick} = StateData) ->
|
||||||
Subject = StateData#state.subject,
|
Subject = case StateData#state.subject of
|
||||||
Packet = #message{type = groupchat, subject = xmpp:mk_text(Subject)},
|
<<"">> -> [#text{}];
|
||||||
|
Subj -> xmpp:mk_text(Subj)
|
||||||
|
end,
|
||||||
|
Packet = #message{type = groupchat, subject = Subject},
|
||||||
ejabberd_router:route(jid:replace_resource(StateData#state.jid, Nick), JID,
|
ejabberd_router:route(jid:replace_resource(StateData#state.jid, Nick), JID,
|
||||||
Packet).
|
Packet).
|
||||||
|
|
||||||
-spec check_subject(message()) -> false | binary().
|
-spec check_subject(message()) -> false | binary().
|
||||||
check_subject(#message{subject = []}) -> false;
|
check_subject(#message{subject = [_|_] = Subj, body = [],
|
||||||
check_subject(#message{subject = Subj}) -> xmpp:get_text(Subj).
|
thread = undefined}) ->
|
||||||
|
xmpp:get_text(Subj);
|
||||||
|
check_subject(_) ->
|
||||||
|
false.
|
||||||
|
|
||||||
-spec can_change_subject(role(), state()) -> boolean().
|
-spec can_change_subject(role(), state()) -> boolean().
|
||||||
can_change_subject(Role, StateData) ->
|
can_change_subject(Role, StateData) ->
|
||||||
|
@ -2552,10 +2517,10 @@ items_with_role(SRole, StateData) ->
|
||||||
items_with_affiliation(SAffiliation, StateData) ->
|
items_with_affiliation(SAffiliation, StateData) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({JID, {Affiliation, Reason}}) ->
|
fun({JID, {Affiliation, Reason}}) ->
|
||||||
#muc_item{affiliation = Affiliation, jid = JID,
|
#muc_item{affiliation = Affiliation, jid = jid:make(JID),
|
||||||
reason = Reason};
|
reason = Reason};
|
||||||
({JID, Affiliation}) ->
|
({JID, Affiliation}) ->
|
||||||
#muc_item{affiliation = Affiliation, jid = JID}
|
#muc_item{affiliation = Affiliation, jid = jid:make(JID)}
|
||||||
end,
|
end,
|
||||||
search_affiliation(SAffiliation, StateData)).
|
search_affiliation(SAffiliation, StateData)).
|
||||||
|
|
||||||
|
@ -2600,23 +2565,29 @@ process_admin_items_set(UJID, Items, Lang, StateData) ->
|
||||||
"room ~s:~n ~p",
|
"room ~s:~n ~p",
|
||||||
[jid:to_string(UJID),
|
[jid:to_string(UJID),
|
||||||
jid:to_string(StateData#state.jid), Res]),
|
jid:to_string(StateData#state.jid), Res]),
|
||||||
NSD = lists:foldl(process_item_change(UJID),
|
case lists:foldl(process_item_change(UJID),
|
||||||
StateData, lists:flatten(Res)),
|
StateData, lists:flatten(Res)) of
|
||||||
store_room(NSD),
|
{error, _} = Err ->
|
||||||
{result, undefined, NSD};
|
Err;
|
||||||
{error, Err} -> {error, Err}
|
NSD ->
|
||||||
|
store_room(NSD),
|
||||||
|
{result, undefined, NSD}
|
||||||
|
end;
|
||||||
|
{error, Err} -> {error, Err}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_item_change(jid()) -> function().
|
-spec process_item_change(jid()) -> function().
|
||||||
process_item_change(UJID) ->
|
process_item_change(UJID) ->
|
||||||
fun(E, SD) ->
|
fun(_, {error, _} = Err) ->
|
||||||
process_item_change(E, SD, UJID)
|
Err;
|
||||||
|
(Item, SD) ->
|
||||||
|
process_item_change(Item, SD, UJID)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-type admin_action() :: {jid(), affiliation | role,
|
-type admin_action() :: {jid(), affiliation | role,
|
||||||
affiliation() | role(), binary()}.
|
affiliation() | role(), binary()}.
|
||||||
|
|
||||||
-spec process_item_change(admin_action(), state(), jid()) -> state().
|
-spec process_item_change(admin_action(), state(), jid()) -> state() | {error, stanza_error()}.
|
||||||
process_item_change(Item, SD, UJID) ->
|
process_item_change(Item, SD, UJID) ->
|
||||||
try case Item of
|
try case Item of
|
||||||
{JID, affiliation, owner, _} when JID#jid.luser == <<"">> ->
|
{JID, affiliation, owner, _} when JID#jid.luser == <<"">> ->
|
||||||
|
@ -2624,23 +2595,23 @@ process_item_change(Item, SD, UJID) ->
|
||||||
%% forget the affiliation completely
|
%% forget the affiliation completely
|
||||||
SD;
|
SD;
|
||||||
{JID, role, none, Reason} ->
|
{JID, role, none, Reason} ->
|
||||||
catch send_kickban_presence(UJID, JID, Reason, 307, SD),
|
send_kickban_presence(UJID, JID, Reason, 307, SD),
|
||||||
set_role(JID, none, SD);
|
set_role(JID, none, SD);
|
||||||
{JID, affiliation, none, Reason} ->
|
{JID, affiliation, none, Reason} ->
|
||||||
case (SD#state.config)#config.members_only of
|
case (SD#state.config)#config.members_only of
|
||||||
true ->
|
true ->
|
||||||
catch send_kickban_presence(UJID, JID, Reason, 321, none, SD),
|
send_kickban_presence(UJID, JID, Reason, 321, none, SD),
|
||||||
maybe_send_affiliation(JID, none, SD),
|
maybe_send_affiliation(JID, none, SD),
|
||||||
SD1 = set_affiliation(JID, none, SD),
|
SD1 = set_affiliation(JID, none, SD),
|
||||||
set_role(JID, none, SD1);
|
set_role(JID, none, SD1);
|
||||||
_ ->
|
_ ->
|
||||||
SD1 = set_affiliation(JID, none, SD),
|
SD1 = set_affiliation(JID, none, SD),
|
||||||
send_update_presence(JID, SD1, SD),
|
send_update_presence(JID, Reason, SD1, SD),
|
||||||
maybe_send_affiliation(JID, none, SD1),
|
maybe_send_affiliation(JID, none, SD1),
|
||||||
SD1
|
SD1
|
||||||
end;
|
end;
|
||||||
{JID, affiliation, outcast, Reason} ->
|
{JID, affiliation, outcast, Reason} ->
|
||||||
catch send_kickban_presence(UJID, JID, Reason, 301, outcast, SD),
|
send_kickban_presence(UJID, JID, Reason, 301, outcast, SD),
|
||||||
maybe_send_affiliation(JID, outcast, SD),
|
maybe_send_affiliation(JID, outcast, SD),
|
||||||
set_affiliation(JID, outcast, set_role(JID, none, SD), Reason);
|
set_affiliation(JID, outcast, set_role(JID, none, SD), Reason);
|
||||||
{JID, affiliation, A, Reason} when (A == admin) or (A == owner) ->
|
{JID, affiliation, A, Reason} when (A == admin) or (A == owner) ->
|
||||||
|
@ -2657,7 +2628,7 @@ process_item_change(Item, SD, UJID) ->
|
||||||
SD2;
|
SD2;
|
||||||
{JID, role, Role, Reason} ->
|
{JID, role, Role, Reason} ->
|
||||||
SD1 = set_role(JID, Role, SD),
|
SD1 = set_role(JID, Role, SD),
|
||||||
catch send_new_presence(JID, Reason, SD1, SD),
|
send_new_presence(JID, Reason, SD1, SD),
|
||||||
SD1;
|
SD1;
|
||||||
{JID, affiliation, A, _Reason} ->
|
{JID, affiliation, A, _Reason} ->
|
||||||
SD1 = set_affiliation(JID, A, SD),
|
SD1 = set_affiliation(JID, A, SD),
|
||||||
|
@ -2669,7 +2640,7 @@ process_item_change(Item, SD, UJID) ->
|
||||||
?ERROR_MSG("failed to set item ~p from ~s: ~p",
|
?ERROR_MSG("failed to set item ~p from ~s: ~p",
|
||||||
[Item, jid:to_string(UJID),
|
[Item, jid:to_string(UJID),
|
||||||
{E, {R, erlang:get_stacktrace()}}]),
|
{E, {R, erlang:get_stacktrace()}}]),
|
||||||
SD
|
{error, xmpp:err_internal_server_error()}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec find_changed_items(jid(), affiliation(), role(),
|
-spec find_changed_items(jid(), affiliation(), role(),
|
||||||
|
@ -2698,12 +2669,11 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
Nick /= <<"">> ->
|
Nick /= <<"">> ->
|
||||||
case find_jids_by_nick(Nick, StateData) of
|
case find_jids_by_nick(Nick, StateData) of
|
||||||
[] ->
|
[] ->
|
||||||
ErrText = iolist_to_binary(
|
ErrText = str:format(
|
||||||
io_lib:format(
|
translate:translate(
|
||||||
translate:translate(
|
Lang,
|
||||||
Lang,
|
<<"Nickname ~s does not exist in the room">>),
|
||||||
<<"Nickname ~s does not exist in the room">>),
|
[Nick]),
|
||||||
[Nick])),
|
|
||||||
throw({error, xmpp:err_not_acceptable(ErrText, Lang)});
|
throw({error, xmpp:err_not_acceptable(ErrText, Lang)});
|
||||||
JIDList ->
|
JIDList ->
|
||||||
JIDList
|
JIDList
|
||||||
|
@ -2740,9 +2710,15 @@ find_changed_items(UJID, UAffiliation, URole,
|
||||||
Items, Lang, StateData,
|
Items, Lang, StateData,
|
||||||
Res);
|
Res);
|
||||||
true ->
|
true ->
|
||||||
MoreRes = [{jid:remove_resource(Jidx),
|
MoreRes = case RoleOrAff of
|
||||||
RoleOrAff, RoleOrAffValue, Reason}
|
affiliation ->
|
||||||
|| Jidx <- JIDs],
|
[{jid:remove_resource(Jidx),
|
||||||
|
RoleOrAff, RoleOrAffValue, Reason}
|
||||||
|
|| Jidx <- JIDs];
|
||||||
|
role ->
|
||||||
|
[{Jidx, RoleOrAff, RoleOrAffValue, Reason}
|
||||||
|
|| Jidx <- JIDs]
|
||||||
|
end,
|
||||||
find_changed_items(UJID, UAffiliation, URole,
|
find_changed_items(UJID, UAffiliation, URole,
|
||||||
Items, Lang, StateData,
|
Items, Lang, StateData,
|
||||||
[MoreRes | Res]);
|
[MoreRes | Res]);
|
||||||
|
@ -2925,12 +2901,13 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation,
|
||||||
StateData#state.users),
|
StateData#state.users),
|
||||||
ActorNick = get_actor_nick(MJID, StateData),
|
ActorNick = get_actor_nick(MJID, StateData),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({_LJID, Info}) ->
|
fun({LJID, Info}) ->
|
||||||
|
IsSelfPresence = jid:tolower(UJID) == LJID,
|
||||||
Item0 = #muc_item{affiliation = Affiliation,
|
Item0 = #muc_item{affiliation = Affiliation,
|
||||||
role = none},
|
role = none},
|
||||||
Item1 = case Info#user.role == moderator orelse
|
Item1 = case Info#user.role == moderator orelse
|
||||||
(StateData#state.config)#config.anonymous
|
(StateData#state.config)#config.anonymous
|
||||||
== false of
|
== false orelse IsSelfPresence of
|
||||||
true -> Item0#muc_item{jid = RealJID};
|
true -> Item0#muc_item{jid = RealJID};
|
||||||
false -> Item0
|
false -> Item0
|
||||||
end,
|
end,
|
||||||
|
@ -2939,9 +2916,12 @@ send_kickban_presence1(MJID, UJID, Reason, Code, Affiliation,
|
||||||
<<"">> -> Item2;
|
<<"">> -> Item2;
|
||||||
_ -> Item2#muc_item{actor = #muc_actor{nick = ActorNick}}
|
_ -> Item2#muc_item{actor = #muc_actor{nick = ActorNick}}
|
||||||
end,
|
end,
|
||||||
|
Codes = if IsSelfPresence -> [110, Code];
|
||||||
|
true -> [Code]
|
||||||
|
end,
|
||||||
Packet = #presence{type = unavailable,
|
Packet = #presence{type = unavailable,
|
||||||
sub_els = [#muc_user{items = [Item],
|
sub_els = [#muc_user{items = [Item],
|
||||||
status_codes = [Code]}]},
|
status_codes = Codes}]},
|
||||||
RoomJIDNick = jid:replace_resource(StateData#state.jid, Nick),
|
RoomJIDNick = jid:replace_resource(StateData#state.jid, Nick),
|
||||||
send_wrapped(RoomJIDNick, Info#user.jid, Packet,
|
send_wrapped(RoomJIDNick, Info#user.jid, Packet,
|
||||||
?NS_MUCSUB_NODES_AFFILIATIONS, StateData),
|
?NS_MUCSUB_NODES_AFFILIATIONS, StateData),
|
||||||
|
@ -2989,13 +2969,21 @@ process_iq_owner(From, #iq{type = set, lang = Lang,
|
||||||
case Config of
|
case Config of
|
||||||
#xdata{type = cancel} ->
|
#xdata{type = cancel} ->
|
||||||
{result, undefined};
|
{result, undefined};
|
||||||
#xdata{type = submit} ->
|
#xdata{type = submit, fields = Fs} ->
|
||||||
case is_allowed_log_change(Config, StateData, From) andalso
|
try muc_roomconfig:decode(Fs) of
|
||||||
is_allowed_persistent_change(Config, StateData, From) andalso
|
Options ->
|
||||||
is_allowed_room_name_desc_limits(Config, StateData) andalso
|
case is_allowed_log_change(Options, StateData, From) andalso
|
||||||
is_password_settings_correct(Config, StateData) of
|
is_allowed_persistent_change(Options, StateData, From) andalso
|
||||||
true -> set_config(Config, StateData, Lang);
|
is_allowed_room_name_desc_limits(Options, StateData) andalso
|
||||||
false -> {error, xmpp:err_not_acceptable()}
|
is_password_settings_correct(Options, StateData) of
|
||||||
|
true ->
|
||||||
|
set_config(Options, StateData, Lang);
|
||||||
|
false ->
|
||||||
|
{error, xmpp:err_not_acceptable()}
|
||||||
|
end
|
||||||
|
catch _:{muc_roomconfig, Why} ->
|
||||||
|
Txt = muc_roomconfig:format_error(Why),
|
||||||
|
{error, xmpp:err_bad_request(Txt, Lang)}
|
||||||
end;
|
end;
|
||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Incorrect data form">>,
|
Txt = <<"Incorrect data form">>,
|
||||||
|
@ -3034,9 +3022,9 @@ process_iq_owner(From, #iq{type = get, lang = Lang,
|
||||||
{error, xmpp:err_bad_request()}
|
{error, xmpp:err_bad_request()}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec is_allowed_log_change(xdata(), state(), jid()) -> boolean().
|
-spec is_allowed_log_change(muc_roomconfig:result(), state(), jid()) -> boolean().
|
||||||
is_allowed_log_change(X, StateData, From) ->
|
is_allowed_log_change(Options, StateData, From) ->
|
||||||
case xmpp_util:has_xdata_var(<<"muc#roomconfig_enablelogging">>, X) of
|
case proplists:is_defined(enablelogging, Options) of
|
||||||
false -> true;
|
false -> true;
|
||||||
true ->
|
true ->
|
||||||
allow ==
|
allow ==
|
||||||
|
@ -3044,9 +3032,9 @@ is_allowed_log_change(X, StateData, From) ->
|
||||||
From)
|
From)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec is_allowed_persistent_change(xdata(), state(), jid()) -> boolean().
|
-spec is_allowed_persistent_change(muc_roomconfig:result(), state(), jid()) -> boolean().
|
||||||
is_allowed_persistent_change(X, StateData, From) ->
|
is_allowed_persistent_change(Options, StateData, From) ->
|
||||||
case xmpp_util:has_xdata_var(<<"muc#roomconfig_persistentroom">>, X) of
|
case proplists:is_defined(persistentroom, Options) of
|
||||||
false -> true;
|
false -> true;
|
||||||
true ->
|
true ->
|
||||||
{_AccessRoute, _AccessCreate, _AccessAdmin,
|
{_AccessRoute, _AccessCreate, _AccessAdmin,
|
||||||
|
@ -3059,66 +3047,43 @@ is_allowed_persistent_change(X, StateData, From) ->
|
||||||
|
|
||||||
%% Check if the Room Name and Room Description defined in the Data Form
|
%% Check if the Room Name and Room Description defined in the Data Form
|
||||||
%% are conformant to the configured limits
|
%% are conformant to the configured limits
|
||||||
-spec is_allowed_room_name_desc_limits(xdata(), state()) -> boolean().
|
-spec is_allowed_room_name_desc_limits(muc_roomconfig:result(), state()) -> boolean().
|
||||||
is_allowed_room_name_desc_limits(XData, StateData) ->
|
is_allowed_room_name_desc_limits(Options, StateData) ->
|
||||||
IsNameAccepted = case xmpp_util:get_xdata_values(
|
RoomName = proplists:get_value(roomname, Options, <<"">>),
|
||||||
<<"muc#roomconfig_roomname">>, XData) of
|
RoomDesc = proplists:get_value(roomdesc, Options, <<"">>),
|
||||||
[N] ->
|
MaxRoomName = gen_mod:get_module_opt(
|
||||||
byte_size(N) =<
|
StateData#state.server_host,
|
||||||
gen_mod:get_module_opt(
|
mod_muc, max_room_name,
|
||||||
StateData#state.server_host,
|
fun(infinity) -> infinity;
|
||||||
mod_muc, max_room_name,
|
(I) when is_integer(I),
|
||||||
fun(infinity) -> infinity;
|
I>0 -> I
|
||||||
(I) when is_integer(I),
|
end, infinity),
|
||||||
I>0 -> I
|
MaxRoomDesc = gen_mod:get_module_opt(
|
||||||
end, infinity);
|
StateData#state.server_host,
|
||||||
_ ->
|
mod_muc, max_room_desc,
|
||||||
true
|
fun(infinity) -> infinity;
|
||||||
end,
|
(I) when is_integer(I),
|
||||||
IsDescAccepted = case xmpp_util:get_xdata_values(
|
I>0 ->
|
||||||
<<"muc#roomconfig_roomdesc">>, XData) of
|
I
|
||||||
[D] ->
|
end, infinity),
|
||||||
byte_size(D) =<
|
(byte_size(RoomName) =< MaxRoomName)
|
||||||
gen_mod:get_module_opt(
|
andalso (byte_size(RoomDesc) =< MaxRoomDesc).
|
||||||
StateData#state.server_host,
|
|
||||||
mod_muc, max_room_desc,
|
|
||||||
fun(infinity) -> infinity;
|
|
||||||
(I) when is_integer(I),
|
|
||||||
I>0 ->
|
|
||||||
I
|
|
||||||
end, infinity);
|
|
||||||
_ -> true
|
|
||||||
end,
|
|
||||||
IsNameAccepted and IsDescAccepted.
|
|
||||||
|
|
||||||
%% Return false if:
|
%% Return false if:
|
||||||
%% "the password for a password-protected room is blank"
|
%% "the password for a password-protected room is blank"
|
||||||
-spec is_password_settings_correct(xdata(), state()) -> boolean().
|
-spec is_password_settings_correct(muc_roomconfig:result(), state()) -> boolean().
|
||||||
is_password_settings_correct(XData, StateData) ->
|
is_password_settings_correct(Options, StateData) ->
|
||||||
Config = StateData#state.config,
|
Config = StateData#state.config,
|
||||||
OldProtected = Config#config.password_protected,
|
OldProtected = Config#config.password_protected,
|
||||||
OldPassword = Config#config.password,
|
OldPassword = Config#config.password,
|
||||||
NewProtected = case xmpp_util:get_xdata_values(
|
NewProtected = proplists:get_value(passwordprotectedroom, Options),
|
||||||
<<"muc#roomconfig_passwordprotectedroom">>, XData) of
|
NewPassword = proplists:get_value(roomsecret, Options),
|
||||||
[<<"1">>] -> true;
|
case {OldProtected, NewProtected, OldPassword, NewPassword} of
|
||||||
[<<"true">>] -> true;
|
{true, undefined, <<"">>, undefined} -> false;
|
||||||
[<<"0">>] -> false;
|
{true, undefined, _, <<"">>} -> false;
|
||||||
[<<"false">>] -> false;
|
{_, true, <<"">>, undefined} -> false;
|
||||||
_ -> undefined
|
{_, true, _, <<"">>} -> false;
|
||||||
end,
|
_ -> true
|
||||||
NewPassword = case xmpp_util:get_xdata_values(
|
|
||||||
<<"muc#roomconfig_roomsecret">>, XData) of
|
|
||||||
[P] -> P;
|
|
||||||
_ -> undefined
|
|
||||||
end,
|
|
||||||
case {OldProtected, NewProtected, OldPassword,
|
|
||||||
NewPassword}
|
|
||||||
of
|
|
||||||
{true, undefined, <<"">>, undefined} -> false;
|
|
||||||
{true, undefined, _, <<"">>} -> false;
|
|
||||||
{_, true, <<"">>, undefined} -> false;
|
|
||||||
{_, true, _, <<"">>} -> false;
|
|
||||||
_ -> true
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec get_default_room_maxusers(state()) -> non_neg_integer().
|
-spec get_default_room_maxusers(state()) -> non_neg_integer().
|
||||||
|
@ -3144,10 +3109,9 @@ get_config(Lang, StateData, From) ->
|
||||||
{N, N};
|
{N, N};
|
||||||
_ -> {0, none}
|
_ -> {0, none}
|
||||||
end,
|
end,
|
||||||
Title = iolist_to_binary(
|
Title = str:format(
|
||||||
io_lib:format(
|
translate:translate(Lang, <<"Configuration of room ~s">>),
|
||||||
translate:translate(Lang, <<"Configuration of room ~s">>),
|
[jid:to_string(StateData#state.jid)]),
|
||||||
[jid:to_string(StateData#state.jid)])),
|
|
||||||
Fs = [{roomname, Config#config.title},
|
Fs = [{roomname, Config#config.title},
|
||||||
{roomdesc, Config#config.description}] ++
|
{roomdesc, Config#config.description}] ++
|
||||||
case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of
|
case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of
|
||||||
|
@ -3208,11 +3172,10 @@ get_config(Lang, StateData, From) ->
|
||||||
fields = muc_roomconfig:encode(
|
fields = muc_roomconfig:encode(
|
||||||
Fields, fun(T) -> translate:translate(Lang, T) end)}.
|
Fields, fun(T) -> translate:translate(Lang, T) end)}.
|
||||||
|
|
||||||
-spec set_config(xdata(), state(), binary()) -> {error, stanza_error()} |
|
-spec set_config(muc_roomconfig:result(), state(), binary()) ->
|
||||||
{result, undefined, state()}.
|
{error, stanza_error()} | {result, undefined, state()}.
|
||||||
set_config(#xdata{fields = Fields}, StateData, Lang) ->
|
set_config(Options, StateData, Lang) ->
|
||||||
try
|
try
|
||||||
Options = muc_roomconfig:decode(Fields),
|
|
||||||
#config{} = Config = set_config(Options, StateData#state.config,
|
#config{} = Config = set_config(Options, StateData#state.config,
|
||||||
StateData#state.server_host, Lang),
|
StateData#state.server_host, Lang),
|
||||||
{result, _, NSD} = Res = change_config(Config, StateData),
|
{result, _, NSD} = Res = change_config(Config, StateData),
|
||||||
|
@ -3227,10 +3190,7 @@ set_config(#xdata{fields = Fields}, StateData, Lang) ->
|
||||||
|| {_, U} <- (?DICT):to_list(StateData#state.users)],
|
|| {_, U} <- (?DICT):to_list(StateData#state.users)],
|
||||||
add_to_log(Type, Users, NSD),
|
add_to_log(Type, Users, NSD),
|
||||||
Res
|
Res
|
||||||
catch _:{muc_roomconfig, Why} ->
|
catch _:{badmatch, {error, #stanza_error{}} = Err} ->
|
||||||
Txt = muc_roomconfig:format_error(Why),
|
|
||||||
{error, xmpp:err_bad_request(Txt, Lang)};
|
|
||||||
_:{badmatch, {error, #stanza_error{}} = Err} ->
|
|
||||||
Err
|
Err
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -3331,18 +3291,23 @@ 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,
|
||||||
Message = #message{type = groupchat,
|
if Codes /= [] ->
|
||||||
id = randoms:get_string(),
|
Message = #message{type = groupchat,
|
||||||
sub_els = [#muc_user{status_codes = Codes}]},
|
id = randoms:get_string(),
|
||||||
send_wrapped_multiple(StateData#state.jid,
|
sub_els = [#muc_user{status_codes = Codes}]},
|
||||||
StateData#state.users,
|
send_wrapped_multiple(StateData#state.jid,
|
||||||
Message,
|
StateData#state.users,
|
||||||
?NS_MUCSUB_NODES_CONFIG,
|
Message,
|
||||||
StateData).
|
?NS_MUCSUB_NODES_CONFIG,
|
||||||
|
StateData);
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
-spec remove_nonmembers(state()) -> state().
|
-spec remove_nonmembers(state()) -> state().
|
||||||
remove_nonmembers(StateData) ->
|
remove_nonmembers(StateData) ->
|
||||||
|
@ -3620,7 +3585,7 @@ iq_disco_info_extras(Lang, StateData) ->
|
||||||
process_iq_disco_items(_From, #iq{type = set, lang = Lang}, _StateData) ->
|
process_iq_disco_items(_From, #iq{type = set, lang = Lang}, _StateData) ->
|
||||||
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
Txt = <<"Value 'set' of 'type' attribute is not allowed">>,
|
||||||
{error, xmpp:err_not_allowed(Txt, Lang)};
|
{error, xmpp:err_not_allowed(Txt, Lang)};
|
||||||
process_iq_disco_items(From, #iq{type = get, lang = Lang}, StateData) ->
|
process_iq_disco_items(From, #iq{type = get}, StateData) ->
|
||||||
case (StateData#state.config)#config.public_list of
|
case (StateData#state.config)#config.public_list of
|
||||||
true ->
|
true ->
|
||||||
{result, get_mucroom_disco_items(StateData)};
|
{result, get_mucroom_disco_items(StateData)};
|
||||||
|
@ -3629,8 +3594,10 @@ process_iq_disco_items(From, #iq{type = get, lang = Lang}, StateData) ->
|
||||||
true ->
|
true ->
|
||||||
{result, get_mucroom_disco_items(StateData)};
|
{result, get_mucroom_disco_items(StateData)};
|
||||||
_ ->
|
_ ->
|
||||||
Txt = <<"Only occupants or administrators can perform this query">>,
|
%% If the list of occupants is private,
|
||||||
{error, xmpp:err_forbidden(Txt, Lang)}
|
%% the room MUST return an empty <query/> element
|
||||||
|
%% (http://xmpp.org/extensions/xep-0045.html#disco-roomitems)
|
||||||
|
{result, #disco_items{}}
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
@ -3661,7 +3628,7 @@ process_iq_vcard(_From, #iq{type = get}, StateData) ->
|
||||||
#xmlel{} = VCard ->
|
#xmlel{} = VCard ->
|
||||||
{result, VCard};
|
{result, VCard};
|
||||||
{error, _} ->
|
{error, _} ->
|
||||||
{result, #vcard_temp{}}
|
{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 = [SubEl]},
|
||||||
StateData) ->
|
StateData) ->
|
||||||
|
@ -3801,8 +3768,7 @@ get_roomdesc_tail(StateData, Lang) ->
|
||||||
_ -> translate:translate(Lang, <<"private, ">>)
|
_ -> translate:translate(Lang, <<"private, ">>)
|
||||||
end,
|
end,
|
||||||
Len = (?DICT):size(StateData#state.users),
|
Len = (?DICT):size(StateData#state.users),
|
||||||
<<" (", Desc/binary,
|
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
|
||||||
(iolist_to_binary(integer_to_list(Len)))/binary, ")">>.
|
|
||||||
|
|
||||||
-spec get_mucroom_disco_items(state()) -> disco_items().
|
-spec get_mucroom_disco_items(state()) -> disco_items().
|
||||||
get_mucroom_disco_items(StateData) ->
|
get_mucroom_disco_items(StateData) ->
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
to_float/1,
|
to_float/1,
|
||||||
prefix/2,
|
prefix/2,
|
||||||
suffix/2,
|
suffix/2,
|
||||||
|
format/2,
|
||||||
to_integer/1]).
|
to_integer/1]).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -277,6 +278,11 @@ prefix(Prefix, B) ->
|
||||||
suffix(B1, B2) ->
|
suffix(B1, B2) ->
|
||||||
lists:suffix(binary_to_list(B1), binary_to_list(B2)).
|
lists:suffix(binary_to_list(B1), binary_to_list(B2)).
|
||||||
|
|
||||||
|
-spec format(io:format(), list()) -> binary().
|
||||||
|
|
||||||
|
format(Format, Args) ->
|
||||||
|
iolist_to_binary(io_lib:format(Format, Args)).
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
|
|
@ -10,23 +10,23 @@
|
||||||
|
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
|
|
||||||
-import(suite, [init_config/1, connect/1, disconnect/1,
|
-import(suite, [init_config/1, connect/1, disconnect/1, recv_message/1,
|
||||||
recv/1, send/2, send_recv/2, my_jid/1, server_jid/1,
|
recv/1, recv_presence/1, send/2, send_recv/2, my_jid/1,
|
||||||
pubsub_jid/1, proxy_jid/1, muc_jid/1, muc_room_jid/1,
|
server_jid/1, pubsub_jid/1, proxy_jid/1, muc_jid/1,
|
||||||
mix_jid/1, mix_room_jid/1, get_features/2, re_register/1,
|
muc_room_jid/1, my_muc_jid/1, peer_muc_jid/1,
|
||||||
is_feature_advertised/2, subscribe_to_events/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,
|
is_feature_advertised/3, set_opt/3, auth_SASL/2,
|
||||||
wait_for_master/1, wait_for_slave/1,
|
wait_for_master/1, wait_for_slave/1, flush/1,
|
||||||
make_iq_result/1, start_event_relay/0,
|
make_iq_result/1, start_event_relay/0, alt_room_jid/1,
|
||||||
stop_event_relay/1, put_event/2, get_event/1,
|
stop_event_relay/1, put_event/2, get_event/1,
|
||||||
bind/1, auth/1, auth/2, open_session/1, open_session/2,
|
bind/1, auth/1, auth/2, open_session/1, open_session/2,
|
||||||
zlib/1, starttls/1, starttls/2, close_socket/1, init_stream/1,
|
zlib/1, starttls/1, starttls/2, close_socket/1, init_stream/1,
|
||||||
auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2]).
|
auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2]).
|
||||||
|
|
||||||
-include("suite.hrl").
|
-include("suite.hrl").
|
||||||
|
|
||||||
suite() ->
|
suite() ->
|
||||||
[{timetrap, {seconds,60}}].
|
[{timetrap, {seconds, 30}}].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
NewConfig = init_config(Config),
|
NewConfig = init_config(Config),
|
||||||
|
@ -83,7 +83,7 @@ init_per_group(Group, Config) ->
|
||||||
|
|
||||||
do_init_per_group(no_db, Config) ->
|
do_init_per_group(no_db, Config) ->
|
||||||
re_register(Config),
|
re_register(Config),
|
||||||
Config;
|
set_opt(persistent_room, false, Config);
|
||||||
do_init_per_group(mnesia, Config) ->
|
do_init_per_group(mnesia, Config) ->
|
||||||
mod_muc:shutdown_rooms(?MNESIA_VHOST),
|
mod_muc:shutdown_rooms(?MNESIA_VHOST),
|
||||||
set_opt(server, ?MNESIA_VHOST, Config);
|
set_opt(server, ?MNESIA_VHOST, Config);
|
||||||
|
@ -202,6 +202,10 @@ init_per_testcase(TestCase, OrigConfig) ->
|
||||||
Test = atom_to_list(TestCase),
|
Test = atom_to_list(TestCase),
|
||||||
IsMaster = lists:suffix("_master", Test),
|
IsMaster = lists:suffix("_master", Test),
|
||||||
IsSlave = lists:suffix("_slave", Test),
|
IsSlave = lists:suffix("_slave", Test),
|
||||||
|
Mode = if IsSlave -> slave;
|
||||||
|
IsMaster -> master;
|
||||||
|
true -> single
|
||||||
|
end,
|
||||||
IsCarbons = lists:prefix("carbons_", Test),
|
IsCarbons = lists:prefix("carbons_", Test),
|
||||||
IsReplaced = lists:prefix("replaced_", Test),
|
IsReplaced = lists:prefix("replaced_", Test),
|
||||||
User = if IsReplaced -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>;
|
User = if IsReplaced -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>;
|
||||||
|
@ -209,6 +213,10 @@ init_per_testcase(TestCase, OrigConfig) ->
|
||||||
IsSlave -> <<"test_slave!#$%^*()`~+-;_=[]{}|\\">>;
|
IsSlave -> <<"test_slave!#$%^*()`~+-;_=[]{}|\\">>;
|
||||||
true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>
|
true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>
|
||||||
end,
|
end,
|
||||||
|
Nick = if IsSlave -> ?config(slave_nick, OrigConfig);
|
||||||
|
IsMaster -> ?config(master_nick, OrigConfig);
|
||||||
|
true -> ?config(nick, OrigConfig)
|
||||||
|
end,
|
||||||
MyResource = if IsMaster and IsCarbons -> MasterResource;
|
MyResource = if IsMaster and IsCarbons -> MasterResource;
|
||||||
IsSlave and IsCarbons -> SlaveResource;
|
IsSlave and IsCarbons -> SlaveResource;
|
||||||
true -> Resource
|
true -> Resource
|
||||||
|
@ -227,10 +235,23 @@ init_per_testcase(TestCase, OrigConfig) ->
|
||||||
true ->
|
true ->
|
||||||
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
|
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
|
||||||
end,
|
end,
|
||||||
Config = set_opt(user, User,
|
Config1 = set_opt(user, User,
|
||||||
set_opt(slave, Slave,
|
set_opt(slave, Slave,
|
||||||
set_opt(master, Master,
|
set_opt(master, Master,
|
||||||
set_opt(resource, MyResource, OrigConfig)))),
|
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
|
case Test of
|
||||||
"test_connect" ++ _ ->
|
"test_connect" ++ _ ->
|
||||||
Config;
|
Config;
|
||||||
|
@ -320,6 +341,8 @@ no_db_tests() ->
|
||||||
[sm,
|
[sm,
|
||||||
sm_resume,
|
sm_resume,
|
||||||
sm_resume_failed]},
|
sm_resume_failed]},
|
||||||
|
muc_tests:single_cases(),
|
||||||
|
muc_tests:master_slave_cases(),
|
||||||
{test_proxy65, [parallel],
|
{test_proxy65, [parallel],
|
||||||
[proxy65_master, proxy65_slave]},
|
[proxy65_master, proxy65_slave]},
|
||||||
{replaced, [parallel],
|
{replaced, [parallel],
|
||||||
|
@ -369,9 +392,9 @@ db_tests(riak) ->
|
||||||
privacy,
|
privacy,
|
||||||
blocking,
|
blocking,
|
||||||
vcard,
|
vcard,
|
||||||
|
muc_tests:single_cases(),
|
||||||
test_unregister]},
|
test_unregister]},
|
||||||
{test_muc_register, [parallel],
|
muc_tests:master_slave_cases(),
|
||||||
[muc_register_master, muc_register_slave]},
|
|
||||||
{test_roster_subscribe, [parallel],
|
{test_roster_subscribe, [parallel],
|
||||||
[roster_subscribe_master,
|
[roster_subscribe_master,
|
||||||
roster_subscribe_slave]},
|
roster_subscribe_slave]},
|
||||||
|
@ -379,8 +402,6 @@ db_tests(riak) ->
|
||||||
[flex_offline_master, flex_offline_slave]},
|
[flex_offline_master, flex_offline_slave]},
|
||||||
{test_offline, [sequence],
|
{test_offline, [sequence],
|
||||||
[offline_master, offline_slave]},
|
[offline_master, offline_slave]},
|
||||||
{test_muc, [parallel],
|
|
||||||
[muc_master, muc_slave]},
|
|
||||||
{test_announce, [sequence],
|
{test_announce, [sequence],
|
||||||
[announce_master, announce_slave]},
|
[announce_master, announce_slave]},
|
||||||
{test_vcard_xupdate, [parallel],
|
{test_vcard_xupdate, [parallel],
|
||||||
|
@ -403,10 +424,10 @@ db_tests(DB) when DB == mnesia; DB == redis ->
|
||||||
blocking,
|
blocking,
|
||||||
vcard,
|
vcard,
|
||||||
pubsub_single_tests(),
|
pubsub_single_tests(),
|
||||||
|
muc_tests:single_cases(),
|
||||||
test_unregister]},
|
test_unregister]},
|
||||||
|
muc_tests:master_slave_cases(),
|
||||||
pubsub_multiple_tests(),
|
pubsub_multiple_tests(),
|
||||||
{test_muc_register, [parallel],
|
|
||||||
[muc_register_master, muc_register_slave]},
|
|
||||||
{test_mix, [parallel],
|
{test_mix, [parallel],
|
||||||
[mix_master, mix_slave]},
|
[mix_master, mix_slave]},
|
||||||
{test_roster_subscribe, [parallel],
|
{test_roster_subscribe, [parallel],
|
||||||
|
@ -424,8 +445,6 @@ db_tests(DB) when DB == mnesia; DB == redis ->
|
||||||
[carbons_master, carbons_slave]},
|
[carbons_master, carbons_slave]},
|
||||||
{test_client_state, [parallel],
|
{test_client_state, [parallel],
|
||||||
[client_state_master, client_state_slave]},
|
[client_state_master, client_state_slave]},
|
||||||
{test_muc, [parallel],
|
|
||||||
[muc_master, muc_slave]},
|
|
||||||
{test_muc_mam, [parallel],
|
{test_muc_mam, [parallel],
|
||||||
[muc_mam_master, muc_mam_slave]},
|
[muc_mam_master, muc_mam_slave]},
|
||||||
{test_announce, [sequence],
|
{test_announce, [sequence],
|
||||||
|
@ -440,21 +459,21 @@ db_tests(_) ->
|
||||||
[{single_user, [sequence],
|
[{single_user, [sequence],
|
||||||
[test_register,
|
[test_register,
|
||||||
legacy_auth_tests(),
|
legacy_auth_tests(),
|
||||||
auth_plain,
|
auth_plain,
|
||||||
auth_md5,
|
auth_md5,
|
||||||
presence_broadcast,
|
presence_broadcast,
|
||||||
last,
|
last,
|
||||||
roster_get,
|
roster_get,
|
||||||
roster_ver,
|
roster_ver,
|
||||||
private,
|
private,
|
||||||
privacy,
|
privacy,
|
||||||
blocking,
|
blocking,
|
||||||
vcard,
|
vcard,
|
||||||
pubsub_single_tests(),
|
pubsub_single_tests(),
|
||||||
test_unregister]},
|
muc_tests:single_cases(),
|
||||||
|
test_unregister]},
|
||||||
|
muc_tests:master_slave_cases(),
|
||||||
pubsub_multiple_tests(),
|
pubsub_multiple_tests(),
|
||||||
{test_muc_register, [parallel],
|
|
||||||
[muc_register_master, muc_register_slave]},
|
|
||||||
{test_mix, [parallel],
|
{test_mix, [parallel],
|
||||||
[mix_master, mix_slave]},
|
[mix_master, mix_slave]},
|
||||||
{test_roster_subscribe, [parallel],
|
{test_roster_subscribe, [parallel],
|
||||||
|
@ -468,8 +487,6 @@ db_tests(_) ->
|
||||||
[mam_old_master, mam_old_slave]},
|
[mam_old_master, mam_old_slave]},
|
||||||
{test_new_mam, [parallel],
|
{test_new_mam, [parallel],
|
||||||
[mam_new_master, mam_new_slave]},
|
[mam_new_master, mam_new_slave]},
|
||||||
{test_muc, [parallel],
|
|
||||||
[muc_master, muc_slave]},
|
|
||||||
{test_muc_mam, [parallel],
|
{test_muc_mam, [parallel],
|
||||||
[muc_mam_master, muc_mam_slave]},
|
[muc_mam_master, muc_mam_slave]},
|
||||||
{test_announce, [sequence],
|
{test_announce, [sequence],
|
||||||
|
@ -604,7 +621,8 @@ test_connect_bad_ns_stream(Config) ->
|
||||||
close_socket(Config0).
|
close_socket(Config0).
|
||||||
|
|
||||||
test_connect_bad_lang(Config) ->
|
test_connect_bad_lang(Config) ->
|
||||||
Config0 = init_stream(set_opt(lang, lists:duplicate(36, $x), Config)),
|
Lang = iolist_to_binary(lists:duplicate(36, $x)),
|
||||||
|
Config0 = init_stream(set_opt(lang, Lang, Config)),
|
||||||
?recv1(#stream_error{reason = 'policy-violation'}),
|
?recv1(#stream_error{reason = 'policy-violation'}),
|
||||||
?recv1({xmlstreamend, <<"stream:stream">>}),
|
?recv1({xmlstreamend, <<"stream:stream">>}),
|
||||||
close_socket(Config0).
|
close_socket(Config0).
|
||||||
|
@ -2272,352 +2290,151 @@ muc_mam_master(Config) ->
|
||||||
muc_mam_slave(Config) ->
|
muc_mam_slave(Config) ->
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
muc_master(Config) ->
|
%% OK, I know this is retarded, but I didn't find a better way to
|
||||||
MyJID = my_jid(Config),
|
%% split the test cases into different modules
|
||||||
PeerJID = ?config(slave, Config),
|
muc_service_presence_error(Config) ->
|
||||||
PeerBareJID = jid:remove_resource(PeerJID),
|
muc_tests:muc_service_presence_error(Config).
|
||||||
PeerJIDStr = jid:to_string(PeerJID),
|
muc_service_message_error(Config) ->
|
||||||
MUC = muc_jid(Config),
|
muc_tests:muc_service_message_error(Config).
|
||||||
Room = muc_room_jid(Config),
|
muc_service_unknown_ns_iq_error(Config) ->
|
||||||
MyNick = ?config(master_nick, Config),
|
muc_tests:muc_service_unknown_ns_iq_error(Config).
|
||||||
MyNickJID = jid:replace_resource(Room, MyNick),
|
muc_service_iq_set_error(Config) ->
|
||||||
PeerNick = ?config(slave_nick, Config),
|
muc_tests:muc_service_iq_set_error(Config).
|
||||||
PeerNickJID = jid:replace_resource(Room, PeerNick),
|
muc_service_improper_iq_error(Config) ->
|
||||||
Subject = ?config(room_subject, Config),
|
muc_tests:muc_service_improper_iq_error(Config).
|
||||||
Localhost = jid:make(<<"">>, <<"localhost">>, <<"">>),
|
muc_service_features(Config) ->
|
||||||
true = is_feature_advertised(Config, ?NS_MUC, MUC),
|
muc_tests:muc_service_features(Config).
|
||||||
%% Joining
|
muc_service_disco_info_node_error(Config) ->
|
||||||
send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
|
muc_tests:muc_service_disco_info_node_error(Config).
|
||||||
%% As per XEP-0045 we MUST receive stanzas in the following order:
|
muc_service_disco_items(Config) ->
|
||||||
%% 1. In-room presence from other occupants
|
muc_tests:muc_service_disco_items(Config).
|
||||||
%% 2. In-room presence from the joining entity itself (so-called "self-presence")
|
muc_service_vcard(Config) ->
|
||||||
%% 3. Room history (if any)
|
muc_tests:muc_service_vcard(Config).
|
||||||
%% 4. The room subject
|
muc_service_unique(Config) ->
|
||||||
%% 5. Live messages, presence updates, new user joins, etc.
|
muc_tests:muc_service_unique(Config).
|
||||||
%% As this is the newly created room, we receive only the 2nd stanza.
|
muc_service_subscriptions(Config) ->
|
||||||
#muc_user{
|
muc_tests:muc_service_subscriptions(Config).
|
||||||
status_codes = Codes,
|
muc_configure_non_existent(Config) ->
|
||||||
items = [#muc_item{role = moderator,
|
muc_tests:muc_configure_non_existent(Config).
|
||||||
jid = MyJID,
|
muc_cancel_configure_non_existent(Config) ->
|
||||||
affiliation = owner}]} =
|
muc_tests:muc_cancel_configure_non_existent(Config).
|
||||||
xmpp:get_subtag(?recv1(#presence{from = MyNickJID}), #muc_user{}),
|
|
||||||
%% 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}]}]}),
|
|
||||||
#muc_user{
|
|
||||||
items = [#muc_item{role = visitor,
|
|
||||||
jid = PeerJID,
|
|
||||||
affiliation = none}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = PeerNickJID}), #muc_user{}),
|
|
||||||
%% Receiving a voice request
|
|
||||||
#message{from = Room,
|
|
||||||
sub_els = [#xdata{type = form,
|
|
||||||
instructions = [_],
|
|
||||||
fields = VoiceReqFs}]} = recv(Config),
|
|
||||||
%% 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
|
|
||||||
#muc_user{items = [#muc_item{role = participant,
|
|
||||||
jid = PeerJID,
|
|
||||||
affiliation = none}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = PeerNickJID}), #muc_user{}),
|
|
||||||
%% 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
|
|
||||||
#muc_user{items = [#muc_item{affiliation = member,
|
|
||||||
jid = PeerJID,
|
|
||||||
role = participant}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = PeerNickJID}), #muc_user{}),
|
|
||||||
?recv1(#message{from = Room,
|
|
||||||
sub_els = [#muc_user{
|
|
||||||
items = [#muc_item{affiliation = member,
|
|
||||||
jid = Localhost,
|
|
||||||
role = none}]}]}),
|
|
||||||
?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 = PeerBareJID}] = 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}]}]}),
|
|
||||||
?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_destroy{
|
|
||||||
reason = Subject}}]}),
|
|
||||||
%% Kicked off
|
|
||||||
?recv1(#presence{from = MyNickJID, type = unavailable,
|
|
||||||
sub_els = [#muc_user{items = [#muc_item{role = none,
|
|
||||||
affiliation = none}],
|
|
||||||
destroy = #muc_destroy{
|
|
||||||
reason = Subject}}]}),
|
|
||||||
?recv1(#iq{type = result, id = I3, sub_els = []}),
|
|
||||||
disconnect(Config).
|
|
||||||
|
|
||||||
muc_slave(Config) ->
|
|
||||||
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),
|
|
||||||
%% Receive an invite from the peer
|
|
||||||
#muc_user{invites = [#muc_invite{from = PeerJID}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#message{from = Room, type = normal}),
|
|
||||||
#muc_user{}),
|
|
||||||
%% 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
|
|
||||||
#muc_user{
|
|
||||||
status_codes = [],
|
|
||||||
items = [#muc_item{role = moderator,
|
|
||||||
affiliation = owner}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = PeerNickJID}), #muc_user{}),
|
|
||||||
%% The next is the self-presence (code 110 means it)
|
|
||||||
#muc_user{status_codes = [110],
|
|
||||||
items = [#muc_item{role = visitor,
|
|
||||||
affiliation = none}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = MyNickJID}), #muc_user{}),
|
|
||||||
%% 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
|
|
||||||
#muc_user{items = [#muc_item{role = participant,
|
|
||||||
affiliation = none}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = MyNickJID}), #muc_user{}),
|
|
||||||
%% Sending private message to the peer
|
|
||||||
send(Config, #message{to = PeerNickJID,
|
|
||||||
body = [#text{data = Subject}]}),
|
|
||||||
%% Becoming a member
|
|
||||||
#muc_user{items = [#muc_item{role = participant,
|
|
||||||
affiliation = member}]} =
|
|
||||||
xmpp:get_subtag(?recv1(#presence{from = MyNickJID}), #muc_user{}),
|
|
||||||
%% 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) ->
|
|
||||||
PrevRegistered = if PrevNick /= <<"">> -> true;
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
NewRegistered = if Nick /= <<"">> -> true;
|
|
||||||
true -> false
|
|
||||||
end,
|
|
||||||
%% Request register form
|
|
||||||
#iq{type = result,
|
|
||||||
sub_els = [#register{registered = PrevRegistered,
|
|
||||||
xdata = #xdata{type = form,
|
|
||||||
fields = FsWithoutNick}}]} =
|
|
||||||
send_recv(Config, #iq{type = get, to = MUC,
|
|
||||||
sub_els = [#register{}]}),
|
|
||||||
%% Check if previous nick is registered
|
|
||||||
PrevNick = proplists:get_value(
|
|
||||||
roomnick, muc_register:decode(FsWithoutNick)),
|
|
||||||
X = #xdata{type = submit, fields = muc_register:encode([{roomnick, Nick}])},
|
|
||||||
%% Submitting form
|
|
||||||
#iq{type = result, sub_els = []} =
|
|
||||||
send_recv(Config, #iq{type = set, to = MUC,
|
|
||||||
sub_els = [#register{xdata = X}]}),
|
|
||||||
%% Check if new nick was registered
|
|
||||||
#iq{type = result,
|
|
||||||
sub_els = [#register{registered = NewRegistered,
|
|
||||||
xdata = #xdata{type = form,
|
|
||||||
fields = FsWithNick}}]} =
|
|
||||||
send_recv(Config, #iq{type = get, to = MUC,
|
|
||||||
sub_els = [#register{}]}),
|
|
||||||
Nick = proplists:get_value(
|
|
||||||
roomnick, muc_register:decode(FsWithNick)).
|
|
||||||
|
|
||||||
muc_register_master(Config) ->
|
muc_register_master(Config) ->
|
||||||
MUC = muc_jid(Config),
|
muc_tests:muc_register_master(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">>),
|
|
||||||
%% Wait for slave to fail trying to register nick "master"
|
|
||||||
wait_for_slave(Config),
|
|
||||||
wait_for_slave(Config),
|
|
||||||
%% Now register empty ("") nick, which means we're unregistering
|
|
||||||
muc_register_nick(Config, MUC, <<"master">>, <<"">>),
|
|
||||||
disconnect(Config).
|
|
||||||
|
|
||||||
muc_register_slave(Config) ->
|
muc_register_slave(Config) ->
|
||||||
MUC = muc_jid(Config),
|
muc_tests:muc_register_slave(Config).
|
||||||
wait_for_master(Config),
|
muc_join_conflict_master(Config) ->
|
||||||
%% Trying to register occupied nick "master"
|
muc_tests:muc_join_conflict_master(Config).
|
||||||
Fs = muc_register:encode([{roomnick, <<"master">>}]),
|
muc_join_conflict_slave(Config) ->
|
||||||
X = #xdata{type = submit, fields = Fs},
|
muc_tests:muc_join_conflict_slave(Config).
|
||||||
#iq{type = error} =
|
muc_groupchat_msg_master(Config) ->
|
||||||
send_recv(Config, #iq{type = set, to = MUC,
|
muc_tests:muc_groupchat_msg_master(Config).
|
||||||
sub_els = [#register{xdata = X}]}),
|
muc_groupchat_msg_slave(Config) ->
|
||||||
wait_for_master(Config),
|
muc_tests:muc_groupchat_msg_slave(Config).
|
||||||
disconnect(Config).
|
muc_private_msg_master(Config) ->
|
||||||
|
muc_tests:muc_private_msg_master(Config).
|
||||||
|
muc_private_msg_slave(Config) ->
|
||||||
|
muc_tests:muc_private_msg_slave(Config).
|
||||||
|
muc_set_subject_master(Config) ->
|
||||||
|
muc_tests:muc_set_subject_master(Config).
|
||||||
|
muc_set_subject_slave(Config) ->
|
||||||
|
muc_tests:muc_set_subject_slave(Config).
|
||||||
|
muc_history_master(Config) ->
|
||||||
|
muc_tests:muc_history_master(Config).
|
||||||
|
muc_history_slave(Config) ->
|
||||||
|
muc_tests:muc_history_slave(Config).
|
||||||
|
muc_invite_master(Config) ->
|
||||||
|
muc_tests:muc_invite_master(Config).
|
||||||
|
muc_invite_slave(Config) ->
|
||||||
|
muc_tests:muc_invite_slave(Config).
|
||||||
|
muc_invite_members_only_master(Config) ->
|
||||||
|
muc_tests:muc_invite_members_only_master(Config).
|
||||||
|
muc_invite_members_only_slave(Config) ->
|
||||||
|
muc_tests:muc_invite_members_only_slave(Config).
|
||||||
|
muc_invite_password_protected_master(Config) ->
|
||||||
|
muc_tests:muc_invite_password_protected_master(Config).
|
||||||
|
muc_invite_password_protected_slave(Config) ->
|
||||||
|
muc_tests:muc_invite_password_protected_slave(Config).
|
||||||
|
muc_voice_request_master(Config) ->
|
||||||
|
muc_tests:muc_voice_request_master(Config).
|
||||||
|
muc_voice_request_slave(Config) ->
|
||||||
|
muc_tests:muc_voice_request_slave(Config).
|
||||||
|
muc_change_role_master(Config) ->
|
||||||
|
muc_tests:muc_change_role_master(Config).
|
||||||
|
muc_change_role_slave(Config) ->
|
||||||
|
muc_tests:muc_change_role_slave(Config).
|
||||||
|
muc_kick_master(Config) ->
|
||||||
|
muc_tests:muc_kick_master(Config).
|
||||||
|
muc_kick_slave(Config) ->
|
||||||
|
muc_tests:muc_kick_slave(Config).
|
||||||
|
muc_change_affiliation_master(Config) ->
|
||||||
|
muc_tests:muc_change_affiliation_master(Config).
|
||||||
|
muc_change_affiliation_slave(Config) ->
|
||||||
|
muc_tests:muc_change_affiliation_slave(Config).
|
||||||
|
muc_destroy_master(Config) ->
|
||||||
|
muc_tests:muc_destroy_master(Config).
|
||||||
|
muc_destroy_slave(Config) ->
|
||||||
|
muc_tests:muc_destroy_slave(Config).
|
||||||
|
muc_vcard_master(Config) ->
|
||||||
|
muc_tests:muc_vcard_master(Config).
|
||||||
|
muc_vcard_slave(Config) ->
|
||||||
|
muc_tests:muc_vcard_slave(Config).
|
||||||
|
muc_nick_change_master(Config) ->
|
||||||
|
muc_tests:muc_nick_change_master(Config).
|
||||||
|
muc_nick_change_slave(Config) ->
|
||||||
|
muc_tests:muc_nick_change_slave(Config).
|
||||||
|
muc_config_title_desc_master(Config) ->
|
||||||
|
muc_tests:muc_config_title_desc_master(Config).
|
||||||
|
muc_config_title_desc_slave(Config) ->
|
||||||
|
muc_tests:muc_config_title_desc_slave(Config).
|
||||||
|
muc_config_public_list_master(Config) ->
|
||||||
|
muc_tests:muc_config_public_list_master(Config).
|
||||||
|
muc_config_public_list_slave(Config) ->
|
||||||
|
muc_tests:muc_config_public_list_slave(Config).
|
||||||
|
muc_config_password_master(Config) ->
|
||||||
|
muc_tests:muc_config_password_master(Config).
|
||||||
|
muc_config_password_slave(Config) ->
|
||||||
|
muc_tests:muc_config_password_slave(Config).
|
||||||
|
muc_config_whois_master(Config) ->
|
||||||
|
muc_tests:muc_config_whois_master(Config).
|
||||||
|
muc_config_whois_slave(Config) ->
|
||||||
|
muc_tests:muc_config_whois_slave(Config).
|
||||||
|
muc_config_members_only_master(Config) ->
|
||||||
|
muc_tests:muc_config_members_only_master(Config).
|
||||||
|
muc_config_members_only_slave(Config) ->
|
||||||
|
muc_tests:muc_config_members_only_slave(Config).
|
||||||
|
muc_config_moderated_master(Config) ->
|
||||||
|
muc_tests:muc_config_moderated_master(Config).
|
||||||
|
muc_config_moderated_slave(Config) ->
|
||||||
|
muc_tests:muc_config_moderated_slave(Config).
|
||||||
|
muc_config_private_messages_master(Config) ->
|
||||||
|
muc_tests:muc_config_private_messages_master(Config).
|
||||||
|
muc_config_private_messages_slave(Config) ->
|
||||||
|
muc_tests:muc_config_private_messages_slave(Config).
|
||||||
|
muc_config_query_master(Config) ->
|
||||||
|
muc_tests:muc_config_query_master(Config).
|
||||||
|
muc_config_query_slave(Config) ->
|
||||||
|
muc_tests:muc_config_query_slave(Config).
|
||||||
|
muc_config_allow_invites_master(Config) ->
|
||||||
|
muc_tests:muc_config_allow_invites_master(Config).
|
||||||
|
muc_config_allow_invites_slave(Config) ->
|
||||||
|
muc_tests:muc_config_allow_invites_slave(Config).
|
||||||
|
muc_config_visitor_status_master(Config) ->
|
||||||
|
muc_tests:muc_config_visitor_status_master(Config).
|
||||||
|
muc_config_visitor_status_slave(Config) ->
|
||||||
|
muc_tests:muc_config_visitor_status_slave(Config).
|
||||||
|
muc_config_allow_voice_requests_master(Config) ->
|
||||||
|
muc_tests:muc_config_allow_voice_requests_master(Config).
|
||||||
|
muc_config_allow_voice_requests_slave(Config) ->
|
||||||
|
muc_tests:muc_config_allow_voice_requests_slave(Config).
|
||||||
|
muc_config_voice_request_interval_master(Config) ->
|
||||||
|
muc_tests:muc_config_voice_request_interval_master(Config).
|
||||||
|
muc_config_voice_request_interval_slave(Config) ->
|
||||||
|
muc_tests:muc_config_voice_request_interval_slave(Config).
|
||||||
|
muc_config_visitor_nickchange_master(Config) ->
|
||||||
|
muc_tests:muc_config_visitor_nickchange_master(Config).
|
||||||
|
muc_config_visitor_nickchange_slave(Config) ->
|
||||||
|
muc_tests:muc_config_visitor_nickchange_slave(Config).
|
||||||
|
|
||||||
announce_master(Config) ->
|
announce_master(Config) ->
|
||||||
MyJID = my_jid(Config),
|
MyJID = my_jid(Config),
|
||||||
|
|
|
@ -443,6 +443,7 @@ modules:
|
||||||
mod_ping: []
|
mod_ping: []
|
||||||
mod_proxy65: []
|
mod_proxy65: []
|
||||||
mod_legacy: []
|
mod_legacy: []
|
||||||
|
mod_muc: []
|
||||||
mod_register:
|
mod_register:
|
||||||
welcome_message:
|
welcome_message:
|
||||||
subject: "Welcome!"
|
subject: "Welcome!"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -70,10 +70,12 @@ init_config(Config) ->
|
||||||
{s2s_port, ct:get_config(s2s_port, 5269)},
|
{s2s_port, ct:get_config(s2s_port, 5269)},
|
||||||
{server, ?COMMON_VHOST},
|
{server, ?COMMON_VHOST},
|
||||||
{user, <<"test_single!#$%^*()`~+-;_=[]{}|\\">>},
|
{user, <<"test_single!#$%^*()`~+-;_=[]{}|\\">>},
|
||||||
|
{nick, <<"nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{master_nick, <<"master_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
{master_nick, <<"master_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
{slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
{room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
|
||||||
{certfile, CertFile},
|
{certfile, CertFile},
|
||||||
|
{persistent_room, true},
|
||||||
{anonymous, false},
|
{anonymous, false},
|
||||||
{type, client},
|
{type, client},
|
||||||
{xmlns, ?NS_CLIENT},
|
{xmlns, ?NS_CLIENT},
|
||||||
|
@ -210,6 +212,7 @@ process_stream_features(Config) ->
|
||||||
end, set_opt(mechs, Mechs, Config), Fs).
|
end, set_opt(mechs, Mechs, Config), Fs).
|
||||||
|
|
||||||
disconnect(Config) ->
|
disconnect(Config) ->
|
||||||
|
ct:comment("Disconnecting"),
|
||||||
Socket = ?config(socket, Config),
|
Socket = ?config(socket, Config),
|
||||||
try
|
try
|
||||||
ok = send_text(Config, ?STREAM_TRAILER)
|
ok = send_text(Config, ?STREAM_TRAILER)
|
||||||
|
@ -435,22 +438,50 @@ match_failure(Received, Matches) ->
|
||||||
recv(Config) ->
|
recv(Config) ->
|
||||||
receive
|
receive
|
||||||
{'$gen_event', {xmlstreamelement, El}} ->
|
{'$gen_event', {xmlstreamelement, El}} ->
|
||||||
NS = case ?config(type, Config) of
|
decode_stream_element(Config, El);
|
||||||
client -> ?NS_CLIENT;
|
|
||||||
server -> ?NS_SERVER;
|
|
||||||
component -> ?NS_COMPONENT
|
|
||||||
end,
|
|
||||||
decode(El, NS, []);
|
|
||||||
{'$gen_event', {xmlstreamstart, Name, Attrs}} ->
|
{'$gen_event', {xmlstreamstart, Name, Attrs}} ->
|
||||||
decode(#xmlel{name = Name, attrs = Attrs}, <<>>, []);
|
decode(#xmlel{name = Name, attrs = Attrs}, <<>>, []);
|
||||||
{'$gen_event', Event} ->
|
{'$gen_event', Event} ->
|
||||||
Event
|
Event
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
recv_iq(Config) ->
|
||||||
|
receive
|
||||||
|
{'$gen_event', {xmlstreamelement, #xmlel{name = <<"iq">>} = El}} ->
|
||||||
|
decode_stream_element(Config, El)
|
||||||
|
end.
|
||||||
|
|
||||||
|
recv_presence(Config) ->
|
||||||
|
receive
|
||||||
|
{'$gen_event', {xmlstreamelement, #xmlel{name = <<"presence">>} = El}} ->
|
||||||
|
decode_stream_element(Config, El)
|
||||||
|
end.
|
||||||
|
|
||||||
|
recv_message(Config) ->
|
||||||
|
receive
|
||||||
|
{'$gen_event', {xmlstreamelement, #xmlel{name = <<"message">>} = El}} ->
|
||||||
|
decode_stream_element(Config, El)
|
||||||
|
end.
|
||||||
|
|
||||||
|
decode_stream_element(Config, El) ->
|
||||||
|
NS = case ?config(type, Config) of
|
||||||
|
client -> ?NS_CLIENT;
|
||||||
|
server -> ?NS_SERVER;
|
||||||
|
component -> ?NS_COMPONENT
|
||||||
|
end,
|
||||||
|
decode(El, NS, []).
|
||||||
|
|
||||||
|
format_element(El) ->
|
||||||
|
case erlang:function_exported(ct, log, 5) of
|
||||||
|
true -> ejabberd_web_admin:pretty_print_xml(El);
|
||||||
|
false -> io_lib:format(" ~s~n", El)
|
||||||
|
end.
|
||||||
|
|
||||||
decode(El, NS, Opts) ->
|
decode(El, NS, Opts) ->
|
||||||
try
|
try
|
||||||
Pkt = xmpp:decode(El, NS, Opts),
|
Pkt = xmpp:decode(El, NS, Opts),
|
||||||
ct:pal("recv: ~p ->~n~s", [El, xmpp:pp(Pkt)]),
|
ct:pal("RECV:~n~s~n~s",
|
||||||
|
[format_element(El), xmpp:pp(Pkt)]),
|
||||||
Pkt
|
Pkt
|
||||||
catch _:{xmpp_codec, Why} ->
|
catch _:{xmpp_codec, Why} ->
|
||||||
ct:fail("recv failed: ~p->~n~s",
|
ct:fail("recv failed: ~p->~n~s",
|
||||||
|
@ -475,7 +506,8 @@ send(State, Pkt) ->
|
||||||
{undefined, Pkt}
|
{undefined, Pkt}
|
||||||
end,
|
end,
|
||||||
El = xmpp:encode(NewPkt),
|
El = xmpp:encode(NewPkt),
|
||||||
ct:pal("sent: ~p <-~n~s", [El, xmpp:pp(NewPkt)]),
|
ct:pal("SENT:~n~s~n~s",
|
||||||
|
[format_element(El), xmpp:pp(NewPkt)]),
|
||||||
Data = case NewPkt of
|
Data = case NewPkt of
|
||||||
#stream_start{} -> fxml:element_to_header(El);
|
#stream_start{} -> fxml:element_to_header(El);
|
||||||
_ -> fxml:element_to_binary(El)
|
_ -> fxml:element_to_binary(El)
|
||||||
|
@ -483,9 +515,15 @@ send(State, Pkt) ->
|
||||||
ok = send_text(State, Data),
|
ok = send_text(State, Data),
|
||||||
NewID.
|
NewID.
|
||||||
|
|
||||||
send_recv(State, IQ) ->
|
send_recv(State, #message{} = Msg) ->
|
||||||
|
ID = send(State, Msg),
|
||||||
|
#message{id = ID} = recv_message(State);
|
||||||
|
send_recv(State, #presence{} = Pres) ->
|
||||||
|
ID = send(State, Pres),
|
||||||
|
#presence{id = ID} = recv_presence(State);
|
||||||
|
send_recv(State, #iq{} = IQ) ->
|
||||||
ID = send(State, IQ),
|
ID = send(State, IQ),
|
||||||
#iq{id = ID} = recv(State).
|
#iq{id = ID} = recv_iq(State).
|
||||||
|
|
||||||
sasl_new(<<"PLAIN">>, User, Server, Password) ->
|
sasl_new(<<"PLAIN">>, User, Server, Password) ->
|
||||||
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
|
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
|
||||||
|
@ -590,6 +628,20 @@ muc_room_jid(Config) ->
|
||||||
Server = ?config(server, Config),
|
Server = ?config(server, Config),
|
||||||
jid:make(<<"test">>, <<"conference.", Server/binary>>, <<>>).
|
jid:make(<<"test">>, <<"conference.", Server/binary>>, <<>>).
|
||||||
|
|
||||||
|
my_muc_jid(Config) ->
|
||||||
|
Nick = ?config(nick, Config),
|
||||||
|
RoomJID = muc_room_jid(Config),
|
||||||
|
jid:replace_resource(RoomJID, Nick).
|
||||||
|
|
||||||
|
peer_muc_jid(Config) ->
|
||||||
|
PeerNick = ?config(peer_nick, Config),
|
||||||
|
RoomJID = muc_room_jid(Config),
|
||||||
|
jid:replace_resource(RoomJID, PeerNick).
|
||||||
|
|
||||||
|
alt_room_jid(Config) ->
|
||||||
|
Server = ?config(server, Config),
|
||||||
|
jid:make(<<"alt">>, <<"conference.", Server/binary>>, <<>>).
|
||||||
|
|
||||||
mix_jid(Config) ->
|
mix_jid(Config) ->
|
||||||
Server = ?config(server, Config),
|
Server = ?config(server, Config),
|
||||||
jid:make(<<>>, <<"mix.", Server/binary>>, <<>>).
|
jid:make(<<>>, <<"mix.", Server/binary>>, <<>>).
|
||||||
|
@ -610,6 +662,7 @@ get_features(Config) ->
|
||||||
get_features(Config, server_jid(Config)).
|
get_features(Config, server_jid(Config)).
|
||||||
|
|
||||||
get_features(Config, To) ->
|
get_features(Config, To) ->
|
||||||
|
ct:comment("Getting features of ~s", [jid:to_string(To)]),
|
||||||
#iq{type = result, sub_els = [#disco_info{features = Features}]} =
|
#iq{type = result, sub_els = [#disco_info{features = Features}]} =
|
||||||
send_recv(Config, #iq{type = get, sub_els = [#disco_info{}], to = To}),
|
send_recv(Config, #iq{type = get, sub_els = [#disco_info{}], to = To}),
|
||||||
Features.
|
Features.
|
||||||
|
@ -707,3 +760,10 @@ get_event(Config) ->
|
||||||
{event, Event, Relay} ->
|
{event, Event, Relay} ->
|
||||||
Event
|
Event
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
flush(Config) ->
|
||||||
|
flush(Config, []).
|
||||||
|
|
||||||
|
flush(Config, Msgs) ->
|
||||||
|
receive Msg -> flush(Config, [Msg|Msgs])
|
||||||
|
after 1000 -> lists:reverse(Msgs) end.
|
||||||
|
|
Loading…
Reference in New Issue