mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-20 17:27:00 +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,
|
||||
or subscribe/unsubscribe presence.
|
||||
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}
|
||||
|
||||
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}
|
||||
modules:
|
||||
...
|
||||
mod_roster:
|
||||
versioning: true
|
||||
store_current_id: true
|
||||
managers:
|
||||
- "icq.example.org"
|
||||
- "msn.example.org"
|
||||
...
|
||||
\end{verbatim}
|
||||
|
||||
|
@ -130,6 +130,9 @@ stop(Host) ->
|
||||
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
|
||||
?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) ->
|
||||
#iq{sub_el = SubEl} = IQ,
|
||||
#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)
|
||||
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,
|
||||
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,
|
||||
Els),
|
||||
IQ#iq{type = result, sub_el = []}.
|
||||
|
||||
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">>,
|
||||
Attrs)),
|
||||
#jid{user = User, luser = LUser, lserver = LServer} =
|
||||
@ -484,12 +488,13 @@ process_item_set(From, To,
|
||||
LJID = jlib:jid_tolower(JID1),
|
||||
F = fun () ->
|
||||
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),
|
||||
case Item2#roster.subscription of
|
||||
remove -> del_roster_t(LUser, LServer, LJID);
|
||||
_ -> update_roster_t(LUser, LServer, LJID, Item2)
|
||||
end,
|
||||
send_itemset_to_managers(From, Item2, Managed),
|
||||
Item3 = ejabberd_hooks:run_fold(roster_process_item,
|
||||
LServer, Item2,
|
||||
[LServer]),
|
||||
@ -511,7 +516,7 @@ process_item_set(From, To,
|
||||
?DEBUG("ROSTER: roster item set error: ~p~n", [E]), ok
|
||||
end
|
||||
end;
|
||||
process_item_set(_From, _To, _) -> ok.
|
||||
process_item_set(_From, _To, _, _Managed) -> ok.
|
||||
|
||||
process_item_attrs(Item, [{Attr, Val} | Attrs]) ->
|
||||
case Attr of
|
||||
@ -1554,6 +1559,91 @@ webadmin_user(Acc, _User, _Server, Lang) ->
|
||||
Acc ++
|
||||
[?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) ->
|
||||
[{roster,
|
||||
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
|
||||
|
Loading…
Reference in New Issue
Block a user