mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
Support XEP-0321: Remote Roster Management (EJAB-1381)
This commit is contained in:
parent
46b2d91105
commit
e211bf522e
@ -4052,15 +4052,28 @@ Options:
|
|||||||
not add/remove/modify contacts,
|
not add/remove/modify contacts,
|
||||||
or subscribe/unsubscribe presence.
|
or subscribe/unsubscribe presence.
|
||||||
By default there aren't restrictions.
|
By default there aren't restrictions.
|
||||||
|
\titem{managers} \ind{options!managers}
|
||||||
|
List of remote entities that can manage users rosters using Remote Roster Management
|
||||||
|
(\xepref{0321}).
|
||||||
|
The protocol sections implemented are:
|
||||||
|
\term{4.2. The remote entity requests current user's roster}.
|
||||||
|
\term{4.3. The user updates roster}.
|
||||||
|
\term{4.4. The remote entity updates the user's roster}.
|
||||||
|
A remote entity cab only get or modify roster items that have the same domain as the entity.
|
||||||
|
Default value is: \term{[]}.
|
||||||
\end{description}
|
\end{description}
|
||||||
|
|
||||||
This example configuration enables Roster Versioning with storage of current id:
|
This example configuration enables Roster Versioning with storage of current id.
|
||||||
|
The ICQ and MSN transports can get ICQ and MSN contacts, add them, or remove them for any local account:
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
modules:
|
modules:
|
||||||
...
|
...
|
||||||
mod_roster:
|
mod_roster:
|
||||||
versioning: true
|
versioning: true
|
||||||
store_current_id: true
|
store_current_id: true
|
||||||
|
managers:
|
||||||
|
- "icq.example.org"
|
||||||
|
- "msn.example.org"
|
||||||
...
|
...
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
@ -130,6 +130,9 @@ stop(Host) ->
|
|||||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
|
||||||
?NS_ROSTER).
|
?NS_ROSTER).
|
||||||
|
|
||||||
|
process_iq(From, To, IQ) when ((From#jid.luser == <<"">>) andalso (From#jid.resource == <<"">>)) ->
|
||||||
|
process_iq_manager(From, To, IQ);
|
||||||
|
|
||||||
process_iq(From, To, IQ) ->
|
process_iq(From, To, IQ) ->
|
||||||
#iq{sub_el = SubEl} = IQ,
|
#iq{sub_el = SubEl} = IQ,
|
||||||
#jid{lserver = LServer} = From,
|
#jid{lserver = LServer} = From,
|
||||||
@ -465,15 +468,16 @@ try_process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
|||||||
process_iq_set(From, To, IQ)
|
process_iq_set(From, To, IQ)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_iq_set(From, To, #iq{sub_el = SubEl} = IQ) ->
|
process_iq_set(From, To, #iq{sub_el = SubEl, id = Id} = IQ) ->
|
||||||
#xmlel{children = Els} = SubEl,
|
#xmlel{children = Els} = SubEl,
|
||||||
lists:foreach(fun (El) -> process_item_set(From, To, El)
|
Managed = is_managed_from_id(Id),
|
||||||
|
lists:foreach(fun (El) -> process_item_set(From, To, El, Managed)
|
||||||
end,
|
end,
|
||||||
Els),
|
Els),
|
||||||
IQ#iq{type = result, sub_el = []}.
|
IQ#iq{type = result, sub_el = []}.
|
||||||
|
|
||||||
process_item_set(From, To,
|
process_item_set(From, To,
|
||||||
#xmlel{attrs = Attrs, children = Els}) ->
|
#xmlel{attrs = Attrs, children = Els}, Managed) ->
|
||||||
JID1 = jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
|
JID1 = jlib:string_to_jid(xml:get_attr_s(<<"jid">>,
|
||||||
Attrs)),
|
Attrs)),
|
||||||
#jid{user = User, luser = LUser, lserver = LServer} =
|
#jid{user = User, luser = LUser, lserver = LServer} =
|
||||||
@ -484,12 +488,13 @@ process_item_set(From, To,
|
|||||||
LJID = jlib:jid_tolower(JID1),
|
LJID = jlib:jid_tolower(JID1),
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
Item = get_roster_by_jid_t(LUser, LServer, LJID),
|
Item = get_roster_by_jid_t(LUser, LServer, LJID),
|
||||||
Item1 = process_item_attrs(Item, Attrs),
|
Item1 = process_item_attrs_managed(Item, Attrs, Managed),
|
||||||
Item2 = process_item_els(Item1, Els),
|
Item2 = process_item_els(Item1, Els),
|
||||||
case Item2#roster.subscription of
|
case Item2#roster.subscription of
|
||||||
remove -> del_roster_t(LUser, LServer, LJID);
|
remove -> del_roster_t(LUser, LServer, LJID);
|
||||||
_ -> update_roster_t(LUser, LServer, LJID, Item2)
|
_ -> update_roster_t(LUser, LServer, LJID, Item2)
|
||||||
end,
|
end,
|
||||||
|
send_itemset_to_managers(From, Item2, Managed),
|
||||||
Item3 = ejabberd_hooks:run_fold(roster_process_item,
|
Item3 = ejabberd_hooks:run_fold(roster_process_item,
|
||||||
LServer, Item2,
|
LServer, Item2,
|
||||||
[LServer]),
|
[LServer]),
|
||||||
@ -511,7 +516,7 @@ process_item_set(From, To,
|
|||||||
?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok
|
?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok
|
||||||
end
|
end
|
||||||
end;
|
end;
|
||||||
process_item_set(_From, _To, _) -> ok.
|
process_item_set(_From, _To, _, _Managed) -> ok.
|
||||||
|
|
||||||
process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
|
process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
|
||||||
case Attr of
|
case Attr of
|
||||||
@ -1554,6 +1559,91 @@ webadmin_user(Acc, _User, _Server, Lang) ->
|
|||||||
Acc ++
|
Acc ++
|
||||||
[?XE(<<"h3">>, [?ACT(<<"roster/">>, <<"Roster">>)])].
|
[?XE(<<"h3">>, [?ACT(<<"roster/">>, <<"Roster">>)])].
|
||||||
|
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
%% Implement XEP-0321 Remote Roster Management
|
||||||
|
|
||||||
|
process_iq_manager(From, To, IQ) ->
|
||||||
|
%% Check what access is allowed for From to To
|
||||||
|
MatchDomain = From#jid.lserver,
|
||||||
|
case is_domain_managed(MatchDomain, To#jid.lserver) of
|
||||||
|
true ->
|
||||||
|
process_iq_manager2(MatchDomain, To, IQ);
|
||||||
|
false ->
|
||||||
|
#iq{sub_el = SubEl} = IQ,
|
||||||
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_BAD_REQUEST]}
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_iq_manager2(MatchDomain, To, IQ) ->
|
||||||
|
%% If IQ is SET, filter the input IQ
|
||||||
|
IQFiltered = maybe_filter_request(MatchDomain, IQ),
|
||||||
|
%% Call the standard function with reversed JIDs
|
||||||
|
IdInitial = IQFiltered#iq.id,
|
||||||
|
ResIQ = process_iq(To, To, IQFiltered#iq{id = <<"roster-remotely-managed">>}),
|
||||||
|
%% Filter the output IQ
|
||||||
|
filter_stanza(MatchDomain, ResIQ#iq{id = IdInitial}).
|
||||||
|
|
||||||
|
is_domain_managed(ContactHost, UserHost) ->
|
||||||
|
Managers = gen_mod:get_module_opt(UserHost, ?MODULE, managers,
|
||||||
|
fun(B) when is_list(B) -> B end,
|
||||||
|
[]),
|
||||||
|
lists:member(ContactHost, Managers).
|
||||||
|
|
||||||
|
maybe_filter_request(MatchDomain, IQ) when IQ#iq.type == set ->
|
||||||
|
filter_stanza(MatchDomain, IQ);
|
||||||
|
maybe_filter_request(_MatchDomain, IQ) ->
|
||||||
|
IQ.
|
||||||
|
|
||||||
|
filter_stanza(_MatchDomain, #iq{sub_el = []} = IQ) ->
|
||||||
|
IQ;
|
||||||
|
filter_stanza(MatchDomain, #iq{sub_el = [SubEl | _]} = IQ) ->
|
||||||
|
#iq{sub_el = SubElFiltered} = IQRes =
|
||||||
|
filter_stanza(MatchDomain, IQ#iq{sub_el = SubEl}),
|
||||||
|
IQRes#iq{sub_el = [SubElFiltered]};
|
||||||
|
filter_stanza(MatchDomain, #iq{sub_el = SubEl} = IQ) ->
|
||||||
|
#xmlel{name = Type, attrs = Attrs, children = Items} = SubEl,
|
||||||
|
ItemsFiltered = lists:filter(
|
||||||
|
fun(Item) ->
|
||||||
|
is_item_of_domain(MatchDomain, Item) end, Items),
|
||||||
|
SubElFiltered = #xmlel{name=Type, attrs = Attrs, children = ItemsFiltered},
|
||||||
|
IQ#iq{sub_el = SubElFiltered}.
|
||||||
|
|
||||||
|
is_item_of_domain(MatchDomain, #xmlel{} = El) ->
|
||||||
|
lists:any(fun(Attr) -> is_jid_of_domain(MatchDomain, Attr) end, El#xmlel.attrs);
|
||||||
|
is_item_of_domain(_MatchDomain, {xmlcdata, _}) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
is_jid_of_domain(MatchDomain, {<<"jid">>, JIDString}) ->
|
||||||
|
case jlib:string_to_jid(JIDString) of
|
||||||
|
JID when JID#jid.lserver == MatchDomain -> true;
|
||||||
|
_ -> false
|
||||||
|
end;
|
||||||
|
is_jid_of_domain(_, _) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
process_item_attrs_managed(Item, Attrs, true) ->
|
||||||
|
process_item_attrs_ws(Item, Attrs);
|
||||||
|
process_item_attrs_managed(Item, _Attrs, false) ->
|
||||||
|
process_item_attrs(Item, _Attrs).
|
||||||
|
|
||||||
|
send_itemset_to_managers(_From, _Item, true) ->
|
||||||
|
ok;
|
||||||
|
send_itemset_to_managers(From, Item, false) ->
|
||||||
|
{_, UserHost} = Item#roster.us,
|
||||||
|
{_ContactUser, ContactHost, _ContactResource} = Item#roster.jid,
|
||||||
|
%% Check if the component is an allowed manager
|
||||||
|
IsManager = is_domain_managed(ContactHost, UserHost),
|
||||||
|
case IsManager of
|
||||||
|
true -> push_item(<<"">>, ContactHost, <<"">>, From, Item);
|
||||||
|
false -> ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_managed_from_id(<<"roster-remotely-managed">>) ->
|
||||||
|
true;
|
||||||
|
is_managed_from_id(_Id) ->
|
||||||
|
false.
|
||||||
|
|
||||||
|
|
||||||
export(_Server) ->
|
export(_Server) ->
|
||||||
[{roster,
|
[{roster,
|
||||||
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
|
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
|
||||||
|
Loading…
Reference in New Issue
Block a user