aboutsummaryrefslogtreecommitdiff
path: root/src/mod_roster_mnesia.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_roster_mnesia.erl')
-rw-r--r--src/mod_roster_mnesia.erl325
1 files changed, 226 insertions, 99 deletions
diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl
index ddfa34d68..00e76c30b 100644
--- a/src/mod_roster_mnesia.erl
+++ b/src/mod_roster_mnesia.erl
@@ -1,45 +1,69 @@
%%%-------------------------------------------------------------------
-%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2016, Evgeny Khramtsov
-%%% @doc
-%%%
-%%% @end
+%%% File : mod_roster_mnesia.erl
+%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%-------------------------------------------------------------------
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2019 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_roster_mnesia).
-behaviour(mod_roster).
%% API
-export([init/2, read_roster_version/2, write_roster_version/4,
- get_roster/2, get_roster_by_jid/3, get_only_items/2,
- roster_subscribe/4, get_roster_by_jid_with_groups/3,
+ get_roster/2, get_roster_item/3, roster_subscribe/4,
remove_user/2, update_roster/4, del_roster/3, transaction/2,
- read_subscription_and_groups/3, import/2]).
+ read_subscription_and_groups/3, import/3, create_roster/1,
+ process_rosteritems/5,
+ use_cache/2]).
+-export([need_transform/1, transform/1]).
--include("jlib.hrl").
-include("mod_roster.hrl").
-include("logger.hrl").
+-include("xmpp.hrl").
%%%===================================================================
%%% API
%%%===================================================================
init(_Host, _Opts) ->
- mnesia:create_table(roster,
- [{disc_copies, [node()]},
- {attributes, record_info(fields, roster)}]),
- mnesia:create_table(roster_version,
- [{disc_copies, [node()]},
+ ejabberd_mnesia:create(?MODULE, roster,
+ [{disc_only_copies, [node()]},
+ {attributes, record_info(fields, roster)},
+ {index, [us]}]),
+ ejabberd_mnesia:create(?MODULE, roster_version,
+ [{disc_only_copies, [node()]},
{attributes,
- record_info(fields, roster_version)}]),
- update_tables(),
- mnesia:add_table_index(roster, us),
- mnesia:add_table_index(roster_version, us).
+ record_info(fields, roster_version)}]).
+
+use_cache(Host, Table) ->
+ case mnesia:table_info(Table, storage_type) of
+ disc_only_copies ->
+ mod_roster_opt:use_cache(Host);
+ _ ->
+ false
+ end.
read_roster_version(LUser, LServer) ->
US = {LUser, LServer},
case mnesia:dirty_read(roster_version, US) of
- [#roster_version{version = V}] -> V;
+ [#roster_version{version = V}] -> {ok, V};
[] -> error
end.
@@ -52,32 +76,17 @@ write_roster_version(LUser, LServer, InTransaction, Ver) ->
end.
get_roster(LUser, LServer) ->
- mnesia:dirty_index_read(roster, {LUser, LServer}, #roster.us).
+ {ok, mnesia:dirty_index_read(roster, {LUser, LServer}, #roster.us)}.
-get_roster_by_jid(LUser, LServer, LJID) ->
+get_roster_item(LUser, LServer, LJID) ->
case mnesia:read({roster, {LUser, LServer, LJID}}) of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID};
- [I] ->
- I#roster{jid = LJID, name = <<"">>, groups = [],
- xs = []}
+ [I] -> {ok, I};
+ [] -> error
end.
-get_only_items(LUser, LServer) ->
- get_roster(LUser, LServer).
-
roster_subscribe(_LUser, _LServer, _LJID, Item) ->
mnesia:write(Item).
-get_roster_by_jid_with_groups(LUser, LServer, LJID) ->
- case mnesia:read({roster, {LUser, LServer, LJID}}) of
- [] ->
- #roster{usj = {LUser, LServer, LJID},
- us = {LUser, LServer}, jid = LJID};
- [I] -> I
- end.
-
remove_user(LUser, LServer) ->
US = {LUser, LServer},
F = fun () ->
@@ -95,8 +104,8 @@ del_roster(LUser, LServer, LJID) ->
read_subscription_and_groups(LUser, LServer, LJID) ->
case mnesia:dirty_read(roster, {LUser, LServer, LJID}) of
- [#roster{subscription = Subscription, groups = Groups}] ->
- {Subscription, Groups};
+ [#roster{subscription = Subscription, ask = Ask, groups = Groups}] ->
+ {ok, {Subscription, Ask, Groups}};
_ ->
error
end.
@@ -104,68 +113,186 @@ read_subscription_and_groups(LUser, LServer, LJID) ->
transaction(_LServer, F) ->
mnesia:transaction(F).
-import(_LServer, #roster{} = R) ->
+create_roster(RItem) ->
+ mnesia:dirty_write(RItem).
+
+import(_LServer, <<"rosterusers">>, #roster{} = R) ->
mnesia:dirty_write(R);
-import(_LServer, #roster_version{} = RV) ->
+import(LServer, <<"roster_version">>, [LUser, Ver]) ->
+ RV = #roster_version{us = {LUser, LServer}, version = Ver},
mnesia:dirty_write(RV).
+need_transform({roster, {U, S, _}, _, _, _, _, _, _, _, _})
+ when is_list(U) orelse is_list(S) ->
+ ?INFO_MSG("Mnesia table 'roster' will be converted to binary", []),
+ true;
+need_transform({roster_version, {U, S}, Ver})
+ when is_list(U) orelse is_list(S) orelse is_list(Ver) ->
+ ?INFO_MSG("Mnesia table 'roster_version' will be converted to binary", []),
+ true;
+need_transform(_) ->
+ false.
+
+transform(#roster{usj = {U, S, {LU, LS, LR}},
+ us = {U1, S1},
+ jid = {U2, S2, R2},
+ name = Name,
+ groups = Gs,
+ askmessage = Ask,
+ xs = Xs} = R) ->
+ R#roster{usj = {iolist_to_binary(U), iolist_to_binary(S),
+ {iolist_to_binary(LU),
+ iolist_to_binary(LS),
+ iolist_to_binary(LR)}},
+ us = {iolist_to_binary(U1), iolist_to_binary(S1)},
+ jid = {iolist_to_binary(U2),
+ iolist_to_binary(S2),
+ iolist_to_binary(R2)},
+ name = iolist_to_binary(Name),
+ groups = [iolist_to_binary(G) || G <- Gs],
+ askmessage = try iolist_to_binary(Ask)
+ catch _:_ -> <<"">> end,
+ xs = [fxml:to_xmlel(X) || X <- Xs]};
+transform(#roster_version{us = {U, S}, version = Ver} = R) ->
+ R#roster_version{us = {iolist_to_binary(U), iolist_to_binary(S)},
+ version = iolist_to_binary(Ver)}.
+
%%%===================================================================
-%%% Internal functions
-%%%===================================================================
-update_tables() ->
- update_roster_table(),
- update_roster_version_table().
-
-update_roster_table() ->
- Fields = record_info(fields, roster),
- case mnesia:table_info(roster, attributes) of
- Fields ->
- ejabberd_config:convert_table_to_binary(
- roster, Fields, set,
- fun(#roster{usj = {U, _, _}}) -> U end,
- fun(#roster{usj = {U, S, {LU, LS, LR}},
- us = {U1, S1},
- jid = {U2, S2, R2},
- name = Name,
- groups = Gs,
- askmessage = Ask,
- xs = Xs} = R) ->
- R#roster{usj = {iolist_to_binary(U),
- iolist_to_binary(S),
- {iolist_to_binary(LU),
- iolist_to_binary(LS),
- iolist_to_binary(LR)}},
- us = {iolist_to_binary(U1),
- iolist_to_binary(S1)},
- jid = {iolist_to_binary(U2),
- iolist_to_binary(S2),
- iolist_to_binary(R2)},
- name = iolist_to_binary(Name),
- groups = [iolist_to_binary(G) || G <- Gs],
- askmessage = try iolist_to_binary(Ask)
- catch _:_ -> <<"">> end,
- xs = [fxml:to_xmlel(X) || X <- Xs]}
- end);
- _ ->
- ?INFO_MSG("Recreating roster table", []),
- mnesia:transform_table(roster, ignore, Fields)
- end.
-%% Convert roster table to support virtual host
-%% Convert roster table: xattrs fields become
-update_roster_version_table() ->
- Fields = record_info(fields, roster_version),
- case mnesia:table_info(roster_version, attributes) of
- Fields ->
- ejabberd_config:convert_table_to_binary(
- roster_version, Fields, set,
- fun(#roster_version{us = {U, _}}) -> U end,
- fun(#roster_version{us = {U, S}, version = Ver} = R) ->
- R#roster_version{us = {iolist_to_binary(U),
- iolist_to_binary(S)},
- version = iolist_to_binary(Ver)}
- end);
+process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
+ Action = case ActionS of
+ "list" -> list;
+ "delete" -> delete
+ end,
+ Subs = lists:foldl(
+ fun(any, _) -> [none, from, to, both];
+ (Sub, Subs) -> [Sub | Subs]
+ end,
+ [],
+ [list_to_atom(S) || S <- string:tokens(SubsS, ":")]
+ ),
+ Asks = lists:foldl(
+ fun(any, _) -> [none, out, in];
+ (Ask, Asks) -> [Ask | Asks]
+ end,
+ [],
+ [list_to_atom(S) || S <- string:tokens(AsksS, ":")]
+ ),
+ Users = lists:foldl(
+ fun("any", _) -> ["*", "*@*"];
+ (U, Us) -> [U | Us]
+ end,
+ [],
+ [S || S <- string:tokens(UsersS, ":")]
+ ),
+ Contacts = lists:foldl(
+ fun("any", _) -> ["*", "*@*"];
+ (U, Us) -> [U | Us]
+ end,
+ [],
+ [S || S <- string:tokens(ContactsS, ":")]
+ ),
+ rosteritem_purge({Action, Subs, Asks, Users, Contacts}).
+
+%% @spec ({Action::atom(), Subs::[atom()], Asks::[atom()], User::string(), Contact::string()}) -> {atomic, ok}
+rosteritem_purge(Options) ->
+ Num_rosteritems = mnesia:table_info(roster, size),
+ io:format("There are ~p roster items in total.~n", [Num_rosteritems]),
+ Key = mnesia:dirty_first(roster),
+ rip(Key, Options, {0, Num_rosteritems, 0, 0}, []).
+
+rip('$end_of_table', _Options, Counters, Res) ->
+ print_progress_line(Counters),
+ Res;
+rip(Key, Options, {Pr, NT, NV, ND}, Res) ->
+ Key_next = mnesia:dirty_next(roster, Key),
+ {Action, _, _, _, _} = Options,
+ {ND2, Res2} = case decide_rip(Key, Options) of
+ true ->
+ Jids = apply_action(Action, Key),
+ {ND+1, [Jids | Res]};
+ false ->
+ {ND, Res}
+ end,
+ NV2 = NV+1,
+ Pr2 = print_progress_line({Pr, NT, NV2, ND2}),
+ rip(Key_next, Options, {Pr2, NT, NV2, ND2}, Res2).
+
+apply_action(list, Key) ->
+ {User, Server, JID} = Key,
+ {RUser, RServer, _} = JID,
+ Jid1string = <<User/binary, "@", Server/binary>>,
+ Jid2string = <<RUser/binary, "@", RServer/binary>>,
+ io:format("Matches: ~ts ~ts~n", [Jid1string, Jid2string]),
+ {Jid1string, Jid2string};
+apply_action(delete, Key) ->
+ R = apply_action(list, Key),
+ mnesia:dirty_delete(roster, Key),
+ R.
+
+print_progress_line({_Pr, 0, _NV, _ND}) ->
+ ok;
+print_progress_line({Pr, NT, NV, ND}) ->
+ Pr2 = trunc((NV/NT)*100),
+ case Pr == Pr2 of
+ true ->
+ ok;
+ false ->
+ io:format("Progress ~p% - visited ~p - deleted ~p~n", [Pr2, NV, ND])
+ end,
+ Pr2.
+
+decide_rip(Key, {_Action, Subs, Asks, User, Contact}) ->
+ case catch mnesia:dirty_read(roster, Key) of
+ [RI] ->
+ lists:member(RI#roster.subscription, Subs)
+ andalso lists:member(RI#roster.ask, Asks)
+ andalso decide_rip_jid(RI#roster.us, User)
+ andalso decide_rip_jid(RI#roster.jid, Contact);
_ ->
- ?INFO_MSG("Recreating roster_version table", []),
- mnesia:transform_table(roster_version, ignore, Fields)
+ false
+ end.
+
+%% Returns true if the server of the JID is included in the servers
+decide_rip_jid({UName, UServer, _UResource}, Match_list) ->
+ decide_rip_jid({UName, UServer}, Match_list);
+decide_rip_jid({UName, UServer}, Match_list) ->
+ lists:any(
+ fun(Match_string) ->
+ MJID = jid:decode(list_to_binary(Match_string)),
+ MName = MJID#jid.luser,
+ MServer = MJID#jid.lserver,
+ Is_server = is_glob_match(UServer, MServer),
+ case MName of
+ <<>> when UName == <<>> ->
+ Is_server;
+ <<>> ->
+ false;
+ _ ->
+ Is_server
+ andalso is_glob_match(UName, MName)
+ end
+ end,
+ Match_list).
+
+%% Copied from ejabberd-2.0.0/src/acl.erl
+is_regexp_match(String, RegExp) ->
+ case ejabberd_regexp:run(String, RegExp) of
+ nomatch ->
+ false;
+ match ->
+ true;
+ {error, ErrDesc} ->
+ io:format(
+ "Wrong regexp ~p in ACL: ~p",
+ [RegExp, ErrDesc]),
+ false
end.
+is_glob_match(String, <<"!", Glob/binary>>) ->
+ not is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob));
+is_glob_match(String, Glob) ->
+ is_regexp_match(String, ejabberd_regexp:sh_to_awk(Glob)).
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================