From 35d19b32f40a4f6aedecca36c3145ab3013ecf87 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Sat, 20 May 2017 22:36:32 +0300 Subject: [PATCH] Implement cache for mod_privacy/mod_blocking --- include/mod_privacy.hrl | 8 - src/mod_admin_extra.erl | 5 +- src/mod_blocking.erl | 212 +++++++-------- src/mod_blocking_mnesia.erl | 100 ------- src/mod_blocking_riak.erl | 113 -------- src/mod_blocking_sql.erl | 107 -------- src/mod_privacy.erl | 510 ++++++++++++++++++++++-------------- src/mod_privacy_mnesia.erl | 150 +++++------ src/mod_privacy_riak.erl | 173 +++++------- src/mod_privacy_sql.erl | 298 ++++++++++----------- src/mod_roster.erl | 18 +- src/prosody2ejabberd.erl | 2 +- test/privacy_tests.erl | 16 +- 13 files changed, 718 insertions(+), 994 deletions(-) delete mode 100644 src/mod_blocking_mnesia.erl delete mode 100644 src/mod_blocking_riak.erl delete mode 100644 src/mod_blocking_sql.erl 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 -%%% Created : 14 Apr 2016 by Evgeny Khramtsov -%%% -%%% -%%% 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 -%%% Created : 14 Apr 2016 by Evgeny Khramtsov -%%% -%%% -%%% 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 -%%% Created : 14 Apr 2016 by Evgeny Khramtsov -%%% -%%% -%%% 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 - 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} -> + 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 = ListName} - || ListName <- ListNames]}) + IQ, #privacy_query{active = Active, + default = Default, + lists = [#privacy_list{name = Name} + || {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. -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 - 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), + 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 = LItems}]}) + #privacy_query{ + 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. --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)) - 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]), + {error, _} -> Txt = <<"Database failure">>, 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; 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) -> +-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 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), - {Default, Items} = Mod:get_user_list(LUser, LServer), - NeedDb = is_list_needdb(Items), - #userlist{name = Default, list = Items, needdb = NeedDb}. + case Mod:set_list(LUser, LServer, Name, List) of + ok -> + delete_cache(Mod, LUser, LServer, [Name]); + {error, _} = Err -> + Err + end. --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 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 c2s_copy_session(ejabberd_c2s:state(), ejabberd_c2s:state()) -> ejabberd_c2s:state(). -c2s_copy_session(State, #{privacy_list := List}) -> - State#{privacy_list => List}. - --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} +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_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 - 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). + 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. - -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). + 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, []} +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{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{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_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 {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([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, - 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([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). + 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. - -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). + 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, []} +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)