From 9d5426315f796fd1467f469e888e4aee58b30d37 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 7 Apr 2014 22:10:08 +0200 Subject: [PATCH 1/2] Carbons: Also forward messages sent to bare JIDs Don't ignore messages sent to bare JIDs, but forward them to all carbon-copy-enabled resources that don't have the highest priority. --- src/ejabberd_sm.erl | 29 ++++++++++++++-------------- src/mod_carboncopy.erl | 43 ++++++++++++++++++++++++------------------ 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 2b704d999..a8038a280 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -38,6 +38,7 @@ bounce_offline_message/3, disconnect_removed_user/2, get_user_resources/2, + get_user_present_resources/2, set_presence/7, unset_presence/6, close_session_unset_presence/5, @@ -167,6 +168,20 @@ get_user_resources(User, Server) -> [element(3, S#session.usr) || S <- clean_session_list(Ss)] end. +-spec get_user_present_resources(binary(), binary()) -> [tuple()]. + +get_user_present_resources(LUser, LServer) -> + US = {LUser, LServer}, + case catch mnesia:dirty_index_read(session, US, + #session.us) + of + {'EXIT', _Reason} -> []; + Ss -> + [{S#session.priority, element(3, S#session.usr)} + || S <- clean_session_list(Ss), + is_integer(S#session.priority)] + end. + -spec get_user_ip(binary(), binary(), binary()) -> ip(). get_user_ip(User, Server, Resource) -> @@ -670,20 +685,6 @@ clean_session_list([S1, S2 | Rest], Res) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -get_user_present_resources(LUser, LServer) -> - US = {LUser, LServer}, - case catch mnesia:dirty_index_read(session, US, - #session.us) - of - {'EXIT', _Reason} -> []; - Ss -> - [{S#session.priority, element(3, S#session.usr)} - || S <- clean_session_list(Ss), - is_integer(S#session.priority)] - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - %% On new session, check if some existing connections need to be replace check_for_sessions_to_replace(User, Server, Resource) -> LUser = jlib:nodeprep(User), diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index d31791cd5..6a5c88706 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -128,23 +128,18 @@ iq_handler(From, _To, #iq{type=set, sub_el = #xmlel{name = Operation, children iq_handler(_From, _To, IQ, _CC)-> IQ#iq{type=error, sub_el = [?ERR_NOT_ALLOWED]}. -user_send_packet(From, _To, Packet) -> - check_and_forward(From, Packet, sent). +user_send_packet(From, To, Packet) -> + check_and_forward(From, To, Packet, sent). -%% Only make carbon copies if the original destination was not a bare jid. -%% If the original destination was a bare jid, the message is going to be delivered to all -%% connected resources anyway. Avoid duplicate delivery. "XEP-0280 : 3.5 Receiving Messages" -user_receive_packet(JID, _From, #jid{resource=Resource} = _To, Packet) when Resource /= <<>> -> - check_and_forward(JID, Packet, received); -user_receive_packet(_JID, _From, _To, _Packet) -> - ok. +user_receive_packet(JID, _From, To, Packet) -> + check_and_forward(JID, To, Packet, received). % verifier si le trafic est local % Modified from original version: % - registered to the user_send_packet hook, to be called only once even for multicast % - do not support "private" message mode, and do not modify the original packet in any way % - we also replicate "read" notifications -check_and_forward(JID, #xmlel{name = <<"message">>, attrs = Attrs} = Packet, Direction)-> +check_and_forward(JID, To, #xmlel{name = <<"message">>, attrs = Attrs} = Packet, Direction)-> case xml:get_attr_s(<<"type">>, Attrs) of <<"chat">> -> case xml:get_subtag(Packet, <<"private">>) of @@ -155,11 +150,11 @@ check_and_forward(JID, #xmlel{name = <<"message">>, attrs = Attrs} = Packet, Dir %% receiving message back to original sender. SubTag = xml:get_subtag(Packet,<<"sent">>), if SubTag == false -> - send_copies(JID, Packet, Direction); + send_copies(JID, To, Packet, Direction); true -> case xml:get_subtag(SubTag,<<"forwarded">>) of false-> - send_copies(JID, Packet, Direction); + send_copies(JID, To, Packet, Direction); _ -> stop end @@ -175,7 +170,7 @@ check_and_forward(JID, #xmlel{name = <<"message">>, attrs = Attrs} = Packet, Dir ok end; -check_and_forward(_JID, _Packet, _)-> ok. +check_and_forward(_JID, _To, _Packet, _)-> ok. remove_connection(User, Server, Resource, _Status)-> disable(Server, User, Resource), @@ -184,14 +179,26 @@ remove_connection(User, Server, Resource, _Status)-> %%% Internal %% Direction = received | sent -send_copies(JID, Packet, Direction)-> +send_copies(JID, To, Packet, Direction)-> {U, S, R} = jlib:jid_tolower(JID), %% list of JIDs that should receive a carbon copy of this message (excluding the - %% receiver of the original message - TargetJIDs = [ {jlib:make_jid({U, S, CCRes}), CC_Version} || {CCRes, CC_Version} <- list(U, S), CCRes /= R ], - %TargetJIDs = lists:delete(JID, [ jlib:make_jid({U, S, CCRes}) || CCRes <- list(U, S) ]), - + %% receiver(s) of the original message + TargetJIDs = case {Direction, To} of + {received, #jid{resource = <<>>}} -> + PrioRes = ejabberd_sm:get_user_present_resources(U, S), + MaxPrio = case catch lists:max(PrioRes) of + {Prio, _Res} -> Prio; + _ -> 0 + end, + OrigTo = fun(Res) -> lists:member({MaxPrio, Res}, PrioRes) end, + [ {jlib:make_jid({U, S, CCRes}), CC_Version} + || {CCRes, CC_Version} <- list(U, S), not OrigTo(CCRes) ]; + _ -> + [ {jlib:make_jid({U, S, CCRes}), CC_Version} + || {CCRes, CC_Version} <- list(U, S), CCRes /= R ] + %TargetJIDs = lists:delete(JID, [ jlib:make_jid({U, S, CCRes}) || CCRes <- list(U, S) ]), + end, lists:map(fun({Dest,Version}) -> {_, _, Resource} = jlib:jid_tolower(Dest), From b3b12effbca94f9b16c3317fa8fdd9b2f4c5554b Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 8 Apr 2014 23:32:30 +0200 Subject: [PATCH 2/2] Carbons: Handle unavailable resource like bare JID As the session manager handles messages sent to unavailable resources just like messages sent to bare JIDs, mod_carboncopy must do that, too. That is, forward them only to those carbon-copy-enabled resources that don't have a top priority, in order to avoid duplicates. --- src/mod_carboncopy.erl | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 6a5c88706..dbb942dd5 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -181,12 +181,21 @@ remove_connection(User, Server, Resource, _Status)-> %% Direction = received | sent send_copies(JID, To, Packet, Direction)-> {U, S, R} = jlib:jid_tolower(JID), + PrioRes = ejabberd_sm:get_user_present_resources(U, S), + IsBareTo = case {Direction, To} of + {received, #jid{lresource = <<>>}} -> true; + {received, #jid{lresource = LRes}} -> + %% unavailable resources are handled like bare JIDs + case lists:keyfind(LRes, 2, PrioRes) of + false -> true; + _ -> false + end; + _ -> false + end, %% list of JIDs that should receive a carbon copy of this message (excluding the %% receiver(s) of the original message - TargetJIDs = case {Direction, To} of - {received, #jid{resource = <<>>}} -> - PrioRes = ejabberd_sm:get_user_present_resources(U, S), + TargetJIDs = if IsBareTo -> MaxPrio = case catch lists:max(PrioRes) of {Prio, _Res} -> Prio; _ -> 0 @@ -194,7 +203,7 @@ send_copies(JID, To, Packet, Direction)-> OrigTo = fun(Res) -> lists:member({MaxPrio, Res}, PrioRes) end, [ {jlib:make_jid({U, S, CCRes}), CC_Version} || {CCRes, CC_Version} <- list(U, S), not OrigTo(CCRes) ]; - _ -> + true -> [ {jlib:make_jid({U, S, CCRes}), CC_Version} || {CCRes, CC_Version} <- list(U, S), CCRes /= R ] %TargetJIDs = lists:delete(JID, [ jlib:make_jid({U, S, CCRes}) || CCRes <- list(U, S) ]),