aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ejabberd_c2s.erl2
-rw-r--r--src/ejabberd_router_multicast.erl49
-rw-r--r--src/mod_muc_room.erl144
3 files changed, 153 insertions, 42 deletions
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index ada7653b6..3218cce51 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -880,7 +880,7 @@ get_priority_from_presence(#presence{priority = Prio}) ->
-spec route_multiple(state(), [jid()], stanza()) -> ok.
route_multiple(#{lserver := LServer}, JIDs, Pkt) ->
From = xmpp:get_from(Pkt),
- ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt).
+ ejabberd_router_multicast:route_multicast(From, LServer, JIDs, Pkt, false).
get_subscription(#jid{luser = LUser, lserver = LServer}, JID) ->
{Subscription, _, _} = ejabberd_hooks:run_fold(
diff --git a/src/ejabberd_router_multicast.erl b/src/ejabberd_router_multicast.erl
index 0d9d6b1d4..6e0201c90 100644
--- a/src/ejabberd_router_multicast.erl
+++ b/src/ejabberd_router_multicast.erl
@@ -30,7 +30,7 @@
-behaviour(gen_server).
%% API
--export([route_multicast/4,
+-export([route_multicast/5,
register_route/1,
unregister_route/1
]).
@@ -58,9 +58,11 @@
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
--spec route_multicast(jid(), binary(), [jid()], stanza()) -> ok.
-route_multicast(From, Domain, Destinations, Packet) ->
- case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From)) of
+-spec route_multicast(jid(), binary(), [jid()], stanza(), boolean()) -> ok.
+route_multicast(From0, Domain0, Destinations0, Packet0, Wrapped0) ->
+ {From, Domain, Destinations, Packet, Wrapped} =
+ ejabberd_hooks:run_fold(multicast_route, {From0, Domain0, Destinations0, Packet0, Wrapped0}, []),
+ case catch do_route(Domain, Destinations, xmpp:set_from(Packet, From), Wrapped) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {From, Domain, Destinations, Packet}]);
@@ -157,7 +159,7 @@ handle_cast(Msg, State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({route_multicast, Domain, Destinations, Packet}, State) ->
- case catch do_route(Domain, Destinations, Packet) of
+ case catch do_route(Domain, Destinations, Packet, false) of
{'EXIT', Reason} ->
?ERROR_MSG("~p~nwhen processing: ~p",
[Reason, {Domain, Destinations, Packet}]);
@@ -204,13 +206,41 @@ terminate(_Reason, _State) ->
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
+-spec update_to_in_wrapped(stanza(), jid()) -> stanza().
+update_to_in_wrapped(Packet, To) ->
+ case Packet of
+ #message{sub_els = [#ps_event{
+ items = #ps_items{
+ items = [#ps_item{
+ sub_els = [Internal]
+ } = PSItem]
+ } = PSItems
+ } = PSEvent]} ->
+ Internal2 = xmpp:set_to(Internal, To),
+ PSItem2 = PSItem#ps_item{sub_els = Internal2},
+ PSItems2 = PSItems#ps_items{items = PSItem2},
+ PSEvent2 = PSEvent#ps_event{items = PSItems2},
+ Packet#message{sub_els = [PSEvent2]};
+ _ ->
+ Packet
+ end.
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
%% From = #jid
%% Destinations = [#jid]
--spec do_route(binary(), [jid()], stanza()) -> any().
-do_route(Domain, Destinations, Packet) ->
+-spec do_route(binary(), [jid()], stanza(), boolean()) -> any().
+do_route(Domain, Destinations, Packet, true) ->
+ ?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n",
+ [xmpp:pp(Packet), Domain,
+ str:join([jid:encode(To) || To <- Destinations], <<", ">>)]),
+ lists:foreach(
+ fun(To) ->
+ Packet2 = update_to_in_wrapped(Packet, To),
+ ejabberd_router:route(Packet2)
+ end, Destinations);
+do_route(Domain, Destinations, Packet, false) ->
?DEBUG("Route multicast:~n~ts~nDomain: ~ts~nDestinations: ~ts~n",
[xmpp:pp(Packet), Domain,
str:join([jid:encode(To) || To <- Destinations], <<", ">>)]),
@@ -236,4 +266,7 @@ pick_multicast_pid(Rs) ->
-spec do_route_normal([jid()], stanza()) -> any().
do_route_normal(Destinations, Packet) ->
- [ejabberd_router:route(xmpp:set_to(Packet, To)) || To <- Destinations].
+ lists:foreach(
+ fun(To) ->
+ ejabberd_router:route(xmpp:set_to(Packet, To))
+ end, Destinations).
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index 2fa08dc79..1c9710c60 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -4624,37 +4624,55 @@ store_room_no_checks(StateData, ChangesHints) ->
-spec send_subscriptions_change_notifications(jid(), binary(), subscribe|unsubscribe, state()) -> ok.
send_subscriptions_change_notifications(From, Nick, Type, State) ->
- maps:fold(fun(_, #subscriber{nodes = Nodes, jid = JID}, _) ->
- case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of
+ {WJ, WN} =
+ maps:fold(
+ fun({WithJid, WithNick} = Res, #subscriber{nodes = Nodes, jid = JID}, _) ->
+ case lists:member(?NS_MUCSUB_NODES_SUBSCRIBERS, Nodes) of
+ true ->
+ case (State#state.config)#config.anonymous == false orelse
+ get_role(JID, State) == moderator orelse
+ get_default_role(get_affiliation(JID, State), State) == moderator of
true ->
- ShowJid = case (State#state.config)#config.anonymous == false orelse
- get_role(JID, State) == moderator orelse
- get_default_role(get_affiliation(JID, State), State) == moderator of
- true -> true;
- _ -> false
- end,
- Payload = case {Type, ShowJid} of
- {subscribe, true} ->
- #muc_subscribe{jid = From, nick = Nick};
- {subscribe, _} ->
- #muc_subscribe{nick = Nick};
- {unsubscribe, true} ->
- #muc_unsubscribe{jid = From, nick = Nick};
- {unsubscribe, _} ->
- #muc_unsubscribe{nick = Nick}
- end,
- Packet = #message{
- sub_els = [#ps_event{
- items = #ps_items{
- node = ?NS_MUCSUB_NODES_SUBSCRIBERS,
- items = [#ps_item{
- id = p1_rand:get_string(),
- sub_els = [Payload]}]}}]},
- ejabberd_router:route(xmpp:set_from_to(Packet, State#state.jid, JID));
- false ->
- ok
- end
- end, ok, State#state.subscribers).
+ {[JID | WithJid], WithNick};
+ _ ->
+ {WithJid, [JID | WithNick]}
+ end;
+ false ->
+ Res
+ end
+ end, ok, State#state.subscribers),
+ if WJ /= [] ->
+ Payload1 = case Type of
+ subscribe -> #muc_subscribe{jid = From, nick = Nick};
+ _ -> #muc_unsubscribe{jid = From, nick = Nick}
+ end,
+ Packet1 = #message{
+ sub_els = [#ps_event{
+ items = #ps_items{
+ node = ?NS_MUCSUB_NODES_SUBSCRIBERS,
+ items = [#ps_item{
+ id = p1_rand:get_string(),
+ sub_els = [Payload1]}]}}]},
+ ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host,
+ WJ, Packet1, true);
+ true -> ok
+ end,
+ if WN /= [] ->
+ Payload2 = case Type of
+ subscribe -> #muc_subscribe{nick = Nick};
+ _ -> #muc_unsubscribe{nick = Nick}
+ end,
+ Packet2 = #message{
+ sub_els = [#ps_event{
+ items = #ps_items{
+ node = ?NS_MUCSUB_NODES_SUBSCRIBERS,
+ items = [#ps_item{
+ id = p1_rand:get_string(),
+ sub_els = [Payload2]}]}}]},
+ ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host,
+ WN, Packet2, true);
+ true -> ok
+ end.
-spec send_wrapped(jid(), jid(), stanza(), binary(), state()) -> ok.
send_wrapped(From, To, Packet, Node, State) ->
@@ -4727,10 +4745,70 @@ wrap(From, To, Packet, Node, Id) ->
-spec send_wrapped_multiple(jid(), users(), stanza(), binary(), state()) -> ok.
send_wrapped_multiple(From, Users, Packet, Node, State) ->
+ {Dir, Wra} =
maps:fold(
- fun(_, #user{jid = To}, _) ->
- send_wrapped(From, To, Packet, Node, State)
- end, ok, Users).
+ fun(_, #user{jid = To, last_presence = LP}, {Direct, Wrapped} = Res) ->
+ IsOffline = LP == undefined,
+ if IsOffline ->
+ LBareTo = jid:tolower(jid:remove_resource(To)),
+ case maps:find(LBareTo, State#state.subscribers) of
+ {ok, #subscriber{nodes = Nodes}} ->
+ case lists:member(Node, Nodes) of
+ true ->
+ {Direct, [To | Wrapped]};
+ _ ->
+ Res
+ end;
+ _ ->
+ Res
+ end;
+ true ->
+ {[To | Direct], Wrapped}
+ end
+ end, {[],[]}, Users),
+ case Dir of
+ [] -> ok;
+ _ ->
+ case Packet of
+ #presence{type = unavailable} ->
+ case xmpp:get_subtag(Packet, #muc_user{}) of
+ #muc_user{destroy = Destroy,
+ status_codes = Codes} ->
+ case Destroy /= undefined orelse
+ (lists:member(110,Codes) andalso
+ not lists:member(303, Codes)) of
+ true ->
+ ejabberd_router_multicast:route_multicast(
+ State#state.jid, State#state.server_host, Dir,
+ #presence{id = p1_rand:get_string(),
+ type = unavailable}, false);
+ false ->
+ ok
+ end;
+ _ ->
+ false
+ end;
+ _ ->
+ ok
+ end,
+ ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host,
+ Dir, Packet, false)
+ end,
+ case Wra of
+ [] -> ok;
+ _ ->
+ MamEnabled = (State#state.config)#config.mam,
+ Id = case xmpp:get_subtag(Packet, #stanza_id{by = #jid{}}) of
+ #stanza_id{id = Id2} ->
+ Id2;
+ _ ->
+ p1_rand:get_string()
+ end,
+ NewPacket = wrap(From, State#state.jid, Packet, Node, Id),
+ NewPacket2 = xmpp:put_meta(NewPacket, in_muc_mam, MamEnabled),
+ ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host,
+ Wra, NewPacket2, true)
+ end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Detect messange stanzas that don't have meaningful content