]> granicus.if.org Git - ejabberd/commitdiff
Clean mod_shared_roster.erl from DB specific code
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 14 Apr 2016 08:45:43 +0000 (11:45 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 14 Apr 2016 08:45:43 +0000 (11:45 +0300)
include/mod_shared_roster.hrl [new file with mode: 0644]
src/mod_shared_roster.erl
src/mod_shared_roster_mnesia.erl [new file with mode: 0644]
src/mod_shared_roster_riak.erl [new file with mode: 0644]
src/mod_shared_roster_sql.erl [new file with mode: 0644]

diff --git a/include/mod_shared_roster.hrl b/include/mod_shared_roster.hrl
new file mode 100644 (file)
index 0000000..1f96b30
--- /dev/null
@@ -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()}}).
index 212a7d47f3c30123967bffaf5fd55102dda77796..6670cf77bb50404de2d5a25999de00adeb62d195 100644 (file)
@@ -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").
 
 -include("ejabberd_web_admin.hrl").
 
--record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()},
-                   opts = [] :: list() | '_' | '$2'}).
-
--record(sr_user, {us = {<<"">>, <<"">>} :: {binary(), binary()},
-                  group_host = {<<"">>, <<"">>} :: {binary(), binary()}}).
+-include("mod_shared_roster.hrl").
+
+-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}].
-
-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.
+    Mod = gen_mod:db_mod(LServer, ?MODULE),
+    Mod:import(LServer).
+
+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 (file)
index 0000000..ca2e55e
--- /dev/null
@@ -0,0 +1,167 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-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 (file)
index 0000000..0df35e3
--- /dev/null
@@ -0,0 +1,139 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-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 (file)
index 0000000..21ea768
--- /dev/null
@@ -0,0 +1,212 @@
+%%%-------------------------------------------------------------------
+%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%% @copyright (C) 2016, Evgeny Khramtsov
+%%% @doc
+%%%
+%%% @end
+%%% Created : 14 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
+%%%-------------------------------------------------------------------
+-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).