2011-05-27 11:43:52 +02:00
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
%%% File : mod_blocking.erl
|
|
|
|
%%% Author : Stephan Maka
|
|
|
|
%%% Purpose : XEP-0191: Simple Communications Blocking
|
|
|
|
%%% Created : 24 Aug 2008 by Stephan Maka <stephan@spaceboyz.net>
|
|
|
|
%%%
|
|
|
|
%%%
|
2016-01-13 12:29:14 +01:00
|
|
|
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
|
2011-05-27 11:43:52 +02:00
|
|
|
%%%
|
|
|
|
%%% This program is free software; you can redistribute it and/or
|
|
|
|
%%% modify it under the terms of the GNU General Public License as
|
|
|
|
%%% published by the Free Software Foundation; either version 2 of the
|
|
|
|
%%% License, or (at your option) any later version.
|
|
|
|
%%%
|
|
|
|
%%% This program is distributed in the hope that it will be useful,
|
|
|
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
%%% General Public License for more details.
|
|
|
|
%%%
|
2015-08-05 09:52:54 +02:00
|
|
|
%%% You should have received a copy of the GNU General Public License along
|
|
|
|
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
2011-05-27 11:43:52 +02:00
|
|
|
%%%
|
|
|
|
%%%----------------------------------------------------------------------
|
|
|
|
|
|
|
|
-module(mod_blocking).
|
|
|
|
|
|
|
|
-behaviour(gen_mod).
|
|
|
|
|
2015-05-21 17:02:36 +02:00
|
|
|
-protocol({xep, 191, '1.2'}).
|
|
|
|
|
2013-03-14 10:33:02 +01:00
|
|
|
-export([start/2, stop/1, process_iq/3,
|
2016-07-06 13:58:48 +02:00
|
|
|
process_iq_set/4, process_iq_get/5, mod_opt_type/1, depends/2]).
|
2011-05-27 11:43:52 +02:00
|
|
|
|
|
|
|
-include("ejabberd.hrl").
|
2013-04-08 11:12:54 +02:00
|
|
|
-include("logger.hrl").
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2011-05-27 11:43:52 +02:00
|
|
|
-include("jlib.hrl").
|
2013-03-14 10:33:02 +01:00
|
|
|
|
2011-05-27 11:43:52 +02:00
|
|
|
-include("mod_privacy.hrl").
|
|
|
|
|
2016-04-14 13:17:20 +02:00
|
|
|
-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.
|
|
|
|
|
2011-05-27 11:43:52 +02:00
|
|
|
start(Host, Opts) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
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),
|
2011-05-27 11:43:52 +02:00
|
|
|
mod_disco:register_feature(Host, ?NS_BLOCKING),
|
2013-03-14 10:33:02 +01:00
|
|
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
|
|
|
?NS_BLOCKING, ?MODULE, process_iq, IQDisc).
|
2011-05-27 11:43:52 +02:00
|
|
|
|
|
|
|
stop(Host) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
ejabberd_hooks:delete(privacy_iq_get, Host, ?MODULE,
|
|
|
|
process_iq_get, 40),
|
|
|
|
ejabberd_hooks:delete(privacy_iq_set, Host, ?MODULE,
|
|
|
|
process_iq_set, 40),
|
2011-05-27 11:43:52 +02:00
|
|
|
mod_disco:unregister_feature(Host, ?NS_BLOCKING),
|
2013-03-14 10:33:02 +01:00
|
|
|
gen_iq_handler:remove_iq_handler(ejabberd_sm, Host,
|
|
|
|
?NS_BLOCKING).
|
2011-05-27 11:43:52 +02:00
|
|
|
|
2016-07-06 13:58:48 +02:00
|
|
|
depends(_Host, _Opts) ->
|
|
|
|
[{mod_privacy, hard}].
|
|
|
|
|
2011-05-27 11:43:52 +02:00
|
|
|
process_iq(_From, _To, IQ) ->
|
|
|
|
SubEl = IQ#iq.sub_el,
|
|
|
|
IQ#iq{type = error, sub_el = [SubEl, ?ERR_NOT_ALLOWED]}.
|
|
|
|
|
|
|
|
process_iq_get(_, From, _To,
|
2016-03-31 10:00:29 +02:00
|
|
|
#iq{xmlns = ?NS_BLOCKING, lang = Lang,
|
2013-03-14 10:33:02 +01:00
|
|
|
sub_el = #xmlel{name = <<"blocklist">>}},
|
2011-05-27 11:43:52 +02:00
|
|
|
_) ->
|
|
|
|
#jid{luser = LUser, lserver = LServer} = From,
|
2016-03-31 10:00:29 +02:00
|
|
|
{stop, process_blocklist_get(LUser, LServer, Lang)};
|
2013-03-14 10:33:02 +01:00
|
|
|
process_iq_get(Acc, _, _, _, _) -> Acc.
|
2011-05-27 11:43:52 +02:00
|
|
|
|
2013-03-14 10:33:02 +01:00
|
|
|
process_iq_set(_, From, _To,
|
2016-03-31 10:00:29 +02:00
|
|
|
#iq{xmlns = ?NS_BLOCKING, lang = Lang,
|
2013-03-14 10:33:02 +01:00
|
|
|
sub_el =
|
|
|
|
#xmlel{name = SubElName, children = SubEls}}) ->
|
2011-05-27 11:43:52 +02:00
|
|
|
#jid{luser = LUser, lserver = LServer} = From,
|
2016-02-03 19:03:17 +01:00
|
|
|
Res = case {SubElName, fxml:remove_cdata(SubEls)} of
|
2016-03-31 10:00:29 +02:00
|
|
|
{<<"block">>, []} ->
|
|
|
|
Txt = <<"No items found in this query">>,
|
|
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)};
|
2013-03-14 10:33:02 +01:00
|
|
|
{<<"block">>, Els} ->
|
|
|
|
JIDs = parse_blocklist_items(Els, []),
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_block(LUser, LServer, JIDs, Lang);
|
2013-03-14 10:33:02 +01:00
|
|
|
{<<"unblock">>, []} ->
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_unblock_all(LUser, LServer, Lang);
|
2013-03-14 10:33:02 +01:00
|
|
|
{<<"unblock">>, Els} ->
|
|
|
|
JIDs = parse_blocklist_items(Els, []),
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_unblock(LUser, LServer, JIDs, Lang);
|
|
|
|
_ ->
|
|
|
|
Txt = <<"Unknown blocking command">>,
|
|
|
|
{error, ?ERRT_BAD_REQUEST(Lang, Txt)}
|
2013-03-14 10:33:02 +01:00
|
|
|
end,
|
2011-12-21 15:20:59 +01:00
|
|
|
{stop, Res};
|
2013-03-14 10:33:02 +01:00
|
|
|
process_iq_set(Acc, _, _, _) -> Acc.
|
2011-05-27 11:43:52 +02:00
|
|
|
|
2013-03-14 10:33:02 +01:00
|
|
|
list_to_blocklist_jids([], JIDs) -> JIDs;
|
2011-05-27 11:43:52 +02:00
|
|
|
list_to_blocklist_jids([#listitem{type = jid,
|
2013-03-14 10:33:02 +01:00
|
|
|
action = deny, value = JID} =
|
|
|
|
Item
|
|
|
|
| Items],
|
|
|
|
JIDs) ->
|
2011-05-27 11:43:52 +02:00
|
|
|
case Item of
|
2013-03-14 10:33:02 +01:00
|
|
|
#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
|
2011-05-27 11:43:52 +02:00
|
|
|
end,
|
2013-03-14 10:33:02 +01:00
|
|
|
if Match -> list_to_blocklist_jids(Items, [JID | JIDs]);
|
|
|
|
true -> list_to_blocklist_jids(Items, JIDs)
|
2011-05-27 11:43:52 +02:00
|
|
|
end;
|
|
|
|
% Skip Privacy List items than cannot be mapped to Blocking items
|
|
|
|
list_to_blocklist_jids([_ | Items], JIDs) ->
|
|
|
|
list_to_blocklist_jids(Items, JIDs).
|
|
|
|
|
2013-03-14 10:33:02 +01:00
|
|
|
parse_blocklist_items([], JIDs) -> JIDs;
|
|
|
|
parse_blocklist_items([#xmlel{name = <<"item">>,
|
|
|
|
attrs = Attrs}
|
|
|
|
| Els],
|
|
|
|
JIDs) ->
|
2016-02-03 19:03:17 +01:00
|
|
|
case fxml:get_attr(<<"jid">>, Attrs) of
|
2013-03-14 10:33:02 +01:00
|
|
|
{value, JID1} ->
|
2015-11-24 16:44:13 +01:00
|
|
|
JID = jid:tolower(jid:from_string(JID1)),
|
2013-03-14 10:33:02 +01:00
|
|
|
parse_blocklist_items(Els, [JID | JIDs]);
|
|
|
|
false -> parse_blocklist_items(Els, JIDs)
|
2011-05-27 11:43:52 +02:00
|
|
|
end;
|
|
|
|
parse_blocklist_items([_ | Els], JIDs) ->
|
|
|
|
parse_blocklist_items(Els, JIDs).
|
|
|
|
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_block(LUser, LServer, JIDs, Lang) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
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,
|
2016-04-14 13:17:20 +02:00
|
|
|
Mod = db_mod(LServer),
|
|
|
|
case Mod:process_blocklist_block(LUser, LServer, Filter) of
|
2013-03-14 10:33:02 +01:00
|
|
|
{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};
|
2015-03-13 11:05:16 +01:00
|
|
|
_Err ->
|
|
|
|
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
|
2016-03-31 10:00:29 +02:00
|
|
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
2012-04-27 11:52:05 +02:00
|
|
|
end.
|
|
|
|
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_unblock_all(LUser, LServer, Lang) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
Filter = fun (List) ->
|
|
|
|
lists:filter(fun (#listitem{action = A}) -> A =/= deny
|
|
|
|
end,
|
|
|
|
List)
|
|
|
|
end,
|
2016-04-14 13:17:20 +02:00
|
|
|
Mod = db_mod(LServer),
|
|
|
|
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
2013-03-14 10:33:02 +01:00
|
|
|
{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};
|
2015-03-13 11:05:16 +01:00
|
|
|
_Err ->
|
|
|
|
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]),
|
2016-03-31 10:00:29 +02:00
|
|
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
2011-05-27 11:43:52 +02:00
|
|
|
end.
|
|
|
|
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
|
2013-03-14 10:33:02 +01:00
|
|
|
Filter = fun (List) ->
|
|
|
|
lists:filter(fun (#listitem{action = deny, type = jid,
|
|
|
|
value = JID}) ->
|
|
|
|
not lists:member(JID, JIDs);
|
|
|
|
(_) -> true
|
|
|
|
end,
|
|
|
|
List)
|
|
|
|
end,
|
2016-04-14 13:17:20 +02:00
|
|
|
Mod = db_mod(LServer),
|
|
|
|
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
2013-03-14 10:33:02 +01:00
|
|
|
{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};
|
2015-03-13 11:05:16 +01:00
|
|
|
_Err ->
|
|
|
|
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
|
2016-03-31 10:00:29 +02:00
|
|
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
2011-05-27 11:43:52 +02:00
|
|
|
end.
|
|
|
|
|
2011-12-21 15:20:59 +01:00
|
|
|
make_userlist(Name, List) ->
|
2012-04-27 11:52:05 +02:00
|
|
|
NeedDb = mod_privacy:is_list_needdb(List),
|
2011-12-21 15:20:59 +01:00
|
|
|
#userlist{name = Name, list = List, needdb = NeedDb}.
|
|
|
|
|
|
|
|
broadcast_list_update(LUser, LServer, Name, UserList) ->
|
2015-11-24 16:44:13 +01:00
|
|
|
ejabberd_sm:route(jid:make(LUser, LServer,
|
2013-03-14 10:33:02 +01:00
|
|
|
<<"">>),
|
2015-11-24 16:44:13 +01:00
|
|
|
jid:make(LUser, LServer, <<"">>),
|
2013-03-14 10:33:02 +01:00
|
|
|
{broadcast, {privacy_list, UserList, Name}}).
|
2011-05-27 11:43:52 +02:00
|
|
|
|
|
|
|
broadcast_blocklist_event(LUser, LServer, Event) ->
|
2015-11-24 16:44:13 +01:00
|
|
|
JID = jid:make(LUser, LServer, <<"">>),
|
2013-03-14 10:33:02 +01:00
|
|
|
ejabberd_sm:route(JID, JID,
|
|
|
|
{broadcast, {blocking, Event}}).
|
2011-05-27 11:43:52 +02:00
|
|
|
|
2016-03-31 10:00:29 +02:00
|
|
|
process_blocklist_get(LUser, LServer, Lang) ->
|
2016-04-14 13:17:20 +02:00
|
|
|
Mod = db_mod(LServer),
|
|
|
|
case Mod:process_blocklist_get(LUser, LServer) of
|
2016-03-31 10:00:29 +02:00
|
|
|
error ->
|
|
|
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
2013-03-14 10:33:02 +01:00
|
|
|
List ->
|
|
|
|
JIDs = list_to_blocklist_jids(List, []),
|
|
|
|
Items = lists:map(fun (JID) ->
|
|
|
|
?DEBUG("JID: ~p", [JID]),
|
|
|
|
#xmlel{name = <<"item">>,
|
|
|
|
attrs =
|
|
|
|
[{<<"jid">>,
|
2015-11-24 16:44:13 +01:00
|
|
|
jid:to_string(JID)}],
|
2013-03-14 10:33:02 +01:00
|
|
|
children = []}
|
|
|
|
end,
|
|
|
|
JIDs),
|
|
|
|
{result,
|
|
|
|
[#xmlel{name = <<"blocklist">>,
|
|
|
|
attrs = [{<<"xmlns">>, ?NS_BLOCKING}],
|
|
|
|
children = Items}]}
|
2012-04-27 11:52:05 +02:00
|
|
|
end.
|
|
|
|
|
2016-04-14 13:17:20 +02:00
|
|
|
db_mod(LServer) ->
|
|
|
|
DBType = gen_mod:db_type(LServer, mod_privacy),
|
|
|
|
gen_mod:db_mod(DBType, ?MODULE).
|
2015-06-01 14:38:27 +02:00
|
|
|
|
|
|
|
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
|
|
|
mod_opt_type(_) -> [iqdisc].
|