diff --git a/include/mod_shared_roster.hrl b/include/mod_shared_roster.hrl new file mode 100644 index 000000000..1f96b3034 --- /dev/null +++ b/include/mod_shared_roster.hrl @@ -0,0 +1,5 @@ +-record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()}, + opts = [] :: list() | '_' | '$2'}). + +-record(sr_user, {us = {<<"">>, <<"">>} :: {binary(), binary()}, + group_host = {<<"">>, <<"">>} :: {binary(), binary()}}). diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 212a7d47f..6670cf77b 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -38,7 +38,7 @@ list_groups/1, create_group/2, create_group/3, delete_group/2, get_group_opts/2, set_group_opts/3, get_group_users/2, get_group_explicit_users/2, - is_user_in_group/3, add_user_to_group/3, + is_user_in_group/3, add_user_to_group/3, opts_to_binary/1, remove_user_from_group/3, mod_opt_type/1]). -include("ejabberd.hrl"). @@ -52,25 +52,28 @@ -include("ejabberd_web_admin.hrl"). --record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()}, - opts = [] :: list() | '_' | '$2'}). +-include("mod_shared_roster.hrl"). --record(sr_user, {us = {<<"">>, <<"">>} :: {binary(), binary()}, - group_host = {<<"">>, <<"">>} :: {binary(), binary()}}). +-type group_options() :: [{atom(), any()}]. +-callback init(binary(), gen_mod:opts()) -> any(). +-callback import(binary(), #sr_user{} | #sr_group{}) -> ok | pass. +-callback list_groups(binary()) -> [binary()]. +-callback groups_with_opts(binary()) -> [{binary(), group_options()}]. +-callback create_group(binary(), binary(), group_options()) -> {atomic, any()}. +-callback delete_group(binary(), binary()) -> {atomic, any()}. +-callback get_group_opts(binary(), binary()) -> group_options() | error. +-callback set_group_opts(binary(), binary(), group_options()) -> {atomic, any()}. +-callback get_user_groups({binary(), binary()}, binary()) -> [binary()]. +-callback get_group_explicit_users(binary(), binary()) -> [{binary(), binary()}]. +-callback get_user_displayed_groups(binary(), binary(), group_options()) -> + [{binary(), group_options()}]. +-callback is_user_in_group({binary(), binary()}, binary(), binary()) -> boolean(). +-callback add_user_to_group(binary(), {binary(), binary()}, binary()) -> {atomic, any()}. +-callback remove_user_from_group(binary(), {binary(), binary()}, binary()) -> {atomic, any()}. start(Host, Opts) -> - case gen_mod:db_type(Host, Opts) of - mnesia -> - mnesia:create_table(sr_group, - [{disc_copies, [node()]}, - {attributes, record_info(fields, sr_group)}]), - mnesia:create_table(sr_user, - [{disc_copies, [node()]}, {type, bag}, - {attributes, record_info(fields, sr_user)}]), - update_tables(), - mnesia:add_table_index(sr_user, group_host); - _ -> ok - end, + Mod = gen_mod:db_mod(Host, Opts, ?MODULE), + Mod:init(Host, Opts), ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE, webadmin_menu, 70), ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE, @@ -391,195 +394,36 @@ process_subscription(Direction, User, Server, JID, end. list_groups(Host) -> - list_groups(Host, gen_mod:db_type(Host, ?MODULE)). - -list_groups(Host, mnesia) -> - mnesia:dirty_select(sr_group, - [{#sr_group{group_host = {'$1', '$2'}, _ = '_'}, - [{'==', '$2', Host}], ['$1']}]); -list_groups(Host, riak) -> - case ejabberd_riak:get_keys_by_index(sr_group, <<"host">>, Host) of - {ok, Gs} -> - [G || {G, _} <- Gs]; - _ -> - [] - end; -list_groups(Host, odbc) -> - case ejabberd_odbc:sql_query(Host, - [<<"select name from sr_group;">>]) - of - {selected, [<<"name">>], Rs} -> [G || [G] <- Rs]; - _ -> [] - end. + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:list_groups(Host). groups_with_opts(Host) -> - groups_with_opts(Host, gen_mod:db_type(Host, ?MODULE)). - -groups_with_opts(Host, mnesia) -> - Gs = mnesia:dirty_select(sr_group, - [{#sr_group{group_host = {'$1', Host}, opts = '$2', - _ = '_'}, - [], [['$1', '$2']]}]), - lists:map(fun ([G, O]) -> {G, O} end, Gs); -groups_with_opts(Host, riak) -> - case ejabberd_riak:get_by_index(sr_group, sr_group_schema(), - <<"host">>, Host) of - {ok, Rs} -> - [{G, O} || #sr_group{group_host = {G, _}, opts = O} <- Rs]; - _ -> - [] - end; -groups_with_opts(Host, odbc) -> - case ejabberd_odbc:sql_query(Host, - [<<"select name, opts from sr_group;">>]) - of - {selected, [<<"name">>, <<"opts">>], Rs} -> - [{G, opts_to_binary(ejabberd_odbc:decode_term(Opts))} - || [G, Opts] <- Rs]; - _ -> [] - end. + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:groups_with_opts(Host). create_group(Host, Group) -> create_group(Host, Group, []). create_group(Host, Group, Opts) -> - create_group(Host, Group, Opts, - gen_mod:db_type(Host, ?MODULE)). - -create_group(Host, Group, Opts, mnesia) -> - R = #sr_group{group_host = {Group, Host}, opts = Opts}, - F = fun () -> mnesia:write(R) end, - mnesia:transaction(F); -create_group(Host, Group, Opts, riak) -> - {atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host}, - opts = Opts}, - sr_group_schema(), - [{'2i', [{<<"host">>, Host}]}])}; -create_group(Host, Group, Opts, odbc) -> - SGroup = ejabberd_odbc:escape(Group), - SOpts = ejabberd_odbc:encode_term(Opts), - F = fun () -> - odbc_queries:update_t(<<"sr_group">>, - [<<"name">>, <<"opts">>], [SGroup, SOpts], - [<<"name='">>, SGroup, <<"'">>]) - end, - ejabberd_odbc:sql_transaction(Host, F). + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:create_group(Host, Group, Opts). delete_group(Host, Group) -> - delete_group(Host, Group, - gen_mod:db_type(Host, ?MODULE)). - -delete_group(Host, Group, mnesia) -> - GroupHost = {Group, Host}, - F = fun () -> - mnesia:delete({sr_group, GroupHost}), - Users = mnesia:index_read(sr_user, GroupHost, - #sr_user.group_host), - lists:foreach(fun (UserEntry) -> - mnesia:delete_object(UserEntry) - end, - Users) - end, - mnesia:transaction(F); -delete_group(Host, Group, riak) -> - try - ok = ejabberd_riak:delete(sr_group, {Group, Host}), - ok = ejabberd_riak:delete_by_index(sr_user, <<"group_host">>, - {Group, Host}), - {atomic, ok} - catch _:{badmatch, Err} -> - {atomic, Err} - end; -delete_group(Host, Group, odbc) -> - SGroup = ejabberd_odbc:escape(Group), - F = fun () -> - ejabberd_odbc:sql_query_t([<<"delete from sr_group where name='">>, - SGroup, <<"';">>]), - ejabberd_odbc:sql_query_t([<<"delete from sr_user where grp='">>, - SGroup, <<"';">>]) - end, - case ejabberd_odbc:sql_transaction(Host, F) of - {atomic,{updated,_}} -> {atomic, ok}; - Res -> Res - end. + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:delete_group(Host, Group). get_group_opts(Host, Group) -> - get_group_opts(Host, Group, - gen_mod:db_type(Host, ?MODULE)). - -get_group_opts(Host, Group, mnesia) -> - case catch mnesia:dirty_read(sr_group, {Group, Host}) of - [#sr_group{opts = Opts}] -> Opts; - _ -> error - end; -get_group_opts(Host, Group, riak) -> - case ejabberd_riak:get(sr_group, sr_group_schema(), {Group, Host}) of - {ok, #sr_group{opts = Opts}} -> Opts; - _ -> error - end; -get_group_opts(Host, Group, odbc) -> - SGroup = ejabberd_odbc:escape(Group), - case catch ejabberd_odbc:sql_query(Host, - [<<"select opts from sr_group where name='">>, - SGroup, <<"';">>]) - of - {selected, [<<"opts">>], [[SOpts]]} -> - opts_to_binary(ejabberd_odbc:decode_term(SOpts)); - _ -> error - end. + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:get_group_opts(Host, Group). set_group_opts(Host, Group, Opts) -> - set_group_opts(Host, Group, Opts, - gen_mod:db_type(Host, ?MODULE)). - -set_group_opts(Host, Group, Opts, mnesia) -> - R = #sr_group{group_host = {Group, Host}, opts = Opts}, - F = fun () -> mnesia:write(R) end, - mnesia:transaction(F); -set_group_opts(Host, Group, Opts, riak) -> - {atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host}, - opts = Opts}, - sr_group_schema(), - [{'2i', [{<<"host">>, Host}]}])}; -set_group_opts(Host, Group, Opts, odbc) -> - SGroup = ejabberd_odbc:escape(Group), - SOpts = ejabberd_odbc:encode_term(Opts), - F = fun () -> - odbc_queries:update_t(<<"sr_group">>, - [<<"name">>, <<"opts">>], [SGroup, SOpts], - [<<"name='">>, SGroup, <<"'">>]) - end, - ejabberd_odbc:sql_transaction(Host, F). + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:set_group_opts(Host, Group, Opts). get_user_groups(US) -> Host = element(2, US), - DBType = gen_mod:db_type(Host, ?MODULE), - get_user_groups(US, Host, DBType) ++ - get_special_users_groups(Host). - -get_user_groups(US, Host, mnesia) -> - case catch mnesia:dirty_read(sr_user, US) of - Rs when is_list(Rs) -> - [Group - || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; - _ -> [] - end; -get_user_groups(US, Host, riak) -> - case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of - {ok, Rs} -> - [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; - _ -> - [] - end; -get_user_groups(US, Host, odbc) -> - SJID = make_jid_s(US), - case catch ejabberd_odbc:sql_query(Host, - [<<"select grp from sr_user where jid='">>, - SJID, <<"';">>]) - of - {selected, [<<"grp">>], Rs} -> [G || [G] <- Rs]; - _ -> [] - end. + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:get_user_groups(US, Host) ++ get_special_users_groups(Host). is_group_enabled(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), @@ -630,39 +474,8 @@ get_group_users(Host, Group, GroupOpts) -> ++ get_group_explicit_users(Host, Group). get_group_explicit_users(Host, Group) -> - get_group_explicit_users(Host, Group, - gen_mod:db_type(Host, ?MODULE)). - -get_group_explicit_users(Host, Group, mnesia) -> - Read = (catch mnesia:dirty_index_read(sr_user, - {Group, Host}, #sr_user.group_host)), - case Read of - Rs when is_list(Rs) -> [R#sr_user.us || R <- Rs]; - _ -> [] - end; -get_group_explicit_users(Host, Group, riak) -> - case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), - <<"group_host">>, {Group, Host}) of - {ok, Rs} -> - [R#sr_user.us || R <- Rs]; - _ -> - [] - end; -get_group_explicit_users(Host, Group, odbc) -> - SGroup = ejabberd_odbc:escape(Group), - case catch ejabberd_odbc:sql_query(Host, - [<<"select jid from sr_user where grp='">>, - SGroup, <<"';">>]) - of - {selected, [<<"jid">>], Rs} -> - lists:map(fun ([JID]) -> - {U, S, _} = - jid:tolower(jid:from_string(JID)), - {U, S} - end, - Rs); - _ -> [] - end. + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:get_group_explicit_users(Host, Group). get_group_name(Host1, Group1) -> {Host, Group} = split_grouphost(Host1, Group1), @@ -718,44 +531,10 @@ get_special_displayed_groups(GroupsOpts) -> %% for the list of groups of that server that user is member %% get the list of groups displayed get_user_displayed_groups(LUser, LServer, GroupsOpts) -> - Groups = get_user_displayed_groups(LUser, LServer, - GroupsOpts, - gen_mod:db_type(LServer, ?MODULE)), + Mod = gen_mod:db_mod(LServer, ?MODULE), + Groups = Mod:get_user_displayed_groups(LUser, LServer, GroupsOpts), displayed_groups(GroupsOpts, Groups). -get_user_displayed_groups(LUser, LServer, GroupsOpts, - mnesia) -> - case catch mnesia:dirty_read(sr_user, {LUser, LServer}) - of - Rs when is_list(Rs) -> - [{Group, proplists:get_value(Group, GroupsOpts, [])} - || #sr_user{group_host = {Group, H}} <- Rs, - H == LServer]; - _ -> [] - end; -get_user_displayed_groups(LUser, LServer, GroupsOpts, - riak) -> - case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), - <<"us">>, {LUser, LServer}) of - {ok, Rs} -> - [{Group, proplists:get_value(Group, GroupsOpts, [])} - || #sr_user{group_host = {Group, _}} <- Rs]; - _ -> - [] - end; -get_user_displayed_groups(LUser, LServer, GroupsOpts, - odbc) -> - SJID = make_jid_s(LUser, LServer), - case catch ejabberd_odbc:sql_query(LServer, - [<<"select grp from sr_user where jid='">>, - SJID, <<"';">>]) - of - {selected, [<<"grp">>], Rs} -> - [{Group, proplists:get_value(Group, GroupsOpts, [])} - || [Group] <- Rs]; - _ -> [] - end. - %% @doc Get the list of groups that are displayed to this user get_user_displayed_groups(US) -> Host = element(2, US), @@ -779,42 +558,12 @@ get_user_displayed_groups(US) -> is_group_enabled(Host, Group)]. is_user_in_group(US, Group, Host) -> - is_user_in_group(US, Group, Host, - gen_mod:db_type(Host, ?MODULE)). - -is_user_in_group(US, Group, Host, mnesia) -> - case catch mnesia:dirty_match_object(#sr_user{us = US, - group_host = {Group, Host}}) - of - [] -> lists:member(US, get_group_users(Host, Group)); - _ -> true - end; -is_user_in_group(US, Group, Host, riak) -> - case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of - {ok, Rs} -> - case lists:any( - fun(#sr_user{group_host = {G, H}}) -> - (Group == G) and (Host == H) - end, Rs) of - false -> - lists:member(US, get_group_users(Host, Group)); - true -> - true - end; - _Err -> - false - end; -is_user_in_group(US, Group, Host, odbc) -> - SJID = make_jid_s(US), - SGroup = ejabberd_odbc:escape(Group), - case catch ejabberd_odbc:sql_query(Host, - [<<"select * from sr_user where jid='">>, - SJID, <<"' and grp='">>, SGroup, - <<"';">>]) - of - {selected, _, []} -> - lists:member(US, get_group_users(Host, Group)); - _ -> true + Mod = gen_mod:db_mod(Host, ?MODULE), + case Mod:is_user_in_group(US, Group, Host) of + false -> + lists:member(US, get_group_users(Host, Group)); + true -> + true end. %% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok} @@ -837,31 +586,10 @@ add_user_to_group(Host, US, Group) -> push_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups), broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups), broadcast_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups), - add_user_to_group(Host, US, Group, gen_mod:db_type(Host, ?MODULE)) + Mod = gen_mod:db_mod(Host, ?MODULE), + Mod:add_user_to_group(Host, US, Group) end. -add_user_to_group(Host, US, Group, mnesia) -> - R = #sr_user{us = US, group_host = {Group, Host}}, - F = fun () -> mnesia:write(R) end, - mnesia:transaction(F); -add_user_to_group(Host, US, Group, riak) -> - {atomic, ejabberd_riak:put( - #sr_user{us = US, group_host = {Group, Host}}, - sr_user_schema(), - [{i, {US, {Group, Host}}}, - {'2i', [{<<"us">>, US}, - {<<"group_host">>, {Group, Host}}]}])}; -add_user_to_group(Host, US, Group, odbc) -> - SJID = make_jid_s(US), - SGroup = ejabberd_odbc:escape(Group), - F = fun () -> - odbc_queries:update_t(<<"sr_user">>, - [<<"jid">>, <<"grp">>], [SJID, SGroup], - [<<"jid='">>, SJID, <<"' and grp='">>, - SGroup, <<"'">>]) - end, - ejabberd_odbc:sql_transaction(Host, F). - get_displayed_groups(Group, LServer) -> GroupsOpts = groups_with_opts(LServer), GroupOpts = proplists:get_value(Group, GroupsOpts, []), @@ -894,8 +622,8 @@ remove_user_from_group(Host, US, Group) -> end, (?MODULE):set_group_opts(Host, Group, NewGroupOpts); nomatch -> - Result = remove_user_from_group(Host, US, Group, - gen_mod:db_type(Host, ?MODULE)), + Mod = gen_mod:db_mod(Host, ?MODULE), + Result = Mod:remove_user_from_group(Host, US, Group), DisplayedToGroups = displayed_to_groups(Group, Host), DisplayedGroups = get_displayed_groups(Group, LServer), push_user_to_displayed(LUser, LServer, Group, Host, remove, DisplayedToGroups), @@ -903,23 +631,6 @@ remove_user_from_group(Host, US, Group) -> Result end. -remove_user_from_group(Host, US, Group, mnesia) -> - R = #sr_user{us = US, group_host = {Group, Host}}, - F = fun () -> mnesia:delete_object(R) end, - mnesia:transaction(F); -remove_user_from_group(Host, US, Group, riak) -> - {atomic, ejabberd_riak:delete(sr_group, {US, {Group, Host}})}; -remove_user_from_group(Host, US, Group, odbc) -> - SJID = make_jid_s(US), - SGroup = ejabberd_odbc:escape(Group), - F = fun () -> - ejabberd_odbc:sql_query_t([<<"delete from sr_user where jid='">>, - SJID, <<"' and grp='">>, SGroup, - <<"';">>]), - ok - end, - ejabberd_odbc:sql_transaction(Host, F). - push_members_to_user(LUser, LServer, Group, Host, Subscription) -> GroupsOpts = groups_with_opts(LServer), @@ -1385,13 +1096,6 @@ displayed_groups_update(Members, DisplayedGroups, Subscription) -> end end, Members). -make_jid_s(U, S) -> - ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:make(U, - S, - <<"">>)))). - -make_jid_s({U, S}) -> make_jid_s(U, S). - opts_to_binary(Opts) -> lists:map( fun({name, Name}) -> @@ -1404,105 +1108,17 @@ opts_to_binary(Opts) -> Opt end, Opts). -sr_group_schema() -> - {record_info(fields, sr_group), #sr_group{}}. - -sr_user_schema() -> - {record_info(fields, sr_user), #sr_user{}}. - -update_tables() -> - update_sr_group_table(), - update_sr_user_table(). - -update_sr_group_table() -> - Fields = record_info(fields, sr_group), - case mnesia:table_info(sr_group, attributes) of - Fields -> - ejabberd_config:convert_table_to_binary( - sr_group, Fields, set, - fun(#sr_group{group_host = {G, _}}) -> G end, - fun(#sr_group{group_host = {G, H}, - opts = Opts} = R) -> - R#sr_group{group_host = {iolist_to_binary(G), - iolist_to_binary(H)}, - opts = opts_to_binary(Opts)} - end); - _ -> - ?INFO_MSG("Recreating sr_group table", []), - mnesia:transform_table(sr_group, ignore, Fields) - end. - -update_sr_user_table() -> - Fields = record_info(fields, sr_user), - case mnesia:table_info(sr_user, attributes) of - Fields -> - ejabberd_config:convert_table_to_binary( - sr_user, Fields, bag, - fun(#sr_user{us = {U, _}}) -> U end, - fun(#sr_user{us = {U, S}, group_host = {G, H}} = R) -> - R#sr_user{us = {iolist_to_binary(U), iolist_to_binary(S)}, - group_host = {iolist_to_binary(G), - iolist_to_binary(H)}} - end); - _ -> - ?INFO_MSG("Recreating sr_user table", []), - mnesia:transform_table(sr_user, ignore, Fields) - end. - -export(_Server) -> - [{sr_group, - fun(Host, #sr_group{group_host = {Group, LServer}, opts = Opts}) - when LServer == Host -> - SGroup = ejabberd_odbc:escape(Group), - SOpts = ejabberd_odbc:encode_term(Opts), - [[<<"delete from sr_group where name='">>, Group, <<"';">>], - [<<"insert into sr_group(name, opts) values ('">>, - SGroup, <<"', '">>, SOpts, <<"');">>]]; - (_Host, _R) -> - [] - end}, - {sr_user, - fun(Host, #sr_user{us = {U, S}, group_host = {Group, LServer}}) - when LServer == Host -> - SGroup = ejabberd_odbc:escape(Group), - SJID = ejabberd_odbc:escape( - jid:to_string( - jid:tolower( - jid:make(U, S, <<"">>)))), - [[<<"delete from sr_user where jid='">>, SJID, - <<"'and grp='">>, Group, <<"';">>], - [<<"insert into sr_user(jid, grp) values ('">>, - SJID, <<"', '">>, SGroup, <<"');">>]]; - (_Host, _R) -> - [] - end}]. +export(LServer) -> + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:export(LServer). import(LServer) -> - [{<<"select name, opts from sr_group;">>, - fun([Group, SOpts]) -> - #sr_group{group_host = {Group, LServer}, - opts = ejabberd_odbc:decode_term(SOpts)} - end}, - {<<"select jid, grp from sr_user;">>, - fun([SJID, Group]) -> - #jid{luser = U, lserver = S} = jid:from_string(SJID), - #sr_user{us = {U, S}, group_host = {Group, LServer}} - end}]. + Mod = gen_mod:db_mod(LServer, ?MODULE), + Mod:import(LServer). -import(_LServer, mnesia, #sr_group{} = G) -> - mnesia:dirty_write(G); - -import(_LServer, mnesia, #sr_user{} = U) -> - mnesia:dirty_write(U); -import(_LServer, riak, #sr_group{group_host = {_, Host}} = G) -> - ejabberd_riak:put(G, sr_group_schema(), [{'2i', [{<<"host">>, Host}]}]); -import(_LServer, riak, #sr_user{us = US, group_host = {Group, Host}} = User) -> - ejabberd_riak:put(User, sr_user_schema(), - [{i, {US, {Group, Host}}}, - {'2i', [{<<"us">>, US}, - {<<"group_host">>, {Group, Host}}]}]); -import(_, _, _) -> - pass. +import(LServer, DBType, Data) -> + Mod = gen_mod:db_mod(DBType, ?MODULE), + Mod:import(LServer, Data). mod_opt_type(db_type) -> fun gen_mod:v_db/1; mod_opt_type(_) -> [db_type]. diff --git a/src/mod_shared_roster_mnesia.erl b/src/mod_shared_roster_mnesia.erl new file mode 100644 index 000000000..ca2e55e2f --- /dev/null +++ b/src/mod_shared_roster_mnesia.erl @@ -0,0 +1,167 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 14 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_shared_roster_mnesia). + +-behaviour(mod_shared_roster). + +%% API +-export([init/2, list_groups/1, groups_with_opts/1, create_group/3, + delete_group/2, get_group_opts/2, set_group_opts/3, + get_user_groups/2, get_group_explicit_users/2, + get_user_displayed_groups/3, is_user_in_group/3, + add_user_to_group/3, remove_user_from_group/3, import/2]). + +-include("jlib.hrl"). +-include("mod_roster.hrl"). +-include("mod_shared_roster.hrl"). +-include("logger.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + mnesia:create_table(sr_group, + [{disc_copies, [node()]}, + {attributes, record_info(fields, sr_group)}]), + mnesia:create_table(sr_user, + [{disc_copies, [node()]}, {type, bag}, + {attributes, record_info(fields, sr_user)}]), + update_tables(), + mnesia:add_table_index(sr_user, group_host). + +list_groups(Host) -> + mnesia:dirty_select(sr_group, + [{#sr_group{group_host = {'$1', '$2'}, _ = '_'}, + [{'==', '$2', Host}], ['$1']}]). + +groups_with_opts(Host) -> + Gs = mnesia:dirty_select(sr_group, + [{#sr_group{group_host = {'$1', Host}, opts = '$2', + _ = '_'}, + [], [['$1', '$2']]}]), + lists:map(fun ([G, O]) -> {G, O} end, Gs). + +create_group(Host, Group, Opts) -> + R = #sr_group{group_host = {Group, Host}, opts = Opts}, + F = fun () -> mnesia:write(R) end, + mnesia:transaction(F). + +delete_group(Host, Group) -> + GroupHost = {Group, Host}, + F = fun () -> + mnesia:delete({sr_group, GroupHost}), + Users = mnesia:index_read(sr_user, GroupHost, + #sr_user.group_host), + lists:foreach(fun (UserEntry) -> + mnesia:delete_object(UserEntry) + end, + Users) + end, + mnesia:transaction(F). + +get_group_opts(Host, Group) -> + case catch mnesia:dirty_read(sr_group, {Group, Host}) of + [#sr_group{opts = Opts}] -> Opts; + _ -> error + end. + +set_group_opts(Host, Group, Opts) -> + R = #sr_group{group_host = {Group, Host}, opts = Opts}, + F = fun () -> mnesia:write(R) end, + mnesia:transaction(F). + +get_user_groups(US, Host) -> + case catch mnesia:dirty_read(sr_user, US) of + Rs when is_list(Rs) -> + [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; + _ -> + [] + end. + +get_group_explicit_users(Host, Group) -> + Read = (catch mnesia:dirty_index_read(sr_user, + {Group, Host}, #sr_user.group_host)), + case Read of + Rs when is_list(Rs) -> [R#sr_user.us || R <- Rs]; + _ -> [] + end. + +get_user_displayed_groups(LUser, LServer, GroupsOpts) -> + case catch mnesia:dirty_read(sr_user, {LUser, LServer}) of + Rs when is_list(Rs) -> + [{Group, proplists:get_value(Group, GroupsOpts, [])} + || #sr_user{group_host = {Group, H}} <- Rs, + H == LServer]; + _ -> + [] + end. + +is_user_in_group(US, Group, Host) -> + case mnesia:dirty_match_object( + #sr_user{us = US, group_host = {Group, Host}}) of + [] -> false; + _ -> true + end. + +add_user_to_group(Host, US, Group) -> + R = #sr_user{us = US, group_host = {Group, Host}}, + F = fun () -> mnesia:write(R) end, + mnesia:transaction(F). + +remove_user_from_group(Host, US, Group) -> + R = #sr_user{us = US, group_host = {Group, Host}}, + F = fun () -> mnesia:delete_object(R) end, + mnesia:transaction(F). + +import(_LServer, #sr_group{} = G) -> + mnesia:dirty_write(G); +import(_LServer, #sr_user{} = U) -> + mnesia:dirty_write(U). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +update_tables() -> + update_sr_group_table(), + update_sr_user_table(). + +update_sr_group_table() -> + Fields = record_info(fields, sr_group), + case mnesia:table_info(sr_group, attributes) of + Fields -> + ejabberd_config:convert_table_to_binary( + sr_group, Fields, set, + fun(#sr_group{group_host = {G, _}}) -> G end, + fun(#sr_group{group_host = {G, H}, + opts = Opts} = R) -> + R#sr_group{group_host = {iolist_to_binary(G), + iolist_to_binary(H)}, + opts = mod_shared_roster:opts_to_binary(Opts)} + end); + _ -> + ?INFO_MSG("Recreating sr_group table", []), + mnesia:transform_table(sr_group, ignore, Fields) + end. + +update_sr_user_table() -> + Fields = record_info(fields, sr_user), + case mnesia:table_info(sr_user, attributes) of + Fields -> + ejabberd_config:convert_table_to_binary( + sr_user, Fields, bag, + fun(#sr_user{us = {U, _}}) -> U end, + fun(#sr_user{us = {U, S}, group_host = {G, H}} = R) -> + R#sr_user{us = {iolist_to_binary(U), iolist_to_binary(S)}, + group_host = {iolist_to_binary(G), + iolist_to_binary(H)}} + end); + _ -> + ?INFO_MSG("Recreating sr_user table", []), + mnesia:transform_table(sr_user, ignore, Fields) + end. diff --git a/src/mod_shared_roster_riak.erl b/src/mod_shared_roster_riak.erl new file mode 100644 index 000000000..0df35e37d --- /dev/null +++ b/src/mod_shared_roster_riak.erl @@ -0,0 +1,139 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 14 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_shared_roster_riak). + +-behaviour(mod_shared_roster). + +%% API +-export([init/2, list_groups/1, groups_with_opts/1, create_group/3, + delete_group/2, get_group_opts/2, set_group_opts/3, + get_user_groups/2, get_group_explicit_users/2, + get_user_displayed_groups/3, is_user_in_group/3, + add_user_to_group/3, remove_user_from_group/3, import/2]). + +-include("jlib.hrl"). +-include("mod_roster.hrl"). +-include("mod_shared_roster.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +list_groups(Host) -> + case ejabberd_riak:get_keys_by_index(sr_group, <<"host">>, Host) of + {ok, Gs} -> + [G || {G, _} <- Gs]; + _ -> + [] + end. + +groups_with_opts(Host) -> + case ejabberd_riak:get_by_index(sr_group, sr_group_schema(), + <<"host">>, Host) of + {ok, Rs} -> + [{G, O} || #sr_group{group_host = {G, _}, opts = O} <- Rs]; + _ -> + [] + end. + +create_group(Host, Group, Opts) -> + {atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host}, + opts = Opts}, + sr_group_schema(), + [{'2i', [{<<"host">>, Host}]}])}. + +delete_group(Host, Group) -> + try + ok = ejabberd_riak:delete(sr_group, {Group, Host}), + ok = ejabberd_riak:delete_by_index(sr_user, <<"group_host">>, + {Group, Host}), + {atomic, ok} + catch _:{badmatch, Err} -> + {atomic, Err} + end. + +get_group_opts(Host, Group) -> + case ejabberd_riak:get(sr_group, sr_group_schema(), {Group, Host}) of + {ok, #sr_group{opts = Opts}} -> Opts; + _ -> error + end. + +set_group_opts(Host, Group, Opts) -> + {atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host}, + opts = Opts}, + sr_group_schema(), + [{'2i', [{<<"host">>, Host}]}])}. + +get_user_groups(US, Host) -> + case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of + {ok, Rs} -> + [Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host]; + _ -> + [] + end. + +get_group_explicit_users(Host, Group) -> + case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), + <<"group_host">>, {Group, Host}) of + {ok, Rs} -> + [R#sr_user.us || R <- Rs]; + _ -> + [] + end. + +get_user_displayed_groups(LUser, LServer, GroupsOpts) -> + case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), + <<"us">>, {LUser, LServer}) of + {ok, Rs} -> + [{Group, proplists:get_value(Group, GroupsOpts, [])} + || #sr_user{group_host = {Group, _}} <- Rs]; + _ -> + [] + end. + +is_user_in_group(US, Group, Host) -> + case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of + {ok, Rs} -> + lists:any( + fun(#sr_user{group_host = {G, H}}) -> + (Group == G) and (Host == H) + end, Rs); + _Err -> + false + end. + +add_user_to_group(Host, US, Group) -> + {atomic, ejabberd_riak:put( + #sr_user{us = US, group_host = {Group, Host}}, + sr_user_schema(), + [{i, {US, {Group, Host}}}, + {'2i', [{<<"us">>, US}, + {<<"group_host">>, {Group, Host}}]}])}. + +remove_user_from_group(Host, US, Group) -> + {atomic, ejabberd_riak:delete(sr_group, {US, {Group, Host}})}. + +import(_LServer, #sr_group{group_host = {_, Host}} = G) -> + ejabberd_riak:put(G, sr_group_schema(), [{'2i', [{<<"host">>, Host}]}]); +import(_LServer, #sr_user{us = US, group_host = {Group, Host}} = User) -> + ejabberd_riak:put(User, sr_user_schema(), + [{i, {US, {Group, Host}}}, + {'2i', [{<<"us">>, US}, + {<<"group_host">>, {Group, Host}}]}]). + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +sr_group_schema() -> + {record_info(fields, sr_group), #sr_group{}}. + +sr_user_schema() -> + {record_info(fields, sr_user), #sr_user{}}. diff --git a/src/mod_shared_roster_sql.erl b/src/mod_shared_roster_sql.erl new file mode 100644 index 000000000..21ea768af --- /dev/null +++ b/src/mod_shared_roster_sql.erl @@ -0,0 +1,212 @@ +%%%------------------------------------------------------------------- +%%% @author Evgeny Khramtsov +%%% @copyright (C) 2016, Evgeny Khramtsov +%%% @doc +%%% +%%% @end +%%% Created : 14 Apr 2016 by Evgeny Khramtsov +%%%------------------------------------------------------------------- +-module(mod_shared_roster_sql). + +-behaviour(mod_shared_roster). + +%% API +-export([init/2, list_groups/1, groups_with_opts/1, create_group/3, + delete_group/2, get_group_opts/2, set_group_opts/3, + get_user_groups/2, get_group_explicit_users/2, + get_user_displayed_groups/3, is_user_in_group/3, + add_user_to_group/3, remove_user_from_group/3, import/1, + import/2, export/1]). + +-include("jlib.hrl"). +-include("mod_roster.hrl"). +-include("mod_shared_roster.hrl"). + +%%%=================================================================== +%%% API +%%%=================================================================== +init(_Host, _Opts) -> + ok. + +list_groups(Host) -> + case ejabberd_odbc:sql_query( + Host, [<<"select name from sr_group;">>]) of + {selected, [<<"name">>], Rs} -> [G || [G] <- Rs]; + _ -> [] + end. + +groups_with_opts(Host) -> + case ejabberd_odbc:sql_query(Host, + [<<"select name, opts from sr_group;">>]) + of + {selected, [<<"name">>, <<"opts">>], Rs} -> + [{G, mod_shared_roster:opts_to_binary(ejabberd_odbc:decode_term(Opts))} + || [G, Opts] <- Rs]; + _ -> [] + end. + +create_group(Host, Group, Opts) -> + SGroup = ejabberd_odbc:escape(Group), + SOpts = ejabberd_odbc:encode_term(Opts), + F = fun () -> + odbc_queries:update_t(<<"sr_group">>, + [<<"name">>, <<"opts">>], [SGroup, SOpts], + [<<"name='">>, SGroup, <<"'">>]) + end, + ejabberd_odbc:sql_transaction(Host, F). + +delete_group(Host, Group) -> + SGroup = ejabberd_odbc:escape(Group), + F = fun () -> + ejabberd_odbc:sql_query_t([<<"delete from sr_group where name='">>, + SGroup, <<"';">>]), + ejabberd_odbc:sql_query_t([<<"delete from sr_user where grp='">>, + SGroup, <<"';">>]) + end, + case ejabberd_odbc:sql_transaction(Host, F) of + {atomic,{updated,_}} -> {atomic, ok}; + Res -> Res + end. + +get_group_opts(Host, Group) -> + SGroup = ejabberd_odbc:escape(Group), + case catch ejabberd_odbc:sql_query( + Host, + [<<"select opts from sr_group where name='">>, + SGroup, <<"';">>]) of + {selected, [<<"opts">>], [[SOpts]]} -> + mod_shared_roster:opts_to_binary(ejabberd_odbc:decode_term(SOpts)); + _ -> error + end. + +set_group_opts(Host, Group, Opts) -> + SGroup = ejabberd_odbc:escape(Group), + SOpts = ejabberd_odbc:encode_term(Opts), + F = fun () -> + odbc_queries:update_t(<<"sr_group">>, + [<<"name">>, <<"opts">>], [SGroup, SOpts], + [<<"name='">>, SGroup, <<"'">>]) + end, + ejabberd_odbc:sql_transaction(Host, F). + +get_user_groups(US, Host) -> + SJID = make_jid_s(US), + case catch ejabberd_odbc:sql_query( + Host, + [<<"select grp from sr_user where jid='">>, + SJID, <<"';">>]) of + {selected, [<<"grp">>], Rs} -> [G || [G] <- Rs]; + _ -> [] + end. + +get_group_explicit_users(Host, Group) -> + SGroup = ejabberd_odbc:escape(Group), + case catch ejabberd_odbc:sql_query( + Host, + [<<"select jid from sr_user where grp='">>, + SGroup, <<"';">>]) of + {selected, [<<"jid">>], Rs} -> + lists:map( + fun([JID]) -> + {U, S, _} = jid:tolower(jid:from_string(JID)), + {U, S} + end, Rs); + _ -> + [] + end. + +get_user_displayed_groups(LUser, LServer, GroupsOpts) -> + SJID = make_jid_s(LUser, LServer), + case catch ejabberd_odbc:sql_query( + LServer, + [<<"select grp from sr_user where jid='">>, + SJID, <<"';">>]) of + {selected, [<<"grp">>], Rs} -> + [{Group, proplists:get_value(Group, GroupsOpts, [])} + || [Group] <- Rs]; + _ -> [] + end. + +is_user_in_group(US, Group, Host) -> + SJID = make_jid_s(US), + SGroup = ejabberd_odbc:escape(Group), + case catch ejabberd_odbc:sql_query(Host, + [<<"select * from sr_user where jid='">>, + SJID, <<"' and grp='">>, SGroup, + <<"';">>]) of + {selected, _, []} -> false; + _ -> true + end. + +add_user_to_group(Host, US, Group) -> + SJID = make_jid_s(US), + SGroup = ejabberd_odbc:escape(Group), + F = fun () -> + odbc_queries:update_t(<<"sr_user">>, + [<<"jid">>, <<"grp">>], [SJID, SGroup], + [<<"jid='">>, SJID, <<"' and grp='">>, + SGroup, <<"'">>]) + end, + ejabberd_odbc:sql_transaction(Host, F). + +remove_user_from_group(Host, US, Group) -> + SJID = make_jid_s(US), + SGroup = ejabberd_odbc:escape(Group), + F = fun () -> + ejabberd_odbc:sql_query_t([<<"delete from sr_user where jid='">>, + SJID, <<"' and grp='">>, SGroup, + <<"';">>]), + ok + end, + ejabberd_odbc:sql_transaction(Host, F). + +export(_Server) -> + [{sr_group, + fun(Host, #sr_group{group_host = {Group, LServer}, opts = Opts}) + when LServer == Host -> + SGroup = ejabberd_odbc:escape(Group), + SOpts = ejabberd_odbc:encode_term(Opts), + [[<<"delete from sr_group where name='">>, Group, <<"';">>], + [<<"insert into sr_group(name, opts) values ('">>, + SGroup, <<"', '">>, SOpts, <<"');">>]]; + (_Host, _R) -> + [] + end}, + {sr_user, + fun(Host, #sr_user{us = {U, S}, group_host = {Group, LServer}}) + when LServer == Host -> + SGroup = ejabberd_odbc:escape(Group), + SJID = ejabberd_odbc:escape( + jid:to_string( + jid:tolower( + jid:make(U, S, <<"">>)))), + [[<<"delete from sr_user where jid='">>, SJID, + <<"'and grp='">>, Group, <<"';">>], + [<<"insert into sr_user(jid, grp) values ('">>, + SJID, <<"', '">>, SGroup, <<"');">>]]; + (_Host, _R) -> + [] + end}]. + +import(LServer) -> + [{<<"select name, opts from sr_group;">>, + fun([Group, SOpts]) -> + #sr_group{group_host = {Group, LServer}, + opts = ejabberd_odbc:decode_term(SOpts)} + end}, + {<<"select jid, grp from sr_user;">>, + fun([SJID, Group]) -> + #jid{luser = U, lserver = S} = jid:from_string(SJID), + #sr_user{us = {U, S}, group_host = {Group, LServer}} + end}]. + +import(_, _) -> + pass. + +%%%=================================================================== +%%% Internal functions +%%%=================================================================== +make_jid_s(U, S) -> + ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:make(U, S, <<"">>)))). + +make_jid_s({U, S}) -> make_jid_s(U, S).