Merge branch 'move-db-code'
This commit is contained in:
commit
fb0ecf3361
|
@ -0,0 +1,5 @@
|
||||||
|
-record(motd, {server = <<"">> :: binary(),
|
||||||
|
packet = #xmlel{} :: xmlel()}).
|
||||||
|
|
||||||
|
-record(motd_users, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
||||||
|
dummy = [] :: [] | '_'}).
|
|
@ -0,0 +1,4 @@
|
||||||
|
-record(caps_features,
|
||||||
|
{node_pair = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||||
|
features = [] :: [binary()] | pos_integer()
|
||||||
|
}).
|
|
@ -0,0 +1,15 @@
|
||||||
|
-type conn_param() :: {binary(), binary(), inet:port_number(), binary()} |
|
||||||
|
{binary(), binary(), inet:port_number()} |
|
||||||
|
{binary(), binary()} |
|
||||||
|
{binary()}.
|
||||||
|
|
||||||
|
-type irc_data() :: [{username, binary()} | {connections_params, [conn_param()]}].
|
||||||
|
|
||||||
|
-record(irc_connection,
|
||||||
|
{jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()},
|
||||||
|
pid = self() :: pid()}).
|
||||||
|
|
||||||
|
-record(irc_custom,
|
||||||
|
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()},
|
||||||
|
binary()},
|
||||||
|
data = [] :: irc_data()}).
|
|
@ -0,0 +1,3 @@
|
||||||
|
-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||||
|
timestamp = 0 :: non_neg_integer(),
|
||||||
|
status = <<"">> :: binary()}).
|
|
@ -0,0 +1,4 @@
|
||||||
|
-record(private_storage,
|
||||||
|
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
|
||||||
|
'$1' | '_'},
|
||||||
|
xml = #xmlel{} :: xmlel() | '_' | '$1'}).
|
|
@ -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()}}).
|
|
@ -0,0 +1,8 @@
|
||||||
|
-record(vcard_search,
|
||||||
|
{us, user, luser, fn, lfn, family, lfamily, given,
|
||||||
|
lgiven, middle, lmiddle, nickname, lnickname, bday,
|
||||||
|
lbday, ctry, lctry, locality, llocality, email, lemail,
|
||||||
|
orgname, lorgname, orgunit, lorgunit}).
|
||||||
|
|
||||||
|
-record(vcard, {us = {<<"">>, <<"">>} :: {binary(), binary()} | binary(),
|
||||||
|
vcard = #xmlel{} :: xmlel()}).
|
|
@ -0,0 +1,2 @@
|
||||||
|
-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||||
|
hash = <<>> :: binary()}).
|
|
@ -36,7 +36,7 @@
|
||||||
loaded_modules/1, loaded_modules_with_opts/1,
|
loaded_modules/1, loaded_modules_with_opts/1,
|
||||||
get_hosts/2, get_module_proc/2, is_loaded/2,
|
get_hosts/2, get_module_proc/2, is_loaded/2,
|
||||||
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
|
start_modules/0, start_modules/1, stop_modules/0, stop_modules/1,
|
||||||
default_db/1, v_db/1, opt_type/1]).
|
default_db/1, v_db/1, opt_type/1, db_mod/2, db_mod/3]).
|
||||||
|
|
||||||
%%-export([behaviour_info/1]).
|
%%-export([behaviour_info/1]).
|
||||||
|
|
||||||
|
@ -319,6 +319,19 @@ db_type(Host, Opts) when is_list(Opts) ->
|
||||||
default_db(Host) ->
|
default_db(Host) ->
|
||||||
ejabberd_config:get_option({default_db, Host}, fun v_db/1, mnesia).
|
ejabberd_config:get_option({default_db, Host}, fun v_db/1, mnesia).
|
||||||
|
|
||||||
|
-spec db_mod(binary() | global | db_type(), module()) -> module().
|
||||||
|
|
||||||
|
db_mod(odbc, Module) -> list_to_atom(atom_to_list(Module) ++ "_sql");
|
||||||
|
db_mod(mnesia, Module) -> list_to_atom(atom_to_list(Module) ++ "_mnesia");
|
||||||
|
db_mod(riak, Module) -> list_to_atom(atom_to_list(Module) ++ "_riak");
|
||||||
|
db_mod(Host, Module) when is_binary(Host) orelse Host == global ->
|
||||||
|
db_mod(db_type(Host, Module), Module).
|
||||||
|
|
||||||
|
-spec db_mod(binary() | global, opts(), module()) -> module().
|
||||||
|
|
||||||
|
db_mod(Host, Opts, Module) when is_list(Opts) ->
|
||||||
|
db_mod(db_type(Host, Opts), Module).
|
||||||
|
|
||||||
-spec loaded_modules(binary()) -> [atom()].
|
-spec loaded_modules(binary()) -> [atom()].
|
||||||
|
|
||||||
loaded_modules(Host) ->
|
loaded_modules(Host) ->
|
||||||
|
|
|
@ -41,11 +41,16 @@
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
-include("adhoc.hrl").
|
-include("adhoc.hrl").
|
||||||
|
-include("mod_announce.hrl").
|
||||||
|
|
||||||
-record(motd, {server = <<"">> :: binary(),
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
packet = #xmlel{} :: xmlel()}).
|
-callback import(binary(), #motd{} | #motd_users{}) -> ok | pass.
|
||||||
-record(motd_users, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
-callback set_motd_users(binary(), [{binary(), binary(), binary()}]) -> {atomic, any()}.
|
||||||
dummy = [] :: [] | '_'}).
|
-callback set_motd(binary(), xmlel()) -> {atomic, any()}.
|
||||||
|
-callback delete_motd(binary()) -> {atomic, any()}.
|
||||||
|
-callback get_motd(binary()) -> {ok, xmlel()} | error.
|
||||||
|
-callback is_motd_user(binary(), binary()) -> boolean().
|
||||||
|
-callback set_motd_user(binary(), binary()) -> {atomic, any()}.
|
||||||
|
|
||||||
-define(PROCNAME, ejabberd_announce).
|
-define(PROCNAME, ejabberd_announce).
|
||||||
|
|
||||||
|
@ -55,20 +60,8 @@
|
||||||
tokenize(Node) -> str:tokens(Node, <<"/#">>).
|
tokenize(Node) -> str:tokens(Node, <<"/#">>).
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(motd,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, motd)}]),
|
|
||||||
mnesia:create_table(motd_users,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, motd_users)}]),
|
|
||||||
update_tables();
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||||
?MODULE, announce, 50),
|
?MODULE, announce, 50),
|
||||||
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
|
ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, disco_identity, 50),
|
||||||
|
@ -789,41 +782,8 @@ announce_motd(Host, Packet) ->
|
||||||
announce_motd_update(LServer, Packet),
|
announce_motd_update(LServer, Packet),
|
||||||
Sessions = ejabberd_sm:get_vh_session_list(LServer),
|
Sessions = ejabberd_sm:get_vh_session_list(LServer),
|
||||||
announce_online1(Sessions, LServer, Packet),
|
announce_online1(Sessions, LServer, Packet),
|
||||||
case gen_mod:db_type(LServer, ?MODULE) of
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
mnesia ->
|
Mod:set_motd_users(LServer, Sessions).
|
||||||
F = fun() ->
|
|
||||||
lists:foreach(
|
|
||||||
fun({U, S, _R}) ->
|
|
||||||
mnesia:write(#motd_users{us = {U, S}})
|
|
||||||
end, Sessions)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
riak ->
|
|
||||||
try
|
|
||||||
lists:foreach(
|
|
||||||
fun({U, S, _R}) ->
|
|
||||||
ok = ejabberd_riak:put(#motd_users{us = {U, S}},
|
|
||||||
motd_users_schema(),
|
|
||||||
[{'2i', [{<<"server">>, S}]}])
|
|
||||||
end, Sessions),
|
|
||||||
{atomic, ok}
|
|
||||||
catch _:{badmatch, Err} ->
|
|
||||||
{atomic, Err}
|
|
||||||
end;
|
|
||||||
odbc ->
|
|
||||||
F = fun() ->
|
|
||||||
lists:foreach(
|
|
||||||
fun({U, _S, _R}) ->
|
|
||||||
Username = ejabberd_odbc:escape(U),
|
|
||||||
odbc_queries:update_t(
|
|
||||||
<<"motd">>,
|
|
||||||
[<<"username">>, <<"xml">>],
|
|
||||||
[Username, <<"">>],
|
|
||||||
[<<"username='">>, Username, <<"'">>])
|
|
||||||
end, Sessions)
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F)
|
|
||||||
end.
|
|
||||||
|
|
||||||
announce_motd_update(From, To, Packet) ->
|
announce_motd_update(From, To, Packet) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
|
@ -853,27 +813,8 @@ announce_all_hosts_motd_update(From, To, Packet) ->
|
||||||
|
|
||||||
announce_motd_update(LServer, Packet) ->
|
announce_motd_update(LServer, Packet) ->
|
||||||
announce_motd_delete(LServer),
|
announce_motd_delete(LServer),
|
||||||
case gen_mod:db_type(LServer, ?MODULE) of
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
mnesia ->
|
Mod:set_motd(LServer, Packet).
|
||||||
F = fun() ->
|
|
||||||
mnesia:write(#motd{server = LServer, packet = Packet})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
riak ->
|
|
||||||
{atomic, ejabberd_riak:put(#motd{server = LServer,
|
|
||||||
packet = Packet},
|
|
||||||
motd_schema())};
|
|
||||||
odbc ->
|
|
||||||
XML = ejabberd_odbc:escape(fxml:element_to_binary(Packet)),
|
|
||||||
F = fun() ->
|
|
||||||
odbc_queries:update_t(
|
|
||||||
<<"motd">>,
|
|
||||||
[<<"username">>, <<"xml">>],
|
|
||||||
[<<"">>, XML],
|
|
||||||
[<<"username=''">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F)
|
|
||||||
end.
|
|
||||||
|
|
||||||
announce_motd_delete(From, To, Packet) ->
|
announce_motd_delete(From, To, Packet) ->
|
||||||
Host = To#jid.lserver,
|
Host = To#jid.lserver,
|
||||||
|
@ -902,112 +843,30 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
announce_motd_delete(LServer) ->
|
announce_motd_delete(LServer) ->
|
||||||
case gen_mod:db_type(LServer, ?MODULE) of
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
mnesia ->
|
Mod:delete_motd(LServer).
|
||||||
F = fun() ->
|
|
||||||
mnesia:delete({motd, LServer}),
|
|
||||||
mnesia:write_lock_table(motd_users),
|
|
||||||
Users = mnesia:select(
|
|
||||||
motd_users,
|
|
||||||
[{#motd_users{us = '$1', _ = '_'},
|
|
||||||
[{'==', {element, 2, '$1'}, LServer}],
|
|
||||||
['$1']}]),
|
|
||||||
lists:foreach(fun(US) ->
|
|
||||||
mnesia:delete({motd_users, US})
|
|
||||||
end, Users)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
riak ->
|
|
||||||
try
|
|
||||||
ok = ejabberd_riak:delete(motd, LServer),
|
|
||||||
ok = ejabberd_riak:delete_by_index(motd_users,
|
|
||||||
<<"server">>,
|
|
||||||
LServer),
|
|
||||||
{atomic, ok}
|
|
||||||
catch _:{badmatch, Err} ->
|
|
||||||
{atomic, Err}
|
|
||||||
end;
|
|
||||||
odbc ->
|
|
||||||
F = fun() ->
|
|
||||||
ejabberd_odbc:sql_query_t([<<"delete from motd;">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F)
|
|
||||||
end.
|
|
||||||
|
|
||||||
send_motd(JID) ->
|
send_motd(#jid{luser = LUser, lserver = LServer} = JID) when LUser /= <<>> ->
|
||||||
send_motd(JID, gen_mod:db_type(JID#jid.lserver, ?MODULE)).
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
case Mod:get_motd(LServer) of
|
||||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID, mnesia) ->
|
{ok, Packet} ->
|
||||||
case catch mnesia:dirty_read({motd, LServer}) of
|
case Mod:is_motd_user(LUser, LServer) of
|
||||||
[#motd{packet = Packet}] ->
|
false ->
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read({motd_users, US}) of
|
|
||||||
[#motd_users{}] ->
|
|
||||||
ok;
|
|
||||||
_ ->
|
|
||||||
Local = jid:make(<<>>, LServer, <<>>),
|
Local = jid:make(<<>>, LServer, <<>>),
|
||||||
ejabberd_router:route(Local, JID, Packet),
|
ejabberd_router:route(Local, JID, Packet),
|
||||||
F = fun() ->
|
Mod:set_motd_user(LUser, LServer);
|
||||||
mnesia:write(#motd_users{us = US})
|
true ->
|
||||||
end,
|
ok
|
||||||
mnesia:transaction(F)
|
|
||||||
end;
|
end;
|
||||||
_ ->
|
error ->
|
||||||
ok
|
ok
|
||||||
end;
|
end;
|
||||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID, riak) ->
|
send_motd(_) ->
|
||||||
case catch ejabberd_riak:get(motd, motd_schema(), LServer) of
|
|
||||||
{ok, #motd{packet = Packet}} ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case ejabberd_riak:get(motd_users, motd_users_schema(), US) of
|
|
||||||
{ok, #motd_users{}} ->
|
|
||||||
ok;
|
|
||||||
_ ->
|
|
||||||
Local = jid:make(<<>>, LServer, <<>>),
|
|
||||||
ejabberd_router:route(Local, JID, Packet),
|
|
||||||
{atomic, ejabberd_riak:put(
|
|
||||||
#motd_users{us = US}, motd_users_schema(),
|
|
||||||
[{'2i', [{<<"server">>, LServer}]}])}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end;
|
|
||||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID, odbc) when LUser /= <<>> ->
|
|
||||||
case catch ejabberd_odbc:sql_query(
|
|
||||||
LServer, [<<"select xml from motd where username='';">>]) of
|
|
||||||
{selected, [<<"xml">>], [[XML]]} ->
|
|
||||||
case fxml_stream:parse_element(XML) of
|
|
||||||
{error, _} ->
|
|
||||||
ok;
|
|
||||||
Packet ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
case catch ejabberd_odbc:sql_query(
|
|
||||||
LServer,
|
|
||||||
[<<"select username from motd "
|
|
||||||
"where username='">>, Username, <<"';">>]) of
|
|
||||||
{selected, [<<"username">>], []} ->
|
|
||||||
Local = jid:make(<<"">>, LServer, <<"">>),
|
|
||||||
ejabberd_router:route(Local, JID, Packet),
|
|
||||||
F = fun() ->
|
|
||||||
odbc_queries:update_t(
|
|
||||||
<<"motd">>,
|
|
||||||
[<<"username">>, <<"xml">>],
|
|
||||||
[Username, <<"">>],
|
|
||||||
[<<"username='">>, Username, <<"'">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end;
|
|
||||||
send_motd(_, odbc) ->
|
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
get_stored_motd(LServer) ->
|
get_stored_motd(LServer) ->
|
||||||
case get_stored_motd_packet(LServer, gen_mod:db_type(LServer, ?MODULE)) of
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
case Mod:get_motd(LServer) of
|
||||||
{ok, Packet} ->
|
{ok, Packet} ->
|
||||||
{fxml:get_subtag_cdata(Packet, <<"subject">>),
|
{fxml:get_subtag_cdata(Packet, <<"subject">>),
|
||||||
fxml:get_subtag_cdata(Packet, <<"body">>)};
|
fxml:get_subtag_cdata(Packet, <<"body">>)};
|
||||||
|
@ -1015,34 +874,6 @@ get_stored_motd(LServer) ->
|
||||||
{<<>>, <<>>}
|
{<<>>, <<>>}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_stored_motd_packet(LServer, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read({motd, LServer}) of
|
|
||||||
[#motd{packet = Packet}] ->
|
|
||||||
{ok, Packet};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
get_stored_motd_packet(LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(motd, motd_schema(), LServer) of
|
|
||||||
{ok, #motd{packet = Packet}} ->
|
|
||||||
{ok, Packet};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
get_stored_motd_packet(LServer, odbc) ->
|
|
||||||
case catch ejabberd_odbc:sql_query(
|
|
||||||
LServer, [<<"select xml from motd where username='';">>]) of
|
|
||||||
{selected, [<<"xml">>], [[XML]]} ->
|
|
||||||
case fxml_stream:parse_element(XML) of
|
|
||||||
{error, _} ->
|
|
||||||
error;
|
|
||||||
Packet ->
|
|
||||||
{ok, Packet}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% This function is similar to others, but doesn't perform any ACL verification
|
%% This function is similar to others, but doesn't perform any ACL verification
|
||||||
send_announcement_to_all(Host, SubjectS, BodyS) ->
|
send_announcement_to_all(Host, SubjectS, BodyS) ->
|
||||||
SubjectEls = if SubjectS /= <<>> ->
|
SubjectEls = if SubjectS /= <<>> ->
|
||||||
|
@ -1076,96 +907,17 @@ get_access(Host) ->
|
||||||
none).
|
none).
|
||||||
|
|
||||||
%%-------------------------------------------------------------------------
|
%%-------------------------------------------------------------------------
|
||||||
|
export(LServer) ->
|
||||||
update_tables() ->
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
update_motd_table(),
|
Mod:export(LServer).
|
||||||
update_motd_users_table().
|
|
||||||
|
|
||||||
update_motd_table() ->
|
|
||||||
Fields = record_info(fields, motd),
|
|
||||||
case mnesia:table_info(motd, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
motd, Fields, set,
|
|
||||||
fun(#motd{server = S}) -> S end,
|
|
||||||
fun(#motd{server = S, packet = P} = R) ->
|
|
||||||
NewS = iolist_to_binary(S),
|
|
||||||
NewP = fxml:to_xmlel(P),
|
|
||||||
R#motd{server = NewS, packet = NewP}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating motd table", []),
|
|
||||||
mnesia:transform_table(motd, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
|
|
||||||
update_motd_users_table() ->
|
|
||||||
Fields = record_info(fields, motd_users),
|
|
||||||
case mnesia:table_info(motd_users, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
motd_users, Fields, set,
|
|
||||||
fun(#motd_users{us = {U, _}}) -> U end,
|
|
||||||
fun(#motd_users{us = {U, S}} = R) ->
|
|
||||||
NewUS = {iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)},
|
|
||||||
R#motd_users{us = NewUS}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating motd_users table", []),
|
|
||||||
mnesia:transform_table(motd_users, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
motd_schema() ->
|
|
||||||
{record_info(fields, motd), #motd{}}.
|
|
||||||
|
|
||||||
motd_users_schema() ->
|
|
||||||
{record_info(fields, motd_users), #motd_users{}}.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{motd,
|
|
||||||
fun(Host, #motd{server = LServer, packet = El})
|
|
||||||
when LServer == Host ->
|
|
||||||
[[<<"delete from motd where username='';">>],
|
|
||||||
[<<"insert into motd(username, xml) values ('', '">>,
|
|
||||||
ejabberd_odbc:escape(fxml:element_to_binary(El)),
|
|
||||||
<<"');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end},
|
|
||||||
{motd_users,
|
|
||||||
fun(Host, #motd_users{us = {LUser, LServer}})
|
|
||||||
when LServer == Host, LUser /= <<"">> ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
[[<<"delete from motd where username='">>, Username, <<"';">>],
|
|
||||||
[<<"insert into motd(username, xml) values ('">>,
|
|
||||||
Username, <<"', '');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select xml from motd where username='';">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([XML]) ->
|
Mod:import(LServer).
|
||||||
El = fxml_stream:parse_element(XML),
|
|
||||||
#motd{server = LServer, packet = El}
|
|
||||||
end},
|
|
||||||
{<<"select username from motd where xml='';">>,
|
|
||||||
fun([LUser]) ->
|
|
||||||
#motd_users{us = {LUser, LServer}}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #motd{} = Motd) ->
|
import(LServer, DBType, LA) ->
|
||||||
mnesia:dirty_write(Motd);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
import(_LServer, mnesia, #motd_users{} = Users) ->
|
Mod:import(LServer, LA).
|
||||||
mnesia:dirty_write(Users);
|
|
||||||
import(_LServer, riak, #motd{} = Motd) ->
|
|
||||||
ejabberd_riak:put(Motd, motd_schema());
|
|
||||||
import(_LServer, riak, #motd_users{us = {_, S}} = Users) ->
|
|
||||||
ejabberd_riak:put(Users, motd_users_schema(),
|
|
||||||
[{'2i', [{<<"server">>, S}]}]);
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(access) ->
|
mod_opt_type(access) ->
|
||||||
fun (A) when is_atom(A) -> A end;
|
fun (A) when is_atom(A) -> A end;
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_announce_mnesia).
|
||||||
|
-behaviour(mod_announce).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
|
||||||
|
get_motd/1, is_motd_user/2, set_motd_user/2, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_announce.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(motd,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, motd)}]),
|
||||||
|
mnesia:create_table(motd_users,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, motd_users)}]),
|
||||||
|
update_tables().
|
||||||
|
|
||||||
|
set_motd_users(_LServer, USRs) ->
|
||||||
|
F = fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({U, S, _R}) ->
|
||||||
|
mnesia:write(#motd_users{us = {U, S}})
|
||||||
|
end, USRs)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
set_motd(LServer, Packet) ->
|
||||||
|
F = fun() ->
|
||||||
|
mnesia:write(#motd{server = LServer, packet = Packet})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
delete_motd(LServer) ->
|
||||||
|
F = fun() ->
|
||||||
|
mnesia:delete({motd, LServer}),
|
||||||
|
mnesia:write_lock_table(motd_users),
|
||||||
|
Users = mnesia:select(
|
||||||
|
motd_users,
|
||||||
|
[{#motd_users{us = '$1', _ = '_'},
|
||||||
|
[{'==', {element, 2, '$1'}, LServer}],
|
||||||
|
['$1']}]),
|
||||||
|
lists:foreach(fun(US) ->
|
||||||
|
mnesia:delete({motd_users, US})
|
||||||
|
end, Users)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
get_motd(LServer) ->
|
||||||
|
case mnesia:dirty_read({motd, LServer}) of
|
||||||
|
[#motd{packet = Packet}] ->
|
||||||
|
{ok, Packet};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_motd_user(LUser, LServer) ->
|
||||||
|
case mnesia:dirty_read({motd_users, {LUser, LServer}}) of
|
||||||
|
[#motd_users{}] -> true;
|
||||||
|
_ -> false
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_motd_user(LUser, LServer) ->
|
||||||
|
F = fun() ->
|
||||||
|
mnesia:write(#motd_users{us = {LUser, LServer}})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #motd{} = Motd) ->
|
||||||
|
mnesia:dirty_write(Motd);
|
||||||
|
import(_LServer, #motd_users{} = Users) ->
|
||||||
|
mnesia:dirty_write(Users).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_tables() ->
|
||||||
|
update_motd_table(),
|
||||||
|
update_motd_users_table().
|
||||||
|
|
||||||
|
update_motd_table() ->
|
||||||
|
Fields = record_info(fields, motd),
|
||||||
|
case mnesia:table_info(motd, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
motd, Fields, set,
|
||||||
|
fun(#motd{server = S}) -> S end,
|
||||||
|
fun(#motd{server = S, packet = P} = R) ->
|
||||||
|
NewS = iolist_to_binary(S),
|
||||||
|
NewP = fxml:to_xmlel(P),
|
||||||
|
R#motd{server = NewS, packet = NewP}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating motd table", []),
|
||||||
|
mnesia:transform_table(motd, ignore, Fields)
|
||||||
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
update_motd_users_table() ->
|
||||||
|
Fields = record_info(fields, motd_users),
|
||||||
|
case mnesia:table_info(motd_users, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
motd_users, Fields, set,
|
||||||
|
fun(#motd_users{us = {U, _}}) -> U end,
|
||||||
|
fun(#motd_users{us = {U, S}} = R) ->
|
||||||
|
NewUS = {iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
R#motd_users{us = NewUS}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating motd_users table", []),
|
||||||
|
mnesia:transform_table(motd_users, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,87 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_announce_riak).
|
||||||
|
-behaviour(mod_announce).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
|
||||||
|
get_motd/1, is_motd_user/2, set_motd_user/2, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_announce.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
set_motd_users(_LServer, USRs) ->
|
||||||
|
try
|
||||||
|
lists:foreach(
|
||||||
|
fun({U, S, _R}) ->
|
||||||
|
ok = ejabberd_riak:put(#motd_users{us = {U, S}},
|
||||||
|
motd_users_schema(),
|
||||||
|
[{'2i', [{<<"server">>, S}]}])
|
||||||
|
end, USRs),
|
||||||
|
{atomic, ok}
|
||||||
|
catch _:{badmatch, Err} ->
|
||||||
|
{atomic, Err}
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_motd(LServer, Packet) ->
|
||||||
|
{atomic, ejabberd_riak:put(#motd{server = LServer,
|
||||||
|
packet = Packet},
|
||||||
|
motd_schema())}.
|
||||||
|
|
||||||
|
delete_motd(LServer) ->
|
||||||
|
try
|
||||||
|
ok = ejabberd_riak:delete(motd, LServer),
|
||||||
|
ok = ejabberd_riak:delete_by_index(motd_users,
|
||||||
|
<<"server">>,
|
||||||
|
LServer),
|
||||||
|
{atomic, ok}
|
||||||
|
catch _:{badmatch, Err} ->
|
||||||
|
{atomic, Err}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_motd(LServer) ->
|
||||||
|
case ejabberd_riak:get(motd, motd_schema(), LServer) of
|
||||||
|
{ok, #motd{packet = Packet}} ->
|
||||||
|
{ok, Packet};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_motd_user(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(motd_users, motd_users_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{ok, #motd_users{}} -> true;
|
||||||
|
_ -> false
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_motd_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:put(
|
||||||
|
#motd_users{us = {LUser, LServer}}, motd_users_schema(),
|
||||||
|
[{'2i', [{<<"server">>, LServer}]}])}.
|
||||||
|
|
||||||
|
import(_LServer, #motd{} = Motd) ->
|
||||||
|
ejabberd_riak:put(Motd, motd_schema());
|
||||||
|
import(_LServer, #motd_users{us = {_, S}} = Users) ->
|
||||||
|
ejabberd_riak:put(Users, motd_users_schema(),
|
||||||
|
[{'2i', [{<<"server">>, S}]}]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
motd_schema() ->
|
||||||
|
{record_info(fields, motd), #motd{}}.
|
||||||
|
|
||||||
|
motd_users_schema() ->
|
||||||
|
{record_info(fields, motd_users), #motd_users{}}.
|
|
@ -0,0 +1,132 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_announce_sql).
|
||||||
|
-behaviour(mod_announce).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, set_motd_users/2, set_motd/2, delete_motd/1,
|
||||||
|
get_motd/1, is_motd_user/2, set_motd_user/2, import/1,
|
||||||
|
import/2, export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_announce.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
set_motd_users(LServer, USRs) ->
|
||||||
|
F = fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({U, _S, _R}) ->
|
||||||
|
Username = ejabberd_odbc:escape(U),
|
||||||
|
odbc_queries:update_t(
|
||||||
|
<<"motd">>,
|
||||||
|
[<<"username">>, <<"xml">>],
|
||||||
|
[Username, <<"">>],
|
||||||
|
[<<"username='">>, Username, <<"'">>])
|
||||||
|
end, USRs)
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
set_motd(LServer, Packet) ->
|
||||||
|
XML = ejabberd_odbc:escape(fxml:element_to_binary(Packet)),
|
||||||
|
F = fun() ->
|
||||||
|
odbc_queries:update_t(
|
||||||
|
<<"motd">>,
|
||||||
|
[<<"username">>, <<"xml">>],
|
||||||
|
[<<"">>, XML],
|
||||||
|
[<<"username=''">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
delete_motd(LServer) ->
|
||||||
|
F = fun() ->
|
||||||
|
ejabberd_odbc:sql_query_t([<<"delete from motd;">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
get_motd(LServer) ->
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer, [<<"select xml from motd where username='';">>]) of
|
||||||
|
{selected, [<<"xml">>], [[XML]]} ->
|
||||||
|
case fxml_stream:parse_element(XML) of
|
||||||
|
{error, _} ->
|
||||||
|
error;
|
||||||
|
Packet ->
|
||||||
|
{ok, Packet}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
is_motd_user(LUser, LServer) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"select username from motd "
|
||||||
|
"where username='">>, Username, <<"';">>]) of
|
||||||
|
{selected, [<<"username">>], [_|_]} ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_motd_user(LUser, LServer) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
F = fun() ->
|
||||||
|
odbc_queries:update_t(
|
||||||
|
<<"motd">>,
|
||||||
|
[<<"username">>, <<"xml">>],
|
||||||
|
[Username, <<"">>],
|
||||||
|
[<<"username='">>, Username, <<"'">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{motd,
|
||||||
|
fun(Host, #motd{server = LServer, packet = El})
|
||||||
|
when LServer == Host ->
|
||||||
|
[[<<"delete from motd where username='';">>],
|
||||||
|
[<<"insert into motd(username, xml) values ('', '">>,
|
||||||
|
ejabberd_odbc:escape(fxml:element_to_binary(El)),
|
||||||
|
<<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end},
|
||||||
|
{motd_users,
|
||||||
|
fun(Host, #motd_users{us = {LUser, LServer}})
|
||||||
|
when LServer == Host, LUser /= <<"">> ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
[[<<"delete from motd where username='">>, Username, <<"';">>],
|
||||||
|
[<<"insert into motd(username, xml) values ('">>,
|
||||||
|
Username, <<"', '');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select xml from motd where username='';">>,
|
||||||
|
fun([XML]) ->
|
||||||
|
El = fxml_stream:parse_element(XML),
|
||||||
|
#motd{server = LServer, packet = El}
|
||||||
|
end},
|
||||||
|
{<<"select username from motd where xml='';">>,
|
||||||
|
fun([LUser]) ->
|
||||||
|
#motd_users{us = {LUser, LServer}}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
|
@ -39,6 +39,10 @@
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
|
-callback process_blocklist_block(binary(), binary(), function()) -> {atomic, any()}.
|
||||||
|
-callback unblock_by_filter(binary(), binary(), function()) -> {atomic, any()}.
|
||||||
|
-callback process_blocklist_get(binary(), binary()) -> [listitem()] | error.
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
one_queue),
|
one_queue),
|
||||||
|
@ -147,9 +151,8 @@ process_blocklist_block(LUser, LServer, JIDs, Lang) ->
|
||||||
end,
|
end,
|
||||||
List, JIDs)
|
List, JIDs)
|
||||||
end,
|
end,
|
||||||
case process_blocklist_block_db(LUser, LServer, Filter,
|
Mod = db_mod(LServer),
|
||||||
gen_mod:db_type(LServer, mod_privacy))
|
case Mod:process_blocklist_block(LUser, LServer, Filter) of
|
||||||
of
|
|
||||||
{atomic, {ok, Default, List}} ->
|
{atomic, {ok, Default, List}} ->
|
||||||
UserList = make_userlist(Default, List),
|
UserList = make_userlist(Default, List),
|
||||||
broadcast_list_update(LUser, LServer, Default,
|
broadcast_list_update(LUser, LServer, Default,
|
||||||
|
@ -162,102 +165,14 @@ process_blocklist_block(LUser, LServer, JIDs, Lang) ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_blocklist_block_db(LUser, LServer, Filter,
|
|
||||||
mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
|
||||||
[] ->
|
|
||||||
P = #privacy{us = {LUser, LServer}},
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = [],
|
|
||||||
List = [];
|
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewDefault = Default,
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists);
|
|
||||||
false ->
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = Lists,
|
|
||||||
List = []
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists = [{NewDefault, NewList} | NewLists1],
|
|
||||||
mnesia:write(P#privacy{default = NewDefault,
|
|
||||||
lists = NewLists}),
|
|
||||||
{ok, NewDefault, NewList}
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
process_blocklist_block_db(LUser, LServer, Filter,
|
|
||||||
riak) ->
|
|
||||||
{atomic,
|
|
||||||
begin
|
|
||||||
case ejabberd_riak:get(privacy, mod_privacy:privacy_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewDefault = Default,
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists);
|
|
||||||
false ->
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = Lists,
|
|
||||||
List = []
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
P = #privacy{us = {LUser, LServer}},
|
|
||||||
NewDefault = <<"Blocked contacts">>,
|
|
||||||
NewLists1 = [],
|
|
||||||
List = []
|
|
||||||
end,
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists = [{NewDefault, NewList} | NewLists1],
|
|
||||||
case ejabberd_riak:put(P#privacy{default = NewDefault,
|
|
||||||
lists = NewLists},
|
|
||||||
mod_privacy:privacy_schema()) of
|
|
||||||
ok ->
|
|
||||||
{ok, NewDefault, NewList};
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end
|
|
||||||
end};
|
|
||||||
process_blocklist_block_db(LUser, LServer, Filter, odbc) ->
|
|
||||||
F = fun () ->
|
|
||||||
Default = case
|
|
||||||
mod_privacy:sql_get_default_privacy_list_t(LUser)
|
|
||||||
of
|
|
||||||
{selected, []} ->
|
|
||||||
Name = <<"Blocked contacts">>,
|
|
||||||
mod_privacy:sql_add_privacy_list(LUser, Name),
|
|
||||||
mod_privacy:sql_set_default_privacy_list(LUser,
|
|
||||||
Name),
|
|
||||||
Name;
|
|
||||||
{selected, [{Name}]} -> Name
|
|
||||||
end,
|
|
||||||
{selected, [{ID}]} =
|
|
||||||
mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
|
|
||||||
case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
|
|
||||||
{selected, RItems = [_ | _]} ->
|
|
||||||
List = lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
|
|
||||||
_ -> List = []
|
|
||||||
end,
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewRItems = lists:map(fun mod_privacy:item_to_raw/1,
|
|
||||||
NewList),
|
|
||||||
mod_privacy:sql_set_privacy_list(ID, NewRItems),
|
|
||||||
{ok, Default, NewList}
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
process_blocklist_unblock_all(LUser, LServer, Lang) ->
|
process_blocklist_unblock_all(LUser, LServer, Lang) ->
|
||||||
Filter = fun (List) ->
|
Filter = fun (List) ->
|
||||||
lists:filter(fun (#listitem{action = A}) -> A =/= deny
|
lists:filter(fun (#listitem{action = A}) -> A =/= deny
|
||||||
end,
|
end,
|
||||||
List)
|
List)
|
||||||
end,
|
end,
|
||||||
DBType = gen_mod:db_type(LServer, mod_privacy),
|
Mod = db_mod(LServer),
|
||||||
case unblock_by_filter(LUser, LServer, Filter, DBType) of
|
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
||||||
{atomic, ok} -> {result, []};
|
{atomic, ok} -> {result, []};
|
||||||
{atomic, {ok, Default, List}} ->
|
{atomic, {ok, Default, List}} ->
|
||||||
UserList = make_userlist(Default, List),
|
UserList = make_userlist(Default, List),
|
||||||
|
@ -279,8 +194,8 @@ process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
|
||||||
end,
|
end,
|
||||||
List)
|
List)
|
||||||
end,
|
end,
|
||||||
DBType = gen_mod:db_type(LServer, mod_privacy),
|
Mod = db_mod(LServer),
|
||||||
case unblock_by_filter(LUser, LServer, Filter, DBType) of
|
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
||||||
{atomic, ok} -> {result, []};
|
{atomic, ok} -> {result, []};
|
||||||
{atomic, {ok, Default, List}} ->
|
{atomic, {ok, Default, List}} ->
|
||||||
UserList = make_userlist(Default, List),
|
UserList = make_userlist(Default, List),
|
||||||
|
@ -294,76 +209,6 @@ process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
unblock_by_filter(LUser, LServer, Filter, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
|
||||||
[] ->
|
|
||||||
% No lists, nothing to unblock
|
|
||||||
ok;
|
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists),
|
|
||||||
NewLists = [{Default, NewList} | NewLists1],
|
|
||||||
mnesia:write(P#privacy{lists = NewLists}),
|
|
||||||
{ok, Default, NewList};
|
|
||||||
false ->
|
|
||||||
% No default list, nothing to unblock
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
unblock_by_filter(LUser, LServer, Filter, riak) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, mod_privacy:privacy_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{error, _} ->
|
|
||||||
%% No lists, nothing to unblock
|
|
||||||
ok;
|
|
||||||
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} ->
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewLists1 = lists:keydelete(Default, 1, Lists),
|
|
||||||
NewLists = [{Default, NewList} | NewLists1],
|
|
||||||
case ejabberd_riak:put(P#privacy{lists = NewLists},
|
|
||||||
mod_privacy:privacy_schema()) of
|
|
||||||
ok ->
|
|
||||||
{ok, Default, NewList};
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end;
|
|
||||||
false ->
|
|
||||||
%% No default list, nothing to unblock
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end};
|
|
||||||
unblock_by_filter(LUser, LServer, Filter, odbc) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mod_privacy:sql_get_default_privacy_list_t(LUser)
|
|
||||||
of
|
|
||||||
{selected, []} -> ok;
|
|
||||||
{selected, [{Default}]} ->
|
|
||||||
{selected, [{ID}]} =
|
|
||||||
mod_privacy:sql_get_privacy_list_id_t(LUser, Default),
|
|
||||||
case mod_privacy:sql_get_privacy_list_data_by_id_t(ID) of
|
|
||||||
{selected, RItems = [_ | _]} ->
|
|
||||||
List = lists:flatmap(fun mod_privacy:raw_to_item/1,
|
|
||||||
RItems),
|
|
||||||
NewList = Filter(List),
|
|
||||||
NewRItems = lists:map(fun mod_privacy:item_to_raw/1,
|
|
||||||
NewList),
|
|
||||||
mod_privacy:sql_set_privacy_list(ID, NewRItems),
|
|
||||||
{ok, Default, NewList};
|
|
||||||
_ -> ok
|
|
||||||
end;
|
|
||||||
_ -> ok
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
make_userlist(Name, List) ->
|
make_userlist(Name, List) ->
|
||||||
NeedDb = mod_privacy:is_list_needdb(List),
|
NeedDb = mod_privacy:is_list_needdb(List),
|
||||||
#userlist{name = Name, list = List, needdb = NeedDb}.
|
#userlist{name = Name, list = List, needdb = NeedDb}.
|
||||||
|
@ -380,9 +225,8 @@ broadcast_blocklist_event(LUser, LServer, Event) ->
|
||||||
{broadcast, {blocking, Event}}).
|
{broadcast, {blocking, Event}}).
|
||||||
|
|
||||||
process_blocklist_get(LUser, LServer, Lang) ->
|
process_blocklist_get(LUser, LServer, Lang) ->
|
||||||
case process_blocklist_get_db(LUser, LServer,
|
Mod = db_mod(LServer),
|
||||||
gen_mod:db_type(LServer, mod_privacy))
|
case Mod:process_blocklist_get(LUser, LServer) of
|
||||||
of
|
|
||||||
error ->
|
error ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||||
List ->
|
List ->
|
||||||
|
@ -402,45 +246,9 @@ process_blocklist_get(LUser, LServer, Lang) ->
|
||||||
children = Items}]}
|
children = Items}]}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_blocklist_get_db(LUser, LServer, mnesia) ->
|
db_mod(LServer) ->
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
DBType = gen_mod:db_type(LServer, mod_privacy),
|
||||||
of
|
gen_mod:db_mod(DBType, ?MODULE).
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> [];
|
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> []
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
process_blocklist_get_db(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(privacy, mod_privacy:privacy_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists}} ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> []
|
|
||||||
end;
|
|
||||||
{error, notfound} ->
|
|
||||||
[];
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
process_blocklist_get_db(LUser, LServer, odbc) ->
|
|
||||||
case catch
|
|
||||||
mod_privacy:sql_get_default_privacy_list(LUser, LServer)
|
|
||||||
of
|
|
||||||
{selected, []} -> [];
|
|
||||||
{selected, [{Default}]} ->
|
|
||||||
case catch mod_privacy:sql_get_privacy_list_data(LUser,
|
|
||||||
LServer, Default)
|
|
||||||
of
|
|
||||||
{selected, RItems} ->
|
|
||||||
lists:flatmap(fun mod_privacy:raw_to_item/1, RItems);
|
|
||||||
{'EXIT', _} -> error
|
|
||||||
end;
|
|
||||||
{'EXIT', _} -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||||
mod_opt_type(_) -> [iqdisc].
|
mod_opt_type(_) -> [iqdisc].
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_blocking_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_blocking).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([process_blocklist_block/3, unblock_by_filter/3,
|
||||||
|
process_blocklist_get/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
process_blocklist_block(LUser, LServer, Filter) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mnesia:wread({privacy, {LUser, LServer}}) of
|
||||||
|
[] ->
|
||||||
|
P = #privacy{us = {LUser, LServer}},
|
||||||
|
NewDefault = <<"Blocked contacts">>,
|
||||||
|
NewLists1 = [],
|
||||||
|
List = [];
|
||||||
|
[#privacy{default = Default, lists = Lists} = P] ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
NewDefault = Default,
|
||||||
|
NewLists1 = lists:keydelete(Default, 1, Lists);
|
||||||
|
false ->
|
||||||
|
NewDefault = <<"Blocked contacts">>,
|
||||||
|
NewLists1 = Lists,
|
||||||
|
List = []
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewLists = [{NewDefault, NewList} | NewLists1],
|
||||||
|
mnesia:write(P#privacy{default = NewDefault,
|
||||||
|
lists = NewLists}),
|
||||||
|
{ok, NewDefault, NewList}
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
unblock_by_filter(LUser, LServer, Filter) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
|
[] ->
|
||||||
|
%% No lists, nothing to unblock
|
||||||
|
ok;
|
||||||
|
[#privacy{default = Default, lists = Lists} = P] ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewLists1 = lists:keydelete(Default, 1, Lists),
|
||||||
|
NewLists = [{Default, NewList} | NewLists1],
|
||||||
|
mnesia:write(P#privacy{lists = NewLists}),
|
||||||
|
{ok, Default, NewList};
|
||||||
|
false ->
|
||||||
|
%% No default list, nothing to unblock
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
process_blocklist_get(LUser, LServer) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
{'EXIT', _Reason} -> error;
|
||||||
|
[] -> [];
|
||||||
|
[#privacy{default = Default, lists = Lists}] ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} -> List;
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
|
@ -0,0 +1,98 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_blocking_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_blocking).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([process_blocklist_block/3, unblock_by_filter/3,
|
||||||
|
process_blocklist_get/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
process_blocklist_block(LUser, LServer, Filter) ->
|
||||||
|
{atomic,
|
||||||
|
begin
|
||||||
|
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
NewDefault = Default,
|
||||||
|
NewLists1 = lists:keydelete(Default, 1, Lists);
|
||||||
|
false ->
|
||||||
|
NewDefault = <<"Blocked contacts">>,
|
||||||
|
NewLists1 = Lists,
|
||||||
|
List = []
|
||||||
|
end;
|
||||||
|
{error, _} ->
|
||||||
|
P = #privacy{us = {LUser, LServer}},
|
||||||
|
NewDefault = <<"Blocked contacts">>,
|
||||||
|
NewLists1 = [],
|
||||||
|
List = []
|
||||||
|
end,
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewLists = [{NewDefault, NewList} | NewLists1],
|
||||||
|
case ejabberd_riak:put(P#privacy{default = NewDefault,
|
||||||
|
lists = NewLists},
|
||||||
|
mod_privacy_riak:privacy_schema()) of
|
||||||
|
ok ->
|
||||||
|
{ok, NewDefault, NewList};
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end
|
||||||
|
end}.
|
||||||
|
|
||||||
|
unblock_by_filter(LUser, LServer, Filter) ->
|
||||||
|
{atomic,
|
||||||
|
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{error, _} ->
|
||||||
|
%% No lists, nothing to unblock
|
||||||
|
ok;
|
||||||
|
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} ->
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewLists1 = lists:keydelete(Default, 1, Lists),
|
||||||
|
NewLists = [{Default, NewList} | NewLists1],
|
||||||
|
case ejabberd_riak:put(P#privacy{lists = NewLists},
|
||||||
|
mod_privacy_riak:privacy_schema()) of
|
||||||
|
ok ->
|
||||||
|
{ok, Default, NewList};
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end;
|
||||||
|
false ->
|
||||||
|
%% No default list, nothing to unblock
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
end}.
|
||||||
|
|
||||||
|
process_blocklist_get(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(privacy, mod_privacy_riak:privacy_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{ok, #privacy{default = Default, lists = Lists}} ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} -> List;
|
||||||
|
_ -> []
|
||||||
|
end;
|
||||||
|
{error, notfound} ->
|
||||||
|
[];
|
||||||
|
{error, _} ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
|
@ -0,0 +1,87 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_blocking_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_blocking).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([process_blocklist_block/3, unblock_by_filter/3,
|
||||||
|
process_blocklist_get/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
process_blocklist_block(LUser, LServer, Filter) ->
|
||||||
|
F = fun () ->
|
||||||
|
Default = case mod_privacy_sql:sql_get_default_privacy_list_t(LUser) of
|
||||||
|
{selected, []} ->
|
||||||
|
Name = <<"Blocked contacts">>,
|
||||||
|
mod_privacy_sql:sql_add_privacy_list(LUser, Name),
|
||||||
|
mod_privacy_sql:sql_set_default_privacy_list(LUser, Name),
|
||||||
|
Name;
|
||||||
|
{selected, [{Name}]} -> Name
|
||||||
|
end,
|
||||||
|
{selected, [{ID}]} =
|
||||||
|
mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Default),
|
||||||
|
case mod_privacy_sql:sql_get_privacy_list_data_by_id_t(ID) of
|
||||||
|
{selected, RItems = [_ | _]} ->
|
||||||
|
List = lists:flatmap(fun mod_privacy_sql:raw_to_item/1, RItems);
|
||||||
|
_ ->
|
||||||
|
List = []
|
||||||
|
end,
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewRItems = lists:map(fun mod_privacy_sql:item_to_raw/1,
|
||||||
|
NewList),
|
||||||
|
mod_privacy_sql:sql_set_privacy_list(ID, NewRItems),
|
||||||
|
{ok, Default, NewList}
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
unblock_by_filter(LUser, LServer, Filter) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mod_privacy_sql:sql_get_default_privacy_list_t(LUser) of
|
||||||
|
{selected, []} -> ok;
|
||||||
|
{selected, [{Default}]} ->
|
||||||
|
{selected, [{ID}]} =
|
||||||
|
mod_privacy_sql:sql_get_privacy_list_id_t(LUser, Default),
|
||||||
|
case mod_privacy_sql:sql_get_privacy_list_data_by_id_t(ID) of
|
||||||
|
{selected, RItems = [_ | _]} ->
|
||||||
|
List = lists:flatmap(fun mod_privacy_sql:raw_to_item/1,
|
||||||
|
RItems),
|
||||||
|
NewList = Filter(List),
|
||||||
|
NewRItems = lists:map(fun mod_privacy_sql:item_to_raw/1,
|
||||||
|
NewList),
|
||||||
|
mod_privacy_sql:sql_set_privacy_list(ID, NewRItems),
|
||||||
|
{ok, Default, NewList};
|
||||||
|
_ -> ok
|
||||||
|
end;
|
||||||
|
_ -> ok
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
process_blocklist_get(LUser, LServer) ->
|
||||||
|
case catch mod_privacy_sql:sql_get_default_privacy_list(LUser, LServer) of
|
||||||
|
{selected, []} -> [];
|
||||||
|
{selected, [{Default}]} ->
|
||||||
|
case catch mod_privacy_sql:sql_get_privacy_list_data(
|
||||||
|
LUser, LServer, Default) of
|
||||||
|
{selected, RItems} ->
|
||||||
|
lists:flatmap(fun mod_privacy_sql:raw_to_item/1, RItems);
|
||||||
|
{'EXIT', _} -> error
|
||||||
|
end;
|
||||||
|
{'EXIT', _} -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
140
src/mod_caps.erl
140
src/mod_caps.erl
|
@ -80,6 +80,12 @@
|
||||||
|
|
||||||
-record(state, {host = <<"">> :: binary()}).
|
-record(state, {host = <<"">> :: binary()}).
|
||||||
|
|
||||||
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
|
-callback caps_read(binary(), {binary(), binary()}) ->
|
||||||
|
{ok, non_neg_integer() | [binary()]} | error.
|
||||||
|
-callback caps_write(binary(), {binary(), binary()},
|
||||||
|
non_neg_integer() | [binary()]) -> any().
|
||||||
|
|
||||||
start_link(Host, Opts) ->
|
start_link(Host, Opts) ->
|
||||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||||
gen_server:start_link({local, Proc}, ?MODULE,
|
gen_server:start_link({local, Proc}, ?MODULE,
|
||||||
|
@ -300,28 +306,9 @@ c2s_broadcast_recipients(InAcc, Host, C2SState,
|
||||||
end;
|
end;
|
||||||
c2s_broadcast_recipients(Acc, _, _, _, _, _) -> Acc.
|
c2s_broadcast_recipients(Acc, _, _, _, _, _) -> Acc.
|
||||||
|
|
||||||
init_db(mnesia, _Host) ->
|
|
||||||
case catch mnesia:table_info(caps_features, storage_type) of
|
|
||||||
{'EXIT', _} ->
|
|
||||||
ok;
|
|
||||||
disc_only_copies ->
|
|
||||||
ok;
|
|
||||||
_ ->
|
|
||||||
mnesia:delete_table(caps_features)
|
|
||||||
end,
|
|
||||||
mnesia:create_table(caps_features,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{local_content, true},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, caps_features)}]),
|
|
||||||
update_table(),
|
|
||||||
mnesia:add_table_copy(caps_features, node(),
|
|
||||||
disc_only_copies);
|
|
||||||
init_db(_, _) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
init_db(gen_mod:db_type(Host, Opts), Host),
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
|
Mod:init(Host, Opts),
|
||||||
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
fun(I) when is_integer(I), I>0 -> I end,
|
||||||
1000),
|
1000),
|
||||||
|
@ -450,65 +437,13 @@ feature_response(_IQResult, Host, From, Caps,
|
||||||
|
|
||||||
caps_read_fun(Host, Node) ->
|
caps_read_fun(Host, Node) ->
|
||||||
LServer = jid:nameprep(Host),
|
LServer = jid:nameprep(Host),
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
caps_read_fun(LServer, Node, DBType).
|
fun() -> Mod:caps_read(LServer, Node) end.
|
||||||
|
|
||||||
caps_read_fun(_LServer, Node, mnesia) ->
|
|
||||||
fun () ->
|
|
||||||
case mnesia:dirty_read({caps_features, Node}) of
|
|
||||||
[#caps_features{features = Features}] -> {ok, Features};
|
|
||||||
_ -> error
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
caps_read_fun(_LServer, Node, riak) ->
|
|
||||||
fun() ->
|
|
||||||
case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of
|
|
||||||
{ok, #caps_features{features = Features}} -> {ok, Features};
|
|
||||||
_ -> error
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
caps_read_fun(LServer, {Node, SubNode}, odbc) ->
|
|
||||||
fun() ->
|
|
||||||
SNode = ejabberd_odbc:escape(Node),
|
|
||||||
SSubNode = ejabberd_odbc:escape(SubNode),
|
|
||||||
case ejabberd_odbc:sql_query(
|
|
||||||
LServer, [<<"select feature from caps_features where ">>,
|
|
||||||
<<"node='">>, SNode, <<"' and subnode='">>,
|
|
||||||
SSubNode, <<"';">>]) of
|
|
||||||
{selected, [<<"feature">>], [[H]|_] = Fs} ->
|
|
||||||
case catch jlib:binary_to_integer(H) of
|
|
||||||
Int when is_integer(Int), Int>=0 ->
|
|
||||||
{ok, Int};
|
|
||||||
_ ->
|
|
||||||
{ok, lists:flatten(Fs)}
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end
|
|
||||||
end.
|
|
||||||
|
|
||||||
caps_write_fun(Host, Node, Features) ->
|
caps_write_fun(Host, Node, Features) ->
|
||||||
LServer = jid:nameprep(Host),
|
LServer = jid:nameprep(Host),
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
caps_write_fun(LServer, Node, Features, DBType).
|
fun() -> Mod:caps_write(LServer, Node, Features) end.
|
||||||
|
|
||||||
caps_write_fun(_LServer, Node, Features, mnesia) ->
|
|
||||||
fun () ->
|
|
||||||
mnesia:dirty_write(#caps_features{node_pair = Node,
|
|
||||||
features = Features})
|
|
||||||
end;
|
|
||||||
caps_write_fun(_LServer, Node, Features, riak) ->
|
|
||||||
fun () ->
|
|
||||||
ejabberd_riak:put(#caps_features{node_pair = Node,
|
|
||||||
features = Features},
|
|
||||||
caps_features_schema())
|
|
||||||
end;
|
|
||||||
caps_write_fun(LServer, NodePair, Features, odbc) ->
|
|
||||||
fun () ->
|
|
||||||
ejabberd_odbc:sql_transaction(
|
|
||||||
LServer,
|
|
||||||
sql_write_features_t(NodePair, Features))
|
|
||||||
end.
|
|
||||||
|
|
||||||
make_my_disco_hash(Host) ->
|
make_my_disco_hash(Host) ->
|
||||||
JID = jid:make(<<"">>, Host, <<"">>),
|
JID = jid:make(<<"">>, Host, <<"">>),
|
||||||
|
@ -658,61 +593,20 @@ is_valid_node(Node) ->
|
||||||
false
|
false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
update_table() ->
|
|
||||||
Fields = record_info(fields, caps_features),
|
|
||||||
case mnesia:table_info(caps_features, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
caps_features, Fields, set,
|
|
||||||
fun(#caps_features{node_pair = {N, _}}) -> N end,
|
|
||||||
fun(#caps_features{node_pair = {N, P},
|
|
||||||
features = Fs} = R) ->
|
|
||||||
NewFs = if is_integer(Fs) ->
|
|
||||||
Fs;
|
|
||||||
true ->
|
|
||||||
[iolist_to_binary(F) || F <- Fs]
|
|
||||||
end,
|
|
||||||
R#caps_features{node_pair = {iolist_to_binary(N),
|
|
||||||
iolist_to_binary(P)},
|
|
||||||
features = NewFs}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating caps_features table", []),
|
|
||||||
mnesia:transform_table(caps_features, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
sql_write_features_t({Node, SubNode}, Features) ->
|
|
||||||
SNode = ejabberd_odbc:escape(Node),
|
|
||||||
SSubNode = ejabberd_odbc:escape(SubNode),
|
|
||||||
NewFeatures = if is_integer(Features) ->
|
|
||||||
[jlib:integer_to_binary(Features)];
|
|
||||||
true ->
|
|
||||||
Features
|
|
||||||
end,
|
|
||||||
[[<<"delete from caps_features where node='">>,
|
|
||||||
SNode, <<"' and subnode='">>, SSubNode, <<"';">>]|
|
|
||||||
[[<<"insert into caps_features(node, subnode, feature) ">>,
|
|
||||||
<<"values ('">>, SNode, <<"', '">>, SSubNode, <<"', '">>,
|
|
||||||
ejabberd_odbc:escape(F), <<"');">>] || F <- NewFeatures]].
|
|
||||||
|
|
||||||
caps_features_schema() ->
|
caps_features_schema() ->
|
||||||
{record_info(fields, caps_features), #caps_features{}}.
|
{record_info(fields, caps_features), #caps_features{}}.
|
||||||
|
|
||||||
export(_Server) ->
|
export(LServer) ->
|
||||||
[{caps_features,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun(_Host, #caps_features{node_pair = NodePair,
|
Mod:export(LServer).
|
||||||
features = Features}) ->
|
|
||||||
sql_write_features_t(NodePair, Features);
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import_info() ->
|
import_info() ->
|
||||||
[{<<"caps_features">>, 4}].
|
[{<<"caps_features">>, 4}].
|
||||||
|
|
||||||
import_start(LServer, DBType) ->
|
import_start(LServer, DBType) ->
|
||||||
ets:new(caps_features_tmp, [private, named_table, bag]),
|
ets:new(caps_features_tmp, [private, named_table, bag]),
|
||||||
init_db(DBType, LServer),
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
|
Mod:init(LServer, []),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
import(_LServer, {odbc, _}, _DBType, <<"caps_features">>,
|
import(_LServer, {odbc, _}, _DBType, <<"caps_features">>,
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_caps_mnesia).
|
||||||
|
-behaviour(mod_caps).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, caps_read/2, caps_write/3]).
|
||||||
|
|
||||||
|
-include("mod_caps.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
case catch mnesia:table_info(caps_features, storage_type) of
|
||||||
|
{'EXIT', _} ->
|
||||||
|
ok;
|
||||||
|
disc_only_copies ->
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
mnesia:delete_table(caps_features)
|
||||||
|
end,
|
||||||
|
mnesia:create_table(caps_features,
|
||||||
|
[{disc_only_copies, [node()]},
|
||||||
|
{local_content, true},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, caps_features)}]),
|
||||||
|
update_table(),
|
||||||
|
mnesia:add_table_copy(caps_features, node(),
|
||||||
|
disc_only_copies).
|
||||||
|
|
||||||
|
caps_read(_LServer, Node) ->
|
||||||
|
case mnesia:dirty_read({caps_features, Node}) of
|
||||||
|
[#caps_features{features = Features}] -> {ok, Features};
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
caps_write(_LServer, Node, Features) ->
|
||||||
|
mnesia:dirty_write(#caps_features{node_pair = Node,
|
||||||
|
features = Features}).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, caps_features),
|
||||||
|
case mnesia:table_info(caps_features, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
caps_features, Fields, set,
|
||||||
|
fun(#caps_features{node_pair = {N, _}}) -> N end,
|
||||||
|
fun(#caps_features{node_pair = {N, P},
|
||||||
|
features = Fs} = R) ->
|
||||||
|
NewFs = if is_integer(Fs) ->
|
||||||
|
Fs;
|
||||||
|
true ->
|
||||||
|
[iolist_to_binary(F) || F <- Fs]
|
||||||
|
end,
|
||||||
|
R#caps_features{node_pair = {iolist_to_binary(N),
|
||||||
|
iolist_to_binary(P)},
|
||||||
|
features = NewFs}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating caps_features table", []),
|
||||||
|
mnesia:transform_table(caps_features, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,38 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_caps_riak).
|
||||||
|
-behaviour(mod_caps).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, caps_read/2, caps_write/3]).
|
||||||
|
|
||||||
|
-include("mod_caps.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
caps_read(_LServer, Node) ->
|
||||||
|
case ejabberd_riak:get(caps_features, caps_features_schema(), Node) of
|
||||||
|
{ok, #caps_features{features = Features}} -> {ok, Features};
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
caps_write(_LServer, Node, Features) ->
|
||||||
|
ejabberd_riak:put(#caps_features{node_pair = Node,
|
||||||
|
features = Features},
|
||||||
|
caps_features_schema()).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
caps_features_schema() ->
|
||||||
|
{record_info(fields, caps_features), #caps_features{}}.
|
|
@ -0,0 +1,71 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_caps_sql).
|
||||||
|
-behaviour(mod_caps).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, caps_read/2, caps_write/3, export/1]).
|
||||||
|
|
||||||
|
-include("mod_caps.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
caps_read(LServer, {Node, SubNode}) ->
|
||||||
|
SNode = ejabberd_odbc:escape(Node),
|
||||||
|
SSubNode = ejabberd_odbc:escape(SubNode),
|
||||||
|
case ejabberd_odbc:sql_query(
|
||||||
|
LServer, [<<"select feature from caps_features where ">>,
|
||||||
|
<<"node='">>, SNode, <<"' and subnode='">>,
|
||||||
|
SSubNode, <<"';">>]) of
|
||||||
|
{selected, [<<"feature">>], [[H]|_] = Fs} ->
|
||||||
|
case catch jlib:binary_to_integer(H) of
|
||||||
|
Int when is_integer(Int), Int>=0 ->
|
||||||
|
{ok, Int};
|
||||||
|
_ ->
|
||||||
|
{ok, lists:flatten(Fs)}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
caps_write(LServer, NodePair, Features) ->
|
||||||
|
ejabberd_odbc:sql_transaction(
|
||||||
|
LServer,
|
||||||
|
sql_write_features_t(NodePair, Features)).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{caps_features,
|
||||||
|
fun(_Host, #caps_features{node_pair = NodePair,
|
||||||
|
features = Features}) ->
|
||||||
|
sql_write_features_t(NodePair, Features);
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
sql_write_features_t({Node, SubNode}, Features) ->
|
||||||
|
SNode = ejabberd_odbc:escape(Node),
|
||||||
|
SSubNode = ejabberd_odbc:escape(SubNode),
|
||||||
|
NewFeatures = if is_integer(Features) ->
|
||||||
|
[jlib:integer_to_binary(Features)];
|
||||||
|
true ->
|
||||||
|
Features
|
||||||
|
end,
|
||||||
|
[[<<"delete from caps_features where node='">>,
|
||||||
|
SNode, <<"' and subnode='">>, SSubNode, <<"';">>]|
|
||||||
|
[[<<"insert into caps_features(node, subnode, feature) ">>,
|
||||||
|
<<"values ('">>, SNode, <<"', '">>, SSubNode, <<"', '">>,
|
||||||
|
ejabberd_odbc:escape(F), <<"');">>] || F <- NewFeatures]].
|
||||||
|
|
176
src/mod_irc.erl
176
src/mod_irc.erl
|
@ -33,7 +33,8 @@
|
||||||
|
|
||||||
%% API
|
%% API
|
||||||
-export([start_link/2, start/2, stop/1, export/1, import/1,
|
-export([start_link/2, start/2, stop/1, export/1, import/1,
|
||||||
import/3, closed_connection/3, get_connection_params/3]).
|
import/3, closed_connection/3, get_connection_params/3,
|
||||||
|
data_to_binary/2]).
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
handle_info/2, terminate/2, code_change/3,
|
handle_info/2, terminate/2, code_change/3,
|
||||||
|
@ -46,6 +47,8 @@
|
||||||
|
|
||||||
-include("adhoc.hrl").
|
-include("adhoc.hrl").
|
||||||
|
|
||||||
|
-include("mod_irc.hrl").
|
||||||
|
|
||||||
-define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>).
|
-define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>).
|
||||||
|
|
||||||
-define(DEFAULT_IRC_PORT, 6667).
|
-define(DEFAULT_IRC_PORT, 6667).
|
||||||
|
@ -58,27 +61,19 @@
|
||||||
[<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>,
|
[<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>,
|
||||||
<<"utf-8">>, <<"utf-8+latin-1">>]).
|
<<"utf-8">>, <<"utf-8+latin-1">>]).
|
||||||
|
|
||||||
-type conn_param() :: {binary(), binary(), inet:port_number(), binary()} |
|
|
||||||
{binary(), binary(), inet:port_number()} |
|
|
||||||
{binary(), binary()} |
|
|
||||||
{binary()}.
|
|
||||||
|
|
||||||
-record(irc_connection,
|
|
||||||
{jid_server_host = {#jid{}, <<"">>, <<"">>} :: {jid(), binary(), binary()},
|
|
||||||
pid = self() :: pid()}).
|
|
||||||
|
|
||||||
-record(irc_custom,
|
|
||||||
{us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()},
|
|
||||||
binary()},
|
|
||||||
data = [] :: [{username, binary()} |
|
|
||||||
{connections_params, [conn_param()]}]}).
|
|
||||||
|
|
||||||
-record(state, {host = <<"">> :: binary(),
|
-record(state, {host = <<"">> :: binary(),
|
||||||
server_host = <<"">> :: binary(),
|
server_host = <<"">> :: binary(),
|
||||||
access = all :: atom()}).
|
access = all :: atom()}).
|
||||||
|
|
||||||
-define(PROCNAME, ejabberd_mod_irc).
|
-define(PROCNAME, ejabberd_mod_irc).
|
||||||
|
|
||||||
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
|
-callback import(binary(), #irc_custom{}) -> ok | pass.
|
||||||
|
-callback get_data(binary(), binary(), {binary(), binary()}) ->
|
||||||
|
error | empty | irc_data().
|
||||||
|
-callback set_data(binary(), binary(), {binary(), binary()}, irc_data()) ->
|
||||||
|
{atomic, any()}.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% API
|
%% API
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
@ -119,14 +114,8 @@ init([Host, Opts]) ->
|
||||||
ejabberd:start_app(iconv),
|
ejabberd:start_app(iconv),
|
||||||
MyHost = gen_mod:get_opt_host(Host, Opts,
|
MyHost = gen_mod:get_opt_host(Host, Opts,
|
||||||
<<"irc.@HOST@">>),
|
<<"irc.@HOST@">>),
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(irc_custom,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes, record_info(fields, irc_custom)}]),
|
|
||||||
update_table();
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
Access = gen_mod:get_opt(access, Opts,
|
Access = gen_mod:get_opt(access, Opts,
|
||||||
fun(A) when is_atom(A) -> A end,
|
fun(A) when is_atom(A) -> A end,
|
||||||
all),
|
all),
|
||||||
|
@ -597,43 +586,8 @@ process_irc_register(ServerHost, Host, From, _To,
|
||||||
|
|
||||||
get_data(ServerHost, Host, From) ->
|
get_data(ServerHost, Host, From) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
get_data(LServer, Host, From,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_data(LServer, Host, From).
|
||||||
|
|
||||||
get_data(_LServer, Host, From, mnesia) ->
|
|
||||||
#jid{luser = LUser, lserver = LServer} = From,
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read({irc_custom, {US, Host}})
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> empty;
|
|
||||||
[#irc_custom{data = Data}] -> Data
|
|
||||||
end;
|
|
||||||
get_data(LServer, Host, From, riak) ->
|
|
||||||
#jid{luser = LUser, lserver = LServer} = From,
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case ejabberd_riak:get(irc_custom, irc_custom_schema(), {US, Host}) of
|
|
||||||
{ok, #irc_custom{data = Data}} ->
|
|
||||||
Data;
|
|
||||||
{error, notfound} ->
|
|
||||||
empty;
|
|
||||||
_Err ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
get_data(LServer, Host, From, odbc) ->
|
|
||||||
SJID =
|
|
||||||
ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:remove_resource(From)))),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
case catch ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select data from irc_custom where jid='">>,
|
|
||||||
SJID, <<"' and host='">>, SHost,
|
|
||||||
<<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"data">>], [[SData]]} ->
|
|
||||||
data_to_binary(From, ejabberd_odbc:decode_term(SData));
|
|
||||||
{'EXIT', _} -> error;
|
|
||||||
{selected, _, _} -> empty
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_form(ServerHost, Host, From, [], Lang) ->
|
get_form(ServerHost, Host, From, [], Lang) ->
|
||||||
#jid{user = User, server = Server} = From,
|
#jid{user = User, server = Server} = From,
|
||||||
|
@ -743,37 +697,8 @@ get_form(_ServerHost, _Host, _, _, _Lang) ->
|
||||||
|
|
||||||
set_data(ServerHost, Host, From, Data) ->
|
set_data(ServerHost, Host, From, Data) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
set_data(LServer, Host, From, data_to_binary(From, Data),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:set_data(LServer, Host, From, data_to_binary(From, Data)).
|
||||||
|
|
||||||
set_data(_LServer, Host, From, Data, mnesia) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(From),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
F = fun () ->
|
|
||||||
mnesia:write(#irc_custom{us_host = {US, Host},
|
|
||||||
data = Data})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
set_data(LServer, Host, From, Data, riak) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(From),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
{atomic, ejabberd_riak:put(#irc_custom{us_host = {US, Host},
|
|
||||||
data = Data},
|
|
||||||
irc_custom_schema())};
|
|
||||||
set_data(LServer, Host, From, Data, odbc) ->
|
|
||||||
SJID =
|
|
||||||
ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:remove_resource(From)))),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
SData = ejabberd_odbc:encode_term(Data),
|
|
||||||
F = fun () ->
|
|
||||||
odbc_queries:update_t(<<"irc_custom">>,
|
|
||||||
[<<"jid">>, <<"host">>, <<"data">>],
|
|
||||||
[SJID, SHost, SData],
|
|
||||||
[<<"jid='">>, SJID, <<"' and host='">>,
|
|
||||||
SHost, <<"'">>]),
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
set_form(ServerHost, Host, From, [], Lang, XData) ->
|
set_form(ServerHost, Host, From, [], Lang, XData) ->
|
||||||
case {lists:keysearch(<<"username">>, 1, XData),
|
case {lists:keysearch(<<"username">>, 1, XData),
|
||||||
|
@ -1314,66 +1239,17 @@ conn_params_to_list(Params) ->
|
||||||
Port, binary_to_list(P)}
|
Port, binary_to_list(P)}
|
||||||
end, Params).
|
end, Params).
|
||||||
|
|
||||||
irc_custom_schema() ->
|
export(LServer) ->
|
||||||
{record_info(fields, irc_custom), #irc_custom{}}.
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Mod:export(LServer).
|
||||||
|
|
||||||
update_table() ->
|
import(LServer) ->
|
||||||
Fields = record_info(fields, irc_custom),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
case mnesia:table_info(irc_custom, attributes) of
|
Mod:import(LServer).
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
irc_custom, Fields, set,
|
|
||||||
fun(#irc_custom{us_host = {_, H}}) -> H end,
|
|
||||||
fun(#irc_custom{us_host = {{U, S}, H},
|
|
||||||
data = Data} = R) ->
|
|
||||||
JID = jid:make(U, S, <<"">>),
|
|
||||||
R#irc_custom{us_host = {{iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)},
|
|
||||||
iolist_to_binary(H)},
|
|
||||||
data = data_to_binary(JID, Data)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating irc_custom table", []),
|
|
||||||
mnesia:transform_table(irc_custom, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(_Server) ->
|
import(LServer, DBType, Data) ->
|
||||||
[{irc_custom,
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
fun(Host, #irc_custom{us_host = {{U, S}, IRCHost},
|
Mod:import(LServer, Data).
|
||||||
data = Data}) ->
|
|
||||||
case str:suffix(Host, IRCHost) of
|
|
||||||
true ->
|
|
||||||
SJID = ejabberd_odbc:escape(
|
|
||||||
jid:to_string(
|
|
||||||
jid:make(U, S, <<"">>))),
|
|
||||||
SIRCHost = ejabberd_odbc:escape(IRCHost),
|
|
||||||
SData = ejabberd_odbc:encode_term(Data),
|
|
||||||
[[<<"delete from irc_custom where jid='">>, SJID,
|
|
||||||
<<"' and host='">>, SIRCHost, <<"';">>],
|
|
||||||
[<<"insert into irc_custom(jid, host, "
|
|
||||||
"data) values ('">>,
|
|
||||||
SJID, <<"', '">>, SIRCHost, <<"', '">>, SData,
|
|
||||||
<<"');">>]];
|
|
||||||
false ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer) ->
|
|
||||||
[{<<"select jid, host, data from irc_custom;">>,
|
|
||||||
fun([SJID, IRCHost, SData]) ->
|
|
||||||
#jid{luser = U, lserver = S} = jid:from_string(SJID),
|
|
||||||
Data = ejabberd_odbc:decode_term(SData),
|
|
||||||
#irc_custom{us_host = {{U, S}, IRCHost},
|
|
||||||
data = Data}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #irc_custom{} = R) ->
|
|
||||||
mnesia:dirty_write(R);
|
|
||||||
import(_LServer, riak, #irc_custom{} = R) ->
|
|
||||||
ejabberd_riak:put(R, irc_custom_schema());
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(access) ->
|
mod_opt_type(access) ->
|
||||||
fun (A) when is_atom(A) -> A end;
|
fun (A) when is_atom(A) -> A end;
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_irc_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_irc).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, get_data/3, set_data/4, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_irc.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(irc_custom,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, irc_custom)}]),
|
||||||
|
update_table().
|
||||||
|
|
||||||
|
get_data(_LServer, Host, From) ->
|
||||||
|
{U, S, _} = jid:tolower(From),
|
||||||
|
case catch mnesia:dirty_read({irc_custom, {{U, S}, Host}}) of
|
||||||
|
{'EXIT', _Reason} -> error;
|
||||||
|
[] -> empty;
|
||||||
|
[#irc_custom{data = Data}] -> Data
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_data(_LServer, Host, From, Data) ->
|
||||||
|
{U, S, _} = jid:tolower(From),
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write(#irc_custom{us_host = {{U, S}, Host},
|
||||||
|
data = Data})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #irc_custom{} = R) ->
|
||||||
|
mnesia:dirty_write(R).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, irc_custom),
|
||||||
|
case mnesia:table_info(irc_custom, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
irc_custom, Fields, set,
|
||||||
|
fun(#irc_custom{us_host = {_, H}}) -> H end,
|
||||||
|
fun(#irc_custom{us_host = {{U, S}, H},
|
||||||
|
data = Data} = R) ->
|
||||||
|
JID = jid:make(U, S, <<"">>),
|
||||||
|
R#irc_custom{us_host = {{iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
iolist_to_binary(H)},
|
||||||
|
data = mod_irc:data_to_binary(JID, Data)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating irc_custom table", []),
|
||||||
|
mnesia:transform_table(irc_custom, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,49 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_irc_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_irc).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, get_data/3, set_data/4, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_irc.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
get_data(_LServer, Host, From) ->
|
||||||
|
{U, S, _} = jid:tolower(From),
|
||||||
|
case ejabberd_riak:get(irc_custom, irc_custom_schema(), {{U, S}, Host}) of
|
||||||
|
{ok, #irc_custom{data = Data}} ->
|
||||||
|
Data;
|
||||||
|
{error, notfound} ->
|
||||||
|
empty;
|
||||||
|
_Err ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_data(_LServer, Host, From, Data) ->
|
||||||
|
{U, S, _} = jid:tolower(From),
|
||||||
|
{atomic, ejabberd_riak:put(#irc_custom{us_host = {{U, S}, Host},
|
||||||
|
data = Data},
|
||||||
|
irc_custom_schema())}.
|
||||||
|
|
||||||
|
import(_LServer, #irc_custom{} = R) ->
|
||||||
|
ejabberd_riak:put(R, irc_custom_schema()).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
irc_custom_schema() ->
|
||||||
|
{record_info(fields, irc_custom), #irc_custom{}}.
|
|
@ -0,0 +1,91 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_irc_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_irc).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, get_data/3, set_data/4, import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_irc.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
get_data(LServer, Host, From) ->
|
||||||
|
LJID = jid:tolower(jid:remove_resource(From)),
|
||||||
|
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"select data from irc_custom where jid='">>,
|
||||||
|
SJID, <<"' and host='">>, SHost,
|
||||||
|
<<"';">>]) of
|
||||||
|
{selected, [<<"data">>], [[SData]]} ->
|
||||||
|
mod_irc:data_to_binary(From, ejabberd_odbc:decode_term(SData));
|
||||||
|
{'EXIT', _} -> error;
|
||||||
|
{selected, _, _} -> empty
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_data(LServer, Host, From, Data) ->
|
||||||
|
LJID = jid:tolower(jid:remove_resource(From)),
|
||||||
|
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
SData = ejabberd_odbc:encode_term(Data),
|
||||||
|
F = fun () ->
|
||||||
|
odbc_queries:update_t(<<"irc_custom">>,
|
||||||
|
[<<"jid">>, <<"host">>, <<"data">>],
|
||||||
|
[SJID, SHost, SData],
|
||||||
|
[<<"jid='">>, SJID, <<"' and host='">>,
|
||||||
|
SHost, <<"'">>]),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{irc_custom,
|
||||||
|
fun(Host, #irc_custom{us_host = {{U, S}, IRCHost},
|
||||||
|
data = Data}) ->
|
||||||
|
case str:suffix(Host, IRCHost) of
|
||||||
|
true ->
|
||||||
|
SJID = ejabberd_odbc:escape(
|
||||||
|
jid:to_string(
|
||||||
|
jid:make(U, S, <<"">>))),
|
||||||
|
SIRCHost = ejabberd_odbc:escape(IRCHost),
|
||||||
|
SData = ejabberd_odbc:encode_term(Data),
|
||||||
|
[[<<"delete from irc_custom where jid='">>, SJID,
|
||||||
|
<<"' and host='">>, SIRCHost, <<"';">>],
|
||||||
|
[<<"insert into irc_custom(jid, host, "
|
||||||
|
"data) values ('">>,
|
||||||
|
SJID, <<"', '">>, SIRCHost, <<"', '">>, SData,
|
||||||
|
<<"');">>]];
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_LServer) ->
|
||||||
|
[{<<"select jid, host, data from irc_custom;">>,
|
||||||
|
fun([SJID, IRCHost, SData]) ->
|
||||||
|
#jid{luser = U, lserver = S} = jid:from_string(SJID),
|
||||||
|
Data = ejabberd_odbc:decode_term(SData),
|
||||||
|
#irc_custom{us_host = {{U, S}, IRCHost},
|
||||||
|
data = Data}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
152
src/mod_last.erl
152
src/mod_last.erl
|
@ -45,23 +45,21 @@
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
|
-include("mod_last.hrl").
|
||||||
|
|
||||||
-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
timestamp = 0 :: non_neg_integer(),
|
-callback import(binary(), #last_activity{}) -> ok | pass.
|
||||||
status = <<"">> :: binary()}).
|
-callback get_last(binary(), binary()) ->
|
||||||
|
{ok, non_neg_integer(), binary()} | not_found | {error, any()}.
|
||||||
|
-callback store_last_info(binary(), binary(), non_neg_integer(), binary()) ->
|
||||||
|
{atomic, any()}.
|
||||||
|
-callback remove_user(binary(), binary()) -> {atomic, any()}.
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
one_queue),
|
one_queue),
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(last_activity,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, last_activity)}]),
|
|
||||||
update_table();
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||||
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
|
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||||
|
@ -163,38 +161,8 @@ process_sm_iq(From, To,
|
||||||
%% @spec (LUser::string(), LServer::string()) ->
|
%% @spec (LUser::string(), LServer::string()) ->
|
||||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
|
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
|
||||||
get_last(LUser, LServer) ->
|
get_last(LUser, LServer) ->
|
||||||
get_last(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_last(LUser, LServer).
|
||||||
|
|
||||||
get_last(LUser, LServer, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(last_activity,
|
|
||||||
{LUser, LServer})
|
|
||||||
of
|
|
||||||
{'EXIT', Reason} -> {error, Reason};
|
|
||||||
[] -> not_found;
|
|
||||||
[#last_activity{timestamp = TimeStamp,
|
|
||||||
status = Status}] ->
|
|
||||||
{ok, TimeStamp, Status}
|
|
||||||
end;
|
|
||||||
get_last(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(last_activity, last_activity_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #last_activity{timestamp = TimeStamp,
|
|
||||||
status = Status}} ->
|
|
||||||
{ok, TimeStamp, Status};
|
|
||||||
{error, notfound} ->
|
|
||||||
not_found;
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end;
|
|
||||||
get_last(LUser, LServer, odbc) ->
|
|
||||||
case catch odbc_queries:get_last(LServer, LUser) of
|
|
||||||
{selected, []} ->
|
|
||||||
not_found;
|
|
||||||
{selected, [{TimeStamp, Status}]} ->
|
|
||||||
{ok, TimeStamp, Status};
|
|
||||||
Reason -> {error, {invalid_result, Reason}}
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_last_iq(#iq{lang = Lang} = IQ, SubEl, LUser, LServer) ->
|
get_last_iq(#iq{lang = Lang} = IQ, SubEl, LUser, LServer) ->
|
||||||
case ejabberd_sm:get_user_resources(LUser, LServer) of
|
case ejabberd_sm:get_user_resources(LUser, LServer) of
|
||||||
|
@ -237,29 +205,8 @@ on_presence_update(User, Server, _Resource, Status) ->
|
||||||
store_last_info(User, Server, TimeStamp, Status) ->
|
store_last_info(User, Server, TimeStamp, Status) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status,
|
Mod:store_last_info(LUser, LServer, TimeStamp, Status).
|
||||||
DBType).
|
|
||||||
|
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status,
|
|
||||||
mnesia) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
F = fun () ->
|
|
||||||
mnesia:write(#last_activity{us = US,
|
|
||||||
timestamp = TimeStamp,
|
|
||||||
status = Status})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status,
|
|
||||||
riak) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
{atomic, ejabberd_riak:put(#last_activity{us = US,
|
|
||||||
timestamp = TimeStamp,
|
|
||||||
status = Status},
|
|
||||||
last_activity_schema())};
|
|
||||||
store_last_info(LUser, LServer, TimeStamp, Status,
|
|
||||||
odbc) ->
|
|
||||||
odbc_queries:set_last_t(LServer, LUser, TimeStamp, Status).
|
|
||||||
|
|
||||||
%% @spec (LUser::string(), LServer::string()) ->
|
%% @spec (LUser::string(), LServer::string()) ->
|
||||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found
|
%% {ok, TimeStamp::integer(), Status::string()} | not_found
|
||||||
|
@ -272,71 +219,20 @@ get_last_info(LUser, LServer) ->
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
remove_user(LUser, LServer, DBType).
|
Mod:remove_user(LUser, LServer).
|
||||||
|
|
||||||
remove_user(LUser, LServer, mnesia) ->
|
export(LServer) ->
|
||||||
US = {LUser, LServer},
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
F = fun () -> mnesia:delete({last_activity, US}) end,
|
Mod:export(LServer).
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_user(LUser, LServer, odbc) ->
|
|
||||||
odbc_queries:del_last(LServer, LUser);
|
|
||||||
remove_user(LUser, LServer, riak) ->
|
|
||||||
{atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}.
|
|
||||||
|
|
||||||
update_table() ->
|
|
||||||
Fields = record_info(fields, last_activity),
|
|
||||||
case mnesia:table_info(last_activity, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
last_activity, Fields, set,
|
|
||||||
fun(#last_activity{us = {U, _}}) -> U end,
|
|
||||||
fun(#last_activity{us = {U, S}, status = Status} = R) ->
|
|
||||||
R#last_activity{us = {iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)},
|
|
||||||
status = iolist_to_binary(Status)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating last_activity table", []),
|
|
||||||
mnesia:transform_table(last_activity, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
last_activity_schema() ->
|
|
||||||
{record_info(fields, last_activity), #last_activity{}}.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{last_activity,
|
|
||||||
fun(Host, #last_activity{us = {LUser, LServer},
|
|
||||||
timestamp = TimeStamp, status = Status})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
Seconds =
|
|
||||||
ejabberd_odbc:escape(jlib:integer_to_binary(TimeStamp)),
|
|
||||||
State = ejabberd_odbc:escape(Status),
|
|
||||||
[[<<"delete from last where username='">>, Username, <<"';">>],
|
|
||||||
[<<"insert into last(username, seconds, "
|
|
||||||
"state) values ('">>,
|
|
||||||
Username, <<"', '">>, Seconds, <<"', '">>, State,
|
|
||||||
<<"');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select username, seconds, state from last">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([LUser, TimeStamp, State]) ->
|
Mod:import(LServer).
|
||||||
#last_activity{us = {LUser, LServer},
|
|
||||||
timestamp = jlib:binary_to_integer(
|
|
||||||
TimeStamp),
|
|
||||||
status = State}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #last_activity{} = LA) ->
|
import(LServer, DBType, LA) ->
|
||||||
mnesia:dirty_write(LA);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
import(_LServer, riak, #last_activity{} = LA) ->
|
Mod:import(LServer, LA).
|
||||||
ejabberd_riak:put(LA, last_activity_schema());
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
transform_options(Opts) ->
|
transform_options(Opts) ->
|
||||||
lists:foldl(fun transform_options/2, [], Opts).
|
lists:foldl(fun transform_options/2, [], Opts).
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_last_mnesia).
|
||||||
|
-behaviour(mod_last).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, get_last/2, store_last_info/4, remove_user/2]).
|
||||||
|
|
||||||
|
-include("mod_last.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(last_activity,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, last_activity)}]),
|
||||||
|
update_table().
|
||||||
|
|
||||||
|
get_last(LUser, LServer) ->
|
||||||
|
case mnesia:dirty_read(last_activity, {LUser, LServer}) of
|
||||||
|
[] ->
|
||||||
|
not_found;
|
||||||
|
[#last_activity{timestamp = TimeStamp,
|
||||||
|
status = Status}] ->
|
||||||
|
{ok, TimeStamp, Status}
|
||||||
|
end.
|
||||||
|
|
||||||
|
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write(#last_activity{us = US,
|
||||||
|
timestamp = TimeStamp,
|
||||||
|
status = Status})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () -> mnesia:delete({last_activity, US}) end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #last_activity{} = LA) ->
|
||||||
|
mnesia:dirty_write(LA).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, last_activity),
|
||||||
|
case mnesia:table_info(last_activity, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
last_activity, Fields, set,
|
||||||
|
fun(#last_activity{us = {U, _}}) -> U end,
|
||||||
|
fun(#last_activity{us = {U, S}, status = Status} = R) ->
|
||||||
|
R#last_activity{us = {iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
status = iolist_to_binary(Status)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating last_activity table", []),
|
||||||
|
mnesia:transform_table(last_activity, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,53 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_last_riak).
|
||||||
|
-behaviour(mod_last).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, get_last/2, store_last_info/4, remove_user/2]).
|
||||||
|
|
||||||
|
-include("mod_last.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
get_last(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(last_activity, last_activity_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{ok, #last_activity{timestamp = TimeStamp,
|
||||||
|
status = Status}} ->
|
||||||
|
{ok, TimeStamp, Status};
|
||||||
|
{error, notfound} ->
|
||||||
|
not_found;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
{atomic, ejabberd_riak:put(#last_activity{us = US,
|
||||||
|
timestamp = TimeStamp,
|
||||||
|
status = Status},
|
||||||
|
last_activity_schema())}.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete(last_activity, {LUser, LServer})}.
|
||||||
|
|
||||||
|
import(_LServer, #last_activity{} = LA) ->
|
||||||
|
ejabberd_riak:put(LA, last_activity_schema()).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
last_activity_schema() ->
|
||||||
|
{record_info(fields, last_activity), #last_activity{}}.
|
|
@ -0,0 +1,75 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_last_sql).
|
||||||
|
-behaviour(mod_last).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, get_last/2, store_last_info/4, remove_user/2,
|
||||||
|
import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-include("mod_last.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
get_last(LUser, LServer) ->
|
||||||
|
case catch odbc_queries:get_last(LServer, LUser) of
|
||||||
|
{selected, []} ->
|
||||||
|
not_found;
|
||||||
|
{selected, [{TimeStamp, Status}]} ->
|
||||||
|
{ok, TimeStamp, Status};
|
||||||
|
Reason ->
|
||||||
|
?ERROR_MSG("failed to get last for user ~s@~s: ~p",
|
||||||
|
[LUser, LServer, Reason]),
|
||||||
|
{error, {invalid_result, Reason}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
store_last_info(LUser, LServer, TimeStamp, Status) ->
|
||||||
|
odbc_queries:set_last_t(LServer, LUser, TimeStamp, Status).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
odbc_queries:del_last(LServer, LUser).
|
||||||
|
|
||||||
|
import(_LServer, _LA) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{last_activity,
|
||||||
|
fun(Host, #last_activity{us = {LUser, LServer},
|
||||||
|
timestamp = TimeStamp, status = Status})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
Seconds =
|
||||||
|
ejabberd_odbc:escape(jlib:integer_to_binary(TimeStamp)),
|
||||||
|
State = ejabberd_odbc:escape(Status),
|
||||||
|
[[<<"delete from last where username='">>, Username, <<"';">>],
|
||||||
|
[<<"insert into last(username, seconds, "
|
||||||
|
"state) values ('">>,
|
||||||
|
Username, <<"', '">>, Seconds, <<"', '">>, State,
|
||||||
|
<<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username, seconds, state from last">>,
|
||||||
|
fun([LUser, TimeStamp, State]) ->
|
||||||
|
#last_activity{us = {LUser, LServer},
|
||||||
|
timestamp = jlib:binary_to_integer(
|
||||||
|
TimeStamp),
|
||||||
|
status = State}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
541
src/mod_mam.erl
541
src/mod_mam.erl
|
@ -35,41 +35,37 @@
|
||||||
|
|
||||||
-export([user_send_packet/4, user_receive_packet/5,
|
-export([user_send_packet/4, user_receive_packet/5,
|
||||||
process_iq_v0_2/3, process_iq_v0_3/3, disco_sm_features/5,
|
process_iq_v0_2/3, process_iq_v0_3/3, disco_sm_features/5,
|
||||||
remove_user/2, remove_user/3, mod_opt_type/1, muc_process_iq/4,
|
remove_user/2, remove_room/3, mod_opt_type/1, muc_process_iq/4,
|
||||||
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
|
muc_filter_message/5, message_is_archived/5, delete_old_messages/2,
|
||||||
get_commands_spec/0]).
|
get_commands_spec/0, msg_to_el/4]).
|
||||||
|
|
||||||
-include_lib("stdlib/include/ms_transform.hrl").
|
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("mod_muc_room.hrl").
|
-include("mod_muc_room.hrl").
|
||||||
-include("ejabberd_commands.hrl").
|
-include("ejabberd_commands.hrl").
|
||||||
|
-include("mod_mam.hrl").
|
||||||
|
|
||||||
-define(DEF_PAGE_SIZE, 50).
|
-define(DEF_PAGE_SIZE, 50).
|
||||||
-define(MAX_PAGE_SIZE, 250).
|
-define(MAX_PAGE_SIZE, 250).
|
||||||
|
|
||||||
-define(BIN_GREATER_THAN(A, B),
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
((A > B andalso byte_size(A) == byte_size(B))
|
-callback remove_user(binary(), binary()) -> any().
|
||||||
orelse byte_size(A) > byte_size(B))).
|
-callback remove_room(binary(), binary(), binary()) -> any().
|
||||||
-define(BIN_LESS_THAN(A, B),
|
-callback delete_old_messages(binary() | global,
|
||||||
((A < B andalso byte_size(A) == byte_size(B))
|
erlang:timestamp(),
|
||||||
orelse byte_size(A) < byte_size(B))).
|
all | chat | groupchat) -> any().
|
||||||
|
-callback extended_fields() -> [xmlel()].
|
||||||
-record(archive_msg,
|
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
|
||||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
jid(), binary(), recv | send) -> {ok, binary()} | any().
|
||||||
id = <<>> :: binary() | '_',
|
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
|
||||||
timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_' | '$1',
|
-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
|
||||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined,
|
-callback select(binary(), jid(), jid(),
|
||||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
none | erlang:timestamp(),
|
||||||
packet = #xmlel{} :: xmlel() | '_',
|
none | erlang:timestamp(),
|
||||||
nick = <<"">> :: binary(),
|
none | ljid() | {text, binary()},
|
||||||
type = chat :: chat | groupchat}).
|
none | #rsm_in{},
|
||||||
|
chat | groupchat) ->
|
||||||
-record(archive_prefs,
|
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
||||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
|
||||||
default = never :: never | always | roster,
|
|
||||||
always = [] :: [ljid()],
|
|
||||||
never = [] :: [ljid()]}).
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% API
|
%%% API
|
||||||
|
@ -77,9 +73,9 @@
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
one_queue),
|
one_queue),
|
||||||
DBType = gen_mod:db_type(Host, Opts),
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
init_db(DBType, Host),
|
Mod:init(Host, Opts),
|
||||||
init_cache(DBType, Opts),
|
init_cache(Opts),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||||
?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc),
|
?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||||
|
@ -120,18 +116,7 @@ start(Host, Opts) ->
|
||||||
ejabberd_commands:register_commands(get_commands_spec()),
|
ejabberd_commands:register_commands(get_commands_spec()),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
init_db(mnesia, _Host) ->
|
init_cache(Opts) ->
|
||||||
mnesia:create_table(archive_msg,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{type, bag},
|
|
||||||
{attributes, record_info(fields, archive_msg)}]),
|
|
||||||
mnesia:create_table(archive_prefs,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{attributes, record_info(fields, archive_prefs)}]);
|
|
||||||
init_db(_, _) ->
|
|
||||||
ok.
|
|
||||||
|
|
||||||
init_cache(_DBType, Opts) ->
|
|
||||||
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
||||||
fun(I) when is_integer(I), I>0 -> I end,
|
fun(I) when is_integer(I), I>0 -> I end,
|
||||||
1000),
|
1000),
|
||||||
|
@ -179,24 +164,14 @@ stop(Host) ->
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
remove_user(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:remove_user(LUser, LServer).
|
||||||
|
|
||||||
remove_user(LUser, LServer, mnesia) ->
|
remove_room(LServer, Name, Host) ->
|
||||||
US = {LUser, LServer},
|
LName = jid:nodeprep(Name),
|
||||||
F = fun () ->
|
LHost = jid:nameprep(Host),
|
||||||
mnesia:delete({archive_msg, US}),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
mnesia:delete({archive_prefs, US})
|
Mod:remove_room(LServer, LName, LHost).
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_user(LUser, LServer, odbc) ->
|
|
||||||
SUser = ejabberd_odbc:escape(LUser),
|
|
||||||
ejabberd_odbc:sql_query(
|
|
||||||
LServer,
|
|
||||||
[<<"delete from archive where username='">>, SUser, <<"';">>]),
|
|
||||||
ejabberd_odbc:sql_query(
|
|
||||||
LServer,
|
|
||||||
[<<"delete from archive_prefs where username='">>, SUser, <<"';">>]).
|
|
||||||
|
|
||||||
user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
|
user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
|
||||||
LUser = JID#jid.luser,
|
LUser = JID#jid.luser,
|
||||||
|
@ -343,10 +318,10 @@ message_is_archived(false, C2SState, Peer,
|
||||||
if_enabled ->
|
if_enabled ->
|
||||||
get_prefs(LUser, LServer);
|
get_prefs(LUser, LServer);
|
||||||
on_request ->
|
on_request ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
cache_tab:lookup(archive_prefs, {LUser, LServer},
|
cache_tab:lookup(archive_prefs, {LUser, LServer},
|
||||||
fun() ->
|
fun() ->
|
||||||
get_prefs(LUser, LServer, DBType)
|
Mod:get_prefs(LUser, LServer)
|
||||||
end);
|
end);
|
||||||
never ->
|
never ->
|
||||||
error
|
error
|
||||||
|
@ -365,21 +340,19 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
|
||||||
Diff = Days * 24 * 60 * 60 * 1000000,
|
Diff = Days * 24 * 60 * 60 * 1000000,
|
||||||
TimeStamp = usec_to_now(p1_time_compat:system_time(micro_seconds) - Diff),
|
TimeStamp = usec_to_now(p1_time_compat:system_time(micro_seconds) - Diff),
|
||||||
Type = jlib:binary_to_atom(TypeBin),
|
Type = jlib:binary_to_atom(TypeBin),
|
||||||
{Results, _} =
|
DBTypes = lists:usort(
|
||||||
lists:foldl(fun(Host, {Results, MnesiaDone}) ->
|
lists:map(
|
||||||
case {gen_mod:db_type(Host, ?MODULE), MnesiaDone} of
|
fun(Host) ->
|
||||||
{mnesia, true} ->
|
case gen_mod:db_type(Host, ?MODULE) of
|
||||||
{Results, true};
|
odbc -> {odbc, Host};
|
||||||
{mnesia, false} ->
|
Other -> {Other, global}
|
||||||
Res = delete_old_messages(TimeStamp, Type,
|
end
|
||||||
global, mnesia),
|
end, ?MYHOSTS)),
|
||||||
{[Res|Results], true};
|
Results = lists:map(
|
||||||
{DBType, _} ->
|
fun({DBType, ServerHost}) ->
|
||||||
Res = delete_old_messages(TimeStamp, Type,
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
Host, DBType),
|
Mod:delete_old_messages(ServerHost, TimeStamp, Type)
|
||||||
{[Res|Results], MnesiaDone}
|
end, DBTypes),
|
||||||
end
|
|
||||||
end, {[], false}, ?MYHOSTS),
|
|
||||||
case lists:filter(fun(Res) -> Res /= ok end, Results) of
|
case lists:filter(fun(Res) -> Res /= ok end, Results) of
|
||||||
[] -> ok;
|
[] -> ok;
|
||||||
[NotOk|_] -> NotOk
|
[NotOk|_] -> NotOk
|
||||||
|
@ -387,21 +360,6 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
|
||||||
delete_old_messages(_TypeBin, _Days) ->
|
delete_old_messages(_TypeBin, _Days) ->
|
||||||
unsupported_type.
|
unsupported_type.
|
||||||
|
|
||||||
delete_old_messages(TimeStamp, Type, global, mnesia) ->
|
|
||||||
MS = ets:fun2ms(fun(#archive_msg{timestamp = MsgTS,
|
|
||||||
type = MsgType} = Msg)
|
|
||||||
when MsgTS < TimeStamp,
|
|
||||||
MsgType == Type orelse Type == all ->
|
|
||||||
Msg
|
|
||||||
end),
|
|
||||||
OldMsgs = mnesia:dirty_select(archive_msg, MS),
|
|
||||||
lists:foreach(fun(Rec) ->
|
|
||||||
ok = mnesia:dirty_delete_object(Rec)
|
|
||||||
end, OldMsgs);
|
|
||||||
delete_old_messages(_TimeStamp, _Type, _Host, _DBType) ->
|
|
||||||
%% TODO
|
|
||||||
not_implemented.
|
|
||||||
|
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%===================================================================
|
%%%===================================================================
|
||||||
|
@ -427,15 +385,9 @@ process_iq(LServer, #iq{sub_el = #xmlel{attrs = Attrs}} = IQ) ->
|
||||||
#xmlel{name = <<"field">>,
|
#xmlel{name = <<"field">>,
|
||||||
attrs = [{<<"type">>, <<"text-single">>},
|
attrs = [{<<"type">>, <<"text-single">>},
|
||||||
{<<"var">>, <<"end">>}]}],
|
{<<"var">>, <<"end">>}]}],
|
||||||
Fields = case gen_mod:db_type(LServer, ?MODULE) of
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
odbc ->
|
ExtendedFields = Mod:extended_fields(),
|
||||||
WithText = #xmlel{name = <<"field">>,
|
Fields = ExtendedFields ++ CommonFields,
|
||||||
attrs = [{<<"type">>, <<"text-single">>},
|
|
||||||
{<<"var">>, <<"withtext">>}]},
|
|
||||||
[WithText|CommonFields];
|
|
||||||
_ ->
|
|
||||||
CommonFields
|
|
||||||
end,
|
|
||||||
Form = #xmlel{name = <<"x">>,
|
Form = #xmlel{name = <<"x">>,
|
||||||
attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
|
attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
|
||||||
children = Fields},
|
children = Fields},
|
||||||
|
@ -715,8 +667,8 @@ store_msg(C2SState, Pkt, LUser, LServer, Peer, Dir) ->
|
||||||
case should_archive_peer(C2SState, Prefs, Peer) of
|
case should_archive_peer(C2SState, Prefs, Peer) of
|
||||||
true ->
|
true ->
|
||||||
US = {LUser, LServer},
|
US = {LUser, LServer},
|
||||||
store(Pkt, LServer, US, chat, Peer, <<"">>, Dir,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE));
|
Mod:store(Pkt, LServer, US, chat, Peer, <<"">>, Dir);
|
||||||
false ->
|
false ->
|
||||||
pass
|
pass
|
||||||
end.
|
end.
|
||||||
|
@ -726,101 +678,26 @@ store_muc(MUCState, Pkt, RoomJID, Peer, Nick) ->
|
||||||
true ->
|
true ->
|
||||||
LServer = MUCState#state.server_host,
|
LServer = MUCState#state.server_host,
|
||||||
{U, S, _} = jid:tolower(RoomJID),
|
{U, S, _} = jid:tolower(RoomJID),
|
||||||
store(Pkt, LServer, {U, S}, groupchat, Peer, Nick, recv,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE));
|
Mod:store(Pkt, LServer, {U, S}, groupchat, Peer, Nick, recv);
|
||||||
false ->
|
false ->
|
||||||
pass
|
pass
|
||||||
end.
|
end.
|
||||||
|
|
||||||
store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir, mnesia) ->
|
|
||||||
LPeer = {PUser, PServer, _} = jid:tolower(Peer),
|
|
||||||
TS = p1_time_compat:timestamp(),
|
|
||||||
ID = jlib:integer_to_binary(now_to_usec(TS)),
|
|
||||||
case mnesia:dirty_write(
|
|
||||||
#archive_msg{us = {LUser, LServer},
|
|
||||||
id = ID,
|
|
||||||
timestamp = TS,
|
|
||||||
peer = LPeer,
|
|
||||||
bare_peer = {PUser, PServer, <<>>},
|
|
||||||
type = Type,
|
|
||||||
nick = Nick,
|
|
||||||
packet = Pkt}) of
|
|
||||||
ok ->
|
|
||||||
{ok, ID};
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end;
|
|
||||||
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir, odbc) ->
|
|
||||||
TSinteger = p1_time_compat:system_time(micro_seconds),
|
|
||||||
ID = TS = jlib:integer_to_binary(TSinteger),
|
|
||||||
SUser = case Type of
|
|
||||||
chat -> LUser;
|
|
||||||
groupchat -> jid:to_string({LUser, LHost, <<>>})
|
|
||||||
end,
|
|
||||||
BarePeer = jid:to_string(
|
|
||||||
jid:tolower(
|
|
||||||
jid:remove_resource(Peer))),
|
|
||||||
LPeer = jid:to_string(
|
|
||||||
jid:tolower(Peer)),
|
|
||||||
XML = fxml:element_to_binary(Pkt),
|
|
||||||
Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
|
|
||||||
case ejabberd_odbc:sql_query(
|
|
||||||
LServer,
|
|
||||||
[<<"insert into archive (username, timestamp, "
|
|
||||||
"peer, bare_peer, xml, txt, kind, nick) values (">>,
|
|
||||||
<<"'">>, ejabberd_odbc:escape(SUser), <<"', ">>,
|
|
||||||
<<"'">>, TS, <<"', ">>,
|
|
||||||
<<"'">>, ejabberd_odbc:escape(LPeer), <<"', ">>,
|
|
||||||
<<"'">>, ejabberd_odbc:escape(BarePeer), <<"', ">>,
|
|
||||||
<<"'">>, ejabberd_odbc:escape(XML), <<"', ">>,
|
|
||||||
<<"'">>, ejabberd_odbc:escape(Body), <<"', ">>,
|
|
||||||
<<"'">>, jlib:atom_to_binary(Type), <<"', ">>,
|
|
||||||
<<"'">>, ejabberd_odbc:escape(Nick), <<"');">>]) of
|
|
||||||
{updated, _} ->
|
|
||||||
{ok, ID};
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end.
|
|
||||||
|
|
||||||
write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
write_prefs(LUser, LServer, Host, Default, Always, Never) ->
|
||||||
DBType = case gen_mod:db_type(Host, ?MODULE) of
|
|
||||||
odbc -> {odbc, Host};
|
|
||||||
DB -> DB
|
|
||||||
end,
|
|
||||||
Prefs = #archive_prefs{us = {LUser, LServer},
|
Prefs = #archive_prefs{us = {LUser, LServer},
|
||||||
default = Default,
|
default = Default,
|
||||||
always = Always,
|
always = Always,
|
||||||
never = Never},
|
never = Never},
|
||||||
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
cache_tab:dirty_insert(
|
cache_tab:dirty_insert(
|
||||||
archive_prefs, {LUser, LServer}, Prefs,
|
archive_prefs, {LUser, LServer}, Prefs,
|
||||||
fun() -> write_prefs(LUser, LServer, Prefs, DBType) end).
|
fun() -> Mod:write_prefs(LUser, LServer, Prefs, Host) end).
|
||||||
|
|
||||||
write_prefs(_LUser, _LServer, Prefs, mnesia) ->
|
|
||||||
mnesia:dirty_write(Prefs);
|
|
||||||
write_prefs(LUser, _LServer, #archive_prefs{default = Default,
|
|
||||||
never = Never,
|
|
||||||
always = Always},
|
|
||||||
{odbc, Host}) ->
|
|
||||||
SUser = ejabberd_odbc:escape(LUser),
|
|
||||||
SDefault = erlang:atom_to_binary(Default, utf8),
|
|
||||||
SAlways = ejabberd_odbc:encode_term(Always),
|
|
||||||
SNever = ejabberd_odbc:encode_term(Never),
|
|
||||||
case update(Host, <<"archive_prefs">>,
|
|
||||||
[<<"username">>, <<"def">>, <<"always">>, <<"never">>],
|
|
||||||
[SUser, SDefault, SAlways, SNever],
|
|
||||||
[<<"username='">>, SUser, <<"'">>]) of
|
|
||||||
{updated, _} ->
|
|
||||||
ok;
|
|
||||||
Err ->
|
|
||||||
Err
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_prefs(LUser, LServer) ->
|
get_prefs(LUser, LServer) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Res = cache_tab:lookup(archive_prefs, {LUser, LServer},
|
Res = cache_tab:lookup(archive_prefs, {LUser, LServer},
|
||||||
fun() -> get_prefs(LUser, LServer,
|
fun() -> Mod:get_prefs(LUser, LServer) end),
|
||||||
DBType)
|
|
||||||
end),
|
|
||||||
case Res of
|
case Res of
|
||||||
{ok, Prefs} ->
|
{ok, Prefs} ->
|
||||||
Prefs;
|
Prefs;
|
||||||
|
@ -842,31 +719,6 @@ get_prefs(LUser, LServer) ->
|
||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_prefs(LUser, LServer, mnesia) ->
|
|
||||||
case mnesia:dirty_read(archive_prefs, {LUser, LServer}) of
|
|
||||||
[Prefs] ->
|
|
||||||
{ok, Prefs};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
get_prefs(LUser, LServer, odbc) ->
|
|
||||||
case ejabberd_odbc:sql_query(
|
|
||||||
LServer,
|
|
||||||
[<<"select def, always, never from archive_prefs ">>,
|
|
||||||
<<"where username='">>,
|
|
||||||
ejabberd_odbc:escape(LUser), <<"';">>]) of
|
|
||||||
{selected, _, [[SDefault, SAlways, SNever]]} ->
|
|
||||||
Default = erlang:binary_to_existing_atom(SDefault, utf8),
|
|
||||||
Always = ejabberd_odbc:decode_term(SAlways),
|
|
||||||
Never = ejabberd_odbc:decode_term(SNever),
|
|
||||||
{ok, #archive_prefs{us = {LUser, LServer},
|
|
||||||
default = Default,
|
|
||||||
always = Always,
|
|
||||||
never = Never}};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
prefs_el(Default, Always, Never, NS) ->
|
prefs_el(Default, Always, Never, NS) ->
|
||||||
Default1 = jlib:atom_to_binary(Default),
|
Default1 = jlib:atom_to_binary(Default),
|
||||||
JFun = fun(L) ->
|
JFun = fun(L) ->
|
||||||
|
@ -890,11 +742,10 @@ maybe_activate_mam(LUser, LServer) ->
|
||||||
false),
|
false),
|
||||||
case ActivateOpt of
|
case ActivateOpt of
|
||||||
true ->
|
true ->
|
||||||
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Res = cache_tab:lookup(archive_prefs, {LUser, LServer},
|
Res = cache_tab:lookup(archive_prefs, {LUser, LServer},
|
||||||
fun() ->
|
fun() ->
|
||||||
get_prefs(LUser, LServer,
|
Mod:get_prefs(LUser, LServer)
|
||||||
gen_mod:db_type(LServer,
|
|
||||||
?MODULE))
|
|
||||||
end),
|
end),
|
||||||
case Res of
|
case Res of
|
||||||
{ok, _Prefs} ->
|
{ok, _Prefs} ->
|
||||||
|
@ -912,31 +763,22 @@ maybe_activate_mam(LUser, LServer) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType) ->
|
select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType) ->
|
||||||
DBType = case gen_mod:db_type(LServer, ?MODULE) of
|
|
||||||
odbc -> {odbc, LServer};
|
|
||||||
DB -> DB
|
|
||||||
end,
|
|
||||||
select_and_send(LServer, From, To, Start, End, With, RSM, IQ,
|
|
||||||
MsgType, DBType).
|
|
||||||
|
|
||||||
select_and_send(LServer, From, To, Start, End, With, RSM, IQ, MsgType, DBType) ->
|
|
||||||
{Msgs, IsComplete, Count} = select_and_start(LServer, From, To, Start, End,
|
{Msgs, IsComplete, Count} = select_and_start(LServer, From, To, Start, End,
|
||||||
With, RSM, MsgType, DBType),
|
With, RSM, MsgType),
|
||||||
SortedMsgs = lists:keysort(2, Msgs),
|
SortedMsgs = lists:keysort(2, Msgs),
|
||||||
send(From, To, SortedMsgs, RSM, Count, IsComplete, IQ).
|
send(From, To, SortedMsgs, RSM, Count, IsComplete, IQ).
|
||||||
|
|
||||||
select_and_start(LServer, From, To, Start, End, With, RSM, MsgType, DBType) ->
|
select_and_start(LServer, From, To, Start, End, With, RSM, MsgType) ->
|
||||||
case MsgType of
|
case MsgType of
|
||||||
chat ->
|
chat ->
|
||||||
select(LServer, From, From, Start, End, With, RSM, MsgType, DBType);
|
select(LServer, From, From, Start, End, With, RSM, MsgType);
|
||||||
{groupchat, _Role, _MUCState} ->
|
{groupchat, _Role, _MUCState} ->
|
||||||
select(LServer, From, To, Start, End, With, RSM, MsgType, DBType)
|
select(LServer, From, To, Start, End, With, RSM, MsgType)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
||||||
{groupchat, _Role, #state{config = #config{mam = false},
|
{groupchat, _Role, #state{config = #config{mam = false},
|
||||||
history = History}} = MsgType,
|
history = History}} = MsgType) ->
|
||||||
_DBType) ->
|
|
||||||
#lqueue{len = L, queue = Q} = History,
|
#lqueue{len = L, queue = Q} = History,
|
||||||
{Msgs0, _} =
|
{Msgs0, _} =
|
||||||
lists:mapfoldl(
|
lists:mapfoldl(
|
||||||
|
@ -970,81 +812,9 @@ select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
||||||
_ ->
|
_ ->
|
||||||
{Msgs, true, L}
|
{Msgs, true, L}
|
||||||
end;
|
end;
|
||||||
select(_LServer, JidRequestor,
|
select(LServer, From, From, Start, End, With, RSM, MsgType) ->
|
||||||
#jid{luser = LUser, lserver = LServer} = JidArchive,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Start, End, With, RSM, MsgType, mnesia) ->
|
Mod:select(LServer, From, From, Start, End, With, RSM, MsgType).
|
||||||
MS = make_matchspec(LUser, LServer, Start, End, With),
|
|
||||||
Msgs = mnesia:dirty_select(archive_msg, MS),
|
|
||||||
SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs),
|
|
||||||
{FilteredMsgs, IsComplete} = filter_by_rsm(SortedMsgs, RSM),
|
|
||||||
Count = length(Msgs),
|
|
||||||
{lists:map(
|
|
||||||
fun(Msg) ->
|
|
||||||
{Msg#archive_msg.id,
|
|
||||||
jlib:binary_to_integer(Msg#archive_msg.id),
|
|
||||||
msg_to_el(Msg, MsgType, JidRequestor, JidArchive)}
|
|
||||||
end, FilteredMsgs), IsComplete, Count};
|
|
||||||
select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
|
|
||||||
Start, End, With, RSM, MsgType, {odbc, Host}) ->
|
|
||||||
User = case MsgType of
|
|
||||||
chat -> LUser;
|
|
||||||
{groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
|
|
||||||
end,
|
|
||||||
{Query, CountQuery} = make_sql_query(User, LServer,
|
|
||||||
Start, End, With, RSM),
|
|
||||||
% TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a
|
|
||||||
% reasonable limit on how many stanzas may be pushed to a client in one
|
|
||||||
% request. If a query returns a number of stanzas greater than this limit
|
|
||||||
% and the client did not specify a limit using RSM then the server should
|
|
||||||
% return a policy-violation error to the client." We currently don't do this
|
|
||||||
% for v0.2 requests, but we do limit #rsm_in.max for v0.3 and newer.
|
|
||||||
case {ejabberd_odbc:sql_query(Host, Query),
|
|
||||||
ejabberd_odbc:sql_query(Host, CountQuery)} of
|
|
||||||
{{selected, _, Res}, {selected, _, [[Count]]}} ->
|
|
||||||
{Max, Direction} = case RSM of
|
|
||||||
#rsm_in{max = M, direction = D} -> {M, D};
|
|
||||||
_ -> {undefined, undefined}
|
|
||||||
end,
|
|
||||||
{Res1, IsComplete} =
|
|
||||||
if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
|
|
||||||
if Direction == before ->
|
|
||||||
{lists:nthtail(1, Res), false};
|
|
||||||
true ->
|
|
||||||
{lists:sublist(Res, Max), false}
|
|
||||||
end;
|
|
||||||
true ->
|
|
||||||
{Res, true}
|
|
||||||
end,
|
|
||||||
{lists:flatmap(
|
|
||||||
fun([TS, XML, PeerBin, Kind, Nick]) ->
|
|
||||||
try
|
|
||||||
#xmlel{} = El = fxml_stream:parse_element(XML),
|
|
||||||
Now = usec_to_now(jlib:binary_to_integer(TS)),
|
|
||||||
PeerJid = jid:tolower(jid:from_string(PeerBin)),
|
|
||||||
T = case Kind of
|
|
||||||
<<"">> -> chat;
|
|
||||||
null -> chat;
|
|
||||||
_ -> jlib:binary_to_atom(Kind)
|
|
||||||
end,
|
|
||||||
[{TS, jlib:binary_to_integer(TS),
|
|
||||||
msg_to_el(#archive_msg{timestamp = Now,
|
|
||||||
packet = El,
|
|
||||||
type = T,
|
|
||||||
nick = Nick,
|
|
||||||
peer = PeerJid},
|
|
||||||
MsgType, JidRequestor, JidArchive)}]
|
|
||||||
catch _:Err ->
|
|
||||||
?ERROR_MSG("failed to parse data from SQL: ~p. "
|
|
||||||
"The data was: "
|
|
||||||
"timestamp = ~s, xml = ~s, "
|
|
||||||
"peer = ~s, kind = ~s, nick = ~s",
|
|
||||||
[Err, TS, XML, PeerBin, Kind, Nick]),
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end, Res1), IsComplete, jlib:binary_to_integer(Count)};
|
|
||||||
_ ->
|
|
||||||
{[], false, 0}
|
|
||||||
end.
|
|
||||||
|
|
||||||
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
|
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
|
||||||
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
||||||
|
@ -1160,7 +930,6 @@ send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
|
||||||
ignore
|
ignore
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
make_rsm_out([], _, Count, Attrs, NS) ->
|
make_rsm_out([], _, Count, Attrs, NS) ->
|
||||||
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
|
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
|
||||||
true -> <<"fin">>
|
true -> <<"fin">>
|
||||||
|
@ -1177,32 +946,6 @@ make_rsm_out([{FirstID, _, _}|_] = Msgs, _, Count, Attrs, NS) ->
|
||||||
#rsm_out{first = FirstID, count = Count,
|
#rsm_out{first = FirstID, count = Count,
|
||||||
last = LastID})}].
|
last = LastID})}].
|
||||||
|
|
||||||
filter_by_rsm(Msgs, none) ->
|
|
||||||
{Msgs, true};
|
|
||||||
filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max < 0 ->
|
|
||||||
{[], true};
|
|
||||||
filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) ->
|
|
||||||
NewMsgs = case Direction of
|
|
||||||
aft when ID /= <<"">> ->
|
|
||||||
lists:filter(
|
|
||||||
fun(#archive_msg{id = I}) ->
|
|
||||||
?BIN_GREATER_THAN(I, ID)
|
|
||||||
end, Msgs);
|
|
||||||
before when ID /= <<"">> ->
|
|
||||||
lists:foldl(
|
|
||||||
fun(#archive_msg{id = I} = Msg, Acc)
|
|
||||||
when ?BIN_LESS_THAN(I, ID) ->
|
|
||||||
[Msg|Acc];
|
|
||||||
(_, Acc) ->
|
|
||||||
Acc
|
|
||||||
end, [], Msgs);
|
|
||||||
before when ID == <<"">> ->
|
|
||||||
lists:reverse(Msgs);
|
|
||||||
_ ->
|
|
||||||
Msgs
|
|
||||||
end,
|
|
||||||
filter_by_max(NewMsgs, Max).
|
|
||||||
|
|
||||||
filter_by_max(Msgs, undefined) ->
|
filter_by_max(Msgs, undefined) ->
|
||||||
{Msgs, true};
|
{Msgs, true};
|
||||||
filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 ->
|
filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 ->
|
||||||
|
@ -1231,126 +974,6 @@ match_rsm(Now, #rsm_in{id = ID, direction = before}) when ID /= <<"">> ->
|
||||||
match_rsm(_Now, _) ->
|
match_rsm(_Now, _) ->
|
||||||
true.
|
true.
|
||||||
|
|
||||||
make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) ->
|
|
||||||
ets:fun2ms(
|
|
||||||
fun(#archive_msg{timestamp = TS,
|
|
||||||
us = US,
|
|
||||||
bare_peer = BPeer} = Msg)
|
|
||||||
when Start =< TS, End >= TS,
|
|
||||||
US == {LUser, LServer},
|
|
||||||
BPeer == With ->
|
|
||||||
Msg
|
|
||||||
end);
|
|
||||||
make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) ->
|
|
||||||
ets:fun2ms(
|
|
||||||
fun(#archive_msg{timestamp = TS,
|
|
||||||
us = US,
|
|
||||||
peer = Peer} = Msg)
|
|
||||||
when Start =< TS, End >= TS,
|
|
||||||
US == {LUser, LServer},
|
|
||||||
Peer == With ->
|
|
||||||
Msg
|
|
||||||
end);
|
|
||||||
make_matchspec(LUser, LServer, Start, End, none) ->
|
|
||||||
ets:fun2ms(
|
|
||||||
fun(#archive_msg{timestamp = TS,
|
|
||||||
us = US,
|
|
||||||
peer = Peer} = Msg)
|
|
||||||
when Start =< TS, End >= TS,
|
|
||||||
US == {LUser, LServer} ->
|
|
||||||
Msg
|
|
||||||
end).
|
|
||||||
|
|
||||||
make_sql_query(User, LServer, Start, End, With, RSM) ->
|
|
||||||
{Max, Direction, ID} = case RSM of
|
|
||||||
#rsm_in{} ->
|
|
||||||
{RSM#rsm_in.max,
|
|
||||||
RSM#rsm_in.direction,
|
|
||||||
RSM#rsm_in.id};
|
|
||||||
none ->
|
|
||||||
{none, none, <<>>}
|
|
||||||
end,
|
|
||||||
ODBCType = ejabberd_config:get_option(
|
|
||||||
{odbc_type, LServer},
|
|
||||||
ejabberd_odbc:opt_type(odbc_type)),
|
|
||||||
LimitClause = if is_integer(Max), Max >= 0, ODBCType /= mssql ->
|
|
||||||
[<<" limit ">>, jlib:integer_to_binary(Max+1)];
|
|
||||||
true ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
TopClause = if is_integer(Max), Max >= 0, ODBCType == mssql ->
|
|
||||||
[<<" TOP ">>, jlib:integer_to_binary(Max+1)];
|
|
||||||
true ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
WithClause = case With of
|
|
||||||
{text, <<>>} ->
|
|
||||||
[];
|
|
||||||
{text, Txt} ->
|
|
||||||
[<<" and match (txt) against ('">>,
|
|
||||||
ejabberd_odbc:escape(Txt), <<"')">>];
|
|
||||||
{_, _, <<>>} ->
|
|
||||||
[<<" and bare_peer='">>,
|
|
||||||
ejabberd_odbc:escape(jid:to_string(With)),
|
|
||||||
<<"'">>];
|
|
||||||
{_, _, _} ->
|
|
||||||
[<<" and peer='">>,
|
|
||||||
ejabberd_odbc:escape(jid:to_string(With)),
|
|
||||||
<<"'">>];
|
|
||||||
none ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
PageClause = case catch jlib:binary_to_integer(ID) of
|
|
||||||
I when is_integer(I), I >= 0 ->
|
|
||||||
case Direction of
|
|
||||||
before ->
|
|
||||||
[<<" AND timestamp < ">>, ID];
|
|
||||||
aft ->
|
|
||||||
[<<" AND timestamp > ">>, ID];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
StartClause = case Start of
|
|
||||||
{_, _, _} ->
|
|
||||||
[<<" and timestamp >= ">>,
|
|
||||||
jlib:integer_to_binary(now_to_usec(Start))];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
EndClause = case End of
|
|
||||||
{_, _, _} ->
|
|
||||||
[<<" and timestamp <= ">>,
|
|
||||||
jlib:integer_to_binary(now_to_usec(End))];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end,
|
|
||||||
SUser = ejabberd_odbc:escape(User),
|
|
||||||
|
|
||||||
Query = [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick"
|
|
||||||
" FROM archive WHERE username='">>,
|
|
||||||
SUser, <<"'">>, WithClause, StartClause, EndClause,
|
|
||||||
PageClause],
|
|
||||||
|
|
||||||
QueryPage =
|
|
||||||
case Direction of
|
|
||||||
before ->
|
|
||||||
% ID can be empty because of
|
|
||||||
% XEP-0059: Result Set Management
|
|
||||||
% 2.5 Requesting the Last Page in a Result Set
|
|
||||||
[<<"SELECT timestamp, xml, peer, kind, nick FROM (">>, Query,
|
|
||||||
<<" ORDER BY timestamp DESC ">>,
|
|
||||||
LimitClause, <<") AS t ORDER BY timestamp ASC;">>];
|
|
||||||
_ ->
|
|
||||||
[Query, <<" ORDER BY timestamp ASC ">>,
|
|
||||||
LimitClause, <<";">>]
|
|
||||||
end,
|
|
||||||
{QueryPage,
|
|
||||||
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
|
|
||||||
SUser, <<"'">>, WithClause, StartClause, EndClause, <<";">>]}.
|
|
||||||
|
|
||||||
now_to_usec({MSec, Sec, USec}) ->
|
now_to_usec({MSec, Sec, USec}) ->
|
||||||
(MSec*1000000 + Sec)*1000000 + USec.
|
(MSec*1000000 + Sec)*1000000 + USec.
|
||||||
|
|
||||||
|
@ -1376,28 +999,6 @@ get_jids(Els) ->
|
||||||
[]
|
[]
|
||||||
end, Els).
|
end, Els).
|
||||||
|
|
||||||
update(LServer, Table, Fields, Vals, Where) ->
|
|
||||||
UPairs = lists:zipwith(fun (A, B) ->
|
|
||||||
<<A/binary, "='", B/binary, "'">>
|
|
||||||
end,
|
|
||||||
Fields, Vals),
|
|
||||||
case ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"update ">>, Table, <<" set ">>,
|
|
||||||
join(UPairs, <<", ">>), <<" where ">>, Where,
|
|
||||||
<<";">>])
|
|
||||||
of
|
|
||||||
{updated, 1} -> {updated, 1};
|
|
||||||
_ ->
|
|
||||||
ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"insert into ">>, Table, <<"(">>,
|
|
||||||
join(Fields, <<", ">>), <<") values ('">>,
|
|
||||||
join(Vals, <<"', '">>), <<"');">>])
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% Almost a copy of string:join/2.
|
|
||||||
join([], _Sep) -> [];
|
|
||||||
join([H | T], Sep) -> [H, [[Sep, X] || X <- T]].
|
|
||||||
|
|
||||||
get_commands_spec() ->
|
get_commands_spec() ->
|
||||||
[#ejabberd_commands{name = delete_old_mam_messages, tags = [purge],
|
[#ejabberd_commands{name = delete_old_mam_messages, tags = [purge],
|
||||||
desc = "Delete MAM messages older than DAYS",
|
desc = "Delete MAM messages older than DAYS",
|
||||||
|
@ -1416,7 +1017,11 @@ mod_opt_type(cache_life_time) ->
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
fun (I) when is_integer(I), I > 0 -> I end;
|
||||||
mod_opt_type(cache_size) ->
|
mod_opt_type(cache_size) ->
|
||||||
fun (I) when is_integer(I), I > 0 -> I end;
|
fun (I) when is_integer(I), I > 0 -> I end;
|
||||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
mod_opt_type(db_type) ->
|
||||||
|
fun(odbc) -> odbc;
|
||||||
|
(internal) -> mnesia;
|
||||||
|
(mnesia) -> mnesia
|
||||||
|
end;
|
||||||
mod_opt_type(default) ->
|
mod_opt_type(default) ->
|
||||||
fun (always) -> always;
|
fun (always) -> always;
|
||||||
(never) -> never;
|
(never) -> never;
|
||||||
|
|
|
@ -0,0 +1,178 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_mam_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_mam).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
||||||
|
extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]).
|
||||||
|
|
||||||
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_mam.hrl").
|
||||||
|
|
||||||
|
-define(BIN_GREATER_THAN(A, B),
|
||||||
|
((A > B andalso byte_size(A) == byte_size(B))
|
||||||
|
orelse byte_size(A) > byte_size(B))).
|
||||||
|
-define(BIN_LESS_THAN(A, B),
|
||||||
|
((A < B andalso byte_size(A) == byte_size(B))
|
||||||
|
orelse byte_size(A) < byte_size(B))).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(archive_msg,
|
||||||
|
[{disc_only_copies, [node()]},
|
||||||
|
{type, bag},
|
||||||
|
{attributes, record_info(fields, archive_msg)}]),
|
||||||
|
mnesia:create_table(archive_prefs,
|
||||||
|
[{disc_only_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, archive_prefs)}]).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:delete({archive_msg, US}),
|
||||||
|
mnesia:delete({archive_prefs, US})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
remove_room(_LServer, LName, LHost) ->
|
||||||
|
remove_user(LName, LHost).
|
||||||
|
|
||||||
|
delete_old_messages(global, TimeStamp, Type) ->
|
||||||
|
MS = ets:fun2ms(fun(#archive_msg{timestamp = MsgTS,
|
||||||
|
type = MsgType} = Msg)
|
||||||
|
when MsgTS < TimeStamp,
|
||||||
|
MsgType == Type orelse Type == all ->
|
||||||
|
Msg
|
||||||
|
end),
|
||||||
|
OldMsgs = mnesia:dirty_select(archive_msg, MS),
|
||||||
|
lists:foreach(fun(Rec) ->
|
||||||
|
ok = mnesia:dirty_delete_object(Rec)
|
||||||
|
end, OldMsgs).
|
||||||
|
|
||||||
|
extended_fields() ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
store(Pkt, _, {LUser, LServer}, Type, Peer, Nick, _Dir) ->
|
||||||
|
LPeer = {PUser, PServer, _} = jid:tolower(Peer),
|
||||||
|
TS = p1_time_compat:timestamp(),
|
||||||
|
ID = jlib:integer_to_binary(now_to_usec(TS)),
|
||||||
|
case mnesia:dirty_write(
|
||||||
|
#archive_msg{us = {LUser, LServer},
|
||||||
|
id = ID,
|
||||||
|
timestamp = TS,
|
||||||
|
peer = LPeer,
|
||||||
|
bare_peer = {PUser, PServer, <<>>},
|
||||||
|
type = Type,
|
||||||
|
nick = Nick,
|
||||||
|
packet = Pkt}) of
|
||||||
|
ok ->
|
||||||
|
{ok, ID};
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
write_prefs(_LUser, _LServer, Prefs, _ServerHost) ->
|
||||||
|
mnesia:dirty_write(Prefs).
|
||||||
|
|
||||||
|
get_prefs(LUser, LServer) ->
|
||||||
|
case mnesia:dirty_read(archive_prefs, {LUser, LServer}) of
|
||||||
|
[Prefs] ->
|
||||||
|
{ok, Prefs};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
select(_LServer, JidRequestor,
|
||||||
|
#jid{luser = LUser, lserver = LServer} = JidArchive,
|
||||||
|
Start, End, With, RSM, MsgType) ->
|
||||||
|
MS = make_matchspec(LUser, LServer, Start, End, With),
|
||||||
|
Msgs = mnesia:dirty_select(archive_msg, MS),
|
||||||
|
SortedMsgs = lists:keysort(#archive_msg.timestamp, Msgs),
|
||||||
|
{FilteredMsgs, IsComplete} = filter_by_rsm(SortedMsgs, RSM),
|
||||||
|
Count = length(Msgs),
|
||||||
|
{lists:map(
|
||||||
|
fun(Msg) ->
|
||||||
|
{Msg#archive_msg.id,
|
||||||
|
jlib:binary_to_integer(Msg#archive_msg.id),
|
||||||
|
mod_mam:msg_to_el(Msg, MsgType, JidRequestor, JidArchive)}
|
||||||
|
end, FilteredMsgs), IsComplete, Count}.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
now_to_usec({MSec, Sec, USec}) ->
|
||||||
|
(MSec*1000000 + Sec)*1000000 + USec.
|
||||||
|
|
||||||
|
make_matchspec(LUser, LServer, Start, End, {_, _, <<>>} = With) ->
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#archive_msg{timestamp = TS,
|
||||||
|
us = US,
|
||||||
|
bare_peer = BPeer} = Msg)
|
||||||
|
when Start =< TS, End >= TS,
|
||||||
|
US == {LUser, LServer},
|
||||||
|
BPeer == With ->
|
||||||
|
Msg
|
||||||
|
end);
|
||||||
|
make_matchspec(LUser, LServer, Start, End, {_, _, _} = With) ->
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#archive_msg{timestamp = TS,
|
||||||
|
us = US,
|
||||||
|
peer = Peer} = Msg)
|
||||||
|
when Start =< TS, End >= TS,
|
||||||
|
US == {LUser, LServer},
|
||||||
|
Peer == With ->
|
||||||
|
Msg
|
||||||
|
end);
|
||||||
|
make_matchspec(LUser, LServer, Start, End, none) ->
|
||||||
|
ets:fun2ms(
|
||||||
|
fun(#archive_msg{timestamp = TS,
|
||||||
|
us = US,
|
||||||
|
peer = Peer} = Msg)
|
||||||
|
when Start =< TS, End >= TS,
|
||||||
|
US == {LUser, LServer} ->
|
||||||
|
Msg
|
||||||
|
end).
|
||||||
|
|
||||||
|
filter_by_rsm(Msgs, none) ->
|
||||||
|
{Msgs, true};
|
||||||
|
filter_by_rsm(_Msgs, #rsm_in{max = Max}) when Max < 0 ->
|
||||||
|
{[], true};
|
||||||
|
filter_by_rsm(Msgs, #rsm_in{max = Max, direction = Direction, id = ID}) ->
|
||||||
|
NewMsgs = case Direction of
|
||||||
|
aft when ID /= <<"">> ->
|
||||||
|
lists:filter(
|
||||||
|
fun(#archive_msg{id = I}) ->
|
||||||
|
?BIN_GREATER_THAN(I, ID)
|
||||||
|
end, Msgs);
|
||||||
|
before when ID /= <<"">> ->
|
||||||
|
lists:foldl(
|
||||||
|
fun(#archive_msg{id = I} = Msg, Acc)
|
||||||
|
when ?BIN_LESS_THAN(I, ID) ->
|
||||||
|
[Msg|Acc];
|
||||||
|
(_, Acc) ->
|
||||||
|
Acc
|
||||||
|
end, [], Msgs);
|
||||||
|
before when ID == <<"">> ->
|
||||||
|
lists:reverse(Msgs);
|
||||||
|
_ ->
|
||||||
|
Msgs
|
||||||
|
end,
|
||||||
|
filter_by_max(NewMsgs, Max).
|
||||||
|
|
||||||
|
filter_by_max(Msgs, undefined) ->
|
||||||
|
{Msgs, true};
|
||||||
|
filter_by_max(Msgs, Len) when is_integer(Len), Len >= 0 ->
|
||||||
|
{lists:sublist(Msgs, Len), length(Msgs) =< Len};
|
||||||
|
filter_by_max(_Msgs, _Junk) ->
|
||||||
|
{[], true}.
|
|
@ -0,0 +1,309 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_mam_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_mam).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, remove_user/2, remove_room/3, delete_old_messages/3,
|
||||||
|
extended_fields/0, store/7, write_prefs/4, get_prefs/2, select/8]).
|
||||||
|
|
||||||
|
-include_lib("stdlib/include/ms_transform.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_mam.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
SUser = ejabberd_odbc:escape(LUser),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"delete from archive where username='">>, SUser, <<"';">>]),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"delete from archive_prefs where username='">>, SUser, <<"';">>]).
|
||||||
|
|
||||||
|
remove_room(LServer, LName, LHost) ->
|
||||||
|
LUser = jid:to_string({LName, LHost, <<>>}),
|
||||||
|
remove_user(LUser, LServer).
|
||||||
|
|
||||||
|
delete_old_messages(ServerHost, TimeStamp, Type) ->
|
||||||
|
TypeClause = if Type == all -> <<"">>;
|
||||||
|
true -> [<<" and kind='">>, jlib:atom_to_binary(Type), <<"'">>]
|
||||||
|
end,
|
||||||
|
TS = integer_to_binary(now_to_usec(TimeStamp)),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
ServerHost, [<<"delete from archive where timestamp<">>,
|
||||||
|
TS, TypeClause, <<";">>]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
extended_fields() ->
|
||||||
|
[#xmlel{name = <<"field">>,
|
||||||
|
attrs = [{<<"type">>, <<"text-single">>},
|
||||||
|
{<<"var">>, <<"withtext">>}]}].
|
||||||
|
|
||||||
|
store(Pkt, LServer, {LUser, LHost}, Type, Peer, Nick, _Dir) ->
|
||||||
|
TSinteger = p1_time_compat:system_time(micro_seconds),
|
||||||
|
ID = TS = jlib:integer_to_binary(TSinteger),
|
||||||
|
SUser = case Type of
|
||||||
|
chat -> LUser;
|
||||||
|
groupchat -> jid:to_string({LUser, LHost, <<>>})
|
||||||
|
end,
|
||||||
|
BarePeer = jid:to_string(
|
||||||
|
jid:tolower(
|
||||||
|
jid:remove_resource(Peer))),
|
||||||
|
LPeer = jid:to_string(
|
||||||
|
jid:tolower(Peer)),
|
||||||
|
XML = fxml:element_to_binary(Pkt),
|
||||||
|
Body = fxml:get_subtag_cdata(Pkt, <<"body">>),
|
||||||
|
case ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"insert into archive (username, timestamp, "
|
||||||
|
"peer, bare_peer, xml, txt, kind, nick) values (">>,
|
||||||
|
<<"'">>, ejabberd_odbc:escape(SUser), <<"', ">>,
|
||||||
|
<<"'">>, TS, <<"', ">>,
|
||||||
|
<<"'">>, ejabberd_odbc:escape(LPeer), <<"', ">>,
|
||||||
|
<<"'">>, ejabberd_odbc:escape(BarePeer), <<"', ">>,
|
||||||
|
<<"'">>, ejabberd_odbc:escape(XML), <<"', ">>,
|
||||||
|
<<"'">>, ejabberd_odbc:escape(Body), <<"', ">>,
|
||||||
|
<<"'">>, jlib:atom_to_binary(Type), <<"', ">>,
|
||||||
|
<<"'">>, ejabberd_odbc:escape(Nick), <<"');">>]) of
|
||||||
|
{updated, _} ->
|
||||||
|
{ok, ID};
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
write_prefs(LUser, _LServer, #archive_prefs{default = Default,
|
||||||
|
never = Never,
|
||||||
|
always = Always},
|
||||||
|
ServerHost) ->
|
||||||
|
SUser = ejabberd_odbc:escape(LUser),
|
||||||
|
SDefault = erlang:atom_to_binary(Default, utf8),
|
||||||
|
SAlways = ejabberd_odbc:encode_term(Always),
|
||||||
|
SNever = ejabberd_odbc:encode_term(Never),
|
||||||
|
case update(ServerHost, <<"archive_prefs">>,
|
||||||
|
[<<"username">>, <<"def">>, <<"always">>, <<"never">>],
|
||||||
|
[SUser, SDefault, SAlways, SNever],
|
||||||
|
[<<"username='">>, SUser, <<"'">>]) of
|
||||||
|
{updated, _} ->
|
||||||
|
ok;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_prefs(LUser, LServer) ->
|
||||||
|
case ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"select def, always, never from archive_prefs ">>,
|
||||||
|
<<"where username='">>,
|
||||||
|
ejabberd_odbc:escape(LUser), <<"';">>]) of
|
||||||
|
{selected, _, [[SDefault, SAlways, SNever]]} ->
|
||||||
|
Default = erlang:binary_to_existing_atom(SDefault, utf8),
|
||||||
|
Always = ejabberd_odbc:decode_term(SAlways),
|
||||||
|
Never = ejabberd_odbc:decode_term(SNever),
|
||||||
|
{ok, #archive_prefs{us = {LUser, LServer},
|
||||||
|
default = Default,
|
||||||
|
always = Always,
|
||||||
|
never = Never}};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
select(LServer, JidRequestor, #jid{luser = LUser} = JidArchive,
|
||||||
|
Start, End, With, RSM, MsgType) ->
|
||||||
|
User = case MsgType of
|
||||||
|
chat -> LUser;
|
||||||
|
{groupchat, _Role, _MUCState} -> jid:to_string(JidArchive)
|
||||||
|
end,
|
||||||
|
{Query, CountQuery} = make_sql_query(User, LServer,
|
||||||
|
Start, End, With, RSM),
|
||||||
|
% TODO from XEP-0313 v0.2: "To conserve resources, a server MAY place a
|
||||||
|
% reasonable limit on how many stanzas may be pushed to a client in one
|
||||||
|
% request. If a query returns a number of stanzas greater than this limit
|
||||||
|
% and the client did not specify a limit using RSM then the server should
|
||||||
|
% return a policy-violation error to the client." We currently don't do this
|
||||||
|
% for v0.2 requests, but we do limit #rsm_in.max for v0.3 and newer.
|
||||||
|
case {ejabberd_odbc:sql_query(LServer, Query),
|
||||||
|
ejabberd_odbc:sql_query(LServer, CountQuery)} of
|
||||||
|
{{selected, _, Res}, {selected, _, [[Count]]}} ->
|
||||||
|
{Max, Direction} = case RSM of
|
||||||
|
#rsm_in{max = M, direction = D} -> {M, D};
|
||||||
|
_ -> {undefined, undefined}
|
||||||
|
end,
|
||||||
|
{Res1, IsComplete} =
|
||||||
|
if Max >= 0 andalso Max /= undefined andalso length(Res) > Max ->
|
||||||
|
if Direction == before ->
|
||||||
|
{lists:nthtail(1, Res), false};
|
||||||
|
true ->
|
||||||
|
{lists:sublist(Res, Max), false}
|
||||||
|
end;
|
||||||
|
true ->
|
||||||
|
{Res, true}
|
||||||
|
end,
|
||||||
|
{lists:flatmap(
|
||||||
|
fun([TS, XML, PeerBin, Kind, Nick]) ->
|
||||||
|
try
|
||||||
|
#xmlel{} = El = fxml_stream:parse_element(XML),
|
||||||
|
Now = usec_to_now(jlib:binary_to_integer(TS)),
|
||||||
|
PeerJid = jid:tolower(jid:from_string(PeerBin)),
|
||||||
|
T = case Kind of
|
||||||
|
<<"">> -> chat;
|
||||||
|
null -> chat;
|
||||||
|
_ -> jlib:binary_to_atom(Kind)
|
||||||
|
end,
|
||||||
|
[{TS, jlib:binary_to_integer(TS),
|
||||||
|
mod_mam:msg_to_el(#archive_msg{timestamp = Now,
|
||||||
|
packet = El,
|
||||||
|
type = T,
|
||||||
|
nick = Nick,
|
||||||
|
peer = PeerJid},
|
||||||
|
MsgType, JidRequestor, JidArchive)}]
|
||||||
|
catch _:Err ->
|
||||||
|
?ERROR_MSG("failed to parse data from SQL: ~p. "
|
||||||
|
"The data was: "
|
||||||
|
"timestamp = ~s, xml = ~s, "
|
||||||
|
"peer = ~s, kind = ~s, nick = ~s",
|
||||||
|
[Err, TS, XML, PeerBin, Kind, Nick]),
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end, Res1), IsComplete, jlib:binary_to_integer(Count)};
|
||||||
|
_ ->
|
||||||
|
{[], false, 0}
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
now_to_usec({MSec, Sec, USec}) ->
|
||||||
|
(MSec*1000000 + Sec)*1000000 + USec.
|
||||||
|
|
||||||
|
usec_to_now(Int) ->
|
||||||
|
Secs = Int div 1000000,
|
||||||
|
USec = Int rem 1000000,
|
||||||
|
MSec = Secs div 1000000,
|
||||||
|
Sec = Secs rem 1000000,
|
||||||
|
{MSec, Sec, USec}.
|
||||||
|
|
||||||
|
make_sql_query(User, LServer, Start, End, With, RSM) ->
|
||||||
|
{Max, Direction, ID} = case RSM of
|
||||||
|
#rsm_in{} ->
|
||||||
|
{RSM#rsm_in.max,
|
||||||
|
RSM#rsm_in.direction,
|
||||||
|
RSM#rsm_in.id};
|
||||||
|
none ->
|
||||||
|
{none, none, <<>>}
|
||||||
|
end,
|
||||||
|
ODBCType = ejabberd_config:get_option(
|
||||||
|
{odbc_type, LServer},
|
||||||
|
ejabberd_odbc:opt_type(odbc_type)),
|
||||||
|
LimitClause = if is_integer(Max), Max >= 0, ODBCType /= mssql ->
|
||||||
|
[<<" limit ">>, jlib:integer_to_binary(Max+1)];
|
||||||
|
true ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
TopClause = if is_integer(Max), Max >= 0, ODBCType == mssql ->
|
||||||
|
[<<" TOP ">>, jlib:integer_to_binary(Max+1)];
|
||||||
|
true ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
WithClause = case With of
|
||||||
|
{text, <<>>} ->
|
||||||
|
[];
|
||||||
|
{text, Txt} ->
|
||||||
|
[<<" and match (txt) against ('">>,
|
||||||
|
ejabberd_odbc:escape(Txt), <<"')">>];
|
||||||
|
{_, _, <<>>} ->
|
||||||
|
[<<" and bare_peer='">>,
|
||||||
|
ejabberd_odbc:escape(jid:to_string(With)),
|
||||||
|
<<"'">>];
|
||||||
|
{_, _, _} ->
|
||||||
|
[<<" and peer='">>,
|
||||||
|
ejabberd_odbc:escape(jid:to_string(With)),
|
||||||
|
<<"'">>];
|
||||||
|
none ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
PageClause = case catch jlib:binary_to_integer(ID) of
|
||||||
|
I when is_integer(I), I >= 0 ->
|
||||||
|
case Direction of
|
||||||
|
before ->
|
||||||
|
[<<" AND timestamp < ">>, ID];
|
||||||
|
aft ->
|
||||||
|
[<<" AND timestamp > ">>, ID];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
StartClause = case Start of
|
||||||
|
{_, _, _} ->
|
||||||
|
[<<" and timestamp >= ">>,
|
||||||
|
jlib:integer_to_binary(now_to_usec(Start))];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
EndClause = case End of
|
||||||
|
{_, _, _} ->
|
||||||
|
[<<" and timestamp <= ">>,
|
||||||
|
jlib:integer_to_binary(now_to_usec(End))];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
SUser = ejabberd_odbc:escape(User),
|
||||||
|
|
||||||
|
Query = [<<"SELECT ">>, TopClause, <<" timestamp, xml, peer, kind, nick"
|
||||||
|
" FROM archive WHERE username='">>,
|
||||||
|
SUser, <<"'">>, WithClause, StartClause, EndClause,
|
||||||
|
PageClause],
|
||||||
|
|
||||||
|
QueryPage =
|
||||||
|
case Direction of
|
||||||
|
before ->
|
||||||
|
% ID can be empty because of
|
||||||
|
% XEP-0059: Result Set Management
|
||||||
|
% 2.5 Requesting the Last Page in a Result Set
|
||||||
|
[<<"SELECT timestamp, xml, peer, kind, nick FROM (">>, Query,
|
||||||
|
<<" ORDER BY timestamp DESC ">>,
|
||||||
|
LimitClause, <<") AS t ORDER BY timestamp ASC;">>];
|
||||||
|
_ ->
|
||||||
|
[Query, <<" ORDER BY timestamp ASC ">>,
|
||||||
|
LimitClause, <<";">>]
|
||||||
|
end,
|
||||||
|
{QueryPage,
|
||||||
|
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
|
||||||
|
SUser, <<"'">>, WithClause, StartClause, EndClause, <<";">>]}.
|
||||||
|
|
||||||
|
update(LServer, Table, Fields, Vals, Where) ->
|
||||||
|
UPairs = lists:zipwith(fun (A, B) ->
|
||||||
|
<<A/binary, "='", B/binary, "'">>
|
||||||
|
end,
|
||||||
|
Fields, Vals),
|
||||||
|
case ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"update ">>, Table, <<" set ">>,
|
||||||
|
join(UPairs, <<", ">>), <<" where ">>, Where,
|
||||||
|
<<";">>])
|
||||||
|
of
|
||||||
|
{updated, 1} -> {updated, 1};
|
||||||
|
_ ->
|
||||||
|
ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"insert into ">>, Table, <<"(">>,
|
||||||
|
join(Fields, <<", ">>), <<") values ('">>,
|
||||||
|
join(Vals, <<"', '">>), <<"');">>])
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% Almost a copy of string:join/2.
|
||||||
|
join([], _Sep) -> [];
|
||||||
|
join([H | T], Sep) -> [H, [[Sep, X] || X <- T]].
|
457
src/mod_muc.erl
457
src/mod_muc.erl
|
@ -48,6 +48,7 @@
|
||||||
export/1,
|
export/1,
|
||||||
import/1,
|
import/1,
|
||||||
import/3,
|
import/3,
|
||||||
|
opts_to_binary/1,
|
||||||
can_use_nick/4]).
|
can_use_nick/4]).
|
||||||
|
|
||||||
-export([init/1, handle_call/3, handle_cast/2,
|
-export([init/1, handle_call/3, handle_cast/2,
|
||||||
|
@ -72,6 +73,17 @@
|
||||||
|
|
||||||
-define(MAX_ROOMS_DISCOITEMS, 100).
|
-define(MAX_ROOMS_DISCOITEMS, 100).
|
||||||
|
|
||||||
|
-type muc_room_opts() :: [{atom(), any()}].
|
||||||
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
|
-callback import(binary(), #muc_room{} | #muc_registered{}) -> ok | pass.
|
||||||
|
-callback store_room(binary(), binary(), binary(), list()) -> {atomic, any()}.
|
||||||
|
-callback restore_room(binary(), binary(), binary()) -> muc_room_opts() | error.
|
||||||
|
-callback forget_room(binary(), binary(), binary()) -> {atomic, any()}.
|
||||||
|
-callback can_use_nick(binary(), binary(), jid(), binary()) -> boolean().
|
||||||
|
-callback get_rooms(binary(), binary()) -> [#muc_room{}].
|
||||||
|
-callback get_nick(binary(), binary(), jid()) -> binary() | error.
|
||||||
|
-callback set_nick(binary(), binary(), jid(), binary()) -> {atomic, ok | false}.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% API
|
%% API
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
@ -125,96 +137,24 @@ create_room(Host, Name, From, Nick, Opts) ->
|
||||||
|
|
||||||
store_room(ServerHost, Host, Name, Opts) ->
|
store_room(ServerHost, Host, Name, Opts) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
store_room(LServer, Host, Name, Opts,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:store_room(LServer, Host, Name, Opts).
|
||||||
|
|
||||||
store_room(_LServer, Host, Name, Opts, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
mnesia:write(#muc_room{name_host = {Name, Host},
|
|
||||||
opts = Opts})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
store_room(_LServer, Host, Name, Opts, riak) ->
|
|
||||||
{atomic, ejabberd_riak:put(#muc_room{name_host = {Name, Host},
|
|
||||||
opts = Opts},
|
|
||||||
muc_room_schema())};
|
|
||||||
store_room(LServer, Host, Name, Opts, odbc) ->
|
|
||||||
SName = ejabberd_odbc:escape(Name),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
SOpts = ejabberd_odbc:encode_term(Opts),
|
|
||||||
F = fun () ->
|
|
||||||
odbc_queries:update_t(<<"muc_room">>,
|
|
||||||
[<<"name">>, <<"host">>, <<"opts">>],
|
|
||||||
[SName, SHost, SOpts],
|
|
||||||
[<<"name='">>, SName, <<"' and host='">>,
|
|
||||||
SHost, <<"'">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
restore_room(ServerHost, Host, Name) ->
|
restore_room(ServerHost, Host, Name) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
restore_room(LServer, Host, Name,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:restore_room(LServer, Host, Name).
|
||||||
|
|
||||||
restore_room(_LServer, Host, Name, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
|
|
||||||
[#muc_room{opts = Opts}] -> Opts;
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
restore_room(_LServer, Host, Name, riak) ->
|
|
||||||
case ejabberd_riak:get(muc_room, muc_room_schema(), {Name, Host}) of
|
|
||||||
{ok, #muc_room{opts = Opts}} -> Opts;
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
restore_room(LServer, Host, Name, odbc) ->
|
|
||||||
SName = ejabberd_odbc:escape(Name),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
case catch ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select opts from muc_room where name='">>,
|
|
||||||
SName, <<"' and host='">>, SHost,
|
|
||||||
<<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"opts">>], [[Opts]]} ->
|
|
||||||
opts_to_binary(ejabberd_odbc:decode_term(Opts));
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
forget_room(ServerHost, Host, Name) ->
|
forget_room(ServerHost, Host, Name) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
forget_room(LServer, Host, Name,
|
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
|
||||||
|
|
||||||
forget_room(LServer, Host, Name, mnesia) ->
|
|
||||||
remove_room_mam(LServer, Host, Name),
|
remove_room_mam(LServer, Host, Name),
|
||||||
F = fun () -> mnesia:delete({muc_room, {Name, Host}})
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
end,
|
Mod:forget_room(LServer, Host, Name).
|
||||||
mnesia:transaction(F);
|
|
||||||
forget_room(LServer, Host, Name, riak) ->
|
|
||||||
remove_room_mam(LServer, Host, Name),
|
|
||||||
{atomic, ejabberd_riak:delete(muc_room, {Name, Host})};
|
|
||||||
forget_room(LServer, Host, Name, odbc) ->
|
|
||||||
remove_room_mam(LServer, Host, Name),
|
|
||||||
SName = ejabberd_odbc:escape(Name),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
F = fun () ->
|
|
||||||
ejabberd_odbc:sql_query_t([<<"delete from muc_room where name='">>,
|
|
||||||
SName, <<"' and host='">>, SHost,
|
|
||||||
<<"';">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
remove_room_mam(LServer, Host, Name) ->
|
remove_room_mam(LServer, Host, Name) ->
|
||||||
case gen_mod:is_loaded(LServer, mod_mam) of
|
case gen_mod:is_loaded(LServer, mod_mam) of
|
||||||
true ->
|
true ->
|
||||||
U = jid:nodeprep(Name),
|
mod_mam:remove_room(LServer, Name, Host);
|
||||||
S = jid:nameprep(Host),
|
|
||||||
DBType = gen_mod:db_type(LServer, mod_mam),
|
|
||||||
if DBType == odbc ->
|
|
||||||
mod_mam:remove_user(jid:to_string({U, S, <<>>}),
|
|
||||||
LServer, DBType);
|
|
||||||
true ->
|
|
||||||
mod_mam:remove_user(U, S, DBType)
|
|
||||||
end;
|
|
||||||
false ->
|
false ->
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
@ -233,48 +173,8 @@ process_iq_disco_items(Host, From, To,
|
||||||
can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false;
|
can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false;
|
||||||
can_use_nick(ServerHost, Host, JID, Nick) ->
|
can_use_nick(ServerHost, Host, JID, Nick) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
can_use_nick(LServer, Host, JID, Nick,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:can_use_nick(LServer, Host, JID, Nick).
|
||||||
|
|
||||||
can_use_nick(_LServer, Host, JID, Nick, mnesia) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(JID),
|
|
||||||
LUS = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_select(muc_registered,
|
|
||||||
[{#muc_registered{us_host = '$1',
|
|
||||||
nick = Nick, _ = '_'},
|
|
||||||
[{'==', {element, 2, '$1'}, Host}],
|
|
||||||
['$_']}])
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> true;
|
|
||||||
[] -> true;
|
|
||||||
[#muc_registered{us_host = {U, _Host}}] -> U == LUS
|
|
||||||
end;
|
|
||||||
can_use_nick(LServer, Host, JID, Nick, riak) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(JID),
|
|
||||||
LUS = {LUser, LServer},
|
|
||||||
case ejabberd_riak:get_by_index(muc_registered,
|
|
||||||
muc_registered_schema(),
|
|
||||||
<<"nick_host">>, {Nick, Host}) of
|
|
||||||
{ok, []} ->
|
|
||||||
true;
|
|
||||||
{ok, [#muc_registered{us_host = {U, _Host}}]} ->
|
|
||||||
U == LUS;
|
|
||||||
{error, _} ->
|
|
||||||
true
|
|
||||||
end;
|
|
||||||
can_use_nick(LServer, Host, JID, Nick, odbc) ->
|
|
||||||
SJID =
|
|
||||||
jid:to_string(jid:tolower(jid:remove_resource(JID))),
|
|
||||||
SNick = ejabberd_odbc:escape(Nick),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
case catch ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select jid from muc_registered ">>,
|
|
||||||
<<"where nick='">>, SNick,
|
|
||||||
<<"' and host='">>, SHost, <<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"jid">>], [[SJID1]]} -> SJID == SJID1;
|
|
||||||
_ -> true
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_server callbacks
|
%% gen_server callbacks
|
||||||
|
@ -283,21 +183,8 @@ can_use_nick(LServer, Host, JID, Nick, odbc) ->
|
||||||
init([Host, Opts]) ->
|
init([Host, Opts]) ->
|
||||||
MyHost = gen_mod:get_opt_host(Host, Opts,
|
MyHost = gen_mod:get_opt_host(Host, Opts,
|
||||||
<<"conference.@HOST@">>),
|
<<"conference.@HOST@">>),
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, [{host, MyHost}|Opts]),
|
||||||
mnesia:create_table(muc_room,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, muc_room)}]),
|
|
||||||
mnesia:create_table(muc_registered,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, muc_registered)}]),
|
|
||||||
update_tables(MyHost),
|
|
||||||
mnesia:add_table_index(muc_registered, nick);
|
|
||||||
_ ->
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
mnesia:create_table(muc_online_room,
|
mnesia:create_table(muc_online_room,
|
||||||
[{ram_copies, [node()]},
|
[{ram_copies, [node()]},
|
||||||
{attributes, record_info(fields, muc_online_room)}]),
|
{attributes, record_info(fields, muc_online_room)}]),
|
||||||
|
@ -647,43 +534,8 @@ check_create_roomid(ServerHost, RoomID) ->
|
||||||
|
|
||||||
get_rooms(ServerHost, Host) ->
|
get_rooms(ServerHost, Host) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
get_rooms(LServer, Host,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_rooms(LServer, Host).
|
||||||
|
|
||||||
get_rooms(_LServer, Host, mnesia) ->
|
|
||||||
case catch mnesia:dirty_select(muc_room,
|
|
||||||
[{#muc_room{name_host = {'_', Host},
|
|
||||||
_ = '_'},
|
|
||||||
[], ['$_']}])
|
|
||||||
of
|
|
||||||
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), [];
|
|
||||||
Rs -> Rs
|
|
||||||
end;
|
|
||||||
get_rooms(_LServer, Host, riak) ->
|
|
||||||
case ejabberd_riak:get(muc_room, muc_room_schema()) of
|
|
||||||
{ok, Rs} ->
|
|
||||||
lists:filter(
|
|
||||||
fun(#muc_room{name_host = {_, H}}) ->
|
|
||||||
Host == H
|
|
||||||
end, Rs);
|
|
||||||
_Err ->
|
|
||||||
[]
|
|
||||||
end;
|
|
||||||
get_rooms(LServer, Host, odbc) ->
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
case catch ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select name, opts from muc_room ">>,
|
|
||||||
<<"where host='">>, SHost, <<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"name">>, <<"opts">>], RoomOpts} ->
|
|
||||||
lists:map(fun ([Room, Opts]) ->
|
|
||||||
#muc_room{name_host = {Room, Host},
|
|
||||||
opts = opts_to_binary(
|
|
||||||
ejabberd_odbc:decode_term(Opts))}
|
|
||||||
end,
|
|
||||||
RoomOpts);
|
|
||||||
Err -> ?ERROR_MSG("failed to get rooms: ~p", [Err]), []
|
|
||||||
end.
|
|
||||||
|
|
||||||
load_permanent_rooms(Host, ServerHost, Access,
|
load_permanent_rooms(Host, ServerHost, Access,
|
||||||
HistorySize, RoomShaper) ->
|
HistorySize, RoomShaper) ->
|
||||||
|
@ -873,41 +725,8 @@ iq_get_unique(From) ->
|
||||||
|
|
||||||
get_nick(ServerHost, Host, From) ->
|
get_nick(ServerHost, Host, From) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
get_nick(LServer, Host, From,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_nick(LServer, Host, From).
|
||||||
|
|
||||||
get_nick(_LServer, Host, From, mnesia) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(From),
|
|
||||||
LUS = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_read(muc_registered,
|
|
||||||
{LUS, Host})
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> error;
|
|
||||||
[#muc_registered{nick = Nick}] -> Nick
|
|
||||||
end;
|
|
||||||
get_nick(LServer, Host, From, riak) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(From),
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case ejabberd_riak:get(muc_registered,
|
|
||||||
muc_registered_schema(),
|
|
||||||
{US, Host}) of
|
|
||||||
{ok, #muc_registered{nick = Nick}} -> Nick;
|
|
||||||
{error, _} -> error
|
|
||||||
end;
|
|
||||||
get_nick(LServer, Host, From, odbc) ->
|
|
||||||
SJID =
|
|
||||||
ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:remove_resource(From)))),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
case catch ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select nick from muc_registered where "
|
|
||||||
"jid='">>,
|
|
||||||
SJID, <<"' and host='">>, SHost,
|
|
||||||
<<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"nick">>], [[Nick]]} -> Nick;
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||||
{Nick, Registered} = case get_nick(ServerHost, Host,
|
{Nick, Registered} = case get_nick(ServerHost, Host,
|
||||||
|
@ -946,107 +765,8 @@ iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||||
|
|
||||||
set_nick(ServerHost, Host, From, Nick) ->
|
set_nick(ServerHost, Host, From, Nick) ->
|
||||||
LServer = jid:nameprep(ServerHost),
|
LServer = jid:nameprep(ServerHost),
|
||||||
set_nick(LServer, Host, From, Nick,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:set_nick(LServer, Host, From, Nick).
|
||||||
|
|
||||||
set_nick(_LServer, Host, From, Nick, mnesia) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(From),
|
|
||||||
LUS = {LUser, LServer},
|
|
||||||
F = fun () ->
|
|
||||||
case Nick of
|
|
||||||
<<"">> ->
|
|
||||||
mnesia:delete({muc_registered, {LUS, Host}}), ok;
|
|
||||||
_ ->
|
|
||||||
Allow = case mnesia:select(muc_registered,
|
|
||||||
[{#muc_registered{us_host =
|
|
||||||
'$1',
|
|
||||||
nick = Nick,
|
|
||||||
_ = '_'},
|
|
||||||
[{'==', {element, 2, '$1'},
|
|
||||||
Host}],
|
|
||||||
['$_']}])
|
|
||||||
of
|
|
||||||
[] -> true;
|
|
||||||
[#muc_registered{us_host = {U, _Host}}] ->
|
|
||||||
U == LUS
|
|
||||||
end,
|
|
||||||
if Allow ->
|
|
||||||
mnesia:write(#muc_registered{us_host = {LUS, Host},
|
|
||||||
nick = Nick}),
|
|
||||||
ok;
|
|
||||||
true -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
set_nick(LServer, Host, From, Nick, riak) ->
|
|
||||||
{LUser, LServer, _} = jid:tolower(From),
|
|
||||||
LUS = {LUser, LServer},
|
|
||||||
{atomic,
|
|
||||||
case Nick of
|
|
||||||
<<"">> ->
|
|
||||||
ejabberd_riak:delete(muc_registered, {LUS, Host});
|
|
||||||
_ ->
|
|
||||||
Allow = case ejabberd_riak:get_by_index(
|
|
||||||
muc_registered,
|
|
||||||
muc_registered_schema(),
|
|
||||||
<<"nick_host">>, {Nick, Host}) of
|
|
||||||
{ok, []} ->
|
|
||||||
true;
|
|
||||||
{ok, [#muc_registered{us_host = {U, _Host}}]} ->
|
|
||||||
U == LUS;
|
|
||||||
{error, _} ->
|
|
||||||
false
|
|
||||||
end,
|
|
||||||
if Allow ->
|
|
||||||
ejabberd_riak:put(#muc_registered{us_host = {LUS, Host},
|
|
||||||
nick = Nick},
|
|
||||||
muc_registered_schema(),
|
|
||||||
[{'2i', [{<<"nick_host">>,
|
|
||||||
{Nick, Host}}]}]);
|
|
||||||
true ->
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end};
|
|
||||||
set_nick(LServer, Host, From, Nick, odbc) ->
|
|
||||||
JID =
|
|
||||||
jid:to_string(jid:tolower(jid:remove_resource(From))),
|
|
||||||
SJID = ejabberd_odbc:escape(JID),
|
|
||||||
SNick = ejabberd_odbc:escape(Nick),
|
|
||||||
SHost = ejabberd_odbc:escape(Host),
|
|
||||||
F = fun () ->
|
|
||||||
case Nick of
|
|
||||||
<<"">> ->
|
|
||||||
ejabberd_odbc:sql_query_t([<<"delete from muc_registered where ">>,
|
|
||||||
<<"jid='">>, SJID,
|
|
||||||
<<"' and host='">>, Host,
|
|
||||||
<<"';">>]),
|
|
||||||
ok;
|
|
||||||
_ ->
|
|
||||||
Allow = case
|
|
||||||
ejabberd_odbc:sql_query_t([<<"select jid from muc_registered ">>,
|
|
||||||
<<"where nick='">>,
|
|
||||||
SNick,
|
|
||||||
<<"' and host='">>,
|
|
||||||
SHost, <<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"jid">>], [[J]]} -> J == JID;
|
|
||||||
_ -> true
|
|
||||||
end,
|
|
||||||
if Allow ->
|
|
||||||
odbc_queries:update_t(<<"muc_registered">>,
|
|
||||||
[<<"jid">>, <<"host">>,
|
|
||||||
<<"nick">>],
|
|
||||||
[SJID, SHost, SNick],
|
|
||||||
[<<"jid='">>, SJID,
|
|
||||||
<<"' and host='">>, SHost,
|
|
||||||
<<"'">>]),
|
|
||||||
ok;
|
|
||||||
true -> false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
iq_set_register_info(ServerHost, Host, From, Nick,
|
iq_set_register_info(ServerHost, Host, From, Nick,
|
||||||
Lang) ->
|
Lang) ->
|
||||||
|
@ -1192,118 +912,17 @@ opts_to_binary(Opts) ->
|
||||||
Opt
|
Opt
|
||||||
end, Opts).
|
end, Opts).
|
||||||
|
|
||||||
update_tables(Host) ->
|
export(LServer) ->
|
||||||
update_muc_room_table(Host),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
update_muc_registered_table(Host).
|
Mod:export(LServer).
|
||||||
|
|
||||||
muc_room_schema() ->
|
import(LServer) ->
|
||||||
{record_info(fields, muc_room), #muc_room{}}.
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Mod:import(LServer).
|
||||||
|
|
||||||
muc_registered_schema() ->
|
import(LServer, DBType, Data) ->
|
||||||
{record_info(fields, muc_registered), #muc_registered{}}.
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
|
Mod:import(LServer, Data).
|
||||||
update_muc_room_table(_Host) ->
|
|
||||||
Fields = record_info(fields, muc_room),
|
|
||||||
case mnesia:table_info(muc_room, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
muc_room, Fields, set,
|
|
||||||
fun(#muc_room{name_host = {N, _}}) -> N end,
|
|
||||||
fun(#muc_room{name_host = {N, H},
|
|
||||||
opts = Opts} = R) ->
|
|
||||||
R#muc_room{name_host = {iolist_to_binary(N),
|
|
||||||
iolist_to_binary(H)},
|
|
||||||
opts = opts_to_binary(Opts)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating muc_room table", []),
|
|
||||||
mnesia:transform_table(muc_room, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
update_muc_registered_table(_Host) ->
|
|
||||||
Fields = record_info(fields, muc_registered),
|
|
||||||
case mnesia:table_info(muc_registered, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
muc_registered, Fields, set,
|
|
||||||
fun(#muc_registered{us_host = {_, H}}) -> H end,
|
|
||||||
fun(#muc_registered{us_host = {{U, S}, H},
|
|
||||||
nick = Nick} = R) ->
|
|
||||||
R#muc_registered{us_host = {{iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)},
|
|
||||||
iolist_to_binary(H)},
|
|
||||||
nick = iolist_to_binary(Nick)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating muc_registered table", []),
|
|
||||||
mnesia:transform_table(muc_registered, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{muc_room,
|
|
||||||
fun(Host, #muc_room{name_host = {Name, RoomHost}, opts = Opts}) ->
|
|
||||||
case str:suffix(Host, RoomHost) of
|
|
||||||
true ->
|
|
||||||
SName = ejabberd_odbc:escape(Name),
|
|
||||||
SRoomHost = ejabberd_odbc:escape(RoomHost),
|
|
||||||
SOpts = ejabberd_odbc:encode_term(Opts),
|
|
||||||
[[<<"delete from muc_room where name='">>, SName,
|
|
||||||
<<"' and host='">>, SRoomHost, <<"';">>],
|
|
||||||
[<<"insert into muc_room(name, host, opts) ",
|
|
||||||
"values (">>,
|
|
||||||
<<"'">>, SName, <<"', '">>, SRoomHost,
|
|
||||||
<<"', '">>, SOpts, <<"');">>]];
|
|
||||||
false ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end},
|
|
||||||
{muc_registered,
|
|
||||||
fun(Host, #muc_registered{us_host = {{U, S}, RoomHost},
|
|
||||||
nick = Nick}) ->
|
|
||||||
case str:suffix(Host, RoomHost) of
|
|
||||||
true ->
|
|
||||||
SJID = ejabberd_odbc:escape(
|
|
||||||
jid:to_string(
|
|
||||||
jid:make(U, S, <<"">>))),
|
|
||||||
SNick = ejabberd_odbc:escape(Nick),
|
|
||||||
SRoomHost = ejabberd_odbc:escape(RoomHost),
|
|
||||||
[[<<"delete from muc_registered where jid='">>,
|
|
||||||
SJID, <<"' and host='">>, SRoomHost, <<"';">>],
|
|
||||||
[<<"insert into muc_registered(jid, host, "
|
|
||||||
"nick) values ('">>,
|
|
||||||
SJID, <<"', '">>, SRoomHost, <<"', '">>, SNick,
|
|
||||||
<<"');">>]];
|
|
||||||
false ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer) ->
|
|
||||||
[{<<"select name, host, opts from muc_room;">>,
|
|
||||||
fun([Name, RoomHost, SOpts]) ->
|
|
||||||
Opts = opts_to_binary(ejabberd_odbc:decode_term(SOpts)),
|
|
||||||
#muc_room{name_host = {Name, RoomHost}, opts = Opts}
|
|
||||||
end},
|
|
||||||
{<<"select jid, host, nick from muc_registered;">>,
|
|
||||||
fun([J, RoomHost, Nick]) ->
|
|
||||||
#jid{user = U, server = S} =
|
|
||||||
jid:from_string(J),
|
|
||||||
#muc_registered{us_host = {{U, S}, RoomHost},
|
|
||||||
nick = Nick}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #muc_room{} = R) ->
|
|
||||||
mnesia:dirty_write(R);
|
|
||||||
import(_LServer, mnesia, #muc_registered{} = R) ->
|
|
||||||
mnesia:dirty_write(R);
|
|
||||||
import(_LServer, riak, #muc_room{} = R) ->
|
|
||||||
ejabberd_riak:put(R, muc_room_schema());
|
|
||||||
import(_LServer, riak,
|
|
||||||
#muc_registered{us_host = {_, Host}, nick = Nick} = R) ->
|
|
||||||
ejabberd_riak:put(R, muc_registered_schema(),
|
|
||||||
[{'2i', [{<<"nick_host">>, {Nick, Host}}]}]);
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(access) ->
|
mod_opt_type(access) ->
|
||||||
fun (A) when is_atom(A) -> A end;
|
fun (A) when is_atom(A) -> A end;
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_muc_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_muc).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, store_room/4, restore_room/3, forget_room/3,
|
||||||
|
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]).
|
||||||
|
|
||||||
|
-include("mod_muc.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, Opts) ->
|
||||||
|
MyHost = proplists:get_value(host, Opts),
|
||||||
|
mnesia:create_table(muc_room,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, muc_room)}]),
|
||||||
|
mnesia:create_table(muc_registered,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, muc_registered)}]),
|
||||||
|
update_tables(MyHost),
|
||||||
|
mnesia:add_table_index(muc_registered, nick).
|
||||||
|
|
||||||
|
store_room(_LServer, Host, Name, Opts) ->
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write(#muc_room{name_host = {Name, Host},
|
||||||
|
opts = Opts})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
restore_room(_LServer, Host, Name) ->
|
||||||
|
case catch mnesia:dirty_read(muc_room, {Name, Host}) of
|
||||||
|
[#muc_room{opts = Opts}] -> Opts;
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
forget_room(_LServer, Host, Name) ->
|
||||||
|
F = fun () -> mnesia:delete({muc_room, {Name, Host}})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
can_use_nick(_LServer, Host, JID, Nick) ->
|
||||||
|
{LUser, LServer, _} = jid:tolower(JID),
|
||||||
|
LUS = {LUser, LServer},
|
||||||
|
case catch mnesia:dirty_select(muc_registered,
|
||||||
|
[{#muc_registered{us_host = '$1',
|
||||||
|
nick = Nick, _ = '_'},
|
||||||
|
[{'==', {element, 2, '$1'}, Host}],
|
||||||
|
['$_']}])
|
||||||
|
of
|
||||||
|
{'EXIT', _Reason} -> true;
|
||||||
|
[] -> true;
|
||||||
|
[#muc_registered{us_host = {U, _Host}}] -> U == LUS
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_rooms(_LServer, Host) ->
|
||||||
|
mnesia:dirty_select(muc_room,
|
||||||
|
[{#muc_room{name_host = {'_', Host},
|
||||||
|
_ = '_'},
|
||||||
|
[], ['$_']}]).
|
||||||
|
|
||||||
|
get_nick(_LServer, Host, From) ->
|
||||||
|
{LUser, LServer, _} = jid:tolower(From),
|
||||||
|
LUS = {LUser, LServer},
|
||||||
|
case mnesia:dirty_read(muc_registered, {LUS, Host}) of
|
||||||
|
[] -> error;
|
||||||
|
[#muc_registered{nick = Nick}] -> Nick
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_nick(_LServer, Host, From, Nick) ->
|
||||||
|
{LUser, LServer, _} = jid:tolower(From),
|
||||||
|
LUS = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
case Nick of
|
||||||
|
<<"">> ->
|
||||||
|
mnesia:delete({muc_registered, {LUS, Host}}),
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
Allow = case mnesia:select(
|
||||||
|
muc_registered,
|
||||||
|
[{#muc_registered{us_host =
|
||||||
|
'$1',
|
||||||
|
nick = Nick,
|
||||||
|
_ = '_'},
|
||||||
|
[{'==', {element, 2, '$1'},
|
||||||
|
Host}],
|
||||||
|
['$_']}]) of
|
||||||
|
[] -> true;
|
||||||
|
[#muc_registered{us_host = {U, _Host}}] ->
|
||||||
|
U == LUS
|
||||||
|
end,
|
||||||
|
if Allow ->
|
||||||
|
mnesia:write(#muc_registered{
|
||||||
|
us_host = {LUS, Host},
|
||||||
|
nick = Nick}),
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #muc_room{} = R) ->
|
||||||
|
mnesia:dirty_write(R);
|
||||||
|
import(_LServer, #muc_registered{} = R) ->
|
||||||
|
mnesia:dirty_write(R).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_tables(Host) ->
|
||||||
|
update_muc_room_table(Host),
|
||||||
|
update_muc_registered_table(Host).
|
||||||
|
|
||||||
|
update_muc_room_table(_Host) ->
|
||||||
|
Fields = record_info(fields, muc_room),
|
||||||
|
case mnesia:table_info(muc_room, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
muc_room, Fields, set,
|
||||||
|
fun(#muc_room{name_host = {N, _}}) -> N end,
|
||||||
|
fun(#muc_room{name_host = {N, H},
|
||||||
|
opts = Opts} = R) ->
|
||||||
|
R#muc_room{name_host = {iolist_to_binary(N),
|
||||||
|
iolist_to_binary(H)},
|
||||||
|
opts = mod_muc:opts_to_binary(Opts)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating muc_room table", []),
|
||||||
|
mnesia:transform_table(muc_room, ignore, Fields)
|
||||||
|
end.
|
||||||
|
|
||||||
|
update_muc_registered_table(_Host) ->
|
||||||
|
Fields = record_info(fields, muc_registered),
|
||||||
|
case mnesia:table_info(muc_registered, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
muc_registered, Fields, set,
|
||||||
|
fun(#muc_registered{us_host = {_, H}}) -> H end,
|
||||||
|
fun(#muc_registered{us_host = {{U, S}, H},
|
||||||
|
nick = Nick} = R) ->
|
||||||
|
R#muc_registered{us_host = {{iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
iolist_to_binary(H)},
|
||||||
|
nick = iolist_to_binary(Nick)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating muc_registered table", []),
|
||||||
|
mnesia:transform_table(muc_registered, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,117 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_muc_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_muc).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, store_room/4, restore_room/3, forget_room/3,
|
||||||
|
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]).
|
||||||
|
|
||||||
|
-include("mod_muc.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
store_room(_LServer, Host, Name, Opts) ->
|
||||||
|
{atomic, ejabberd_riak:put(#muc_room{name_host = {Name, Host},
|
||||||
|
opts = Opts},
|
||||||
|
muc_room_schema())}.
|
||||||
|
|
||||||
|
restore_room(_LServer, Host, Name) ->
|
||||||
|
case ejabberd_riak:get(muc_room, muc_room_schema(), {Name, Host}) of
|
||||||
|
{ok, #muc_room{opts = Opts}} -> Opts;
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
forget_room(_LServer, Host, Name) ->
|
||||||
|
{atomic, ejabberd_riak:delete(muc_room, {Name, Host})}.
|
||||||
|
|
||||||
|
can_use_nick(LServer, Host, JID, Nick) ->
|
||||||
|
{LUser, LServer, _} = jid:tolower(JID),
|
||||||
|
LUS = {LUser, LServer},
|
||||||
|
case ejabberd_riak:get_by_index(muc_registered,
|
||||||
|
muc_registered_schema(),
|
||||||
|
<<"nick_host">>, {Nick, Host}) of
|
||||||
|
{ok, []} ->
|
||||||
|
true;
|
||||||
|
{ok, [#muc_registered{us_host = {U, _Host}}]} ->
|
||||||
|
U == LUS;
|
||||||
|
{error, _} ->
|
||||||
|
true
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_rooms(_LServer, Host) ->
|
||||||
|
case ejabberd_riak:get(muc_room, muc_room_schema()) of
|
||||||
|
{ok, Rs} ->
|
||||||
|
lists:filter(
|
||||||
|
fun(#muc_room{name_host = {_, H}}) ->
|
||||||
|
Host == H
|
||||||
|
end, Rs);
|
||||||
|
_Err ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_nick(LServer, Host, From) ->
|
||||||
|
{LUser, LServer, _} = jid:tolower(From),
|
||||||
|
US = {LUser, LServer},
|
||||||
|
case ejabberd_riak:get(muc_registered,
|
||||||
|
muc_registered_schema(),
|
||||||
|
{US, Host}) of
|
||||||
|
{ok, #muc_registered{nick = Nick}} -> Nick;
|
||||||
|
{error, _} -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_nick(LServer, Host, From, Nick) ->
|
||||||
|
{LUser, LServer, _} = jid:tolower(From),
|
||||||
|
LUS = {LUser, LServer},
|
||||||
|
{atomic,
|
||||||
|
case Nick of
|
||||||
|
<<"">> ->
|
||||||
|
ejabberd_riak:delete(muc_registered, {LUS, Host});
|
||||||
|
_ ->
|
||||||
|
Allow = case ejabberd_riak:get_by_index(
|
||||||
|
muc_registered,
|
||||||
|
muc_registered_schema(),
|
||||||
|
<<"nick_host">>, {Nick, Host}) of
|
||||||
|
{ok, []} ->
|
||||||
|
true;
|
||||||
|
{ok, [#muc_registered{us_host = {U, _Host}}]} ->
|
||||||
|
U == LUS;
|
||||||
|
{error, _} ->
|
||||||
|
false
|
||||||
|
end,
|
||||||
|
if Allow ->
|
||||||
|
ejabberd_riak:put(#muc_registered{us_host = {LUS, Host},
|
||||||
|
nick = Nick},
|
||||||
|
muc_registered_schema(),
|
||||||
|
[{'2i', [{<<"nick_host">>,
|
||||||
|
{Nick, Host}}]}]);
|
||||||
|
true ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end}.
|
||||||
|
|
||||||
|
import(_LServer, #muc_room{} = R) ->
|
||||||
|
ejabberd_riak:put(R, muc_room_schema());
|
||||||
|
import(_LServer, #muc_registered{us_host = {_, Host}, nick = Nick} = R) ->
|
||||||
|
ejabberd_riak:put(R, muc_registered_schema(),
|
||||||
|
[{'2i', [{<<"nick_host">>, {Nick, Host}}]}]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
muc_room_schema() ->
|
||||||
|
{record_info(fields, muc_room), #muc_room{}}.
|
||||||
|
|
||||||
|
muc_registered_schema() ->
|
||||||
|
{record_info(fields, muc_registered), #muc_registered{}}.
|
|
@ -0,0 +1,202 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_muc_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_muc).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, store_room/4, restore_room/3, forget_room/3,
|
||||||
|
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4,
|
||||||
|
import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_muc.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
store_room(LServer, Host, Name, Opts) ->
|
||||||
|
SName = ejabberd_odbc:escape(Name),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
SOpts = ejabberd_odbc:encode_term(Opts),
|
||||||
|
F = fun () ->
|
||||||
|
odbc_queries:update_t(<<"muc_room">>,
|
||||||
|
[<<"name">>, <<"host">>, <<"opts">>],
|
||||||
|
[SName, SHost, SOpts],
|
||||||
|
[<<"name='">>, SName, <<"' and host='">>,
|
||||||
|
SHost, <<"'">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
restore_room(LServer, Host, Name) ->
|
||||||
|
SName = ejabberd_odbc:escape(Name),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
case catch ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"select opts from muc_room where name='">>,
|
||||||
|
SName, <<"' and host='">>, SHost,
|
||||||
|
<<"';">>]) of
|
||||||
|
{selected, [<<"opts">>], [[Opts]]} ->
|
||||||
|
mod_muc:opts_to_binary(ejabberd_odbc:decode_term(Opts));
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
forget_room(LServer, Host, Name) ->
|
||||||
|
SName = ejabberd_odbc:escape(Name),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
F = fun () ->
|
||||||
|
ejabberd_odbc:sql_query_t([<<"delete from muc_room where name='">>,
|
||||||
|
SName, <<"' and host='">>, SHost,
|
||||||
|
<<"';">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
can_use_nick(LServer, Host, JID, Nick) ->
|
||||||
|
SJID = jid:to_string(jid:tolower(jid:remove_resource(JID))),
|
||||||
|
SNick = ejabberd_odbc:escape(Nick),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
case catch ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"select jid from muc_registered ">>,
|
||||||
|
<<"where nick='">>, SNick,
|
||||||
|
<<"' and host='">>, SHost, <<"';">>]) of
|
||||||
|
{selected, [<<"jid">>], [[SJID1]]} -> SJID == SJID1;
|
||||||
|
_ -> true
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_rooms(LServer, Host) ->
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
case catch ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"select name, opts from muc_room ">>,
|
||||||
|
<<"where host='">>, SHost, <<"';">>]) of
|
||||||
|
{selected, [<<"name">>, <<"opts">>], RoomOpts} ->
|
||||||
|
lists:map(
|
||||||
|
fun([Room, Opts]) ->
|
||||||
|
#muc_room{name_host = {Room, Host},
|
||||||
|
opts = mod_muc:opts_to_binary(
|
||||||
|
ejabberd_odbc:decode_term(Opts))}
|
||||||
|
end, RoomOpts);
|
||||||
|
Err ->
|
||||||
|
?ERROR_MSG("failed to get rooms: ~p", [Err]),
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_nick(LServer, Host, From) ->
|
||||||
|
SJID = ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:remove_resource(From)))),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
case catch ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"select nick from muc_registered where "
|
||||||
|
"jid='">>,
|
||||||
|
SJID, <<"' and host='">>, SHost,
|
||||||
|
<<"';">>]) of
|
||||||
|
{selected, [<<"nick">>], [[Nick]]} -> Nick;
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_nick(LServer, Host, From, Nick) ->
|
||||||
|
JID = jid:to_string(jid:tolower(jid:remove_resource(From))),
|
||||||
|
SJID = ejabberd_odbc:escape(JID),
|
||||||
|
SNick = ejabberd_odbc:escape(Nick),
|
||||||
|
SHost = ejabberd_odbc:escape(Host),
|
||||||
|
F = fun () ->
|
||||||
|
case Nick of
|
||||||
|
<<"">> ->
|
||||||
|
ejabberd_odbc:sql_query_t(
|
||||||
|
[<<"delete from muc_registered where ">>,
|
||||||
|
<<"jid='">>, SJID,
|
||||||
|
<<"' and host='">>, Host,
|
||||||
|
<<"';">>]),
|
||||||
|
ok;
|
||||||
|
_ ->
|
||||||
|
Allow = case ejabberd_odbc:sql_query_t(
|
||||||
|
[<<"select jid from muc_registered ">>,
|
||||||
|
<<"where nick='">>,
|
||||||
|
SNick,
|
||||||
|
<<"' and host='">>,
|
||||||
|
SHost, <<"';">>]) of
|
||||||
|
{selected, [<<"jid">>], [[J]]} -> J == JID;
|
||||||
|
_ -> true
|
||||||
|
end,
|
||||||
|
if Allow ->
|
||||||
|
odbc_queries:update_t(<<"muc_registered">>,
|
||||||
|
[<<"jid">>, <<"host">>,
|
||||||
|
<<"nick">>],
|
||||||
|
[SJID, SHost, SNick],
|
||||||
|
[<<"jid='">>, SJID,
|
||||||
|
<<"' and host='">>, SHost,
|
||||||
|
<<"'">>]),
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{muc_room,
|
||||||
|
fun(Host, #muc_room{name_host = {Name, RoomHost}, opts = Opts}) ->
|
||||||
|
case str:suffix(Host, RoomHost) of
|
||||||
|
true ->
|
||||||
|
SName = ejabberd_odbc:escape(Name),
|
||||||
|
SRoomHost = ejabberd_odbc:escape(RoomHost),
|
||||||
|
SOpts = ejabberd_odbc:encode_term(Opts),
|
||||||
|
[[<<"delete from muc_room where name='">>, SName,
|
||||||
|
<<"' and host='">>, SRoomHost, <<"';">>],
|
||||||
|
[<<"insert into muc_room(name, host, opts) ",
|
||||||
|
"values (">>,
|
||||||
|
<<"'">>, SName, <<"', '">>, SRoomHost,
|
||||||
|
<<"', '">>, SOpts, <<"');">>]];
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end},
|
||||||
|
{muc_registered,
|
||||||
|
fun(Host, #muc_registered{us_host = {{U, S}, RoomHost},
|
||||||
|
nick = Nick}) ->
|
||||||
|
case str:suffix(Host, RoomHost) of
|
||||||
|
true ->
|
||||||
|
SJID = ejabberd_odbc:escape(
|
||||||
|
jid:to_string(
|
||||||
|
jid:make(U, S, <<"">>))),
|
||||||
|
SNick = ejabberd_odbc:escape(Nick),
|
||||||
|
SRoomHost = ejabberd_odbc:escape(RoomHost),
|
||||||
|
[[<<"delete from muc_registered where jid='">>,
|
||||||
|
SJID, <<"' and host='">>, SRoomHost, <<"';">>],
|
||||||
|
[<<"insert into muc_registered(jid, host, "
|
||||||
|
"nick) values ('">>,
|
||||||
|
SJID, <<"', '">>, SRoomHost, <<"', '">>, SNick,
|
||||||
|
<<"');">>]];
|
||||||
|
false ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_LServer) ->
|
||||||
|
[{<<"select name, host, opts from muc_room;">>,
|
||||||
|
fun([Name, RoomHost, SOpts]) ->
|
||||||
|
Opts = mod_muc:opts_to_binary(ejabberd_odbc:decode_term(SOpts)),
|
||||||
|
#muc_room{name_host = {Name, RoomHost}, opts = Opts}
|
||||||
|
end},
|
||||||
|
{<<"select jid, host, nick from muc_registered;">>,
|
||||||
|
fun([J, RoomHost, Nick]) ->
|
||||||
|
#jid{user = U, server = S} = jid:from_string(J),
|
||||||
|
#muc_registered{us_host = {{U, S}, RoomHost},
|
||||||
|
nick = Nick}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,232 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_offline_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_offline).
|
||||||
|
|
||||||
|
-export([init/2, store_messages/5, pop_messages/2, remove_expired_messages/1,
|
||||||
|
remove_old_messages/2, remove_user/2, read_message_headers/2,
|
||||||
|
read_message/3, remove_message/3, read_all_messages/2,
|
||||||
|
remove_all_messages/2, count_messages/2, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_offline.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
-define(OFFLINE_TABLE_LOCK_THRESHOLD, 1000).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(offline_msg,
|
||||||
|
[{disc_only_copies, [node()]}, {type, bag},
|
||||||
|
{attributes, record_info(fields, offline_msg)}]),
|
||||||
|
update_table().
|
||||||
|
|
||||||
|
store_messages(_Host, US, Msgs, Len, MaxOfflineMsgs) ->
|
||||||
|
F = fun () ->
|
||||||
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
|
Len + count_mnesia_records(US);
|
||||||
|
true -> 0
|
||||||
|
end,
|
||||||
|
if Count > MaxOfflineMsgs -> discard;
|
||||||
|
true ->
|
||||||
|
if Len >= (?OFFLINE_TABLE_LOCK_THRESHOLD) ->
|
||||||
|
mnesia:write_lock_table(offline_msg);
|
||||||
|
true -> ok
|
||||||
|
end,
|
||||||
|
lists:foreach(fun (M) -> mnesia:write(M) end, Msgs)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
pop_messages(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
Rs = mnesia:wread({offline_msg, US}),
|
||||||
|
mnesia:delete({offline_msg, US}),
|
||||||
|
Rs
|
||||||
|
end,
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, L} ->
|
||||||
|
{ok, lists:keysort(#offline_msg.timestamp, L)};
|
||||||
|
{aborted, Reason} ->
|
||||||
|
{error, Reason}
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_expired_messages(_LServer) ->
|
||||||
|
TimeStamp = p1_time_compat:timestamp(),
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write_lock_table(offline_msg),
|
||||||
|
mnesia:foldl(fun (Rec, _Acc) ->
|
||||||
|
case Rec#offline_msg.expire of
|
||||||
|
never -> ok;
|
||||||
|
TS ->
|
||||||
|
if TS < TimeStamp ->
|
||||||
|
mnesia:delete_object(Rec);
|
||||||
|
true -> ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
ok, offline_msg)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
remove_old_messages(Days, _LServer) ->
|
||||||
|
S = p1_time_compat:system_time(seconds) - 60 * 60 * 24 * Days,
|
||||||
|
MegaSecs1 = S div 1000000,
|
||||||
|
Secs1 = S rem 1000000,
|
||||||
|
TimeStamp = {MegaSecs1, Secs1, 0},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write_lock_table(offline_msg),
|
||||||
|
mnesia:foldl(fun (#offline_msg{timestamp = TS} = Rec,
|
||||||
|
_Acc)
|
||||||
|
when TS < TimeStamp ->
|
||||||
|
mnesia:delete_object(Rec);
|
||||||
|
(_Rec, _Acc) -> ok
|
||||||
|
end,
|
||||||
|
ok, offline_msg)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () -> mnesia:delete({offline_msg, US}) end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
read_message_headers(LUser, LServer) ->
|
||||||
|
Msgs = mnesia:dirty_read({offline_msg, {LUser, LServer}}),
|
||||||
|
Hdrs = lists:map(
|
||||||
|
fun(#offline_msg{from = From, to = To, packet = Pkt,
|
||||||
|
timestamp = TS}) ->
|
||||||
|
Seq = now_to_integer(TS),
|
||||||
|
NewPkt = jlib:add_delay_info(Pkt, LServer, TS,
|
||||||
|
<<"Offline Storage">>),
|
||||||
|
{Seq, From, To, NewPkt}
|
||||||
|
end, Msgs),
|
||||||
|
lists:keysort(1, Hdrs).
|
||||||
|
|
||||||
|
read_message(LUser, LServer, I) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
TS = integer_to_now(I),
|
||||||
|
case mnesia:dirty_match_object(
|
||||||
|
offline_msg, #offline_msg{us = US, timestamp = TS, _ = '_'}) of
|
||||||
|
[Msg|_] ->
|
||||||
|
{ok, Msg};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_message(LUser, LServer, I) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
TS = integer_to_now(I),
|
||||||
|
Msgs = mnesia:dirty_match_object(
|
||||||
|
offline_msg, #offline_msg{us = US, timestamp = TS, _ = '_'}),
|
||||||
|
lists:foreach(
|
||||||
|
fun(Msg) ->
|
||||||
|
mnesia:dirty_delete_object(Msg)
|
||||||
|
end, Msgs).
|
||||||
|
|
||||||
|
read_all_messages(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
lists:keysort(#offline_msg.timestamp,
|
||||||
|
mnesia:dirty_read({offline_msg, US})).
|
||||||
|
|
||||||
|
remove_all_messages(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write_lock_table(offline_msg),
|
||||||
|
lists:foreach(fun (Msg) -> mnesia:delete_object(Msg) end,
|
||||||
|
mnesia:dirty_read({offline_msg, US}))
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
count_messages(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
count_mnesia_records(US)
|
||||||
|
end,
|
||||||
|
case catch mnesia:async_dirty(F) of
|
||||||
|
I when is_integer(I) -> I;
|
||||||
|
_ -> 0
|
||||||
|
end.
|
||||||
|
|
||||||
|
import(_LServer, #offline_msg{} = Msg) ->
|
||||||
|
mnesia:dirty_write(Msg).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
%% Return the number of records matching a given match expression.
|
||||||
|
%% This function is intended to be used inside a Mnesia transaction.
|
||||||
|
%% The count has been written to use the fewest possible memory by
|
||||||
|
%% getting the record by small increment and by using continuation.
|
||||||
|
-define(BATCHSIZE, 100).
|
||||||
|
|
||||||
|
count_mnesia_records(US) ->
|
||||||
|
MatchExpression = #offline_msg{us = US, _ = '_'},
|
||||||
|
case mnesia:select(offline_msg, [{MatchExpression, [], [[]]}],
|
||||||
|
?BATCHSIZE, read) of
|
||||||
|
{Result, Cont} ->
|
||||||
|
Count = length(Result),
|
||||||
|
count_records_cont(Cont, Count);
|
||||||
|
'$end_of_table' ->
|
||||||
|
0
|
||||||
|
end.
|
||||||
|
|
||||||
|
count_records_cont(Cont, Count) ->
|
||||||
|
case mnesia:select(Cont) of
|
||||||
|
{Result, Cont} ->
|
||||||
|
NewCount = Count + length(Result),
|
||||||
|
count_records_cont(Cont, NewCount);
|
||||||
|
'$end_of_table' ->
|
||||||
|
Count
|
||||||
|
end.
|
||||||
|
|
||||||
|
jid_to_binary(#jid{user = U, server = S, resource = R,
|
||||||
|
luser = LU, lserver = LS, lresource = LR}) ->
|
||||||
|
#jid{user = iolist_to_binary(U),
|
||||||
|
server = iolist_to_binary(S),
|
||||||
|
resource = iolist_to_binary(R),
|
||||||
|
luser = iolist_to_binary(LU),
|
||||||
|
lserver = iolist_to_binary(LS),
|
||||||
|
lresource = iolist_to_binary(LR)}.
|
||||||
|
|
||||||
|
now_to_integer({MS, S, US}) ->
|
||||||
|
(MS * 1000000 + S) * 1000000 + US.
|
||||||
|
|
||||||
|
integer_to_now(Int) ->
|
||||||
|
Secs = Int div 1000000,
|
||||||
|
USec = Int rem 1000000,
|
||||||
|
MSec = Secs div 1000000,
|
||||||
|
Sec = Secs rem 1000000,
|
||||||
|
{MSec, Sec, USec}.
|
||||||
|
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, offline_msg),
|
||||||
|
case mnesia:table_info(offline_msg, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
offline_msg, Fields, bag,
|
||||||
|
fun(#offline_msg{us = {U, _}}) -> U end,
|
||||||
|
fun(#offline_msg{us = {U, S},
|
||||||
|
from = From,
|
||||||
|
to = To,
|
||||||
|
packet = El} = R) ->
|
||||||
|
R#offline_msg{us = {iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
from = jid_to_binary(From),
|
||||||
|
to = jid_to_binary(To),
|
||||||
|
packet = fxml:to_xmlel(El)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating offline_msg table", []),
|
||||||
|
mnesia:transform_table(offline_msg, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,153 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_offline_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_offline).
|
||||||
|
|
||||||
|
-export([init/2, store_messages/5, pop_messages/2, remove_expired_messages/1,
|
||||||
|
remove_old_messages/2, remove_user/2, read_message_headers/2,
|
||||||
|
read_message/3, remove_message/3, read_all_messages/2,
|
||||||
|
remove_all_messages/2, count_messages/2, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_offline.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
store_messages(Host, {User, _}, Msgs, Len, MaxOfflineMsgs) ->
|
||||||
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
|
Len + count_messages(User, Host);
|
||||||
|
true -> 0
|
||||||
|
end,
|
||||||
|
if
|
||||||
|
Count > MaxOfflineMsgs ->
|
||||||
|
{atomic, discard};
|
||||||
|
true ->
|
||||||
|
try
|
||||||
|
lists:foreach(
|
||||||
|
fun(#offline_msg{us = US,
|
||||||
|
timestamp = TS} = M) ->
|
||||||
|
ok = ejabberd_riak:put(
|
||||||
|
M, offline_msg_schema(),
|
||||||
|
[{i, TS}, {'2i', [{<<"us">>, US}]}])
|
||||||
|
end, Msgs),
|
||||||
|
{atomic, ok}
|
||||||
|
catch _:{badmatch, Err} ->
|
||||||
|
{atomic, Err}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
pop_messages(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get_by_index(offline_msg, offline_msg_schema(),
|
||||||
|
<<"us">>, {LUser, LServer}) of
|
||||||
|
{ok, Rs} ->
|
||||||
|
try
|
||||||
|
lists:foreach(
|
||||||
|
fun(#offline_msg{timestamp = T}) ->
|
||||||
|
ok = ejabberd_riak:delete(offline_msg, T)
|
||||||
|
end, Rs),
|
||||||
|
{ok, lists:keysort(#offline_msg.timestamp, Rs)}
|
||||||
|
catch _:{badmatch, Err} ->
|
||||||
|
Err
|
||||||
|
end;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_expired_messages(_LServer) ->
|
||||||
|
%% TODO
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
remove_old_messages(_Days, _LServer) ->
|
||||||
|
%% TODO
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete_by_index(offline_msg,
|
||||||
|
<<"us">>, {LUser, LServer})}.
|
||||||
|
|
||||||
|
read_message_headers(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get_by_index(
|
||||||
|
offline_msg, offline_msg_schema(),
|
||||||
|
<<"us">>, {LUser, LServer}) of
|
||||||
|
{ok, Rs} ->
|
||||||
|
Hdrs = lists:map(
|
||||||
|
fun(#offline_msg{from = From, to = To, packet = Pkt,
|
||||||
|
timestamp = TS}) ->
|
||||||
|
Seq = now_to_integer(TS),
|
||||||
|
NewPkt = jlib:add_delay_info(
|
||||||
|
Pkt, LServer, TS, <<"Offline Storage">>),
|
||||||
|
{Seq, From, To, NewPkt}
|
||||||
|
end, Rs),
|
||||||
|
lists:keysort(1, Hdrs);
|
||||||
|
_Err ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
read_message(_LUser, _LServer, I) ->
|
||||||
|
TS = integer_to_now(I),
|
||||||
|
case ejabberd_riak:get(offline_msg, offline_msg_schema(), TS) of
|
||||||
|
{ok, Msg} ->
|
||||||
|
{ok, Msg};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_message(_LUser, _LServer, I) ->
|
||||||
|
TS = integer_to_now(I),
|
||||||
|
ejabberd_riak:delete(offline_msg, TS),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
read_all_messages(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get_by_index(
|
||||||
|
offline_msg, offline_msg_schema(),
|
||||||
|
<<"us">>, {LUser, LServer}) of
|
||||||
|
{ok, Rs} ->
|
||||||
|
lists:keysort(#offline_msg.timestamp, Rs);
|
||||||
|
_Err ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_all_messages(LUser, LServer) ->
|
||||||
|
Res = ejabberd_riak:delete_by_index(offline_msg,
|
||||||
|
<<"us">>, {LUser, LServer}),
|
||||||
|
{atomic, Res}.
|
||||||
|
|
||||||
|
count_messages(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:count_by_index(
|
||||||
|
offline_msg, <<"us">>, {LUser, LServer}) of
|
||||||
|
{ok, Res} ->
|
||||||
|
Res;
|
||||||
|
_ ->
|
||||||
|
0
|
||||||
|
end.
|
||||||
|
|
||||||
|
import(_LServer, #offline_msg{us = US, timestamp = TS} = M) ->
|
||||||
|
ejabberd_riak:put(M, offline_msg_schema(),
|
||||||
|
[{i, TS}, {'2i', [{<<"us">>, US}]}]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
offline_msg_schema() ->
|
||||||
|
{record_info(fields, offline_msg), #offline_msg{}}.
|
||||||
|
|
||||||
|
now_to_integer({MS, S, US}) ->
|
||||||
|
(MS * 1000000 + S) * 1000000 + US.
|
||||||
|
|
||||||
|
integer_to_now(Int) ->
|
||||||
|
Secs = Int div 1000000,
|
||||||
|
USec = Int rem 1000000,
|
||||||
|
MSec = Secs div 1000000,
|
||||||
|
Sec = Secs rem 1000000,
|
||||||
|
{MSec, Sec, USec}.
|
|
@ -0,0 +1,252 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 15 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_offline_sql).
|
||||||
|
|
||||||
|
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||||
|
|
||||||
|
-behaviour(mod_offline).
|
||||||
|
|
||||||
|
-export([init/2, store_messages/5, pop_messages/2, remove_expired_messages/1,
|
||||||
|
remove_old_messages/2, remove_user/2, read_message_headers/2,
|
||||||
|
read_message/3, remove_message/3, read_all_messages/2,
|
||||||
|
remove_all_messages/2, count_messages/2, import/1, import/2,
|
||||||
|
export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_offline.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
-include("ejabberd_sql_pt.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
store_messages(Host, {User, _Server}, Msgs, Len, MaxOfflineMsgs) ->
|
||||||
|
Count = if MaxOfflineMsgs =/= infinity ->
|
||||||
|
Len + count_messages(User, Host);
|
||||||
|
true -> 0
|
||||||
|
end,
|
||||||
|
if Count > MaxOfflineMsgs -> {atomic, discard};
|
||||||
|
true ->
|
||||||
|
Query = lists:map(
|
||||||
|
fun(M) ->
|
||||||
|
Username =
|
||||||
|
ejabberd_odbc:escape((M#offline_msg.to)#jid.luser),
|
||||||
|
From = M#offline_msg.from,
|
||||||
|
To = M#offline_msg.to,
|
||||||
|
Packet =
|
||||||
|
jlib:replace_from_to(From, To,
|
||||||
|
M#offline_msg.packet),
|
||||||
|
NewPacket =
|
||||||
|
jlib:add_delay_info(Packet, Host,
|
||||||
|
M#offline_msg.timestamp,
|
||||||
|
<<"Offline Storage">>),
|
||||||
|
XML =
|
||||||
|
ejabberd_odbc:escape(fxml:element_to_binary(NewPacket)),
|
||||||
|
odbc_queries:add_spool_sql(Username, XML)
|
||||||
|
end,
|
||||||
|
Msgs),
|
||||||
|
odbc_queries:add_spool(Host, Query)
|
||||||
|
end.
|
||||||
|
|
||||||
|
pop_messages(LUser, LServer) ->
|
||||||
|
case odbc_queries:get_and_del_spool_msg_t(LServer, LUser) of
|
||||||
|
{atomic, {selected, Rs}} ->
|
||||||
|
{ok, lists:flatmap(
|
||||||
|
fun({_, XML}) ->
|
||||||
|
case xml_to_offline_msg(XML) of
|
||||||
|
{ok, Msg} ->
|
||||||
|
[Msg];
|
||||||
|
_Err ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end, Rs)};
|
||||||
|
Err ->
|
||||||
|
{error, Err}
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_expired_messages(_LServer) ->
|
||||||
|
%% TODO
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
remove_old_messages(Days, LServer) ->
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"DELETE FROM spool"
|
||||||
|
" WHERE created_at < "
|
||||||
|
"DATE_SUB(CURDATE(), INTERVAL ">>,
|
||||||
|
integer_to_list(Days), <<" DAY);">>]) of
|
||||||
|
{updated, N} ->
|
||||||
|
?INFO_MSG("~p message(s) deleted from offline spool", [N]);
|
||||||
|
_Error ->
|
||||||
|
?ERROR_MSG("Cannot delete message in offline spool: ~p", [_Error])
|
||||||
|
end,
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
odbc_queries:del_spool_msg(LServer, LUser).
|
||||||
|
|
||||||
|
read_message_headers(LUser, LServer) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer, [<<"select xml, seq from spool where username ='">>,
|
||||||
|
Username, <<"' order by seq;">>]) of
|
||||||
|
{selected, [<<"xml">>, <<"seq">>], Rows} ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun([XML, Seq]) ->
|
||||||
|
case xml_to_offline_msg(XML) of
|
||||||
|
{ok, #offline_msg{from = From,
|
||||||
|
to = To,
|
||||||
|
packet = El}} ->
|
||||||
|
Seq0 = binary_to_integer(Seq),
|
||||||
|
[{Seq0, From, To, El}];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end, Rows);
|
||||||
|
_Err ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
read_message(LUser, LServer, Seq) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SSeq = ejabberd_odbc:escape(integer_to_binary(Seq)),
|
||||||
|
case ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"select xml from spool where username='">>, Username,
|
||||||
|
<<"' and seq='">>, SSeq, <<"';">>]) of
|
||||||
|
{selected, [<<"xml">>], [[RawXML]|_]} ->
|
||||||
|
case xml_to_offline_msg(RawXML) of
|
||||||
|
{ok, Msg} ->
|
||||||
|
{ok, Msg};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_message(LUser, LServer, Seq) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SSeq = ejabberd_odbc:escape(integer_to_binary(Seq)),
|
||||||
|
ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"delete from spool where username='">>, Username,
|
||||||
|
<<"' and seq='">>, SSeq, <<"';">>]),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
read_all_messages(LUser, LServer) ->
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
?SQL("select @(xml)s from spool where "
|
||||||
|
"username=%(LUser)s order by seq")) of
|
||||||
|
{selected, Rs} ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun({XML}) ->
|
||||||
|
case xml_to_offline_msg(XML) of
|
||||||
|
{ok, Msg} -> [Msg];
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
end, Rs);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_all_messages(LUser, LServer) ->
|
||||||
|
odbc_queries:del_spool_msg(LServer, LUser),
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
count_messages(LUser, LServer) ->
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
?SQL("select @(count(*))d from spool "
|
||||||
|
"where username=%(LUser)s")) of
|
||||||
|
{selected, [{Res}]} ->
|
||||||
|
Res;
|
||||||
|
_ -> 0
|
||||||
|
end.
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{offline_msg,
|
||||||
|
fun(Host, #offline_msg{us = {LUser, LServer},
|
||||||
|
timestamp = TimeStamp, from = From, to = To,
|
||||||
|
packet = Packet})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
Packet1 = jlib:replace_from_to(From, To, Packet),
|
||||||
|
Packet2 = jlib:add_delay_info(Packet1, LServer, TimeStamp,
|
||||||
|
<<"Offline Storage">>),
|
||||||
|
XML = ejabberd_odbc:escape(fxml:element_to_binary(Packet2)),
|
||||||
|
[[<<"delete from spool where username='">>, Username, <<"';">>],
|
||||||
|
[<<"insert into spool(username, xml) values ('">>,
|
||||||
|
Username, <<"', '">>, XML, <<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username, xml from spool;">>,
|
||||||
|
fun([LUser, XML]) ->
|
||||||
|
El = #xmlel{} = fxml_stream:parse_element(XML),
|
||||||
|
From = #jid{} = jid:from_string(
|
||||||
|
fxml:get_attr_s(<<"from">>, El#xmlel.attrs)),
|
||||||
|
To = #jid{} = jid:from_string(
|
||||||
|
fxml:get_attr_s(<<"to">>, El#xmlel.attrs)),
|
||||||
|
Stamp = fxml:get_path_s(El, [{elem, <<"delay">>},
|
||||||
|
{attr, <<"stamp">>}]),
|
||||||
|
TS = case jlib:datetime_string_to_timestamp(Stamp) of
|
||||||
|
{_, _, _} = Now ->
|
||||||
|
Now;
|
||||||
|
undefined ->
|
||||||
|
p1_time_compat:timestamp()
|
||||||
|
end,
|
||||||
|
Expire = mod_offline:find_x_expire(TS, El#xmlel.children),
|
||||||
|
#offline_msg{us = {LUser, LServer},
|
||||||
|
from = From, to = To,
|
||||||
|
packet = El,
|
||||||
|
timestamp = TS, expire = Expire}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
xml_to_offline_msg(XML) ->
|
||||||
|
case fxml_stream:parse_element(XML) of
|
||||||
|
#xmlel{} = El ->
|
||||||
|
el_to_offline_msg(El);
|
||||||
|
Err ->
|
||||||
|
?ERROR_MSG("got ~p when parsing XML packet ~s",
|
||||||
|
[Err, XML]),
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
||||||
|
el_to_offline_msg(El) ->
|
||||||
|
To_s = fxml:get_tag_attr_s(<<"to">>, El),
|
||||||
|
From_s = fxml:get_tag_attr_s(<<"from">>, El),
|
||||||
|
To = jid:from_string(To_s),
|
||||||
|
From = jid:from_string(From_s),
|
||||||
|
if To == error ->
|
||||||
|
?ERROR_MSG("failed to get 'to' JID from offline XML ~p", [El]),
|
||||||
|
{error, bad_jid_to};
|
||||||
|
From == error ->
|
||||||
|
?ERROR_MSG("failed to get 'from' JID from offline XML ~p", [El]),
|
||||||
|
{error, bad_jid_from};
|
||||||
|
true ->
|
||||||
|
{ok, #offline_msg{us = {To#jid.luser, To#jid.lserver},
|
||||||
|
from = From,
|
||||||
|
to = To,
|
||||||
|
timestamp = undefined,
|
||||||
|
expire = undefined,
|
||||||
|
packet = El}}
|
||||||
|
end.
|
|
@ -33,19 +33,10 @@
|
||||||
|
|
||||||
-export([start/2, stop/1, process_iq/3, export/1, import/1,
|
-export([start/2, stop/1, process_iq/3, export/1, import/1,
|
||||||
process_iq_set/4, process_iq_get/5, get_user_list/3,
|
process_iq_set/4, process_iq_get/5, get_user_list/3,
|
||||||
check_packet/6, remove_user/2, item_to_raw/1,
|
check_packet/6, remove_user/2,
|
||||||
raw_to_item/1, is_list_needdb/1, updated_list/3,
|
is_list_needdb/1, updated_list/3,
|
||||||
item_to_xml/1, get_user_lists/2, import/3,
|
item_to_xml/1, get_user_lists/2, import/3,
|
||||||
set_privacy_list/1]).
|
set_privacy_list/1, mod_opt_type/1]).
|
||||||
|
|
||||||
-export([sql_add_privacy_list/2,
|
|
||||||
sql_get_default_privacy_list/2,
|
|
||||||
sql_get_default_privacy_list_t/1,
|
|
||||||
sql_get_privacy_list_data/3,
|
|
||||||
sql_get_privacy_list_data_by_id_t/1,
|
|
||||||
sql_get_privacy_list_id_t/2,
|
|
||||||
sql_set_default_privacy_list/2, sql_set_privacy_list/2,
|
|
||||||
privacy_schema/0, mod_opt_type/1]).
|
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
@ -54,20 +45,24 @@
|
||||||
|
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
privacy_schema() ->
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
{record_info(fields, privacy), #privacy{}}.
|
-callback import(binary(), #privacy{}) -> ok | pass.
|
||||||
|
-callback process_lists_get(binary(), binary()) -> {none | binary(), [xmlel()]} | error.
|
||||||
|
-callback process_list_get(binary(), binary(), binary()) -> [listitem()] | error | not_found.
|
||||||
|
-callback process_default_set(binary(), binary(), {value, binary()} | false) -> {atomic, any()}.
|
||||||
|
-callback process_active_set(binary(), binary(), binary()) -> [listitem()] | error.
|
||||||
|
-callback remove_privacy_list(binary(), binary(), binary()) -> {atomic, any()}.
|
||||||
|
-callback set_privacy_list(#privacy{}) -> any().
|
||||||
|
-callback set_privacy_list(binary(), binary(), binary(), [listitem()]) -> {atomic, any()}.
|
||||||
|
-callback get_user_list(binary(), binary()) -> {none | binary(), [listitem()]}.
|
||||||
|
-callback get_user_lists(binary(), binary()) -> {ok, #privacy{}} | error.
|
||||||
|
-callback remove_user(binary(), binary()) -> {atomic, any()}.
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
one_queue),
|
one_queue),
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(privacy,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes, record_info(fields, privacy)}]),
|
|
||||||
update_table();
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
mod_disco:register_feature(Host, ?NS_PRIVACY),
|
mod_disco:register_feature(Host, ?NS_PRIVACY),
|
||||||
ejabberd_hooks:add(privacy_iq_get, Host, ?MODULE,
|
ejabberd_hooks:add(privacy_iq_get, Host, ?MODULE,
|
||||||
process_iq_get, 50),
|
process_iq_get, 50),
|
||||||
|
@ -124,9 +119,8 @@ process_iq_get(_, From, _To, #iq{lang = Lang, sub_el = SubEl},
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_lists_get(LUser, LServer, Active, Lang) ->
|
process_lists_get(LUser, LServer, Active, Lang) ->
|
||||||
case process_lists_get_db(LUser, LServer, Active,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE))
|
case Mod:process_lists_get(LUser, LServer) of
|
||||||
of
|
|
||||||
error -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
error -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||||
{_Default, []} ->
|
{_Default, []} ->
|
||||||
{result,
|
{result,
|
||||||
|
@ -153,57 +147,9 @@ process_lists_get(LUser, LServer, Active, Lang) ->
|
||||||
children = ADItems}]}
|
children = ADItems}]}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_lists_get_db(LUser, LServer, _Active, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> {none, []};
|
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
|
||||||
LItems = lists:map(fun ({N, _}) ->
|
|
||||||
#xmlel{name = <<"list">>,
|
|
||||||
attrs = [{<<"name">>, N}],
|
|
||||||
children = []}
|
|
||||||
end,
|
|
||||||
Lists),
|
|
||||||
{Default, LItems}
|
|
||||||
end;
|
|
||||||
process_lists_get_db(LUser, LServer, _Active, riak) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists}} ->
|
|
||||||
LItems = lists:map(fun ({N, _}) ->
|
|
||||||
#xmlel{name = <<"list">>,
|
|
||||||
attrs = [{<<"name">>, N}],
|
|
||||||
children = []}
|
|
||||||
end,
|
|
||||||
Lists),
|
|
||||||
{Default, LItems};
|
|
||||||
{error, notfound} ->
|
|
||||||
{none, []};
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
process_lists_get_db(LUser, LServer, _Active, odbc) ->
|
|
||||||
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
|
|
||||||
{selected, []} -> none;
|
|
||||||
{selected, [{DefName}]} -> DefName;
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
case catch sql_get_privacy_list_names(LUser, LServer) of
|
|
||||||
{selected, Names} ->
|
|
||||||
LItems = lists:map(fun ({N}) ->
|
|
||||||
#xmlel{name = <<"list">>,
|
|
||||||
attrs = [{<<"name">>, N}],
|
|
||||||
children = []}
|
|
||||||
end,
|
|
||||||
Names),
|
|
||||||
{Default, LItems};
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_list_get(LUser, LServer, {value, Name}, Lang) ->
|
process_list_get(LUser, LServer, {value, Name}, Lang) ->
|
||||||
case process_list_get_db(LUser, LServer, Name,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE))
|
case Mod:process_list_get(LUser, LServer, Name) of
|
||||||
of
|
|
||||||
error -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
error -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||||
not_found -> {error, ?ERR_ITEM_NOT_FOUND};
|
not_found -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
Items ->
|
Items ->
|
||||||
|
@ -218,41 +164,6 @@ process_list_get(LUser, LServer, {value, Name}, Lang) ->
|
||||||
process_list_get(_LUser, _LServer, false, _Lang) ->
|
process_list_get(_LUser, _LServer, false, _Lang) ->
|
||||||
{error, ?ERR_BAD_REQUEST}.
|
{error, ?ERR_BAD_REQUEST}.
|
||||||
|
|
||||||
process_list_get_db(LUser, LServer, Name, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> error;
|
|
||||||
[] -> not_found;
|
|
||||||
[#privacy{lists = Lists}] ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> not_found
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
process_list_get_db(LUser, LServer, Name, riak) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{lists = Lists}} ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
_ -> not_found
|
|
||||||
end;
|
|
||||||
{error, notfound} ->
|
|
||||||
not_found;
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
process_list_get_db(LUser, LServer, Name, odbc) ->
|
|
||||||
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
|
|
||||||
{selected, []} -> not_found;
|
|
||||||
{selected, [{ID}]} ->
|
|
||||||
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
lists:flatmap(fun raw_to_item/1, RItems);
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
item_to_xml(Item) ->
|
item_to_xml(Item) ->
|
||||||
Attrs1 = [{<<"action">>,
|
Attrs1 = [{<<"action">>,
|
||||||
action_to_list(Item#listitem.action)},
|
action_to_list(Item#listitem.action)},
|
||||||
|
@ -357,9 +268,8 @@ process_iq_set(_, From, _To, #iq{lang = Lang, sub_el = SubEl}) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_default_set(LUser, LServer, Value, Lang) ->
|
process_default_set(LUser, LServer, Value, Lang) ->
|
||||||
case process_default_set_db(LUser, LServer, Value,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE))
|
case Mod:process_default_set(LUser, LServer, Value) of
|
||||||
of
|
|
||||||
{atomic, error} ->
|
{atomic, error} ->
|
||||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||||
{atomic, not_found} -> {error, ?ERR_ITEM_NOT_FOUND};
|
{atomic, not_found} -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
|
@ -367,79 +277,9 @@ process_default_set(LUser, LServer, Value, Lang) ->
|
||||||
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
|
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
process_default_set_db(LUser, LServer, {value, Name},
|
|
||||||
mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
|
||||||
[] -> not_found;
|
|
||||||
[#privacy{lists = Lists} = P] ->
|
|
||||||
case lists:keymember(Name, 1, Lists) of
|
|
||||||
true ->
|
|
||||||
mnesia:write(P#privacy{default = Name,
|
|
||||||
lists = Lists}),
|
|
||||||
ok;
|
|
||||||
false -> not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
process_default_set_db(LUser, LServer, {value, Name}, riak) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{lists = Lists} = P} ->
|
|
||||||
case lists:keymember(Name, 1, Lists) of
|
|
||||||
true ->
|
|
||||||
ejabberd_riak:put(P#privacy{default = Name,
|
|
||||||
lists = Lists},
|
|
||||||
privacy_schema());
|
|
||||||
false ->
|
|
||||||
not_found
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
not_found
|
|
||||||
end};
|
|
||||||
process_default_set_db(LUser, LServer, {value, Name},
|
|
||||||
odbc) ->
|
|
||||||
F = fun () ->
|
|
||||||
case sql_get_privacy_list_names_t(LUser) of
|
|
||||||
{selected, []} -> not_found;
|
|
||||||
{selected, Names} ->
|
|
||||||
case lists:member({Name}, Names) of
|
|
||||||
true -> sql_set_default_privacy_list(LUser, Name), ok;
|
|
||||||
false -> not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
odbc_queries:sql_transaction(LServer, F);
|
|
||||||
process_default_set_db(LUser, LServer, false, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
|
||||||
[] -> ok;
|
|
||||||
[R] -> mnesia:write(R#privacy{default = none})
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
process_default_set_db(LUser, LServer, false, riak) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, R} ->
|
|
||||||
ejabberd_riak:put(R#privacy{default = none}, privacy_schema());
|
|
||||||
{error, _} ->
|
|
||||||
ok
|
|
||||||
end};
|
|
||||||
process_default_set_db(LUser, LServer, false, odbc) ->
|
|
||||||
case catch sql_unset_default_privacy_list(LUser,
|
|
||||||
LServer)
|
|
||||||
of
|
|
||||||
{'EXIT', _Reason} -> {atomic, error};
|
|
||||||
{error, _Reason} -> {atomic, error};
|
|
||||||
_ -> {atomic, ok}
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_active_set(LUser, LServer, {value, Name}) ->
|
process_active_set(LUser, LServer, {value, Name}) ->
|
||||||
case process_active_set(LUser, LServer, Name,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE))
|
case Mod:process_active_set(LUser, LServer, Name) of
|
||||||
of
|
|
||||||
error -> {error, ?ERR_ITEM_NOT_FOUND};
|
error -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||||
Items ->
|
Items ->
|
||||||
NeedDb = is_list_needdb(Items),
|
NeedDb = is_list_needdb(Items),
|
||||||
|
@ -449,157 +289,16 @@ process_active_set(LUser, LServer, {value, Name}) ->
|
||||||
process_active_set(_LUser, _LServer, false) ->
|
process_active_set(_LUser, _LServer, false) ->
|
||||||
{result, [], #userlist{}}.
|
{result, [], #userlist{}}.
|
||||||
|
|
||||||
process_active_set(LUser, LServer, Name, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
|
||||||
of
|
|
||||||
[] -> error;
|
|
||||||
[#privacy{lists = Lists}] ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
false -> error
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
process_active_set(LUser, LServer, Name, riak) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{lists = Lists}} ->
|
|
||||||
case lists:keysearch(Name, 1, Lists) of
|
|
||||||
{value, {_, List}} -> List;
|
|
||||||
false -> error
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
process_active_set(LUser, LServer, Name, odbc) ->
|
|
||||||
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
|
|
||||||
{selected, []} -> error;
|
|
||||||
{selected, [{ID}]} ->
|
|
||||||
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
lists:flatmap(fun raw_to_item/1, RItems);
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
_ -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
remove_privacy_list(LUser, LServer, Name, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:read({privacy, {LUser, LServer}}) of
|
|
||||||
[] -> ok;
|
|
||||||
[#privacy{default = Default, lists = Lists} = P] ->
|
|
||||||
if Name == Default -> conflict;
|
|
||||||
true ->
|
|
||||||
NewLists = lists:keydelete(Name, 1, Lists),
|
|
||||||
mnesia:write(P#privacy{lists = NewLists})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_privacy_list(LUser, LServer, Name, riak) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
|
||||||
if Name == Default ->
|
|
||||||
conflict;
|
|
||||||
true ->
|
|
||||||
NewLists = lists:keydelete(Name, 1, Lists),
|
|
||||||
ejabberd_riak:put(P#privacy{lists = NewLists},
|
|
||||||
privacy_schema())
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
ok
|
|
||||||
end};
|
|
||||||
remove_privacy_list(LUser, LServer, Name, odbc) ->
|
|
||||||
F = fun () ->
|
|
||||||
case sql_get_default_privacy_list_t(LUser) of
|
|
||||||
{selected, []} ->
|
|
||||||
sql_remove_privacy_list(LUser, Name), ok;
|
|
||||||
{selected, [{Default}]} ->
|
|
||||||
if Name == Default -> conflict;
|
|
||||||
true -> sql_remove_privacy_list(LUser, Name), ok
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
odbc_queries:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
|
set_privacy_list(#privacy{us = {_, LServer}} = Privacy) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
set_privacy_list(Privacy, DBType).
|
Mod:set_privacy_list(Privacy).
|
||||||
|
|
||||||
set_privacy_list(Privacy, mnesia) ->
|
|
||||||
mnesia:dirty_write(Privacy);
|
|
||||||
set_privacy_list(Privacy, riak) ->
|
|
||||||
ejabberd_riak:put(Privacy, privacy_schema());
|
|
||||||
set_privacy_list(#privacy{us = {LUser, LServer},
|
|
||||||
default = Default,
|
|
||||||
lists = Lists}, odbc) ->
|
|
||||||
F = fun() ->
|
|
||||||
lists:foreach(
|
|
||||||
fun({Name, List}) ->
|
|
||||||
sql_add_privacy_list(LUser, Name),
|
|
||||||
{selected, [<<"id">>], [[I]]} =
|
|
||||||
sql_get_privacy_list_id_t(LUser, Name),
|
|
||||||
RItems = lists:map(fun item_to_raw/1, List),
|
|
||||||
sql_set_privacy_list(I, RItems),
|
|
||||||
if is_binary(Default) ->
|
|
||||||
sql_set_default_privacy_list(LUser, Default),
|
|
||||||
ok;
|
|
||||||
true ->
|
|
||||||
ok
|
|
||||||
end
|
|
||||||
end, Lists)
|
|
||||||
end,
|
|
||||||
odbc_queries:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
set_privacy_list(LUser, LServer, Name, List, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
case mnesia:wread({privacy, {LUser, LServer}}) of
|
|
||||||
[] ->
|
|
||||||
NewLists = [{Name, List}],
|
|
||||||
mnesia:write(#privacy{us = {LUser, LServer},
|
|
||||||
lists = NewLists});
|
|
||||||
[#privacy{lists = Lists} = P] ->
|
|
||||||
NewLists1 = lists:keydelete(Name, 1, Lists),
|
|
||||||
NewLists = [{Name, List} | NewLists1],
|
|
||||||
mnesia:write(P#privacy{lists = NewLists})
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
set_privacy_list(LUser, LServer, Name, List, riak) ->
|
|
||||||
{atomic,
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{lists = Lists} = P} ->
|
|
||||||
NewLists1 = lists:keydelete(Name, 1, Lists),
|
|
||||||
NewLists = [{Name, List} | NewLists1],
|
|
||||||
ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema());
|
|
||||||
{error, _} ->
|
|
||||||
NewLists = [{Name, List}],
|
|
||||||
ejabberd_riak:put(#privacy{us = {LUser, LServer},
|
|
||||||
lists = NewLists},
|
|
||||||
privacy_schema())
|
|
||||||
end};
|
|
||||||
set_privacy_list(LUser, LServer, Name, List, odbc) ->
|
|
||||||
RItems = lists:map(fun item_to_raw/1, List),
|
|
||||||
F = fun () ->
|
|
||||||
ID = case sql_get_privacy_list_id_t(LUser, Name) of
|
|
||||||
{selected, []} ->
|
|
||||||
sql_add_privacy_list(LUser, Name),
|
|
||||||
{selected, [{I}]} =
|
|
||||||
sql_get_privacy_list_id_t(LUser, Name),
|
|
||||||
I;
|
|
||||||
{selected, [{I}]} -> I
|
|
||||||
end,
|
|
||||||
sql_set_privacy_list(ID, RItems),
|
|
||||||
ok
|
|
||||||
end,
|
|
||||||
odbc_queries:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
process_list_set(LUser, LServer, {value, Name}, Els, Lang) ->
|
process_list_set(LUser, LServer, {value, Name}, Els, Lang) ->
|
||||||
case parse_items(Els) of
|
case parse_items(Els) of
|
||||||
false -> {error, ?ERR_BAD_REQUEST};
|
false -> {error, ?ERR_BAD_REQUEST};
|
||||||
remove ->
|
remove ->
|
||||||
case remove_privacy_list(LUser, LServer, Name,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE))
|
case Mod:remove_privacy_list(LUser, LServer, Name) of
|
||||||
of
|
|
||||||
{atomic, conflict} ->
|
{atomic, conflict} ->
|
||||||
Txt = <<"Cannot remove default list">>,
|
Txt = <<"Cannot remove default list">>,
|
||||||
{error, ?ERRT_CONFLICT(Lang, Txt)};
|
{error, ?ERRT_CONFLICT(Lang, Txt)};
|
||||||
|
@ -615,9 +314,8 @@ process_list_set(LUser, LServer, {value, Name}, Els, Lang) ->
|
||||||
_ -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
_ -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)}
|
||||||
end;
|
end;
|
||||||
List ->
|
List ->
|
||||||
case set_privacy_list(LUser, LServer, Name, List,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE))
|
case Mod:set_privacy_list(LUser, LServer, Name, List) of
|
||||||
of
|
|
||||||
{atomic, ok} ->
|
{atomic, ok} ->
|
||||||
NeedDb = is_list_needdb(List),
|
NeedDb = is_list_needdb(List),
|
||||||
ejabberd_sm:route(jid:make(LUser, LServer,
|
ejabberd_sm:route(jid:make(LUser, LServer,
|
||||||
|
@ -737,105 +435,20 @@ is_list_needdb(Items) ->
|
||||||
end,
|
end,
|
||||||
Items).
|
Items).
|
||||||
|
|
||||||
get_user_list(Acc, User, Server) ->
|
get_user_list(_Acc, User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
{Default, Items} = get_user_list(Acc, LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)),
|
{Default, Items} = Mod:get_user_list(LUser, LServer),
|
||||||
NeedDb = is_list_needdb(Items),
|
NeedDb = is_list_needdb(Items),
|
||||||
#userlist{name = Default, list = Items,
|
#userlist{name = Default, list = Items,
|
||||||
needdb = NeedDb}.
|
needdb = NeedDb}.
|
||||||
|
|
||||||
get_user_list(_, LUser, LServer, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
|
||||||
of
|
|
||||||
[] -> {none, []};
|
|
||||||
[#privacy{default = Default, lists = Lists}] ->
|
|
||||||
case Default of
|
|
||||||
none -> {none, []};
|
|
||||||
_ ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> {Default, List};
|
|
||||||
_ -> {none, []}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
_ -> {none, []}
|
|
||||||
end;
|
|
||||||
get_user_list(_, LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{default = Default, lists = Lists}} ->
|
|
||||||
case Default of
|
|
||||||
none -> {none, []};
|
|
||||||
_ ->
|
|
||||||
case lists:keysearch(Default, 1, Lists) of
|
|
||||||
{value, {_, List}} -> {Default, List};
|
|
||||||
_ -> {none, []}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
{error, _} ->
|
|
||||||
{none, []}
|
|
||||||
end;
|
|
||||||
get_user_list(_, LUser, LServer, odbc) ->
|
|
||||||
case catch sql_get_default_privacy_list(LUser, LServer)
|
|
||||||
of
|
|
||||||
{selected, []} -> {none, []};
|
|
||||||
{selected, [{Default}]} ->
|
|
||||||
case catch sql_get_privacy_list_data(LUser, LServer,
|
|
||||||
Default) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
{Default, lists:flatmap(fun raw_to_item/1, RItems)};
|
|
||||||
_ -> {none, []}
|
|
||||||
end;
|
|
||||||
_ -> {none, []}
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_user_lists(User, Server) ->
|
get_user_lists(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
get_user_lists(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Mod:get_user_lists(LUser, LServer).
|
||||||
get_user_lists(LUser, LServer, mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
|
||||||
[#privacy{} = P] ->
|
|
||||||
{ok, P};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
get_user_lists(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
|
||||||
{ok, #privacy{} = P} ->
|
|
||||||
{ok, P};
|
|
||||||
{error, _} ->
|
|
||||||
error
|
|
||||||
end;
|
|
||||||
get_user_lists(LUser, LServer, odbc) ->
|
|
||||||
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
|
|
||||||
{selected, []} ->
|
|
||||||
none;
|
|
||||||
{selected, [{DefName}]} ->
|
|
||||||
DefName;
|
|
||||||
_ ->
|
|
||||||
none
|
|
||||||
end,
|
|
||||||
case catch sql_get_privacy_list_names(LUser, LServer) of
|
|
||||||
{selected, Names} ->
|
|
||||||
Lists =
|
|
||||||
lists:flatmap(
|
|
||||||
fun({Name}) ->
|
|
||||||
case catch sql_get_privacy_list_data(
|
|
||||||
LUser, LServer, Name) of
|
|
||||||
{selected, RItems} ->
|
|
||||||
[{Name, lists:flatmap(fun raw_to_item/1, RItems)}];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end, Names),
|
|
||||||
{ok, #privacy{default = Default,
|
|
||||||
us = {LUser, LServer},
|
|
||||||
lists = Lists}};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
%% From is the sender, To is the destination.
|
%% From is the sender, To is the destination.
|
||||||
%% If Dir = out, User@Server is the sender account (From).
|
%% If Dir = out, User@Server is the sender account (From).
|
||||||
|
@ -959,17 +572,8 @@ is_type_match(Type, Value, JID, Subscription, Groups) ->
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
remove_user(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:remove_user(LUser, LServer).
|
||||||
|
|
||||||
remove_user(LUser, LServer, mnesia) ->
|
|
||||||
F = fun () -> mnesia:delete({privacy, {LUser, LServer}})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_user(LUser, LServer, riak) ->
|
|
||||||
{atomic, ejabberd_riak:delete(privacy, {LUser, LServer})};
|
|
||||||
remove_user(LUser, LServer, odbc) ->
|
|
||||||
sql_del_privacy_lists(LUser, LServer).
|
|
||||||
|
|
||||||
updated_list(_, #userlist{name = OldName} = Old,
|
updated_list(_, #userlist{name = OldName} = Old,
|
||||||
#userlist{name = NewName} = New) ->
|
#userlist{name = NewName} = New) ->
|
||||||
|
@ -977,250 +581,17 @@ updated_list(_, #userlist{name = OldName} = Old,
|
||||||
true -> Old
|
true -> Old
|
||||||
end.
|
end.
|
||||||
|
|
||||||
raw_to_item({SType, SValue, SAction, Order, MatchAll,
|
export(LServer) ->
|
||||||
MatchIQ, MatchMessage, MatchPresenceIn,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
MatchPresenceOut} = Row) ->
|
Mod:export(LServer).
|
||||||
try
|
|
||||||
{Type, Value} = case SType of
|
|
||||||
<<"n">> -> {none, none};
|
|
||||||
<<"j">> ->
|
|
||||||
case jid:from_string(SValue) of
|
|
||||||
#jid{} = JID ->
|
|
||||||
{jid, jid:tolower(JID)}
|
|
||||||
end;
|
|
||||||
<<"g">> -> {group, SValue};
|
|
||||||
<<"s">> ->
|
|
||||||
case SValue of
|
|
||||||
<<"none">> -> {subscription, none};
|
|
||||||
<<"both">> -> {subscription, both};
|
|
||||||
<<"from">> -> {subscription, from};
|
|
||||||
<<"to">> -> {subscription, to}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
Action = case SAction of
|
|
||||||
<<"a">> -> allow;
|
|
||||||
<<"d">> -> deny
|
|
||||||
end,
|
|
||||||
[#listitem{type = Type, value = Value, action = Action,
|
|
||||||
order = Order, match_all = MatchAll, match_iq = MatchIQ,
|
|
||||||
match_message = MatchMessage,
|
|
||||||
match_presence_in = MatchPresenceIn,
|
|
||||||
match_presence_out = MatchPresenceOut}]
|
|
||||||
catch _:_ ->
|
|
||||||
?WARNING_MSG("failed to parse row: ~p", [Row]),
|
|
||||||
[]
|
|
||||||
end.
|
|
||||||
|
|
||||||
item_to_raw(#listitem{type = Type, value = Value,
|
|
||||||
action = Action, order = Order, match_all = MatchAll,
|
|
||||||
match_iq = MatchIQ, match_message = MatchMessage,
|
|
||||||
match_presence_in = MatchPresenceIn,
|
|
||||||
match_presence_out = MatchPresenceOut}) ->
|
|
||||||
{SType, SValue} = case Type of
|
|
||||||
none -> {<<"n">>, <<"">>};
|
|
||||||
jid ->
|
|
||||||
{<<"j">>,
|
|
||||||
ejabberd_odbc:escape(jid:to_string(Value))};
|
|
||||||
group -> {<<"g">>, ejabberd_odbc:escape(Value)};
|
|
||||||
subscription ->
|
|
||||||
case Value of
|
|
||||||
none -> {<<"s">>, <<"none">>};
|
|
||||||
both -> {<<"s">>, <<"both">>};
|
|
||||||
from -> {<<"s">>, <<"from">>};
|
|
||||||
to -> {<<"s">>, <<"to">>}
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
SAction = case Action of
|
|
||||||
allow -> <<"a">>;
|
|
||||||
deny -> <<"d">>
|
|
||||||
end,
|
|
||||||
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
|
|
||||||
MatchMessage, MatchPresenceIn, MatchPresenceOut}.
|
|
||||||
|
|
||||||
sql_get_default_privacy_list(LUser, LServer) ->
|
|
||||||
odbc_queries:get_default_privacy_list(LServer, LUser).
|
|
||||||
|
|
||||||
sql_get_default_privacy_list_t(LUser) ->
|
|
||||||
odbc_queries:get_default_privacy_list_t(LUser).
|
|
||||||
|
|
||||||
sql_get_privacy_list_names(LUser, LServer) ->
|
|
||||||
odbc_queries:get_privacy_list_names(LServer, LUser).
|
|
||||||
|
|
||||||
sql_get_privacy_list_names_t(LUser) ->
|
|
||||||
odbc_queries:get_privacy_list_names_t(LUser).
|
|
||||||
|
|
||||||
sql_get_privacy_list_id(LUser, LServer, Name) ->
|
|
||||||
odbc_queries:get_privacy_list_id(LServer, LUser, Name).
|
|
||||||
|
|
||||||
sql_get_privacy_list_id_t(LUser, Name) ->
|
|
||||||
odbc_queries:get_privacy_list_id_t(LUser, Name).
|
|
||||||
|
|
||||||
sql_get_privacy_list_data(LUser, LServer, Name) ->
|
|
||||||
odbc_queries:get_privacy_list_data(LServer, LUser, Name).
|
|
||||||
|
|
||||||
sql_get_privacy_list_data_t(LUser, Name) ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SName = ejabberd_odbc:escape(Name),
|
|
||||||
odbc_queries:get_privacy_list_data_t(Username, SName).
|
|
||||||
|
|
||||||
sql_get_privacy_list_data_by_id(ID, LServer) ->
|
|
||||||
odbc_queries:get_privacy_list_data_by_id(LServer, ID).
|
|
||||||
|
|
||||||
sql_get_privacy_list_data_by_id_t(ID) ->
|
|
||||||
odbc_queries:get_privacy_list_data_by_id_t(ID).
|
|
||||||
|
|
||||||
sql_set_default_privacy_list(LUser, Name) ->
|
|
||||||
odbc_queries:set_default_privacy_list(LUser, Name).
|
|
||||||
|
|
||||||
sql_unset_default_privacy_list(LUser, LServer) ->
|
|
||||||
odbc_queries:unset_default_privacy_list(LServer, LUser).
|
|
||||||
|
|
||||||
sql_remove_privacy_list(LUser, Name) ->
|
|
||||||
odbc_queries:remove_privacy_list(LUser, Name).
|
|
||||||
|
|
||||||
sql_add_privacy_list(LUser, Name) ->
|
|
||||||
odbc_queries:add_privacy_list(LUser, Name).
|
|
||||||
|
|
||||||
sql_set_privacy_list(ID, RItems) ->
|
|
||||||
odbc_queries:set_privacy_list(ID, RItems).
|
|
||||||
|
|
||||||
sql_del_privacy_lists(LUser, LServer) ->
|
|
||||||
odbc_queries:del_privacy_lists(LServer, LUser).
|
|
||||||
|
|
||||||
update_table() ->
|
|
||||||
Fields = record_info(fields, privacy),
|
|
||||||
case mnesia:table_info(privacy, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
privacy, Fields, set,
|
|
||||||
fun(#privacy{us = {U, _}}) -> U end,
|
|
||||||
fun(#privacy{us = {U, S}, default = Def, lists = Lists} = R) ->
|
|
||||||
NewLists =
|
|
||||||
lists:map(
|
|
||||||
fun({Name, Ls}) ->
|
|
||||||
NewLs =
|
|
||||||
lists:map(
|
|
||||||
fun(#listitem{value = Val} = L) ->
|
|
||||||
NewVal =
|
|
||||||
case Val of
|
|
||||||
{LU, LS, LR} ->
|
|
||||||
{iolist_to_binary(LU),
|
|
||||||
iolist_to_binary(LS),
|
|
||||||
iolist_to_binary(LR)};
|
|
||||||
none -> none;
|
|
||||||
both -> both;
|
|
||||||
from -> from;
|
|
||||||
to -> to;
|
|
||||||
_ -> iolist_to_binary(Val)
|
|
||||||
end,
|
|
||||||
L#listitem{value = NewVal}
|
|
||||||
end, Ls),
|
|
||||||
{iolist_to_binary(Name), NewLs}
|
|
||||||
end, Lists),
|
|
||||||
NewDef = case Def of
|
|
||||||
none -> none;
|
|
||||||
_ -> iolist_to_binary(Def)
|
|
||||||
end,
|
|
||||||
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
|
|
||||||
R#privacy{us = NewUS, default = NewDef,
|
|
||||||
lists = NewLists}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating privacy table", []),
|
|
||||||
mnesia:transform_table(privacy, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(Server) ->
|
|
||||||
case catch ejabberd_odbc:sql_query(jid:nameprep(Server),
|
|
||||||
[<<"select id from privacy_list order by "
|
|
||||||
"id desc limit 1;">>]) of
|
|
||||||
{selected, [<<"id">>], [[I]]} ->
|
|
||||||
put(id, jlib:binary_to_integer(I));
|
|
||||||
_ ->
|
|
||||||
put(id, 0)
|
|
||||||
end,
|
|
||||||
[{privacy,
|
|
||||||
fun(Host, #privacy{us = {LUser, LServer}, lists = Lists,
|
|
||||||
default = Default})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
if Default /= none ->
|
|
||||||
SDefault = ejabberd_odbc:escape(Default),
|
|
||||||
[[<<"delete from privacy_default_list where ">>,
|
|
||||||
<<"username='">>, Username, <<"';">>],
|
|
||||||
[<<"insert into privacy_default_list(username, "
|
|
||||||
"name) ">>,
|
|
||||||
<<"values ('">>, Username, <<"', '">>,
|
|
||||||
SDefault, <<"');">>]];
|
|
||||||
true ->
|
|
||||||
[]
|
|
||||||
end ++
|
|
||||||
lists:flatmap(
|
|
||||||
fun({Name, List}) ->
|
|
||||||
SName = ejabberd_odbc:escape(Name),
|
|
||||||
RItems = lists:map(fun item_to_raw/1, List),
|
|
||||||
ID = jlib:integer_to_binary(get_id()),
|
|
||||||
[[<<"delete from privacy_list where username='">>,
|
|
||||||
Username, <<"' and name='">>,
|
|
||||||
SName, <<"';">>],
|
|
||||||
[<<"insert into privacy_list(username, "
|
|
||||||
"name, id) values ('">>,
|
|
||||||
Username, <<"', '">>, SName,
|
|
||||||
<<"', '">>, ID, <<"');">>],
|
|
||||||
[<<"delete from privacy_list_data where "
|
|
||||||
"id='">>, ID, <<"';">>]] ++
|
|
||||||
[[<<"insert into privacy_list_data(id, t, "
|
|
||||||
"value, action, ord, match_all, match_iq, "
|
|
||||||
"match_message, match_presence_in, "
|
|
||||||
"match_presence_out) values ('">>,
|
|
||||||
ID, <<"', '">>, str:join(Items, <<"', '">>),
|
|
||||||
<<"');">>] || Items <- RItems]
|
|
||||||
end,
|
|
||||||
Lists);
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
get_id() ->
|
|
||||||
ID = get(id),
|
|
||||||
put(id, ID + 1),
|
|
||||||
ID + 1.
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select username from privacy_list;">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([LUser]) ->
|
Mod:import(LServer).
|
||||||
Default = case sql_get_default_privacy_list_t(LUser) of
|
|
||||||
{selected, [<<"name">>], []} ->
|
|
||||||
none;
|
|
||||||
{selected, [<<"name">>], [[DefName]]} ->
|
|
||||||
DefName;
|
|
||||||
_ ->
|
|
||||||
none
|
|
||||||
end,
|
|
||||||
{selected, [<<"name">>], Names} =
|
|
||||||
sql_get_privacy_list_names_t(LUser),
|
|
||||||
Lists = lists:flatmap(
|
|
||||||
fun([Name]) ->
|
|
||||||
case sql_get_privacy_list_data_t(LUser, Name) of
|
|
||||||
{selected, _, RItems} ->
|
|
||||||
[{Name,
|
|
||||||
lists:map(fun raw_to_item/1,
|
|
||||||
RItems)}];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end, Names),
|
|
||||||
#privacy{default = Default,
|
|
||||||
us = {LUser, LServer},
|
|
||||||
lists = Lists}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #privacy{} = P) ->
|
import(LServer, DBType, Data) ->
|
||||||
mnesia:dirty_write(P);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
import(_LServer, riak, #privacy{} = P) ->
|
Mod:import(LServer, Data).
|
||||||
ejabberd_riak:put(P, privacy_schema());
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_privacy_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_privacy).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, process_lists_get/2, process_list_get/3,
|
||||||
|
process_default_set/3, process_active_set/3,
|
||||||
|
remove_privacy_list/3, set_privacy_list/1,
|
||||||
|
set_privacy_list/4, get_user_list/2, get_user_lists/2,
|
||||||
|
remove_user/2, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_privacy.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(privacy,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, privacy)}]),
|
||||||
|
update_table().
|
||||||
|
|
||||||
|
process_lists_get(LUser, LServer) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
{'EXIT', _Reason} -> error;
|
||||||
|
[] -> {none, []};
|
||||||
|
[#privacy{default = Default, lists = Lists}] ->
|
||||||
|
LItems = lists:map(fun ({N, _}) ->
|
||||||
|
#xmlel{name = <<"list">>,
|
||||||
|
attrs = [{<<"name">>, N}],
|
||||||
|
children = []}
|
||||||
|
end, Lists),
|
||||||
|
{Default, LItems}
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_list_get(LUser, LServer, Name) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
{'EXIT', _Reason} -> error;
|
||||||
|
[] -> not_found;
|
||||||
|
[#privacy{lists = Lists}] ->
|
||||||
|
case lists:keysearch(Name, 1, Lists) of
|
||||||
|
{value, {_, List}} -> List;
|
||||||
|
_ -> not_found
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_default_set(LUser, LServer, {value, Name}) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
|
[] -> not_found;
|
||||||
|
[#privacy{lists = Lists} = P] ->
|
||||||
|
case lists:keymember(Name, 1, Lists) of
|
||||||
|
true ->
|
||||||
|
mnesia:write(P#privacy{default = Name,
|
||||||
|
lists = Lists}),
|
||||||
|
ok;
|
||||||
|
false -> not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F);
|
||||||
|
process_default_set(LUser, LServer, false) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
|
[] -> ok;
|
||||||
|
[R] -> mnesia:write(R#privacy{default = none})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
process_active_set(LUser, LServer, Name) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
[] -> error;
|
||||||
|
[#privacy{lists = Lists}] ->
|
||||||
|
case lists:keysearch(Name, 1, Lists) of
|
||||||
|
{value, {_, List}} -> List;
|
||||||
|
false -> error
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_privacy_list(LUser, LServer, Name) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mnesia:read({privacy, {LUser, LServer}}) of
|
||||||
|
[] -> ok;
|
||||||
|
[#privacy{default = Default, lists = Lists} = P] ->
|
||||||
|
if Name == Default -> conflict;
|
||||||
|
true ->
|
||||||
|
NewLists = lists:keydelete(Name, 1, Lists),
|
||||||
|
mnesia:write(P#privacy{lists = NewLists})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
set_privacy_list(Privacy) ->
|
||||||
|
mnesia:dirty_write(Privacy).
|
||||||
|
|
||||||
|
set_privacy_list(LUser, LServer, Name, List) ->
|
||||||
|
F = fun () ->
|
||||||
|
case mnesia:wread({privacy, {LUser, LServer}}) of
|
||||||
|
[] ->
|
||||||
|
NewLists = [{Name, List}],
|
||||||
|
mnesia:write(#privacy{us = {LUser, LServer},
|
||||||
|
lists = NewLists});
|
||||||
|
[#privacy{lists = Lists} = P] ->
|
||||||
|
NewLists1 = lists:keydelete(Name, 1, Lists),
|
||||||
|
NewLists = [{Name, List} | NewLists1],
|
||||||
|
mnesia:write(P#privacy{lists = NewLists})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
get_user_list(LUser, LServer) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer})
|
||||||
|
of
|
||||||
|
[] -> {none, []};
|
||||||
|
[#privacy{default = Default, lists = Lists}] ->
|
||||||
|
case Default of
|
||||||
|
none -> {none, []};
|
||||||
|
_ ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} -> {Default, List};
|
||||||
|
_ -> {none, []}
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
_ -> {none, []}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_user_lists(LUser, LServer) ->
|
||||||
|
case catch mnesia:dirty_read(privacy, {LUser, LServer}) of
|
||||||
|
[#privacy{} = P] ->
|
||||||
|
{ok, P};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
F = fun () -> mnesia:delete({privacy, {LUser, LServer}}) end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #privacy{} = P) ->
|
||||||
|
mnesia:dirty_write(P).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, privacy),
|
||||||
|
case mnesia:table_info(privacy, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
privacy, Fields, set,
|
||||||
|
fun(#privacy{us = {U, _}}) -> U end,
|
||||||
|
fun(#privacy{us = {U, S}, default = Def, lists = Lists} = R) ->
|
||||||
|
NewLists =
|
||||||
|
lists:map(
|
||||||
|
fun({Name, Ls}) ->
|
||||||
|
NewLs =
|
||||||
|
lists:map(
|
||||||
|
fun(#listitem{value = Val} = L) ->
|
||||||
|
NewVal =
|
||||||
|
case Val of
|
||||||
|
{LU, LS, LR} ->
|
||||||
|
{iolist_to_binary(LU),
|
||||||
|
iolist_to_binary(LS),
|
||||||
|
iolist_to_binary(LR)};
|
||||||
|
none -> none;
|
||||||
|
both -> both;
|
||||||
|
from -> from;
|
||||||
|
to -> to;
|
||||||
|
_ -> iolist_to_binary(Val)
|
||||||
|
end,
|
||||||
|
L#listitem{value = NewVal}
|
||||||
|
end, Ls),
|
||||||
|
{iolist_to_binary(Name), NewLs}
|
||||||
|
end, Lists),
|
||||||
|
NewDef = case Def of
|
||||||
|
none -> none;
|
||||||
|
_ -> iolist_to_binary(Def)
|
||||||
|
end,
|
||||||
|
NewUS = {iolist_to_binary(U), iolist_to_binary(S)},
|
||||||
|
R#privacy{us = NewUS, default = NewDef,
|
||||||
|
lists = NewLists}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating privacy table", []),
|
||||||
|
mnesia:transform_table(privacy, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,160 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_privacy_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_privacy).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, process_lists_get/2, process_list_get/3,
|
||||||
|
process_default_set/3, process_active_set/3,
|
||||||
|
remove_privacy_list/3, set_privacy_list/1,
|
||||||
|
set_privacy_list/4, get_user_list/2, get_user_lists/2,
|
||||||
|
remove_user/2, import/2]).
|
||||||
|
|
||||||
|
-export([privacy_schema/0]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_privacy.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
process_lists_get(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{default = Default, lists = Lists}} ->
|
||||||
|
LItems = lists:map(fun ({N, _}) ->
|
||||||
|
#xmlel{name = <<"list">>,
|
||||||
|
attrs = [{<<"name">>, N}],
|
||||||
|
children = []}
|
||||||
|
end,
|
||||||
|
Lists),
|
||||||
|
{Default, LItems};
|
||||||
|
{error, notfound} ->
|
||||||
|
{none, []};
|
||||||
|
{error, _} ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_list_get(LUser, LServer, Name) ->
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{lists = Lists}} ->
|
||||||
|
case lists:keysearch(Name, 1, Lists) of
|
||||||
|
{value, {_, List}} -> List;
|
||||||
|
_ -> not_found
|
||||||
|
end;
|
||||||
|
{error, notfound} ->
|
||||||
|
not_found;
|
||||||
|
{error, _} ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_default_set(LUser, LServer, {value, Name}) ->
|
||||||
|
{atomic,
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{lists = Lists} = P} ->
|
||||||
|
case lists:keymember(Name, 1, Lists) of
|
||||||
|
true ->
|
||||||
|
ejabberd_riak:put(P#privacy{default = Name,
|
||||||
|
lists = Lists},
|
||||||
|
privacy_schema());
|
||||||
|
false ->
|
||||||
|
not_found
|
||||||
|
end;
|
||||||
|
{error, _} ->
|
||||||
|
not_found
|
||||||
|
end};
|
||||||
|
process_default_set(LUser, LServer, false) ->
|
||||||
|
{atomic,
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, R} ->
|
||||||
|
ejabberd_riak:put(R#privacy{default = none}, privacy_schema());
|
||||||
|
{error, _} ->
|
||||||
|
ok
|
||||||
|
end}.
|
||||||
|
|
||||||
|
process_active_set(LUser, LServer, Name) ->
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{lists = Lists}} ->
|
||||||
|
case lists:keysearch(Name, 1, Lists) of
|
||||||
|
{value, {_, List}} -> List;
|
||||||
|
false -> error
|
||||||
|
end;
|
||||||
|
{error, _} ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_privacy_list(LUser, LServer, Name) ->
|
||||||
|
{atomic,
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{default = Default, lists = Lists} = P} ->
|
||||||
|
if Name == Default ->
|
||||||
|
conflict;
|
||||||
|
true ->
|
||||||
|
NewLists = lists:keydelete(Name, 1, Lists),
|
||||||
|
ejabberd_riak:put(P#privacy{lists = NewLists},
|
||||||
|
privacy_schema())
|
||||||
|
end;
|
||||||
|
{error, _} ->
|
||||||
|
ok
|
||||||
|
end}.
|
||||||
|
|
||||||
|
set_privacy_list(Privacy) ->
|
||||||
|
ejabberd_riak:put(Privacy, privacy_schema()).
|
||||||
|
|
||||||
|
set_privacy_list(LUser, LServer, Name, List) ->
|
||||||
|
{atomic,
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{lists = Lists} = P} ->
|
||||||
|
NewLists1 = lists:keydelete(Name, 1, Lists),
|
||||||
|
NewLists = [{Name, List} | NewLists1],
|
||||||
|
ejabberd_riak:put(P#privacy{lists = NewLists}, privacy_schema());
|
||||||
|
{error, _} ->
|
||||||
|
NewLists = [{Name, List}],
|
||||||
|
ejabberd_riak:put(#privacy{us = {LUser, LServer},
|
||||||
|
lists = NewLists},
|
||||||
|
privacy_schema())
|
||||||
|
end}.
|
||||||
|
|
||||||
|
get_user_list(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{default = Default, lists = Lists}} ->
|
||||||
|
case Default of
|
||||||
|
none -> {none, []};
|
||||||
|
_ ->
|
||||||
|
case lists:keysearch(Default, 1, Lists) of
|
||||||
|
{value, {_, List}} -> {Default, List};
|
||||||
|
_ -> {none, []}
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
{error, _} ->
|
||||||
|
{none, []}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_user_lists(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(privacy, privacy_schema(), {LUser, LServer}) of
|
||||||
|
{ok, #privacy{} = P} ->
|
||||||
|
{ok, P};
|
||||||
|
{error, _} ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete(privacy, {LUser, LServer})}.
|
||||||
|
|
||||||
|
import(_LServer, #privacy{} = P) ->
|
||||||
|
ejabberd_riak:put(P, privacy_schema()).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
privacy_schema() ->
|
||||||
|
{record_info(fields, privacy), #privacy{}}.
|
|
@ -0,0 +1,397 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_privacy_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_privacy).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, process_lists_get/2, process_list_get/3,
|
||||||
|
process_default_set/3, process_active_set/3,
|
||||||
|
remove_privacy_list/3, set_privacy_list/1,
|
||||||
|
set_privacy_list/4, get_user_list/2, get_user_lists/2,
|
||||||
|
remove_user/2, import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-export([item_to_raw/1, raw_to_item/1,
|
||||||
|
sql_add_privacy_list/2,
|
||||||
|
sql_get_default_privacy_list/2,
|
||||||
|
sql_get_default_privacy_list_t/1,
|
||||||
|
sql_get_privacy_list_data/3,
|
||||||
|
sql_get_privacy_list_data_by_id_t/1,
|
||||||
|
sql_get_privacy_list_id_t/2,
|
||||||
|
sql_set_default_privacy_list/2, sql_set_privacy_list/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_privacy.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
process_lists_get(LUser, LServer) ->
|
||||||
|
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
|
||||||
|
{selected, []} -> none;
|
||||||
|
{selected, [{DefName}]} -> DefName;
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
case catch sql_get_privacy_list_names(LUser, LServer) of
|
||||||
|
{selected, Names} ->
|
||||||
|
LItems = lists:map(fun ({N}) ->
|
||||||
|
#xmlel{name = <<"list">>,
|
||||||
|
attrs = [{<<"name">>, N}],
|
||||||
|
children = []}
|
||||||
|
end,
|
||||||
|
Names),
|
||||||
|
{Default, LItems};
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_list_get(LUser, LServer, Name) ->
|
||||||
|
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
|
||||||
|
{selected, []} -> not_found;
|
||||||
|
{selected, [{ID}]} ->
|
||||||
|
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
|
||||||
|
{selected, RItems} ->
|
||||||
|
lists:flatmap(fun raw_to_item/1, RItems);
|
||||||
|
_ -> error
|
||||||
|
end;
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_default_set(LUser, LServer, {value, Name}) ->
|
||||||
|
F = fun () ->
|
||||||
|
case sql_get_privacy_list_names_t(LUser) of
|
||||||
|
{selected, []} -> not_found;
|
||||||
|
{selected, Names} ->
|
||||||
|
case lists:member({Name}, Names) of
|
||||||
|
true -> sql_set_default_privacy_list(LUser, Name), ok;
|
||||||
|
false -> not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
odbc_queries:sql_transaction(LServer, F);
|
||||||
|
process_default_set(LUser, LServer, false) ->
|
||||||
|
case catch sql_unset_default_privacy_list(LUser,
|
||||||
|
LServer)
|
||||||
|
of
|
||||||
|
{'EXIT', _Reason} -> {atomic, error};
|
||||||
|
{error, _Reason} -> {atomic, error};
|
||||||
|
_ -> {atomic, ok}
|
||||||
|
end.
|
||||||
|
|
||||||
|
process_active_set(LUser, LServer, Name) ->
|
||||||
|
case catch sql_get_privacy_list_id(LUser, LServer, Name) of
|
||||||
|
{selected, []} -> error;
|
||||||
|
{selected, [{ID}]} ->
|
||||||
|
case catch sql_get_privacy_list_data_by_id(ID, LServer) of
|
||||||
|
{selected, RItems} ->
|
||||||
|
lists:flatmap(fun raw_to_item/1, RItems);
|
||||||
|
_ -> error
|
||||||
|
end;
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_privacy_list(LUser, LServer, Name) ->
|
||||||
|
F = fun () ->
|
||||||
|
case sql_get_default_privacy_list_t(LUser) of
|
||||||
|
{selected, []} ->
|
||||||
|
sql_remove_privacy_list(LUser, Name), ok;
|
||||||
|
{selected, [{Default}]} ->
|
||||||
|
if Name == Default -> conflict;
|
||||||
|
true -> sql_remove_privacy_list(LUser, Name), ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
odbc_queries:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
set_privacy_list(#privacy{us = {LUser, LServer},
|
||||||
|
default = Default,
|
||||||
|
lists = Lists}) ->
|
||||||
|
F = fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({Name, List}) ->
|
||||||
|
sql_add_privacy_list(LUser, Name),
|
||||||
|
{selected, [<<"id">>], [[I]]} =
|
||||||
|
sql_get_privacy_list_id_t(LUser, Name),
|
||||||
|
RItems = lists:map(fun item_to_raw/1, List),
|
||||||
|
sql_set_privacy_list(I, RItems),
|
||||||
|
if is_binary(Default) ->
|
||||||
|
sql_set_default_privacy_list(LUser, Default),
|
||||||
|
ok;
|
||||||
|
true ->
|
||||||
|
ok
|
||||||
|
end
|
||||||
|
end, Lists)
|
||||||
|
end,
|
||||||
|
odbc_queries:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
set_privacy_list(LUser, LServer, Name, List) ->
|
||||||
|
RItems = lists:map(fun item_to_raw/1, List),
|
||||||
|
F = fun () ->
|
||||||
|
ID = case sql_get_privacy_list_id_t(LUser, Name) of
|
||||||
|
{selected, []} ->
|
||||||
|
sql_add_privacy_list(LUser, Name),
|
||||||
|
{selected, [{I}]} =
|
||||||
|
sql_get_privacy_list_id_t(LUser, Name),
|
||||||
|
I;
|
||||||
|
{selected, [{I}]} -> I
|
||||||
|
end,
|
||||||
|
sql_set_privacy_list(ID, RItems),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
odbc_queries:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
get_user_list(LUser, LServer) ->
|
||||||
|
case catch sql_get_default_privacy_list(LUser, LServer)
|
||||||
|
of
|
||||||
|
{selected, []} -> {none, []};
|
||||||
|
{selected, [{Default}]} ->
|
||||||
|
case catch sql_get_privacy_list_data(LUser, LServer,
|
||||||
|
Default) of
|
||||||
|
{selected, RItems} ->
|
||||||
|
{Default, lists:flatmap(fun raw_to_item/1, RItems)};
|
||||||
|
_ -> {none, []}
|
||||||
|
end;
|
||||||
|
_ -> {none, []}
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_user_lists(LUser, LServer) ->
|
||||||
|
Default = case catch sql_get_default_privacy_list(LUser, LServer) of
|
||||||
|
{selected, []} ->
|
||||||
|
none;
|
||||||
|
{selected, [{DefName}]} ->
|
||||||
|
DefName;
|
||||||
|
_ ->
|
||||||
|
none
|
||||||
|
end,
|
||||||
|
case catch sql_get_privacy_list_names(LUser, LServer) of
|
||||||
|
{selected, Names} ->
|
||||||
|
Lists =
|
||||||
|
lists:flatmap(
|
||||||
|
fun({Name}) ->
|
||||||
|
case catch sql_get_privacy_list_data(
|
||||||
|
LUser, LServer, Name) of
|
||||||
|
{selected, RItems} ->
|
||||||
|
[{Name, lists:flatmap(fun raw_to_item/1, RItems)}];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end, Names),
|
||||||
|
{ok, #privacy{default = Default,
|
||||||
|
us = {LUser, LServer},
|
||||||
|
lists = Lists}};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
sql_del_privacy_lists(LUser, LServer).
|
||||||
|
|
||||||
|
export(Server) ->
|
||||||
|
case catch ejabberd_odbc:sql_query(jid:nameprep(Server),
|
||||||
|
[<<"select id from privacy_list order by "
|
||||||
|
"id desc limit 1;">>]) of
|
||||||
|
{selected, [<<"id">>], [[I]]} ->
|
||||||
|
put(id, jlib:binary_to_integer(I));
|
||||||
|
_ ->
|
||||||
|
put(id, 0)
|
||||||
|
end,
|
||||||
|
[{privacy,
|
||||||
|
fun(Host, #privacy{us = {LUser, LServer}, lists = Lists,
|
||||||
|
default = Default})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
if Default /= none ->
|
||||||
|
SDefault = ejabberd_odbc:escape(Default),
|
||||||
|
[[<<"delete from privacy_default_list where ">>,
|
||||||
|
<<"username='">>, Username, <<"';">>],
|
||||||
|
[<<"insert into privacy_default_list(username, "
|
||||||
|
"name) ">>,
|
||||||
|
<<"values ('">>, Username, <<"', '">>,
|
||||||
|
SDefault, <<"');">>]];
|
||||||
|
true ->
|
||||||
|
[]
|
||||||
|
end ++
|
||||||
|
lists:flatmap(
|
||||||
|
fun({Name, List}) ->
|
||||||
|
SName = ejabberd_odbc:escape(Name),
|
||||||
|
RItems = lists:map(fun item_to_raw/1, List),
|
||||||
|
ID = jlib:integer_to_binary(get_id()),
|
||||||
|
[[<<"delete from privacy_list where username='">>,
|
||||||
|
Username, <<"' and name='">>,
|
||||||
|
SName, <<"';">>],
|
||||||
|
[<<"insert into privacy_list(username, "
|
||||||
|
"name, id) values ('">>,
|
||||||
|
Username, <<"', '">>, SName,
|
||||||
|
<<"', '">>, ID, <<"');">>],
|
||||||
|
[<<"delete from privacy_list_data where "
|
||||||
|
"id='">>, ID, <<"';">>]] ++
|
||||||
|
[[<<"insert into privacy_list_data(id, t, "
|
||||||
|
"value, action, ord, match_all, match_iq, "
|
||||||
|
"match_message, match_presence_in, "
|
||||||
|
"match_presence_out) values ('">>,
|
||||||
|
ID, <<"', '">>, str:join(Items, <<"', '">>),
|
||||||
|
<<"');">>] || Items <- RItems]
|
||||||
|
end,
|
||||||
|
Lists);
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
get_id() ->
|
||||||
|
ID = get(id),
|
||||||
|
put(id, ID + 1),
|
||||||
|
ID + 1.
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username from privacy_list;">>,
|
||||||
|
fun([LUser]) ->
|
||||||
|
Default = case sql_get_default_privacy_list_t(LUser) of
|
||||||
|
{selected, [<<"name">>], []} ->
|
||||||
|
none;
|
||||||
|
{selected, [<<"name">>], [[DefName]]} ->
|
||||||
|
DefName;
|
||||||
|
_ ->
|
||||||
|
none
|
||||||
|
end,
|
||||||
|
{selected, [<<"name">>], Names} =
|
||||||
|
sql_get_privacy_list_names_t(LUser),
|
||||||
|
Lists = lists:flatmap(
|
||||||
|
fun([Name]) ->
|
||||||
|
case sql_get_privacy_list_data_t(LUser, Name) of
|
||||||
|
{selected, _, RItems} ->
|
||||||
|
[{Name,
|
||||||
|
lists:map(fun raw_to_item/1,
|
||||||
|
RItems)}];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end, Names),
|
||||||
|
#privacy{default = Default,
|
||||||
|
us = {LUser, LServer},
|
||||||
|
lists = Lists}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
raw_to_item({SType, SValue, SAction, Order, MatchAll,
|
||||||
|
MatchIQ, MatchMessage, MatchPresenceIn,
|
||||||
|
MatchPresenceOut} = Row) ->
|
||||||
|
try
|
||||||
|
{Type, Value} = case SType of
|
||||||
|
<<"n">> -> {none, none};
|
||||||
|
<<"j">> ->
|
||||||
|
case jid:from_string(SValue) of
|
||||||
|
#jid{} = JID ->
|
||||||
|
{jid, jid:tolower(JID)}
|
||||||
|
end;
|
||||||
|
<<"g">> -> {group, SValue};
|
||||||
|
<<"s">> ->
|
||||||
|
case SValue of
|
||||||
|
<<"none">> -> {subscription, none};
|
||||||
|
<<"both">> -> {subscription, both};
|
||||||
|
<<"from">> -> {subscription, from};
|
||||||
|
<<"to">> -> {subscription, to}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
Action = case SAction of
|
||||||
|
<<"a">> -> allow;
|
||||||
|
<<"d">> -> deny
|
||||||
|
end,
|
||||||
|
[#listitem{type = Type, value = Value, action = Action,
|
||||||
|
order = Order, match_all = MatchAll, match_iq = MatchIQ,
|
||||||
|
match_message = MatchMessage,
|
||||||
|
match_presence_in = MatchPresenceIn,
|
||||||
|
match_presence_out = MatchPresenceOut}]
|
||||||
|
catch _:_ ->
|
||||||
|
?WARNING_MSG("failed to parse row: ~p", [Row]),
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
item_to_raw(#listitem{type = Type, value = Value,
|
||||||
|
action = Action, order = Order, match_all = MatchAll,
|
||||||
|
match_iq = MatchIQ, match_message = MatchMessage,
|
||||||
|
match_presence_in = MatchPresenceIn,
|
||||||
|
match_presence_out = MatchPresenceOut}) ->
|
||||||
|
{SType, SValue} = case Type of
|
||||||
|
none -> {<<"n">>, <<"">>};
|
||||||
|
jid ->
|
||||||
|
{<<"j">>,
|
||||||
|
ejabberd_odbc:escape(jid:to_string(Value))};
|
||||||
|
group -> {<<"g">>, ejabberd_odbc:escape(Value)};
|
||||||
|
subscription ->
|
||||||
|
case Value of
|
||||||
|
none -> {<<"s">>, <<"none">>};
|
||||||
|
both -> {<<"s">>, <<"both">>};
|
||||||
|
from -> {<<"s">>, <<"from">>};
|
||||||
|
to -> {<<"s">>, <<"to">>}
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
SAction = case Action of
|
||||||
|
allow -> <<"a">>;
|
||||||
|
deny -> <<"d">>
|
||||||
|
end,
|
||||||
|
{SType, SValue, SAction, Order, MatchAll, MatchIQ,
|
||||||
|
MatchMessage, MatchPresenceIn, MatchPresenceOut}.
|
||||||
|
|
||||||
|
sql_get_default_privacy_list(LUser, LServer) ->
|
||||||
|
odbc_queries:get_default_privacy_list(LServer, LUser).
|
||||||
|
|
||||||
|
sql_get_default_privacy_list_t(LUser) ->
|
||||||
|
odbc_queries:get_default_privacy_list_t(LUser).
|
||||||
|
|
||||||
|
sql_get_privacy_list_names(LUser, LServer) ->
|
||||||
|
odbc_queries:get_privacy_list_names(LServer, LUser).
|
||||||
|
|
||||||
|
sql_get_privacy_list_names_t(LUser) ->
|
||||||
|
odbc_queries:get_privacy_list_names_t(LUser).
|
||||||
|
|
||||||
|
sql_get_privacy_list_id(LUser, LServer, Name) ->
|
||||||
|
odbc_queries:get_privacy_list_id(LServer, LUser, Name).
|
||||||
|
|
||||||
|
sql_get_privacy_list_id_t(LUser, Name) ->
|
||||||
|
odbc_queries:get_privacy_list_id_t(LUser, Name).
|
||||||
|
|
||||||
|
sql_get_privacy_list_data(LUser, LServer, Name) ->
|
||||||
|
odbc_queries:get_privacy_list_data(LServer, LUser, Name).
|
||||||
|
|
||||||
|
sql_get_privacy_list_data_t(LUser, Name) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SName = ejabberd_odbc:escape(Name),
|
||||||
|
odbc_queries:get_privacy_list_data_t(Username, SName).
|
||||||
|
|
||||||
|
sql_get_privacy_list_data_by_id(ID, LServer) ->
|
||||||
|
odbc_queries:get_privacy_list_data_by_id(LServer, ID).
|
||||||
|
|
||||||
|
sql_get_privacy_list_data_by_id_t(ID) ->
|
||||||
|
odbc_queries:get_privacy_list_data_by_id_t(ID).
|
||||||
|
|
||||||
|
sql_set_default_privacy_list(LUser, Name) ->
|
||||||
|
odbc_queries:set_default_privacy_list(LUser, Name).
|
||||||
|
|
||||||
|
sql_unset_default_privacy_list(LUser, LServer) ->
|
||||||
|
odbc_queries:unset_default_privacy_list(LServer, LUser).
|
||||||
|
|
||||||
|
sql_remove_privacy_list(LUser, Name) ->
|
||||||
|
odbc_queries:remove_privacy_list(LUser, Name).
|
||||||
|
|
||||||
|
sql_add_privacy_list(LUser, Name) ->
|
||||||
|
odbc_queries:add_privacy_list(LUser, Name).
|
||||||
|
|
||||||
|
sql_set_privacy_list(ID, RItems) ->
|
||||||
|
odbc_queries:set_privacy_list(ID, RItems).
|
||||||
|
|
||||||
|
sql_del_privacy_lists(LUser, LServer) ->
|
||||||
|
odbc_queries:del_privacy_lists(LServer, LUser).
|
|
@ -39,12 +39,14 @@
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
-include("mod_private.hrl").
|
||||||
|
|
||||||
-record(private_storage,
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
|
-callback import(binary(), #private_storage{}) -> ok | pass.
|
||||||
'$1' | '_'},
|
-callback set_data(binary(), binary(), [{binary(), xmlel()}]) -> {atomic, any()}.
|
||||||
xml = #xmlel{} :: xmlel() | '_' | '$1'}).
|
-callback get_data(binary(), binary(), binary()) -> {ok, xmlel()} | error.
|
||||||
|
-callback get_all_data(binary(), binary()) -> [xmlel()].
|
||||||
|
|
||||||
-define(Xmlel_Query(Attrs, Children),
|
-define(Xmlel_Query(Attrs, Children),
|
||||||
#xmlel{name = <<"query">>, attrs = Attrs,
|
#xmlel{name = <<"query">>, attrs = Attrs,
|
||||||
children = Children}).
|
children = Children}).
|
||||||
|
@ -52,15 +54,8 @@
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
one_queue),
|
one_queue),
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(private_storage,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, private_storage)}]),
|
|
||||||
update_table();
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
||||||
remove_user, 50),
|
remove_user, 50),
|
||||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||||
|
@ -139,190 +134,44 @@ filter_xmlels([_ | Xmlels], Data) ->
|
||||||
filter_xmlels(Xmlels, Data).
|
filter_xmlels(Xmlels, Data).
|
||||||
|
|
||||||
set_data(LUser, LServer, Data) ->
|
set_data(LUser, LServer, Data) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
F = fun () ->
|
Mod:set_data(LUser, LServer, Data).
|
||||||
lists:foreach(fun (Datum) ->
|
|
||||||
set_data(LUser, LServer,
|
|
||||||
Datum, DBType)
|
|
||||||
end,
|
|
||||||
Data)
|
|
||||||
end,
|
|
||||||
case DBType of
|
|
||||||
odbc -> ejabberd_odbc:sql_transaction(LServer, F);
|
|
||||||
mnesia -> mnesia:transaction(F);
|
|
||||||
riak -> F()
|
|
||||||
end.
|
|
||||||
|
|
||||||
set_data(LUser, LServer, {XmlNS, Xmlel}, mnesia) ->
|
|
||||||
mnesia:write(#private_storage{usns =
|
|
||||||
{LUser, LServer, XmlNS},
|
|
||||||
xml = Xmlel});
|
|
||||||
set_data(LUser, LServer, {XMLNS, El}, odbc) ->
|
|
||||||
SData = fxml:element_to_binary(El),
|
|
||||||
odbc_queries:set_private_data(LServer, LUser, XMLNS, SData);
|
|
||||||
set_data(LUser, LServer, {XMLNS, El}, riak) ->
|
|
||||||
ejabberd_riak:put(#private_storage{usns = {LUser, LServer, XMLNS},
|
|
||||||
xml = El},
|
|
||||||
private_storage_schema(),
|
|
||||||
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
|
|
||||||
|
|
||||||
get_data(LUser, LServer, Data) ->
|
get_data(LUser, LServer, Data) ->
|
||||||
get_data(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE), Data, []).
|
get_data(LUser, LServer, Data, Mod, []).
|
||||||
|
|
||||||
get_data(_LUser, _LServer, _DBType, [],
|
get_data(_LUser, _LServer, [], _Mod, Storage_Xmlels) ->
|
||||||
Storage_Xmlels) ->
|
|
||||||
lists:reverse(Storage_Xmlels);
|
lists:reverse(Storage_Xmlels);
|
||||||
get_data(LUser, LServer, mnesia,
|
get_data(LUser, LServer, [{XmlNS, Xmlel} | Data], Mod, Storage_Xmlels) ->
|
||||||
[{XmlNS, Xmlel} | Data], Storage_Xmlels) ->
|
case Mod:get_data(LUser, LServer, XmlNS) of
|
||||||
case mnesia:dirty_read(private_storage,
|
{ok, Storage_Xmlel} ->
|
||||||
{LUser, LServer, XmlNS})
|
get_data(LUser, LServer, Data, Mod, [Storage_Xmlel | Storage_Xmlels]);
|
||||||
of
|
error ->
|
||||||
[#private_storage{xml = Storage_Xmlel}] ->
|
get_data(LUser, LServer, Data, Mod, [Xmlel | Storage_Xmlels])
|
||||||
get_data(LUser, LServer, mnesia, Data,
|
|
||||||
[Storage_Xmlel | Storage_Xmlels]);
|
|
||||||
_ ->
|
|
||||||
get_data(LUser, LServer, mnesia, Data,
|
|
||||||
[Xmlel | Storage_Xmlels])
|
|
||||||
end;
|
|
||||||
get_data(LUser, LServer, odbc, [{XMLNS, El} | Els],
|
|
||||||
Res) ->
|
|
||||||
case catch odbc_queries:get_private_data(LServer,
|
|
||||||
LUser, XMLNS)
|
|
||||||
of
|
|
||||||
{selected, [{SData}]} ->
|
|
||||||
case fxml_stream:parse_element(SData) of
|
|
||||||
Data when is_record(Data, xmlel) ->
|
|
||||||
get_data(LUser, LServer, odbc, Els, [Data | Res])
|
|
||||||
end;
|
|
||||||
_ -> get_data(LUser, LServer, odbc, Els, [El | Res])
|
|
||||||
end;
|
|
||||||
get_data(LUser, LServer, riak, [{XMLNS, El} | Els],
|
|
||||||
Res) ->
|
|
||||||
case ejabberd_riak:get(private_storage, private_storage_schema(),
|
|
||||||
{LUser, LServer, XMLNS}) of
|
|
||||||
{ok, #private_storage{xml = NewEl}} ->
|
|
||||||
get_data(LUser, LServer, riak, Els, [NewEl|Res]);
|
|
||||||
_ ->
|
|
||||||
get_data(LUser, LServer, riak, Els, [El|Res])
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_data(LUser, LServer) ->
|
get_data(LUser, LServer) ->
|
||||||
get_all_data(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_all_data(LUser, LServer).
|
||||||
|
|
||||||
get_all_data(LUser, LServer, mnesia) ->
|
|
||||||
lists:flatten(
|
|
||||||
mnesia:dirty_select(private_storage,
|
|
||||||
[{#private_storage{usns = {LUser, LServer, '_'},
|
|
||||||
xml = '$1'},
|
|
||||||
[], ['$1']}]));
|
|
||||||
get_all_data(LUser, LServer, odbc) ->
|
|
||||||
case catch odbc_queries:get_private_data(LServer, LUser) of
|
|
||||||
{selected, Res} ->
|
|
||||||
lists:flatmap(
|
|
||||||
fun({_, SData}) ->
|
|
||||||
case fxml_stream:parse_element(SData) of
|
|
||||||
#xmlel{} = El ->
|
|
||||||
[El];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end, Res);
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end;
|
|
||||||
get_all_data(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get_by_index(
|
|
||||||
private_storage, private_storage_schema(),
|
|
||||||
<<"us">>, {LUser, LServer}) of
|
|
||||||
{ok, Res} ->
|
|
||||||
[El || #private_storage{xml = El} <- Res];
|
|
||||||
_ ->
|
|
||||||
[]
|
|
||||||
end.
|
|
||||||
|
|
||||||
private_storage_schema() ->
|
|
||||||
{record_info(fields, private_storage), #private_storage{}}.
|
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
remove_user(LUser, LServer,
|
Mod = gen_mod:db_mod(Server, ?MODULE),
|
||||||
gen_mod:db_type(Server, ?MODULE)).
|
Mod:remove_user(LUser, LServer).
|
||||||
|
|
||||||
remove_user(LUser, LServer, mnesia) ->
|
export(LServer) ->
|
||||||
F = fun () ->
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Namespaces = mnesia:select(private_storage,
|
Mod:export(LServer).
|
||||||
[{#private_storage{usns =
|
|
||||||
{LUser,
|
|
||||||
LServer,
|
|
||||||
'$1'},
|
|
||||||
_ = '_'},
|
|
||||||
[], ['$$']}]),
|
|
||||||
lists:foreach(fun ([Namespace]) ->
|
|
||||||
mnesia:delete({private_storage,
|
|
||||||
{LUser, LServer,
|
|
||||||
Namespace}})
|
|
||||||
end,
|
|
||||||
Namespaces)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_user(LUser, LServer, odbc) ->
|
|
||||||
odbc_queries:del_user_private_storage(LServer, LUser);
|
|
||||||
remove_user(LUser, LServer, riak) ->
|
|
||||||
{atomic, ejabberd_riak:delete_by_index(private_storage,
|
|
||||||
<<"us">>, {LUser, LServer})}.
|
|
||||||
|
|
||||||
update_table() ->
|
|
||||||
Fields = record_info(fields, private_storage),
|
|
||||||
case mnesia:table_info(private_storage, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
private_storage, Fields, set,
|
|
||||||
fun(#private_storage{usns = {U, _, _}}) -> U end,
|
|
||||||
fun(#private_storage{usns = {U, S, NS}, xml = El} = R) ->
|
|
||||||
R#private_storage{usns = {iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S),
|
|
||||||
iolist_to_binary(NS)},
|
|
||||||
xml = fxml:to_xmlel(El)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating private_storage table", []),
|
|
||||||
mnesia:transform_table(private_storage, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{private_storage,
|
|
||||||
fun(Host, #private_storage{usns = {LUser, LServer, XMLNS},
|
|
||||||
xml = Data})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
LXMLNS = ejabberd_odbc:escape(XMLNS),
|
|
||||||
SData =
|
|
||||||
ejabberd_odbc:escape(fxml:element_to_binary(Data)),
|
|
||||||
odbc_queries:set_private_data_sql(Username, LXMLNS,
|
|
||||||
SData);
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select username, namespace, data from private_storage;">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([LUser, XMLNS, XML]) ->
|
Mod:import(LServer).
|
||||||
El = #xmlel{} = fxml_stream:parse_element(XML),
|
|
||||||
#private_storage{usns = {LUser, LServer, XMLNS},
|
|
||||||
xml = El}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #private_storage{} = PS) ->
|
import(LServer, DBType, PD) ->
|
||||||
mnesia:dirty_write(PS);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
|
Mod:import(LServer, PD).
|
||||||
import(_LServer, riak, #private_storage{usns = {LUser, LServer, _}} = PS) ->
|
|
||||||
ejabberd_riak:put(PS, private_storage_schema(),
|
|
||||||
[{'2i', [{<<"us">>, {LUser, LServer}}]}]);
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_private_mnesia).
|
||||||
|
-behaviour(mod_private).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, set_data/3, get_data/3, get_all_data/2, remove_user/2,
|
||||||
|
import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_private.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(private_storage,
|
||||||
|
[{disc_only_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, private_storage)}]),
|
||||||
|
update_table().
|
||||||
|
|
||||||
|
set_data(LUser, LServer, Data) ->
|
||||||
|
F = fun () ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({XmlNS, Xmlel}) ->
|
||||||
|
mnesia:write(
|
||||||
|
#private_storage{
|
||||||
|
usns = {LUser, LServer, XmlNS},
|
||||||
|
xml = Xmlel})
|
||||||
|
end, Data)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
get_data(LUser, LServer, XmlNS) ->
|
||||||
|
case mnesia:dirty_read(private_storage, {LUser, LServer, XmlNS}) of
|
||||||
|
[#private_storage{xml = Storage_Xmlel}] ->
|
||||||
|
{ok, Storage_Xmlel};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_all_data(LUser, LServer) ->
|
||||||
|
lists:flatten(
|
||||||
|
mnesia:dirty_select(private_storage,
|
||||||
|
[{#private_storage{usns = {LUser, LServer, '_'},
|
||||||
|
xml = '$1'},
|
||||||
|
[], ['$1']}])).
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
F = fun () ->
|
||||||
|
Namespaces = mnesia:select(private_storage,
|
||||||
|
[{#private_storage{usns =
|
||||||
|
{LUser,
|
||||||
|
LServer,
|
||||||
|
'$1'},
|
||||||
|
_ = '_'},
|
||||||
|
[], ['$$']}]),
|
||||||
|
lists:foreach(fun ([Namespace]) ->
|
||||||
|
mnesia:delete({private_storage,
|
||||||
|
{LUser, LServer,
|
||||||
|
Namespace}})
|
||||||
|
end,
|
||||||
|
Namespaces)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #private_storage{} = PS) ->
|
||||||
|
mnesia:dirty_write(PS).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, private_storage),
|
||||||
|
case mnesia:table_info(private_storage, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
private_storage, Fields, set,
|
||||||
|
fun(#private_storage{usns = {U, _, _}}) -> U end,
|
||||||
|
fun(#private_storage{usns = {U, S, NS}, xml = El} = R) ->
|
||||||
|
R#private_storage{usns = {iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S),
|
||||||
|
iolist_to_binary(NS)},
|
||||||
|
xml = fxml:to_xmlel(El)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating private_storage table", []),
|
||||||
|
mnesia:transform_table(private_storage, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,67 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_private_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_private).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, set_data/3, get_data/3, get_all_data/2, remove_user/2,
|
||||||
|
import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_private.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
set_data(LUser, LServer, Data) ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({XMLNS, El}) ->
|
||||||
|
ejabberd_riak:put(#private_storage{usns = {LUser, LServer, XMLNS},
|
||||||
|
xml = El},
|
||||||
|
private_storage_schema(),
|
||||||
|
[{'2i', [{<<"us">>, {LUser, LServer}}]}])
|
||||||
|
end, Data),
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
get_data(LUser, LServer, XMLNS) ->
|
||||||
|
case ejabberd_riak:get(private_storage, private_storage_schema(),
|
||||||
|
{LUser, LServer, XMLNS}) of
|
||||||
|
{ok, #private_storage{xml = El}} ->
|
||||||
|
{ok, El};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_all_data(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get_by_index(
|
||||||
|
private_storage, private_storage_schema(),
|
||||||
|
<<"us">>, {LUser, LServer}) of
|
||||||
|
{ok, Res} ->
|
||||||
|
[El || #private_storage{xml = El} <- Res];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete_by_index(private_storage,
|
||||||
|
<<"us">>, {LUser, LServer})}.
|
||||||
|
|
||||||
|
import(_LServer, #private_storage{usns = {LUser, LServer, _}} = PS) ->
|
||||||
|
ejabberd_riak:put(PS, private_storage_schema(),
|
||||||
|
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
private_storage_schema() ->
|
||||||
|
{record_info(fields, private_storage), #private_storage{}}.
|
|
@ -0,0 +1,97 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_private_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_private).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, set_data/3, get_data/3, get_all_data/2, remove_user/2,
|
||||||
|
import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_private.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
set_data(LUser, LServer, Data) ->
|
||||||
|
F = fun() ->
|
||||||
|
lists:foreach(
|
||||||
|
fun({XMLNS, El}) ->
|
||||||
|
SData = fxml:element_to_binary(El),
|
||||||
|
odbc_queries:set_private_data(
|
||||||
|
LServer, LUser, XMLNS, SData)
|
||||||
|
end, Data)
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
get_data(LUser, LServer, XMLNS) ->
|
||||||
|
case catch odbc_queries:get_private_data(LServer, LUser, XMLNS) of
|
||||||
|
{selected, [{SData}]} ->
|
||||||
|
case fxml_stream:parse_element(SData) of
|
||||||
|
Data when is_record(Data, xmlel) ->
|
||||||
|
{ok, Data};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_all_data(LUser, LServer) ->
|
||||||
|
case catch odbc_queries:get_private_data(LServer, LUser) of
|
||||||
|
{selected, Res} ->
|
||||||
|
lists:flatmap(
|
||||||
|
fun({_, SData}) ->
|
||||||
|
case fxml_stream:parse_element(SData) of
|
||||||
|
#xmlel{} = El ->
|
||||||
|
[El];
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end, Res);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
odbc_queries:del_user_private_storage(LServer, LUser).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{private_storage,
|
||||||
|
fun(Host, #private_storage{usns = {LUser, LServer, XMLNS},
|
||||||
|
xml = Data})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
LXMLNS = ejabberd_odbc:escape(XMLNS),
|
||||||
|
SData =
|
||||||
|
ejabberd_odbc:escape(fxml:element_to_binary(Data)),
|
||||||
|
odbc_queries:set_private_data_sql(Username, LXMLNS,
|
||||||
|
SData);
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username, namespace, data from private_storage;">>,
|
||||||
|
fun([LUser, XMLNS, XML]) ->
|
||||||
|
El = #xmlel{} = fxml_stream:parse_element(XML),
|
||||||
|
#private_storage{usns = {LUser, LServer, XMLNS},
|
||||||
|
xml = El}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
|
@ -49,7 +49,6 @@
|
||||||
get_jid_info/4, item_to_xml/1, webadmin_page/3,
|
get_jid_info/4, item_to_xml/1, webadmin_page/3,
|
||||||
webadmin_user/4, get_versioning_feature/2,
|
webadmin_user/4, get_versioning_feature/2,
|
||||||
roster_versioning_enabled/1, roster_version/2,
|
roster_versioning_enabled/1, roster_version/2,
|
||||||
record_to_string/1, groups_to_string/1,
|
|
||||||
mod_opt_type/1, set_roster/1]).
|
mod_opt_type/1, set_roster/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
@ -65,23 +64,27 @@
|
||||||
|
|
||||||
-export_type([subscription/0]).
|
-export_type([subscription/0]).
|
||||||
|
|
||||||
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
|
-callback import(binary(), #roster{} | #roster_version{}) -> ok | pass.
|
||||||
|
-callback read_roster_version(binary(), binary()) -> binary() | error.
|
||||||
|
-callback write_roster_version(binary(), binary(), boolean(), binary()) -> any().
|
||||||
|
-callback get_roster(binary(), binary()) -> [#roster{}].
|
||||||
|
-callback get_roster_by_jid(binary(), binary(), ljid()) -> #roster{}.
|
||||||
|
-callback get_only_items(binary(), binary()) -> [#roster{}].
|
||||||
|
-callback roster_subscribe(binary(), binary(), ljid(), #roster{}) -> any().
|
||||||
|
-callback transaction(binary(), function()) -> {atomic, any()} | {aborted, any()}.
|
||||||
|
-callback get_roster_by_jid_with_groups(binary(), binary(), ljid()) -> #roster{}.
|
||||||
|
-callback remove_user(binary(), binary()) -> {atomic, any()}.
|
||||||
|
-callback update_roster(binary(), binary(), ljid(), #roster{}) -> any().
|
||||||
|
-callback del_roster(binary(), binary(), ljid()) -> any().
|
||||||
|
-callback read_subscription_and_groups(binary(), binary(), ljid()) ->
|
||||||
|
{subscription(), [binary()]}.
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
one_queue),
|
one_queue),
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(roster,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes, record_info(fields, roster)}]),
|
|
||||||
mnesia:create_table(roster_version,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, roster_version)}]),
|
|
||||||
update_tables(),
|
|
||||||
mnesia:add_table_index(roster, us),
|
|
||||||
mnesia:add_table_index(roster_version, us);
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:add(roster_get, Host, ?MODULE,
|
ejabberd_hooks:add(roster_get, Host, ?MODULE,
|
||||||
get_user_roster, 50),
|
get_user_roster, 50),
|
||||||
ejabberd_hooks:add(roster_in_subscription, Host,
|
ejabberd_hooks:add(roster_in_subscription, Host,
|
||||||
|
@ -194,26 +197,8 @@ roster_version(LServer, LUser) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
read_roster_version(LUser, LServer) ->
|
read_roster_version(LUser, LServer) ->
|
||||||
read_roster_version(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:read_roster_version(LUser, LServer).
|
||||||
|
|
||||||
read_roster_version(LUser, LServer, mnesia) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case mnesia:dirty_read(roster_version, US) of
|
|
||||||
[#roster_version{version = V}] -> V;
|
|
||||||
[] -> error
|
|
||||||
end;
|
|
||||||
read_roster_version(LUser, LServer, odbc) ->
|
|
||||||
case odbc_queries:get_roster_version(LServer, LUser) of
|
|
||||||
{selected, [{Version}]} -> Version;
|
|
||||||
{selected, []} -> error
|
|
||||||
end;
|
|
||||||
read_roster_version(LServer, LUser, riak) ->
|
|
||||||
case ejabberd_riak:get(roster_version, roster_version_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #roster_version{version = V}} -> V;
|
|
||||||
_Err -> error
|
|
||||||
end.
|
|
||||||
|
|
||||||
write_roster_version(LUser, LServer) ->
|
write_roster_version(LUser, LServer) ->
|
||||||
write_roster_version(LUser, LServer, false).
|
write_roster_version(LUser, LServer, false).
|
||||||
|
@ -223,38 +208,10 @@ write_roster_version_t(LUser, LServer) ->
|
||||||
|
|
||||||
write_roster_version(LUser, LServer, InTransaction) ->
|
write_roster_version(LUser, LServer, InTransaction) ->
|
||||||
Ver = p1_sha:sha(term_to_binary(p1_time_compat:unique_integer())),
|
Ver = p1_sha:sha(term_to_binary(p1_time_compat:unique_integer())),
|
||||||
write_roster_version(LUser, LServer, InTransaction, Ver,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)),
|
Mod:write_roster_version(LUser, LServer, InTransaction, Ver),
|
||||||
Ver.
|
Ver.
|
||||||
|
|
||||||
write_roster_version(LUser, LServer, InTransaction, Ver,
|
|
||||||
mnesia) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
if InTransaction ->
|
|
||||||
mnesia:write(#roster_version{us = US, version = Ver});
|
|
||||||
true ->
|
|
||||||
mnesia:dirty_write(#roster_version{us = US,
|
|
||||||
version = Ver})
|
|
||||||
end;
|
|
||||||
write_roster_version(LUser, LServer, InTransaction, Ver,
|
|
||||||
odbc) ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
EVer = ejabberd_odbc:escape(Ver),
|
|
||||||
if InTransaction ->
|
|
||||||
odbc_queries:set_roster_version(Username, EVer);
|
|
||||||
true ->
|
|
||||||
odbc_queries:sql_transaction(LServer,
|
|
||||||
fun () ->
|
|
||||||
odbc_queries:set_roster_version(Username,
|
|
||||||
EVer)
|
|
||||||
end)
|
|
||||||
end;
|
|
||||||
write_roster_version(LUser, LServer, _InTransaction, Ver,
|
|
||||||
riak) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
ejabberd_riak:put(#roster_version{us = US, version = Ver},
|
|
||||||
roster_version_schema()).
|
|
||||||
|
|
||||||
%% Load roster from DB only if neccesary.
|
%% Load roster from DB only if neccesary.
|
||||||
%% It is neccesary if
|
%% It is neccesary if
|
||||||
%% - roster versioning is disabled in server OR
|
%% - roster versioning is disabled in server OR
|
||||||
|
@ -350,56 +307,8 @@ get_user_roster(Acc, {LUser, LServer}) ->
|
||||||
++ Acc.
|
++ Acc.
|
||||||
|
|
||||||
get_roster(LUser, LServer) ->
|
get_roster(LUser, LServer) ->
|
||||||
get_roster(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_roster(LUser, LServer).
|
||||||
|
|
||||||
get_roster(LUser, LServer, mnesia) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
case catch mnesia:dirty_index_read(roster, US,
|
|
||||||
#roster.us)
|
|
||||||
of
|
|
||||||
Items when is_list(Items)-> Items;
|
|
||||||
_ -> []
|
|
||||||
end;
|
|
||||||
get_roster(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get_by_index(roster, roster_schema(),
|
|
||||||
<<"us">>, {LUser, LServer}) of
|
|
||||||
{ok, Items} -> Items;
|
|
||||||
_Err -> []
|
|
||||||
end;
|
|
||||||
get_roster(LUser, LServer, odbc) ->
|
|
||||||
case catch odbc_queries:get_roster(LServer, LUser) of
|
|
||||||
{selected, Items} when is_list(Items) ->
|
|
||||||
JIDGroups = case catch odbc_queries:get_roster_jid_groups(
|
|
||||||
LServer, LUser) of
|
|
||||||
{selected, JGrps}
|
|
||||||
when is_list(JGrps) ->
|
|
||||||
JGrps;
|
|
||||||
_ -> []
|
|
||||||
end,
|
|
||||||
GroupsDict = lists:foldl(fun({J, G}, Acc) ->
|
|
||||||
dict:append(J, G, Acc)
|
|
||||||
end,
|
|
||||||
dict:new(), JIDGroups),
|
|
||||||
RItems =
|
|
||||||
lists:flatmap(
|
|
||||||
fun(I) ->
|
|
||||||
case raw_to_record(LServer, I) of
|
|
||||||
%% Bad JID in database:
|
|
||||||
error -> [];
|
|
||||||
R ->
|
|
||||||
SJID = jid:to_string(R#roster.jid),
|
|
||||||
Groups = case dict:find(SJID, GroupsDict) of
|
|
||||||
{ok, Gs} -> Gs;
|
|
||||||
error -> []
|
|
||||||
end,
|
|
||||||
[R#roster{groups = Groups}]
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
Items),
|
|
||||||
RItems;
|
|
||||||
_ -> []
|
|
||||||
end.
|
|
||||||
|
|
||||||
set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
|
set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
|
||||||
transaction(
|
transaction(
|
||||||
|
@ -437,48 +346,8 @@ item_to_xml(Item) ->
|
||||||
children = SubEls}.
|
children = SubEls}.
|
||||||
|
|
||||||
get_roster_by_jid_t(LUser, LServer, LJID) ->
|
get_roster_by_jid_t(LUser, LServer, LJID) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
get_roster_by_jid_t(LUser, LServer, LJID, DBType).
|
Mod:get_roster_by_jid(LUser, LServer, LJID).
|
||||||
|
|
||||||
get_roster_by_jid_t(LUser, LServer, LJID, mnesia) ->
|
|
||||||
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 = []}
|
|
||||||
end;
|
|
||||||
get_roster_by_jid_t(LUser, LServer, LJID, odbc) ->
|
|
||||||
{selected, Res} =
|
|
||||||
odbc_queries:get_roster_by_jid(LServer, LUser, jid:to_string(LJID)),
|
|
||||||
case Res of
|
|
||||||
[] ->
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID};
|
|
||||||
[I] ->
|
|
||||||
R = raw_to_record(LServer, I),
|
|
||||||
case R of
|
|
||||||
%% Bad JID in database:
|
|
||||||
error ->
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID};
|
|
||||||
_ ->
|
|
||||||
R#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID, name = <<"">>}
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
get_roster_by_jid_t(LUser, LServer, LJID, riak) ->
|
|
||||||
case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
|
|
||||||
{ok, I} ->
|
|
||||||
I#roster{jid = LJID, name = <<"">>, groups = [],
|
|
||||||
xs = []};
|
|
||||||
{error, notfound} ->
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID};
|
|
||||||
Err ->
|
|
||||||
exit(Err)
|
|
||||||
end.
|
|
||||||
|
|
||||||
try_process_iq_set(From, To, #iq{sub_el = SubEl, lang = Lang} = IQ) ->
|
try_process_iq_set(From, To, #iq{sub_el = SubEl, lang = Lang} = IQ) ->
|
||||||
#jid{server = Server} = From,
|
#jid{server = Server} = From,
|
||||||
|
@ -632,77 +501,37 @@ push_item_version(Server, User, From, Item,
|
||||||
end,
|
end,
|
||||||
ejabberd_sm:get_user_resources(User, Server)).
|
ejabberd_sm:get_user_resources(User, Server)).
|
||||||
|
|
||||||
get_subscription_lists(Acc, User, Server) ->
|
get_subscription_lists(_Acc, User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
Items = get_subscription_lists(Acc, LUser, LServer,
|
Items = Mod:get_only_items(LUser, LServer),
|
||||||
DBType),
|
|
||||||
fill_subscription_lists(LServer, Items, [], []).
|
fill_subscription_lists(LServer, Items, [], []).
|
||||||
|
|
||||||
get_subscription_lists(_, LUser, LServer, mnesia) ->
|
fill_subscription_lists(LServer, [I | Is], F, T) ->
|
||||||
US = {LUser, LServer},
|
|
||||||
case mnesia:dirty_index_read(roster, US, #roster.us) of
|
|
||||||
Items when is_list(Items) -> Items;
|
|
||||||
_ -> []
|
|
||||||
end;
|
|
||||||
get_subscription_lists(_, LUser, LServer, odbc) ->
|
|
||||||
case catch odbc_queries:get_roster(LServer, LUser) of
|
|
||||||
{selected, Items} when is_list(Items) ->
|
|
||||||
lists:map(fun(I) -> raw_to_record(LServer, I) end, Items);
|
|
||||||
_ -> []
|
|
||||||
end;
|
|
||||||
get_subscription_lists(_, LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get_by_index(roster, roster_schema(),
|
|
||||||
<<"us">>, {LUser, LServer}) of
|
|
||||||
{ok, Items} -> Items;
|
|
||||||
_Err -> []
|
|
||||||
end.
|
|
||||||
|
|
||||||
fill_subscription_lists(LServer, [#roster{} = I | Is],
|
|
||||||
F, T) ->
|
|
||||||
J = element(3, I#roster.usj),
|
J = element(3, I#roster.usj),
|
||||||
case I#roster.subscription of
|
case I#roster.subscription of
|
||||||
both ->
|
both ->
|
||||||
fill_subscription_lists(LServer, Is, [J | F], [J | T]);
|
fill_subscription_lists(LServer, Is, [J | F], [J | T]);
|
||||||
from ->
|
from ->
|
||||||
fill_subscription_lists(LServer, Is, [J | F], T);
|
fill_subscription_lists(LServer, Is, [J | F], T);
|
||||||
to -> fill_subscription_lists(LServer, Is, F, [J | T]);
|
to -> fill_subscription_lists(LServer, Is, F, [J | T]);
|
||||||
_ -> fill_subscription_lists(LServer, Is, F, T)
|
_ -> fill_subscription_lists(LServer, Is, F, T)
|
||||||
end;
|
end;
|
||||||
fill_subscription_lists(LServer, [RawI | Is], F, T) ->
|
fill_subscription_lists(_LServer, [], F, T) ->
|
||||||
I = raw_to_record(LServer, RawI),
|
{F, T}.
|
||||||
case I of
|
|
||||||
%% Bad JID in database:
|
|
||||||
error -> fill_subscription_lists(LServer, Is, F, T);
|
|
||||||
_ -> fill_subscription_lists(LServer, [I | Is], F, T)
|
|
||||||
end;
|
|
||||||
fill_subscription_lists(_LServer, [], F, T) -> {F, T}.
|
|
||||||
|
|
||||||
ask_to_pending(subscribe) -> out;
|
ask_to_pending(subscribe) -> out;
|
||||||
ask_to_pending(unsubscribe) -> none;
|
ask_to_pending(unsubscribe) -> none;
|
||||||
ask_to_pending(Ask) -> Ask.
|
ask_to_pending(Ask) -> Ask.
|
||||||
|
|
||||||
roster_subscribe_t(LUser, LServer, LJID, Item) ->
|
roster_subscribe_t(LUser, LServer, LJID, Item) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
roster_subscribe_t(LUser, LServer, LJID, Item, DBType).
|
Mod:roster_subscribe(LUser, LServer, LJID, Item).
|
||||||
|
|
||||||
roster_subscribe_t(_LUser, _LServer, _LJID, Item,
|
|
||||||
mnesia) ->
|
|
||||||
mnesia:write(Item);
|
|
||||||
roster_subscribe_t(_LUser, _LServer, _LJID, Item, odbc) ->
|
|
||||||
ItemVals = record_to_row(Item),
|
|
||||||
odbc_queries:roster_subscribe(ItemVals);
|
|
||||||
roster_subscribe_t(LUser, LServer, _LJID, Item, riak) ->
|
|
||||||
ejabberd_riak:put(Item, roster_schema(),
|
|
||||||
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
|
|
||||||
|
|
||||||
transaction(LServer, F) ->
|
transaction(LServer, F) ->
|
||||||
case gen_mod:db_type(LServer, ?MODULE) of
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
mnesia -> mnesia:transaction(F);
|
Mod:transaction(LServer, F).
|
||||||
odbc -> ejabberd_odbc:sql_transaction(LServer, F);
|
|
||||||
riak -> {atomic, F()}
|
|
||||||
end.
|
|
||||||
|
|
||||||
in_subscription(_, User, Server, JID, Type, Reason) ->
|
in_subscription(_, User, Server, JID, Type, Reason) ->
|
||||||
process_subscription(in, User, Server, JID, Type,
|
process_subscription(in, User, Server, JID, Type,
|
||||||
|
@ -712,45 +541,8 @@ out_subscription(User, Server, JID, Type) ->
|
||||||
process_subscription(out, User, Server, JID, Type, <<"">>).
|
process_subscription(out, User, Server, JID, Type, <<"">>).
|
||||||
|
|
||||||
get_roster_by_jid_with_groups_t(LUser, LServer, LJID) ->
|
get_roster_by_jid_with_groups_t(LUser, LServer, LJID) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
|
Mod:get_roster_by_jid_with_groups(LUser, LServer, LJID).
|
||||||
DBType).
|
|
||||||
|
|
||||||
get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
|
|
||||||
mnesia) ->
|
|
||||||
case mnesia:read({roster, {LUser, LServer, LJID}}) of
|
|
||||||
[] ->
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID};
|
|
||||||
[I] -> I
|
|
||||||
end;
|
|
||||||
get_roster_by_jid_with_groups_t(LUser, LServer, LJID,
|
|
||||||
odbc) ->
|
|
||||||
SJID = jid:to_string(LJID),
|
|
||||||
case odbc_queries:get_roster_by_jid(LServer, LUser, SJID) of
|
|
||||||
{selected, [I]} ->
|
|
||||||
R = raw_to_record(LServer, I),
|
|
||||||
Groups =
|
|
||||||
case odbc_queries:get_roster_groups(LServer, LUser, SJID) of
|
|
||||||
{selected, JGrps} when is_list(JGrps) ->
|
|
||||||
[JGrp || {JGrp} <- JGrps];
|
|
||||||
_ -> []
|
|
||||||
end,
|
|
||||||
R#roster{groups = Groups};
|
|
||||||
{selected, []} ->
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID}
|
|
||||||
end;
|
|
||||||
get_roster_by_jid_with_groups_t(LUser, LServer, LJID, riak) ->
|
|
||||||
case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
|
|
||||||
{ok, I} ->
|
|
||||||
I;
|
|
||||||
{error, notfound} ->
|
|
||||||
#roster{usj = {LUser, LServer, LJID},
|
|
||||||
us = {LUser, LServer}, jid = LJID};
|
|
||||||
Err ->
|
|
||||||
exit(Err)
|
|
||||||
end.
|
|
||||||
|
|
||||||
process_subscription(Direction, User, Server, JID1,
|
process_subscription(Direction, User, Server, JID1,
|
||||||
Type, Reason) ->
|
Type, Reason) ->
|
||||||
|
@ -948,21 +740,8 @@ remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
send_unsubscription_to_rosteritems(LUser, LServer),
|
send_unsubscription_to_rosteritems(LUser, LServer),
|
||||||
remove_user(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:remove_user(LUser, LServer).
|
||||||
|
|
||||||
remove_user(LUser, LServer, mnesia) ->
|
|
||||||
US = {LUser, LServer},
|
|
||||||
F = fun () ->
|
|
||||||
lists:foreach(fun (R) -> mnesia:delete_object(R) end,
|
|
||||||
mnesia:index_read(roster, US, #roster.us))
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_user(LUser, LServer, odbc) ->
|
|
||||||
odbc_queries:del_user_roster_t(LServer, LUser),
|
|
||||||
ok;
|
|
||||||
remove_user(LUser, LServer, riak) ->
|
|
||||||
{atomic, ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer})}.
|
|
||||||
|
|
||||||
%% For each contact with Subscription:
|
%% For each contact with Subscription:
|
||||||
%% Both or From, send a "unsubscribed" presence stanza;
|
%% Both or From, send a "unsubscribed" presence stanza;
|
||||||
|
@ -1020,33 +799,12 @@ set_items(User, Server, SubEl) ->
|
||||||
transaction(LServer, F).
|
transaction(LServer, F).
|
||||||
|
|
||||||
update_roster_t(LUser, LServer, LJID, Item) ->
|
update_roster_t(LUser, LServer, LJID, Item) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
update_roster_t(LUser, LServer, LJID, Item, DBType).
|
Mod:update_roster(LUser, LServer, LJID, Item).
|
||||||
|
|
||||||
update_roster_t(_LUser, _LServer, _LJID, Item,
|
|
||||||
mnesia) ->
|
|
||||||
mnesia:write(Item);
|
|
||||||
update_roster_t(LUser, LServer, LJID, Item, odbc) ->
|
|
||||||
SJID = jid:to_string(LJID),
|
|
||||||
ItemVals = record_to_row(Item),
|
|
||||||
ItemGroups = Item#roster.groups,
|
|
||||||
odbc_queries:update_roster(LServer, LUser, SJID, ItemVals,
|
|
||||||
ItemGroups);
|
|
||||||
update_roster_t(LUser, LServer, _LJID, Item, riak) ->
|
|
||||||
ejabberd_riak:put(Item, roster_schema(),
|
|
||||||
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
|
|
||||||
|
|
||||||
del_roster_t(LUser, LServer, LJID) ->
|
del_roster_t(LUser, LServer, LJID) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
del_roster_t(LUser, LServer, LJID, DBType).
|
Mod:del_roster(LUser, LServer, LJID).
|
||||||
|
|
||||||
del_roster_t(LUser, LServer, LJID, mnesia) ->
|
|
||||||
mnesia:delete({roster, {LUser, LServer, LJID}});
|
|
||||||
del_roster_t(LUser, LServer, LJID, odbc) ->
|
|
||||||
SJID = jid:to_string(LJID),
|
|
||||||
odbc_queries:del_roster(LServer, LUser, SJID);
|
|
||||||
del_roster_t(LUser, LServer, LJID, riak) ->
|
|
||||||
ejabberd_riak:delete(roster, {LUser, LServer, LJID}).
|
|
||||||
|
|
||||||
process_item_set_t(LUser, LServer,
|
process_item_set_t(LUser, LServer,
|
||||||
#xmlel{attrs = Attrs, children = Els}) ->
|
#xmlel{attrs = Attrs, children = Els}) ->
|
||||||
|
@ -1109,13 +867,12 @@ process_item_attrs_ws(Item, []) -> Item.
|
||||||
|
|
||||||
get_in_pending_subscriptions(Ls, User, Server) ->
|
get_in_pending_subscriptions(Ls, User, Server) ->
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
get_in_pending_subscriptions(Ls, User, Server,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
get_in_pending_subscriptions(Ls, User, Server, Mod).
|
||||||
|
|
||||||
get_in_pending_subscriptions(Ls, User, Server, DBType)
|
get_in_pending_subscriptions(Ls, User, Server, Mod) ->
|
||||||
when DBType == mnesia; DBType == riak ->
|
|
||||||
JID = jid:make(User, Server, <<"">>),
|
JID = jid:make(User, Server, <<"">>),
|
||||||
Result = get_roster(JID#jid.luser, JID#jid.lserver, DBType),
|
Result = Mod:get_only_items(JID#jid.luser, JID#jid.lserver),
|
||||||
Ls ++ lists:map(fun (R) ->
|
Ls ++ lists:map(fun (R) ->
|
||||||
Message = R#roster.askmessage,
|
Message = R#roster.askmessage,
|
||||||
Status = if is_binary(Message) -> (Message);
|
Status = if is_binary(Message) -> (Message);
|
||||||
|
@ -1140,93 +897,15 @@ get_in_pending_subscriptions(Ls, User, Server, DBType)
|
||||||
_ -> false
|
_ -> false
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
Result));
|
Result)).
|
||||||
get_in_pending_subscriptions(Ls, User, Server, odbc) ->
|
|
||||||
JID = jid:make(User, Server, <<"">>),
|
|
||||||
LUser = JID#jid.luser,
|
|
||||||
LServer = JID#jid.lserver,
|
|
||||||
case catch odbc_queries:get_roster(LServer, LUser) of
|
|
||||||
{selected, Items} when is_list(Items) ->
|
|
||||||
Ls ++
|
|
||||||
lists:map(fun (R) ->
|
|
||||||
Message = R#roster.askmessage,
|
|
||||||
#xmlel{name = <<"presence">>,
|
|
||||||
attrs =
|
|
||||||
[{<<"from">>,
|
|
||||||
jid:to_string(R#roster.jid)},
|
|
||||||
{<<"to">>, jid:to_string(JID)},
|
|
||||||
{<<"type">>, <<"subscribe">>}],
|
|
||||||
children =
|
|
||||||
[#xmlel{name = <<"status">>,
|
|
||||||
attrs = [],
|
|
||||||
children =
|
|
||||||
[{xmlcdata, Message}]}]}
|
|
||||||
end,
|
|
||||||
lists:flatmap(fun (I) ->
|
|
||||||
case raw_to_record(LServer, I) of
|
|
||||||
%% Bad JID in database:
|
|
||||||
error -> [];
|
|
||||||
R ->
|
|
||||||
case R#roster.ask of
|
|
||||||
in -> [R];
|
|
||||||
both -> [R];
|
|
||||||
_ -> []
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
Items));
|
|
||||||
_ -> Ls
|
|
||||||
end.
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
read_subscription_and_groups(User, Server, LJID) ->
|
read_subscription_and_groups(User, Server, LJID) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
read_subscription_and_groups(LUser, LServer, LJID,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:read_subscription_and_groups(LUser, LServer, LJID).
|
||||||
|
|
||||||
read_subscription_and_groups(LUser, LServer, LJID,
|
|
||||||
mnesia) ->
|
|
||||||
case catch mnesia:dirty_read(roster,
|
|
||||||
{LUser, LServer, LJID})
|
|
||||||
of
|
|
||||||
[#roster{subscription = Subscription,
|
|
||||||
groups = Groups}] ->
|
|
||||||
{Subscription, Groups};
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
read_subscription_and_groups(LUser, LServer, LJID,
|
|
||||||
odbc) ->
|
|
||||||
SJID = jid:to_string(LJID),
|
|
||||||
case catch odbc_queries:get_subscription(LServer, LUser, SJID) of
|
|
||||||
{selected, [{SSubscription}]} ->
|
|
||||||
Subscription = case SSubscription of
|
|
||||||
<<"B">> -> both;
|
|
||||||
<<"T">> -> to;
|
|
||||||
<<"F">> -> from;
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
Groups = case catch
|
|
||||||
odbc_queries:get_rostergroup_by_jid(LServer, LUser,
|
|
||||||
SJID)
|
|
||||||
of
|
|
||||||
{selected, JGrps} when is_list(JGrps) ->
|
|
||||||
[JGrp || {JGrp} <- JGrps];
|
|
||||||
_ -> []
|
|
||||||
end,
|
|
||||||
{Subscription, Groups};
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
read_subscription_and_groups(LUser, LServer, LJID,
|
|
||||||
riak) ->
|
|
||||||
case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
|
|
||||||
{ok, #roster{subscription = Subscription,
|
|
||||||
groups = Groups}} ->
|
|
||||||
{Subscription, Groups};
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
get_jid_info(_, User, Server, JID) ->
|
get_jid_info(_, User, Server, JID) ->
|
||||||
LJID = jid:tolower(JID),
|
LJID = jid:tolower(JID),
|
||||||
|
@ -1246,155 +925,6 @@ get_jid_info(_, User, Server, JID) ->
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
raw_to_record(LServer,
|
|
||||||
[User, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
|
||||||
_SServer, _SSubscribe, _SType]) ->
|
|
||||||
raw_to_record(LServer,
|
|
||||||
{User, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
|
||||||
_SServer, _SSubscribe, _SType});
|
|
||||||
raw_to_record(LServer,
|
|
||||||
{User, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
|
||||||
_SServer, _SSubscribe, _SType}) ->
|
|
||||||
case jid:from_string(SJID) of
|
|
||||||
error -> error;
|
|
||||||
JID ->
|
|
||||||
LJID = jid:tolower(JID),
|
|
||||||
Subscription = case SSubscription of
|
|
||||||
<<"B">> -> both;
|
|
||||||
<<"T">> -> to;
|
|
||||||
<<"F">> -> from;
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
Ask = case SAsk of
|
|
||||||
<<"S">> -> subscribe;
|
|
||||||
<<"U">> -> unsubscribe;
|
|
||||||
<<"B">> -> both;
|
|
||||||
<<"O">> -> out;
|
|
||||||
<<"I">> -> in;
|
|
||||||
_ -> none
|
|
||||||
end,
|
|
||||||
#roster{usj = {User, LServer, LJID},
|
|
||||||
us = {User, LServer}, jid = LJID, name = Nick,
|
|
||||||
subscription = Subscription, ask = Ask,
|
|
||||||
askmessage = SAskMessage}
|
|
||||||
end.
|
|
||||||
|
|
||||||
record_to_string(#roster{us = {User, _Server},
|
|
||||||
jid = JID, name = Name, subscription = Subscription,
|
|
||||||
ask = Ask, askmessage = AskMessage}) ->
|
|
||||||
Username = ejabberd_odbc:escape(User),
|
|
||||||
SJID =
|
|
||||||
ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
|
|
||||||
Nick = ejabberd_odbc:escape(Name),
|
|
||||||
SSubscription = case Subscription of
|
|
||||||
both -> <<"B">>;
|
|
||||||
to -> <<"T">>;
|
|
||||||
from -> <<"F">>;
|
|
||||||
none -> <<"N">>
|
|
||||||
end,
|
|
||||||
SAsk = case Ask of
|
|
||||||
subscribe -> <<"S">>;
|
|
||||||
unsubscribe -> <<"U">>;
|
|
||||||
both -> <<"B">>;
|
|
||||||
out -> <<"O">>;
|
|
||||||
in -> <<"I">>;
|
|
||||||
none -> <<"N">>
|
|
||||||
end,
|
|
||||||
SAskMessage = ejabberd_odbc:escape(AskMessage),
|
|
||||||
[Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
|
||||||
<<"N">>, <<"">>, <<"item">>].
|
|
||||||
|
|
||||||
record_to_row(
|
|
||||||
#roster{us = {LUser, _LServer},
|
|
||||||
jid = JID, name = Name, subscription = Subscription,
|
|
||||||
ask = Ask, askmessage = AskMessage}) ->
|
|
||||||
SJID = jid:to_string(jid:tolower(JID)),
|
|
||||||
SSubscription = case Subscription of
|
|
||||||
both -> <<"B">>;
|
|
||||||
to -> <<"T">>;
|
|
||||||
from -> <<"F">>;
|
|
||||||
none -> <<"N">>
|
|
||||||
end,
|
|
||||||
SAsk = case Ask of
|
|
||||||
subscribe -> <<"S">>;
|
|
||||||
unsubscribe -> <<"U">>;
|
|
||||||
both -> <<"B">>;
|
|
||||||
out -> <<"O">>;
|
|
||||||
in -> <<"I">>;
|
|
||||||
none -> <<"N">>
|
|
||||||
end,
|
|
||||||
{LUser, SJID, Name, SSubscription, SAsk, AskMessage}.
|
|
||||||
|
|
||||||
groups_to_string(#roster{us = {User, _Server},
|
|
||||||
jid = JID, groups = Groups}) ->
|
|
||||||
Username = ejabberd_odbc:escape(User),
|
|
||||||
SJID =
|
|
||||||
ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
|
|
||||||
lists:foldl(fun (<<"">>, Acc) -> Acc;
|
|
||||||
(Group, Acc) ->
|
|
||||||
G = ejabberd_odbc:escape(Group),
|
|
||||||
[[Username, SJID, G] | Acc]
|
|
||||||
end,
|
|
||||||
[], Groups).
|
|
||||||
|
|
||||||
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);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating roster_version table", []),
|
|
||||||
mnesia:transform_table(roster_version, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
webadmin_page(_, Host,
|
webadmin_page(_, Host,
|
||||||
#request{us = _US, path = [<<"user">>, U, <<"roster">>],
|
#request{us = _US, path = [<<"user">>, U, <<"roster">>],
|
||||||
q = Query, lang = Lang} =
|
q = Query, lang = Lang} =
|
||||||
|
@ -1692,68 +1222,17 @@ is_managed_from_id(<<"roster-remotely-managed">>) ->
|
||||||
is_managed_from_id(_Id) ->
|
is_managed_from_id(_Id) ->
|
||||||
false.
|
false.
|
||||||
|
|
||||||
roster_schema() ->
|
export(LServer) ->
|
||||||
{record_info(fields, roster), #roster{}}.
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Mod:export(LServer).
|
||||||
roster_version_schema() ->
|
|
||||||
{record_info(fields, roster_version), #roster_version{}}.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{roster,
|
|
||||||
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
|
|
||||||
ItemVals = record_to_string(R),
|
|
||||||
ItemGroups = groups_to_string(R),
|
|
||||||
odbc_queries:update_roster_sql(Username, SJID,
|
|
||||||
ItemVals, ItemGroups);
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end},
|
|
||||||
{roster_version,
|
|
||||||
fun(Host, #roster_version{us = {LUser, LServer}, version = Ver})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SVer = ejabberd_odbc:escape(Ver),
|
|
||||||
[[<<"delete from roster_version where username='">>,
|
|
||||||
Username, <<"';">>],
|
|
||||||
[<<"insert into roster_version(username, version) values('">>,
|
|
||||||
Username, <<"', '">>, SVer, <<"');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select username, jid, nick, subscription, "
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
"ask, askmessage, server, subscribe, type from rosterusers;">>,
|
Mod:import(LServer).
|
||||||
fun([LUser, JID|_] = Row) ->
|
|
||||||
Item = raw_to_record(LServer, Row),
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SJID = ejabberd_odbc:escape(JID),
|
|
||||||
{selected, _, Rows} =
|
|
||||||
ejabberd_odbc:sql_query_t(
|
|
||||||
[<<"select grp from rostergroups where username='">>,
|
|
||||||
Username, <<"' and jid='">>, SJID, <<"'">>]),
|
|
||||||
Groups = [Grp || [Grp] <- Rows],
|
|
||||||
Item#roster{groups = Groups}
|
|
||||||
end},
|
|
||||||
{<<"select username, version from roster_version;">>,
|
|
||||||
fun([LUser, Ver]) ->
|
|
||||||
#roster_version{us = {LUser, LServer}, version = Ver}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #roster{} = R) ->
|
import(LServer, DBType, R) ->
|
||||||
mnesia:dirty_write(R);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
import(_LServer, mnesia, #roster_version{} = RV) ->
|
Mod:import(LServer, R).
|
||||||
mnesia:dirty_write(RV);
|
|
||||||
import(_LServer, riak, #roster{us = {LUser, LServer}} = R) ->
|
|
||||||
ejabberd_riak:put(R, roster_schema(),
|
|
||||||
[{'2i', [{<<"us">>, {LUser, LServer}}]}]);
|
|
||||||
import(_LServer, riak, #roster_version{} = RV) ->
|
|
||||||
ejabberd_riak:put(RV, roster_version_schema());
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(access) ->
|
mod_opt_type(access) ->
|
||||||
fun (A) when is_atom(A) -> A end;
|
fun (A) when is_atom(A) -> A end;
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-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,
|
||||||
|
remove_user/2, update_roster/4, del_roster/3, transaction/2,
|
||||||
|
read_subscription_and_groups/3, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_roster.hrl").
|
||||||
|
-include("logger.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()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, roster_version)}]),
|
||||||
|
update_tables(),
|
||||||
|
mnesia:add_table_index(roster, us),
|
||||||
|
mnesia:add_table_index(roster_version, us).
|
||||||
|
|
||||||
|
read_roster_version(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
case mnesia:dirty_read(roster_version, US) of
|
||||||
|
[#roster_version{version = V}] -> V;
|
||||||
|
[] -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
write_roster_version(LUser, LServer, InTransaction, Ver) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
if InTransaction ->
|
||||||
|
mnesia:write(#roster_version{us = US, version = Ver});
|
||||||
|
true ->
|
||||||
|
mnesia:dirty_write(#roster_version{us = US, version = Ver})
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_roster(LUser, LServer) ->
|
||||||
|
mnesia:dirty_index_read(roster, {LUser, LServer}, #roster.us).
|
||||||
|
|
||||||
|
get_roster_by_jid(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 = []}
|
||||||
|
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 () ->
|
||||||
|
lists:foreach(
|
||||||
|
fun (R) -> mnesia:delete_object(R) end,
|
||||||
|
mnesia:index_read(roster, US, #roster.us))
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
update_roster(_LUser, _LServer, _LJID, Item) ->
|
||||||
|
mnesia:write(Item).
|
||||||
|
|
||||||
|
del_roster(LUser, LServer, LJID) ->
|
||||||
|
mnesia:delete({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};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
transaction(_LServer, F) ->
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #roster{} = R) ->
|
||||||
|
mnesia:dirty_write(R);
|
||||||
|
import(_LServer, #roster_version{} = RV) ->
|
||||||
|
mnesia:dirty_write(RV).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% 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);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating roster_version table", []),
|
||||||
|
mnesia:transform_table(roster_version, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,113 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_roster_riak).
|
||||||
|
|
||||||
|
|
||||||
|
-behaviour(mod_roster).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, read_roster_version/2, write_roster_version/4,
|
||||||
|
get_roster/2, get_roster_by_jid/3,
|
||||||
|
roster_subscribe/4, get_roster_by_jid_with_groups/3,
|
||||||
|
remove_user/2, update_roster/4, del_roster/3, transaction/2,
|
||||||
|
read_subscription_and_groups/3, get_only_items/2, import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_roster.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
read_roster_version(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(roster_version, roster_version_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{ok, #roster_version{version = V}} -> V;
|
||||||
|
_Err -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
write_roster_version(LUser, LServer, _InTransaction, Ver) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
ejabberd_riak:put(#roster_version{us = US, version = Ver},
|
||||||
|
roster_version_schema()).
|
||||||
|
|
||||||
|
get_roster(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get_by_index(roster, roster_schema(),
|
||||||
|
<<"us">>, {LUser, LServer}) of
|
||||||
|
{ok, Items} -> Items;
|
||||||
|
_Err -> []
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_roster_by_jid(LUser, LServer, LJID) ->
|
||||||
|
case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
|
||||||
|
{ok, I} ->
|
||||||
|
I#roster{jid = LJID, name = <<"">>, groups = [], xs = []};
|
||||||
|
{error, notfound} ->
|
||||||
|
#roster{usj = {LUser, LServer, LJID},
|
||||||
|
us = {LUser, LServer}, jid = LJID};
|
||||||
|
Err ->
|
||||||
|
exit(Err)
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_only_items(LUser, LServer) ->
|
||||||
|
get_roster(LUser, LServer).
|
||||||
|
|
||||||
|
roster_subscribe(LUser, LServer, _LJID, Item) ->
|
||||||
|
ejabberd_riak:put(Item, roster_schema(),
|
||||||
|
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
|
||||||
|
|
||||||
|
transaction(_LServer, F) ->
|
||||||
|
{atomic, F()}.
|
||||||
|
|
||||||
|
get_roster_by_jid_with_groups(LUser, LServer, LJID) ->
|
||||||
|
case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
|
||||||
|
{ok, I} ->
|
||||||
|
I;
|
||||||
|
{error, notfound} ->
|
||||||
|
#roster{usj = {LUser, LServer, LJID},
|
||||||
|
us = {LUser, LServer}, jid = LJID};
|
||||||
|
Err ->
|
||||||
|
exit(Err)
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete_by_index(roster, <<"us">>, {LUser, LServer})}.
|
||||||
|
|
||||||
|
update_roster(LUser, LServer, _LJID, Item) ->
|
||||||
|
ejabberd_riak:put(Item, roster_schema(),
|
||||||
|
[{'2i', [{<<"us">>, {LUser, LServer}}]}]).
|
||||||
|
|
||||||
|
del_roster(LUser, LServer, LJID) ->
|
||||||
|
ejabberd_riak:delete(roster, {LUser, LServer, LJID}).
|
||||||
|
|
||||||
|
read_subscription_and_groups(LUser, LServer, LJID) ->
|
||||||
|
case ejabberd_riak:get(roster, roster_schema(), {LUser, LServer, LJID}) of
|
||||||
|
{ok, #roster{subscription = Subscription,
|
||||||
|
groups = Groups}} ->
|
||||||
|
{Subscription, Groups};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
import(_LServer, #roster{us = {LUser, LServer}} = R) ->
|
||||||
|
ejabberd_riak:put(R, roster_schema(),
|
||||||
|
[{'2i', [{<<"us">>, {LUser, LServer}}]}]);
|
||||||
|
import(_LServer, #roster_version{} = RV) ->
|
||||||
|
ejabberd_riak:put(RV, roster_version_schema()).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
roster_schema() ->
|
||||||
|
{record_info(fields, roster), #roster{}}.
|
||||||
|
|
||||||
|
roster_version_schema() ->
|
||||||
|
{record_info(fields, roster_version), #roster_version{}}.
|
|
@ -0,0 +1,308 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @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_roster_sql).
|
||||||
|
|
||||||
|
-behaviour(mod_roster).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, read_roster_version/2, write_roster_version/4,
|
||||||
|
get_roster/2, get_roster_by_jid/3,
|
||||||
|
roster_subscribe/4, get_roster_by_jid_with_groups/3,
|
||||||
|
remove_user/2, update_roster/4, del_roster/3, transaction/2,
|
||||||
|
read_subscription_and_groups/3, get_only_items/2,
|
||||||
|
import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_roster.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
read_roster_version(LUser, LServer) ->
|
||||||
|
case odbc_queries:get_roster_version(LServer, LUser) of
|
||||||
|
{selected, [{Version}]} -> Version;
|
||||||
|
{selected, []} -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
write_roster_version(LUser, LServer, InTransaction, Ver) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
EVer = ejabberd_odbc:escape(Ver),
|
||||||
|
if InTransaction ->
|
||||||
|
odbc_queries:set_roster_version(Username, EVer);
|
||||||
|
true ->
|
||||||
|
odbc_queries:sql_transaction(
|
||||||
|
LServer,
|
||||||
|
fun () ->
|
||||||
|
odbc_queries:set_roster_version(Username, EVer)
|
||||||
|
end)
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_roster(LUser, LServer) ->
|
||||||
|
case catch odbc_queries:get_roster(LServer, LUser) of
|
||||||
|
{selected, Items} when is_list(Items) ->
|
||||||
|
JIDGroups = case catch odbc_queries:get_roster_jid_groups(
|
||||||
|
LServer, LUser) of
|
||||||
|
{selected, JGrps} when is_list(JGrps) ->
|
||||||
|
JGrps;
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end,
|
||||||
|
GroupsDict = lists:foldl(fun({J, G}, Acc) ->
|
||||||
|
dict:append(J, G, Acc)
|
||||||
|
end,
|
||||||
|
dict:new(), JIDGroups),
|
||||||
|
lists:flatmap(
|
||||||
|
fun(I) ->
|
||||||
|
case raw_to_record(LServer, I) of
|
||||||
|
%% Bad JID in database:
|
||||||
|
error -> [];
|
||||||
|
R ->
|
||||||
|
SJID = jid:to_string(R#roster.jid),
|
||||||
|
Groups = case dict:find(SJID, GroupsDict) of
|
||||||
|
{ok, Gs} -> Gs;
|
||||||
|
error -> []
|
||||||
|
end,
|
||||||
|
[R#roster{groups = Groups}]
|
||||||
|
end
|
||||||
|
end, Items);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_roster_by_jid(LUser, LServer, LJID) ->
|
||||||
|
{selected, Res} =
|
||||||
|
odbc_queries:get_roster_by_jid(LServer, LUser, jid:to_string(LJID)),
|
||||||
|
case Res of
|
||||||
|
[] ->
|
||||||
|
#roster{usj = {LUser, LServer, LJID},
|
||||||
|
us = {LUser, LServer}, jid = LJID};
|
||||||
|
[I] ->
|
||||||
|
R = raw_to_record(LServer, I),
|
||||||
|
case R of
|
||||||
|
%% Bad JID in database:
|
||||||
|
error ->
|
||||||
|
#roster{usj = {LUser, LServer, LJID},
|
||||||
|
us = {LUser, LServer}, jid = LJID};
|
||||||
|
_ ->
|
||||||
|
R#roster{usj = {LUser, LServer, LJID},
|
||||||
|
us = {LUser, LServer}, jid = LJID, name = <<"">>}
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
get_only_items(LUser, LServer) ->
|
||||||
|
case catch odbc_queries:get_roster(LServer, LUser) of
|
||||||
|
{selected, Is} when is_list(Is) ->
|
||||||
|
lists:map(fun(I) -> raw_to_record(LServer, I) end, Is);
|
||||||
|
_ -> []
|
||||||
|
end.
|
||||||
|
|
||||||
|
roster_subscribe(_LUser, _LServer, _LJID, Item) ->
|
||||||
|
ItemVals = record_to_row(Item),
|
||||||
|
odbc_queries:roster_subscribe(ItemVals).
|
||||||
|
|
||||||
|
transaction(LServer, F) ->
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
get_roster_by_jid_with_groups(LUser, LServer, LJID) ->
|
||||||
|
SJID = jid:to_string(LJID),
|
||||||
|
case odbc_queries:get_roster_by_jid(LServer, LUser, SJID) of
|
||||||
|
{selected, [I]} ->
|
||||||
|
R = raw_to_record(LServer, I),
|
||||||
|
Groups =
|
||||||
|
case odbc_queries:get_roster_groups(LServer, LUser, SJID) of
|
||||||
|
{selected, JGrps} when is_list(JGrps) ->
|
||||||
|
[JGrp || {JGrp} <- JGrps];
|
||||||
|
_ -> []
|
||||||
|
end,
|
||||||
|
R#roster{groups = Groups};
|
||||||
|
{selected, []} ->
|
||||||
|
#roster{usj = {LUser, LServer, LJID},
|
||||||
|
us = {LUser, LServer}, jid = LJID}
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
odbc_queries:del_user_roster_t(LServer, LUser),
|
||||||
|
{atomic, ok}.
|
||||||
|
|
||||||
|
update_roster(LUser, LServer, LJID, Item) ->
|
||||||
|
SJID = jid:to_string(LJID),
|
||||||
|
ItemVals = record_to_row(Item),
|
||||||
|
ItemGroups = Item#roster.groups,
|
||||||
|
odbc_queries:update_roster(LServer, LUser, SJID, ItemVals,
|
||||||
|
ItemGroups).
|
||||||
|
|
||||||
|
del_roster(LUser, LServer, LJID) ->
|
||||||
|
SJID = jid:to_string(LJID),
|
||||||
|
odbc_queries:del_roster(LServer, LUser, SJID).
|
||||||
|
|
||||||
|
read_subscription_and_groups(LUser, LServer, LJID) ->
|
||||||
|
SJID = jid:to_string(LJID),
|
||||||
|
case catch odbc_queries:get_subscription(LServer, LUser, SJID) of
|
||||||
|
{selected, [{SSubscription}]} ->
|
||||||
|
Subscription = case SSubscription of
|
||||||
|
<<"B">> -> both;
|
||||||
|
<<"T">> -> to;
|
||||||
|
<<"F">> -> from;
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
Groups = case catch odbc_queries:get_rostergroup_by_jid(
|
||||||
|
LServer, LUser, SJID) of
|
||||||
|
{selected, JGrps} when is_list(JGrps) ->
|
||||||
|
[JGrp || {JGrp} <- JGrps];
|
||||||
|
_ -> []
|
||||||
|
end,
|
||||||
|
{Subscription, Groups};
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{roster,
|
||||||
|
fun(Host, #roster{usj = {LUser, LServer, LJID}} = R)
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SJID = ejabberd_odbc:escape(jid:to_string(LJID)),
|
||||||
|
ItemVals = record_to_string(R),
|
||||||
|
ItemGroups = groups_to_string(R),
|
||||||
|
odbc_queries:update_roster_sql(Username, SJID,
|
||||||
|
ItemVals, ItemGroups);
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end},
|
||||||
|
{roster_version,
|
||||||
|
fun(Host, #roster_version{us = {LUser, LServer}, version = Ver})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SVer = ejabberd_odbc:escape(Ver),
|
||||||
|
[[<<"delete from roster_version where username='">>,
|
||||||
|
Username, <<"';">>],
|
||||||
|
[<<"insert into roster_version(username, version) values('">>,
|
||||||
|
Username, <<"', '">>, SVer, <<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username, jid, nick, subscription, "
|
||||||
|
"ask, askmessage, server, subscribe, type from rosterusers;">>,
|
||||||
|
fun([LUser, JID|_] = Row) ->
|
||||||
|
Item = raw_to_record(LServer, Row),
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SJID = ejabberd_odbc:escape(JID),
|
||||||
|
{selected, _, Rows} =
|
||||||
|
ejabberd_odbc:sql_query_t(
|
||||||
|
[<<"select grp from rostergroups where username='">>,
|
||||||
|
Username, <<"' and jid='">>, SJID, <<"'">>]),
|
||||||
|
Groups = [Grp || [Grp] <- Rows],
|
||||||
|
Item#roster{groups = Groups}
|
||||||
|
end},
|
||||||
|
{<<"select username, version from roster_version;">>,
|
||||||
|
fun([LUser, Ver]) ->
|
||||||
|
#roster_version{us = {LUser, LServer}, version = Ver}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
raw_to_record(LServer,
|
||||||
|
[User, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
||||||
|
_SServer, _SSubscribe, _SType]) ->
|
||||||
|
raw_to_record(LServer,
|
||||||
|
{User, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
||||||
|
_SServer, _SSubscribe, _SType});
|
||||||
|
raw_to_record(LServer,
|
||||||
|
{User, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
||||||
|
_SServer, _SSubscribe, _SType}) ->
|
||||||
|
case jid:from_string(SJID) of
|
||||||
|
error -> error;
|
||||||
|
JID ->
|
||||||
|
LJID = jid:tolower(JID),
|
||||||
|
Subscription = case SSubscription of
|
||||||
|
<<"B">> -> both;
|
||||||
|
<<"T">> -> to;
|
||||||
|
<<"F">> -> from;
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
Ask = case SAsk of
|
||||||
|
<<"S">> -> subscribe;
|
||||||
|
<<"U">> -> unsubscribe;
|
||||||
|
<<"B">> -> both;
|
||||||
|
<<"O">> -> out;
|
||||||
|
<<"I">> -> in;
|
||||||
|
_ -> none
|
||||||
|
end,
|
||||||
|
#roster{usj = {User, LServer, LJID},
|
||||||
|
us = {User, LServer}, jid = LJID, name = Nick,
|
||||||
|
subscription = Subscription, ask = Ask,
|
||||||
|
askmessage = SAskMessage}
|
||||||
|
end.
|
||||||
|
|
||||||
|
record_to_string(#roster{us = {User, _Server},
|
||||||
|
jid = JID, name = Name, subscription = Subscription,
|
||||||
|
ask = Ask, askmessage = AskMessage}) ->
|
||||||
|
Username = ejabberd_odbc:escape(User),
|
||||||
|
SJID =
|
||||||
|
ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
|
||||||
|
Nick = ejabberd_odbc:escape(Name),
|
||||||
|
SSubscription = case Subscription of
|
||||||
|
both -> <<"B">>;
|
||||||
|
to -> <<"T">>;
|
||||||
|
from -> <<"F">>;
|
||||||
|
none -> <<"N">>
|
||||||
|
end,
|
||||||
|
SAsk = case Ask of
|
||||||
|
subscribe -> <<"S">>;
|
||||||
|
unsubscribe -> <<"U">>;
|
||||||
|
both -> <<"B">>;
|
||||||
|
out -> <<"O">>;
|
||||||
|
in -> <<"I">>;
|
||||||
|
none -> <<"N">>
|
||||||
|
end,
|
||||||
|
SAskMessage = ejabberd_odbc:escape(AskMessage),
|
||||||
|
[Username, SJID, Nick, SSubscription, SAsk, SAskMessage,
|
||||||
|
<<"N">>, <<"">>, <<"item">>].
|
||||||
|
|
||||||
|
record_to_row(
|
||||||
|
#roster{us = {LUser, _LServer},
|
||||||
|
jid = JID, name = Name, subscription = Subscription,
|
||||||
|
ask = Ask, askmessage = AskMessage}) ->
|
||||||
|
SJID = jid:to_string(jid:tolower(JID)),
|
||||||
|
SSubscription = case Subscription of
|
||||||
|
both -> <<"B">>;
|
||||||
|
to -> <<"T">>;
|
||||||
|
from -> <<"F">>;
|
||||||
|
none -> <<"N">>
|
||||||
|
end,
|
||||||
|
SAsk = case Ask of
|
||||||
|
subscribe -> <<"S">>;
|
||||||
|
unsubscribe -> <<"U">>;
|
||||||
|
both -> <<"B">>;
|
||||||
|
out -> <<"O">>;
|
||||||
|
in -> <<"I">>;
|
||||||
|
none -> <<"N">>
|
||||||
|
end,
|
||||||
|
{LUser, SJID, Name, SSubscription, SAsk, AskMessage}.
|
||||||
|
|
||||||
|
groups_to_string(#roster{us = {User, _Server},
|
||||||
|
jid = JID, groups = Groups}) ->
|
||||||
|
Username = ejabberd_odbc:escape(User),
|
||||||
|
SJID =
|
||||||
|
ejabberd_odbc:escape(jid:to_string(jid:tolower(JID))),
|
||||||
|
lists:foldl(fun (<<"">>, Acc) -> Acc;
|
||||||
|
(Group, Acc) ->
|
||||||
|
G = ejabberd_odbc:escape(Group),
|
||||||
|
[[Username, SJID, G] | Acc]
|
||||||
|
end,
|
||||||
|
[], Groups).
|
|
@ -38,7 +38,7 @@
|
||||||
list_groups/1, create_group/2, create_group/3,
|
list_groups/1, create_group/2, create_group/3,
|
||||||
delete_group/2, get_group_opts/2, set_group_opts/3,
|
delete_group/2, get_group_opts/2, set_group_opts/3,
|
||||||
get_group_users/2, get_group_explicit_users/2,
|
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]).
|
remove_user_from_group/3, mod_opt_type/1]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
|
@ -52,25 +52,28 @@
|
||||||
|
|
||||||
-include("ejabberd_web_admin.hrl").
|
-include("ejabberd_web_admin.hrl").
|
||||||
|
|
||||||
-record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()},
|
-include("mod_shared_roster.hrl").
|
||||||
opts = [] :: list() | '_' | '$2'}).
|
|
||||||
|
|
||||||
-record(sr_user, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
-type group_options() :: [{atom(), any()}].
|
||||||
group_host = {<<"">>, <<"">>} :: {binary(), binary()}}).
|
-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) ->
|
start(Host, Opts) ->
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod: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);
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE,
|
ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE,
|
||||||
webadmin_menu, 70),
|
webadmin_menu, 70),
|
||||||
ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE,
|
ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE,
|
||||||
|
@ -391,195 +394,36 @@ process_subscription(Direction, User, Server, JID,
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list_groups(Host) ->
|
list_groups(Host) ->
|
||||||
list_groups(Host, gen_mod:db_type(Host, ?MODULE)).
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
|
Mod:list_groups(Host).
|
||||||
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.
|
|
||||||
|
|
||||||
groups_with_opts(Host) ->
|
groups_with_opts(Host) ->
|
||||||
groups_with_opts(Host, gen_mod:db_type(Host, ?MODULE)).
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
|
Mod:groups_with_opts(Host).
|
||||||
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.
|
|
||||||
|
|
||||||
create_group(Host, Group) ->
|
create_group(Host, Group) ->
|
||||||
create_group(Host, Group, []).
|
create_group(Host, Group, []).
|
||||||
|
|
||||||
create_group(Host, Group, Opts) ->
|
create_group(Host, Group, Opts) ->
|
||||||
create_group(Host, Group, Opts,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)).
|
Mod:create_group(Host, Group, Opts).
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
delete_group(Host, Group) ->
|
delete_group(Host, Group) ->
|
||||||
delete_group(Host, Group,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)).
|
Mod:delete_group(Host, Group).
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
get_group_opts(Host, Group) ->
|
get_group_opts(Host, Group) ->
|
||||||
get_group_opts(Host, Group,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)).
|
Mod:get_group_opts(Host, Group).
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
set_group_opts(Host, Group, Opts) ->
|
set_group_opts(Host, Group, Opts) ->
|
||||||
set_group_opts(Host, Group, Opts,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)).
|
Mod:set_group_opts(Host, Group, Opts).
|
||||||
|
|
||||||
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).
|
|
||||||
|
|
||||||
get_user_groups(US) ->
|
get_user_groups(US) ->
|
||||||
Host = element(2, US),
|
Host = element(2, US),
|
||||||
DBType = gen_mod:db_type(Host, ?MODULE),
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
get_user_groups(US, Host, DBType) ++
|
Mod:get_user_groups(US, Host) ++ get_special_users_groups(Host).
|
||||||
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.
|
|
||||||
|
|
||||||
is_group_enabled(Host1, Group1) ->
|
is_group_enabled(Host1, Group1) ->
|
||||||
{Host, Group} = split_grouphost(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) ->
|
get_group_explicit_users(Host, Group) ->
|
||||||
get_group_explicit_users(Host, Group,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)).
|
Mod:get_group_explicit_users(Host, Group).
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
get_group_name(Host1, Group1) ->
|
get_group_name(Host1, Group1) ->
|
||||||
{Host, Group} = split_grouphost(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
|
%% for the list of groups of that server that user is member
|
||||||
%% get the list of groups displayed
|
%% get the list of groups displayed
|
||||||
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
||||||
Groups = get_user_displayed_groups(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
GroupsOpts,
|
Groups = Mod:get_user_displayed_groups(LUser, LServer, GroupsOpts),
|
||||||
gen_mod:db_type(LServer, ?MODULE)),
|
|
||||||
displayed_groups(GroupsOpts, Groups).
|
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
|
%% @doc Get the list of groups that are displayed to this user
|
||||||
get_user_displayed_groups(US) ->
|
get_user_displayed_groups(US) ->
|
||||||
Host = element(2, US),
|
Host = element(2, US),
|
||||||
|
@ -779,42 +558,12 @@ get_user_displayed_groups(US) ->
|
||||||
is_group_enabled(Host, Group)].
|
is_group_enabled(Host, Group)].
|
||||||
|
|
||||||
is_user_in_group(US, Group, Host) ->
|
is_user_in_group(US, Group, Host) ->
|
||||||
is_user_in_group(US, Group, Host,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)).
|
case Mod:is_user_in_group(US, Group, Host) of
|
||||||
|
false ->
|
||||||
is_user_in_group(US, Group, Host, mnesia) ->
|
lists:member(US, get_group_users(Host, Group));
|
||||||
case catch mnesia:dirty_match_object(#sr_user{us = US,
|
true ->
|
||||||
group_host = {Group, Host}})
|
true
|
||||||
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
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
%% @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),
|
push_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups),
|
||||||
broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups),
|
broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups),
|
||||||
broadcast_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups),
|
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.
|
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) ->
|
get_displayed_groups(Group, LServer) ->
|
||||||
GroupsOpts = groups_with_opts(LServer),
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||||
|
@ -894,8 +622,8 @@ remove_user_from_group(Host, US, Group) ->
|
||||||
end,
|
end,
|
||||||
(?MODULE):set_group_opts(Host, Group, NewGroupOpts);
|
(?MODULE):set_group_opts(Host, Group, NewGroupOpts);
|
||||||
nomatch ->
|
nomatch ->
|
||||||
Result = remove_user_from_group(Host, US, Group,
|
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||||
gen_mod:db_type(Host, ?MODULE)),
|
Result = Mod:remove_user_from_group(Host, US, Group),
|
||||||
DisplayedToGroups = displayed_to_groups(Group, Host),
|
DisplayedToGroups = displayed_to_groups(Group, Host),
|
||||||
DisplayedGroups = get_displayed_groups(Group, LServer),
|
DisplayedGroups = get_displayed_groups(Group, LServer),
|
||||||
push_user_to_displayed(LUser, LServer, Group, Host, remove, DisplayedToGroups),
|
push_user_to_displayed(LUser, LServer, Group, Host, remove, DisplayedToGroups),
|
||||||
|
@ -903,23 +631,6 @@ remove_user_from_group(Host, US, Group) ->
|
||||||
Result
|
Result
|
||||||
end.
|
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,
|
push_members_to_user(LUser, LServer, Group, Host,
|
||||||
Subscription) ->
|
Subscription) ->
|
||||||
GroupsOpts = groups_with_opts(LServer),
|
GroupsOpts = groups_with_opts(LServer),
|
||||||
|
@ -1385,13 +1096,6 @@ displayed_groups_update(Members, DisplayedGroups, Subscription) ->
|
||||||
end
|
end
|
||||||
end, Members).
|
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) ->
|
opts_to_binary(Opts) ->
|
||||||
lists:map(
|
lists:map(
|
||||||
fun({name, Name}) ->
|
fun({name, Name}) ->
|
||||||
|
@ -1404,105 +1108,17 @@ opts_to_binary(Opts) ->
|
||||||
Opt
|
Opt
|
||||||
end, Opts).
|
end, Opts).
|
||||||
|
|
||||||
sr_group_schema() ->
|
export(LServer) ->
|
||||||
{record_info(fields, sr_group), #sr_group{}}.
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Mod:export(LServer).
|
||||||
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}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select name, opts from sr_group;">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([Group, SOpts]) ->
|
Mod:import(LServer).
|
||||||
#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) ->
|
import(LServer, DBType, Data) ->
|
||||||
mnesia:dirty_write(G);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
|
Mod:import(LServer, Data).
|
||||||
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_opt_type(db_type) -> fun gen_mod:v_db/1;
|
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||||
mod_opt_type(_) -> [db_type].
|
mod_opt_type(_) -> [db_type].
|
||||||
|
|
|
@ -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.
|
|
@ -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{}}.
|
|
@ -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).
|
|
@ -25,8 +25,6 @@
|
||||||
|
|
||||||
-module(mod_vcard).
|
-module(mod_vcard).
|
||||||
|
|
||||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
|
||||||
|
|
||||||
-author('alexey@process-one.net').
|
-author('alexey@process-one.net').
|
||||||
|
|
||||||
-protocol({xep, 54, '1.2'}).
|
-protocol({xep, 54, '1.2'}).
|
||||||
|
@ -35,54 +33,31 @@
|
||||||
-behaviour(gen_mod).
|
-behaviour(gen_mod).
|
||||||
|
|
||||||
-export([start/2, init/3, stop/1, get_sm_features/5,
|
-export([start/2, init/3, stop/1, get_sm_features/5,
|
||||||
process_local_iq/3, process_sm_iq/3, reindex_vcards/0,
|
process_local_iq/3, process_sm_iq/3, string2lower/1,
|
||||||
remove_user/2, export/1, import/1, import/3,
|
remove_user/2, export/1, import/1, import/3,
|
||||||
mod_opt_type/1, set_vcard/3]).
|
mod_opt_type/1, set_vcard/3, make_vcard_search/4]).
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
-include("ejabberd_sql_pt.hrl").
|
|
||||||
|
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
-include("mod_vcard.hrl").
|
||||||
|
|
||||||
-define(JUD_MATCHES, 30).
|
-define(JUD_MATCHES, 30).
|
||||||
|
|
||||||
-record(vcard_search,
|
|
||||||
{us, user, luser, fn, lfn, family, lfamily, given,
|
|
||||||
lgiven, middle, lmiddle, nickname, lnickname, bday,
|
|
||||||
lbday, ctry, lctry, locality, llocality, email, lemail,
|
|
||||||
orgname, lorgname, orgunit, lorgunit}).
|
|
||||||
|
|
||||||
-record(vcard, {us = {<<"">>, <<"">>} :: {binary(), binary()} | binary(),
|
|
||||||
vcard = #xmlel{} :: xmlel()}).
|
|
||||||
|
|
||||||
-define(PROCNAME, ejabberd_mod_vcard).
|
-define(PROCNAME, ejabberd_mod_vcard).
|
||||||
|
|
||||||
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
|
-callback import(binary(), #vcard{} | #vcard_search{}) -> ok | pass.
|
||||||
|
-callback get_vcard(binary(), binary()) -> [xmlel()] | error.
|
||||||
|
-callback set_vcard(binary(), binary(),
|
||||||
|
xmlel(), #vcard_search{}) -> {atomic, any()}.
|
||||||
|
-callback search(binary(), [{binary(), [binary()]}], boolean(),
|
||||||
|
infinity | pos_integer()) -> [binary()].
|
||||||
|
-callback remove_user(binary(), binary()) -> {atomic, any()}.
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(vcard,
|
|
||||||
[{disc_only_copies, [node()]},
|
|
||||||
{attributes, record_info(fields, vcard)}]),
|
|
||||||
mnesia:create_table(vcard_search,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, vcard_search)}]),
|
|
||||||
update_tables(),
|
|
||||||
mnesia:add_table_index(vcard_search, luser),
|
|
||||||
mnesia:add_table_index(vcard_search, lfn),
|
|
||||||
mnesia:add_table_index(vcard_search, lfamily),
|
|
||||||
mnesia:add_table_index(vcard_search, lgiven),
|
|
||||||
mnesia:add_table_index(vcard_search, lmiddle),
|
|
||||||
mnesia:add_table_index(vcard_search, lnickname),
|
|
||||||
mnesia:add_table_index(vcard_search, lbday),
|
|
||||||
mnesia:add_table_index(vcard_search, lctry),
|
|
||||||
mnesia:add_table_index(vcard_search, llocality),
|
|
||||||
mnesia:add_table_index(vcard_search, lemail),
|
|
||||||
mnesia:add_table_index(vcard_search, lorgname),
|
|
||||||
mnesia:add_table_index(vcard_search, lorgunit);
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
||||||
remove_user, 50),
|
remove_user, 50),
|
||||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||||
|
@ -206,38 +181,10 @@ process_sm_iq(From, To,
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_vcard(LUser, LServer) ->
|
get_vcard(LUser, LServer) ->
|
||||||
get_vcard(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_vcard(LUser, LServer).
|
||||||
|
|
||||||
get_vcard(LUser, LServer, mnesia) ->
|
make_vcard_search(User, LUser, LServer, VCARD) ->
|
||||||
US = {LUser, LServer},
|
|
||||||
F = fun () -> mnesia:read({vcard, US}) end,
|
|
||||||
case mnesia:transaction(F) of
|
|
||||||
{atomic, Rs} ->
|
|
||||||
lists:map(fun (R) -> R#vcard.vcard end, Rs);
|
|
||||||
{aborted, _Reason} -> error
|
|
||||||
end;
|
|
||||||
get_vcard(LUser, LServer, odbc) ->
|
|
||||||
case catch odbc_queries:get_vcard(LServer, LUser) of
|
|
||||||
{selected, [{SVCARD}]} ->
|
|
||||||
case fxml_stream:parse_element(SVCARD) of
|
|
||||||
{error, _Reason} -> error;
|
|
||||||
VCARD -> [VCARD]
|
|
||||||
end;
|
|
||||||
{selected, []} -> [];
|
|
||||||
_ -> error
|
|
||||||
end;
|
|
||||||
get_vcard(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(vcard, vcard_schema(), {LUser, LServer}) of
|
|
||||||
{ok, R} ->
|
|
||||||
[R#vcard.vcard];
|
|
||||||
{error, notfound} ->
|
|
||||||
[];
|
|
||||||
_ ->
|
|
||||||
error
|
|
||||||
end.
|
|
||||||
|
|
||||||
set_vcard(User, LServer, VCARD) ->
|
|
||||||
FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
|
FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
|
||||||
Family = fxml:get_path_s(VCARD,
|
Family = fxml:get_path_s(VCARD,
|
||||||
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
|
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
|
||||||
|
@ -266,7 +213,6 @@ set_vcard(User, LServer, VCARD) ->
|
||||||
<<"">> -> EMail2;
|
<<"">> -> EMail2;
|
||||||
_ -> EMail1
|
_ -> EMail1
|
||||||
end,
|
end,
|
||||||
LUser = jid:nodeprep(User),
|
|
||||||
LFN = string2lower(FN),
|
LFN = string2lower(FN),
|
||||||
LFamily = string2lower(Family),
|
LFamily = string2lower(Family),
|
||||||
LGiven = string2lower(Given),
|
LGiven = string2lower(Given),
|
||||||
|
@ -278,80 +224,42 @@ set_vcard(User, LServer, VCARD) ->
|
||||||
LEMail = string2lower(EMail),
|
LEMail = string2lower(EMail),
|
||||||
LOrgName = string2lower(OrgName),
|
LOrgName = string2lower(OrgName),
|
||||||
LOrgUnit = string2lower(OrgUnit),
|
LOrgUnit = string2lower(OrgUnit),
|
||||||
if (LUser == error) ->
|
US = {LUser, LServer},
|
||||||
{error, badarg};
|
#vcard_search{us = US,
|
||||||
true ->
|
user = {User, LServer},
|
||||||
case gen_mod:db_type(LServer, ?MODULE) of
|
luser = LUser, fn = FN,
|
||||||
mnesia ->
|
lfn = LFN,
|
||||||
US = {LUser, LServer},
|
family = Family,
|
||||||
F = fun () ->
|
lfamily = LFamily,
|
||||||
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
given = Given,
|
||||||
mnesia:write(#vcard_search{us = US,
|
lgiven = LGiven,
|
||||||
user = {User, LServer},
|
middle = Middle,
|
||||||
luser = LUser, fn = FN,
|
lmiddle = LMiddle,
|
||||||
lfn = LFN,
|
nickname = Nickname,
|
||||||
family = Family,
|
lnickname = LNickname,
|
||||||
lfamily = LFamily,
|
bday = BDay,
|
||||||
given = Given,
|
lbday = LBDay,
|
||||||
lgiven = LGiven,
|
ctry = CTRY,
|
||||||
middle = Middle,
|
lctry = LCTRY,
|
||||||
lmiddle = LMiddle,
|
locality = Locality,
|
||||||
nickname = Nickname,
|
llocality = LLocality,
|
||||||
lnickname = LNickname,
|
email = EMail,
|
||||||
bday = BDay,
|
lemail = LEMail,
|
||||||
lbday = LBDay,
|
orgname = OrgName,
|
||||||
ctry = CTRY,
|
lorgname = LOrgName,
|
||||||
lctry = LCTRY,
|
orgunit = OrgUnit,
|
||||||
locality = Locality,
|
lorgunit = LOrgUnit}.
|
||||||
llocality = LLocality,
|
|
||||||
email = EMail,
|
set_vcard(User, LServer, VCARD) ->
|
||||||
lemail = LEMail,
|
case jid:nodeprep(User) of
|
||||||
orgname = OrgName,
|
error ->
|
||||||
lorgname = LOrgName,
|
{error, badarg};
|
||||||
orgunit = OrgUnit,
|
LUser ->
|
||||||
lorgunit = LOrgUnit})
|
VCardSearch = make_vcard_search(User, LUser, LServer, VCARD),
|
||||||
end,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
mnesia:transaction(F);
|
Mod:set_vcard(LUser, LServer, VCARD, VCardSearch),
|
||||||
riak ->
|
ejabberd_hooks:run(vcard_set, LServer,
|
||||||
US = {LUser, LServer},
|
[LUser, LServer, VCARD])
|
||||||
ejabberd_riak:put(#vcard{us = US, vcard = VCARD},
|
|
||||||
vcard_schema(),
|
|
||||||
[{'2i', [{<<"user">>, User},
|
|
||||||
{<<"luser">>, LUser},
|
|
||||||
{<<"fn">>, FN},
|
|
||||||
{<<"lfn">>, LFN},
|
|
||||||
{<<"family">>, Family},
|
|
||||||
{<<"lfamily">>, LFamily},
|
|
||||||
{<<"given">>, Given},
|
|
||||||
{<<"lgiven">>, LGiven},
|
|
||||||
{<<"middle">>, Middle},
|
|
||||||
{<<"lmiddle">>, LMiddle},
|
|
||||||
{<<"nickname">>, Nickname},
|
|
||||||
{<<"lnickname">>, LNickname},
|
|
||||||
{<<"bday">>, BDay},
|
|
||||||
{<<"lbday">>, LBDay},
|
|
||||||
{<<"ctry">>, CTRY},
|
|
||||||
{<<"lctry">>, LCTRY},
|
|
||||||
{<<"locality">>, Locality},
|
|
||||||
{<<"llocality">>, LLocality},
|
|
||||||
{<<"email">>, EMail},
|
|
||||||
{<<"lemail">>, LEMail},
|
|
||||||
{<<"orgname">>, OrgName},
|
|
||||||
{<<"lorgname">>, LOrgName},
|
|
||||||
{<<"orgunit">>, OrgUnit},
|
|
||||||
{<<"lorgunit">>, LOrgUnit}]}]);
|
|
||||||
odbc ->
|
|
||||||
SVCARD = fxml:element_to_binary(VCARD),
|
|
||||||
odbc_queries:set_vcard(LServer, LUser, BDay, CTRY,
|
|
||||||
EMail, FN, Family, Given, LBDay,
|
|
||||||
LCTRY, LEMail, LFN, LFamily,
|
|
||||||
LGiven, LLocality, LMiddle,
|
|
||||||
LNickname, LOrgName, LOrgUnit,
|
|
||||||
Locality, Middle, Nickname, OrgName,
|
|
||||||
OrgUnit, SVCARD, User)
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:run(vcard_set, LServer,
|
|
||||||
[LUser, LServer, VCARD])
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
string2lower(String) ->
|
string2lower(String) ->
|
||||||
|
@ -655,500 +563,36 @@ record_to_item(_LServer, #vcard_search{} = R) ->
|
||||||
?FIELD(<<"orgunit">>, (R#vcard_search.orgunit))]}.
|
?FIELD(<<"orgunit">>, (R#vcard_search.orgunit))]}.
|
||||||
|
|
||||||
search(LServer, Data) ->
|
search(LServer, Data) ->
|
||||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
MatchSpec = make_matchspec(LServer, Data, DBType),
|
|
||||||
AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE, allow_return_all,
|
AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE, allow_return_all,
|
||||||
fun(B) when is_boolean(B) -> B end,
|
fun(B) when is_boolean(B) -> B end,
|
||||||
false),
|
false),
|
||||||
search(LServer, MatchSpec, AllowReturnAll, DBType).
|
MaxMatch = gen_mod:get_module_opt(LServer, ?MODULE, matches,
|
||||||
|
fun(infinity) -> infinity;
|
||||||
search(LServer, MatchSpec, AllowReturnAll, mnesia) ->
|
(I) when is_integer(I),
|
||||||
if (MatchSpec == #vcard_search{_ = '_'}) and
|
I>0 ->
|
||||||
not AllowReturnAll ->
|
I
|
||||||
[];
|
end, ?JUD_MATCHES),
|
||||||
true ->
|
Mod:search(LServer, Data, AllowReturnAll, MaxMatch).
|
||||||
case catch mnesia:dirty_select(vcard_search,
|
|
||||||
[{MatchSpec, [], ['$_']}])
|
|
||||||
of
|
|
||||||
{'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), [];
|
|
||||||
Rs ->
|
|
||||||
case gen_mod:get_module_opt(LServer, ?MODULE, matches,
|
|
||||||
fun(infinity) -> infinity;
|
|
||||||
(I) when is_integer(I),
|
|
||||||
I>0 ->
|
|
||||||
I
|
|
||||||
end, ?JUD_MATCHES) of
|
|
||||||
infinity ->
|
|
||||||
Rs;
|
|
||||||
Val ->
|
|
||||||
lists:sublist(Rs, Val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
search(LServer, MatchSpec, AllowReturnAll, odbc) ->
|
|
||||||
if (MatchSpec == <<"">>) and not AllowReturnAll -> [];
|
|
||||||
true ->
|
|
||||||
Limit = case gen_mod:get_module_opt(LServer, ?MODULE, matches,
|
|
||||||
fun(infinity) -> infinity;
|
|
||||||
(I) when is_integer(I),
|
|
||||||
I>0 ->
|
|
||||||
I
|
|
||||||
end, ?JUD_MATCHES) of
|
|
||||||
infinity ->
|
|
||||||
<<"">>;
|
|
||||||
Val ->
|
|
||||||
[<<" LIMIT ">>,
|
|
||||||
jlib:integer_to_binary(Val)]
|
|
||||||
end,
|
|
||||||
case catch ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select username, fn, family, given, "
|
|
||||||
"middle, nickname, bday, ctry, "
|
|
||||||
"locality, email, orgname, orgunit "
|
|
||||||
"from vcard_search ">>,
|
|
||||||
MatchSpec, Limit, <<";">>])
|
|
||||||
of
|
|
||||||
{selected,
|
|
||||||
[<<"username">>, <<"fn">>, <<"family">>, <<"given">>,
|
|
||||||
<<"middle">>, <<"nickname">>, <<"bday">>, <<"ctry">>,
|
|
||||||
<<"locality">>, <<"email">>, <<"orgname">>,
|
|
||||||
<<"orgunit">>],
|
|
||||||
Rs}
|
|
||||||
when is_list(Rs) ->
|
|
||||||
Rs;
|
|
||||||
Error -> ?ERROR_MSG("~p", [Error]), []
|
|
||||||
end
|
|
||||||
end;
|
|
||||||
search(_LServer, _MatchSpec, _AllowReturnAll, riak) ->
|
|
||||||
[].
|
|
||||||
|
|
||||||
make_matchspec(LServer, Data, mnesia) ->
|
|
||||||
GlobMatch = #vcard_search{_ = '_'},
|
|
||||||
Match = filter_fields(Data, GlobMatch, LServer, mnesia),
|
|
||||||
Match;
|
|
||||||
make_matchspec(LServer, Data, odbc) ->
|
|
||||||
filter_fields(Data, <<"">>, LServer, odbc);
|
|
||||||
make_matchspec(_LServer, _Data, riak) ->
|
|
||||||
[].
|
|
||||||
|
|
||||||
filter_fields([], Match, _LServer, mnesia) -> Match;
|
|
||||||
filter_fields([], Match, _LServer, odbc) ->
|
|
||||||
case Match of
|
|
||||||
<<"">> -> <<"">>;
|
|
||||||
_ -> [<<" where ">>, Match]
|
|
||||||
end;
|
|
||||||
filter_fields([{SVar, [Val]} | Ds], Match, LServer,
|
|
||||||
mnesia)
|
|
||||||
when is_binary(Val) and (Val /= <<"">>) ->
|
|
||||||
LVal = string2lower(Val),
|
|
||||||
NewMatch = case SVar of
|
|
||||||
<<"user">> ->
|
|
||||||
case gen_mod:get_module_opt(LServer, ?MODULE,
|
|
||||||
search_all_hosts,
|
|
||||||
fun(B) when is_boolean(B) ->
|
|
||||||
B
|
|
||||||
end, true)
|
|
||||||
of
|
|
||||||
true -> Match#vcard_search{luser = make_val(LVal)};
|
|
||||||
false ->
|
|
||||||
Host = find_my_host(LServer),
|
|
||||||
Match#vcard_search{us = {make_val(LVal), Host}}
|
|
||||||
end;
|
|
||||||
<<"fn">> -> Match#vcard_search{lfn = make_val(LVal)};
|
|
||||||
<<"last">> ->
|
|
||||||
Match#vcard_search{lfamily = make_val(LVal)};
|
|
||||||
<<"first">> ->
|
|
||||||
Match#vcard_search{lgiven = make_val(LVal)};
|
|
||||||
<<"middle">> ->
|
|
||||||
Match#vcard_search{lmiddle = make_val(LVal)};
|
|
||||||
<<"nick">> ->
|
|
||||||
Match#vcard_search{lnickname = make_val(LVal)};
|
|
||||||
<<"bday">> ->
|
|
||||||
Match#vcard_search{lbday = make_val(LVal)};
|
|
||||||
<<"ctry">> ->
|
|
||||||
Match#vcard_search{lctry = make_val(LVal)};
|
|
||||||
<<"locality">> ->
|
|
||||||
Match#vcard_search{llocality = make_val(LVal)};
|
|
||||||
<<"email">> ->
|
|
||||||
Match#vcard_search{lemail = make_val(LVal)};
|
|
||||||
<<"orgname">> ->
|
|
||||||
Match#vcard_search{lorgname = make_val(LVal)};
|
|
||||||
<<"orgunit">> ->
|
|
||||||
Match#vcard_search{lorgunit = make_val(LVal)};
|
|
||||||
_ -> Match
|
|
||||||
end,
|
|
||||||
filter_fields(Ds, NewMatch, LServer, mnesia);
|
|
||||||
filter_fields([{SVar, [Val]} | Ds], Match, LServer,
|
|
||||||
odbc)
|
|
||||||
when is_binary(Val) and (Val /= <<"">>) ->
|
|
||||||
LVal = string2lower(Val),
|
|
||||||
NewMatch = case SVar of
|
|
||||||
<<"user">> -> make_val(Match, <<"lusername">>, LVal);
|
|
||||||
<<"fn">> -> make_val(Match, <<"lfn">>, LVal);
|
|
||||||
<<"last">> -> make_val(Match, <<"lfamily">>, LVal);
|
|
||||||
<<"first">> -> make_val(Match, <<"lgiven">>, LVal);
|
|
||||||
<<"middle">> -> make_val(Match, <<"lmiddle">>, LVal);
|
|
||||||
<<"nick">> -> make_val(Match, <<"lnickname">>, LVal);
|
|
||||||
<<"bday">> -> make_val(Match, <<"lbday">>, LVal);
|
|
||||||
<<"ctry">> -> make_val(Match, <<"lctry">>, LVal);
|
|
||||||
<<"locality">> ->
|
|
||||||
make_val(Match, <<"llocality">>, LVal);
|
|
||||||
<<"email">> -> make_val(Match, <<"lemail">>, LVal);
|
|
||||||
<<"orgname">> -> make_val(Match, <<"lorgname">>, LVal);
|
|
||||||
<<"orgunit">> -> make_val(Match, <<"lorgunit">>, LVal);
|
|
||||||
_ -> Match
|
|
||||||
end,
|
|
||||||
filter_fields(Ds, NewMatch, LServer, odbc);
|
|
||||||
filter_fields([_ | Ds], Match, LServer, DBType) ->
|
|
||||||
filter_fields(Ds, Match, LServer, DBType).
|
|
||||||
|
|
||||||
make_val(Match, Field, Val) ->
|
|
||||||
Condition = case str:suffix(<<"*">>, Val) of
|
|
||||||
true ->
|
|
||||||
Val1 = str:substr(Val, 1, byte_size(Val) - 1),
|
|
||||||
SVal = <<(ejabberd_odbc:escape_like(Val1))/binary,
|
|
||||||
"%">>,
|
|
||||||
[Field, <<" LIKE '">>, SVal, <<"'">>];
|
|
||||||
_ ->
|
|
||||||
SVal = ejabberd_odbc:escape(Val),
|
|
||||||
[Field, <<" = '">>, SVal, <<"'">>]
|
|
||||||
end,
|
|
||||||
case Match of
|
|
||||||
<<"">> -> Condition;
|
|
||||||
_ -> [Match, <<" and ">>, Condition]
|
|
||||||
end.
|
|
||||||
|
|
||||||
make_val(Val) ->
|
|
||||||
case str:suffix(<<"*">>, Val) of
|
|
||||||
true -> [str:substr(Val, 1, byte_size(Val) - 1)] ++ '_';
|
|
||||||
_ -> Val
|
|
||||||
end.
|
|
||||||
|
|
||||||
find_my_host(LServer) ->
|
|
||||||
Parts = str:tokens(LServer, <<".">>),
|
|
||||||
find_my_host(Parts, ?MYHOSTS).
|
|
||||||
|
|
||||||
find_my_host([], _Hosts) -> ?MYNAME;
|
|
||||||
find_my_host([_ | Tail] = Parts, Hosts) ->
|
|
||||||
Domain = parts_to_string(Parts),
|
|
||||||
case lists:member(Domain, Hosts) of
|
|
||||||
true -> Domain;
|
|
||||||
false -> find_my_host(Tail, Hosts)
|
|
||||||
end.
|
|
||||||
|
|
||||||
parts_to_string(Parts) ->
|
|
||||||
str:strip(list_to_binary(
|
|
||||||
lists:map(fun (S) -> <<S/binary, $.>> end, Parts)),
|
|
||||||
right, $.).
|
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
set_vcard_t(R, _) ->
|
|
||||||
US = R#vcard.us,
|
|
||||||
User = US,
|
|
||||||
VCARD = R#vcard.vcard,
|
|
||||||
FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
|
|
||||||
Family = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
|
|
||||||
Given = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"N">>}, {elem, <<"GIVEN">>}, cdata]),
|
|
||||||
Middle = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"N">>}, {elem, <<"MIDDLE">>}, cdata]),
|
|
||||||
Nickname = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"NICKNAME">>}, cdata]),
|
|
||||||
BDay = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"BDAY">>}, cdata]),
|
|
||||||
CTRY = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"ADR">>}, {elem, <<"CTRY">>}, cdata]),
|
|
||||||
Locality = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"ADR">>}, {elem, <<"LOCALITY">>},
|
|
||||||
cdata]),
|
|
||||||
EMail = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"EMAIL">>}, cdata]),
|
|
||||||
OrgName = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"ORG">>}, {elem, <<"ORGNAME">>}, cdata]),
|
|
||||||
OrgUnit = fxml:get_path_s(VCARD,
|
|
||||||
[{elem, <<"ORG">>}, {elem, <<"ORGUNIT">>}, cdata]),
|
|
||||||
{LUser, _LServer} = US,
|
|
||||||
LFN = string2lower(FN),
|
|
||||||
LFamily = string2lower(Family),
|
|
||||||
LGiven = string2lower(Given),
|
|
||||||
LMiddle = string2lower(Middle),
|
|
||||||
LNickname = string2lower(Nickname),
|
|
||||||
LBDay = string2lower(BDay),
|
|
||||||
LCTRY = string2lower(CTRY),
|
|
||||||
LLocality = string2lower(Locality),
|
|
||||||
LEMail = string2lower(EMail),
|
|
||||||
LOrgName = string2lower(OrgName),
|
|
||||||
LOrgUnit = string2lower(OrgUnit),
|
|
||||||
mnesia:write(#vcard_search{us = US, user = User,
|
|
||||||
luser = LUser, fn = FN, lfn = LFN,
|
|
||||||
family = Family, lfamily = LFamily,
|
|
||||||
given = Given, lgiven = LGiven,
|
|
||||||
middle = Middle, lmiddle = LMiddle,
|
|
||||||
nickname = Nickname,
|
|
||||||
lnickname = LNickname, bday = BDay,
|
|
||||||
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
|
|
||||||
locality = Locality,
|
|
||||||
llocality = LLocality, email = EMail,
|
|
||||||
lemail = LEMail, orgname = OrgName,
|
|
||||||
lorgname = LOrgName, orgunit = OrgUnit,
|
|
||||||
lorgunit = LOrgUnit}).
|
|
||||||
|
|
||||||
reindex_vcards() ->
|
|
||||||
F = fun () -> mnesia:foldl(fun set_vcard_t/2, [], vcard)
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F).
|
|
||||||
|
|
||||||
remove_user(User, Server) ->
|
remove_user(User, Server) ->
|
||||||
LUser = jid:nodeprep(User),
|
LUser = jid:nodeprep(User),
|
||||||
LServer = jid:nameprep(Server),
|
LServer = jid:nameprep(Server),
|
||||||
remove_user(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:remove_user(LUser, LServer).
|
||||||
|
|
||||||
remove_user(LUser, LServer, mnesia) ->
|
export(LServer) ->
|
||||||
US = {LUser, LServer},
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
F = fun () ->
|
Mod:export(LServer).
|
||||||
mnesia:delete({vcard, US}),
|
|
||||||
mnesia:delete({vcard_search, US})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_user(LUser, LServer, odbc) ->
|
|
||||||
ejabberd_odbc:sql_transaction(
|
|
||||||
LServer,
|
|
||||||
fun() ->
|
|
||||||
ejabberd_odbc:sql_query_t(
|
|
||||||
?SQL("delete from vcard where username=%(LUser)s")),
|
|
||||||
ejabberd_odbc:sql_query_t(
|
|
||||||
?SQL("delete from vcard_search where lusername=%(LUser)s"))
|
|
||||||
end);
|
|
||||||
remove_user(LUser, LServer, riak) ->
|
|
||||||
{atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}.
|
|
||||||
|
|
||||||
update_tables() ->
|
|
||||||
update_vcard_table(),
|
|
||||||
update_vcard_search_table().
|
|
||||||
|
|
||||||
update_vcard_table() ->
|
|
||||||
Fields = record_info(fields, vcard),
|
|
||||||
case mnesia:table_info(vcard, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
vcard, Fields, set,
|
|
||||||
fun(#vcard{us = {U, _}}) -> U end,
|
|
||||||
fun(#vcard{us = {U, S}, vcard = El} = R) ->
|
|
||||||
R#vcard{us = {iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)},
|
|
||||||
vcard = fxml:to_xmlel(El)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating vcard table", []),
|
|
||||||
mnesia:transform_table(vcard, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
update_vcard_search_table() ->
|
|
||||||
Fields = record_info(fields, vcard_search),
|
|
||||||
case mnesia:table_info(vcard_search, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
vcard_search, Fields, set,
|
|
||||||
fun(#vcard_search{us = {U, _}}) -> U end,
|
|
||||||
fun(#vcard_search{} = VS) ->
|
|
||||||
[vcard_search | L] = tuple_to_list(VS),
|
|
||||||
NewL = lists:map(
|
|
||||||
fun({U, S}) ->
|
|
||||||
{iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)};
|
|
||||||
(Str) ->
|
|
||||||
iolist_to_binary(Str)
|
|
||||||
end, L),
|
|
||||||
list_to_tuple([vcard_search | NewL])
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating vcard_search table", []),
|
|
||||||
mnesia:transform_table(vcard_search, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
vcard_schema() ->
|
|
||||||
{record_info(fields, vcard), #vcard{}}.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{vcard,
|
|
||||||
fun(Host, #vcard{us = {LUser, LServer}, vcard = VCARD})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SVCARD =
|
|
||||||
ejabberd_odbc:escape(fxml:element_to_binary(VCARD)),
|
|
||||||
[[<<"delete from vcard where username='">>, Username, <<"';">>],
|
|
||||||
[<<"insert into vcard(username, vcard) values ('">>,
|
|
||||||
Username, <<"', '">>, SVCARD, <<"');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end},
|
|
||||||
{vcard_search,
|
|
||||||
fun(Host, #vcard_search{user = {User, LServer}, luser = LUser,
|
|
||||||
fn = FN, lfn = LFN, family = Family,
|
|
||||||
lfamily = LFamily, given = Given,
|
|
||||||
lgiven = LGiven, middle = Middle,
|
|
||||||
lmiddle = LMiddle, nickname = Nickname,
|
|
||||||
lnickname = LNickname, bday = BDay,
|
|
||||||
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
|
|
||||||
locality = Locality, llocality = LLocality,
|
|
||||||
email = EMail, lemail = LEMail,
|
|
||||||
orgname = OrgName, lorgname = LOrgName,
|
|
||||||
orgunit = OrgUnit, lorgunit = LOrgUnit})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(User),
|
|
||||||
LUsername = ejabberd_odbc:escape(LUser),
|
|
||||||
SFN = ejabberd_odbc:escape(FN),
|
|
||||||
SLFN = ejabberd_odbc:escape(LFN),
|
|
||||||
SFamily = ejabberd_odbc:escape(Family),
|
|
||||||
SLFamily = ejabberd_odbc:escape(LFamily),
|
|
||||||
SGiven = ejabberd_odbc:escape(Given),
|
|
||||||
SLGiven = ejabberd_odbc:escape(LGiven),
|
|
||||||
SMiddle = ejabberd_odbc:escape(Middle),
|
|
||||||
SLMiddle = ejabberd_odbc:escape(LMiddle),
|
|
||||||
SNickname = ejabberd_odbc:escape(Nickname),
|
|
||||||
SLNickname = ejabberd_odbc:escape(LNickname),
|
|
||||||
SBDay = ejabberd_odbc:escape(BDay),
|
|
||||||
SLBDay = ejabberd_odbc:escape(LBDay),
|
|
||||||
SCTRY = ejabberd_odbc:escape(CTRY),
|
|
||||||
SLCTRY = ejabberd_odbc:escape(LCTRY),
|
|
||||||
SLocality = ejabberd_odbc:escape(Locality),
|
|
||||||
SLLocality = ejabberd_odbc:escape(LLocality),
|
|
||||||
SEMail = ejabberd_odbc:escape(EMail),
|
|
||||||
SLEMail = ejabberd_odbc:escape(LEMail),
|
|
||||||
SOrgName = ejabberd_odbc:escape(OrgName),
|
|
||||||
SLOrgName = ejabberd_odbc:escape(LOrgName),
|
|
||||||
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
|
|
||||||
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
|
|
||||||
[[<<"delete from vcard_search where lusername='">>,
|
|
||||||
LUsername, <<"';">>],
|
|
||||||
[<<"insert into vcard_search( username, "
|
|
||||||
"lusername, fn, lfn, family, lfamily, "
|
|
||||||
" given, lgiven, middle, lmiddle, "
|
|
||||||
"nickname, lnickname, bday, lbday, "
|
|
||||||
"ctry, lctry, locality, llocality, "
|
|
||||||
" email, lemail, orgname, lorgname, "
|
|
||||||
"orgunit, lorgunit)values (">>,
|
|
||||||
<<" '">>, Username, <<"', '">>, LUsername,
|
|
||||||
<<"', '">>, SFN, <<"', '">>, SLFN,
|
|
||||||
<<"', '">>, SFamily, <<"', '">>, SLFamily,
|
|
||||||
<<"', '">>, SGiven, <<"', '">>, SLGiven,
|
|
||||||
<<"', '">>, SMiddle, <<"', '">>, SLMiddle,
|
|
||||||
<<"', '">>, SNickname, <<"', '">>, SLNickname,
|
|
||||||
<<"', '">>, SBDay, <<"', '">>, SLBDay,
|
|
||||||
<<"', '">>, SCTRY, <<"', '">>, SLCTRY,
|
|
||||||
<<"', '">>, SLocality, <<"', '">>, SLLocality,
|
|
||||||
<<"', '">>, SEMail, <<"', '">>, SLEMail,
|
|
||||||
<<"', '">>, SOrgName, <<"', '">>, SLOrgName,
|
|
||||||
<<"', '">>, SOrgUnit, <<"', '">>, SLOrgUnit,
|
|
||||||
<<"');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select username, vcard from vcard;">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([LUser, SVCard]) ->
|
Mod:import(LServer).
|
||||||
#xmlel{} = VCARD = fxml_stream:parse_element(SVCard),
|
|
||||||
#vcard{us = {LUser, LServer}, vcard = VCARD}
|
|
||||||
end},
|
|
||||||
{<<"select username, lusername, fn, lfn, family, lfamily, "
|
|
||||||
"given, lgiven, middle, lmiddle, nickname, lnickname, "
|
|
||||||
"bday, lbday, ctry, lctry, locality, llocality, email, "
|
|
||||||
"lemail, orgname, lorgname, orgunit, lorgunit from vcard_search;">>,
|
|
||||||
fun([User, LUser, FN, LFN,
|
|
||||||
Family, LFamily, Given, LGiven,
|
|
||||||
Middle, LMiddle, Nickname, LNickname,
|
|
||||||
BDay, LBDay, CTRY, LCTRY, Locality, LLocality,
|
|
||||||
EMail, LEMail, OrgName, LOrgName, OrgUnit, LOrgUnit]) ->
|
|
||||||
#vcard_search{us = {LUser, LServer},
|
|
||||||
user = {User, LServer}, luser = LUser,
|
|
||||||
fn = FN, lfn = LFN, family = Family,
|
|
||||||
lfamily = LFamily, given = Given,
|
|
||||||
lgiven = LGiven, middle = Middle,
|
|
||||||
lmiddle = LMiddle, nickname = Nickname,
|
|
||||||
lnickname = LNickname, bday = BDay,
|
|
||||||
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
|
|
||||||
locality = Locality, llocality = LLocality,
|
|
||||||
email = EMail, lemail = LEMail,
|
|
||||||
orgname = OrgName, lorgname = LOrgName,
|
|
||||||
orgunit = OrgUnit, lorgunit = LOrgUnit}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #vcard{} = VCard) ->
|
import(LServer, DBType, VCard) ->
|
||||||
mnesia:dirty_write(VCard);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
import(_LServer, mnesia, #vcard_search{} = S) ->
|
Mod:import(LServer, VCard).
|
||||||
mnesia:dirty_write(S);
|
|
||||||
import(_LServer, riak, #vcard{us = {LUser, _}, vcard = El} = VCard) ->
|
|
||||||
FN = fxml:get_path_s(El, [{elem, <<"FN">>}, cdata]),
|
|
||||||
Family = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
|
|
||||||
Given = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"N">>}, {elem, <<"GIVEN">>}, cdata]),
|
|
||||||
Middle = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"N">>}, {elem, <<"MIDDLE">>}, cdata]),
|
|
||||||
Nickname = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"NICKNAME">>}, cdata]),
|
|
||||||
BDay = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"BDAY">>}, cdata]),
|
|
||||||
CTRY = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"ADR">>}, {elem, <<"CTRY">>}, cdata]),
|
|
||||||
Locality = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"ADR">>}, {elem, <<"LOCALITY">>},
|
|
||||||
cdata]),
|
|
||||||
EMail1 = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"EMAIL">>}, {elem, <<"USERID">>}, cdata]),
|
|
||||||
EMail2 = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"EMAIL">>}, cdata]),
|
|
||||||
OrgName = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"ORG">>}, {elem, <<"ORGNAME">>}, cdata]),
|
|
||||||
OrgUnit = fxml:get_path_s(El,
|
|
||||||
[{elem, <<"ORG">>}, {elem, <<"ORGUNIT">>}, cdata]),
|
|
||||||
EMail = case EMail1 of
|
|
||||||
<<"">> -> EMail2;
|
|
||||||
_ -> EMail1
|
|
||||||
end,
|
|
||||||
LFN = string2lower(FN),
|
|
||||||
LFamily = string2lower(Family),
|
|
||||||
LGiven = string2lower(Given),
|
|
||||||
LMiddle = string2lower(Middle),
|
|
||||||
LNickname = string2lower(Nickname),
|
|
||||||
LBDay = string2lower(BDay),
|
|
||||||
LCTRY = string2lower(CTRY),
|
|
||||||
LLocality = string2lower(Locality),
|
|
||||||
LEMail = string2lower(EMail),
|
|
||||||
LOrgName = string2lower(OrgName),
|
|
||||||
LOrgUnit = string2lower(OrgUnit),
|
|
||||||
ejabberd_riak:put(VCard, vcard_schema(),
|
|
||||||
[{'2i', [{<<"user">>, LUser},
|
|
||||||
{<<"luser">>, LUser},
|
|
||||||
{<<"fn">>, FN},
|
|
||||||
{<<"lfn">>, LFN},
|
|
||||||
{<<"family">>, Family},
|
|
||||||
{<<"lfamily">>, LFamily},
|
|
||||||
{<<"given">>, Given},
|
|
||||||
{<<"lgiven">>, LGiven},
|
|
||||||
{<<"middle">>, Middle},
|
|
||||||
{<<"lmiddle">>, LMiddle},
|
|
||||||
{<<"nickname">>, Nickname},
|
|
||||||
{<<"lnickname">>, LNickname},
|
|
||||||
{<<"bday">>, BDay},
|
|
||||||
{<<"lbday">>, LBDay},
|
|
||||||
{<<"ctry">>, CTRY},
|
|
||||||
{<<"lctry">>, LCTRY},
|
|
||||||
{<<"locality">>, Locality},
|
|
||||||
{<<"llocality">>, LLocality},
|
|
||||||
{<<"email">>, EMail},
|
|
||||||
{<<"lemail">>, LEMail},
|
|
||||||
{<<"orgname">>, OrgName},
|
|
||||||
{<<"lorgname">>, LOrgName},
|
|
||||||
{<<"orgunit">>, OrgUnit},
|
|
||||||
{<<"lorgunit">>, LOrgUnit}]}]);
|
|
||||||
import(_LServer, riak, #vcard_search{}) ->
|
|
||||||
ok;
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(allow_return_all) ->
|
mod_opt_type(allow_return_all) ->
|
||||||
fun (B) when is_boolean(B) -> B end;
|
fun (B) when is_boolean(B) -> B end;
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_vcard_mnesia).
|
||||||
|
|
||||||
|
-behaviour(mod_vcard).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, get_vcard/2, set_vcard/4, search/4, remove_user/2]).
|
||||||
|
|
||||||
|
-include("ejabberd.hrl").
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_vcard.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(vcard,
|
||||||
|
[{disc_only_copies, [node()]},
|
||||||
|
{attributes, record_info(fields, vcard)}]),
|
||||||
|
mnesia:create_table(vcard_search,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, vcard_search)}]),
|
||||||
|
update_tables(),
|
||||||
|
mnesia:add_table_index(vcard_search, luser),
|
||||||
|
mnesia:add_table_index(vcard_search, lfn),
|
||||||
|
mnesia:add_table_index(vcard_search, lfamily),
|
||||||
|
mnesia:add_table_index(vcard_search, lgiven),
|
||||||
|
mnesia:add_table_index(vcard_search, lmiddle),
|
||||||
|
mnesia:add_table_index(vcard_search, lnickname),
|
||||||
|
mnesia:add_table_index(vcard_search, lbday),
|
||||||
|
mnesia:add_table_index(vcard_search, lctry),
|
||||||
|
mnesia:add_table_index(vcard_search, llocality),
|
||||||
|
mnesia:add_table_index(vcard_search, lemail),
|
||||||
|
mnesia:add_table_index(vcard_search, lorgname),
|
||||||
|
mnesia:add_table_index(vcard_search, lorgunit).
|
||||||
|
|
||||||
|
get_vcard(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () -> mnesia:read({vcard, US}) end,
|
||||||
|
case mnesia:transaction(F) of
|
||||||
|
{atomic, Rs} ->
|
||||||
|
lists:map(fun (R) -> R#vcard.vcard end, Rs);
|
||||||
|
{aborted, _Reason} -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_vcard(LUser, LServer, VCARD, VCardSearch) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
||||||
|
mnesia:write(VCardSearch)
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
search(LServer, Data, AllowReturnAll, MaxMatch) ->
|
||||||
|
MatchSpec = make_matchspec(LServer, Data),
|
||||||
|
if (MatchSpec == #vcard_search{_ = '_'}) and
|
||||||
|
not AllowReturnAll ->
|
||||||
|
[];
|
||||||
|
true ->
|
||||||
|
case catch mnesia:dirty_select(vcard_search,
|
||||||
|
[{MatchSpec, [], ['$_']}]) of
|
||||||
|
{'EXIT', Reason} ->
|
||||||
|
?ERROR_MSG("~p", [Reason]), [];
|
||||||
|
Rs ->
|
||||||
|
case MaxMatch of
|
||||||
|
infinity ->
|
||||||
|
Rs;
|
||||||
|
Val ->
|
||||||
|
lists:sublist(Rs, Val)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:delete({vcard, US}),
|
||||||
|
mnesia:delete({vcard_search, US})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #vcard{} = VCard) ->
|
||||||
|
mnesia:dirty_write(VCard);
|
||||||
|
import(_LServer, #vcard_search{} = S) ->
|
||||||
|
mnesia:dirty_write(S).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_tables() ->
|
||||||
|
update_vcard_table(),
|
||||||
|
update_vcard_search_table().
|
||||||
|
|
||||||
|
update_vcard_table() ->
|
||||||
|
Fields = record_info(fields, vcard),
|
||||||
|
case mnesia:table_info(vcard, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
vcard, Fields, set,
|
||||||
|
fun(#vcard{us = {U, _}}) -> U end,
|
||||||
|
fun(#vcard{us = {U, S}, vcard = El} = R) ->
|
||||||
|
R#vcard{us = {iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
vcard = fxml:to_xmlel(El)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating vcard table", []),
|
||||||
|
mnesia:transform_table(vcard, ignore, Fields)
|
||||||
|
end.
|
||||||
|
|
||||||
|
update_vcard_search_table() ->
|
||||||
|
Fields = record_info(fields, vcard_search),
|
||||||
|
case mnesia:table_info(vcard_search, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
vcard_search, Fields, set,
|
||||||
|
fun(#vcard_search{us = {U, _}}) -> U end,
|
||||||
|
fun(#vcard_search{} = VS) ->
|
||||||
|
[vcard_search | L] = tuple_to_list(VS),
|
||||||
|
NewL = lists:map(
|
||||||
|
fun({U, S}) ->
|
||||||
|
{iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)};
|
||||||
|
(Str) ->
|
||||||
|
iolist_to_binary(Str)
|
||||||
|
end, L),
|
||||||
|
list_to_tuple([vcard_search | NewL])
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating vcard_search table", []),
|
||||||
|
mnesia:transform_table(vcard_search, ignore, Fields)
|
||||||
|
end.
|
||||||
|
|
||||||
|
make_matchspec(LServer, Data) ->
|
||||||
|
GlobMatch = #vcard_search{_ = '_'},
|
||||||
|
Match = filter_fields(Data, GlobMatch, LServer),
|
||||||
|
Match.
|
||||||
|
|
||||||
|
filter_fields([], Match, _LServer) ->
|
||||||
|
Match;
|
||||||
|
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
||||||
|
when is_binary(Val) and (Val /= <<"">>) ->
|
||||||
|
LVal = mod_vcard:string2lower(Val),
|
||||||
|
NewMatch = case SVar of
|
||||||
|
<<"user">> ->
|
||||||
|
case gen_mod:get_module_opt(LServer, ?MODULE,
|
||||||
|
search_all_hosts,
|
||||||
|
fun(B) when is_boolean(B) ->
|
||||||
|
B
|
||||||
|
end, true) of
|
||||||
|
true -> Match#vcard_search{luser = make_val(LVal)};
|
||||||
|
false ->
|
||||||
|
Host = find_my_host(LServer),
|
||||||
|
Match#vcard_search{us = {make_val(LVal), Host}}
|
||||||
|
end;
|
||||||
|
<<"fn">> -> Match#vcard_search{lfn = make_val(LVal)};
|
||||||
|
<<"last">> ->
|
||||||
|
Match#vcard_search{lfamily = make_val(LVal)};
|
||||||
|
<<"first">> ->
|
||||||
|
Match#vcard_search{lgiven = make_val(LVal)};
|
||||||
|
<<"middle">> ->
|
||||||
|
Match#vcard_search{lmiddle = make_val(LVal)};
|
||||||
|
<<"nick">> ->
|
||||||
|
Match#vcard_search{lnickname = make_val(LVal)};
|
||||||
|
<<"bday">> ->
|
||||||
|
Match#vcard_search{lbday = make_val(LVal)};
|
||||||
|
<<"ctry">> ->
|
||||||
|
Match#vcard_search{lctry = make_val(LVal)};
|
||||||
|
<<"locality">> ->
|
||||||
|
Match#vcard_search{llocality = make_val(LVal)};
|
||||||
|
<<"email">> ->
|
||||||
|
Match#vcard_search{lemail = make_val(LVal)};
|
||||||
|
<<"orgname">> ->
|
||||||
|
Match#vcard_search{lorgname = make_val(LVal)};
|
||||||
|
<<"orgunit">> ->
|
||||||
|
Match#vcard_search{lorgunit = make_val(LVal)};
|
||||||
|
_ -> Match
|
||||||
|
end,
|
||||||
|
filter_fields(Ds, NewMatch, LServer);
|
||||||
|
filter_fields([_ | Ds], Match, LServer) ->
|
||||||
|
filter_fields(Ds, Match, LServer).
|
||||||
|
|
||||||
|
make_val(Val) ->
|
||||||
|
case str:suffix(<<"*">>, Val) of
|
||||||
|
true -> [str:substr(Val, 1, byte_size(Val) - 1)] ++ '_';
|
||||||
|
_ -> Val
|
||||||
|
end.
|
||||||
|
|
||||||
|
find_my_host(LServer) ->
|
||||||
|
Parts = str:tokens(LServer, <<".">>),
|
||||||
|
find_my_host(Parts, ?MYHOSTS).
|
||||||
|
|
||||||
|
find_my_host([], _Hosts) -> ?MYNAME;
|
||||||
|
find_my_host([_ | Tail] = Parts, Hosts) ->
|
||||||
|
Domain = parts_to_string(Parts),
|
||||||
|
case lists:member(Domain, Hosts) of
|
||||||
|
true -> Domain;
|
||||||
|
false -> find_my_host(Tail, Hosts)
|
||||||
|
end.
|
||||||
|
|
||||||
|
parts_to_string(Parts) ->
|
||||||
|
str:strip(list_to_binary(
|
||||||
|
lists:map(fun (S) -> <<S/binary, $.>> end, Parts)),
|
||||||
|
right, $.).
|
|
@ -0,0 +1,151 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_vcard_riak).
|
||||||
|
|
||||||
|
-behaviour(mod_vcard).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, get_vcard/2, set_vcard/4, search/4, remove_user/2,
|
||||||
|
import/2]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_vcard.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
get_vcard(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(vcard, vcard_schema(), {LUser, LServer}) of
|
||||||
|
{ok, R} ->
|
||||||
|
[R#vcard.vcard];
|
||||||
|
{error, notfound} ->
|
||||||
|
[];
|
||||||
|
_ ->
|
||||||
|
error
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_vcard(LUser, LServer, VCARD,
|
||||||
|
#vcard_search{user = {User, _},
|
||||||
|
fn = FN,
|
||||||
|
lfn = LFN,
|
||||||
|
family = Family,
|
||||||
|
lfamily = LFamily,
|
||||||
|
given = Given,
|
||||||
|
lgiven = LGiven,
|
||||||
|
middle = Middle,
|
||||||
|
lmiddle = LMiddle,
|
||||||
|
nickname = Nickname,
|
||||||
|
lnickname = LNickname,
|
||||||
|
bday = BDay,
|
||||||
|
lbday = LBDay,
|
||||||
|
ctry = CTRY,
|
||||||
|
lctry = LCTRY,
|
||||||
|
locality = Locality,
|
||||||
|
llocality = LLocality,
|
||||||
|
email = EMail,
|
||||||
|
lemail = LEMail,
|
||||||
|
orgname = OrgName,
|
||||||
|
lorgname = LOrgName,
|
||||||
|
orgunit = OrgUnit,
|
||||||
|
lorgunit = LOrgUnit}) ->
|
||||||
|
US = {LUser, LServer},
|
||||||
|
{atomic,
|
||||||
|
ejabberd_riak:put(#vcard{us = US, vcard = VCARD},
|
||||||
|
vcard_schema(),
|
||||||
|
[{'2i', [{<<"user">>, User},
|
||||||
|
{<<"luser">>, LUser},
|
||||||
|
{<<"fn">>, FN},
|
||||||
|
{<<"lfn">>, LFN},
|
||||||
|
{<<"family">>, Family},
|
||||||
|
{<<"lfamily">>, LFamily},
|
||||||
|
{<<"given">>, Given},
|
||||||
|
{<<"lgiven">>, LGiven},
|
||||||
|
{<<"middle">>, Middle},
|
||||||
|
{<<"lmiddle">>, LMiddle},
|
||||||
|
{<<"nickname">>, Nickname},
|
||||||
|
{<<"lnickname">>, LNickname},
|
||||||
|
{<<"bday">>, BDay},
|
||||||
|
{<<"lbday">>, LBDay},
|
||||||
|
{<<"ctry">>, CTRY},
|
||||||
|
{<<"lctry">>, LCTRY},
|
||||||
|
{<<"locality">>, Locality},
|
||||||
|
{<<"llocality">>, LLocality},
|
||||||
|
{<<"email">>, EMail},
|
||||||
|
{<<"lemail">>, LEMail},
|
||||||
|
{<<"orgname">>, OrgName},
|
||||||
|
{<<"lorgname">>, LOrgName},
|
||||||
|
{<<"orgunit">>, OrgUnit},
|
||||||
|
{<<"lorgunit">>, LOrgUnit}]}])}.
|
||||||
|
|
||||||
|
search(_LServer, _Data, _AllowReturnAll, _MaxMatch) ->
|
||||||
|
[].
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete(vcard, {LUser, LServer})}.
|
||||||
|
|
||||||
|
import(_LServer, #vcard{us = {LUser, LServer}, vcard = El} = VCard) ->
|
||||||
|
#vcard_search{fn = FN,
|
||||||
|
lfn = LFN,
|
||||||
|
family = Family,
|
||||||
|
lfamily = LFamily,
|
||||||
|
given = Given,
|
||||||
|
lgiven = LGiven,
|
||||||
|
middle = Middle,
|
||||||
|
lmiddle = LMiddle,
|
||||||
|
nickname = Nickname,
|
||||||
|
lnickname = LNickname,
|
||||||
|
bday = BDay,
|
||||||
|
lbday = LBDay,
|
||||||
|
ctry = CTRY,
|
||||||
|
lctry = LCTRY,
|
||||||
|
locality = Locality,
|
||||||
|
llocality = LLocality,
|
||||||
|
email = EMail,
|
||||||
|
lemail = LEMail,
|
||||||
|
orgname = OrgName,
|
||||||
|
lorgname = LOrgName,
|
||||||
|
orgunit = OrgUnit,
|
||||||
|
lorgunit = LOrgUnit} =
|
||||||
|
mod_vcard:make_vcard_search(LUser, LUser, LServer, El),
|
||||||
|
ejabberd_riak:put(VCard, vcard_schema(),
|
||||||
|
[{'2i', [{<<"user">>, LUser},
|
||||||
|
{<<"luser">>, LUser},
|
||||||
|
{<<"fn">>, FN},
|
||||||
|
{<<"lfn">>, LFN},
|
||||||
|
{<<"family">>, Family},
|
||||||
|
{<<"lfamily">>, LFamily},
|
||||||
|
{<<"given">>, Given},
|
||||||
|
{<<"lgiven">>, LGiven},
|
||||||
|
{<<"middle">>, Middle},
|
||||||
|
{<<"lmiddle">>, LMiddle},
|
||||||
|
{<<"nickname">>, Nickname},
|
||||||
|
{<<"lnickname">>, LNickname},
|
||||||
|
{<<"bday">>, BDay},
|
||||||
|
{<<"lbday">>, LBDay},
|
||||||
|
{<<"ctry">>, CTRY},
|
||||||
|
{<<"lctry">>, LCTRY},
|
||||||
|
{<<"locality">>, Locality},
|
||||||
|
{<<"llocality">>, LLocality},
|
||||||
|
{<<"email">>, EMail},
|
||||||
|
{<<"lemail">>, LEMail},
|
||||||
|
{<<"orgname">>, OrgName},
|
||||||
|
{<<"lorgname">>, LOrgName},
|
||||||
|
{<<"orgunit">>, OrgUnit},
|
||||||
|
{<<"lorgunit">>, LOrgUnit}]}]);
|
||||||
|
import(_LServer, #vcard_search{}) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
vcard_schema() ->
|
||||||
|
{record_info(fields, vcard), #vcard{}}.
|
|
@ -0,0 +1,268 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_vcard_sql).
|
||||||
|
|
||||||
|
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||||
|
|
||||||
|
-behaviour(mod_vcard).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, get_vcard/2, set_vcard/4, search/4, remove_user/2,
|
||||||
|
import/1, import/2, export/1]).
|
||||||
|
|
||||||
|
-include("jlib.hrl").
|
||||||
|
-include("mod_vcard.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
-include("ejabberd_sql_pt.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
get_vcard(LUser, LServer) ->
|
||||||
|
case catch odbc_queries:get_vcard(LServer, LUser) of
|
||||||
|
{selected, [{SVCARD}]} ->
|
||||||
|
case fxml_stream:parse_element(SVCARD) of
|
||||||
|
{error, _Reason} -> error;
|
||||||
|
VCARD -> [VCARD]
|
||||||
|
end;
|
||||||
|
{selected, []} -> [];
|
||||||
|
_ -> error
|
||||||
|
end.
|
||||||
|
|
||||||
|
set_vcard(LUser, LServer, VCARD,
|
||||||
|
#vcard_search{user = {User, _},
|
||||||
|
fn = FN,
|
||||||
|
lfn = LFN,
|
||||||
|
family = Family,
|
||||||
|
lfamily = LFamily,
|
||||||
|
given = Given,
|
||||||
|
lgiven = LGiven,
|
||||||
|
middle = Middle,
|
||||||
|
lmiddle = LMiddle,
|
||||||
|
nickname = Nickname,
|
||||||
|
lnickname = LNickname,
|
||||||
|
bday = BDay,
|
||||||
|
lbday = LBDay,
|
||||||
|
ctry = CTRY,
|
||||||
|
lctry = LCTRY,
|
||||||
|
locality = Locality,
|
||||||
|
llocality = LLocality,
|
||||||
|
email = EMail,
|
||||||
|
lemail = LEMail,
|
||||||
|
orgname = OrgName,
|
||||||
|
lorgname = LOrgName,
|
||||||
|
orgunit = OrgUnit,
|
||||||
|
lorgunit = LOrgUnit}) ->
|
||||||
|
SVCARD = fxml:element_to_binary(VCARD),
|
||||||
|
odbc_queries:set_vcard(LServer, LUser, BDay, CTRY,
|
||||||
|
EMail, FN, Family, Given, LBDay,
|
||||||
|
LCTRY, LEMail, LFN, LFamily,
|
||||||
|
LGiven, LLocality, LMiddle,
|
||||||
|
LNickname, LOrgName, LOrgUnit,
|
||||||
|
Locality, Middle, Nickname, OrgName,
|
||||||
|
OrgUnit, SVCARD, User).
|
||||||
|
|
||||||
|
search(LServer, Data, AllowReturnAll, MaxMatch) ->
|
||||||
|
MatchSpec = make_matchspec(LServer, Data),
|
||||||
|
if (MatchSpec == <<"">>) and not AllowReturnAll -> [];
|
||||||
|
true ->
|
||||||
|
Limit = case MaxMatch of
|
||||||
|
infinity ->
|
||||||
|
<<"">>;
|
||||||
|
Val ->
|
||||||
|
[<<" LIMIT ">>, jlib:integer_to_binary(Val)]
|
||||||
|
end,
|
||||||
|
case catch ejabberd_odbc:sql_query(
|
||||||
|
LServer,
|
||||||
|
[<<"select username, fn, family, given, "
|
||||||
|
"middle, nickname, bday, ctry, "
|
||||||
|
"locality, email, orgname, orgunit "
|
||||||
|
"from vcard_search ">>,
|
||||||
|
MatchSpec, Limit, <<";">>]) of
|
||||||
|
{selected,
|
||||||
|
[<<"username">>, <<"fn">>, <<"family">>, <<"given">>,
|
||||||
|
<<"middle">>, <<"nickname">>, <<"bday">>, <<"ctry">>,
|
||||||
|
<<"locality">>, <<"email">>, <<"orgname">>,
|
||||||
|
<<"orgunit">>], Rs} when is_list(Rs) ->
|
||||||
|
Rs;
|
||||||
|
Error ->
|
||||||
|
?ERROR_MSG("~p", [Error]), []
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_user(LUser, LServer) ->
|
||||||
|
ejabberd_odbc:sql_transaction(
|
||||||
|
LServer,
|
||||||
|
fun() ->
|
||||||
|
ejabberd_odbc:sql_query_t(
|
||||||
|
?SQL("delete from vcard where username=%(LUser)s")),
|
||||||
|
ejabberd_odbc:sql_query_t(
|
||||||
|
?SQL("delete from vcard_search where lusername=%(LUser)s"))
|
||||||
|
end).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{vcard,
|
||||||
|
fun(Host, #vcard{us = {LUser, LServer}, vcard = VCARD})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SVCARD =
|
||||||
|
ejabberd_odbc:escape(fxml:element_to_binary(VCARD)),
|
||||||
|
[[<<"delete from vcard where username='">>, Username, <<"';">>],
|
||||||
|
[<<"insert into vcard(username, vcard) values ('">>,
|
||||||
|
Username, <<"', '">>, SVCARD, <<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end},
|
||||||
|
{vcard_search,
|
||||||
|
fun(Host, #vcard_search{user = {User, LServer}, luser = LUser,
|
||||||
|
fn = FN, lfn = LFN, family = Family,
|
||||||
|
lfamily = LFamily, given = Given,
|
||||||
|
lgiven = LGiven, middle = Middle,
|
||||||
|
lmiddle = LMiddle, nickname = Nickname,
|
||||||
|
lnickname = LNickname, bday = BDay,
|
||||||
|
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
|
||||||
|
locality = Locality, llocality = LLocality,
|
||||||
|
email = EMail, lemail = LEMail,
|
||||||
|
orgname = OrgName, lorgname = LOrgName,
|
||||||
|
orgunit = OrgUnit, lorgunit = LOrgUnit})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(User),
|
||||||
|
LUsername = ejabberd_odbc:escape(LUser),
|
||||||
|
SFN = ejabberd_odbc:escape(FN),
|
||||||
|
SLFN = ejabberd_odbc:escape(LFN),
|
||||||
|
SFamily = ejabberd_odbc:escape(Family),
|
||||||
|
SLFamily = ejabberd_odbc:escape(LFamily),
|
||||||
|
SGiven = ejabberd_odbc:escape(Given),
|
||||||
|
SLGiven = ejabberd_odbc:escape(LGiven),
|
||||||
|
SMiddle = ejabberd_odbc:escape(Middle),
|
||||||
|
SLMiddle = ejabberd_odbc:escape(LMiddle),
|
||||||
|
SNickname = ejabberd_odbc:escape(Nickname),
|
||||||
|
SLNickname = ejabberd_odbc:escape(LNickname),
|
||||||
|
SBDay = ejabberd_odbc:escape(BDay),
|
||||||
|
SLBDay = ejabberd_odbc:escape(LBDay),
|
||||||
|
SCTRY = ejabberd_odbc:escape(CTRY),
|
||||||
|
SLCTRY = ejabberd_odbc:escape(LCTRY),
|
||||||
|
SLocality = ejabberd_odbc:escape(Locality),
|
||||||
|
SLLocality = ejabberd_odbc:escape(LLocality),
|
||||||
|
SEMail = ejabberd_odbc:escape(EMail),
|
||||||
|
SLEMail = ejabberd_odbc:escape(LEMail),
|
||||||
|
SOrgName = ejabberd_odbc:escape(OrgName),
|
||||||
|
SLOrgName = ejabberd_odbc:escape(LOrgName),
|
||||||
|
SOrgUnit = ejabberd_odbc:escape(OrgUnit),
|
||||||
|
SLOrgUnit = ejabberd_odbc:escape(LOrgUnit),
|
||||||
|
[[<<"delete from vcard_search where lusername='">>,
|
||||||
|
LUsername, <<"';">>],
|
||||||
|
[<<"insert into vcard_search( username, "
|
||||||
|
"lusername, fn, lfn, family, lfamily, "
|
||||||
|
" given, lgiven, middle, lmiddle, "
|
||||||
|
"nickname, lnickname, bday, lbday, "
|
||||||
|
"ctry, lctry, locality, llocality, "
|
||||||
|
" email, lemail, orgname, lorgname, "
|
||||||
|
"orgunit, lorgunit)values (">>,
|
||||||
|
<<" '">>, Username, <<"', '">>, LUsername,
|
||||||
|
<<"', '">>, SFN, <<"', '">>, SLFN,
|
||||||
|
<<"', '">>, SFamily, <<"', '">>, SLFamily,
|
||||||
|
<<"', '">>, SGiven, <<"', '">>, SLGiven,
|
||||||
|
<<"', '">>, SMiddle, <<"', '">>, SLMiddle,
|
||||||
|
<<"', '">>, SNickname, <<"', '">>, SLNickname,
|
||||||
|
<<"', '">>, SBDay, <<"', '">>, SLBDay,
|
||||||
|
<<"', '">>, SCTRY, <<"', '">>, SLCTRY,
|
||||||
|
<<"', '">>, SLocality, <<"', '">>, SLLocality,
|
||||||
|
<<"', '">>, SEMail, <<"', '">>, SLEMail,
|
||||||
|
<<"', '">>, SOrgName, <<"', '">>, SLOrgName,
|
||||||
|
<<"', '">>, SOrgUnit, <<"', '">>, SLOrgUnit,
|
||||||
|
<<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username, vcard from vcard;">>,
|
||||||
|
fun([LUser, SVCard]) ->
|
||||||
|
#xmlel{} = VCARD = fxml_stream:parse_element(SVCard),
|
||||||
|
#vcard{us = {LUser, LServer}, vcard = VCARD}
|
||||||
|
end},
|
||||||
|
{<<"select username, lusername, fn, lfn, family, lfamily, "
|
||||||
|
"given, lgiven, middle, lmiddle, nickname, lnickname, "
|
||||||
|
"bday, lbday, ctry, lctry, locality, llocality, email, "
|
||||||
|
"lemail, orgname, lorgname, orgunit, lorgunit from vcard_search;">>,
|
||||||
|
fun([User, LUser, FN, LFN,
|
||||||
|
Family, LFamily, Given, LGiven,
|
||||||
|
Middle, LMiddle, Nickname, LNickname,
|
||||||
|
BDay, LBDay, CTRY, LCTRY, Locality, LLocality,
|
||||||
|
EMail, LEMail, OrgName, LOrgName, OrgUnit, LOrgUnit]) ->
|
||||||
|
#vcard_search{us = {LUser, LServer},
|
||||||
|
user = {User, LServer}, luser = LUser,
|
||||||
|
fn = FN, lfn = LFN, family = Family,
|
||||||
|
lfamily = LFamily, given = Given,
|
||||||
|
lgiven = LGiven, middle = Middle,
|
||||||
|
lmiddle = LMiddle, nickname = Nickname,
|
||||||
|
lnickname = LNickname, bday = BDay,
|
||||||
|
lbday = LBDay, ctry = CTRY, lctry = LCTRY,
|
||||||
|
locality = Locality, llocality = LLocality,
|
||||||
|
email = EMail, lemail = LEMail,
|
||||||
|
orgname = OrgName, lorgname = LOrgName,
|
||||||
|
orgunit = OrgUnit, lorgunit = LOrgUnit}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
make_matchspec(LServer, Data) ->
|
||||||
|
filter_fields(Data, <<"">>, LServer).
|
||||||
|
|
||||||
|
filter_fields([], Match, _LServer) ->
|
||||||
|
case Match of
|
||||||
|
<<"">> -> <<"">>;
|
||||||
|
_ -> [<<" where ">>, Match]
|
||||||
|
end;
|
||||||
|
filter_fields([{SVar, [Val]} | Ds], Match, LServer)
|
||||||
|
when is_binary(Val) and (Val /= <<"">>) ->
|
||||||
|
LVal = mod_vcard:string2lower(Val),
|
||||||
|
NewMatch = case SVar of
|
||||||
|
<<"user">> -> make_val(Match, <<"lusername">>, LVal);
|
||||||
|
<<"fn">> -> make_val(Match, <<"lfn">>, LVal);
|
||||||
|
<<"last">> -> make_val(Match, <<"lfamily">>, LVal);
|
||||||
|
<<"first">> -> make_val(Match, <<"lgiven">>, LVal);
|
||||||
|
<<"middle">> -> make_val(Match, <<"lmiddle">>, LVal);
|
||||||
|
<<"nick">> -> make_val(Match, <<"lnickname">>, LVal);
|
||||||
|
<<"bday">> -> make_val(Match, <<"lbday">>, LVal);
|
||||||
|
<<"ctry">> -> make_val(Match, <<"lctry">>, LVal);
|
||||||
|
<<"locality">> ->
|
||||||
|
make_val(Match, <<"llocality">>, LVal);
|
||||||
|
<<"email">> -> make_val(Match, <<"lemail">>, LVal);
|
||||||
|
<<"orgname">> -> make_val(Match, <<"lorgname">>, LVal);
|
||||||
|
<<"orgunit">> -> make_val(Match, <<"lorgunit">>, LVal);
|
||||||
|
_ -> Match
|
||||||
|
end,
|
||||||
|
filter_fields(Ds, NewMatch, LServer);
|
||||||
|
filter_fields([_ | Ds], Match, LServer) ->
|
||||||
|
filter_fields(Ds, Match, LServer).
|
||||||
|
|
||||||
|
make_val(Match, Field, Val) ->
|
||||||
|
Condition = case str:suffix(<<"*">>, Val) of
|
||||||
|
true ->
|
||||||
|
Val1 = str:substr(Val, 1, byte_size(Val) - 1),
|
||||||
|
SVal = <<(ejabberd_odbc:escape_like(Val1))/binary,
|
||||||
|
"%">>,
|
||||||
|
[Field, <<" LIKE '">>, SVal, <<"'">>];
|
||||||
|
_ ->
|
||||||
|
SVal = ejabberd_odbc:escape(Val),
|
||||||
|
[Field, <<" = '">>, SVal, <<"'">>]
|
||||||
|
end,
|
||||||
|
case Match of
|
||||||
|
<<"">> -> Condition;
|
||||||
|
_ -> [Match, <<" and ">>, Condition]
|
||||||
|
end.
|
|
@ -17,26 +17,22 @@
|
||||||
|
|
||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
-include("mod_vcard_xupdate.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()},
|
-callback init(binary(), gen_mod:opts()) -> any().
|
||||||
hash = <<>> :: binary()}).
|
-callback import(binary(), #vcard_xupdate{}) -> ok | pass.
|
||||||
|
-callback add_xupdate(binary(), binary(), binary()) -> {atomic, any()}.
|
||||||
|
-callback get_xupdate(binary(), binary()) -> binary() | undefined.
|
||||||
|
-callback remove_xupdate(binary(), binary()) -> {atomic, any()}.
|
||||||
|
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
%% gen_mod callbacks
|
%% gen_mod callbacks
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
start(Host, Opts) ->
|
start(Host, Opts) ->
|
||||||
case gen_mod:db_type(Host, Opts) of
|
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||||
mnesia ->
|
Mod:init(Host, Opts),
|
||||||
mnesia:create_table(vcard_xupdate,
|
|
||||||
[{disc_copies, [node()]},
|
|
||||||
{attributes,
|
|
||||||
record_info(fields, vcard_xupdate)}]),
|
|
||||||
update_table();
|
|
||||||
_ -> ok
|
|
||||||
end,
|
|
||||||
ejabberd_hooks:add(c2s_update_presence, Host, ?MODULE,
|
ejabberd_hooks:add(c2s_update_presence, Host, ?MODULE,
|
||||||
update_presence, 100),
|
update_presence, 100),
|
||||||
ejabberd_hooks:add(vcard_set, Host, ?MODULE, vcard_set,
|
ejabberd_hooks:add(vcard_set, Host, ?MODULE, vcard_set,
|
||||||
|
@ -79,75 +75,16 @@ vcard_set(LUser, LServer, VCARD) ->
|
||||||
%%====================================================================
|
%%====================================================================
|
||||||
|
|
||||||
add_xupdate(LUser, LServer, Hash) ->
|
add_xupdate(LUser, LServer, Hash) ->
|
||||||
add_xupdate(LUser, LServer, Hash,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:add_xupdate(LUser, LServer, Hash).
|
||||||
|
|
||||||
add_xupdate(LUser, LServer, Hash, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
mnesia:write(#vcard_xupdate{us = {LUser, LServer},
|
|
||||||
hash = Hash})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
add_xupdate(LUser, LServer, Hash, riak) ->
|
|
||||||
{atomic, ejabberd_riak:put(#vcard_xupdate{us = {LUser, LServer},
|
|
||||||
hash = Hash},
|
|
||||||
vcard_xupdate_schema())};
|
|
||||||
add_xupdate(LUser, LServer, Hash, odbc) ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SHash = ejabberd_odbc:escape(Hash),
|
|
||||||
F = fun () ->
|
|
||||||
odbc_queries:update_t(<<"vcard_xupdate">>,
|
|
||||||
[<<"username">>, <<"hash">>],
|
|
||||||
[Username, SHash],
|
|
||||||
[<<"username='">>, Username, <<"'">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
get_xupdate(LUser, LServer) ->
|
get_xupdate(LUser, LServer) ->
|
||||||
get_xupdate(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:get_xupdate(LUser, LServer).
|
||||||
|
|
||||||
get_xupdate(LUser, LServer, mnesia) ->
|
|
||||||
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer})
|
|
||||||
of
|
|
||||||
[#vcard_xupdate{hash = Hash}] -> Hash;
|
|
||||||
_ -> undefined
|
|
||||||
end;
|
|
||||||
get_xupdate(LUser, LServer, riak) ->
|
|
||||||
case ejabberd_riak:get(vcard_xupdate, vcard_xupdate_schema(),
|
|
||||||
{LUser, LServer}) of
|
|
||||||
{ok, #vcard_xupdate{hash = Hash}} -> Hash;
|
|
||||||
_ -> undefined
|
|
||||||
end;
|
|
||||||
get_xupdate(LUser, LServer, odbc) ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
case ejabberd_odbc:sql_query(LServer,
|
|
||||||
[<<"select hash from vcard_xupdate where "
|
|
||||||
"username='">>,
|
|
||||||
Username, <<"';">>])
|
|
||||||
of
|
|
||||||
{selected, [<<"hash">>], [[Hash]]} -> Hash;
|
|
||||||
_ -> undefined
|
|
||||||
end.
|
|
||||||
|
|
||||||
remove_xupdate(LUser, LServer) ->
|
remove_xupdate(LUser, LServer) ->
|
||||||
remove_xupdate(LUser, LServer,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
gen_mod:db_type(LServer, ?MODULE)).
|
Mod:remove_xupdate(LUser, LServer).
|
||||||
|
|
||||||
remove_xupdate(LUser, LServer, mnesia) ->
|
|
||||||
F = fun () ->
|
|
||||||
mnesia:delete({vcard_xupdate, {LUser, LServer}})
|
|
||||||
end,
|
|
||||||
mnesia:transaction(F);
|
|
||||||
remove_xupdate(LUser, LServer, riak) ->
|
|
||||||
{atomic, ejabberd_riak:delete(vcard_xupdate, {LUser, LServer})};
|
|
||||||
remove_xupdate(LUser, LServer, odbc) ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
F = fun () ->
|
|
||||||
ejabberd_odbc:sql_query_t([<<"delete from vcard_xupdate where username='">>,
|
|
||||||
Username, <<"';">>])
|
|
||||||
end,
|
|
||||||
ejabberd_odbc:sql_transaction(LServer, F).
|
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Presence stanza rebuilding
|
%%% Presence stanza rebuilding
|
||||||
|
@ -184,53 +121,17 @@ build_xphotoel(User, Host) ->
|
||||||
attrs = [{<<"xmlns">>, ?NS_VCARD_UPDATE}],
|
attrs = [{<<"xmlns">>, ?NS_VCARD_UPDATE}],
|
||||||
children = PhotoEl}.
|
children = PhotoEl}.
|
||||||
|
|
||||||
vcard_xupdate_schema() ->
|
export(LServer) ->
|
||||||
{record_info(fields, vcard_xupdate), #vcard_xupdate{}}.
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
Mod:export(LServer).
|
||||||
update_table() ->
|
|
||||||
Fields = record_info(fields, vcard_xupdate),
|
|
||||||
case mnesia:table_info(vcard_xupdate, attributes) of
|
|
||||||
Fields ->
|
|
||||||
ejabberd_config:convert_table_to_binary(
|
|
||||||
vcard_xupdate, Fields, set,
|
|
||||||
fun(#vcard_xupdate{us = {U, _}}) -> U end,
|
|
||||||
fun(#vcard_xupdate{us = {U, S}, hash = Hash} = R) ->
|
|
||||||
R#vcard_xupdate{us = {iolist_to_binary(U),
|
|
||||||
iolist_to_binary(S)},
|
|
||||||
hash = iolist_to_binary(Hash)}
|
|
||||||
end);
|
|
||||||
_ ->
|
|
||||||
?INFO_MSG("Recreating vcard_xupdate table", []),
|
|
||||||
mnesia:transform_table(vcard_xupdate, ignore, Fields)
|
|
||||||
end.
|
|
||||||
|
|
||||||
export(_Server) ->
|
|
||||||
[{vcard_xupdate,
|
|
||||||
fun(Host, #vcard_xupdate{us = {LUser, LServer}, hash = Hash})
|
|
||||||
when LServer == Host ->
|
|
||||||
Username = ejabberd_odbc:escape(LUser),
|
|
||||||
SHash = ejabberd_odbc:escape(Hash),
|
|
||||||
[[<<"delete from vcard_xupdate where username='">>,
|
|
||||||
Username, <<"';">>],
|
|
||||||
[<<"insert into vcard_xupdate(username, "
|
|
||||||
"hash) values ('">>,
|
|
||||||
Username, <<"', '">>, SHash, <<"');">>]];
|
|
||||||
(_Host, _R) ->
|
|
||||||
[]
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(LServer) ->
|
import(LServer) ->
|
||||||
[{<<"select username, hash from vcard_xupdate;">>,
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
fun([LUser, Hash]) ->
|
Mod:import(LServer).
|
||||||
#vcard_xupdate{us = {LUser, LServer}, hash = Hash}
|
|
||||||
end}].
|
|
||||||
|
|
||||||
import(_LServer, mnesia, #vcard_xupdate{} = R) ->
|
import(LServer, DBType, LA) ->
|
||||||
mnesia:dirty_write(R);
|
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||||
import(_LServer, riak, #vcard_xupdate{} = R) ->
|
Mod:import(LServer, LA).
|
||||||
ejabberd_riak:put(R, vcard_xupdate_schema());
|
|
||||||
import(_, _, _) ->
|
|
||||||
pass.
|
|
||||||
|
|
||||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||||
mod_opt_type(_) -> [db_type].
|
mod_opt_type(_) -> [db_type].
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_vcard_xupdate_mnesia).
|
||||||
|
-behaviour(mod_vcard_xupdate).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, add_xupdate/3, get_xupdate/2, remove_xupdate/2]).
|
||||||
|
|
||||||
|
-include("mod_vcard_xupdate.hrl").
|
||||||
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
mnesia:create_table(vcard_xupdate,
|
||||||
|
[{disc_copies, [node()]},
|
||||||
|
{attributes,
|
||||||
|
record_info(fields, vcard_xupdate)}]),
|
||||||
|
update_table().
|
||||||
|
|
||||||
|
add_xupdate(LUser, LServer, Hash) ->
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:write(#vcard_xupdate{us = {LUser, LServer},
|
||||||
|
hash = Hash})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
get_xupdate(LUser, LServer) ->
|
||||||
|
case mnesia:dirty_read(vcard_xupdate, {LUser, LServer})
|
||||||
|
of
|
||||||
|
[#vcard_xupdate{hash = Hash}] -> Hash;
|
||||||
|
_ -> undefined
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_xupdate(LUser, LServer) ->
|
||||||
|
F = fun () ->
|
||||||
|
mnesia:delete({vcard_xupdate, {LUser, LServer}})
|
||||||
|
end,
|
||||||
|
mnesia:transaction(F).
|
||||||
|
|
||||||
|
import(_LServer, #vcard_xupdate{} = R) ->
|
||||||
|
mnesia:dirty_write(R).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
update_table() ->
|
||||||
|
Fields = record_info(fields, vcard_xupdate),
|
||||||
|
case mnesia:table_info(vcard_xupdate, attributes) of
|
||||||
|
Fields ->
|
||||||
|
ejabberd_config:convert_table_to_binary(
|
||||||
|
vcard_xupdate, Fields, set,
|
||||||
|
fun(#vcard_xupdate{us = {U, _}}) -> U end,
|
||||||
|
fun(#vcard_xupdate{us = {U, S}, hash = Hash} = R) ->
|
||||||
|
R#vcard_xupdate{us = {iolist_to_binary(U),
|
||||||
|
iolist_to_binary(S)},
|
||||||
|
hash = iolist_to_binary(Hash)}
|
||||||
|
end);
|
||||||
|
_ ->
|
||||||
|
?INFO_MSG("Recreating vcard_xupdate table", []),
|
||||||
|
mnesia:transform_table(vcard_xupdate, ignore, Fields)
|
||||||
|
end.
|
|
@ -0,0 +1,44 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_vcard_xupdate_riak).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, add_xupdate/3, get_xupdate/2, remove_xupdate/2]).
|
||||||
|
|
||||||
|
-include("mod_vcard_xupdate.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
add_xupdate(LUser, LServer, Hash) ->
|
||||||
|
{atomic, ejabberd_riak:put(#vcard_xupdate{us = {LUser, LServer},
|
||||||
|
hash = Hash},
|
||||||
|
vcard_xupdate_schema())}.
|
||||||
|
|
||||||
|
get_xupdate(LUser, LServer) ->
|
||||||
|
case ejabberd_riak:get(vcard_xupdate, vcard_xupdate_schema(),
|
||||||
|
{LUser, LServer}) of
|
||||||
|
{ok, #vcard_xupdate{hash = Hash}} -> Hash;
|
||||||
|
_ -> undefined
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_xupdate(LUser, LServer) ->
|
||||||
|
{atomic, ejabberd_riak:delete(vcard_xupdate, {LUser, LServer})}.
|
||||||
|
|
||||||
|
import(_LServer, #vcard_xupdate{} = R) ->
|
||||||
|
ejabberd_riak:put(R, vcard_xupdate_schema()).
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
||||||
|
vcard_xupdate_schema() ->
|
||||||
|
{record_info(fields, vcard_xupdate), #vcard_xupdate{}}.
|
|
@ -0,0 +1,79 @@
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%% @copyright (C) 2016, Evgeny Khramtsov
|
||||||
|
%%% @doc
|
||||||
|
%%%
|
||||||
|
%%% @end
|
||||||
|
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
|
||||||
|
%%%-------------------------------------------------------------------
|
||||||
|
-module(mod_vcard_xupdate_sql).
|
||||||
|
|
||||||
|
%% API
|
||||||
|
-export([init/2, import/2, add_xupdate/3, get_xupdate/2, remove_xupdate/2,
|
||||||
|
import/1, export/1]).
|
||||||
|
|
||||||
|
-include("mod_vcard_xupdate.hrl").
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% API
|
||||||
|
%%%===================================================================
|
||||||
|
init(_Host, _Opts) ->
|
||||||
|
ok.
|
||||||
|
|
||||||
|
add_xupdate(LUser, LServer, Hash) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SHash = ejabberd_odbc:escape(Hash),
|
||||||
|
F = fun () ->
|
||||||
|
odbc_queries:update_t(<<"vcard_xupdate">>,
|
||||||
|
[<<"username">>, <<"hash">>],
|
||||||
|
[Username, SHash],
|
||||||
|
[<<"username='">>, Username, <<"'">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
get_xupdate(LUser, LServer) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
case ejabberd_odbc:sql_query(LServer,
|
||||||
|
[<<"select hash from vcard_xupdate where "
|
||||||
|
"username='">>,
|
||||||
|
Username, <<"';">>])
|
||||||
|
of
|
||||||
|
{selected, [<<"hash">>], [[Hash]]} -> Hash;
|
||||||
|
_ -> undefined
|
||||||
|
end.
|
||||||
|
|
||||||
|
remove_xupdate(LUser, LServer) ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
F = fun () ->
|
||||||
|
ejabberd_odbc:sql_query_t([<<"delete from vcard_xupdate where username='">>,
|
||||||
|
Username, <<"';">>])
|
||||||
|
end,
|
||||||
|
ejabberd_odbc:sql_transaction(LServer, F).
|
||||||
|
|
||||||
|
export(_Server) ->
|
||||||
|
[{vcard_xupdate,
|
||||||
|
fun(Host, #vcard_xupdate{us = {LUser, LServer}, hash = Hash})
|
||||||
|
when LServer == Host ->
|
||||||
|
Username = ejabberd_odbc:escape(LUser),
|
||||||
|
SHash = ejabberd_odbc:escape(Hash),
|
||||||
|
[[<<"delete from vcard_xupdate where username='">>,
|
||||||
|
Username, <<"';">>],
|
||||||
|
[<<"insert into vcard_xupdate(username, "
|
||||||
|
"hash) values ('">>,
|
||||||
|
Username, <<"', '">>, SHash, <<"');">>]];
|
||||||
|
(_Host, _R) ->
|
||||||
|
[]
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(LServer) ->
|
||||||
|
[{<<"select username, hash from vcard_xupdate;">>,
|
||||||
|
fun([LUser, Hash]) ->
|
||||||
|
#vcard_xupdate{us = {LUser, LServer}, hash = Hash}
|
||||||
|
end}].
|
||||||
|
|
||||||
|
import(_LServer, _) ->
|
||||||
|
pass.
|
||||||
|
|
||||||
|
%%%===================================================================
|
||||||
|
%%% Internal functions
|
||||||
|
%%%===================================================================
|
Loading…
Reference in New Issue