aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>2017-05-20 22:36:32 +0300
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>2017-05-20 22:36:32 +0300
commit35d19b32f40a4f6aedecca36c3145ab3013ecf87 (patch)
tree2d724e209978999b79fe69be9d0be679cf4fb559
parentexport_all is not needed here (diff)
Implement cache for mod_privacy/mod_blocking
-rw-r--r--include/mod_privacy.hrl8
-rw-r--r--src/mod_admin_extra.erl5
-rw-r--r--src/mod_blocking.erl212
-rw-r--r--src/mod_blocking_mnesia.erl100
-rw-r--r--src/mod_blocking_riak.erl113
-rw-r--r--src/mod_blocking_sql.erl107
-rw-r--r--src/mod_privacy.erl496
-rw-r--r--src/mod_privacy_mnesia.erl152
-rw-r--r--src/mod_privacy_riak.erl173
-rw-r--r--src/mod_privacy_sql.erl302
-rw-r--r--src/mod_roster.erl18
-rw-r--r--src/prosody2ejabberd.erl2
-rw-r--r--test/privacy_tests.erl16
13 files changed, 714 insertions, 990 deletions
diff --git a/include/mod_privacy.hrl b/include/mod_privacy.hrl
index b628a5e1e..0d773fb20 100644
--- a/include/mod_privacy.hrl
+++ b/include/mod_privacy.hrl
@@ -38,11 +38,3 @@
-type listitem_type() :: none | jid | group | subscription.
-type listitem_value() :: none | both | from | to | jid:ljid() | binary().
-type listitem_action() :: allow | deny.
-
--record(userlist, {name = none :: none | binary(),
- list = [] :: [listitem()],
- needdb = false :: boolean()}).
-
--type userlist() :: #userlist{}.
-
--export_type([userlist/0]).
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 0b3b007ce..fa681f87a 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -1485,10 +1485,7 @@ privacy_set(Username, Host, QueryS) ->
SubEl = xmpp:decode(QueryEl),
IQ = #iq{type = set, id = <<"push">>, sub_els = [SubEl],
from = From, to = To},
- ejabberd_hooks:run_fold(privacy_iq_set,
- Host,
- {error, xmpp:err_feature_not_implemented()},
- [IQ, #userlist{}]),
+ mod_privacy:process_iq(IQ),
ok.
%%%
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index 6b6f4f19a..738c5e16f 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -39,12 +39,6 @@
-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) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
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).
-spec process_block(iq(), [ljid()]) -> iq().
-process_block(#iq{from = #jid{luser = LUser, lserver = LServer},
- lang = Lang} = IQ, JIDs) ->
- Filter = fun (List) ->
- AlreadyBlocked = listitems_to_jids(List, []),
- lists:foldr(fun (JID, List1) ->
- case lists:member(JID, AlreadyBlocked)
- of
- true -> List1;
- false ->
- [#listitem{type = jid,
- value = JID,
- action = deny,
- order = 0,
- match_all = true}
- | List1]
- end
- end,
- List, JIDs)
- end,
- Mod = db_mod(LServer),
- case Mod:process_blocklist_block(LUser, LServer, Filter) of
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, 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)
+process_block(#iq{from = From} = IQ, LJIDs) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case mod_privacy:get_user_list(LUser, LServer, default) of
+ {error, _} ->
+ err_db_failure(IQ);
+ Res ->
+ {Name, List} = case Res of
+ error -> {<<"Blocked contacts">>, []};
+ {ok, NameList} -> NameList
+ end,
+ AlreadyBlocked = listitems_to_jids(List, []),
+ NewList = lists:foldr(
+ fun(LJID, List1) ->
+ case lists:member(LJID, AlreadyBlocked) of
+ true ->
+ List1;
+ false ->
+ [#listitem{type = jid,
+ value = LJID,
+ action = deny,
+ order = 0,
+ match_all = true}|List1]
+ end
+ end, List, LJIDs),
+ case mod_privacy:set_list(LUser, LServer, Name, NewList) of
+ ok ->
+ case (if Res == error ->
+ mod_privacy:set_default_list(
+ LUser, LServer, Name);
+ true ->
+ ok
+ end) of
+ ok ->
+ mod_privacy:push_list_update(From, Name),
+ Items = [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.
-spec process_unblock_all(iq()) -> iq().
-process_unblock_all(#iq{from = #jid{luser = LUser, lserver = LServer},
- lang = Lang} = IQ) ->
- Filter = fun (List) ->
- lists:filter(fun (#listitem{action = A}) -> A =/= deny
- end,
- List)
- end,
- Mod = db_mod(LServer),
- case Mod:unblock_by_filter(LUser, LServer, Filter) of
- {atomic, ok} ->
+process_unblock_all(#iq{from = From} = IQ) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case mod_privacy:get_user_list(LUser, LServer, default) of
+ {ok, {Name, List}} ->
+ NewList = lists:filter(
+ fun(#listitem{action = A}) ->
+ A /= deny
+ end, List),
+ case mod_privacy:set_list(LUser, LServer, Name, NewList) of
+ ok ->
+ mod_privacy:push_list_update(From, Name),
+ broadcast_event(From, #unblock{}),
+ xmpp:make_iq_result(IQ);
+ {error, _} ->
+ err_db_failure(IQ)
+ end;
+ error ->
+ broadcast_event(From, #unblock{}),
xmpp:make_iq_result(IQ);
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, UserList, Default),
- broadcast_event(LUser, LServer, #unblock{}),
- xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, UserList));
- _Err ->
- ?ERROR_MSG("Error processing ~p: ~p", [{LUser, LServer}, _Err]),
- Err = xmpp:err_internal_server_error(<<"Database failure">>, Lang),
- xmpp:make_error(IQ, Err)
+ {error, _} ->
+ err_db_failure(IQ)
end.
-spec process_unblock(iq(), [ljid()]) -> iq().
-process_unblock(#iq{from = #jid{luser = LUser, lserver = LServer},
- lang = Lang} = IQ, JIDs) ->
- Filter = fun (List) ->
- lists:filter(fun (#listitem{action = deny, type = jid,
- value = JID}) ->
- not lists:member(JID, JIDs);
- (_) -> true
- end,
- List)
- end,
- Mod = db_mod(LServer),
- case Mod:unblock_by_filter(LUser, LServer, Filter) of
- {atomic, ok} ->
+process_unblock(#iq{from = From} = IQ, LJIDs) ->
+ #jid{luser = LUser, lserver = LServer} = From,
+ case mod_privacy:get_user_list(LUser, LServer, default) of
+ {ok, {Name, List}} ->
+ NewList = lists:filter(
+ fun(#listitem{action = deny, type = jid,
+ value = LJID}) ->
+ not lists:member(LJID, LJIDs);
+ (_) ->
+ true
+ end, List),
+ case mod_privacy:set_list(LUser, LServer, Name, NewList) of
+ ok ->
+ mod_privacy:push_list_update(From, Name),
+ Items = [jid:make(LJID) || LJID <- LJIDs],
+ broadcast_event(From, #unblock{items = Items}),
+ xmpp:make_iq_result(IQ);
+ {error, _} ->
+ err_db_failure(IQ)
+ end;
+ error ->
+ Items = [jid:make(LJID) || LJID <- LJIDs],
+ broadcast_event(From, #unblock{items = Items}),
xmpp:make_iq_result(IQ);
- {atomic, {ok, Default, List}} ->
- UserList = make_userlist(Default, List),
- broadcast_list_update(LUser, LServer, UserList, Default),
- broadcast_event(LUser, LServer,
- #unblock{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)
+ {error, _} ->
+ err_db_failure(IQ)
end.
--spec make_userlist(binary(), [listitem()]) -> userlist().
-make_userlist(Name, List) ->
- 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),
+-spec broadcast_event(jid(), block() | unblock()) -> ok.
+broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
lists:foreach(
fun(R) ->
To = jid:replace_resource(From, R),
@@ -247,23 +251,21 @@ broadcast_event(LUser, LServer, Event) ->
end, ejabberd_sm:get_user_resources(LUser, LServer)).
-spec process_get(iq()) -> iq().
-process_get(#iq{from = #jid{luser = LUser, lserver = LServer},
- lang = Lang} = IQ) ->
- Mod = db_mod(LServer),
- case Mod:process_blocklist_get(LUser, LServer) of
- error ->
- Err = xmpp:err_internal_server_error(<<"Database failure">>, Lang),
- xmpp:make_error(IQ, Err);
- List ->
+process_get(#iq{from = #jid{luser = LUser, lserver = LServer}} = IQ) ->
+ case mod_privacy:get_user_list(LUser, LServer, default) of
+ {ok, {_, List}} ->
LJIDs = listitems_to_jids(List, []),
- Items = [jid:make(J) || J <- LJIDs],
- xmpp:make_iq_result(IQ, #block_list{items = Items})
+ Items = [jid:make(J) || J <- LJIDs],
+ xmpp:make_iq_result(IQ, #block_list{items = Items});
+ error ->
+ xmpp:make_iq_result(IQ, #block_list{});
+ {error, _} ->
+ err_db_failure(IQ)
end.
--spec db_mod(binary()) -> module().
-db_mod(LServer) ->
- DBType = gen_mod:db_type(LServer, mod_privacy),
- gen_mod:db_mod(DBType, ?MODULE).
+err_db_failure(#iq{lang = Lang} = IQ) ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang)).
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
mod_opt_type(_) -> [iqdisc].
diff --git a/src/mod_blocking_mnesia.erl b/src/mod_blocking_mnesia.erl
deleted file mode 100644
index f22e8171d..000000000
--- a/src/mod_blocking_mnesia.erl
+++ /dev/null
@@ -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
-%%%===================================================================
diff --git a/src/mod_blocking_riak.erl b/src/mod_blocking_riak.erl
deleted file mode 100644
index 307cd8744..000000000
--- a/src/mod_blocking_riak.erl
+++ /dev/null
@@ -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
-%%%===================================================================
diff --git a/src/mod_blocking_sql.erl b/src/mod_blocking_sql.erl
deleted file mode 100644
index 191c389d9..000000000
--- a/src/mod_blocking_sql.erl
+++ /dev/null
@@ -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
-%%%===================================================================
diff --git a/src/mod_privacy.erl b/src/mod_privacy.erl
index 32a103e40..eca229813 100644
--- a/src/mod_privacy.erl
+++ b/src/mod_privacy.erl
@@ -31,42 +31,51 @@
-behaviour(gen_mod).
--export([start/2, stop/1, reload/3, process_iq/1, export/1, import_info/0,
- c2s_session_opened/1, c2s_copy_session/2, push_list_update/3,
- user_send_packet/1, user_receive_packet/1, disco_features/5,
+-export([start/2, stop/1, reload/3, process_iq/1, export/1,
+ c2s_copy_session/2, push_list_update/2, disco_features/5,
check_packet/4, remove_user/2, encode_list_item/1,
- is_list_needdb/1, import_start/2, import_stop/2,
- item_to_xml/1, get_user_lists/2, import/5,
- set_privacy_list/1, mod_opt_type/1, depends/2]).
+ get_user_lists/2, get_user_list/3,
+ set_list/1, set_list/4, set_default_list/3,
+ 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("logger.hrl").
-
-include("xmpp.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 import(#privacy{}) -> ok.
--callback process_lists_get(binary(), binary()) -> {none | binary(), [binary()]} | error.
--callback process_list_get(binary(), binary(), binary()) -> [listitem()] | error | not_found.
--callback process_default_set(binary(), binary(), binary() | none) -> {atomic, any()}.
--callback process_active_set(binary(), binary(), binary()) -> [listitem()] | error.
--callback remove_privacy_list(binary(), binary(), binary()) -> {atomic, any()}.
--callback set_privacy_list(#privacy{}) -> any().
--callback set_privacy_list(binary(), binary(), binary(), [listitem()]) -> {atomic, any()}.
--callback get_user_list(binary(), binary()) -> {none | binary(), [listitem()]}.
--callback get_user_lists(binary(), binary()) -> {ok, #privacy{}} | error.
--callback remove_user(binary(), binary()) -> any().
+-callback set_default(binary(), binary(), binary()) ->
+ ok | {error, notfound | any()}.
+-callback unset_default(binary(), binary()) -> ok | {error, any()}.
+-callback remove_list(binary(), binary(), binary()) ->
+ ok | {error, notfound | conflict | any()}.
+-callback remove_lists(binary(), binary()) -> ok | {error, any()}.
+-callback set_lists(#privacy{}) -> ok | {error, any()}.
+-callback set_list(binary(), binary(), binary(), listitem()) ->
+ ok | {error, 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) ->
IQDisc = gen_mod:get_opt(iqdisc, Opts, gen_iq_handler:iqdisc(Host)),
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
+ init_cache(Mod, Host, Opts),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE,
disco_features, 50),
- ejabberd_hooks:add(c2s_session_opened, Host, ?MODULE,
- c2s_session_opened, 50),
ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE,
c2s_copy_session, 50),
ejabberd_hooks:add(user_send_packet, Host, ?MODULE,
@@ -83,8 +92,6 @@ start(Host, Opts) ->
stop(Host) ->
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE,
disco_features, 50),
- ejabberd_hooks:delete(c2s_session_opened, Host, ?MODULE,
- c2s_session_opened, 50),
ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE,
c2s_copy_session, 50),
ejabberd_hooks:delete(user_send_packet, Host, ?MODULE,
@@ -106,6 +113,7 @@ reload(Host, NewOpts, OldOpts) ->
true ->
ok
end,
+ init_cache(NewMod, Host, NewOpts),
case gen_mod:is_equal_opt(iqdisc, NewOpts, OldOpts, gen_iq_handler:iqdisc(Host)) of
{false, IQDisc, _} ->
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().
process_lists_get(#iq{from = #jid{luser = LUser, lserver = LServer},
- lang = Lang,
- meta = #{privacy_active_list := Active}} = IQ) ->
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- case Mod:process_lists_get(LUser, LServer) of
+ lang = Lang} = IQ) ->
+ case get_user_lists(LUser, LServer) of
+ {ok, #privacy{default = Default, lists = Lists}} ->
+ Active = xmpp:get_meta(IQ, privacy_active_list, none),
+ xmpp:make_iq_result(
+ IQ, #privacy_query{active = Active,
+ default = Default,
+ lists = [#privacy_list{name = Name}
+ || {Name, _} <- Lists]});
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(
- IQ,
- #privacy_query{active = Active,
- default = Default,
- lists = [#privacy_list{name = ListName}
- || ListName <- ListNames]})
+ IQ, #privacy_query{active = none, default = none});
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end.
-spec process_list_get(iq(), binary()) -> iq().
process_list_get(#iq{from = #jid{luser = LUser, lserver = LServer},
lang = Lang} = IQ, Name) ->
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- case Mod:process_list_get(LUser, LServer, Name) of
+ case get_user_list(LUser, LServer, Name) of
+ {ok, {_, List}} ->
+ Items = lists:map(fun encode_list_item/1, List),
+ xmpp:make_iq_result(
+ IQ,
+ #privacy_query{
+ lists = [#privacy_list{name = Name, items = Items}]});
error ->
- 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(
- IQ,
- #privacy_query{
- lists = [#privacy_list{name = Name, items = LItems}]})
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end.
--spec item_to_xml(listitem()) -> xmlel().
-item_to_xml(ListItem) ->
- xmpp:encode(encode_list_item(ListItem)).
-
-spec encode_list_item(listitem()) -> privacy_item().
encode_list_item(#listitem{action = Action,
order = Order,
@@ -283,69 +285,69 @@ process_iq_set(#iq{lang = Lang} = IQ) ->
Txt = <<"No module is handling this query">>,
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},
lang = Lang} = IQ, Value) ->
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- case Mod:process_default_set(LUser, LServer, Value) of
- {atomic, error} ->
- Txt = <<"Database failure">>,
- xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang));
- {atomic, not_found} ->
+ case set_default_list(LUser, LServer, Value) of
+ ok ->
+ xmpp:make_iq_result(IQ);
+ {error, notfound} ->
Txt = <<"No privacy list with this name found">>,
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
- {atomic, ok} ->
- xmpp:make_iq_result(IQ);
- Err ->
- ?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())
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end.
-spec process_active_set(IQ, none | binary()) -> IQ.
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},
lang = Lang} = IQ, Name) ->
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- case Mod:process_active_set(LUser, LServer, Name) of
+ case get_user_list(LUser, LServer, Name) of
+ {ok, _} ->
+ xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_active_list, Name));
error ->
Txt = <<"No privacy list with this name found">>,
xmpp:make_error(IQ, xmpp:err_item_not_found(Txt, Lang));
- Items ->
- NeedDb = is_list_needdb(Items),
- List = #userlist{name = Name, list = Items, needdb = NeedDb},
- xmpp:make_iq_result(xmpp:put_meta(IQ, privacy_list, List))
+ {error, _} ->
+ Txt = <<"Database failure">>,
+ xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end.
--spec set_privacy_list(privacy()) -> any().
-set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
+-spec set_list(privacy()) -> ok | {error, any()}.
+set_list(#privacy{us = {LUser, LServer}, lists = Lists} = Privacy) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Mod:set_privacy_list(Privacy).
+ 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{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,
+process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer},
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">>,
+ 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));
- {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">>,
- xmpp:make_error(IQ, xmpp:err_internal_server_error(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;
process_lists_set(#iq{from = #jid{luser = LUser, lserver = LServer} = From,
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),
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
List ->
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- case Mod:set_privacy_list(LUser, LServer, Name, List) of
- {atomic, ok} ->
- UserList = #userlist{name = Name, list = List,
- needdb = is_list_needdb(List)},
- push_list_update(From, UserList, Name),
+ case set_list(LUser, LServer, Name, List) of
+ ok ->
+ push_list_update(From, Name),
xmpp:make_iq_result(IQ);
- Err ->
- ?ERROR_MSG("failed to set privacy list '~s' "
- "for user ~s@~s: ~p",
- [Name, LUser, LServer, Err]),
+ {error, _} ->
Txt = <<"Database failure">>,
xmpp:make_error(IQ, xmpp:err_internal_server_error(Txt, Lang))
end
end.
--spec push_list_update(jid(), #userlist{}, binary() | none) -> ok.
-push_list_update(From, List, Name) ->
+-spec push_list_update(jid(), binary()) -> ok.
+push_list_update(From, Name) ->
BareFrom = jid:remove_resource(From),
lists:foreach(
fun(R) ->
@@ -379,44 +375,10 @@ push_list_update(From, List, Name) ->
IQ = #iq{type = set, from = BareFrom, to = To,
id = <<"push", (randoms:get_string())/binary>>,
sub_els = [#privacy_query{
- lists = [#privacy_list{name = Name}]}],
- meta = #{privacy_updated_list => List}},
+ lists = [#privacy_list{name = Name}]}]},
ejabberd_router:route(IQ)
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().
decode_item(#privacy_item{order = Order,
action = Action,
@@ -448,47 +410,145 @@ decode_item(#privacy_item{order = Order,
match_presence_out = MatchPresenceOut}
end.
--spec is_list_needdb([listitem()]) -> boolean().
-is_list_needdb(Items) ->
- lists:any(fun (X) ->
- case X#listitem.type of
- subscription -> true;
- group -> true;
- _ -> false
- end
- end,
- Items).
+-spec c2s_copy_session(ejabberd_c2s:state(), c2s_state()) -> c2s_state().
+c2s_copy_session(State, #{privacy_active_list := List}) ->
+ State#{privacy_active_list => List}.
--spec get_user_list(binary(), binary()) -> #userlist{}.
-get_user_list(LUser, LServer) ->
- Mod = gen_mod:db_mod(LServer, ?MODULE),
- {Default, Items} = Mod:get_user_list(LUser, LServer),
- NeedDb = is_list_needdb(Items),
- #userlist{name = Default, list = Items, needdb = NeedDb}.
+-spec user_send_packet({stanza(), c2s_state()}) -> {stanza(), 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_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,
+ {NewIQ, State};
+user_send_packet(Acc) ->
+ Acc.
--spec c2s_session_opened(ejabberd_c2s:state()) -> ejabberd_c2s:state().
-c2s_session_opened(#{jid := #jid{luser = LUser, lserver = LServer}} = State) ->
- State#{privacy_list => get_user_list(LUser, LServer)}.
+-spec user_receive_packet({stanza(), c2s_state()}) -> {stanza(), c2s_state()}.
+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),
+ case Mod:set_list(LUser, LServer, Name, List) of
+ ok ->
+ delete_cache(Mod, LUser, LServer, [Name]);
+ {error, _} = Err ->
+ Err
+ end.
--spec c2s_copy_session(ejabberd_c2s:state(), ejabberd_c2s:state()) -> ejabberd_c2s:state().
-c2s_copy_session(State, #{privacy_list := List}) ->
- State#{privacy_list => List}.
+-spec remove_list(binary(), binary(), binary()) ->
+ ok | {error, conflict | notfound | any()}.
+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 get_user_lists(binary(), binary()) -> {ok, privacy()} | error.
+-spec get_user_lists(binary(), binary()) -> {ok, privacy()} | error | {error, any()}.
get_user_lists(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
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.
%% If Dir = out, User@Server is the sender account (From).
%% If Dir = in, User@Server is the destination account (To).
--spec check_packet(allow | deny, ejabberd_c2s:state() | jid(),
- stanza(), in | out) -> allow | deny.
-check_packet(_, #{jid := #jid{luser = LUser, lserver = LServer},
- privacy_list := #userlist{list = List, needdb = NeedDb}},
- Packet, Dir) ->
+-spec do_check_packet(jid(), [listitem()], stanza(), in | out) -> allow | deny.
+do_check_packet(_, [], _, _) ->
+ allow;
+do_check_packet(#jid{luser = LUser, lserver = LServer}, List, Packet, Dir) ->
From = xmpp:get_from(Packet),
To = xmpp:get_to(Packet),
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 ->
%% Allow outgoing packets from user's full jid to his bare JID
allow;
- _ when List == [] ->
- allow;
_ ->
PType = case Packet of
#message{} -> message;
@@ -529,21 +587,11 @@ check_packet(_, #{jid := #jid{luser = LUser, lserver = LServer},
in -> jid:tolower(From);
out -> jid:tolower(To)
end,
- {Subscription, Groups} =
- case NeedDb of
- true ->
- ejabberd_hooks:run_fold(roster_get_jid_info,
- LServer,
- {none, []},
- [LUser, LServer, LJID]);
- false ->
- {[], []}
- end,
- check_packet_aux(List, PType2, LJID, Subscription, Groups)
- 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).
+ {Subscription, Groups} = ejabberd_hooks:run_fold(
+ roster_get_jid_info, LServer,
+ {none, []}, [LUser, LServer, LJID]),
+ check_packet_aux(List, PType2, LJID, Subscription, Groups)
+ end.
-spec check_packet_aux([listitem()],
message | iq | presence_in | presence_out | other,
@@ -608,12 +656,82 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
group -> lists:member(Value, Groups)
end.
--spec remove_user(binary(), binary()) -> any().
+-spec remove_user(binary(), binary()) -> ok.
remove_user(User, Server) ->
LUser = jid:nodeprep(User),
LServer = jid:nameprep(Server),
+ Privacy = get_user_lists(LUser, LServer),
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>>) ->
<<"0">>;
diff --git a/src/mod_privacy_mnesia.erl b/src/mod_privacy_mnesia.erl
index efa4ae6c8..7449262b9 100644
--- a/src/mod_privacy_mnesia.erl
+++ b/src/mod_privacy_mnesia.erl
@@ -27,11 +27,9 @@
-behaviour(mod_privacy).
%% API
--export([init/2, process_lists_get/2, process_list_get/3,
- process_default_set/3, process_active_set/3,
- remove_privacy_list/3, set_privacy_list/1,
- set_privacy_list/4, get_user_list/2, get_user_lists/2,
- remove_user/2, import/1]).
+-export([init/2, set_default/3, unset_default/2, set_lists/1,
+ set_list/4, get_lists/2, get_list/3, remove_lists/2,
+ remove_list/3, use_cache/1, import/1]).
-export([need_transform/1, transform/1]).
-include("xmpp.hrl").
@@ -43,122 +41,106 @@
%%%===================================================================
init(_Host, _Opts) ->
ejabberd_mnesia:create(?MODULE, privacy,
- [{disc_copies, [node()]},
+ [{disc_only_copies, [node()]},
{attributes, record_info(fields, privacy)}]).
-process_lists_get(LUser, LServer) ->
- case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
- {'EXIT', _Reason} -> error;
- [] -> {none, []};
- [#privacy{default = Default, lists = Lists}] ->
- LItems = lists:map(fun ({N, _}) -> N end, Lists),
- {Default, LItems}
- end.
-
-process_list_get(LUser, LServer, Name) ->
- 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
+use_cache(Host) ->
+ case mnesia:table_info(privacy, storage_type) of
+ disc_only_copies ->
+ gen_mod:get_module_opt(
+ Host, mod_privacy, use_cache,
+ ejabberd_config:use_cache(Host));
+ _ ->
+ false
end.
-process_default_set(LUser, LServer, none) ->
+unset_default(LUser, LServer) ->
F = fun () ->
case mnesia:read({privacy, {LUser, LServer}}) of
[] -> ok;
[R] -> mnesia:write(R#privacy{default = none})
end
end,
- mnesia:transaction(F);
-process_default_set(LUser, LServer, Name) ->
+ transaction(F).
+
+set_default(LUser, LServer, Name) ->
F = fun () ->
case mnesia:read({privacy, {LUser, LServer}}) of
- [] -> not_found;
+ [] ->
+ {error, notfound};
[#privacy{lists = Lists} = P] ->
case lists:keymember(Name, 1, Lists) of
true ->
mnesia:write(P#privacy{default = Name,
- lists = Lists}),
- ok;
- false -> not_found
+ lists = Lists});
+ false ->
+ {error, notfound}
end
end
end,
- mnesia:transaction(F).
-
-process_active_set(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.
+ transaction(F).
-remove_privacy_list(LUser, LServer, Name) ->
+remove_list(LUser, LServer, Name) ->
F = fun () ->
case mnesia:read({privacy, {LUser, LServer}}) of
- [] -> ok;
- [#privacy{default = Default, lists = Lists} = P] ->
- if Name == Default -> conflict;
- true ->
- NewLists = lists:keydelete(Name, 1, Lists),
- mnesia:write(P#privacy{lists = NewLists})
- end
+ [] ->
+ {error, notfound};
+ [#privacy{default = Default, lists = Lists} = P] ->
+ if Name == Default ->
+ {error, conflict};
+ true ->
+ NewLists = lists:keydelete(Name, 1, Lists),
+ mnesia:write(P#privacy{lists = NewLists})
+ end
end
end,
- mnesia:transaction(F).
+ transaction(F).
-set_privacy_list(Privacy) ->
+set_lists(Privacy) ->
mnesia:dirty_write(Privacy).
-set_privacy_list(LUser, LServer, Name, List) ->
+set_list(LUser, LServer, Name, List) ->
F = fun () ->
case mnesia:wread({privacy, {LUser, LServer}}) of
- [] ->
- NewLists = [{Name, List}],
- mnesia:write(#privacy{us = {LUser, LServer},
- lists = NewLists});
- [#privacy{lists = Lists} = P] ->
- NewLists1 = lists:keydelete(Name, 1, Lists),
- NewLists = [{Name, List} | NewLists1],
- mnesia:write(P#privacy{lists = NewLists})
+ [] ->
+ NewLists = [{Name, List}],
+ mnesia:write(#privacy{us = {LUser, LServer},
+ lists = NewLists});
+ [#privacy{lists = Lists} = P] ->
+ NewLists1 = lists:keydelete(Name, 1, Lists),
+ NewLists = [{Name, List} | NewLists1],
+ mnesia:write(P#privacy{lists = NewLists})
end
end,
- mnesia:transaction(F).
-
-get_user_list(LUser, LServer) ->
- case catch mnesia:dirty_read(privacy, {LUser, LServer})
- of
- [] -> {none, []};
- [#privacy{default = Default, lists = Lists}] ->
- case Default of
- none -> {none, []};
- _ ->
- case lists:keysearch(Default, 1, Lists) of
- {value, {_, List}} -> {Default, List};
- _ -> {none, []}
- end
- end;
- _ -> {none, []}
+ transaction(F).
+
+get_list(LUser, LServer, Name) ->
+ case mnesia:dirty_read(privacy, {LUser, LServer}) of
+ [#privacy{default = Default, lists = Lists}] when Name == default ->
+ case lists:keyfind(Default, 1, Lists) of
+ {_, List} -> {ok, {Default, List}};
+ false -> error
+ end;
+ [#privacy{lists = Lists}] ->
+ case lists:keyfind(Name, 1, Lists) of
+ {_, List} -> {ok, {Name, List}};
+ false -> error
+ end;
+ [] ->
+ error
end.
-get_user_lists(LUser, LServer) ->
- case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
+get_lists(LUser, LServer) ->
+ case mnesia:dirty_read(privacy, {LUser, LServer}) of
[#privacy{} = P] ->
{ok, P};
_ ->
error
end.
-remove_user(LUser, LServer) ->
+remove_lists(LUser, LServer) ->
F = fun () -> mnesia:delete({privacy, {LUser, LServer}}) end,
- mnesia:transaction(F).
+ transaction(F).
import(#privacy{} = P) ->
mnesia:dirty_write(P).
@@ -199,3 +181,11 @@ transform(#privacy{us = {U, S}, default = Def, lists = Lists} = R) ->
%%%===================================================================
%%% 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.
diff --git a/src/mod_privacy_riak.erl b/src/mod_privacy_riak.erl
index 28851d042..0cd39c110 100644
--- a/src/mod_privacy_riak.erl
+++ b/src/mod_privacy_riak.erl
@@ -27,11 +27,9 @@
-behaviour(mod_privacy).
%% API
--export([init/2, process_lists_get/2, process_list_get/3,
- process_default_set/3, process_active_set/3,
- remove_privacy_list/3, set_privacy_list/1,
- set_privacy_list/4, get_user_list/2, get_user_lists/2,
- remove_user/2, import/1]).
+-export([init/2, set_default/3, unset_default/2, set_lists/1,
+ set_list/4, get_lists/2, get_list/3, remove_lists/2,
+ remove_list/3, import/1]).
-export([privacy_schema/0]).
@@ -44,122 +42,93 @@
init(_Host, _Opts) ->
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
+ {ok, R} ->
+ ejabberd_riak:put(R#privacy{default = none}, privacy_schema());
+ {error, notfound} ->
+ ok;
+ Err ->
+ Err
end.
-process_list_get(LUser, LServer, Name) ->
+set_default(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
+ {ok, #privacy{lists = Lists} = P} ->
+ case lists:keymember(Name, 1, Lists) of
+ true ->
+ ejabberd_riak:put(P#privacy{default = Name,
+ lists = Lists},
+ privacy_schema());
+ false ->
+ {error, notfound}
+ end;
+ Err ->
+ Err
end.
-process_default_set(LUser, LServer, none) ->
- {atomic,
- case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
- {ok, R} ->
- ejabberd_riak:put(R#privacy{default = none}, privacy_schema());
- {error, _} ->
- ok
- end};
-process_default_set(LUser, LServer, Name) ->
- {atomic,
- case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
- {ok, #privacy{lists = Lists} = P} ->
- case lists:keymember(Name, 1, Lists) of
- true ->
- ejabberd_riak:put(P#privacy{default = Name,
- lists = Lists},
- privacy_schema());
- false ->
- not_found
- end;
- {error, _} ->
- not_found
- end}.
-
-process_active_set(LUser, LServer, Name) ->
+remove_list(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
+ {ok, #privacy{default = Default, lists = Lists} = P} ->
+ if Name == Default ->
+ {error, conflict};
+ true ->
+ NewLists = lists:keydelete(Name, 1, Lists),
+ ejabberd_riak:put(P#privacy{lists = NewLists},
+ privacy_schema())
+ end;
+ Err ->
+ Err
end.
-remove_privacy_list(LUser, LServer, Name) ->
- {atomic,
- case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
- {ok, #privacy{default = Default, lists = Lists} = P} ->
- if Name == Default ->
- conflict;
- true ->
- NewLists = lists:keydelete(Name, 1, Lists),
- ejabberd_riak:put(P#privacy{lists = NewLists},
- privacy_schema())
- end;
- {error, _} ->
- ok
- end}.
-
-set_privacy_list(Privacy) ->
+set_lists(Privacy) ->
ejabberd_riak:put(Privacy, privacy_schema()).
-set_privacy_list(LUser, LServer, Name, List) ->
- {atomic,
- case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
- {ok, #privacy{lists = Lists} = P} ->
- NewLists1 = lists:keydelete(Name, 1, Lists),
- NewLists = [{Name, List} | NewLists1],
- ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema());
- {error, _} ->
- NewLists = [{Name, List}],
- ejabberd_riak:put(#privacy{us = {LUser, LServer},
- lists = NewLists},
- privacy_schema())
- end}.
-
-get_user_list(LUser, LServer) ->
+set_list(LUser, LServer, Name, List) ->
+ case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
+ {ok, #privacy{lists = Lists} = P} ->
+ NewLists1 = lists:keydelete(Name, 1, Lists),
+ NewLists = [{Name, List} | NewLists1],
+ ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema());
+ {error, notfound} ->
+ NewLists = [{Name, List}],
+ ejabberd_riak:put(#privacy{us = {LUser, LServer},
+ lists = NewLists},
+ privacy_schema());
+ Err ->
+ Err
+ end.
+
+get_list(LUser, LServer, Name) ->
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, []}
+ {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_user_lists(LUser, LServer) ->
+get_lists(LUser, LServer) ->
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
{ok, #privacy{} = P} ->
{ok, P};
- {error, _} ->
- error
+ {error, notfound} ->
+ error;
+ Err ->
+ Err
end.
-remove_user(LUser, LServer) ->
- {atomic, ejabberd_riak:delete(privacy, {LUser, LServer})}.
+remove_lists(LUser, LServer) ->
+ ejabberd_riak:delete(privacy, {LUser, LServer}).
import(#privacy{} = P) ->
ejabberd_riak:put(P, privacy_schema()).
diff --git a/src/mod_privacy_sql.erl b/src/mod_privacy_sql.erl
index 615ef7a0d..45c86e12c 100644
--- a/src/mod_privacy_sql.erl
+++ b/src/mod_privacy_sql.erl
@@ -29,20 +29,11 @@
-behaviour(mod_privacy).
%% API
--export([init/2, process_lists_get/2, process_list_get/3,
- process_default_set/3, process_active_set/3,
- remove_privacy_list/3, set_privacy_list/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,
- 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]).
+-export([init/2, set_default/3, unset_default/2, set_lists/1,
+ set_list/4, get_lists/2, get_list/3, remove_lists/2,
+ remove_list/3, import/1, export/1]).
+
+-export([item_to_raw/1, raw_to_item/1]).
-include("xmpp.hrl").
-include("mod_privacy.hrl").
@@ -55,159 +46,143 @@
init(_Host, _Opts) ->
ok.
-process_lists_get(LUser, LServer) ->
- Default = case catch sql_get_default_privacy_list(LUser, LServer) of
- {selected, []} -> none;
- {selected, [{DefName}]} -> DefName;
- _ -> none
- end,
- case catch sql_get_privacy_list_names(LUser, LServer) of
- {selected, Names} ->
- LItems = lists:map(fun ({N}) -> N end, Names),
- {Default, LItems};
- _ -> error
+unset_default(LUser, LServer) ->
+ case unset_default_privacy_list(LUser, LServer) of
+ ok ->
+ ok;
+ _Err ->
+ {error, db_failure}
end.
-process_list_get(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) ->
+set_default(LUser, LServer, Name) ->
F = fun () ->
- case sql_get_privacy_list_names_t(LUser) of
- {selected, []} -> not_found;
- {selected, Names} ->
- case lists:member({Name}, Names) of
- true -> sql_set_default_privacy_list(LUser, Name), ok;
- false -> not_found
- end
+ case get_privacy_list_names_t(LUser) of
+ {selected, []} ->
+ {error, notfound};
+ {selected, Names} ->
+ case lists:member({Name}, Names) of
+ true ->
+ set_default_privacy_list(LUser, Name);
+ false ->
+ {error, notfound}
+ end
end
end,
- sql_queries:sql_transaction(LServer, F).
-
-process_active_set(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.
+ transaction(LServer, F).
-remove_privacy_list(LUser, LServer, Name) ->
+remove_list(LUser, LServer, Name) ->
F = fun () ->
- case sql_get_default_privacy_list_t(LUser) of
- {selected, []} ->
- sql_remove_privacy_list(LUser, Name), ok;
- {selected, [{Default}]} ->
- if Name == Default -> conflict;
- true -> sql_remove_privacy_list(LUser, Name), ok
- end
+ case get_default_privacy_list_t(LUser) of
+ {selected, []} ->
+ remove_privacy_list(LUser, Name);
+ {selected, [{Default}]} ->
+ if Name == Default ->
+ {error, conflict};
+ true ->
+ remove_privacy_list(LUser, Name)
+ end
end
end,
- sql_queries:sql_transaction(LServer, F).
+ transaction(LServer, F).
-set_privacy_list(#privacy{us = {LUser, LServer},
- default = Default,
- lists = Lists}) ->
+set_lists(#privacy{us = {LUser, LServer},
+ default = Default,
+ lists = Lists}) ->
F = fun() ->
lists:foreach(
fun({Name, List}) ->
- sql_add_privacy_list(LUser, Name),
+ add_privacy_list(LUser, Name),
{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),
- sql_set_privacy_list(I, RItems),
+ set_privacy_list(I, RItems),
if is_binary(Default) ->
- sql_set_default_privacy_list(LUser, Default),
- ok;
+ set_default_privacy_list(LUser, Default);
true ->
ok
end
end, Lists)
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),
F = fun () ->
- ID = case sql_get_privacy_list_id_t(LUser, Name) of
+ ID = case get_privacy_list_id_t(LUser, Name) of
{selected, []} ->
- sql_add_privacy_list(LUser, Name),
- {selected, [{I}]} =
- sql_get_privacy_list_id_t(LUser, Name),
- I;
- {selected, [{I}]} -> I
+ add_privacy_list(LUser, Name),
+ {selected, [{I}]} =
+ get_privacy_list_id_t(LUser, Name),
+ I;
+ {selected, [{I}]} -> I
end,
- sql_set_privacy_list(ID, RItems),
- ok
+ set_privacy_list(ID, RItems)
end,
- sql_queries:sql_transaction(LServer, F).
-
-get_user_list(LUser, LServer) ->
- case catch sql_get_default_privacy_list(LUser, LServer)
- of
- {selected, []} -> {none, []};
- {selected, [{Default}]} ->
- case catch sql_get_privacy_list_data(LUser, LServer,
- Default) of
- {selected, RItems} ->
- {Default, lists:flatmap(fun raw_to_item/1, RItems)};
- _ -> {none, []}
- end;
- _ -> {none, []}
+ transaction(LServer, F).
+
+get_list(LUser, LServer, default) ->
+ case get_default_privacy_list(LUser, LServer) of
+ {selected, []} ->
+ error;
+ {selected, [{Default}]} ->
+ get_list(LUser, LServer, Default);
+ _Err ->
+ {error, db_failure}
+ end;
+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.
-get_user_lists(LUser, LServer) ->
- Default = case catch sql_get_default_privacy_list(LUser, LServer) of
- {selected, []} ->
- none;
- {selected, [{DefName}]} ->
- DefName;
- _ ->
- none
- end,
- case catch sql_get_privacy_list_names(LUser, LServer) of
- {selected, Names} ->
- Lists =
- lists:flatmap(
- fun({Name}) ->
- case catch sql_get_privacy_list_data(
- LUser, LServer, Name) of
- {selected, RItems} ->
- [{Name, lists:flatmap(fun raw_to_item/1, RItems)}];
- _ ->
- []
- end
- end, Names),
- {ok, #privacy{default = Default,
- us = {LUser, LServer},
- lists = Lists}};
- _ ->
- error
+get_lists(LUser, LServer) ->
+ case get_default_privacy_list(LUser, LServer) of
+ {selected, Selected} ->
+ Default = case Selected of
+ [] -> none;
+ [{DefName}] -> DefName
+ end,
+ case get_privacy_list_names(LUser, LServer) of
+ {selected, Names} ->
+ case lists:foldl(
+ fun(_, {error, _} = Err) ->
+ Err;
+ ({Name}, Acc) ->
+ case get_privacy_list_data(LUser, LServer, Name) of
+ {selected, RItems} ->
+ Items = lists:flatmap(
+ fun raw_to_item/1,
+ RItems),
+ [{Name, Items}|Acc];
+ _Err ->
+ {error, db_failure}
+ end
+ end, [], Names) of
+ {error, Reason} ->
+ {error, Reason};
+ Lists ->
+ {ok, #privacy{default = Default,
+ us = {LUser, LServer},
+ lists = Lists}}
+ end;
+ _Err ->
+ {error, db_failure}
+ end;
+ _Err ->
+ {error, db_failure}
end.
-remove_user(LUser, LServer) ->
- sql_del_privacy_lists(LUser, LServer).
+remove_lists(LUser, LServer) ->
+ case del_privacy_lists(LUser, LServer) of
+ ok ->
+ ok;
+ _Err ->
+ {error, db_failure}
+ end.
export(Server) ->
case catch ejabberd_sql:sql_query(jid:nameprep(Server),
@@ -271,6 +246,12 @@ import(_) ->
%%%===================================================================
%%% 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,
MatchIQ, MatchMessage, MatchPresenceIn,
MatchPresenceOut} = Row) ->
@@ -327,47 +308,48 @@ item_to_raw(#listitem{type = Type, value = Value,
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
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_get_default_privacy_list_t(LUser) ->
+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_get_privacy_list_names_t(LUser) ->
+get_privacy_list_names_t(LUser) ->
sql_queries:get_privacy_list_names_t(LUser).
-sql_get_privacy_list_id(LUser, LServer, Name) ->
- sql_queries:get_privacy_list_id(LServer, LUser, Name).
-
-sql_get_privacy_list_id_t(LUser, Name) ->
+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_get_privacy_list_data_by_id(ID, LServer) ->
- 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) ->
+set_default_privacy_list(LUser, Name) ->
sql_queries:set_default_privacy_list(LUser, Name).
-sql_unset_default_privacy_list(LUser, LServer) ->
- sql_queries:unset_default_privacy_list(LServer, LUser).
+unset_default_privacy_list(LUser, LServer) ->
+ case sql_queries:unset_default_privacy_list(LServer, LUser) of
+ {updated, _} -> ok;
+ Err -> Err
+ end.
-sql_remove_privacy_list(LUser, Name) ->
- sql_queries:remove_privacy_list(LUser, Name).
+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_set_privacy_list(ID, RItems) ->
+set_privacy_list(ID, RItems) ->
sql_queries:set_privacy_list(ID, RItems).
-sql_del_privacy_lists(LUser, LServer) ->
- sql_queries:del_privacy_lists(LServer, LUser).
+del_privacy_lists(LUser, LServer) ->
+ case sql_queries:del_privacy_lists(LServer, LUser) of
+ {updated, _} -> ok;
+ Err -> Err
+ end.
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index d1dc714ef..28bc06171 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -368,15 +368,16 @@ get_roster_item(LUser, LServer, LJID) ->
{ok, Item} ->
Item;
error ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID}
+ LBJID = jid:remove_resource(LJID),
+ #roster{usj = {LUser, LServer, LBJID},
+ us = {LUser, LServer}, jid = LBJID}
end.
get_subscription_and_groups(LUser, LServer, LJID) ->
+ LBJID = jid:remove_resource(LJID),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Res = case use_cache(Mod, LServer, roster) of
true ->
- LBJID = jid:remove_resource(LJID),
ets_cache:lookup(
?ROSTER_ITEM_CACHE, {LUser, LServer, LBJID},
fun() ->
@@ -384,19 +385,12 @@ get_subscription_and_groups(LUser, LServer, LJID) ->
case lists:keyfind(LBJID, #roster.jid, Items) of
#roster{subscription = Sub, groups = 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 ->
- error
- end
+ error
end
end);
false ->
- Mod:read_subscription_and_groups(LUser, LServer, LJID)
+ Mod:read_subscription_and_groups(LUser, LServer, LBJID)
end,
case Res of
{ok, SubAndGroups} ->
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
index f575724c6..072da0908 100644
--- a/src/prosody2ejabberd.erl
+++ b/src/prosody2ejabberd.erl
@@ -210,7 +210,7 @@ convert_data(Host, "privacy", User, [Data]) ->
ListItems -> [{Name, ListItems}]
end
end, Lists)},
- mod_privacy:set_privacy_list(Priv);
+ mod_privacy:set_list(Priv);
convert_data(_Host, _Type, _User, _Data) ->
ok.
diff --git a/test/privacy_tests.erl b/test/privacy_tests.erl
index 44c050cbf..cf4168262 100644
--- a/test/privacy_tests.erl
+++ b/test/privacy_tests.erl
@@ -43,6 +43,7 @@ single_cases() ->
[single_test(feature_enabled),
single_test(set_get_list),
single_test(get_list_non_existent),
+ single_test(get_empty_lists),
single_test(set_default),
single_test(del_default),
single_test(set_default_non_existent),
@@ -52,8 +53,7 @@ single_cases() ->
single_test(remove_list),
single_test(remove_default_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(malformed_iq_query),
single_test(malformed_get),
@@ -98,6 +98,12 @@ get_list_non_existent(Config) ->
#stanza_error{reason = 'item-not-found'} = get_list(Config, ListName),
disconnect(Config).
+get_empty_lists(Config) ->
+ #privacy_query{default = none,
+ active = none,
+ lists = []} = get_lists(Config),
+ disconnect(Config).
+
set_default(Config) ->
ListName = <<"set-default">>,
Item = #privacy_item{order = 0, action = deny},
@@ -561,12 +567,6 @@ del_list(Config, Name) ->
lists = [#privacy_list{
name = Name}]}]}) of
#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;
#iq{type = error} = Err ->
xmpp:get_error(Err)