Rewrite mod_blocking to use XML generator

This commit is contained in:
Evgeniy Khramtsov 2016-07-19 10:07:04 +03:00
parent a4a9dd7f03
commit bc802a4049
5 changed files with 140 additions and 90 deletions

View File

@ -347,7 +347,7 @@
text = [] :: [#text{}]}). text = [] :: [#text{}]}).
-type sasl_failure() :: #sasl_failure{}. -type sasl_failure() :: #sasl_failure{}.
-record(block_list, {}). -record(block_list, {items = [] :: [any()]}).
-type block_list() :: #block_list{}. -type block_list() :: #block_list{}.
-record(xdata_field, {label :: binary(), -record(xdata_field, {label :: binary(),

View File

@ -29,13 +29,13 @@
-protocol({xep, 191, '1.2'}). -protocol({xep, 191, '1.2'}).
-export([start/2, stop/1, process_iq/3, -export([start/2, stop/1, process_iq/1,
process_iq_set/4, process_iq_get/5, mod_opt_type/1, depends/2]). process_iq_set/2, process_iq_get/3, mod_opt_type/1, depends/2]).
-include("ejabberd.hrl"). -include("ejabberd.hrl").
-include("logger.hrl"). -include("logger.hrl").
-include("jlib.hrl"). -include("xmpp.hrl").
-include("mod_privacy.hrl"). -include("mod_privacy.hrl").
@ -43,6 +43,8 @@
-callback unblock_by_filter(binary(), binary(), function()) -> {atomic, any()}. -callback unblock_by_filter(binary(), binary(), function()) -> {atomic, any()}.
-callback process_blocklist_get(binary(), binary()) -> [listitem()] | error. -callback process_blocklist_get(binary(), binary()) -> [listitem()] | error.
-type block_event() :: {block, [jid()]} | {unblock, [jid()]} | unblock_all.
start(Host, Opts) -> start(Host, Opts) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
one_queue), one_queue),
@ -66,55 +68,65 @@ stop(Host) ->
depends(_Host, _Opts) -> depends(_Host, _Opts) ->
[{mod_privacy, hard}]. [{mod_privacy, hard}].
process_iq(_From, _To, IQ) -> -spec process_iq(iq()) -> iq().
SubEl = IQ#iq.sub_el, process_iq(IQ) ->
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}. xmpp:make_error(IQ, xmpp:err_not_allowed()).
process_iq_get(_, From, _To, -spec process_iq_get({error, error()} | {result, xmpp_element() | undefined},
#iq{xmlns = ?NS_BLOCKING, lang = Lang, iq(), userlist()) ->
sub_el = #xmlel{name = <<"blocklist">>}}, {error, error()} | {result, block_list()}.
_) -> process_iq_get(_, #iq{lang = Lang, from = From,
sub_els = [#block_list{}]}, _) ->
#jid{luser = LUser, lserver = LServer} = From, #jid{luser = LUser, lserver = LServer} = From,
{stop, process_blocklist_get(LUser, LServer, Lang)}; {stop, process_blocklist_get(LUser, LServer, Lang)};
process_iq_get(Acc, _, _, _, _) -> Acc. process_iq_get(Acc, _, _) -> Acc.
process_iq_set(_, From, _To, -spec process_iq_set({error, error()} |
#iq{xmlns = ?NS_BLOCKING, lang = Lang, {result, xmpp_element() | undefined} |
sub_el = {result, xmpp_element() | undefined, userlist()},
#xmlel{name = SubElName, children = SubEls}}) -> iq()) -> {error, error()} |
{result, undefined} |
{result, undefined, userlist()}.
process_iq_set(_, #iq{from = From, lang = Lang, sub_els = [SubEl]}) ->
#jid{luser = LUser, lserver = LServer} = From, #jid{luser = LUser, lserver = LServer} = From,
Res = case {SubElName, fxml:remove_cdata(SubEls)} of Res = case SubEl of
{<<"block">>, []} -> #block{items = []} ->
Txt = <<"No items found in this query">>, Txt = <<"No items found in this query">>,
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}; {error, xmpp:err_bad_request(Txt, Lang)};
{<<"block">>, Els} -> #block{items = Items} ->
JIDs = parse_blocklist_items(Els, []), JIDs = [jid:tolower(Item) || Item <- Items],
process_blocklist_block(LUser, LServer, JIDs, Lang); process_blocklist_block(LUser, LServer, JIDs, Lang);
{<<"unblock">>, []} -> #unblock{items = []} ->
process_blocklist_unblock_all(LUser, LServer, Lang); process_blocklist_unblock_all(LUser, LServer, Lang);
{<<"unblock">>, Els} -> #unblock{items = Items} ->
JIDs = parse_blocklist_items(Els, []), JIDs = [jid:tolower(Item) || Item <- Items],
process_blocklist_unblock(LUser, LServer, JIDs, Lang); process_blocklist_unblock(LUser, LServer, JIDs, Lang);
_ -> _ ->
Txt = <<"Unknown blocking command">>, Txt = <<"Only <block/> and <unblock/> are allowed "
{error, ?ERRT_BAD_REQUEST(Lang, Txt)} "in this request">>,
{error, xmpp:err_bad_request(Txt, Lang)}
end, end,
{stop, Res}; {stop, Res};
process_iq_set(Acc, _, _, _) -> Acc. process_iq_set(Acc, _) -> Acc.
-spec list_to_blocklist_jids([listitem()], [ljid()]) -> [ljid()].
list_to_blocklist_jids([], JIDs) -> JIDs; list_to_blocklist_jids([], JIDs) -> JIDs;
list_to_blocklist_jids([#listitem{type = jid, list_to_blocklist_jids([#listitem{type = jid,
action = deny, value = JID} = action = deny, value = JID} =
Item Item
| Items], | Items],
JIDs) -> JIDs) ->
case Item of Match = case Item of
#listitem{match_all = true} -> Match = true; #listitem{match_all = true} ->
#listitem{match_iq = true, match_message = true, true;
match_presence_in = true, match_presence_out = true} -> #listitem{match_iq = true,
Match = true; match_message = true,
_ -> Match = false match_presence_in = true,
end, match_presence_out = true} ->
true;
_ ->
false
end,
if Match -> list_to_blocklist_jids(Items, [JID | JIDs]); if Match -> list_to_blocklist_jids(Items, [JID | JIDs]);
true -> list_to_blocklist_jids(Items, JIDs) true -> list_to_blocklist_jids(Items, JIDs)
end; end;
@ -122,20 +134,10 @@ list_to_blocklist_jids([#listitem{type = jid,
list_to_blocklist_jids([_ | Items], JIDs) -> list_to_blocklist_jids([_ | Items], JIDs) ->
list_to_blocklist_jids(Items, JIDs). list_to_blocklist_jids(Items, JIDs).
parse_blocklist_items([], JIDs) -> JIDs; -spec process_blocklist_block(binary(), binary(), [ljid()],
parse_blocklist_items([#xmlel{name = <<"item">>, undefined | binary()) ->
attrs = Attrs} {error, error()} |
| Els], {result, undefined, userlist()}.
JIDs) ->
case fxml:get_attr(<<"jid">>, Attrs) of
{value, JID1} ->
JID = jid:tolower(jid:from_string(JID1)),
parse_blocklist_items(Els, [JID | JIDs]);
false -> parse_blocklist_items(Els, JIDs)
end;
parse_blocklist_items([_ | Els], JIDs) ->
parse_blocklist_items(Els, JIDs).
process_blocklist_block(LUser, LServer, JIDs, Lang) -> process_blocklist_block(LUser, LServer, JIDs, Lang) ->
Filter = fun (List) -> Filter = fun (List) ->
AlreadyBlocked = list_to_blocklist_jids(List, []), AlreadyBlocked = list_to_blocklist_jids(List, []),
@ -161,13 +163,17 @@ process_blocklist_block(LUser, LServer, JIDs, Lang) ->
broadcast_list_update(LUser, LServer, Default, broadcast_list_update(LUser, LServer, Default,
UserList), UserList),
broadcast_blocklist_event(LUser, LServer, broadcast_blocklist_event(LUser, LServer,
{block, JIDs}), {block, [jid:make(J) || J <- JIDs]}),
{result, [], UserList}; {result, undefined, UserList};
_Err -> _Err ->
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]), ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)} {error, xmpp:err_internal_server_error(<<"Database failure">>, Lang)}
end. end.
-spec process_blocklist_unblock_all(binary(), binary(), undefined | binary()) ->
{error, error()} |
{result, undefined} |
{result, undefined, userlist()}.
process_blocklist_unblock_all(LUser, LServer, Lang) -> process_blocklist_unblock_all(LUser, LServer, Lang) ->
Filter = fun (List) -> Filter = fun (List) ->
lists:filter(fun (#listitem{action = A}) -> A =/= deny lists:filter(fun (#listitem{action = A}) -> A =/= deny
@ -176,18 +182,23 @@ process_blocklist_unblock_all(LUser, LServer, Lang) ->
end, end,
Mod = db_mod(LServer), Mod = db_mod(LServer),
case Mod:unblock_by_filter(LUser, LServer, Filter) of case Mod:unblock_by_filter(LUser, LServer, Filter) of
{atomic, ok} -> {result, []}; {atomic, ok} -> {result, undefined};
{atomic, {ok, Default, List}} -> {atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List), UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, broadcast_list_update(LUser, LServer, Default,
UserList), UserList),
broadcast_blocklist_event(LUser, LServer, unblock_all), broadcast_blocklist_event(LUser, LServer, unblock_all),
{result, [], UserList}; {result, undefined, UserList};
_Err -> _Err ->
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]), ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]),
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)} {error, xmpp:err_internal_server_error(<<"Database failure">>, Lang)}
end. end.
-spec process_blocklist_unblock(binary(), binary(), [ljid()],
undefined | binary()) ->
{error, error()} |
{result, undefined} |
{result, undefined, userlist()}.
process_blocklist_unblock(LUser, LServer, JIDs, Lang) -> process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
Filter = fun (List) -> Filter = fun (List) ->
lists:filter(fun (#listitem{action = deny, type = jid, lists:filter(fun (#listitem{action = deny, type = jid,
@ -199,56 +210,53 @@ process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
end, end,
Mod = db_mod(LServer), Mod = db_mod(LServer),
case Mod:unblock_by_filter(LUser, LServer, Filter) of case Mod:unblock_by_filter(LUser, LServer, Filter) of
{atomic, ok} -> {result, []}; {atomic, ok} -> {result, undefined};
{atomic, {ok, Default, List}} -> {atomic, {ok, Default, List}} ->
UserList = make_userlist(Default, List), UserList = make_userlist(Default, List),
broadcast_list_update(LUser, LServer, Default, broadcast_list_update(LUser, LServer, Default,
UserList), UserList),
broadcast_blocklist_event(LUser, LServer, broadcast_blocklist_event(LUser, LServer,
{unblock, JIDs}), {unblock, [jid:make(J) || J <- JIDs]}),
{result, [], UserList}; {result, undefined, UserList};
_Err -> _Err ->
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]), ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)} {error, xmpp:err_internal_server_error(<<"Database failure">>, Lang)}
end. end.
-spec make_userlist(binary(), [listitem()]) -> userlist().
make_userlist(Name, List) -> make_userlist(Name, List) ->
NeedDb = mod_privacy:is_list_needdb(List), NeedDb = mod_privacy:is_list_needdb(List),
#userlist{name = Name, list = List, needdb = NeedDb}. #userlist{name = Name, list = List, needdb = NeedDb}.
-spec broadcast_list_update(binary(), binary(), binary(), userlist()) ->
{broadcast,
{privacy_list, userlist(), binary() | none}}.
broadcast_list_update(LUser, LServer, Name, UserList) -> broadcast_list_update(LUser, LServer, Name, UserList) ->
ejabberd_sm:route(jid:make(LUser, LServer, ejabberd_sm:route(jid:make(LUser, LServer, <<"">>),
<<"">>),
jid:make(LUser, LServer, <<"">>), jid:make(LUser, LServer, <<"">>),
{broadcast, {privacy_list, UserList, Name}}). {broadcast, {privacy_list, UserList, Name}}).
-spec broadcast_blocklist_event(binary(), binary(), block_event()) ->
{broadcast, {blocking, block_event()}}.
broadcast_blocklist_event(LUser, LServer, Event) -> broadcast_blocklist_event(LUser, LServer, Event) ->
JID = jid:make(LUser, LServer, <<"">>), JID = jid:make(LUser, LServer, <<"">>),
ejabberd_sm:route(JID, JID, ejabberd_sm:route(JID, JID,
{broadcast, {blocking, Event}}). {broadcast, {blocking, Event}}).
-spec process_blocklist_get(binary(), binary(), undefined | binary()) ->
{error, error()} | {result, block_list()}.
process_blocklist_get(LUser, LServer, Lang) -> process_blocklist_get(LUser, LServer, Lang) ->
Mod = db_mod(LServer), Mod = db_mod(LServer),
case Mod:process_blocklist_get(LUser, LServer) of case Mod:process_blocklist_get(LUser, LServer) of
error -> error ->
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}; {error, xmpp:err_internal_server_error(<<"Database failure">>, Lang)};
List -> List ->
JIDs = list_to_blocklist_jids(List, []), LJIDs = list_to_blocklist_jids(List, []),
Items = lists:map(fun (JID) -> Items = [jid:make(J) || J <- LJIDs],
?DEBUG("JID: ~p", [JID]), {result, #block_list{items = Items}}
#xmlel{name = <<"item">>,
attrs =
[{<<"jid">>,
jid:to_string(JID)}],
children = []}
end,
JIDs),
{result,
[#xmlel{name = <<"blocklist">>,
attrs = [{<<"xmlns">>, ?NS_BLOCKING}],
children = Items}]}
end. end.
-spec db_mod(binary()) -> module().
db_mod(LServer) -> db_mod(LServer) ->
DBType = gen_mod:db_type(LServer, mod_privacy), DBType = gen_mod:db_type(LServer, mod_privacy),
gen_mod:db_mod(DBType, ?MODULE). gen_mod:db_mod(DBType, ?MODULE).

View File

@ -100,7 +100,7 @@ stop(Host) ->
process_iq(IQ) -> process_iq(IQ) ->
xmpp:make_error(IQ, xmpp:err_not_allowed()). xmpp:make_error(IQ, xmpp:err_not_allowed()).
-spec process_iq_get({error, error()} | iq(), -spec process_iq_get({error, error()} | {result, xmpp_element() | undefined},
iq(), userlist()) -> {error, error()} | {result, privacy_query()}. iq(), userlist()) -> {error, error()} | {result, privacy_query()}.
process_iq_get(_, #iq{from = From, lang = Lang, process_iq_get(_, #iq{from = From, lang = Lang,
sub_els = [#privacy_query{lists = Lists}]}, sub_els = [#privacy_query{lists = Lists}]},
@ -215,7 +215,8 @@ decode_value(Type, Value) ->
end. end.
-spec process_iq_set({error, error()} | -spec process_iq_set({error, error()} |
{result, privacy_query() | undefined, userlist()}, {result, xmpp_element() | undefined} |
{result, xmpp_element() | undefined, userlist()},
iq()) -> {error, error()} | iq()) -> {error, error()} |
{result, undefined, userlist()}. {result, undefined, userlist()}.
process_iq_set(_, #iq{from = From, lang = Lang, process_iq_set(_, #iq{from = From, lang = Lang,

View File

@ -1899,7 +1899,7 @@ encode({block, _} = Block) ->
encode({unblock, _} = Unblock) -> encode({unblock, _} = Unblock) ->
encode_unblock(Unblock, encode_unblock(Unblock,
[{<<"xmlns">>, <<"urn:xmpp:blocking">>}]); [{<<"xmlns">>, <<"urn:xmpp:blocking">>}]);
encode({block_list} = Blocklist) -> encode({block_list, _} = Blocklist) ->
encode_block_list(Blocklist, encode_block_list(Blocklist,
[{<<"xmlns">>, <<"urn:xmpp:blocking">>}]); [{<<"xmlns">>, <<"urn:xmpp:blocking">>}]);
encode({identity, _, _, _, _} = Identity) -> encode({identity, _, _, _, _} = Identity) ->
@ -2351,7 +2351,7 @@ get_name({privacy_list, _, _}) -> <<"list">>;
get_name({privacy_query, _, _, _}) -> <<"query">>; get_name({privacy_query, _, _, _}) -> <<"query">>;
get_name({block, _}) -> <<"block">>; get_name({block, _}) -> <<"block">>;
get_name({unblock, _}) -> <<"unblock">>; get_name({unblock, _}) -> <<"unblock">>;
get_name({block_list}) -> <<"blocklist">>; get_name({block_list, _}) -> <<"blocklist">>;
get_name({identity, _, _, _, _}) -> <<"identity">>; get_name({identity, _, _, _, _}) -> <<"identity">>;
get_name({disco_info, _, _, _, _}) -> <<"query">>; get_name({disco_info, _, _, _, _}) -> <<"query">>;
get_name({disco_item, _, _, _}) -> <<"item">>; get_name({disco_item, _, _, _}) -> <<"item">>;
@ -2520,7 +2520,7 @@ get_ns({privacy_query, _, _, _}) ->
<<"jabber:iq:privacy">>; <<"jabber:iq:privacy">>;
get_ns({block, _}) -> <<"urn:xmpp:blocking">>; get_ns({block, _}) -> <<"urn:xmpp:blocking">>;
get_ns({unblock, _}) -> <<"urn:xmpp:blocking">>; get_ns({unblock, _}) -> <<"urn:xmpp:blocking">>;
get_ns({block_list}) -> <<"urn:xmpp:blocking">>; get_ns({block_list, _}) -> <<"urn:xmpp:blocking">>;
get_ns({identity, _, _, _, _}) -> get_ns({identity, _, _, _, _}) ->
<<"http://jabber.org/protocol/disco#info">>; <<"http://jabber.org/protocol/disco#info">>;
get_ns({disco_info, _, _, _, _}) -> get_ns({disco_info, _, _, _, _}) ->
@ -2796,7 +2796,7 @@ pp(privacy_list, 2) -> [name, items];
pp(privacy_query, 3) -> [lists, default, active]; pp(privacy_query, 3) -> [lists, default, active];
pp(block, 1) -> [items]; pp(block, 1) -> [items];
pp(unblock, 1) -> [items]; pp(unblock, 1) -> [items];
pp(block_list, 0) -> []; pp(block_list, 1) -> [items];
pp(identity, 4) -> [category, type, lang, name]; pp(identity, 4) -> [category, type, lang, name];
pp(disco_info, 4) -> pp(disco_info, 4) ->
[node, identities, features, xdata]; [node, identities, features, xdata];
@ -22454,13 +22454,52 @@ encode_disco_identity_attr_name(_val, _acc) ->
decode_block_list(__TopXMLNS, __IgnoreEls, decode_block_list(__TopXMLNS, __IgnoreEls,
{xmlel, <<"blocklist">>, _attrs, _els}) -> {xmlel, <<"blocklist">>, _attrs, _els}) ->
{block_list}. Items = decode_block_list_els(__TopXMLNS, __IgnoreEls,
_els, []),
{block_list, Items}.
encode_block_list({block_list}, _xmlns_attrs) -> decode_block_list_els(__TopXMLNS, __IgnoreEls, [],
_els = [], Items) ->
lists:reverse(Items);
decode_block_list_els(__TopXMLNS, __IgnoreEls,
[{xmlel, <<"item">>, _attrs, _} = _el | _els], Items) ->
case get_attr(<<"xmlns">>, _attrs) of
<<"">> when __TopXMLNS == <<"urn:xmpp:blocking">> ->
decode_block_list_els(__TopXMLNS, __IgnoreEls, _els,
case decode_block_item(__TopXMLNS, __IgnoreEls,
_el)
of
undefined -> Items;
_new_el -> [_new_el | Items]
end);
<<"urn:xmpp:blocking">> ->
decode_block_list_els(__TopXMLNS, __IgnoreEls, _els,
case decode_block_item(<<"urn:xmpp:blocking">>,
__IgnoreEls, _el)
of
undefined -> Items;
_new_el -> [_new_el | Items]
end);
_ ->
decode_block_list_els(__TopXMLNS, __IgnoreEls, _els,
Items)
end;
decode_block_list_els(__TopXMLNS, __IgnoreEls,
[_ | _els], Items) ->
decode_block_list_els(__TopXMLNS, __IgnoreEls, _els,
Items).
encode_block_list({block_list, Items}, _xmlns_attrs) ->
_els = lists:reverse('encode_block_list_$items'(Items,
[])),
_attrs = _xmlns_attrs, _attrs = _xmlns_attrs,
{xmlel, <<"blocklist">>, _attrs, _els}. {xmlel, <<"blocklist">>, _attrs, _els}.
'encode_block_list_$items'([], _acc) -> _acc;
'encode_block_list_$items'([Items | _els], _acc) ->
'encode_block_list_$items'(_els,
[encode_block_item(Items, []) | _acc]).
decode_unblock(__TopXMLNS, __IgnoreEls, decode_unblock(__TopXMLNS, __IgnoreEls,
{xmlel, <<"unblock">>, _attrs, _els}) -> {xmlel, <<"unblock">>, _attrs, _els}) ->
Items = decode_unblock_els(__TopXMLNS, __IgnoreEls, Items = decode_unblock_els(__TopXMLNS, __IgnoreEls,

View File

@ -176,7 +176,9 @@
-xml(block_list, -xml(block_list,
#elem{name = <<"blocklist">>, #elem{name = <<"blocklist">>,
xmlns = <<"urn:xmpp:blocking">>, xmlns = <<"urn:xmpp:blocking">>,
result = {block_list}}). result = {block_list, '$items'},
refs = [#ref{name = block_item,
label = '$items'}]}).
-xml(disco_identity, -xml(disco_identity,
#elem{name = <<"identity">>, #elem{name = <<"identity">>,