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