diff options
Diffstat (limited to 'src/mod_blocking.erl')
-rw-r--r-- | src/mod_blocking.erl | 408 |
1 files changed, 209 insertions, 199 deletions
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index 818d53259..3ddb10a6d 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -5,7 +5,7 @@ %%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net> %%% %%% -%%% ejabberd, Copyright (C) 2002-2016 ProcessOne +%%% ejabberd, Copyright (C) 2002-2019 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -29,229 +29,239 @@ -protocol({xep, 191, '1.2'}). --export([start/2, stop/1, process_iq/3, - process_iq_set/4, process_iq_get/5, mod_opt_type/1, depends/2]). +-export([start/2, stop/1, reload/3, process_iq/1, depends/2, + disco_features/5, mod_options/1]). --include("ejabberd.hrl"). -include("logger.hrl"). - --include("jlib.hrl"). - +-include("xmpp.hrl"). -include("mod_privacy.hrl"). +-include("translate.hrl"). --callback process_blocklist_block(binary(), binary(), function()) -> {atomic, any()}. --callback unblock_by_filter(binary(), binary(), function()) -> {atomic, any()}. --callback process_blocklist_get(binary(), binary()) -> [listitem()] | error. - -start(Host, Opts) -> - IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1, - one_queue), - ejabberd_hooks:add(privacy_iq_get, Host, ?MODULE, - process_iq_get, 40), - ejabberd_hooks:add(privacy_iq_set, Host, ?MODULE, - process_iq_set, 40), - mod_disco:register_feature(Host, ?NS_BLOCKING), +start(Host, _Opts) -> + ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, - ?NS_BLOCKING, ?MODULE, process_iq, IQDisc). + ?NS_BLOCKING, ?MODULE, process_iq). stop(Host) -> - ejabberd_hooks:delete(privacy_iq_get, Host, ?MODULE, - process_iq_get, 40), - ejabberd_hooks:delete(privacy_iq_set, Host, ?MODULE, - process_iq_set, 40), - mod_disco:unregister_feature(Host, ?NS_BLOCKING), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, - ?NS_BLOCKING). + ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 50), + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_BLOCKING). + +reload(_Host, _NewOpts, _OldOpts) -> + ok. depends(_Host, _Opts) -> [{mod_privacy, hard}]. -process_iq(_From, _To, IQ) -> - SubEl = IQ#iq.sub_el, - IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}. +-spec disco_features({error, stanza_error()} | {result, [binary()]} | empty, + jid(), jid(), binary(), binary()) -> + {error, stanza_error()} | {result, [binary()]}. +disco_features({error, Err}, _From, _To, _Node, _Lang) -> + {error, Err}; +disco_features(empty, _From, _To, <<"">>, _Lang) -> + {result, [?NS_BLOCKING]}; +disco_features({result, Feats}, _From, _To, <<"">>, _Lang) -> + {result, [?NS_BLOCKING|Feats]}; +disco_features(Acc, _From, _To, _Node, _Lang) -> + Acc. -process_iq_get(_, From, _To, - #iq{xmlns = ?NS_BLOCKING, lang = Lang, - sub_el = #xmlel{name = <<"blocklist">>}}, - _) -> - #jid{luser = LUser, lserver = LServer} = From, - {stop, process_blocklist_get(LUser, LServer, Lang)}; -process_iq_get(Acc, _, _, _, _) -> Acc. +-spec process_iq(iq()) -> iq(). +process_iq(#iq{type = Type, + from = #jid{luser = U, lserver = S}, + to = #jid{luser = U, lserver = S}} = IQ) -> + case Type of + get -> process_iq_get(IQ); + set -> process_iq_set(IQ) + end; +process_iq(#iq{lang = Lang} = IQ) -> + Txt = ?T("Query to another users is forbidden"), + xmpp:make_error(IQ, xmpp:err_forbidden(Txt, Lang)). -process_iq_set(_, From, _To, - #iq{xmlns = ?NS_BLOCKING, lang = Lang, - sub_el = - #xmlel{name = SubElName, children = SubEls}}) -> - #jid{luser = LUser, lserver = LServer} = From, - Res = case {SubElName, fxml:remove_cdata(SubEls)} of - {<<"block">>, []} -> - Txt = <<"No items found in this query">>, - {error, ?ERRT_BAD_REQUEST(Lang, Txt)}; - {<<"block">>, Els} -> - JIDs = parse_blocklist_items(Els, []), - process_blocklist_block(LUser, LServer, JIDs, Lang); - {<<"unblock">>, []} -> - process_blocklist_unblock_all(LUser, LServer, Lang); - {<<"unblock">>, Els} -> - JIDs = parse_blocklist_items(Els, []), - process_blocklist_unblock(LUser, LServer, JIDs, Lang); - _ -> - Txt = <<"Unknown blocking command">>, - {error, ?ERRT_BAD_REQUEST(Lang, Txt)} - end, - {stop, Res}; -process_iq_set(Acc, _, _, _) -> Acc. +-spec process_iq_get(iq()) -> iq(). +process_iq_get(#iq{sub_els = [#block_list{}]} = IQ) -> + process_get(IQ); +process_iq_get(#iq{lang = Lang} = IQ) -> + Txt = ?T("No module is handling this query"), + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)). + +-spec process_iq_set(iq()) -> iq(). +process_iq_set(#iq{lang = Lang, sub_els = [SubEl]} = IQ) -> + case SubEl of + #block{items = []} -> + Txt = ?T("No items found in this query"), + xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang)); + #block{items = Items} -> + JIDs = [jid:tolower(JID) || #block_item{jid = JID} <- Items], + process_block(IQ, JIDs); + #unblock{items = []} -> + process_unblock_all(IQ); + #unblock{items = Items} -> + JIDs = [jid:tolower(JID) || #block_item{jid = JID} <- Items], + process_unblock(IQ, JIDs); + _ -> + Txt = ?T("No module is handling this query"), + xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)) + end. -list_to_blocklist_jids([], JIDs) -> JIDs; -list_to_blocklist_jids([#listitem{type = jid, - action = deny, value = JID} = - Item - | Items], +-spec listitems_to_jids([listitem()], [ljid()]) -> [ljid()]. +listitems_to_jids([], JIDs) -> + JIDs; +listitems_to_jids([#listitem{type = jid, + action = deny, value = JID} = Item | Items], JIDs) -> - case Item of - #listitem{match_all = true} -> Match = true; - #listitem{match_iq = true, match_message = true, - match_presence_in = true, match_presence_out = true} -> - Match = true; - _ -> Match = false - end, - if Match -> list_to_blocklist_jids(Items, [JID | JIDs]); - true -> list_to_blocklist_jids(Items, JIDs) + Match = case Item of + #listitem{match_all = true} -> + true; + #listitem{match_iq = true, + match_message = true, + match_presence_in = true, + match_presence_out = true} -> + true; + _ -> + false + end, + if Match -> listitems_to_jids(Items, [JID | JIDs]); + true -> listitems_to_jids(Items, JIDs) end; % Skip Privacy List items than cannot be mapped to Blocking items -list_to_blocklist_jids([_ | Items], JIDs) -> - list_to_blocklist_jids(Items, JIDs). - -parse_blocklist_items([], JIDs) -> JIDs; -parse_blocklist_items([#xmlel{name = <<"item">>, - attrs = Attrs} - | Els], - 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). +listitems_to_jids([_ | Items], JIDs) -> + listitems_to_jids(Items, JIDs). -process_blocklist_block(LUser, LServer, JIDs, Lang) -> - Filter = fun (List) -> - AlreadyBlocked = list_to_blocklist_jids(List, []), - lists:foldr(fun (JID, List1) -> - case lists:member(JID, AlreadyBlocked) - of - true -> List1; - false -> - [#listitem{type = jid, - value = JID, - action = deny, - order = 0, - match_all = true} - | List1] - end - end, - List, JIDs) - end, - Mod = db_mod(LServer), - case Mod:process_blocklist_block(LUser, LServer, Filter) of - {atomic, {ok, Default, List}} -> - UserList = make_userlist(Default, List), - broadcast_list_update(LUser, LServer, Default, - UserList), - broadcast_blocklist_event(LUser, LServer, - {block, JIDs}), - {result, [], UserList}; - _Err -> - ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]), - {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)} +-spec process_block(iq(), [ljid()]) -> iq(). +process_block(#iq{from = From} = IQ, LJIDs) -> + #jid{luser = LUser, lserver = LServer} = From, + case mod_privacy:get_user_list(LUser, LServer, default) of + {error, _} -> + err_db_failure(IQ); + Res -> + {Name, List} = case Res of + error -> {<<"Blocked contacts">>, []}; + {ok, NameList} -> NameList + end, + AlreadyBlocked = listitems_to_jids(List, []), + NewList = lists:foldr( + fun(LJID, List1) -> + case lists:member(LJID, AlreadyBlocked) of + true -> + List1; + false -> + [#listitem{type = jid, + value = LJID, + action = deny, + order = 0, + match_all = true}|List1] + end + end, List, LJIDs), + case mod_privacy:set_list(LUser, LServer, Name, NewList) of + ok -> + case (if Res == error -> + mod_privacy:set_default_list( + LUser, LServer, Name); + true -> + ok + end) of + ok -> + mod_privacy:push_list_update(From, Name), + Items = [#block_item{jid = jid:make(LJID)} + || LJID <- LJIDs], + broadcast_event(From, #block{items = Items}), + xmpp:make_iq_result(IQ); + {error, notfound} -> + ?ERROR_MSG("Failed to set default list '~ts': " + "the list should exist, but not found", + [Name]), + err_db_failure(IQ); + {error, _} -> + err_db_failure(IQ) + end; + {error, _} -> + err_db_failure(IQ) + end end. -process_blocklist_unblock_all(LUser, LServer, Lang) -> - Filter = fun (List) -> - lists:filter(fun (#listitem{action = A}) -> A =/= deny - end, - List) - end, - Mod = db_mod(LServer), - case Mod:unblock_by_filter(LUser, LServer, Filter) of - {atomic, ok} -> {result, []}; - {atomic, {ok, Default, List}} -> - UserList = make_userlist(Default, List), - broadcast_list_update(LUser, LServer, Default, - UserList), - broadcast_blocklist_event(LUser, LServer, unblock_all), - {result, [], UserList}; - _Err -> - ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]), - {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)} +-spec process_unblock_all(iq()) -> iq(). +process_unblock_all(#iq{from = From} = IQ) -> + #jid{luser = LUser, lserver = LServer} = From, + case mod_privacy:get_user_list(LUser, LServer, default) of + {ok, {Name, List}} -> + NewList = lists:filter( + fun(#listitem{action = A}) -> + A /= deny + end, List), + case mod_privacy:set_list(LUser, LServer, Name, NewList) of + ok -> + mod_privacy:push_list_update(From, Name), + broadcast_event(From, #unblock{}), + xmpp:make_iq_result(IQ); + {error, _} -> + err_db_failure(IQ) + end; + error -> + broadcast_event(From, #unblock{}), + xmpp:make_iq_result(IQ); + {error, _} -> + err_db_failure(IQ) end. -process_blocklist_unblock(LUser, LServer, JIDs, Lang) -> - Filter = fun (List) -> - lists:filter(fun (#listitem{action = deny, type = jid, - value = JID}) -> - not lists:member(JID, JIDs); - (_) -> true - end, - List) - end, - Mod = db_mod(LServer), - case Mod:unblock_by_filter(LUser, LServer, Filter) of - {atomic, ok} -> {result, []}; - {atomic, {ok, Default, List}} -> - UserList = make_userlist(Default, List), - broadcast_list_update(LUser, LServer, Default, - UserList), - broadcast_blocklist_event(LUser, LServer, - {unblock, JIDs}), - {result, [], UserList}; - _Err -> - ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]), - {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)} +-spec process_unblock(iq(), [ljid()]) -> iq(). +process_unblock(#iq{from = From} = IQ, LJIDs) -> + #jid{luser = LUser, lserver = LServer} = From, + case mod_privacy:get_user_list(LUser, LServer, default) of + {ok, {Name, List}} -> + NewList = lists:filter( + fun(#listitem{action = deny, type = jid, + value = LJID}) -> + not lists:member(LJID, LJIDs); + (_) -> + true + end, List), + case mod_privacy:set_list(LUser, LServer, Name, NewList) of + ok -> + mod_privacy:push_list_update(From, Name), + Items = [#block_item{jid = jid:make(LJID)} + || LJID <- LJIDs], + broadcast_event(From, #unblock{items = Items}), + xmpp:make_iq_result(IQ); + {error, _} -> + err_db_failure(IQ) + end; + error -> + Items = [#block_item{jid = jid:make(LJID)} + || LJID <- LJIDs], + broadcast_event(From, #unblock{items = Items}), + xmpp:make_iq_result(IQ); + {error, _} -> + err_db_failure(IQ) end. -make_userlist(Name, List) -> - NeedDb = mod_privacy:is_list_needdb(List), - #userlist{name = Name, list = List, needdb = NeedDb}. - -broadcast_list_update(LUser, LServer, Name, UserList) -> - ejabberd_sm:route(jid:make(LUser, LServer, - <<"">>), - jid:make(LUser, LServer, <<"">>), - {broadcast, {privacy_list, UserList, Name}}). - -broadcast_blocklist_event(LUser, LServer, Event) -> - JID = jid:make(LUser, LServer, <<"">>), - ejabberd_sm:route(JID, JID, - {broadcast, {blocking, Event}}). +-spec broadcast_event(jid(), block() | unblock()) -> ok. +broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) -> + BFrom = jid:remove_resource(From), + lists:foreach( + fun(R) -> + To = jid:replace_resource(From, R), + IQ = #iq{type = set, from = BFrom, to = To, + id = <<"push", (p1_rand:get_string())/binary>>, + sub_els = [Event]}, + ejabberd_router:route(IQ) + end, ejabberd_sm:get_user_resources(LUser, LServer)). -process_blocklist_get(LUser, LServer, Lang) -> - Mod = db_mod(LServer), - case Mod:process_blocklist_get(LUser, LServer) of - error -> - {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}; - List -> - JIDs = list_to_blocklist_jids(List, []), - Items = lists:map(fun (JID) -> - ?DEBUG("JID: ~p", [JID]), - #xmlel{name = <<"item">>, - attrs = - [{<<"jid">>, - jid:to_string(JID)}], - children = []} - end, - JIDs), - {result, - [#xmlel{name = <<"blocklist">>, - attrs = [{<<"xmlns">>, ?NS_BLOCKING}], - children = Items}]} +-spec process_get(iq()) -> iq(). +process_get(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) -> + case mod_privacy:get_user_list(LUser, LServer, default) of + {ok, {_, List}} -> + LJIDs = listitems_to_jids(List, []), + Items = [#block_item{jid = jid:make(J)} || J <- LJIDs], + xmpp:make_iq_result(IQ, #block_list{items = Items}); + error -> + xmpp:make_iq_result(IQ, #block_list{}); + {error, _} -> + err_db_failure(IQ) end. -db_mod(LServer) -> - DBType = gen_mod:db_type(LServer, mod_privacy), - gen_mod:db_mod(DBType, ?MODULE). +-spec err_db_failure(iq()) -> iq(). +err_db_failure(#iq{lang = Lang} = IQ) -> + Txt = ?T("Database failure"), + xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)). -mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1; -mod_opt_type(_) -> [iqdisc]. +mod_options(_Host) -> + []. |