summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2019-05-15 10:57:55 +0200
committerBadlop <badlop@process-one.net>2019-05-15 10:57:55 +0200
commite996579dd19a7830b09aa36fb537883732e5bd18 (patch)
treeb44f06af83239a675d91b5c2dac8618e4423aab6
parentDon't put duplicate polling attribute in bosh payload (diff)
Preliminary support for SQL in process_rosteritems, and move code (#2448)
-rw-r--r--src/mod_admin_extra.erl184
-rw-r--r--src/mod_roster.erl6
-rw-r--r--src/mod_roster_mnesia.erl138
-rw-r--r--src/mod_roster_sql.erl38
4 files changed, 212 insertions, 154 deletions
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 68fc6e81..d86e5025 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -57,7 +57,7 @@
% Roster
add_rosteritem/7, delete_rosteritem/4,
- process_rosteritems/5, get_roster/2, push_roster/3,
+ get_roster/2, push_roster/3,
push_roster_all/1, push_alltoall/2,
push_roster_item/5, build_roster_item/3,
@@ -506,7 +506,7 @@ get_commands_spec() ->
args_desc = ["User name", "Server name", "Contact user name", "Contact server name"],
result = {res, rescode}},
#ejabberd_commands{name = process_rosteritems, tags = [roster],
- desc = "List/delete rosteritems that match filter (only Mnesia)",
+ desc = "List/delete rosteritems that match filter",
longdesc = "Explanation of each argument:\n"
" - action: what to do with each rosteritem that "
"matches all the filtering options\n"
@@ -515,6 +515,8 @@ get_commands_spec() ->
" - users: the JIDs of the local user\n"
" - contacts: the JIDs of the contact in the roster\n"
"\n"
+ " *** Mnesia: \n"
+ "\n"
"Allowed values in the arguments:\n"
" ACTION = list | delete\n"
" SUBS = SUB[:SUB]* | any\n"
@@ -532,8 +534,26 @@ get_commands_spec() ->
"'example.org' and that the contact JID is either a "
"bare server name (without user part) or that has a "
"user part and the server part contains the word 'icq'"
- ":\n list none:from:to any *@example.org *:*@*icq*",
- module = ?MODULE, function = process_rosteritems,
+ ":\n list none:from:to any *@example.org *:*@*icq*"
+ "\n\n"
+ " *** SQL:\n"
+ "\n"
+ "Allowed values in the arguments:\n"
+ " ACTION = list | delete\n"
+ " SUBS = any | none | from | to | both\n"
+ " ASKS = any | none | out | in\n"
+ " USERS = JID\n"
+ " CONTACTS = JID\n"
+ " JID = characters valid in a JID, and can use the "
+ "globs: _ and %\n"
+ "\n"
+ "This example will list roster items with subscription "
+ "'to' that have any ask property, of "
+ "local users which JID is in the virtual host "
+ "'example.org' and that the contact JID's "
+ "server part contains the word 'icq'"
+ ":\n list to any %@example.org %@%icq%",
+ module = mod_roster, function = process_rosteritems,
args = [{action, string}, {subs, string},
{asks, string}, {users, string},
{contacts, string}],
@@ -1535,163 +1555,19 @@ stats(Name, Host) ->
end.
-
-%%-----------------------------
-%% Purge roster items
-%%-----------------------------
-
-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: ~s ~s~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);
- _ ->
- 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).
-
user_action(User, Server, Fun, OK) ->
case ejabberd_auth:user_exists(User, Server) of
true ->
- case catch Fun() of
+ case catch Fun() of
OK -> ok;
- {error, Error} -> throw(Error);
+ {error, Error} -> throw(Error);
Error ->
?ERROR_MSG("Command returned: ~p", [Error]),
- 1
- end;
- false ->
- throw({not_found, "unknown_user"})
- end.
-
-%% 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
+ 1
+ end;
+ false ->
+ throw({not_found, "unknown_user"})
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)).
num_prio(Priority) when is_integer(Priority) ->
Priority;
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index a0e52ee7..6d8e4551 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -50,6 +50,7 @@
webadmin_user/4, get_versioning_feature/2,
roster_versioning_enabled/1, roster_version/2,
mod_opt_type/1, mod_options/1, set_roster/1, del_roster/3,
+ process_rosteritems/5,
depends/2]).
-include("logger.hrl").
@@ -892,6 +893,11 @@ is_subscribed(From, #jid{luser = LUser, lserver = LServer}) ->
(Sub /= none) orelse (Ask == subscribe)
orelse (Ask == out) orelse (Ask == both).
+process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
+ LServer = ejabberd_config:get_myname(),
+ Mod = gen_mod:db_mod(LServer, ?MODULE),
+ Mod:process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
webadmin_page(_, Host,
diff --git a/src/mod_roster_mnesia.erl b/src/mod_roster_mnesia.erl
index c67d7d5e..41da9a6b 100644
--- a/src/mod_roster_mnesia.erl
+++ b/src/mod_roster_mnesia.erl
@@ -31,11 +31,13 @@
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/3, create_roster/1,
+ process_rosteritems/5,
use_cache/2]).
-export([need_transform/1, transform/1]).
-include("mod_roster.hrl").
-include("logger.hrl").
+-include("xmpp.hrl").
%%%===================================================================
%%% API
@@ -155,5 +157,141 @@ transform(#roster_version{us = {U, S}, version = Ver} = R) ->
version = iolist_to_binary(Ver)}.
%%%===================================================================
+
+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: ~s ~s~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);
+ _ ->
+ 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
%%%===================================================================
diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl
index 91242f61..a512f1bf 100644
--- a/src/mod_roster_sql.erl
+++ b/src/mod_roster_sql.erl
@@ -33,11 +33,13 @@
get_roster/2, get_roster_item/3, roster_subscribe/4,
read_subscription_and_groups/3, remove_user/2,
update_roster/4, del_roster/3, transaction/2,
+ process_rosteritems/5,
import/3, export/1, raw_to_record/2]).
-include("mod_roster.hrl").
-include("ejabberd_sql_pt.hrl").
-include("logger.hrl").
+-include("jid.hrl").
%%%===================================================================
%%% API
@@ -375,3 +377,39 @@ format_row_error(User, Server, Why) ->
{ask, Ask} -> ["Malformed 'ask' field with value '", Ask, "'"]
end,
" detected for ", User, "@", Server, " in table 'rosterusers'"].
+
+process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) ->
+ process_rosteritems_sql(ActionS, list_to_atom(SubsS), list_to_atom(AsksS),
+ list_to_binary(UsersS), list_to_binary(ContactsS)).
+
+process_rosteritems_sql(ActionS, Subscription, Ask, SLocalJID, SJID) ->
+ [LUser, LServer] = binary:split(SLocalJID, <<"@">>),
+ SSubscription = case Subscription of
+ any -> <<"_">>;
+ both -> <<"B">>;
+ to -> <<"T">>;
+ from -> <<"F">>;
+ none -> <<"N">>
+ end,
+ SAsk = case Ask of
+ any -> <<"_">>;
+ subscribe -> <<"S">>;
+ unsubscribe -> <<"U">>;
+ both -> <<"B">>;
+ out -> <<"O">>;
+ in -> <<"I">>;
+ none -> <<"N">>
+ end,
+ {selected, List} = ejabberd_sql:sql_query(
+ LServer,
+ ?SQL("select @(username)s, @(jid)s from rosterusers "
+ "where username LIKE %(LUser)s"
+ " and %(LServer)H"
+ " and jid LIKE %(SJID)s"
+ " and subscription LIKE %(SSubscription)s"
+ " and ask LIKE %(SAsk)s")),
+ case ActionS of
+ "delete" -> [mod_roster:del_roster(User, LServer, jid:decode(Contact)) || {User, Contact} <- List];
+ "list" -> ok
+ end,
+ List.