aboutsummaryrefslogtreecommitdiff
path: root/src/mod_blocking.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_blocking.erl')
-rw-r--r--src/mod_blocking.erl408
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) ->
+ [].