mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Implement cache for mod_privacy/mod_blocking
This commit is contained in:
parent
654d907dcf
commit
35d19b32f4
@ -38,11 +38,3 @@
|
|||||||
-type listitem_type() :: none | jid | group | subscription.
|
-type listitem_type() :: none | jid | group | subscription.
|
||||||
-type listitem_value() :: none | both | from | to | jid:ljid() | binary().
|
-type listitem_value() :: none | both | from | to | jid:ljid() | binary().
|
||||||
-type listitem_action() :: allow | deny.
|
-type listitem_action() :: allow | deny.
|
||||||
|
|
||||||
-record(userlist, {name = none :: none | binary(),
|
|
||||||
list = [] :: [listitem()],
|
|
||||||
needdb = false :: boolean()}).
|
|
||||||
|
|
||||||
-type userlist() :: #userlist{}.
|
|
||||||
|
|
||||||
-export_type([userlist/0]).
|
|
||||||
|
@ -1485,10 +1485,7 @@ privacy_set(Username, Host, QueryS) ->
|
|||||||
SubEl = xmpp:decode(QueryEl),
|
SubEl = xmpp:decode(QueryEl),
|
||||||
IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
|
IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
|
||||||
from = From, to = To},
|
from = From, to = To},
|
||||||
ejabberd_hooks:run_fold(privacy_iq_set,
|
mod_privacy:process_iq(IQ),
|
||||||
Host,
|
|
||||||
{error, xmpp:err_feature_not_implemented()},
|
|
||||||
[IQ, #userlist{}]),
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
|
@ -39,12 +39,6 @@
|
|||||||
|
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.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.
|
|
||||||
|
|
||||||
-type block_event() :: {block, [jid()]} | {unblock, [jid()]} | unblock_all.
|
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
|
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 50),
|
||||||
@ -142,101 +136,111 @@ listitems_to_jids([_ | Items], JIDs) ->
|
|||||||
listitems_to_jids(Items, JIDs).
|
listitems_to_jids(Items, JIDs).
|
||||||
|
|
||||||
-spec process_block(iq(), [ljid()]) -> iq().
|
-spec process_block(iq(), [ljid()]) -> iq().
|
||||||
process_block(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_block(#iq{from = From} = IQ, LJIDs) ->
|
||||||
lang = Lang} = IQ, JIDs) ->
|
#jid{luser = LUser, lserver = LServer} = From,
|
||||||
Filter = fun (List) ->
|
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, []),
|
AlreadyBlocked = listitems_to_jids(List, []),
|
||||||
lists:foldr(fun (JID, List1) ->
|
NewList = lists:foldr(
|
||||||
case lists:member(JID, AlreadyBlocked)
|
fun(LJID, List1) ->
|
||||||
of
|
case lists:member(LJID, AlreadyBlocked) of
|
||||||
true -> List1;
|
true ->
|
||||||
|
List1;
|
||||||
false ->
|
false ->
|
||||||
[#listitem{type = jid,
|
[#listitem{type = jid,
|
||||||
value = JID,
|
value = LJID,
|
||||||
action = deny,
|
action = deny,
|
||||||
order = 0,
|
order = 0,
|
||||||
match_all = true}
|
match_all = true}|List1]
|
||||||
| 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 = [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 '~s': "
|
||||||
|
"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
|
||||||
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, UserList, Default),
|
|
||||||
broadcast_event(LUser, LServer,
|
|
||||||
#block{items = [jid:make(J) || J <- JIDs]}),
|
|
||||||
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, UserList));
|
|
||||||
_Err ->
|
|
||||||
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
|
|
||||||
Err = xmpp:err_internal_server_error(<<"Database failure">>, Lang),
|
|
||||||
xmpp:make_error(IQ, Err)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_unblock_all(iq()) -> iq().
|
-spec process_unblock_all(iq()) -> iq().
|
||||||
process_unblock_all(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_unblock_all(#iq{from = From} = IQ) ->
|
||||||
lang = Lang} = IQ) ->
|
#jid{luser = LUser, lserver = LServer} = From,
|
||||||
Filter = fun (List) ->
|
case mod_privacy:get_user_list(LUser, LServer, default) of
|
||||||
lists:filter(fun (#listitem{action = A}) -> A =/= deny
|
{ok, {Name, List}} ->
|
||||||
end,
|
NewList = lists:filter(
|
||||||
List)
|
fun(#listitem{action = A}) ->
|
||||||
end,
|
A /= deny
|
||||||
Mod = db_mod(LServer),
|
end, List),
|
||||||
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
case mod_privacy:set_list(LUser, LServer, Name, NewList) of
|
||||||
{atomic, ok} ->
|
ok ->
|
||||||
|
mod_privacy:push_list_update(From, Name),
|
||||||
|
broadcast_event(From, #unblock{}),
|
||||||
xmpp:make_iq_result(IQ);
|
xmpp:make_iq_result(IQ);
|
||||||
{atomic, {ok, Default, List}} ->
|
{error, _} ->
|
||||||
UserList = make_userlist(Default, List),
|
err_db_failure(IQ)
|
||||||
broadcast_list_update(LUser, LServer, UserList, Default),
|
end;
|
||||||
broadcast_event(LUser, LServer, #unblock{}),
|
error ->
|
||||||
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, UserList));
|
broadcast_event(From, #unblock{}),
|
||||||
_Err ->
|
xmpp:make_iq_result(IQ);
|
||||||
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]),
|
{error, _} ->
|
||||||
Err = xmpp:err_internal_server_error(<<"Database failure">>, Lang),
|
err_db_failure(IQ)
|
||||||
xmpp:make_error(IQ, Err)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_unblock(iq(), [ljid()]) -> iq().
|
-spec process_unblock(iq(), [ljid()]) -> iq().
|
||||||
process_unblock(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_unblock(#iq{from = From} = IQ, LJIDs) ->
|
||||||
lang = Lang} = IQ, JIDs) ->
|
#jid{luser = LUser, lserver = LServer} = From,
|
||||||
Filter = fun (List) ->
|
case mod_privacy:get_user_list(LUser, LServer, default) of
|
||||||
lists:filter(fun (#listitem{action = deny, type = jid,
|
{ok, {Name, List}} ->
|
||||||
value = JID}) ->
|
NewList = lists:filter(
|
||||||
not lists:member(JID, JIDs);
|
fun(#listitem{action = deny, type = jid,
|
||||||
(_) -> true
|
value = LJID}) ->
|
||||||
end,
|
not lists:member(LJID, LJIDs);
|
||||||
List)
|
(_) ->
|
||||||
end,
|
true
|
||||||
Mod = db_mod(LServer),
|
end, List),
|
||||||
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
case mod_privacy:set_list(LUser, LServer, Name, NewList) of
|
||||||
{atomic, ok} ->
|
ok ->
|
||||||
|
mod_privacy:push_list_update(From, Name),
|
||||||
|
Items = [jid:make(LJID) || LJID <- LJIDs],
|
||||||
|
broadcast_event(From, #unblock{items = Items}),
|
||||||
xmpp:make_iq_result(IQ);
|
xmpp:make_iq_result(IQ);
|
||||||
{atomic, {ok, Default, List}} ->
|
{error, _} ->
|
||||||
UserList = make_userlist(Default, List),
|
err_db_failure(IQ)
|
||||||
broadcast_list_update(LUser, LServer, UserList, Default),
|
end;
|
||||||
broadcast_event(LUser, LServer,
|
error ->
|
||||||
#unblock{items = [jid:make(J) || J <- JIDs]}),
|
Items = [jid:make(LJID) || LJID <- LJIDs],
|
||||||
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, UserList));
|
broadcast_event(From, #unblock{items = Items}),
|
||||||
_Err ->
|
xmpp:make_iq_result(IQ);
|
||||||
?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer, JIDs}, _Err]),
|
{error, _} ->
|
||||||
Err = xmpp:err_internal_server_error(<<"Database failure">>, Lang),
|
err_db_failure(IQ)
|
||||||
xmpp:make_error(IQ, Err)
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec make_userlist(binary(), [listitem()]) -> userlist().
|
-spec broadcast_event(jid(), block() | unblock()) -> ok.
|
||||||
make_userlist(Name, List) ->
|
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
|
||||||
NeedDb = mod_privacy:is_list_needdb(List),
|
|
||||||
#userlist{name = Name, list = List, needdb = NeedDb}.
|
|
||||||
|
|
||||||
-spec broadcast_list_update(binary(), binary(), userlist(), binary()) -> ok.
|
|
||||||
broadcast_list_update(LUser, LServer, UserList, Name) ->
|
|
||||||
mod_privacy:push_list_update(jid:make(LUser, LServer), UserList, Name).
|
|
||||||
|
|
||||||
-spec broadcast_event(binary(), binary(), block_event()) -> ok.
|
|
||||||
broadcast_event(LUser, LServer, Event) ->
|
|
||||||
From = jid:make(LUser, LServer),
|
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
To = jid:replace_resource(From, R),
|
To = jid:replace_resource(From, R),
|
||||||
@ -247,23 +251,21 @@ broadcast_event(LUser, LServer, Event) ->
|
|||||||
end, ejabberd_sm:get_user_resources(LUser, LServer)).
|
end, ejabberd_sm:get_user_resources(LUser, LServer)).
|
||||||
|
|
||||||
-spec process_get(iq()) -> iq().
|
-spec process_get(iq()) -> iq().
|
||||||
process_get(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_get(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) ->
|
||||||
lang = Lang} = IQ) ->
|
case mod_privacy:get_user_list(LUser, LServer, default) of
|
||||||
Mod = db_mod(LServer),
|
{ok, {_, List}} ->
|
||||||
case Mod:process_blocklist_get(LUser, LServer) of
|
|
||||||
error ->
|
|
||||||
Err = xmpp:err_internal_server_error(<<"Database failure">>, Lang),
|
|
||||||
xmpp:make_error(IQ, Err);
|
|
||||||
List ->
|
|
||||||
LJIDs = listitems_to_jids(List, []),
|
LJIDs = listitems_to_jids(List, []),
|
||||||
Items = [jid:make(J) || J <- LJIDs],
|
Items = [jid:make(J) || J <- LJIDs],
|
||||||
xmpp:make_iq_result(IQ, #block_list{items = Items})
|
xmpp:make_iq_result(IQ, #block_list{items = Items});
|
||||||
|
error ->
|
||||||
|
xmpp:make_iq_result(IQ, #block_list{});
|
||||||
|
{error, _} ->
|
||||||
|
err_db_failure(IQ)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec db_mod(binary()) -> module().
|
err_db_failure(#iq{lang = Lang} = IQ) ->
|
||||||
db_mod(LServer) ->
|
Txt = <<"Database failure">>,
|
||||||
DBType = gen_mod:db_type(LServer, mod_privacy),
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)).
|
||||||
gen_mod:db_mod(DBType, ?MODULE).
|
|
||||||
|
|
||||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||||
mod_opt_type(_) -> [iqdisc].
|
mod_opt_type(_) -> [iqdisc].
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% File : mod_blocking_mnesia.erl
|
|
||||||
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% 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.
|
|
||||||
%%%
|
|
||||||
%%% 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.
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(mod_blocking_mnesia).
|
|
||||||
|
|
||||||
-behaviour(mod_blocking).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([process_blocklist_block/3, unblock_by_filter/3,
|
|
||||||
process_blocklist_get/2]).
|
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% API
|
|
||||||
%%%===================================================================
|
|
||||||
process_blocklist_block(LUser, LServer, Filter) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
|
||||||
[] ->
|
|
||||||
P = #privacy{us = {LUser, LServer}},
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = [],
|
|
||||||
List = [];
|
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewDefault = Default,
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists);
|
|
||||||
false ->
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = Lists,
|
|
||||||
List = []
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists = [{NewDefault, NewList} | NewLists1],
|
|
||||||
mnesia:write(P#privacy{default = NewDefault,
|
|
||||||
lists = NewLists}),
|
|
||||||
{ok, NewDefault, NewList}
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F).
|
|
||||||
|
|
||||||
unblock_by_filter(LUser, LServer, Filter) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
|
||||||
[] ->
|
|
||||||
%% No lists, nothing to unblock
|
|
||||||
ok;
|
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists),
|
|
||||||
NewLists = [{Default, NewList} | NewLists1],
|
|
||||||
mnesia:write(P#privacy{lists = NewLists}),
|
|
||||||
{ok, Default, NewList};
|
|
||||||
false ->
|
|
||||||
%% No default list, nothing to unblock
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F).
|
|
||||||
|
|
||||||
process_blocklist_get(LUser, LServer) ->
|
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> [];
|
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> []
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% Internal functions
|
|
||||||
%%%===================================================================
|
|
@ -1,113 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% File : mod_blocking_riak.erl
|
|
||||||
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% 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.
|
|
||||||
%%%
|
|
||||||
%%% 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.
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(mod_blocking_riak).
|
|
||||||
|
|
||||||
-behaviour(mod_blocking).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([process_blocklist_block/3, unblock_by_filter/3,
|
|
||||||
process_blocklist_get/2]).
|
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% API
|
|
||||||
%%%===================================================================
|
|
||||||
process_blocklist_block(LUser, LServer, Filter) ->
|
|
||||||
{atomic,
|
|
||||||
begin
|
|
||||||
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewDefault = Default,
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists);
|
|
||||||
false ->
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = Lists,
|
|
||||||
List = []
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
P = #privacy{us = {LUser, LServer}},
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = [],
|
|
||||||
List = []
|
|
||||||
end,
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists = [{NewDefault, NewList} | NewLists1],
|
|
||||||
case ejabberd_riak:put(P#privacy{default = NewDefault,
|
|
||||||
lists = NewLists},
|
|
||||||
mod_privacy_riak:privacy_schema()) of
|
|
||||||
ok ->
|
|
||||||
{ok, NewDefault, NewList};
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end
|
|
||||||
end}.
|
|
||||||
|
|
||||||
unblock_by_filter(LUser, LServer, Filter) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{error, _} ->
|
|
||||||
%% No lists, nothing to unblock
|
|
||||||
ok;
|
|
||||||
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists),
|
|
||||||
NewLists = [{Default, NewList} | NewLists1],
|
|
||||||
case ejabberd_riak:put(P#privacy{lists = NewLists},
|
|
||||||
mod_privacy_riak:privacy_schema()) of
|
|
||||||
ok ->
|
|
||||||
{ok, Default, NewList};
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
%% No default list, nothing to unblock
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end}.
|
|
||||||
|
|
||||||
process_blocklist_get(LUser, LServer) ->
|
|
||||||
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists}} ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> []
|
|
||||||
end;
|
|
||||||
{error, notfound} ->
|
|
||||||
[];
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% Internal functions
|
|
||||||
%%%===================================================================
|
|
@ -1,107 +0,0 @@
|
|||||||
%%%-------------------------------------------------------------------
|
|
||||||
%%% File : mod_blocking_sql.erl
|
|
||||||
%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
|
||||||
%%%
|
|
||||||
%%%
|
|
||||||
%%% ejabberd, Copyright (C) 2002-2017 ProcessOne
|
|
||||||
%%%
|
|
||||||
%%% 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.
|
|
||||||
%%%
|
|
||||||
%%% 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.
|
|
||||||
%%%
|
|
||||||
%%%----------------------------------------------------------------------
|
|
||||||
|
|
||||||
-module(mod_blocking_sql).
|
|
||||||
|
|
||||||
-behaviour(mod_blocking).
|
|
||||||
|
|
||||||
%% API
|
|
||||||
-export([process_blocklist_block/3, unblock_by_filter/3,
|
|
||||||
process_blocklist_get/2]).
|
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% API
|
|
||||||
%%%===================================================================
|
|
||||||
process_blocklist_block(LUser, LServer, Filter) ->
|
|
||||||
F = fun () ->
|
|
||||||
Default = case mod_privacy_sql:sql_get_default_privacy_list_t(LUser) of
|
|
||||||
{selected, []} ->
|
|
||||||
Name = <<"Blocked contacts">>,
|
|
||||||
case mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Name) of
|
|
||||||
{selected, []} ->
|
|
||||||
mod_privacy_sql:sql_add_privacy_list(LUser, Name);
|
|
||||||
{selected, [{_ID}]} ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
mod_privacy_sql:sql_set_default_privacy_list(LUser, Name),
|
|
||||||
Name;
|
|
||||||
{selected, [{Name}]} -> Name
|
|
||||||
end,
|
|
||||||
{selected, [{ID}]} =
|
|
||||||
mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Default),
|
|
||||||
case mod_privacy_sql:sql_get_privacy_list_data_by_id_t(ID) of
|
|
||||||
{selected, RItems = [_ | _]} ->
|
|
||||||
List = lists:flatmap(fun mod_privacy_sql:raw_to_item/1, RItems);
|
|
||||||
_ ->
|
|
||||||
List = []
|
|
||||||
end,
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewRItems = lists:map(fun mod_privacy_sql:item_to_raw/1,
|
|
||||||
NewList),
|
|
||||||
mod_privacy_sql:sql_set_privacy_list(ID, NewRItems),
|
|
||||||
{ok, Default, NewList}
|
|
||||||
end,
|
|
||||||
ejabberd_sql:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
unblock_by_filter(LUser, LServer, Filter) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mod_privacy_sql:sql_get_default_privacy_list_t(LUser) of
|
|
||||||
{selected, []} -> ok;
|
|
||||||
{selected, [{Default}]} ->
|
|
||||||
{selected, [{ID}]} =
|
|
||||||
mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Default),
|
|
||||||
case mod_privacy_sql:sql_get_privacy_list_data_by_id_t(ID) of
|
|
||||||
{selected, RItems = [_ | _]} ->
|
|
||||||
List = lists:flatmap(fun mod_privacy_sql:raw_to_item/1,
|
|
||||||
RItems),
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewRItems = lists:map(fun mod_privacy_sql:item_to_raw/1,
|
|
||||||
NewList),
|
|
||||||
mod_privacy_sql:sql_set_privacy_list(ID, NewRItems),
|
|
||||||
{ok, Default, NewList};
|
|
||||||
_ -> ok
|
|
||||||
end;
|
|
||||||
_ -> ok
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
ejabberd_sql:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
process_blocklist_get(LUser, LServer) ->
|
|
||||||
case catch mod_privacy_sql:sql_get_default_privacy_list(LUser, LServer) of
|
|
||||||
{selected, []} -> [];
|
|
||||||
{selected, [{Default}]} ->
|
|
||||||
case catch mod_privacy_sql:sql_get_privacy_list_data(
|
|
||||||
LUser, LServer, Default) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
lists:flatmap(fun mod_privacy_sql:raw_to_item/1, RItems);
|
|
||||||
{'EXIT', _} -> error
|
|
||||||
end;
|
|
||||||
{'EXIT', _} -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%===================================================================
|
|
||||||
%%% Internal functions
|
|
||||||
%%%===================================================================
|
|
@ -31,42 +31,51 @@
|
|||||||
|
|
||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
-export([start/2, stop/1, reload/3, process_iq/1, export/1, import_info/0,
|
-export([start/2, stop/1, reload/3, process_iq/1, export/1,
|
||||||
c2s_session_opened/1, c2s_copy_session/2, push_list_update/3,
|
c2s_copy_session/2, push_list_update/2, disco_features/5,
|
||||||
user_send_packet/1, user_receive_packet/1, disco_features/5,
|
|
||||||
check_packet/4, remove_user/2, encode_list_item/1,
|
check_packet/4, remove_user/2, encode_list_item/1,
|
||||||
is_list_needdb/1, import_start/2, import_stop/2,
|
get_user_lists/2, get_user_list/3,
|
||||||
item_to_xml/1, get_user_lists/2, import/5,
|
set_list/1, set_list/4, set_default_list/3,
|
||||||
set_privacy_list/1, mod_opt_type/1, depends/2]).
|
user_send_packet/1, user_receive_packet/1,
|
||||||
|
import_start/2, import_stop/2, import/5, import_info/0,
|
||||||
|
mod_opt_type/1, depends/2]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
|
-define(PRIVACY_CACHE, privacy_cache).
|
||||||
|
-define(PRIVACY_LIST_CACHE, privacy_list_cache).
|
||||||
|
|
||||||
|
-type c2s_state() :: ejabberd_c2s:state().
|
||||||
-callback init(binary(), gen_mod:opts()) -> any().
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
-callback import(#privacy{}) -> ok.
|
-callback import(#privacy{}) -> ok.
|
||||||
-callback process_lists_get(binary(), binary()) -> {none | binary(), [binary()]} | error.
|
-callback set_default(binary(), binary(), binary()) ->
|
||||||
-callback process_list_get(binary(), binary(), binary()) -> [listitem()] | error | not_found.
|
ok | {error, notfound | any()}.
|
||||||
-callback process_default_set(binary(), binary(), binary() | none) -> {atomic, any()}.
|
-callback unset_default(binary(), binary()) -> ok | {error, any()}.
|
||||||
-callback process_active_set(binary(), binary(), binary()) -> [listitem()] | error.
|
-callback remove_list(binary(), binary(), binary()) ->
|
||||||
-callback remove_privacy_list(binary(), binary(), binary()) -> {atomic, any()}.
|
ok | {error, notfound | conflict | any()}.
|
||||||
-callback set_privacy_list(#privacy{}) -> any().
|
-callback remove_lists(binary(), binary()) -> ok | {error, any()}.
|
||||||
-callback set_privacy_list(binary(), binary(), binary(), [listitem()]) -> {atomic, any()}.
|
-callback set_lists(#privacy{}) -> ok | {error, any()}.
|
||||||
-callback get_user_list(binary(), binary()) -> {none | binary(), [listitem()]}.
|
-callback set_list(binary(), binary(), binary(), listitem()) ->
|
||||||
-callback get_user_lists(binary(), binary()) -> {ok, #privacy{}} | error.
|
ok | {error, any()}.
|
||||||
-callback remove_user(binary(), binary()) -> any().
|
-callback get_list(binary(), binary(), binary() | default) ->
|
||||||
|
{ok, {binary(), [listitem()]}} | error | {error, any()}.
|
||||||
|
-callback get_lists(binary(), binary()) ->
|
||||||
|
{ok, #privacy{}} | error | {error, any()}.
|
||||||
|
-callback use_cache(binary()) -> boolean().
|
||||||
|
-callback cache_nodes(binary()) -> [node()].
|
||||||
|
|
||||||
|
-optional_callbacks([use_cache/1, cache_nodes/1]).
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
|
||||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
Mod:init(Host, Opts),
|
Mod:init(Host, Opts),
|
||||||
|
init_cache(Mod, Host, Opts),
|
||||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
|
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
|
||||||
disco_features, 50),
|
disco_features, 50),
|
||||||
ejabberd_hooks:add(c2s_session_opened, Host, ?MODULE,
|
|
||||||
c2s_session_opened, 50),
|
|
||||||
ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE,
|
ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE,
|
||||||
c2s_copy_session, 50),
|
c2s_copy_session, 50),
|
||||||
ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
|
ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
|
||||||
@ -83,8 +92,6 @@ start(Host, Opts) ->
|
|||||||
stop(Host) ->
|
stop(Host) ->
|
||||||
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE,
|
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE,
|
||||||
disco_features, 50),
|
disco_features, 50),
|
||||||
ejabberd_hooks:delete(c2s_session_opened, Host, ?MODULE,
|
|
||||||
c2s_session_opened, 50),
|
|
||||||
ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE,
|
ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE,
|
||||||
c2s_copy_session, 50),
|
c2s_copy_session, 50),
|
||||||
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
|
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
|
||||||
@ -106,6 +113,7 @@ reload(Host, NewOpts, OldOpts) ->
|
|||||||
true ->
|
true ->
|
||||||
ok
|
ok
|
||||||
end,
|
end,
|
||||||
|
init_cache(NewMod, Host, NewOpts),
|
||||||
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
|
||||||
{false, IQDisc, _} ->
|
{false, IQDisc, _} ->
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PRIVACY,
|
||||||
@ -162,47 +170,41 @@ process_iq_get(#iq{lang = Lang} = IQ) ->
|
|||||||
|
|
||||||
-spec process_lists_get(iq()) -> iq().
|
-spec process_lists_get(iq()) -> iq().
|
||||||
process_lists_get(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_lists_get(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
lang = Lang,
|
lang = Lang} = IQ) ->
|
||||||
meta = #{privacy_active_list := Active}} = IQ) ->
|
case get_user_lists(LUser, LServer) of
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
{ok, #privacy{default = Default, lists = Lists}} ->
|
||||||
case Mod:process_lists_get(LUser, LServer) of
|
Active = xmpp:get_meta(IQ, privacy_active_list, none),
|
||||||
error ->
|
|
||||||
Txt = <<"Database failure">>,
|
|
||||||
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang));
|
|
||||||
{_Default, []} ->
|
|
||||||
xmpp:make_iq_result(IQ, #privacy_query{});
|
|
||||||
{Default, ListNames} ->
|
|
||||||
xmpp:make_iq_result(
|
xmpp:make_iq_result(
|
||||||
IQ,
|
IQ, #privacy_query{active = Active,
|
||||||
#privacy_query{active = Active,
|
|
||||||
default = Default,
|
default = Default,
|
||||||
lists = [#privacy_list{name = ListName}
|
lists = [#privacy_list{name = Name}
|
||||||
|| ListName <- ListNames]})
|
|| {Name, _} <- Lists]});
|
||||||
|
error ->
|
||||||
|
xmpp:make_iq_result(
|
||||||
|
IQ, #privacy_query{active = none, default = none});
|
||||||
|
{error, _} ->
|
||||||
|
Txt = <<"Database failure">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_list_get(iq(), binary()) -> iq().
|
-spec process_list_get(iq(), binary()) -> iq().
|
||||||
process_list_get(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_list_get(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
lang = Lang} = IQ, Name) ->
|
lang = Lang} = IQ, Name) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
case get_user_list(LUser, LServer, Name) of
|
||||||
case Mod:process_list_get(LUser, LServer, Name) of
|
{ok, {_, List}} ->
|
||||||
error ->
|
Items = lists:map(fun encode_list_item/1, List),
|
||||||
Txt = <<"Database failure">>,
|
|
||||||
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang));
|
|
||||||
not_found ->
|
|
||||||
Txt = <<"No privacy list with this name found">>,
|
|
||||||
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
|
||||||
Items ->
|
|
||||||
LItems = lists:map(fun encode_list_item/1, Items),
|
|
||||||
xmpp:make_iq_result(
|
xmpp:make_iq_result(
|
||||||
IQ,
|
IQ,
|
||||||
#privacy_query{
|
#privacy_query{
|
||||||
lists = [#privacy_list{name = Name, items = LItems}]})
|
lists = [#privacy_list{name = Name, items = Items}]});
|
||||||
|
error ->
|
||||||
|
Txt = <<"No privacy list with this name found">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
||||||
|
{error, _} ->
|
||||||
|
Txt = <<"Database failure">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec item_to_xml(listitem()) -> xmlel().
|
|
||||||
item_to_xml(ListItem) ->
|
|
||||||
xmpp:encode(encode_list_item(ListItem)).
|
|
||||||
|
|
||||||
-spec encode_list_item(listitem()) -> privacy_item().
|
-spec encode_list_item(listitem()) -> privacy_item().
|
||||||
encode_list_item(#listitem{action = Action,
|
encode_list_item(#listitem{action = Action,
|
||||||
order = Order,
|
order = Order,
|
||||||
@ -283,69 +285,69 @@ process_iq_set(#iq{lang = Lang} = IQ) ->
|
|||||||
Txt = <<"No module is handling this query">>,
|
Txt = <<"No module is handling this query">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||||
|
|
||||||
-spec process_default_set(iq(), binary()) -> iq().
|
-spec process_default_set(iq(), none | binary()) -> iq().
|
||||||
process_default_set(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_default_set(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
lang = Lang} = IQ, Value) ->
|
lang = Lang} = IQ, Value) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
case set_default_list(LUser, LServer, Value) of
|
||||||
case Mod:process_default_set(LUser, LServer, Value) of
|
ok ->
|
||||||
{atomic, error} ->
|
xmpp:make_iq_result(IQ);
|
||||||
Txt = <<"Database failure">>,
|
{error, notfound} ->
|
||||||
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang));
|
|
||||||
{atomic, not_found} ->
|
|
||||||
Txt = <<"No privacy list with this name found">>,
|
Txt = <<"No privacy list with this name found">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
||||||
{atomic, ok} ->
|
{error, _} ->
|
||||||
xmpp:make_iq_result(IQ);
|
Txt = <<"Database failure">>,
|
||||||
Err ->
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
||||||
?ERROR_MSG("failed to set default list '~s' for user ~s@~s: ~p",
|
|
||||||
[Value, LUser, LServer, Err]),
|
|
||||||
xmpp:make_error(IQ, xmpp:err_internal_server_error())
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec process_active_set(IQ, none | binary()) -> IQ.
|
-spec process_active_set(IQ, none | binary()) -> IQ.
|
||||||
process_active_set(IQ, none) ->
|
process_active_set(IQ, none) ->
|
||||||
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, #userlist{}));
|
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_active_list, none));
|
||||||
process_active_set(#iq{from = #jid{luser = LUser, lserver = LServer},
|
process_active_set(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
lang = Lang} = IQ, Name) ->
|
lang = Lang} = IQ, Name) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
case get_user_list(LUser, LServer, Name) of
|
||||||
case Mod:process_active_set(LUser, LServer, Name) of
|
{ok, _} ->
|
||||||
|
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_active_list, Name));
|
||||||
error ->
|
error ->
|
||||||
Txt = <<"No privacy list with this name found">>,
|
Txt = <<"No privacy list with this name found">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
||||||
Items ->
|
{error, _} ->
|
||||||
NeedDb = is_list_needdb(Items),
|
|
||||||
List = #userlist{name = Name, list = Items, needdb = NeedDb},
|
|
||||||
xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, List))
|
|
||||||
end.
|
|
||||||
|
|
||||||
-spec set_privacy_list(privacy()) -> any().
|
|
||||||
set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
|
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
|
||||||
Mod:set_privacy_list(Privacy).
|
|
||||||
|
|
||||||
-spec process_lists_set(iq(), binary(), [privacy_item()]) -> iq().
|
|
||||||
process_lists_set(#iq{meta = #{privacy_active_list := Name},
|
|
||||||
lang = Lang} = IQ, Name, []) ->
|
|
||||||
Txt = <<"Cannot remove active list">>,
|
|
||||||
xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang));
|
|
||||||
process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer} = From,
|
|
||||||
lang = Lang} = IQ, Name, []) ->
|
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
|
||||||
case Mod:remove_privacy_list(LUser, LServer, Name) of
|
|
||||||
{atomic, conflict} ->
|
|
||||||
Txt = <<"Cannot remove default list">>,
|
|
||||||
xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang));
|
|
||||||
{atomic, not_found} ->
|
|
||||||
Txt = <<"No privacy list with this name found">>,
|
|
||||||
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
|
||||||
{atomic, ok} ->
|
|
||||||
push_list_update(From, #userlist{name = Name}, Name),
|
|
||||||
xmpp:make_iq_result(IQ);
|
|
||||||
Err ->
|
|
||||||
?ERROR_MSG("failed to remove privacy list '~s' for user ~s@~s: ~p",
|
|
||||||
[Name, LUser, LServer, Err]),
|
|
||||||
Txt = <<"Database failure">>,
|
Txt = <<"Database failure">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec set_list(privacy()) -> ok | {error, any()}.
|
||||||
|
set_list(#privacy{us = {LUser, LServer}, lists = Lists} = Privacy) ->
|
||||||
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
case Mod:set_lists(Privacy) of
|
||||||
|
ok ->
|
||||||
|
Names = [Name || {Name, _} <- Lists],
|
||||||
|
delete_cache(Mod, LUser, LServer, Names);
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec process_lists_set(iq(), binary(), [privacy_item()]) -> iq().
|
||||||
|
process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer},
|
||||||
|
lang = Lang} = IQ, Name, []) ->
|
||||||
|
case xmpp:get_meta(IQ, privacy_active_list, none) of
|
||||||
|
Name ->
|
||||||
|
Txt = <<"Cannot remove active list">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang));
|
||||||
|
_ ->
|
||||||
|
case remove_list(LUser, LServer, Name) of
|
||||||
|
ok ->
|
||||||
|
xmpp:make_iq_result(IQ);
|
||||||
|
{error, conflict} ->
|
||||||
|
Txt = <<"Cannot remove default list">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_conflict(Txt, Lang));
|
||||||
|
{error, notfound} ->
|
||||||
|
Txt = <<"No privacy list with this name found">>,
|
||||||
|
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
|
||||||
|
{error, _} ->
|
||||||
|
Txt = <<"Database failure">>,
|
||||||
|
Err = xmpp:err_internal_server_error(Txt, Lang),
|
||||||
|
xmpp:make_error(IQ, Err)
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer} = From,
|
process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer} = From,
|
||||||
lang = Lang} = IQ, Name, Items) ->
|
lang = Lang} = IQ, Name, Items) ->
|
||||||
@ -354,24 +356,18 @@ process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer} = From,
|
|||||||
Txt = xmpp:format_error(Why),
|
Txt = xmpp:format_error(Why),
|
||||||
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
|
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
|
||||||
List ->
|
List ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
case set_list(LUser, LServer, Name, List) of
|
||||||
case Mod:set_privacy_list(LUser, LServer, Name, List) of
|
ok ->
|
||||||
{atomic, ok} ->
|
push_list_update(From, Name),
|
||||||
UserList = #userlist{name = Name, list = List,
|
|
||||||
needdb = is_list_needdb(List)},
|
|
||||||
push_list_update(From, UserList, Name),
|
|
||||||
xmpp:make_iq_result(IQ);
|
xmpp:make_iq_result(IQ);
|
||||||
Err ->
|
{error, _} ->
|
||||||
?ERROR_MSG("failed to set privacy list '~s' "
|
|
||||||
"for user ~s@~s: ~p",
|
|
||||||
[Name, LUser, LServer, Err]),
|
|
||||||
Txt = <<"Database failure">>,
|
Txt = <<"Database failure">>,
|
||||||
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec push_list_update(jid(), #userlist{}, binary() | none) -> ok.
|
-spec push_list_update(jid(), binary()) -> ok.
|
||||||
push_list_update(From, List, Name) ->
|
push_list_update(From, Name) ->
|
||||||
BareFrom = jid:remove_resource(From),
|
BareFrom = jid:remove_resource(From),
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun(R) ->
|
fun(R) ->
|
||||||
@ -379,44 +375,10 @@ push_list_update(From, List, Name) ->
|
|||||||
IQ = #iq{type = set, from = BareFrom, to = To,
|
IQ = #iq{type = set, from = BareFrom, to = To,
|
||||||
id = <<"push", (randoms:get_string())/binary>>,
|
id = <<"push", (randoms:get_string())/binary>>,
|
||||||
sub_els = [#privacy_query{
|
sub_els = [#privacy_query{
|
||||||
lists = [#privacy_list{name = Name}]}],
|
lists = [#privacy_list{name = Name}]}]},
|
||||||
meta = #{privacy_updated_list => List}},
|
|
||||||
ejabberd_router:route(IQ)
|
ejabberd_router:route(IQ)
|
||||||
end, ejabberd_sm:get_user_resources(From#jid.luser, From#jid.lserver)).
|
end, ejabberd_sm:get_user_resources(From#jid.luser, From#jid.lserver)).
|
||||||
|
|
||||||
-spec user_send_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}.
|
|
||||||
user_send_packet({#iq{type = Type,
|
|
||||||
to = #jid{luser = U, lserver = S, lresource = <<"">>},
|
|
||||||
from = #jid{luser = U, lserver = S},
|
|
||||||
sub_els = [_]} = IQ,
|
|
||||||
#{privacy_list := #userlist{name = Name}} = State})
|
|
||||||
when Type == get; Type == set ->
|
|
||||||
NewIQ = case xmpp:has_subtag(IQ, #privacy_query{}) of
|
|
||||||
true -> xmpp:put_meta(IQ, privacy_active_list, Name);
|
|
||||||
false -> IQ
|
|
||||||
end,
|
|
||||||
{NewIQ, State};
|
|
||||||
user_send_packet(Acc) ->
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
-spec user_receive_packet({stanza(), ejabberd_c2s:state()}) -> {stanza(), ejabberd_c2s:state()}.
|
|
||||||
user_receive_packet({#iq{type = result, meta = #{privacy_list := List}} = IQ,
|
|
||||||
State}) ->
|
|
||||||
{IQ, State#{privacy_list => List}};
|
|
||||||
user_receive_packet({#iq{type = set, meta = #{privacy_updated_list := New}} = IQ,
|
|
||||||
#{user := U, server := S, resource := R,
|
|
||||||
privacy_list := Old} = State}) ->
|
|
||||||
State1 = if Old#userlist.name == New#userlist.name ->
|
|
||||||
State#{privacy_list => New};
|
|
||||||
true ->
|
|
||||||
State
|
|
||||||
end,
|
|
||||||
From = jid:make(U, S),
|
|
||||||
To = jid:make(U, S, R),
|
|
||||||
{xmpp:set_from_to(IQ, From, To), State1};
|
|
||||||
user_receive_packet(Acc) ->
|
|
||||||
Acc.
|
|
||||||
|
|
||||||
-spec decode_item(privacy_item()) -> listitem().
|
-spec decode_item(privacy_item()) -> listitem().
|
||||||
decode_item(#privacy_item{order = Order,
|
decode_item(#privacy_item{order = Order,
|
||||||
action = Action,
|
action = Action,
|
||||||
@ -448,47 +410,145 @@ decode_item(#privacy_item{order = Order,
|
|||||||
match_presence_out = MatchPresenceOut}
|
match_presence_out = MatchPresenceOut}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec is_list_needdb([listitem()]) -> boolean().
|
-spec c2s_copy_session(ejabberd_c2s:state(), c2s_state()) -> c2s_state().
|
||||||
is_list_needdb(Items) ->
|
c2s_copy_session(State, #{privacy_active_list := List}) ->
|
||||||
lists:any(fun (X) ->
|
State#{privacy_active_list => List}.
|
||||||
case X#listitem.type of
|
|
||||||
subscription -> true;
|
-spec user_send_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}.
|
||||||
group -> true;
|
user_send_packet({#iq{type = Type,
|
||||||
_ -> false
|
to = #jid{luser = U, lserver = S, lresource = <<"">>},
|
||||||
end
|
from = #jid{luser = U, lserver = S},
|
||||||
|
sub_els = [_]} = IQ,
|
||||||
|
#{privacy_active_list := Name} = State})
|
||||||
|
when Type == get; Type == set ->
|
||||||
|
NewIQ = case xmpp:has_subtag(IQ, #privacy_query{}) of
|
||||||
|
true -> xmpp:put_meta(IQ, privacy_active_list, Name);
|
||||||
|
false -> IQ
|
||||||
end,
|
end,
|
||||||
Items).
|
{NewIQ, State};
|
||||||
|
user_send_packet(Acc) ->
|
||||||
|
Acc.
|
||||||
|
|
||||||
-spec get_user_list(binary(), binary()) -> #userlist{}.
|
-spec user_receive_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}.
|
||||||
get_user_list(LUser, LServer) ->
|
user_receive_packet({#iq{type = result,
|
||||||
|
meta = #{privacy_active_list := Name}} = IQ, State}) ->
|
||||||
|
{IQ, State#{privacy_active_list => Name}};
|
||||||
|
user_receive_packet(Acc) ->
|
||||||
|
Acc.
|
||||||
|
|
||||||
|
-spec set_list(binary(), binary(), binary(), [listitem()]) -> ok | {error, any()}.
|
||||||
|
set_list(LUser, LServer, Name, List) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
{Default, Items} = Mod:get_user_list(LUser, LServer),
|
case Mod:set_list(LUser, LServer, Name, List) of
|
||||||
NeedDb = is_list_needdb(Items),
|
ok ->
|
||||||
#userlist{name = Default, list = Items, needdb = NeedDb}.
|
delete_cache(Mod, LUser, LServer, [Name]);
|
||||||
|
{error, _} = Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
-spec c2s_session_opened(ejabberd_c2s:state()) -> ejabberd_c2s:state().
|
-spec remove_list(binary(), binary(), binary()) ->
|
||||||
c2s_session_opened(#{jid := #jid{luser = LUser, lserver = LServer}} = State) ->
|
ok | {error, conflict | notfound | any()}.
|
||||||
State#{privacy_list => get_user_list(LUser, LServer)}.
|
remove_list(LUser, LServer, Name) ->
|
||||||
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
case Mod:remove_list(LUser, LServer, Name) of
|
||||||
|
ok ->
|
||||||
|
delete_cache(Mod, LUser, LServer, [Name]);
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
-spec c2s_copy_session(ejabberd_c2s:state(), ejabberd_c2s:state()) -> ejabberd_c2s:state().
|
-spec get_user_lists(binary(), binary()) -> {ok, privacy()} | error | {error, any()}.
|
||||||
c2s_copy_session(State, #{privacy_list := List}) ->
|
|
||||||
State#{privacy_list => List}.
|
|
||||||
|
|
||||||
-spec get_user_lists(binary(), binary()) -> {ok, privacy()} | error.
|
|
||||||
get_user_lists(User, Server) ->
|
get_user_lists(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:get_user_lists(LUser, LServer).
|
case use_cache(Mod, LServer) of
|
||||||
|
true ->
|
||||||
|
ets_cache:lookup(
|
||||||
|
?PRIVACY_CACHE, {LUser, LServer},
|
||||||
|
fun() -> Mod:get_lists(LUser, LServer) end);
|
||||||
|
false ->
|
||||||
|
Mod:get_lists(LUser, LServer)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_user_list(binary(), binary(), binary() | default) ->
|
||||||
|
{ok, {binary(), [listitem()]}} | error | {error, any()}.
|
||||||
|
get_user_list(LUser, LServer, Name) ->
|
||||||
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
case use_cache(Mod, LServer) of
|
||||||
|
true ->
|
||||||
|
ets_cache:lookup(
|
||||||
|
?PRIVACY_LIST_CACHE, {LUser, LServer, Name},
|
||||||
|
fun() ->
|
||||||
|
case ets_cache:lookup(
|
||||||
|
?PRIVACY_CACHE, {LUser, LServer}) of
|
||||||
|
{ok, Privacy} ->
|
||||||
|
get_list_by_name(Privacy, Name);
|
||||||
|
error ->
|
||||||
|
Mod:get_list(LUser, LServer, Name)
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
false ->
|
||||||
|
Mod:get_list(LUser, LServer, Name)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec get_list_by_name(#privacy{}, binary() | default) ->
|
||||||
|
{ok, {binary(), [listitem()]}} | error.
|
||||||
|
get_list_by_name(#privacy{default = Default} = Privacy, default) ->
|
||||||
|
get_list_by_name(Privacy, Default);
|
||||||
|
get_list_by_name(#privacy{lists = Lists}, Name) ->
|
||||||
|
case lists:keyfind(Name, 1, Lists) of
|
||||||
|
{_, List} -> {ok, {Name, List}};
|
||||||
|
false -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec set_default_list(binary(), binary(), binary() | none) ->
|
||||||
|
ok | {error, notfound | any()}.
|
||||||
|
set_default_list(LUser, LServer, Name) ->
|
||||||
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Res = case Name of
|
||||||
|
none -> Mod:unset_default(LUser, LServer);
|
||||||
|
_ -> Mod:set_default(LUser, LServer, Name)
|
||||||
|
end,
|
||||||
|
case Res of
|
||||||
|
ok ->
|
||||||
|
delete_cache(Mod, LUser, LServer, []);
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec check_packet(allow | deny, c2s_state() | jid(), stanza(), in | out) -> allow | deny.
|
||||||
|
check_packet(Acc, #{jid := JID} = State, Packet, Dir) ->
|
||||||
|
case maps:get(privacy_active_list, State, none) of
|
||||||
|
none ->
|
||||||
|
check_packet(Acc, JID, Packet, Dir);
|
||||||
|
ListName ->
|
||||||
|
#jid{luser = LUser, lserver = LServer} = JID,
|
||||||
|
case get_user_list(LUser, LServer, ListName) of
|
||||||
|
{ok, {_, List}} ->
|
||||||
|
do_check_packet(JID, List, Packet, Dir);
|
||||||
|
_ ->
|
||||||
|
?DEBUG("Non-existing active list '~s' is set "
|
||||||
|
"for user '~s'", [ListName, jid:encode(JID)]),
|
||||||
|
check_packet(Acc, JID, Packet, Dir)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
check_packet(_, JID, Packet, Dir) ->
|
||||||
|
#jid{luser = LUser, lserver = LServer} = JID,
|
||||||
|
case get_user_list(LUser, LServer, default) of
|
||||||
|
{ok, {_, List}} ->
|
||||||
|
do_check_packet(JID, List, Packet, Dir);
|
||||||
|
_ ->
|
||||||
|
allow
|
||||||
|
end.
|
||||||
|
|
||||||
%% From is the sender, To is the destination.
|
%% From is the sender, To is the destination.
|
||||||
%% If Dir = out, User@Server is the sender account (From).
|
%% If Dir = out, User@Server is the sender account (From).
|
||||||
%% If Dir = in, User@Server is the destination account (To).
|
%% If Dir = in, User@Server is the destination account (To).
|
||||||
-spec check_packet(allow | deny, ejabberd_c2s:state() | jid(),
|
-spec do_check_packet(jid(), [listitem()], stanza(), in | out) -> allow | deny.
|
||||||
stanza(), in | out) -> allow | deny.
|
do_check_packet(_, [], _, _) ->
|
||||||
check_packet(_, #{jid := #jid{luser = LUser, lserver = LServer},
|
allow;
|
||||||
privacy_list := #userlist{list = List, needdb = NeedDb}},
|
do_check_packet(#jid{luser = LUser, lserver = LServer}, List, Packet, Dir) ->
|
||||||
Packet, Dir) ->
|
|
||||||
From = xmpp:get_from(Packet),
|
From = xmpp:get_from(Packet),
|
||||||
To = xmpp:get_to(Packet),
|
To = xmpp:get_to(Packet),
|
||||||
case {From, To} of
|
case {From, To} of
|
||||||
@ -508,8 +568,6 @@ check_packet(_, #{jid := #jid{luser = LUser, lserver = LServer},
|
|||||||
#jid{luser = LUser, lserver = LServer, lresource = <<"">>}} when Dir == out ->
|
#jid{luser = LUser, lserver = LServer, lresource = <<"">>}} when Dir == out ->
|
||||||
%% Allow outgoing packets from user's full jid to his bare JID
|
%% Allow outgoing packets from user's full jid to his bare JID
|
||||||
allow;
|
allow;
|
||||||
_ when List == [] ->
|
|
||||||
allow;
|
|
||||||
_ ->
|
_ ->
|
||||||
PType = case Packet of
|
PType = case Packet of
|
||||||
#message{} -> message;
|
#message{} -> message;
|
||||||
@ -529,21 +587,11 @@ check_packet(_, #{jid := #jid{luser = LUser, lserver = LServer},
|
|||||||
in -> jid:tolower(From);
|
in -> jid:tolower(From);
|
||||||
out -> jid:tolower(To)
|
out -> jid:tolower(To)
|
||||||
end,
|
end,
|
||||||
{Subscription, Groups} =
|
{Subscription, Groups} = ejabberd_hooks:run_fold(
|
||||||
case NeedDb of
|
roster_get_jid_info, LServer,
|
||||||
true ->
|
{none, []}, [LUser, LServer, LJID]),
|
||||||
ejabberd_hooks:run_fold(roster_get_jid_info,
|
|
||||||
LServer,
|
|
||||||
{none, []},
|
|
||||||
[LUser, LServer, LJID]);
|
|
||||||
false ->
|
|
||||||
{[], []}
|
|
||||||
end,
|
|
||||||
check_packet_aux(List, PType2, LJID, Subscription, Groups)
|
check_packet_aux(List, PType2, LJID, Subscription, Groups)
|
||||||
end;
|
end.
|
||||||
check_packet(Acc, #jid{luser = LUser, lserver = LServer} = JID, Packet, Dir) ->
|
|
||||||
List = get_user_list(LUser, LServer),
|
|
||||||
check_packet(Acc, #{jid => JID, privacy_list => List}, Packet, Dir).
|
|
||||||
|
|
||||||
-spec check_packet_aux([listitem()],
|
-spec check_packet_aux([listitem()],
|
||||||
message | iq | presence_in | presence_out | other,
|
message | iq | presence_in | presence_out | other,
|
||||||
@ -608,12 +656,82 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
|
|||||||
group -> lists:member(Value, Groups)
|
group -> lists:member(Value, Groups)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
-spec remove_user(binary(), binary()) -> any().
|
-spec remove_user(binary(), binary()) -> ok.
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
|
Privacy = get_user_lists(LUser, LServer),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Mod:remove_user(LUser, LServer).
|
Mod:remove_lists(LUser, LServer),
|
||||||
|
case Privacy of
|
||||||
|
{ok, #privacy{lists = Lists}} ->
|
||||||
|
Names = [Name || {Name, _} <- Lists],
|
||||||
|
delete_cache(Mod, LUser, LServer, Names);
|
||||||
|
_ ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec init_cache(module(), binary(), gen_mod:opts()) -> ok.
|
||||||
|
init_cache(Mod, Host, Opts) ->
|
||||||
|
case use_cache(Mod, Host) of
|
||||||
|
true ->
|
||||||
|
CacheOpts = cache_opts(Host, Opts),
|
||||||
|
ets_cache:new(?PRIVACY_CACHE, CacheOpts),
|
||||||
|
ets_cache:new(?PRIVACY_LIST_CACHE, CacheOpts);
|
||||||
|
false ->
|
||||||
|
ets_cache:delete(?PRIVACY_CACHE),
|
||||||
|
ets_cache:delete(?PRIVACY_LIST_CACHE)
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec cache_opts(binary(), gen_mod:opts()) -> [proplists:property()].
|
||||||
|
cache_opts(Host, Opts) ->
|
||||||
|
MaxSize = gen_mod:get_opt(
|
||||||
|
cache_size, Opts,
|
||||||
|
ejabberd_config:cache_size(Host)),
|
||||||
|
CacheMissed = gen_mod:get_opt(
|
||||||
|
cache_missed, Opts,
|
||||||
|
ejabberd_config:cache_missed(Host)),
|
||||||
|
LifeTime = case gen_mod:get_opt(
|
||||||
|
cache_life_time, Opts,
|
||||||
|
ejabberd_config:cache_life_time(Host)) of
|
||||||
|
infinity -> infinity;
|
||||||
|
I -> timer:seconds(I)
|
||||||
|
end,
|
||||||
|
[{max_size, MaxSize}, {cache_missed, CacheMissed}, {life_time, LifeTime}].
|
||||||
|
|
||||||
|
-spec use_cache(module(), binary()) -> boolean().
|
||||||
|
use_cache(Mod, Host) ->
|
||||||
|
case erlang:function_exported(Mod, use_cache, 1) of
|
||||||
|
true -> Mod:use_cache(Host);
|
||||||
|
false ->
|
||||||
|
gen_mod:get_module_opt(
|
||||||
|
Host, ?MODULE, use_cache,
|
||||||
|
ejabberd_config:use_cache(Host))
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec cache_nodes(module(), binary()) -> [node()].
|
||||||
|
cache_nodes(Mod, Host) ->
|
||||||
|
case erlang:function_exported(Mod, cache_nodes, 1) of
|
||||||
|
true -> Mod:cache_nodes(Host);
|
||||||
|
false -> ejabberd_cluster:get_nodes()
|
||||||
|
end.
|
||||||
|
|
||||||
|
-spec delete_cache(module(), binary(), binary(), [binary()]) -> ok.
|
||||||
|
delete_cache(Mod, LUser, LServer, Names) ->
|
||||||
|
case use_cache(Mod, LServer) of
|
||||||
|
true ->
|
||||||
|
Nodes = cache_nodes(Mod, LServer),
|
||||||
|
ets_cache:delete(?PRIVACY_CACHE, {LUser, LServer}, Nodes),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Name) ->
|
||||||
|
ets_cache:delete(
|
||||||
|
?PRIVACY_LIST_CACHE,
|
||||||
|
{LUser, LServer, Name},
|
||||||
|
Nodes)
|
||||||
|
end, [default|Names]);
|
||||||
|
false ->
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
numeric_to_binary(<<0, 0, _/binary>>) ->
|
numeric_to_binary(<<0, 0, _/binary>>) ->
|
||||||
<<"0">>;
|
<<"0">>;
|
||||||
|
@ -27,11 +27,9 @@
|
|||||||
-behaviour(mod_privacy).
|
-behaviour(mod_privacy).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, process_lists_get/2, process_list_get/3,
|
-export([init/2, set_default/3, unset_default/2, set_lists/1,
|
||||||
process_default_set/3, process_active_set/3,
|
set_list/4, get_lists/2, get_list/3, remove_lists/2,
|
||||||
remove_privacy_list/3, set_privacy_list/1,
|
remove_list/3, use_cache/1, import/1]).
|
||||||
set_privacy_list/4, get_user_list/2, get_user_lists/2,
|
|
||||||
remove_user/2, import/1]).
|
|
||||||
-export([need_transform/1, transform/1]).
|
-export([need_transform/1, transform/1]).
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
@ -43,81 +41,65 @@
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
init(_Host, _Opts) ->
|
init(_Host, _Opts) ->
|
||||||
ejabberd_mnesia:create(?MODULE, privacy,
|
ejabberd_mnesia:create(?MODULE, privacy,
|
||||||
[{disc_copies, [node()]},
|
[{disc_only_copies, [node()]},
|
||||||
{attributes, record_info(fields, privacy)}]).
|
{attributes, record_info(fields, privacy)}]).
|
||||||
|
|
||||||
process_lists_get(LUser, LServer) ->
|
use_cache(Host) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
case mnesia:table_info(privacy, storage_type) of
|
||||||
{'EXIT', _Reason} -> error;
|
disc_only_copies ->
|
||||||
[] -> {none, []};
|
gen_mod:get_module_opt(
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
Host, mod_privacy, use_cache,
|
||||||
LItems = lists:map(fun ({N, _}) -> N end, Lists),
|
ejabberd_config:use_cache(Host));
|
||||||
{Default, LItems}
|
_ ->
|
||||||
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_list_get(LUser, LServer, Name) ->
|
unset_default(LUser, LServer) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> not_found;
|
|
||||||
[#privacy{lists = Lists}] ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> not_found
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_default_set(LUser, LServer, none) ->
|
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
[] -> ok;
|
[] -> ok;
|
||||||
[R] -> mnesia:write(R#privacy{default = none})
|
[R] -> mnesia:write(R#privacy{default = none})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F);
|
transaction(F).
|
||||||
process_default_set(LUser, LServer, Name) ->
|
|
||||||
|
set_default(LUser, LServer, Name) ->
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
[] -> not_found;
|
[] ->
|
||||||
|
{error, notfound};
|
||||||
[#privacy{lists = Lists} = P] ->
|
[#privacy{lists = Lists} = P] ->
|
||||||
case lists:keymember(Name, 1, Lists) of
|
case lists:keymember(Name, 1, Lists) of
|
||||||
true ->
|
true ->
|
||||||
mnesia:write(P#privacy{default = Name,
|
mnesia:write(P#privacy{default = Name,
|
||||||
lists = Lists}),
|
lists = Lists});
|
||||||
ok;
|
false ->
|
||||||
false -> not_found
|
{error, notfound}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
transaction(F).
|
||||||
|
|
||||||
process_active_set(LUser, LServer, Name) ->
|
remove_list(LUser, LServer, Name) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
|
||||||
[] -> error;
|
|
||||||
[#privacy{lists = Lists}] ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
false -> error
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
remove_privacy_list(LUser, LServer, Name) ->
|
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
[] -> ok;
|
[] ->
|
||||||
|
{error, notfound};
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
[#privacy{default = Default, lists = Lists} = P] ->
|
||||||
if Name == Default -> conflict;
|
if Name == Default ->
|
||||||
|
{error, conflict};
|
||||||
true ->
|
true ->
|
||||||
NewLists = lists:keydelete(Name, 1, Lists),
|
NewLists = lists:keydelete(Name, 1, Lists),
|
||||||
mnesia:write(P#privacy{lists = NewLists})
|
mnesia:write(P#privacy{lists = NewLists})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
transaction(F).
|
||||||
|
|
||||||
set_privacy_list(Privacy) ->
|
set_lists(Privacy) ->
|
||||||
mnesia:dirty_write(Privacy).
|
mnesia:dirty_write(Privacy).
|
||||||
|
|
||||||
set_privacy_list(LUser, LServer, Name, List) ->
|
set_list(LUser, LServer, Name, List) ->
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
case mnesia:wread({privacy, {LUser, LServer}}) of
|
||||||
[] ->
|
[] ->
|
||||||
@ -130,35 +112,35 @@ set_privacy_list(LUser, LServer, Name, List) ->
|
|||||||
mnesia:write(P#privacy{lists = NewLists})
|
mnesia:write(P#privacy{lists = NewLists})
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
mnesia:transaction(F).
|
transaction(F).
|
||||||
|
|
||||||
get_user_list(LUser, LServer) ->
|
get_list(LUser, LServer, Name) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
case mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
of
|
[#privacy{default = Default, lists = Lists}] when Name == default ->
|
||||||
[] -> {none, []};
|
case lists:keyfind(Default, 1, Lists) of
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
{_, List} -> {ok, {Default, List}};
|
||||||
case Default of
|
false -> error
|
||||||
none -> {none, []};
|
|
||||||
_ ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> {Default, List};
|
|
||||||
_ -> {none, []}
|
|
||||||
end
|
|
||||||
end;
|
end;
|
||||||
_ -> {none, []}
|
[#privacy{lists = Lists}] ->
|
||||||
|
case lists:keyfind(Name, 1, Lists) of
|
||||||
|
{_, List} -> {ok, {Name, List}};
|
||||||
|
false -> error
|
||||||
|
end;
|
||||||
|
[] ->
|
||||||
|
error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_user_lists(LUser, LServer) ->
|
get_lists(LUser, LServer) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
case mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
[#privacy{} = P] ->
|
[#privacy{} = P] ->
|
||||||
{ok, P};
|
{ok, P};
|
||||||
_ ->
|
_ ->
|
||||||
error
|
error
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(LUser, LServer) ->
|
remove_lists(LUser, LServer) ->
|
||||||
F = fun () -> mnesia:delete({privacy, {LUser, LServer}}) end,
|
F = fun () -> mnesia:delete({privacy, {LUser, LServer}}) end,
|
||||||
mnesia:transaction(F).
|
transaction(F).
|
||||||
|
|
||||||
import(#privacy{} = P) ->
|
import(#privacy{} = P) ->
|
||||||
mnesia:dirty_write(P).
|
mnesia:dirty_write(P).
|
||||||
@ -199,3 +181,11 @@ transform(#privacy{us = {U, S}, default = Def, lists = Lists} = R) ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
transaction(F) ->
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, Result} ->
|
||||||
|
Result;
|
||||||
|
{aborted, Reason} ->
|
||||||
|
?ERROR_MSG("Mnesia transaction failed: ~p", [Reason]),
|
||||||
|
{error, db_failure}
|
||||||
|
end.
|
||||||
|
@ -27,11 +27,9 @@
|
|||||||
-behaviour(mod_privacy).
|
-behaviour(mod_privacy).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, process_lists_get/2, process_list_get/3,
|
-export([init/2, set_default/3, unset_default/2, set_lists/1,
|
||||||
process_default_set/3, process_active_set/3,
|
set_list/4, get_lists/2, get_list/3, remove_lists/2,
|
||||||
remove_privacy_list/3, set_privacy_list/1,
|
remove_list/3, import/1]).
|
||||||
set_privacy_list/4, get_user_list/2, get_user_lists/2,
|
|
||||||
remove_user/2, import/1]).
|
|
||||||
|
|
||||||
-export([privacy_schema/0]).
|
-export([privacy_schema/0]).
|
||||||
|
|
||||||
@ -44,40 +42,17 @@
|
|||||||
init(_Host, _Opts) ->
|
init(_Host, _Opts) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
process_lists_get(LUser, LServer) ->
|
unset_default(LUser, LServer) ->
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists}} ->
|
|
||||||
LItems = lists:map(fun ({N, _}) -> N end, Lists),
|
|
||||||
{Default, LItems};
|
|
||||||
{error, notfound} ->
|
|
||||||
{none, []};
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_list_get(LUser, LServer, Name) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{lists = Lists}} ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> not_found
|
|
||||||
end;
|
|
||||||
{error, notfound} ->
|
|
||||||
not_found;
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_default_set(LUser, LServer, none) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
{ok, R} ->
|
{ok, R} ->
|
||||||
ejabberd_riak:put(R#privacy{default = none}, privacy_schema());
|
ejabberd_riak:put(R#privacy{default = none}, privacy_schema());
|
||||||
{error, _} ->
|
{error, notfound} ->
|
||||||
ok
|
ok;
|
||||||
end};
|
Err ->
|
||||||
process_default_set(LUser, LServer, Name) ->
|
Err
|
||||||
{atomic,
|
end.
|
||||||
|
|
||||||
|
set_default(LUser, LServer, Name) ->
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
{ok, #privacy{lists = Lists} = P} ->
|
{ok, #privacy{lists = Lists} = P} ->
|
||||||
case lists:keymember(Name, 1, Lists) of
|
case lists:keymember(Name, 1, Lists) of
|
||||||
@ -86,80 +61,74 @@ process_default_set(LUser, LServer, Name) ->
|
|||||||
lists = Lists},
|
lists = Lists},
|
||||||
privacy_schema());
|
privacy_schema());
|
||||||
false ->
|
false ->
|
||||||
not_found
|
{error, notfound}
|
||||||
end;
|
end;
|
||||||
{error, _} ->
|
Err ->
|
||||||
not_found
|
Err
|
||||||
end}.
|
|
||||||
|
|
||||||
process_active_set(LUser, LServer, Name) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{lists = Lists}} ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
false -> error
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_privacy_list(LUser, LServer, Name) ->
|
remove_list(LUser, LServer, Name) ->
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
||||||
if Name == Default ->
|
if Name == Default ->
|
||||||
conflict;
|
{error, conflict};
|
||||||
true ->
|
true ->
|
||||||
NewLists = lists:keydelete(Name, 1, Lists),
|
NewLists = lists:keydelete(Name, 1, Lists),
|
||||||
ejabberd_riak:put(P#privacy{lists = NewLists},
|
ejabberd_riak:put(P#privacy{lists = NewLists},
|
||||||
privacy_schema())
|
privacy_schema())
|
||||||
end;
|
end;
|
||||||
{error, _} ->
|
Err ->
|
||||||
ok
|
Err
|
||||||
end}.
|
end.
|
||||||
|
|
||||||
set_privacy_list(Privacy) ->
|
set_lists(Privacy) ->
|
||||||
ejabberd_riak:put(Privacy, privacy_schema()).
|
ejabberd_riak:put(Privacy, privacy_schema()).
|
||||||
|
|
||||||
set_privacy_list(LUser, LServer, Name, List) ->
|
set_list(LUser, LServer, Name, List) ->
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
{ok, #privacy{lists = Lists} = P} ->
|
{ok, #privacy{lists = Lists} = P} ->
|
||||||
NewLists1 = lists:keydelete(Name, 1, Lists),
|
NewLists1 = lists:keydelete(Name, 1, Lists),
|
||||||
NewLists = [{Name, List} | NewLists1],
|
NewLists = [{Name, List} | NewLists1],
|
||||||
ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema());
|
ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema());
|
||||||
{error, _} ->
|
{error, notfound} ->
|
||||||
NewLists = [{Name, List}],
|
NewLists = [{Name, List}],
|
||||||
ejabberd_riak:put(#privacy{us = {LUser, LServer},
|
ejabberd_riak:put(#privacy{us = {LUser, LServer},
|
||||||
lists = NewLists},
|
lists = NewLists},
|
||||||
privacy_schema())
|
privacy_schema());
|
||||||
end}.
|
Err ->
|
||||||
|
Err
|
||||||
get_user_list(LUser, LServer) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists}} ->
|
|
||||||
case Default of
|
|
||||||
none -> {none, []};
|
|
||||||
_ ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> {Default, List};
|
|
||||||
_ -> {none, []}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
{none, []}
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_user_lists(LUser, LServer) ->
|
get_list(LUser, LServer, Name) ->
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{default = Default, lists = Lists}} when Name == default ->
|
||||||
|
case lists:keyfind(Default, 1, Lists) of
|
||||||
|
{_, List} -> {ok, {Default, List}};
|
||||||
|
false -> error
|
||||||
|
end;
|
||||||
|
{ok, #privacy{lists = Lists}} ->
|
||||||
|
case lists:keyfind(Name, 1, Lists) of
|
||||||
|
{_, List} -> {ok, {Name, List}};
|
||||||
|
false -> error
|
||||||
|
end;
|
||||||
|
{error, notfound} ->
|
||||||
|
error;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_lists(LUser, LServer) ->
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
{ok, #privacy{} = P} ->
|
{ok, #privacy{} = P} ->
|
||||||
{ok, P};
|
{ok, P};
|
||||||
{error, _} ->
|
{error, notfound} ->
|
||||||
error
|
error;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(LUser, LServer) ->
|
remove_lists(LUser, LServer) ->
|
||||||
{atomic, ejabberd_riak:delete(privacy, {LUser, LServer})}.
|
ejabberd_riak:delete(privacy, {LUser, LServer}).
|
||||||
|
|
||||||
import(#privacy{} = P) ->
|
import(#privacy{} = P) ->
|
||||||
ejabberd_riak:put(P, privacy_schema()).
|
ejabberd_riak:put(P, privacy_schema()).
|
||||||
|
@ -29,20 +29,11 @@
|
|||||||
-behaviour(mod_privacy).
|
-behaviour(mod_privacy).
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([init/2, process_lists_get/2, process_list_get/3,
|
-export([init/2, set_default/3, unset_default/2, set_lists/1,
|
||||||
process_default_set/3, process_active_set/3,
|
set_list/4, get_lists/2, get_list/3, remove_lists/2,
|
||||||
remove_privacy_list/3, set_privacy_list/1,
|
remove_list/3, import/1, export/1]).
|
||||||
set_privacy_list/4, get_user_list/2, get_user_lists/2,
|
|
||||||
remove_user/2, import/1, export/1]).
|
|
||||||
|
|
||||||
-export([item_to_raw/1, raw_to_item/1,
|
-export([item_to_raw/1, raw_to_item/1]).
|
||||||
sql_add_privacy_list/2,
|
|
||||||
sql_get_default_privacy_list/2,
|
|
||||||
sql_get_default_privacy_list_t/1,
|
|
||||||
sql_get_privacy_list_data/3,
|
|
||||||
sql_get_privacy_list_data_by_id_t/1,
|
|
||||||
sql_get_privacy_list_id_t/2,
|
|
||||||
sql_set_default_privacy_list/2, sql_set_privacy_list/2]).
|
|
||||||
|
|
||||||
-include("xmpp.hrl").
|
-include("xmpp.hrl").
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
@ -55,159 +46,143 @@
|
|||||||
init(_Host, _Opts) ->
|
init(_Host, _Opts) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
process_lists_get(LUser, LServer) ->
|
unset_default(LUser, LServer) ->
|
||||||
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
|
case unset_default_privacy_list(LUser, LServer) of
|
||||||
{selected, []} -> none;
|
ok ->
|
||||||
{selected, [{DefName}]} -> DefName;
|
ok;
|
||||||
_ -> none
|
_Err ->
|
||||||
end,
|
{error, db_failure}
|
||||||
case catch sql_get_privacy_list_names(LUser, LServer) of
|
|
||||||
{selected, Names} ->
|
|
||||||
LItems = lists:map(fun ({N}) -> N end, Names),
|
|
||||||
{Default, LItems};
|
|
||||||
_ -> error
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_list_get(LUser, LServer, Name) ->
|
set_default(LUser, LServer, Name) ->
|
||||||
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
|
|
||||||
{selected, []} -> not_found;
|
|
||||||
{selected, [{ID}]} ->
|
|
||||||
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
lists:flatmap(fun raw_to_item/1, RItems);
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_default_set(LUser, LServer, none) ->
|
|
||||||
case catch sql_unset_default_privacy_list(LUser,
|
|
||||||
LServer)
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> {atomic, error};
|
|
||||||
{error, _Reason} -> {atomic, error};
|
|
||||||
_ -> {atomic, ok}
|
|
||||||
end;
|
|
||||||
process_default_set(LUser, LServer, Name) ->
|
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case sql_get_privacy_list_names_t(LUser) of
|
case get_privacy_list_names_t(LUser) of
|
||||||
{selected, []} -> not_found;
|
{selected, []} ->
|
||||||
|
{error, notfound};
|
||||||
{selected, Names} ->
|
{selected, Names} ->
|
||||||
case lists:member({Name}, Names) of
|
case lists:member({Name}, Names) of
|
||||||
true -> sql_set_default_privacy_list(LUser, Name), ok;
|
true ->
|
||||||
false -> not_found
|
set_default_privacy_list(LUser, Name);
|
||||||
|
false ->
|
||||||
|
{error, notfound}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
sql_queries:sql_transaction(LServer, F).
|
transaction(LServer, F).
|
||||||
|
|
||||||
process_active_set(LUser, LServer, Name) ->
|
remove_list(LUser, LServer, Name) ->
|
||||||
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
|
|
||||||
{selected, []} -> error;
|
|
||||||
{selected, [{ID}]} ->
|
|
||||||
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
lists:flatmap(fun raw_to_item/1, RItems);
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
remove_privacy_list(LUser, LServer, Name) ->
|
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
case sql_get_default_privacy_list_t(LUser) of
|
case get_default_privacy_list_t(LUser) of
|
||||||
{selected, []} ->
|
{selected, []} ->
|
||||||
sql_remove_privacy_list(LUser, Name), ok;
|
remove_privacy_list(LUser, Name);
|
||||||
{selected, [{Default}]} ->
|
{selected, [{Default}]} ->
|
||||||
if Name == Default -> conflict;
|
if Name == Default ->
|
||||||
true -> sql_remove_privacy_list(LUser, Name), ok
|
{error, conflict};
|
||||||
|
true ->
|
||||||
|
remove_privacy_list(LUser, Name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
sql_queries:sql_transaction(LServer, F).
|
transaction(LServer, F).
|
||||||
|
|
||||||
set_privacy_list(#privacy{us = {LUser, LServer},
|
set_lists(#privacy{us = {LUser, LServer},
|
||||||
default = Default,
|
default = Default,
|
||||||
lists = Lists}) ->
|
lists = Lists}) ->
|
||||||
F = fun() ->
|
F = fun() ->
|
||||||
lists:foreach(
|
lists:foreach(
|
||||||
fun({Name, List}) ->
|
fun({Name, List}) ->
|
||||||
sql_add_privacy_list(LUser, Name),
|
add_privacy_list(LUser, Name),
|
||||||
{selected, [<<"id">>], [[I]]} =
|
{selected, [<<"id">>], [[I]]} =
|
||||||
sql_get_privacy_list_id_t(LUser, Name),
|
get_privacy_list_id_t(LUser, Name),
|
||||||
RItems = lists:map(fun item_to_raw/1, List),
|
RItems = lists:map(fun item_to_raw/1, List),
|
||||||
sql_set_privacy_list(I, RItems),
|
set_privacy_list(I, RItems),
|
||||||
if is_binary(Default) ->
|
if is_binary(Default) ->
|
||||||
sql_set_default_privacy_list(LUser, Default),
|
set_default_privacy_list(LUser, Default);
|
||||||
ok;
|
|
||||||
true ->
|
true ->
|
||||||
ok
|
ok
|
||||||
end
|
end
|
||||||
end, Lists)
|
end, Lists)
|
||||||
end,
|
end,
|
||||||
sql_queries:sql_transaction(LServer, F).
|
transaction(LServer, F).
|
||||||
|
|
||||||
set_privacy_list(LUser, LServer, Name, List) ->
|
set_list(LUser, LServer, Name, List) ->
|
||||||
RItems = lists:map(fun item_to_raw/1, List),
|
RItems = lists:map(fun item_to_raw/1, List),
|
||||||
F = fun () ->
|
F = fun () ->
|
||||||
ID = case sql_get_privacy_list_id_t(LUser, Name) of
|
ID = case get_privacy_list_id_t(LUser, Name) of
|
||||||
{selected, []} ->
|
{selected, []} ->
|
||||||
sql_add_privacy_list(LUser, Name),
|
add_privacy_list(LUser, Name),
|
||||||
{selected, [{I}]} =
|
{selected, [{I}]} =
|
||||||
sql_get_privacy_list_id_t(LUser, Name),
|
get_privacy_list_id_t(LUser, Name),
|
||||||
I;
|
I;
|
||||||
{selected, [{I}]} -> I
|
{selected, [{I}]} -> I
|
||||||
end,
|
end,
|
||||||
sql_set_privacy_list(ID, RItems),
|
set_privacy_list(ID, RItems)
|
||||||
ok
|
|
||||||
end,
|
end,
|
||||||
sql_queries:sql_transaction(LServer, F).
|
transaction(LServer, F).
|
||||||
|
|
||||||
get_user_list(LUser, LServer) ->
|
get_list(LUser, LServer, default) ->
|
||||||
case catch sql_get_default_privacy_list(LUser, LServer)
|
case get_default_privacy_list(LUser, LServer) of
|
||||||
of
|
{selected, []} ->
|
||||||
{selected, []} -> {none, []};
|
error;
|
||||||
{selected, [{Default}]} ->
|
{selected, [{Default}]} ->
|
||||||
case catch sql_get_privacy_list_data(LUser, LServer,
|
get_list(LUser, LServer, Default);
|
||||||
Default) of
|
_Err ->
|
||||||
{selected, RItems} ->
|
{error, db_failure}
|
||||||
{Default, lists:flatmap(fun raw_to_item/1, RItems)};
|
|
||||||
_ -> {none, []}
|
|
||||||
end;
|
end;
|
||||||
_ -> {none, []}
|
get_list(LUser, LServer, Name) ->
|
||||||
|
case get_privacy_list_data(LUser, LServer, Name) of
|
||||||
|
{selected, []} ->
|
||||||
|
error;
|
||||||
|
{selected, RItems} ->
|
||||||
|
{ok, {Name, lists:flatmap(fun raw_to_item/1, RItems)}};
|
||||||
|
_Err ->
|
||||||
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_user_lists(LUser, LServer) ->
|
get_lists(LUser, LServer) ->
|
||||||
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
|
case get_default_privacy_list(LUser, LServer) of
|
||||||
{selected, []} ->
|
{selected, Selected} ->
|
||||||
none;
|
Default = case Selected of
|
||||||
{selected, [{DefName}]} ->
|
[] -> none;
|
||||||
DefName;
|
[{DefName}] -> DefName
|
||||||
_ ->
|
|
||||||
none
|
|
||||||
end,
|
end,
|
||||||
case catch sql_get_privacy_list_names(LUser, LServer) of
|
case get_privacy_list_names(LUser, LServer) of
|
||||||
{selected, Names} ->
|
{selected, Names} ->
|
||||||
Lists =
|
case lists:foldl(
|
||||||
lists:flatmap(
|
fun(_, {error, _} = Err) ->
|
||||||
fun({Name}) ->
|
Err;
|
||||||
case catch sql_get_privacy_list_data(
|
({Name}, Acc) ->
|
||||||
LUser, LServer, Name) of
|
case get_privacy_list_data(LUser, LServer, Name) of
|
||||||
{selected, RItems} ->
|
{selected, RItems} ->
|
||||||
[{Name, lists:flatmap(fun raw_to_item/1, RItems)}];
|
Items = lists:flatmap(
|
||||||
_ ->
|
fun raw_to_item/1,
|
||||||
[]
|
RItems),
|
||||||
|
[{Name, Items}|Acc];
|
||||||
|
_Err ->
|
||||||
|
{error, db_failure}
|
||||||
end
|
end
|
||||||
end, Names),
|
end, [], Names) of
|
||||||
|
{error, Reason} ->
|
||||||
|
{error, Reason};
|
||||||
|
Lists ->
|
||||||
{ok, #privacy{default = Default,
|
{ok, #privacy{default = Default,
|
||||||
us = {LUser, LServer},
|
us = {LUser, LServer},
|
||||||
lists = Lists}};
|
lists = Lists}}
|
||||||
_ ->
|
end;
|
||||||
error
|
_Err ->
|
||||||
|
{error, db_failure}
|
||||||
|
end;
|
||||||
|
_Err ->
|
||||||
|
{error, db_failure}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
remove_user(LUser, LServer) ->
|
remove_lists(LUser, LServer) ->
|
||||||
sql_del_privacy_lists(LUser, LServer).
|
case del_privacy_lists(LUser, LServer) of
|
||||||
|
ok ->
|
||||||
|
ok;
|
||||||
|
_Err ->
|
||||||
|
{error, db_failure}
|
||||||
|
end.
|
||||||
|
|
||||||
export(Server) ->
|
export(Server) ->
|
||||||
case catch ejabberd_sql:sql_query(jid:nameprep(Server),
|
case catch ejabberd_sql:sql_query(jid:nameprep(Server),
|
||||||
@ -271,6 +246,12 @@ import(_) ->
|
|||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
transaction(LServer, F) ->
|
||||||
|
case ejabberd_sql:sql_transaction(LServer, F) of
|
||||||
|
{atomic, Res} -> Res;
|
||||||
|
{aborted, _Reason} -> {error, db_failure}
|
||||||
|
end.
|
||||||
|
|
||||||
raw_to_item({SType, SValue, SAction, Order, MatchAll,
|
raw_to_item({SType, SValue, SAction, Order, MatchAll,
|
||||||
MatchIQ, MatchMessage, MatchPresenceIn,
|
MatchIQ, MatchMessage, MatchPresenceIn,
|
||||||
MatchPresenceOut} = Row) ->
|
MatchPresenceOut} = Row) ->
|
||||||
@ -327,47 +308,48 @@ item_to_raw(#listitem{type = Type, value = Value,
|
|||||||
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
|
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
|
||||||
MatchMessage, MatchPresenceIn, MatchPresenceOut}.
|
MatchMessage, MatchPresenceIn, MatchPresenceOut}.
|
||||||
|
|
||||||
sql_get_default_privacy_list(LUser, LServer) ->
|
get_default_privacy_list(LUser, LServer) ->
|
||||||
sql_queries:get_default_privacy_list(LServer, LUser).
|
sql_queries:get_default_privacy_list(LServer, LUser).
|
||||||
|
|
||||||
sql_get_default_privacy_list_t(LUser) ->
|
get_default_privacy_list_t(LUser) ->
|
||||||
sql_queries:get_default_privacy_list_t(LUser).
|
sql_queries:get_default_privacy_list_t(LUser).
|
||||||
|
|
||||||
sql_get_privacy_list_names(LUser, LServer) ->
|
get_privacy_list_names(LUser, LServer) ->
|
||||||
sql_queries:get_privacy_list_names(LServer, LUser).
|
sql_queries:get_privacy_list_names(LServer, LUser).
|
||||||
|
|
||||||
sql_get_privacy_list_names_t(LUser) ->
|
get_privacy_list_names_t(LUser) ->
|
||||||
sql_queries:get_privacy_list_names_t(LUser).
|
sql_queries:get_privacy_list_names_t(LUser).
|
||||||
|
|
||||||
sql_get_privacy_list_id(LUser, LServer, Name) ->
|
get_privacy_list_id_t(LUser, Name) ->
|
||||||
sql_queries:get_privacy_list_id(LServer, LUser, Name).
|
|
||||||
|
|
||||||
sql_get_privacy_list_id_t(LUser, Name) ->
|
|
||||||
sql_queries:get_privacy_list_id_t(LUser, Name).
|
sql_queries:get_privacy_list_id_t(LUser, Name).
|
||||||
|
|
||||||
sql_get_privacy_list_data(LUser, LServer, Name) ->
|
get_privacy_list_data(LUser, LServer, Name) ->
|
||||||
sql_queries:get_privacy_list_data(LServer, LUser, Name).
|
sql_queries:get_privacy_list_data(LServer, LUser, Name).
|
||||||
|
|
||||||
sql_get_privacy_list_data_by_id(ID, LServer) ->
|
set_default_privacy_list(LUser, Name) ->
|
||||||
sql_queries:get_privacy_list_data_by_id(LServer, ID).
|
|
||||||
|
|
||||||
sql_get_privacy_list_data_by_id_t(ID) ->
|
|
||||||
sql_queries:get_privacy_list_data_by_id_t(ID).
|
|
||||||
|
|
||||||
sql_set_default_privacy_list(LUser, Name) ->
|
|
||||||
sql_queries:set_default_privacy_list(LUser, Name).
|
sql_queries:set_default_privacy_list(LUser, Name).
|
||||||
|
|
||||||
sql_unset_default_privacy_list(LUser, LServer) ->
|
unset_default_privacy_list(LUser, LServer) ->
|
||||||
sql_queries:unset_default_privacy_list(LServer, LUser).
|
case sql_queries:unset_default_privacy_list(LServer, LUser) of
|
||||||
|
{updated, _} -> ok;
|
||||||
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
sql_remove_privacy_list(LUser, Name) ->
|
remove_privacy_list(LUser, Name) ->
|
||||||
sql_queries:remove_privacy_list(LUser, Name).
|
case sql_queries:remove_privacy_list(LUser, Name) of
|
||||||
|
{updated, 0} -> {error, notfound};
|
||||||
|
{updated, _} -> ok;
|
||||||
|
Err -> Err
|
||||||
|
end.
|
||||||
|
|
||||||
sql_add_privacy_list(LUser, Name) ->
|
add_privacy_list(LUser, Name) ->
|
||||||
sql_queries:add_privacy_list(LUser, Name).
|
sql_queries:add_privacy_list(LUser, Name).
|
||||||
|
|
||||||
sql_set_privacy_list(ID, RItems) ->
|
set_privacy_list(ID, RItems) ->
|
||||||
sql_queries:set_privacy_list(ID, RItems).
|
sql_queries:set_privacy_list(ID, RItems).
|
||||||
|
|
||||||
sql_del_privacy_lists(LUser, LServer) ->
|
del_privacy_lists(LUser, LServer) ->
|
||||||
sql_queries:del_privacy_lists(LServer, LUser).
|
case sql_queries:del_privacy_lists(LServer, LUser) of
|
||||||
|
{updated, _} -> ok;
|
||||||
|
Err -> Err
|
||||||
|
end.
|
||||||
|
@ -368,15 +368,16 @@ get_roster_item(LUser, LServer, LJID) ->
|
|||||||
{ok, Item} ->
|
{ok, Item} ->
|
||||||
Item;
|
Item;
|
||||||
error ->
|
error ->
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
LBJID = jid:remove_resource(LJID),
|
||||||
us = {LUser, LServer}, jid = LJID}
|
#roster{usj = {LUser, LServer, LBJID},
|
||||||
|
us = {LUser, LServer}, jid = LBJID}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_subscription_and_groups(LUser, LServer, LJID) ->
|
get_subscription_and_groups(LUser, LServer, LJID) ->
|
||||||
|
LBJID = jid:remove_resource(LJID),
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Res = case use_cache(Mod, LServer, roster) of
|
Res = case use_cache(Mod, LServer, roster) of
|
||||||
true ->
|
true ->
|
||||||
LBJID = jid:remove_resource(LJID),
|
|
||||||
ets_cache:lookup(
|
ets_cache:lookup(
|
||||||
?ROSTER_ITEM_CACHE, {LUser, LServer, LBJID},
|
?ROSTER_ITEM_CACHE, {LUser, LServer, LBJID},
|
||||||
fun() ->
|
fun() ->
|
||||||
@ -384,19 +385,12 @@ get_subscription_and_groups(LUser, LServer, LJID) ->
|
|||||||
case lists:keyfind(LBJID, #roster.jid, Items) of
|
case lists:keyfind(LBJID, #roster.jid, Items) of
|
||||||
#roster{subscription = Sub, groups = Groups} ->
|
#roster{subscription = Sub, groups = Groups} ->
|
||||||
{ok, {Sub, Groups}};
|
{ok, {Sub, Groups}};
|
||||||
false when element(3, LJID) == <<"">> ->
|
|
||||||
error;
|
|
||||||
false ->
|
|
||||||
case lists:keyfind(LJID, #roster.jid, Items) of
|
|
||||||
{Sub, Groups} ->
|
|
||||||
{ok, {Sub, Groups}};
|
|
||||||
false ->
|
false ->
|
||||||
error
|
error
|
||||||
end
|
end
|
||||||
end
|
|
||||||
end);
|
end);
|
||||||
false ->
|
false ->
|
||||||
Mod:read_subscription_and_groups(LUser, LServer, LJID)
|
Mod:read_subscription_and_groups(LUser, LServer, LBJID)
|
||||||
end,
|
end,
|
||||||
case Res of
|
case Res of
|
||||||
{ok, SubAndGroups} ->
|
{ok, SubAndGroups} ->
|
||||||
|
@ -210,7 +210,7 @@ convert_data(Host, "privacy", User, [Data]) ->
|
|||||||
ListItems -> [{Name, ListItems}]
|
ListItems -> [{Name, ListItems}]
|
||||||
end
|
end
|
||||||
end, Lists)},
|
end, Lists)},
|
||||||
mod_privacy:set_privacy_list(Priv);
|
mod_privacy:set_list(Priv);
|
||||||
convert_data(_Host, _Type, _User, _Data) ->
|
convert_data(_Host, _Type, _User, _Data) ->
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
@ -43,6 +43,7 @@ single_cases() ->
|
|||||||
[single_test(feature_enabled),
|
[single_test(feature_enabled),
|
||||||
single_test(set_get_list),
|
single_test(set_get_list),
|
||||||
single_test(get_list_non_existent),
|
single_test(get_list_non_existent),
|
||||||
|
single_test(get_empty_lists),
|
||||||
single_test(set_default),
|
single_test(set_default),
|
||||||
single_test(del_default),
|
single_test(del_default),
|
||||||
single_test(set_default_non_existent),
|
single_test(set_default_non_existent),
|
||||||
@ -52,8 +53,7 @@ single_cases() ->
|
|||||||
single_test(remove_list),
|
single_test(remove_list),
|
||||||
single_test(remove_default_list),
|
single_test(remove_default_list),
|
||||||
single_test(remove_active_list),
|
single_test(remove_active_list),
|
||||||
%% TODO: this should be fixed
|
single_test(remove_list_non_existent),
|
||||||
%% single_test(remove_list_non_existent),
|
|
||||||
single_test(allow_local_server),
|
single_test(allow_local_server),
|
||||||
single_test(malformed_iq_query),
|
single_test(malformed_iq_query),
|
||||||
single_test(malformed_get),
|
single_test(malformed_get),
|
||||||
@ -98,6 +98,12 @@ get_list_non_existent(Config) ->
|
|||||||
#stanza_error{reason = 'item-not-found'} = get_list(Config, ListName),
|
#stanza_error{reason = 'item-not-found'} = get_list(Config, ListName),
|
||||||
disconnect(Config).
|
disconnect(Config).
|
||||||
|
|
||||||
|
get_empty_lists(Config) ->
|
||||||
|
#privacy_query{default = none,
|
||||||
|
active = none,
|
||||||
|
lists = []} = get_lists(Config),
|
||||||
|
disconnect(Config).
|
||||||
|
|
||||||
set_default(Config) ->
|
set_default(Config) ->
|
||||||
ListName = <<"set-default">>,
|
ListName = <<"set-default">>,
|
||||||
Item = #privacy_item{order = 0, action = deny},
|
Item = #privacy_item{order = 0, action = deny},
|
||||||
@ -561,12 +567,6 @@ del_list(Config, Name) ->
|
|||||||
lists = [#privacy_list{
|
lists = [#privacy_list{
|
||||||
name = Name}]}]}) of
|
name = Name}]}]}) of
|
||||||
#iq{type = result, sub_els = []} ->
|
#iq{type = result, sub_els = []} ->
|
||||||
ct:comment("Receiving privacy list push"),
|
|
||||||
#iq{type = set, id = ID,
|
|
||||||
sub_els = [#privacy_query{lists = [#privacy_list{
|
|
||||||
name = Name}]}]} =
|
|
||||||
recv_iq(Config),
|
|
||||||
send(Config, #iq{type = result, id = ID}),
|
|
||||||
ok;
|
ok;
|
||||||
#iq{type = error} = Err ->
|
#iq{type = error} = Err ->
|
||||||
xmpp:get_error(Err)
|
xmpp:get_error(Err)
|
||||||
|
Loading…
Reference in New Issue
Block a user