mirror of
https://github.com/processone/ejabberd.git
synced 2025-01-03 18:02:28 +01:00
Merge branch 'move-db-code'
This commit is contained in:
commit
fb0ecf3361
5
include/mod_announce.hrl
Normal file
5
include/mod_announce.hrl
Normal file
@ -0,0 +1,5 @@
|
||||
-record(motd, {server = <<"">> :: binary(),
|
||||
packet = #xmlel{} :: xmlel()}).
|
||||
|
||||
-record(motd_users, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
||||
dummy = [] :: [] | '_'}).
|
4
include/mod_caps.hrl
Normal file
4
include/mod_caps.hrl
Normal file
@ -0,0 +1,4 @@
|
||||
-record(caps_features,
|
||||
{node_pair = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
features = [] :: [binary()] | pos_integer()
|
||||
}).
|
15
include/mod_irc.hrl
Normal file
15
include/mod_irc.hrl
Normal file
@ -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()}).
|
3
include/mod_last.hrl
Normal file
3
include/mod_last.hrl
Normal file
@ -0,0 +1,3 @@
|
||||
-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
timestamp = 0 :: non_neg_integer(),
|
||||
status = <<"">> :: binary()}).
|
4
include/mod_private.hrl
Normal file
4
include/mod_private.hrl
Normal file
@ -0,0 +1,4 @@
|
||||
-record(private_storage,
|
||||
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
|
||||
'$1' | '_'},
|
||||
xml = #xmlel{} :: xmlel() | '_' | '$1'}).
|
5
include/mod_shared_roster.hrl
Normal file
5
include/mod_shared_roster.hrl
Normal file
@ -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()}}).
|
8
include/mod_vcard.hrl
Normal file
8
include/mod_vcard.hrl
Normal file
@ -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()}).
|
2
include/mod_vcard_xupdate.hrl
Normal file
2
include/mod_vcard_xupdate.hrl
Normal file
@ -0,0 +1,2 @@
|
||||
-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
hash = <<>> :: binary()}).
|
@ -36,7 +36,7 @@
|
||||
loaded_modules/1, loaded_modules_with_opts/1,
|
||||
get_hosts/2, get_module_proc/2, is_loaded/2,
|
||||
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]).
|
||||
|
||||
@ -319,6 +319,19 @@ db_type(Host, Opts) when is_list(Opts) ->
|
||||
default_db(Host) ->
|
||||
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()].
|
||||
|
||||
loaded_modules(Host) ->
|
||||
|
@ -41,11 +41,16 @@
|
||||
-include("logger.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("adhoc.hrl").
|
||||
-include("mod_announce.hrl").
|
||||
|
||||
-record(motd, {server = <<"">> :: binary(),
|
||||
packet = #xmlel{} :: xmlel()}).
|
||||
-record(motd_users, {us = {<<"">>, <<"">>} :: {binary(), binary()} | '$1',
|
||||
dummy = [] :: [] | '_'}).
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-callback import(binary(), #motd{} | #motd_users{}) -> ok | pass.
|
||||
-callback set_motd_users(binary(), [{binary(), binary(), binary()}]) -> {atomic, any()}.
|
||||
-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).
|
||||
|
||||
@ -55,20 +60,8 @@
|
||||
tokenize(Node) -> str:tokens(Node, <<"/#">>).
|
||||
|
||||
start(Host, Opts) ->
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
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,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(local_send_to_resource_hook, Host,
|
||||
?MODULE, announce, 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),
|
||||
Sessions = ejabberd_sm:get_vh_session_list(LServer),
|
||||
announce_online1(Sessions, LServer, Packet),
|
||||
case gen_mod:db_type(LServer, ?MODULE) of
|
||||
mnesia ->
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_motd_users(LServer, Sessions).
|
||||
|
||||
announce_motd_update(From, To, Packet) ->
|
||||
Host = To#jid.lserver,
|
||||
@ -853,27 +813,8 @@ announce_all_hosts_motd_update(From, To, Packet) ->
|
||||
|
||||
announce_motd_update(LServer, Packet) ->
|
||||
announce_motd_delete(LServer),
|
||||
case gen_mod:db_type(LServer, ?MODULE) of
|
||||
mnesia ->
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_motd(LServer, Packet).
|
||||
|
||||
announce_motd_delete(From, To, Packet) ->
|
||||
Host = To#jid.lserver,
|
||||
@ -902,112 +843,30 @@ announce_all_hosts_motd_delete(From, To, Packet) ->
|
||||
end.
|
||||
|
||||
announce_motd_delete(LServer) ->
|
||||
case gen_mod:db_type(LServer, ?MODULE) of
|
||||
mnesia ->
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:delete_motd(LServer).
|
||||
|
||||
send_motd(JID) ->
|
||||
send_motd(JID, gen_mod:db_type(JID#jid.lserver, ?MODULE)).
|
||||
|
||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID, mnesia) ->
|
||||
case catch mnesia:dirty_read({motd, LServer}) of
|
||||
[#motd{packet = Packet}] ->
|
||||
US = {LUser, LServer},
|
||||
case catch mnesia:dirty_read({motd_users, US}) of
|
||||
[#motd_users{}] ->
|
||||
ok;
|
||||
_ ->
|
||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID) when LUser /= <<>> ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:get_motd(LServer) of
|
||||
{ok, Packet} ->
|
||||
case Mod:is_motd_user(LUser, LServer) of
|
||||
false ->
|
||||
Local = jid:make(<<>>, LServer, <<>>),
|
||||
ejabberd_router:route(Local, JID, Packet),
|
||||
F = fun() ->
|
||||
mnesia:write(#motd_users{us = US})
|
||||
end,
|
||||
mnesia:transaction(F)
|
||||
end;
|
||||
_ ->
|
||||
Mod:set_motd_user(LUser, LServer);
|
||||
true ->
|
||||
ok
|
||||
end;
|
||||
send_motd(#jid{luser = LUser, lserver = LServer} = JID, riak) ->
|
||||
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;
|
||||
_ ->
|
||||
error ->
|
||||
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) ->
|
||||
send_motd(_) ->
|
||||
ok.
|
||||
|
||||
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} ->
|
||||
{fxml:get_subtag_cdata(Packet, <<"subject">>),
|
||||
fxml:get_subtag_cdata(Packet, <<"body">>)};
|
||||
@ -1015,34 +874,6 @@ get_stored_motd(LServer) ->
|
||||
{<<>>, <<>>}
|
||||
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
|
||||
send_announcement_to_all(Host, SubjectS, BodyS) ->
|
||||
SubjectEls = if SubjectS /= <<>> ->
|
||||
@ -1076,96 +907,17 @@ get_access(Host) ->
|
||||
none).
|
||||
|
||||
%%-------------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
|
||||
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}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #motd{} = Motd) ->
|
||||
mnesia:dirty_write(Motd);
|
||||
import(_LServer, mnesia, #motd_users{} = Users) ->
|
||||
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.
|
||||
import(LServer, DBType, LA) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, LA).
|
||||
|
||||
mod_opt_type(access) ->
|
||||
fun (A) when is_atom(A) -> A end;
|
||||
|
129
src/mod_announce_mnesia.erl
Normal file
129
src/mod_announce_mnesia.erl
Normal file
@ -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.
|
87
src/mod_announce_riak.erl
Normal file
87
src/mod_announce_riak.erl
Normal file
@ -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{}}.
|
132
src/mod_announce_sql.erl
Normal file
132
src/mod_announce_sql.erl
Normal file
@ -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").
|
||||
|
||||
-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) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
@ -147,9 +151,8 @@ process_blocklist_block(LUser, LServer, JIDs, Lang) ->
|
||||
end,
|
||||
List, JIDs)
|
||||
end,
|
||||
case process_blocklist_block_db(LUser, LServer, Filter,
|
||||
gen_mod:db_type(LServer, mod_privacy))
|
||||
of
|
||||
Mod = db_mod(LServer),
|
||||
case Mod:process_blocklist_block(LUser, LServer, Filter) of
|
||||
{atomic, {ok, Default, List}} ->
|
||||
UserList = make_userlist(Default, List),
|
||||
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">>)}
|
||||
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) ->
|
||||
Filter = fun (List) ->
|
||||
lists:filter(fun (#listitem{action = A}) -> A =/= deny
|
||||
end,
|
||||
List)
|
||||
end,
|
||||
DBType = gen_mod:db_type(LServer, mod_privacy),
|
||||
case unblock_by_filter(LUser, LServer, Filter, DBType) of
|
||||
Mod = db_mod(LServer),
|
||||
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
||||
{atomic, ok} -> {result, []};
|
||||
{atomic, {ok, Default, List}} ->
|
||||
UserList = make_userlist(Default, List),
|
||||
@ -279,8 +194,8 @@ process_blocklist_unblock(LUser, LServer, JIDs, Lang) ->
|
||||
end,
|
||||
List)
|
||||
end,
|
||||
DBType = gen_mod:db_type(LServer, mod_privacy),
|
||||
case unblock_by_filter(LUser, LServer, Filter, DBType) of
|
||||
Mod = db_mod(LServer),
|
||||
case Mod:unblock_by_filter(LUser, LServer, Filter) of
|
||||
{atomic, ok} -> {result, []};
|
||||
{atomic, {ok, 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">>)}
|
||||
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) ->
|
||||
NeedDb = mod_privacy:is_list_needdb(List),
|
||||
#userlist{name = Name, list = List, needdb = NeedDb}.
|
||||
@ -380,9 +225,8 @@ broadcast_blocklist_event(LUser, LServer, Event) ->
|
||||
{broadcast, {blocking, Event}}).
|
||||
|
||||
process_blocklist_get(LUser, LServer, Lang) ->
|
||||
case process_blocklist_get_db(LUser, LServer,
|
||||
gen_mod:db_type(LServer, mod_privacy))
|
||||
of
|
||||
Mod = db_mod(LServer),
|
||||
case Mod:process_blocklist_get(LUser, LServer) of
|
||||
error ->
|
||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||
List ->
|
||||
@ -402,45 +246,9 @@ process_blocklist_get(LUser, LServer, Lang) ->
|
||||
children = Items}]}
|
||||
end.
|
||||
|
||||
process_blocklist_get_db(LUser, LServer, mnesia) ->
|
||||
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;
|
||||
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.
|
||||
db_mod(LServer) ->
|
||||
DBType = gen_mod:db_type(LServer, mod_privacy),
|
||||
gen_mod:db_mod(DBType, ?MODULE).
|
||||
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
mod_opt_type(_) -> [iqdisc].
|
||||
|
85
src/mod_blocking_mnesia.erl
Normal file
85
src/mod_blocking_mnesia.erl
Normal file
@ -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
|
||||
%%%===================================================================
|
98
src/mod_blocking_riak.erl
Normal file
98
src/mod_blocking_riak.erl
Normal file
@ -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
|
||||
%%%===================================================================
|
87
src/mod_blocking_sql.erl
Normal file
87
src/mod_blocking_sql.erl
Normal file
@ -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()}).
|
||||
|
||||
-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) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
|
||||
gen_server:start_link({local, Proc}, ?MODULE,
|
||||
@ -300,28 +306,9 @@ c2s_broadcast_recipients(InAcc, Host, C2SState,
|
||||
end;
|
||||
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_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,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
1000),
|
||||
@ -450,65 +437,13 @@ feature_response(_IQResult, Host, From, Caps,
|
||||
|
||||
caps_read_fun(Host, Node) ->
|
||||
LServer = jid:nameprep(Host),
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
caps_read_fun(LServer, Node, DBType).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
fun() -> Mod:caps_read(LServer, Node) end.
|
||||
|
||||
caps_write_fun(Host, Node, Features) ->
|
||||
LServer = jid:nameprep(Host),
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
caps_write_fun(LServer, Node, Features, DBType).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
fun() -> Mod:caps_write(LServer, Node, Features) end.
|
||||
|
||||
make_my_disco_hash(Host) ->
|
||||
JID = jid:make(<<"">>, Host, <<"">>),
|
||||
@ -658,61 +593,20 @@ is_valid_node(Node) ->
|
||||
false
|
||||
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() ->
|
||||
{record_info(fields, caps_features), #caps_features{}}.
|
||||
|
||||
export(_Server) ->
|
||||
[{caps_features,
|
||||
fun(_Host, #caps_features{node_pair = NodePair,
|
||||
features = Features}) ->
|
||||
sql_write_features_t(NodePair, Features);
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
import_info() ->
|
||||
[{<<"caps_features">>, 4}].
|
||||
|
||||
import_start(LServer, DBType) ->
|
||||
ets:new(caps_features_tmp, [private, named_table, bag]),
|
||||
init_db(DBType, LServer),
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:init(LServer, []),
|
||||
ok.
|
||||
|
||||
import(_LServer, {odbc, _}, _DBType, <<"caps_features">>,
|
||||
|
73
src/mod_caps_mnesia.erl
Normal file
73
src/mod_caps_mnesia.erl
Normal file
@ -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.
|
38
src/mod_caps_riak.erl
Normal file
38
src/mod_caps_riak.erl
Normal file
@ -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{}}.
|
71
src/mod_caps_sql.erl
Normal file
71
src/mod_caps_sql.erl
Normal file
@ -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
|
||||
-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,
|
||||
handle_info/2, terminate/2, code_change/3,
|
||||
@ -46,6 +47,8 @@
|
||||
|
||||
-include("adhoc.hrl").
|
||||
|
||||
-include("mod_irc.hrl").
|
||||
|
||||
-define(DEFAULT_IRC_ENCODING, <<"iso8859-15">>).
|
||||
|
||||
-define(DEFAULT_IRC_PORT, 6667).
|
||||
@ -58,27 +61,19 @@
|
||||
[<<"koi8-r">>, <<"iso8859-15">>, <<"iso8859-1">>, <<"iso8859-2">>,
|
||||
<<"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(),
|
||||
server_host = <<"">> :: binary(),
|
||||
access = all :: atom()}).
|
||||
|
||||
-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
|
||||
%%====================================================================
|
||||
@ -119,14 +114,8 @@ init([Host, Opts]) ->
|
||||
ejabberd:start_app(iconv),
|
||||
MyHost = gen_mod:get_opt_host(Host, Opts,
|
||||
<<"irc.@HOST@">>),
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
mnesia:create_table(irc_custom,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, irc_custom)}]),
|
||||
update_table();
|
||||
_ -> ok
|
||||
end,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
Access = gen_mod:get_opt(access, Opts,
|
||||
fun(A) when is_atom(A) -> A end,
|
||||
all),
|
||||
@ -597,43 +586,8 @@ process_irc_register(ServerHost, Host, From, _To,
|
||||
|
||||
get_data(ServerHost, Host, From) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
get_data(LServer, Host, From,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_data(LServer, Host, From).
|
||||
|
||||
get_form(ServerHost, Host, From, [], Lang) ->
|
||||
#jid{user = User, server = Server} = From,
|
||||
@ -743,37 +697,8 @@ get_form(_ServerHost, _Host, _, _, _Lang) ->
|
||||
|
||||
set_data(ServerHost, Host, From, Data) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
set_data(LServer, Host, From, data_to_binary(From, Data),
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_data(LServer, Host, From, data_to_binary(From, Data)).
|
||||
|
||||
set_form(ServerHost, Host, From, [], Lang, XData) ->
|
||||
case {lists:keysearch(<<"username">>, 1, XData),
|
||||
@ -1314,66 +1239,17 @@ conn_params_to_list(Params) ->
|
||||
Port, binary_to_list(P)}
|
||||
end, Params).
|
||||
|
||||
irc_custom_schema() ->
|
||||
{record_info(fields, irc_custom), #irc_custom{}}.
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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 = data_to_binary(JID, Data)}
|
||||
end);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating irc_custom table", []),
|
||||
mnesia:transform_table(irc_custom, ignore, Fields)
|
||||
end.
|
||||
import(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
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(_LServer, mnesia, #irc_custom{} = R) ->
|
||||
mnesia:dirty_write(R);
|
||||
import(_LServer, riak, #irc_custom{} = R) ->
|
||||
ejabberd_riak:put(R, irc_custom_schema());
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, DBType, Data) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, Data).
|
||||
|
||||
mod_opt_type(access) ->
|
||||
fun (A) when is_atom(A) -> A end;
|
||||
|
69
src/mod_irc_mnesia.erl
Normal file
69
src/mod_irc_mnesia.erl
Normal file
@ -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.
|
49
src/mod_irc_riak.erl
Normal file
49
src/mod_irc_riak.erl
Normal file
@ -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{}}.
|
91
src/mod_irc_sql.erl
Normal file
91
src/mod_irc_sql.erl
Normal file
@ -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("mod_privacy.hrl").
|
||||
-include("mod_last.hrl").
|
||||
|
||||
-record(last_activity, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
timestamp = 0 :: non_neg_integer(),
|
||||
status = <<"">> :: binary()}).
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-callback import(binary(), #last_activity{}) -> ok | pass.
|
||||
-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) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
mnesia:create_table(last_activity,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes,
|
||||
record_info(fields, last_activity)}]),
|
||||
update_table();
|
||||
_ -> ok
|
||||
end,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_LAST, ?MODULE, process_local_iq, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
@ -163,38 +161,8 @@ process_sm_iq(From, To,
|
||||
%% @spec (LUser::string(), LServer::string()) ->
|
||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found | {error, Reason}
|
||||
get_last(LUser, LServer) ->
|
||||
get_last(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_last(LUser, LServer).
|
||||
|
||||
get_last_iq(#iq{lang = Lang} = IQ, SubEl, LUser, LServer) ->
|
||||
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) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:store_last_info(LUser, LServer, TimeStamp, Status).
|
||||
|
||||
%% @spec (LUser::string(), LServer::string()) ->
|
||||
%% {ok, TimeStamp::integer(), Status::string()} | not_found
|
||||
@ -272,71 +219,20 @@ get_last_info(LUser, LServer) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
remove_user(LUser, LServer, DBType).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer).
|
||||
|
||||
remove_user(LUser, LServer, mnesia) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun () -> mnesia:delete({last_activity, US}) end,
|
||||
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}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #last_activity{} = LA) ->
|
||||
mnesia:dirty_write(LA);
|
||||
import(_LServer, riak, #last_activity{} = LA) ->
|
||||
ejabberd_riak:put(LA, last_activity_schema());
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, DBType, LA) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, LA).
|
||||
|
||||
transform_options(Opts) ->
|
||||
lists:foldl(fun transform_options/2, [], Opts).
|
||||
|
72
src/mod_last_mnesia.erl
Normal file
72
src/mod_last_mnesia.erl
Normal file
@ -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.
|
53
src/mod_last_riak.erl
Normal file
53
src/mod_last_riak.erl
Normal file
@ -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{}}.
|
75
src/mod_last_sql.erl
Normal file
75
src/mod_last_sql.erl
Normal file
@ -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
|
||||
%%%===================================================================
|
539
src/mod_mam.erl
539
src/mod_mam.erl
@ -35,41 +35,37 @@
|
||||
|
||||
-export([user_send_packet/4, user_receive_packet/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,
|
||||
get_commands_spec/0]).
|
||||
get_commands_spec/0, msg_to_el/4]).
|
||||
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("jlib.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("mod_muc_room.hrl").
|
||||
-include("ejabberd_commands.hrl").
|
||||
-include("mod_mam.hrl").
|
||||
|
||||
-define(DEF_PAGE_SIZE, 50).
|
||||
-define(MAX_PAGE_SIZE, 250).
|
||||
|
||||
-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))).
|
||||
|
||||
-record(archive_msg,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()} | '$2',
|
||||
id = <<>> :: binary() | '_',
|
||||
timestamp = p1_time_compat:timestamp() :: erlang:timestamp() | '_' | '$1',
|
||||
peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3' | undefined,
|
||||
bare_peer = {<<"">>, <<"">>, <<"">>} :: ljid() | '_' | '$3',
|
||||
packet = #xmlel{} :: xmlel() | '_',
|
||||
nick = <<"">> :: binary(),
|
||||
type = chat :: chat | groupchat}).
|
||||
|
||||
-record(archive_prefs,
|
||||
{us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
default = never :: never | always | roster,
|
||||
always = [] :: [ljid()],
|
||||
never = [] :: [ljid()]}).
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-callback remove_user(binary(), binary()) -> any().
|
||||
-callback remove_room(binary(), binary(), binary()) -> any().
|
||||
-callback delete_old_messages(binary() | global,
|
||||
erlang:timestamp(),
|
||||
all | chat | groupchat) -> any().
|
||||
-callback extended_fields() -> [xmlel()].
|
||||
-callback store(xmlel(), binary(), {binary(), binary()}, chat | groupchat,
|
||||
jid(), binary(), recv | send) -> {ok, binary()} | any().
|
||||
-callback write_prefs(binary(), binary(), #archive_prefs{}, binary()) -> ok | any().
|
||||
-callback get_prefs(binary(), binary()) -> {ok, #archive_prefs{}} | error.
|
||||
-callback select(binary(), jid(), jid(),
|
||||
none | erlang:timestamp(),
|
||||
none | erlang:timestamp(),
|
||||
none | ljid() | {text, binary()},
|
||||
none | #rsm_in{},
|
||||
chat | groupchat) ->
|
||||
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
|
||||
|
||||
%%%===================================================================
|
||||
%%% API
|
||||
@ -77,9 +73,9 @@
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
DBType = gen_mod:db_type(Host, Opts),
|
||||
init_db(DBType, Host),
|
||||
init_cache(DBType, Opts),
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
init_cache(Opts),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_local, Host,
|
||||
?NS_MAM_TMP, ?MODULE, process_iq_v0_2, IQDisc),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
@ -120,18 +116,7 @@ start(Host, Opts) ->
|
||||
ejabberd_commands:register_commands(get_commands_spec()),
|
||||
ok.
|
||||
|
||||
init_db(mnesia, _Host) ->
|
||||
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) ->
|
||||
init_cache(Opts) ->
|
||||
MaxSize = gen_mod:get_opt(cache_size, Opts,
|
||||
fun(I) when is_integer(I), I>0 -> I end,
|
||||
1000),
|
||||
@ -179,24 +164,14 @@ stop(Host) ->
|
||||
remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
remove_user(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer).
|
||||
|
||||
remove_user(LUser, LServer, mnesia) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun () ->
|
||||
mnesia:delete({archive_msg, US}),
|
||||
mnesia:delete({archive_prefs, US})
|
||||
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, <<"';">>]).
|
||||
remove_room(LServer, Name, Host) ->
|
||||
LName = jid:nodeprep(Name),
|
||||
LHost = jid:nameprep(Host),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_room(LServer, LName, LHost).
|
||||
|
||||
user_receive_packet(Pkt, C2SState, JID, Peer, To) ->
|
||||
LUser = JID#jid.luser,
|
||||
@ -343,10 +318,10 @@ message_is_archived(false, C2SState, Peer,
|
||||
if_enabled ->
|
||||
get_prefs(LUser, LServer);
|
||||
on_request ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
cache_tab:lookup(archive_prefs, {LUser, LServer},
|
||||
fun() ->
|
||||
get_prefs(LUser, LServer, DBType)
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end);
|
||||
never ->
|
||||
error
|
||||
@ -365,21 +340,19 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
|
||||
Diff = Days * 24 * 60 * 60 * 1000000,
|
||||
TimeStamp = usec_to_now(p1_time_compat:system_time(micro_seconds) - Diff),
|
||||
Type = jlib:binary_to_atom(TypeBin),
|
||||
{Results, _} =
|
||||
lists:foldl(fun(Host, {Results, MnesiaDone}) ->
|
||||
case {gen_mod:db_type(Host, ?MODULE), MnesiaDone} of
|
||||
{mnesia, true} ->
|
||||
{Results, true};
|
||||
{mnesia, false} ->
|
||||
Res = delete_old_messages(TimeStamp, Type,
|
||||
global, mnesia),
|
||||
{[Res|Results], true};
|
||||
{DBType, _} ->
|
||||
Res = delete_old_messages(TimeStamp, Type,
|
||||
Host, DBType),
|
||||
{[Res|Results], MnesiaDone}
|
||||
DBTypes = lists:usort(
|
||||
lists:map(
|
||||
fun(Host) ->
|
||||
case gen_mod:db_type(Host, ?MODULE) of
|
||||
odbc -> {odbc, Host};
|
||||
Other -> {Other, global}
|
||||
end
|
||||
end, {[], false}, ?MYHOSTS),
|
||||
end, ?MYHOSTS)),
|
||||
Results = lists:map(
|
||||
fun({DBType, ServerHost}) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:delete_old_messages(ServerHost, TimeStamp, Type)
|
||||
end, DBTypes),
|
||||
case lists:filter(fun(Res) -> Res /= ok end, Results) of
|
||||
[] -> ok;
|
||||
[NotOk|_] -> NotOk
|
||||
@ -387,21 +360,6 @@ delete_old_messages(TypeBin, Days) when TypeBin == <<"chat">>;
|
||||
delete_old_messages(_TypeBin, _Days) ->
|
||||
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
|
||||
%%%===================================================================
|
||||
@ -427,15 +385,9 @@ process_iq(LServer, #iq{sub_el = #xmlel{attrs = Attrs}} = IQ) ->
|
||||
#xmlel{name = <<"field">>,
|
||||
attrs = [{<<"type">>, <<"text-single">>},
|
||||
{<<"var">>, <<"end">>}]}],
|
||||
Fields = case gen_mod:db_type(LServer, ?MODULE) of
|
||||
odbc ->
|
||||
WithText = #xmlel{name = <<"field">>,
|
||||
attrs = [{<<"type">>, <<"text-single">>},
|
||||
{<<"var">>, <<"withtext">>}]},
|
||||
[WithText|CommonFields];
|
||||
_ ->
|
||||
CommonFields
|
||||
end,
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
ExtendedFields = Mod:extended_fields(),
|
||||
Fields = ExtendedFields ++ CommonFields,
|
||||
Form = #xmlel{name = <<"x">>,
|
||||
attrs = [{<<"xmlns">>, ?NS_XDATA}, {<<"type">>, <<"form">>}],
|
||||
children = Fields},
|
||||
@ -715,8 +667,8 @@ store_msg(C2SState, Pkt, LUser, LServer, Peer, Dir) ->
|
||||
case should_archive_peer(C2SState, Prefs, Peer) of
|
||||
true ->
|
||||
US = {LUser, LServer},
|
||||
store(Pkt, LServer, US, chat, Peer, <<"">>, Dir,
|
||||
gen_mod:db_type(LServer, ?MODULE));
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:store(Pkt, LServer, US, chat, Peer, <<"">>, Dir);
|
||||
false ->
|
||||
pass
|
||||
end.
|
||||
@ -726,101 +678,26 @@ store_muc(MUCState, Pkt, RoomJID, Peer, Nick) ->
|
||||
true ->
|
||||
LServer = MUCState#state.server_host,
|
||||
{U, S, _} = jid:tolower(RoomJID),
|
||||
store(Pkt, LServer, {U, S}, groupchat, Peer, Nick, recv,
|
||||
gen_mod:db_type(LServer, ?MODULE));
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:store(Pkt, LServer, {U, S}, groupchat, Peer, Nick, recv);
|
||||
false ->
|
||||
pass
|
||||
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) ->
|
||||
DBType = case gen_mod:db_type(Host, ?MODULE) of
|
||||
odbc -> {odbc, Host};
|
||||
DB -> DB
|
||||
end,
|
||||
Prefs = #archive_prefs{us = {LUser, LServer},
|
||||
default = Default,
|
||||
always = Always,
|
||||
never = Never},
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
cache_tab:dirty_insert(
|
||||
archive_prefs, {LUser, LServer}, Prefs,
|
||||
fun() -> write_prefs(LUser, LServer, Prefs, DBType) 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.
|
||||
fun() -> Mod:write_prefs(LUser, LServer, Prefs, Host) end).
|
||||
|
||||
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},
|
||||
fun() -> get_prefs(LUser, LServer,
|
||||
DBType)
|
||||
end),
|
||||
fun() -> Mod:get_prefs(LUser, LServer) end),
|
||||
case Res of
|
||||
{ok, Prefs} ->
|
||||
Prefs;
|
||||
@ -842,31 +719,6 @@ get_prefs(LUser, LServer) ->
|
||||
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) ->
|
||||
Default1 = jlib:atom_to_binary(Default),
|
||||
JFun = fun(L) ->
|
||||
@ -890,11 +742,10 @@ maybe_activate_mam(LUser, LServer) ->
|
||||
false),
|
||||
case ActivateOpt of
|
||||
true ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Res = cache_tab:lookup(archive_prefs, {LUser, LServer},
|
||||
fun() ->
|
||||
get_prefs(LUser, LServer,
|
||||
gen_mod:db_type(LServer,
|
||||
?MODULE))
|
||||
Mod:get_prefs(LUser, LServer)
|
||||
end),
|
||||
case Res of
|
||||
{ok, _Prefs} ->
|
||||
@ -912,31 +763,22 @@ maybe_activate_mam(LUser, LServer) ->
|
||||
end.
|
||||
|
||||
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,
|
||||
With, RSM, MsgType, DBType),
|
||||
With, RSM, MsgType),
|
||||
SortedMsgs = lists:keysort(2, Msgs),
|
||||
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
|
||||
chat ->
|
||||
select(LServer, From, From, Start, End, With, RSM, MsgType, DBType);
|
||||
select(LServer, From, From, Start, End, With, RSM, MsgType);
|
||||
{groupchat, _Role, _MUCState} ->
|
||||
select(LServer, From, To, Start, End, With, RSM, MsgType, DBType)
|
||||
select(LServer, From, To, Start, End, With, RSM, MsgType)
|
||||
end.
|
||||
|
||||
select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
||||
{groupchat, _Role, #state{config = #config{mam = false},
|
||||
history = History}} = MsgType,
|
||||
_DBType) ->
|
||||
history = History}} = MsgType) ->
|
||||
#lqueue{len = L, queue = Q} = History,
|
||||
{Msgs0, _} =
|
||||
lists:mapfoldl(
|
||||
@ -970,81 +812,9 @@ select(_LServer, JidRequestor, JidArchive, Start, End, _With, RSM,
|
||||
_ ->
|
||||
{Msgs, true, L}
|
||||
end;
|
||||
select(_LServer, JidRequestor,
|
||||
#jid{luser = LUser, lserver = LServer} = JidArchive,
|
||||
Start, End, With, RSM, MsgType, mnesia) ->
|
||||
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.
|
||||
select(LServer, From, From, Start, End, With, RSM, MsgType) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:select(LServer, From, From, Start, End, With, RSM, MsgType).
|
||||
|
||||
msg_to_el(#archive_msg{timestamp = TS, packet = Pkt1, nick = Nick, peer = Peer},
|
||||
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
||||
@ -1160,7 +930,6 @@ send(From, To, Msgs, RSM, Count, IsComplete, #iq{sub_el = SubEl} = IQ) ->
|
||||
ignore
|
||||
end.
|
||||
|
||||
|
||||
make_rsm_out([], _, Count, Attrs, NS) ->
|
||||
Tag = if NS == ?NS_MAM_TMP -> <<"query">>;
|
||||
true -> <<"fin">>
|
||||
@ -1177,32 +946,6 @@ make_rsm_out([{FirstID, _, _}|_] = Msgs, _, Count, Attrs, NS) ->
|
||||
#rsm_out{first = FirstID, count = Count,
|
||||
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) ->
|
||||
{Msgs, true};
|
||||
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, _) ->
|
||||
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}) ->
|
||||
(MSec*1000000 + Sec)*1000000 + USec.
|
||||
|
||||
@ -1376,28 +999,6 @@ get_jids(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() ->
|
||||
[#ejabberd_commands{name = delete_old_mam_messages, tags = [purge],
|
||||
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;
|
||||
mod_opt_type(cache_size) ->
|
||||
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) ->
|
||||
fun (always) -> always;
|
||||
(never) -> never;
|
||||
|
178
src/mod_mam_mnesia.erl
Normal file
178
src/mod_mam_mnesia.erl
Normal file
@ -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}.
|
309
src/mod_mam_sql.erl
Normal file
309
src/mod_mam_sql.erl
Normal file
@ -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,
|
||||
import/1,
|
||||
import/3,
|
||||
opts_to_binary/1,
|
||||
can_use_nick/4]).
|
||||
|
||||
-export([init/1, handle_call/3, handle_cast/2,
|
||||
@ -72,6 +73,17 @@
|
||||
|
||||
-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
|
||||
%%====================================================================
|
||||
@ -125,96 +137,24 @@ create_room(Host, Name, From, Nick, Opts) ->
|
||||
|
||||
store_room(ServerHost, Host, Name, Opts) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
store_room(LServer, Host, Name, Opts,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:store_room(LServer, Host, Name, Opts).
|
||||
|
||||
restore_room(ServerHost, Host, Name) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
restore_room(LServer, Host, Name,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:restore_room(LServer, Host, Name).
|
||||
|
||||
forget_room(ServerHost, Host, Name) ->
|
||||
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),
|
||||
F = fun () -> mnesia:delete({muc_room, {Name, Host}})
|
||||
end,
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:forget_room(LServer, Host, Name).
|
||||
|
||||
remove_room_mam(LServer, Host, Name) ->
|
||||
case gen_mod:is_loaded(LServer, mod_mam) of
|
||||
true ->
|
||||
U = jid:nodeprep(Name),
|
||||
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;
|
||||
mod_mam:remove_room(LServer, Name, Host);
|
||||
false ->
|
||||
ok
|
||||
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, Nick) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
can_use_nick(LServer, Host, JID, Nick,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:can_use_nick(LServer, Host, JID, Nick).
|
||||
|
||||
%%====================================================================
|
||||
%% gen_server callbacks
|
||||
@ -283,21 +183,8 @@ can_use_nick(LServer, Host, JID, Nick, odbc) ->
|
||||
init([Host, Opts]) ->
|
||||
MyHost = gen_mod:get_opt_host(Host, Opts,
|
||||
<<"conference.@HOST@">>),
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
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,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, [{host, MyHost}|Opts]),
|
||||
mnesia:create_table(muc_online_room,
|
||||
[{ram_copies, [node()]},
|
||||
{attributes, record_info(fields, muc_online_room)}]),
|
||||
@ -647,43 +534,8 @@ check_create_roomid(ServerHost, RoomID) ->
|
||||
|
||||
get_rooms(ServerHost, Host) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
get_rooms(LServer, Host,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_rooms(LServer, Host).
|
||||
|
||||
load_permanent_rooms(Host, ServerHost, Access,
|
||||
HistorySize, RoomShaper) ->
|
||||
@ -873,41 +725,8 @@ iq_get_unique(From) ->
|
||||
|
||||
get_nick(ServerHost, Host, From) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
get_nick(LServer, Host, From,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_nick(LServer, Host, From).
|
||||
|
||||
iq_get_register_info(ServerHost, Host, From, Lang) ->
|
||||
{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) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
set_nick(LServer, Host, From, Nick,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_nick(LServer, Host, From, Nick).
|
||||
|
||||
iq_set_register_info(ServerHost, Host, From, Nick,
|
||||
Lang) ->
|
||||
@ -1192,118 +912,17 @@ opts_to_binary(Opts) ->
|
||||
Opt
|
||||
end, Opts).
|
||||
|
||||
update_tables(Host) ->
|
||||
update_muc_room_table(Host),
|
||||
update_muc_registered_table(Host).
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
muc_room_schema() ->
|
||||
{record_info(fields, muc_room), #muc_room{}}.
|
||||
import(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
muc_registered_schema() ->
|
||||
{record_info(fields, muc_registered), #muc_registered{}}.
|
||||
|
||||
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.
|
||||
import(LServer, DBType, Data) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, Data).
|
||||
|
||||
mod_opt_type(access) ->
|
||||
fun (A) when is_atom(A) -> A end;
|
||||
|
163
src/mod_muc_mnesia.erl
Normal file
163
src/mod_muc_mnesia.erl
Normal file
@ -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.
|
117
src/mod_muc_riak.erl
Normal file
117
src/mod_muc_riak.erl
Normal file
@ -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{}}.
|
202
src/mod_muc_sql.erl
Normal file
202
src/mod_muc_sql.erl
Normal file
@ -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
232
src/mod_offline_mnesia.erl
Normal file
232
src/mod_offline_mnesia.erl
Normal file
@ -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.
|
153
src/mod_offline_riak.erl
Normal file
153
src/mod_offline_riak.erl
Normal file
@ -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}.
|
252
src/mod_offline_sql.erl
Normal file
252
src/mod_offline_sql.erl
Normal file
@ -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,
|
||||
process_iq_set/4, process_iq_get/5, get_user_list/3,
|
||||
check_packet/6, remove_user/2, item_to_raw/1,
|
||||
raw_to_item/1, is_list_needdb/1, updated_list/3,
|
||||
check_packet/6, remove_user/2,
|
||||
is_list_needdb/1, updated_list/3,
|
||||
item_to_xml/1, get_user_lists/2, import/3,
|
||||
set_privacy_list/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]).
|
||||
set_privacy_list/1, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
@ -54,20 +45,24 @@
|
||||
|
||||
-include("mod_privacy.hrl").
|
||||
|
||||
privacy_schema() ->
|
||||
{record_info(fields, privacy), #privacy{}}.
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-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) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
mnesia:create_table(privacy,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, privacy)}]),
|
||||
update_table();
|
||||
_ -> ok
|
||||
end,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
mod_disco:register_feature(Host, ?NS_PRIVACY),
|
||||
ejabberd_hooks:add(privacy_iq_get, Host, ?MODULE,
|
||||
process_iq_get, 50),
|
||||
@ -124,9 +119,8 @@ process_iq_get(_, From, _To, #iq{lang = Lang, sub_el = SubEl},
|
||||
end.
|
||||
|
||||
process_lists_get(LUser, LServer, Active, Lang) ->
|
||||
case process_lists_get_db(LUser, LServer, Active,
|
||||
gen_mod:db_type(LServer, ?MODULE))
|
||||
of
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:process_lists_get(LUser, LServer) of
|
||||
error -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||
{_Default, []} ->
|
||||
{result,
|
||||
@ -153,57 +147,9 @@ process_lists_get(LUser, LServer, Active, Lang) ->
|
||||
children = ADItems}]}
|
||||
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) ->
|
||||
case process_list_get_db(LUser, LServer, Name,
|
||||
gen_mod:db_type(LServer, ?MODULE))
|
||||
of
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:process_list_get(LUser, LServer, Name) of
|
||||
error -> {error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||
not_found -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||
Items ->
|
||||
@ -218,41 +164,6 @@ process_list_get(LUser, LServer, {value, Name}, Lang) ->
|
||||
process_list_get(_LUser, _LServer, false, _Lang) ->
|
||||
{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) ->
|
||||
Attrs1 = [{<<"action">>,
|
||||
action_to_list(Item#listitem.action)},
|
||||
@ -357,9 +268,8 @@ process_iq_set(_, From, _To, #iq{lang = Lang, sub_el = SubEl}) ->
|
||||
end.
|
||||
|
||||
process_default_set(LUser, LServer, Value, Lang) ->
|
||||
case process_default_set_db(LUser, LServer, Value,
|
||||
gen_mod:db_type(LServer, ?MODULE))
|
||||
of
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:process_default_set(LUser, LServer, Value) of
|
||||
{atomic, error} ->
|
||||
{error, ?ERRT_INTERNAL_SERVER_ERROR(Lang, <<"Database failure">>)};
|
||||
{atomic, not_found} -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||
@ -367,79 +277,9 @@ process_default_set(LUser, LServer, Value, Lang) ->
|
||||
_ -> {error, ?ERR_INTERNAL_SERVER_ERROR}
|
||||
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}) ->
|
||||
case process_active_set(LUser, LServer, Name,
|
||||
gen_mod:db_type(LServer, ?MODULE))
|
||||
of
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:process_active_set(LUser, LServer, Name) of
|
||||
error -> {error, ?ERR_ITEM_NOT_FOUND};
|
||||
Items ->
|
||||
NeedDb = is_list_needdb(Items),
|
||||
@ -449,157 +289,16 @@ process_active_set(LUser, LServer, {value, Name}) ->
|
||||
process_active_set(_LUser, _LServer, false) ->
|
||||
{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) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
set_privacy_list(Privacy, DBType).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_privacy_list(Privacy).
|
||||
|
||||
process_list_set(LUser, LServer, {value, Name}, Els, Lang) ->
|
||||
case parse_items(Els) of
|
||||
false -> {error, ?ERR_BAD_REQUEST};
|
||||
remove ->
|
||||
case remove_privacy_list(LUser, LServer, Name,
|
||||
gen_mod:db_type(LServer, ?MODULE))
|
||||
of
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:remove_privacy_list(LUser, LServer, Name) of
|
||||
{atomic, conflict} ->
|
||||
Txt = <<"Cannot remove default list">>,
|
||||
{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">>)}
|
||||
end;
|
||||
List ->
|
||||
case set_privacy_list(LUser, LServer, Name, List,
|
||||
gen_mod:db_type(LServer, ?MODULE))
|
||||
of
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
case Mod:set_privacy_list(LUser, LServer, Name, List) of
|
||||
{atomic, ok} ->
|
||||
NeedDb = is_list_needdb(List),
|
||||
ejabberd_sm:route(jid:make(LUser, LServer,
|
||||
@ -737,105 +435,20 @@ is_list_needdb(Items) ->
|
||||
end,
|
||||
Items).
|
||||
|
||||
get_user_list(Acc, User, Server) ->
|
||||
get_user_list(_Acc, User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
{Default, Items} = get_user_list(Acc, LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
{Default, Items} = Mod:get_user_list(LUser, LServer),
|
||||
NeedDb = is_list_needdb(Items),
|
||||
#userlist{name = Default, list = Items,
|
||||
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) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
get_user_lists(LUser, LServer, gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_user_lists(LUser, LServer).
|
||||
|
||||
%% From is the sender, To is the destination.
|
||||
%% 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) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
remove_user(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer).
|
||||
|
||||
updated_list(_, #userlist{name = OldName} = Old,
|
||||
#userlist{name = NewName} = New) ->
|
||||
@ -977,250 +581,17 @@ updated_list(_, #userlist{name = OldName} = Old,
|
||||
true -> Old
|
||||
end.
|
||||
|
||||
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).
|
||||
|
||||
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.
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #privacy{} = P) ->
|
||||
mnesia:dirty_write(P);
|
||||
import(_LServer, riak, #privacy{} = P) ->
|
||||
ejabberd_riak:put(P, privacy_schema());
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, DBType, Data) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, Data).
|
||||
|
||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
|
198
src/mod_privacy_mnesia.erl
Normal file
198
src/mod_privacy_mnesia.erl
Normal file
@ -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.
|
160
src/mod_privacy_riak.erl
Normal file
160
src/mod_privacy_riak.erl
Normal file
@ -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{}}.
|
397
src/mod_privacy_sql.erl
Normal file
397
src/mod_privacy_sql.erl
Normal file
@ -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,11 +39,13 @@
|
||||
-include("logger.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("mod_private.hrl").
|
||||
|
||||
-record(private_storage,
|
||||
{usns = {<<"">>, <<"">>, <<"">>} :: {binary(), binary(), binary() |
|
||||
'$1' | '_'},
|
||||
xml = #xmlel{} :: xmlel() | '_' | '$1'}).
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-callback import(binary(), #private_storage{}) -> ok | pass.
|
||||
-callback set_data(binary(), binary(), [{binary(), xmlel()}]) -> {atomic, any()}.
|
||||
-callback get_data(binary(), binary(), binary()) -> {ok, xmlel()} | error.
|
||||
-callback get_all_data(binary(), binary()) -> [xmlel()].
|
||||
|
||||
-define(Xmlel_Query(Attrs, Children),
|
||||
#xmlel{name = <<"query">>, attrs = Attrs,
|
||||
@ -52,15 +54,8 @@
|
||||
start(Host, Opts) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
mnesia:create_table(private_storage,
|
||||
[{disc_only_copies, [node()]},
|
||||
{attributes,
|
||||
record_info(fields, private_storage)}]),
|
||||
update_table();
|
||||
_ -> ok
|
||||
end,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
||||
remove_user, 50),
|
||||
gen_iq_handler:add_iq_handler(ejabberd_sm, Host,
|
||||
@ -139,190 +134,44 @@ filter_xmlels([_ | Xmlels], Data) ->
|
||||
filter_xmlels(Xmlels, Data).
|
||||
|
||||
set_data(LUser, LServer, Data) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
F = fun () ->
|
||||
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}}]}]).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_data(LUser, LServer, Data).
|
||||
|
||||
get_data(LUser, LServer, Data) ->
|
||||
get_data(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE), Data, []).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
get_data(LUser, LServer, Data, Mod, []).
|
||||
|
||||
get_data(_LUser, _LServer, _DBType, [],
|
||||
Storage_Xmlels) ->
|
||||
get_data(_LUser, _LServer, [], _Mod, Storage_Xmlels) ->
|
||||
lists:reverse(Storage_Xmlels);
|
||||
get_data(LUser, LServer, mnesia,
|
||||
[{XmlNS, Xmlel} | Data], Storage_Xmlels) ->
|
||||
case mnesia:dirty_read(private_storage,
|
||||
{LUser, LServer, XmlNS})
|
||||
of
|
||||
[#private_storage{xml = Storage_Xmlel}] ->
|
||||
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])
|
||||
get_data(LUser, LServer, [{XmlNS, Xmlel} | Data], Mod, Storage_Xmlels) ->
|
||||
case Mod:get_data(LUser, LServer, XmlNS) of
|
||||
{ok, Storage_Xmlel} ->
|
||||
get_data(LUser, LServer, Data, Mod, [Storage_Xmlel | Storage_Xmlels]);
|
||||
error ->
|
||||
get_data(LUser, LServer, Data, Mod, [Xmlel | Storage_Xmlels])
|
||||
end.
|
||||
|
||||
get_data(LUser, LServer) ->
|
||||
get_all_data(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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{}}.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_all_data(LUser, LServer).
|
||||
|
||||
remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
remove_user(LUser, LServer,
|
||||
gen_mod:db_type(Server, ?MODULE)).
|
||||
Mod = gen_mod:db_mod(Server, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer).
|
||||
|
||||
remove_user(LUser, LServer, mnesia) ->
|
||||
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);
|
||||
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}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #private_storage{} = PS) ->
|
||||
mnesia:dirty_write(PS);
|
||||
|
||||
import(_LServer, riak, #private_storage{usns = {LUser, LServer, _}} = PS) ->
|
||||
ejabberd_riak:put(PS, private_storage_schema(),
|
||||
[{'2i', [{<<"us">>, {LUser, LServer}}]}]);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, DBType, PD) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, PD).
|
||||
|
||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||
mod_opt_type(iqdisc) -> fun gen_iq_handler:check_type/1;
|
||||
|
97
src/mod_private_mnesia.erl
Normal file
97
src/mod_private_mnesia.erl
Normal file
@ -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.
|
67
src/mod_private_riak.erl
Normal file
67
src/mod_private_riak.erl
Normal file
@ -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{}}.
|
97
src/mod_private_sql.erl
Normal file
97
src/mod_private_sql.erl
Normal file
@ -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,
|
||||
webadmin_user/4, get_versioning_feature/2,
|
||||
roster_versioning_enabled/1, roster_version/2,
|
||||
record_to_string/1, groups_to_string/1,
|
||||
mod_opt_type/1, set_roster/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@ -65,23 +64,27 @@
|
||||
|
||||
-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) ->
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
one_queue),
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
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,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(roster_get, Host, ?MODULE,
|
||||
get_user_roster, 50),
|
||||
ejabberd_hooks:add(roster_in_subscription, Host,
|
||||
@ -194,26 +197,8 @@ roster_version(LServer, LUser) ->
|
||||
end.
|
||||
|
||||
read_roster_version(LUser, LServer) ->
|
||||
read_roster_version(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:read_roster_version(LUser, LServer).
|
||||
|
||||
write_roster_version(LUser, LServer) ->
|
||||
write_roster_version(LUser, LServer, false).
|
||||
@ -223,38 +208,10 @@ write_roster_version_t(LUser, LServer) ->
|
||||
|
||||
write_roster_version(LUser, LServer, InTransaction) ->
|
||||
Ver = p1_sha:sha(term_to_binary(p1_time_compat:unique_integer())),
|
||||
write_roster_version(LUser, LServer, InTransaction, Ver,
|
||||
gen_mod:db_type(LServer, ?MODULE)),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:write_roster_version(LUser, LServer, InTransaction, 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.
|
||||
%% It is neccesary if
|
||||
%% - roster versioning is disabled in server OR
|
||||
@ -350,56 +307,8 @@ get_user_roster(Acc, {LUser, LServer}) ->
|
||||
++ Acc.
|
||||
|
||||
get_roster(LUser, LServer) ->
|
||||
get_roster(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_roster(LUser, LServer).
|
||||
|
||||
set_roster(#roster{us = {LUser, LServer}, jid = LJID} = Item) ->
|
||||
transaction(
|
||||
@ -437,48 +346,8 @@ item_to_xml(Item) ->
|
||||
children = SubEls}.
|
||||
|
||||
get_roster_by_jid_t(LUser, LServer, LJID) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
get_roster_by_jid_t(LUser, LServer, LJID, DBType).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_roster_by_jid(LUser, LServer, LJID).
|
||||
|
||||
try_process_iq_set(From, To, #iq{sub_el = SubEl, lang = Lang} = IQ) ->
|
||||
#jid{server = Server} = From,
|
||||
@ -632,35 +501,14 @@ push_item_version(Server, User, From, Item,
|
||||
end,
|
||||
ejabberd_sm:get_user_resources(User, Server)).
|
||||
|
||||
get_subscription_lists(Acc, User, Server) ->
|
||||
get_subscription_lists(_Acc, User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
Items = get_subscription_lists(Acc, LUser, LServer,
|
||||
DBType),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Items = Mod:get_only_items(LUser, LServer),
|
||||
fill_subscription_lists(LServer, Items, [], []).
|
||||
|
||||
get_subscription_lists(_, LUser, LServer, mnesia) ->
|
||||
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) ->
|
||||
fill_subscription_lists(LServer, [I | Is], F, T) ->
|
||||
J = element(3, I#roster.usj),
|
||||
case I#roster.subscription of
|
||||
both ->
|
||||
@ -670,39 +518,20 @@ fill_subscription_lists(LServer, [#roster{} = I | Is],
|
||||
to -> fill_subscription_lists(LServer, Is, F, [J | T]);
|
||||
_ -> fill_subscription_lists(LServer, Is, F, T)
|
||||
end;
|
||||
fill_subscription_lists(LServer, [RawI | Is], F, T) ->
|
||||
I = raw_to_record(LServer, RawI),
|
||||
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}.
|
||||
fill_subscription_lists(_LServer, [], F, T) ->
|
||||
{F, T}.
|
||||
|
||||
ask_to_pending(subscribe) -> out;
|
||||
ask_to_pending(unsubscribe) -> none;
|
||||
ask_to_pending(Ask) -> Ask.
|
||||
|
||||
roster_subscribe_t(LUser, LServer, LJID, Item) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
roster_subscribe_t(LUser, LServer, LJID, Item, DBType).
|
||||
|
||||
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}}]}]).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:roster_subscribe(LUser, LServer, LJID, Item).
|
||||
|
||||
transaction(LServer, F) ->
|
||||
case gen_mod:db_type(LServer, ?MODULE) of
|
||||
mnesia -> mnesia:transaction(F);
|
||||
odbc -> ejabberd_odbc:sql_transaction(LServer, F);
|
||||
riak -> {atomic, F()}
|
||||
end.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:transaction(LServer, F).
|
||||
|
||||
in_subscription(_, User, Server, JID, Type, Reason) ->
|
||||
process_subscription(in, User, Server, JID, Type,
|
||||
@ -712,45 +541,8 @@ out_subscription(User, Server, JID, Type) ->
|
||||
process_subscription(out, User, Server, JID, Type, <<"">>).
|
||||
|
||||
get_roster_by_jid_with_groups_t(LUser, LServer, LJID) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
get_roster_by_jid_with_groups_t(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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_roster_by_jid_with_groups(LUser, LServer, LJID).
|
||||
|
||||
process_subscription(Direction, User, Server, JID1,
|
||||
Type, Reason) ->
|
||||
@ -948,21 +740,8 @@ remove_user(User, Server) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
send_unsubscription_to_rosteritems(LUser, LServer),
|
||||
remove_user(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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})}.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer).
|
||||
|
||||
%% For each contact with Subscription:
|
||||
%% Both or From, send a "unsubscribed" presence stanza;
|
||||
@ -1020,33 +799,12 @@ set_items(User, Server, SubEl) ->
|
||||
transaction(LServer, F).
|
||||
|
||||
update_roster_t(LUser, LServer, LJID, Item) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
update_roster_t(LUser, LServer, LJID, Item, DBType).
|
||||
|
||||
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}}]}]).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:update_roster(LUser, LServer, LJID, Item).
|
||||
|
||||
del_roster_t(LUser, LServer, LJID) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
del_roster_t(LUser, LServer, LJID, DBType).
|
||||
|
||||
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}).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:del_roster(LUser, LServer, LJID).
|
||||
|
||||
process_item_set_t(LUser, LServer,
|
||||
#xmlel{attrs = Attrs, children = Els}) ->
|
||||
@ -1109,13 +867,12 @@ process_item_attrs_ws(Item, []) -> Item.
|
||||
|
||||
get_in_pending_subscriptions(Ls, User, Server) ->
|
||||
LServer = jid:nameprep(Server),
|
||||
get_in_pending_subscriptions(Ls, User, Server,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
get_in_pending_subscriptions(Ls, User, Server, Mod).
|
||||
|
||||
get_in_pending_subscriptions(Ls, User, Server, DBType)
|
||||
when DBType == mnesia; DBType == riak ->
|
||||
get_in_pending_subscriptions(Ls, User, Server, Mod) ->
|
||||
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) ->
|
||||
Message = R#roster.askmessage,
|
||||
Status = if is_binary(Message) -> (Message);
|
||||
@ -1140,93 +897,15 @@ get_in_pending_subscriptions(Ls, User, Server, DBType)
|
||||
_ -> false
|
||||
end
|
||||
end,
|
||||
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.
|
||||
Result)).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
read_subscription_and_groups(User, Server, LJID) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
read_subscription_and_groups(LUser, LServer, LJID,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:read_subscription_and_groups(LUser, LServer, LJID).
|
||||
|
||||
get_jid_info(_, User, Server, 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,
|
||||
#request{us = _US, path = [<<"user">>, U, <<"roster">>],
|
||||
q = Query, lang = Lang} =
|
||||
@ -1692,68 +1222,17 @@ is_managed_from_id(<<"roster-remotely-managed">>) ->
|
||||
is_managed_from_id(_Id) ->
|
||||
false.
|
||||
|
||||
roster_schema() ->
|
||||
{record_info(fields, roster), #roster{}}.
|
||||
|
||||
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}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #roster{} = R) ->
|
||||
mnesia:dirty_write(R);
|
||||
import(_LServer, mnesia, #roster_version{} = RV) ->
|
||||
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.
|
||||
import(LServer, DBType, R) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, R).
|
||||
|
||||
mod_opt_type(access) ->
|
||||
fun (A) when is_atom(A) -> A end;
|
||||
|
171
src/mod_roster_mnesia.erl
Normal file
171
src/mod_roster_mnesia.erl
Normal file
@ -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.
|
113
src/mod_roster_riak.erl
Normal file
113
src/mod_roster_riak.erl
Normal file
@ -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{}}.
|
308
src/mod_roster_sql.erl
Normal file
308
src/mod_roster_sql.erl
Normal file
@ -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,
|
||||
delete_group/2, get_group_opts/2, set_group_opts/3,
|
||||
get_group_users/2, get_group_explicit_users/2,
|
||||
is_user_in_group/3, add_user_to_group/3,
|
||||
is_user_in_group/3, add_user_to_group/3, opts_to_binary/1,
|
||||
remove_user_from_group/3, mod_opt_type/1]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
@ -52,25 +52,28 @@
|
||||
|
||||
-include("ejabberd_web_admin.hrl").
|
||||
|
||||
-record(sr_group, {group_host = {<<"">>, <<"">>} :: {'$1' | binary(), '$2' | binary()},
|
||||
opts = [] :: list() | '_' | '$2'}).
|
||||
-include("mod_shared_roster.hrl").
|
||||
|
||||
-record(sr_user, {us = {<<"">>, <<"">>} :: {binary(), binary()},
|
||||
group_host = {<<"">>, <<"">>} :: {binary(), binary()}}).
|
||||
-type group_options() :: [{atom(), any()}].
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-callback import(binary(), #sr_user{} | #sr_group{}) -> ok | pass.
|
||||
-callback list_groups(binary()) -> [binary()].
|
||||
-callback groups_with_opts(binary()) -> [{binary(), group_options()}].
|
||||
-callback create_group(binary(), binary(), group_options()) -> {atomic, any()}.
|
||||
-callback delete_group(binary(), binary()) -> {atomic, any()}.
|
||||
-callback get_group_opts(binary(), binary()) -> group_options() | error.
|
||||
-callback set_group_opts(binary(), binary(), group_options()) -> {atomic, any()}.
|
||||
-callback get_user_groups({binary(), binary()}, binary()) -> [binary()].
|
||||
-callback get_group_explicit_users(binary(), binary()) -> [{binary(), binary()}].
|
||||
-callback get_user_displayed_groups(binary(), binary(), group_options()) ->
|
||||
[{binary(), group_options()}].
|
||||
-callback is_user_in_group({binary(), binary()}, binary(), binary()) -> boolean().
|
||||
-callback add_user_to_group(binary(), {binary(), binary()}, binary()) -> {atomic, any()}.
|
||||
-callback remove_user_from_group(binary(), {binary(), binary()}, binary()) -> {atomic, any()}.
|
||||
|
||||
start(Host, Opts) ->
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
mnesia:create_table(sr_group,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes, record_info(fields, sr_group)}]),
|
||||
mnesia:create_table(sr_user,
|
||||
[{disc_copies, [node()]}, {type, bag},
|
||||
{attributes, record_info(fields, sr_user)}]),
|
||||
update_tables(),
|
||||
mnesia:add_table_index(sr_user, group_host);
|
||||
_ -> ok
|
||||
end,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(webadmin_menu_host, Host, ?MODULE,
|
||||
webadmin_menu, 70),
|
||||
ejabberd_hooks:add(webadmin_page_host, Host, ?MODULE,
|
||||
@ -391,195 +394,36 @@ process_subscription(Direction, User, Server, JID,
|
||||
end.
|
||||
|
||||
list_groups(Host) ->
|
||||
list_groups(Host, gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
list_groups(Host, mnesia) ->
|
||||
mnesia:dirty_select(sr_group,
|
||||
[{#sr_group{group_host = {'$1', '$2'}, _ = '_'},
|
||||
[{'==', '$2', Host}], ['$1']}]);
|
||||
list_groups(Host, riak) ->
|
||||
case ejabberd_riak:get_keys_by_index(sr_group, <<"host">>, Host) of
|
||||
{ok, Gs} ->
|
||||
[G || {G, _} <- Gs];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
list_groups(Host, odbc) ->
|
||||
case ejabberd_odbc:sql_query(Host,
|
||||
[<<"select name from sr_group;">>])
|
||||
of
|
||||
{selected, [<<"name">>], Rs} -> [G || [G] <- Rs];
|
||||
_ -> []
|
||||
end.
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:list_groups(Host).
|
||||
|
||||
groups_with_opts(Host) ->
|
||||
groups_with_opts(Host, gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
groups_with_opts(Host, mnesia) ->
|
||||
Gs = mnesia:dirty_select(sr_group,
|
||||
[{#sr_group{group_host = {'$1', Host}, opts = '$2',
|
||||
_ = '_'},
|
||||
[], [['$1', '$2']]}]),
|
||||
lists:map(fun ([G, O]) -> {G, O} end, Gs);
|
||||
groups_with_opts(Host, riak) ->
|
||||
case ejabberd_riak:get_by_index(sr_group, sr_group_schema(),
|
||||
<<"host">>, Host) of
|
||||
{ok, Rs} ->
|
||||
[{G, O} || #sr_group{group_host = {G, _}, opts = O} <- Rs];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
groups_with_opts(Host, odbc) ->
|
||||
case ejabberd_odbc:sql_query(Host,
|
||||
[<<"select name, opts from sr_group;">>])
|
||||
of
|
||||
{selected, [<<"name">>, <<"opts">>], Rs} ->
|
||||
[{G, opts_to_binary(ejabberd_odbc:decode_term(Opts))}
|
||||
|| [G, Opts] <- Rs];
|
||||
_ -> []
|
||||
end.
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:groups_with_opts(Host).
|
||||
|
||||
create_group(Host, Group) ->
|
||||
create_group(Host, Group, []).
|
||||
|
||||
create_group(Host, Group, Opts) ->
|
||||
create_group(Host, Group, Opts,
|
||||
gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
create_group(Host, Group, Opts, mnesia) ->
|
||||
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||
F = fun () -> mnesia:write(R) end,
|
||||
mnesia:transaction(F);
|
||||
create_group(Host, Group, Opts, riak) ->
|
||||
{atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host},
|
||||
opts = Opts},
|
||||
sr_group_schema(),
|
||||
[{'2i', [{<<"host">>, Host}]}])};
|
||||
create_group(Host, Group, Opts, odbc) ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
SOpts = ejabberd_odbc:encode_term(Opts),
|
||||
F = fun () ->
|
||||
odbc_queries:update_t(<<"sr_group">>,
|
||||
[<<"name">>, <<"opts">>], [SGroup, SOpts],
|
||||
[<<"name='">>, SGroup, <<"'">>])
|
||||
end,
|
||||
ejabberd_odbc:sql_transaction(Host, F).
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:create_group(Host, Group, Opts).
|
||||
|
||||
delete_group(Host, Group) ->
|
||||
delete_group(Host, Group,
|
||||
gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
delete_group(Host, Group, mnesia) ->
|
||||
GroupHost = {Group, Host},
|
||||
F = fun () ->
|
||||
mnesia:delete({sr_group, GroupHost}),
|
||||
Users = mnesia:index_read(sr_user, GroupHost,
|
||||
#sr_user.group_host),
|
||||
lists:foreach(fun (UserEntry) ->
|
||||
mnesia:delete_object(UserEntry)
|
||||
end,
|
||||
Users)
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
delete_group(Host, Group, riak) ->
|
||||
try
|
||||
ok = ejabberd_riak:delete(sr_group, {Group, Host}),
|
||||
ok = ejabberd_riak:delete_by_index(sr_user, <<"group_host">>,
|
||||
{Group, Host}),
|
||||
{atomic, ok}
|
||||
catch _:{badmatch, Err} ->
|
||||
{atomic, Err}
|
||||
end;
|
||||
delete_group(Host, Group, odbc) ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
F = fun () ->
|
||||
ejabberd_odbc:sql_query_t([<<"delete from sr_group where name='">>,
|
||||
SGroup, <<"';">>]),
|
||||
ejabberd_odbc:sql_query_t([<<"delete from sr_user where grp='">>,
|
||||
SGroup, <<"';">>])
|
||||
end,
|
||||
case ejabberd_odbc:sql_transaction(Host, F) of
|
||||
{atomic,{updated,_}} -> {atomic, ok};
|
||||
Res -> Res
|
||||
end.
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:delete_group(Host, Group).
|
||||
|
||||
get_group_opts(Host, Group) ->
|
||||
get_group_opts(Host, Group,
|
||||
gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
get_group_opts(Host, Group, mnesia) ->
|
||||
case catch mnesia:dirty_read(sr_group, {Group, Host}) of
|
||||
[#sr_group{opts = Opts}] -> Opts;
|
||||
_ -> error
|
||||
end;
|
||||
get_group_opts(Host, Group, riak) ->
|
||||
case ejabberd_riak:get(sr_group, sr_group_schema(), {Group, Host}) of
|
||||
{ok, #sr_group{opts = Opts}} -> Opts;
|
||||
_ -> error
|
||||
end;
|
||||
get_group_opts(Host, Group, odbc) ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
case catch ejabberd_odbc:sql_query(Host,
|
||||
[<<"select opts from sr_group where name='">>,
|
||||
SGroup, <<"';">>])
|
||||
of
|
||||
{selected, [<<"opts">>], [[SOpts]]} ->
|
||||
opts_to_binary(ejabberd_odbc:decode_term(SOpts));
|
||||
_ -> error
|
||||
end.
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:get_group_opts(Host, Group).
|
||||
|
||||
set_group_opts(Host, Group, Opts) ->
|
||||
set_group_opts(Host, Group, Opts,
|
||||
gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
set_group_opts(Host, Group, Opts, mnesia) ->
|
||||
R = #sr_group{group_host = {Group, Host}, opts = Opts},
|
||||
F = fun () -> mnesia:write(R) end,
|
||||
mnesia:transaction(F);
|
||||
set_group_opts(Host, Group, Opts, riak) ->
|
||||
{atomic, ejabberd_riak:put(#sr_group{group_host = {Group, Host},
|
||||
opts = Opts},
|
||||
sr_group_schema(),
|
||||
[{'2i', [{<<"host">>, Host}]}])};
|
||||
set_group_opts(Host, Group, Opts, odbc) ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
SOpts = ejabberd_odbc:encode_term(Opts),
|
||||
F = fun () ->
|
||||
odbc_queries:update_t(<<"sr_group">>,
|
||||
[<<"name">>, <<"opts">>], [SGroup, SOpts],
|
||||
[<<"name='">>, SGroup, <<"'">>])
|
||||
end,
|
||||
ejabberd_odbc:sql_transaction(Host, F).
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:set_group_opts(Host, Group, Opts).
|
||||
|
||||
get_user_groups(US) ->
|
||||
Host = element(2, US),
|
||||
DBType = gen_mod:db_type(Host, ?MODULE),
|
||||
get_user_groups(US, Host, DBType) ++
|
||||
get_special_users_groups(Host).
|
||||
|
||||
get_user_groups(US, Host, mnesia) ->
|
||||
case catch mnesia:dirty_read(sr_user, US) of
|
||||
Rs when is_list(Rs) ->
|
||||
[Group
|
||||
|| #sr_user{group_host = {Group, H}} <- Rs, H == Host];
|
||||
_ -> []
|
||||
end;
|
||||
get_user_groups(US, Host, riak) ->
|
||||
case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of
|
||||
{ok, Rs} ->
|
||||
[Group || #sr_user{group_host = {Group, H}} <- Rs, H == Host];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
get_user_groups(US, Host, odbc) ->
|
||||
SJID = make_jid_s(US),
|
||||
case catch ejabberd_odbc:sql_query(Host,
|
||||
[<<"select grp from sr_user where jid='">>,
|
||||
SJID, <<"';">>])
|
||||
of
|
||||
{selected, [<<"grp">>], Rs} -> [G || [G] <- Rs];
|
||||
_ -> []
|
||||
end.
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:get_user_groups(US, Host) ++ get_special_users_groups(Host).
|
||||
|
||||
is_group_enabled(Host1, Group1) ->
|
||||
{Host, Group} = split_grouphost(Host1, Group1),
|
||||
@ -630,39 +474,8 @@ get_group_users(Host, Group, GroupOpts) ->
|
||||
++ get_group_explicit_users(Host, Group).
|
||||
|
||||
get_group_explicit_users(Host, Group) ->
|
||||
get_group_explicit_users(Host, Group,
|
||||
gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
get_group_explicit_users(Host, Group, mnesia) ->
|
||||
Read = (catch mnesia:dirty_index_read(sr_user,
|
||||
{Group, Host}, #sr_user.group_host)),
|
||||
case Read of
|
||||
Rs when is_list(Rs) -> [R#sr_user.us || R <- Rs];
|
||||
_ -> []
|
||||
end;
|
||||
get_group_explicit_users(Host, Group, riak) ->
|
||||
case ejabberd_riak:get_by_index(sr_user, sr_user_schema(),
|
||||
<<"group_host">>, {Group, Host}) of
|
||||
{ok, Rs} ->
|
||||
[R#sr_user.us || R <- Rs];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
get_group_explicit_users(Host, Group, odbc) ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
case catch ejabberd_odbc:sql_query(Host,
|
||||
[<<"select jid from sr_user where grp='">>,
|
||||
SGroup, <<"';">>])
|
||||
of
|
||||
{selected, [<<"jid">>], Rs} ->
|
||||
lists:map(fun ([JID]) ->
|
||||
{U, S, _} =
|
||||
jid:tolower(jid:from_string(JID)),
|
||||
{U, S}
|
||||
end,
|
||||
Rs);
|
||||
_ -> []
|
||||
end.
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:get_group_explicit_users(Host, Group).
|
||||
|
||||
get_group_name(Host1, Group1) ->
|
||||
{Host, Group} = split_grouphost(Host1, Group1),
|
||||
@ -718,44 +531,10 @@ get_special_displayed_groups(GroupsOpts) ->
|
||||
%% for the list of groups of that server that user is member
|
||||
%% get the list of groups displayed
|
||||
get_user_displayed_groups(LUser, LServer, GroupsOpts) ->
|
||||
Groups = get_user_displayed_groups(LUser, LServer,
|
||||
GroupsOpts,
|
||||
gen_mod:db_type(LServer, ?MODULE)),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Groups = Mod:get_user_displayed_groups(LUser, LServer, GroupsOpts),
|
||||
displayed_groups(GroupsOpts, Groups).
|
||||
|
||||
get_user_displayed_groups(LUser, LServer, GroupsOpts,
|
||||
mnesia) ->
|
||||
case catch mnesia:dirty_read(sr_user, {LUser, LServer})
|
||||
of
|
||||
Rs when is_list(Rs) ->
|
||||
[{Group, proplists:get_value(Group, GroupsOpts, [])}
|
||||
|| #sr_user{group_host = {Group, H}} <- Rs,
|
||||
H == LServer];
|
||||
_ -> []
|
||||
end;
|
||||
get_user_displayed_groups(LUser, LServer, GroupsOpts,
|
||||
riak) ->
|
||||
case ejabberd_riak:get_by_index(sr_user, sr_user_schema(),
|
||||
<<"us">>, {LUser, LServer}) of
|
||||
{ok, Rs} ->
|
||||
[{Group, proplists:get_value(Group, GroupsOpts, [])}
|
||||
|| #sr_user{group_host = {Group, _}} <- Rs];
|
||||
_ ->
|
||||
[]
|
||||
end;
|
||||
get_user_displayed_groups(LUser, LServer, GroupsOpts,
|
||||
odbc) ->
|
||||
SJID = make_jid_s(LUser, LServer),
|
||||
case catch ejabberd_odbc:sql_query(LServer,
|
||||
[<<"select grp from sr_user where jid='">>,
|
||||
SJID, <<"';">>])
|
||||
of
|
||||
{selected, [<<"grp">>], Rs} ->
|
||||
[{Group, proplists:get_value(Group, GroupsOpts, [])}
|
||||
|| [Group] <- Rs];
|
||||
_ -> []
|
||||
end.
|
||||
|
||||
%% @doc Get the list of groups that are displayed to this user
|
||||
get_user_displayed_groups(US) ->
|
||||
Host = element(2, US),
|
||||
@ -779,42 +558,12 @@ get_user_displayed_groups(US) ->
|
||||
is_group_enabled(Host, Group)].
|
||||
|
||||
is_user_in_group(US, Group, Host) ->
|
||||
is_user_in_group(US, Group, Host,
|
||||
gen_mod:db_type(Host, ?MODULE)).
|
||||
|
||||
is_user_in_group(US, Group, Host, mnesia) ->
|
||||
case catch mnesia:dirty_match_object(#sr_user{us = US,
|
||||
group_host = {Group, Host}})
|
||||
of
|
||||
[] -> lists:member(US, get_group_users(Host, Group));
|
||||
_ -> true
|
||||
end;
|
||||
is_user_in_group(US, Group, Host, riak) ->
|
||||
case ejabberd_riak:get_by_index(sr_user, sr_user_schema(), <<"us">>, US) of
|
||||
{ok, Rs} ->
|
||||
case lists:any(
|
||||
fun(#sr_user{group_host = {G, H}}) ->
|
||||
(Group == G) and (Host == H)
|
||||
end, Rs) of
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
case Mod:is_user_in_group(US, Group, Host) of
|
||||
false ->
|
||||
lists:member(US, get_group_users(Host, Group));
|
||||
true ->
|
||||
true
|
||||
end;
|
||||
_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.
|
||||
|
||||
%% @spec (Host::string(), {User::string(), Server::string()}, Group::string()) -> {atomic, ok}
|
||||
@ -837,31 +586,10 @@ add_user_to_group(Host, US, Group) ->
|
||||
push_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups),
|
||||
broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups),
|
||||
broadcast_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups),
|
||||
add_user_to_group(Host, US, Group, gen_mod:db_type(Host, ?MODULE))
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Mod:add_user_to_group(Host, US, Group)
|
||||
end.
|
||||
|
||||
add_user_to_group(Host, US, Group, mnesia) ->
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun () -> mnesia:write(R) end,
|
||||
mnesia:transaction(F);
|
||||
add_user_to_group(Host, US, Group, riak) ->
|
||||
{atomic, ejabberd_riak:put(
|
||||
#sr_user{us = US, group_host = {Group, Host}},
|
||||
sr_user_schema(),
|
||||
[{i, {US, {Group, Host}}},
|
||||
{'2i', [{<<"us">>, US},
|
||||
{<<"group_host">>, {Group, Host}}]}])};
|
||||
add_user_to_group(Host, US, Group, odbc) ->
|
||||
SJID = make_jid_s(US),
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
F = fun () ->
|
||||
odbc_queries:update_t(<<"sr_user">>,
|
||||
[<<"jid">>, <<"grp">>], [SJID, SGroup],
|
||||
[<<"jid='">>, SJID, <<"' and grp='">>,
|
||||
SGroup, <<"'">>])
|
||||
end,
|
||||
ejabberd_odbc:sql_transaction(Host, F).
|
||||
|
||||
get_displayed_groups(Group, LServer) ->
|
||||
GroupsOpts = groups_with_opts(LServer),
|
||||
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
|
||||
@ -894,8 +622,8 @@ remove_user_from_group(Host, US, Group) ->
|
||||
end,
|
||||
(?MODULE):set_group_opts(Host, Group, NewGroupOpts);
|
||||
nomatch ->
|
||||
Result = remove_user_from_group(Host, US, Group,
|
||||
gen_mod:db_type(Host, ?MODULE)),
|
||||
Mod = gen_mod:db_mod(Host, ?MODULE),
|
||||
Result = Mod:remove_user_from_group(Host, US, Group),
|
||||
DisplayedToGroups = displayed_to_groups(Group, Host),
|
||||
DisplayedGroups = get_displayed_groups(Group, LServer),
|
||||
push_user_to_displayed(LUser, LServer, Group, Host, remove, DisplayedToGroups),
|
||||
@ -903,23 +631,6 @@ remove_user_from_group(Host, US, Group) ->
|
||||
Result
|
||||
end.
|
||||
|
||||
remove_user_from_group(Host, US, Group, mnesia) ->
|
||||
R = #sr_user{us = US, group_host = {Group, Host}},
|
||||
F = fun () -> mnesia:delete_object(R) end,
|
||||
mnesia:transaction(F);
|
||||
remove_user_from_group(Host, US, Group, riak) ->
|
||||
{atomic, ejabberd_riak:delete(sr_group, {US, {Group, Host}})};
|
||||
remove_user_from_group(Host, US, Group, odbc) ->
|
||||
SJID = make_jid_s(US),
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
F = fun () ->
|
||||
ejabberd_odbc:sql_query_t([<<"delete from sr_user where jid='">>,
|
||||
SJID, <<"' and grp='">>, SGroup,
|
||||
<<"';">>]),
|
||||
ok
|
||||
end,
|
||||
ejabberd_odbc:sql_transaction(Host, F).
|
||||
|
||||
push_members_to_user(LUser, LServer, Group, Host,
|
||||
Subscription) ->
|
||||
GroupsOpts = groups_with_opts(LServer),
|
||||
@ -1385,13 +1096,6 @@ displayed_groups_update(Members, DisplayedGroups, Subscription) ->
|
||||
end
|
||||
end, Members).
|
||||
|
||||
make_jid_s(U, S) ->
|
||||
ejabberd_odbc:escape(jid:to_string(jid:tolower(jid:make(U,
|
||||
S,
|
||||
<<"">>)))).
|
||||
|
||||
make_jid_s({U, S}) -> make_jid_s(U, S).
|
||||
|
||||
opts_to_binary(Opts) ->
|
||||
lists:map(
|
||||
fun({name, Name}) ->
|
||||
@ -1404,105 +1108,17 @@ opts_to_binary(Opts) ->
|
||||
Opt
|
||||
end, Opts).
|
||||
|
||||
sr_group_schema() ->
|
||||
{record_info(fields, sr_group), #sr_group{}}.
|
||||
|
||||
sr_user_schema() ->
|
||||
{record_info(fields, sr_user), #sr_user{}}.
|
||||
|
||||
update_tables() ->
|
||||
update_sr_group_table(),
|
||||
update_sr_user_table().
|
||||
|
||||
update_sr_group_table() ->
|
||||
Fields = record_info(fields, sr_group),
|
||||
case mnesia:table_info(sr_group, attributes) of
|
||||
Fields ->
|
||||
ejabberd_config:convert_table_to_binary(
|
||||
sr_group, Fields, set,
|
||||
fun(#sr_group{group_host = {G, _}}) -> G end,
|
||||
fun(#sr_group{group_host = {G, H},
|
||||
opts = Opts} = R) ->
|
||||
R#sr_group{group_host = {iolist_to_binary(G),
|
||||
iolist_to_binary(H)},
|
||||
opts = opts_to_binary(Opts)}
|
||||
end);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating sr_group table", []),
|
||||
mnesia:transform_table(sr_group, ignore, Fields)
|
||||
end.
|
||||
|
||||
update_sr_user_table() ->
|
||||
Fields = record_info(fields, sr_user),
|
||||
case mnesia:table_info(sr_user, attributes) of
|
||||
Fields ->
|
||||
ejabberd_config:convert_table_to_binary(
|
||||
sr_user, Fields, bag,
|
||||
fun(#sr_user{us = {U, _}}) -> U end,
|
||||
fun(#sr_user{us = {U, S}, group_host = {G, H}} = R) ->
|
||||
R#sr_user{us = {iolist_to_binary(U), iolist_to_binary(S)},
|
||||
group_host = {iolist_to_binary(G),
|
||||
iolist_to_binary(H)}}
|
||||
end);
|
||||
_ ->
|
||||
?INFO_MSG("Recreating sr_user table", []),
|
||||
mnesia:transform_table(sr_user, ignore, Fields)
|
||||
end.
|
||||
|
||||
export(_Server) ->
|
||||
[{sr_group,
|
||||
fun(Host, #sr_group{group_host = {Group, LServer}, opts = Opts})
|
||||
when LServer == Host ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
SOpts = ejabberd_odbc:encode_term(Opts),
|
||||
[[<<"delete from sr_group where name='">>, Group, <<"';">>],
|
||||
[<<"insert into sr_group(name, opts) values ('">>,
|
||||
SGroup, <<"', '">>, SOpts, <<"');">>]];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end},
|
||||
{sr_user,
|
||||
fun(Host, #sr_user{us = {U, S}, group_host = {Group, LServer}})
|
||||
when LServer == Host ->
|
||||
SGroup = ejabberd_odbc:escape(Group),
|
||||
SJID = ejabberd_odbc:escape(
|
||||
jid:to_string(
|
||||
jid:tolower(
|
||||
jid:make(U, S, <<"">>)))),
|
||||
[[<<"delete from sr_user where jid='">>, SJID,
|
||||
<<"'and grp='">>, Group, <<"';">>],
|
||||
[<<"insert into sr_user(jid, grp) values ('">>,
|
||||
SJID, <<"', '">>, SGroup, <<"');">>]];
|
||||
(_Host, _R) ->
|
||||
[]
|
||||
end}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
import(LServer) ->
|
||||
[{<<"select name, opts from sr_group;">>,
|
||||
fun([Group, SOpts]) ->
|
||||
#sr_group{group_host = {Group, LServer},
|
||||
opts = ejabberd_odbc:decode_term(SOpts)}
|
||||
end},
|
||||
{<<"select jid, grp from sr_user;">>,
|
||||
fun([SJID, Group]) ->
|
||||
#jid{luser = U, lserver = S} = jid:from_string(SJID),
|
||||
#sr_user{us = {U, S}, group_host = {Group, LServer}}
|
||||
end}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #sr_group{} = G) ->
|
||||
mnesia:dirty_write(G);
|
||||
|
||||
import(_LServer, mnesia, #sr_user{} = U) ->
|
||||
mnesia:dirty_write(U);
|
||||
import(_LServer, riak, #sr_group{group_host = {_, Host}} = G) ->
|
||||
ejabberd_riak:put(G, sr_group_schema(), [{'2i', [{<<"host">>, Host}]}]);
|
||||
import(_LServer, riak, #sr_user{us = US, group_host = {Group, Host}} = User) ->
|
||||
ejabberd_riak:put(User, sr_user_schema(),
|
||||
[{i, {US, {Group, Host}}},
|
||||
{'2i', [{<<"us">>, US},
|
||||
{<<"group_host">>, {Group, Host}}]}]);
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, DBType, Data) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, Data).
|
||||
|
||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||
mod_opt_type(_) -> [db_type].
|
||||
|
167
src/mod_shared_roster_mnesia.erl
Normal file
167
src/mod_shared_roster_mnesia.erl
Normal file
@ -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.
|
139
src/mod_shared_roster_riak.erl
Normal file
139
src/mod_shared_roster_riak.erl
Normal file
@ -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{}}.
|
212
src/mod_shared_roster_sql.erl
Normal file
212
src/mod_shared_roster_sql.erl
Normal file
@ -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).
|
||||
|
||||
-compile([{parse_transform, ejabberd_sql_pt}]).
|
||||
|
||||
-author('alexey@process-one.net').
|
||||
|
||||
-protocol({xep, 54, '1.2'}).
|
||||
@ -35,54 +33,31 @@
|
||||
-behaviour(gen_mod).
|
||||
|
||||
-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,
|
||||
mod_opt_type/1, set_vcard/3]).
|
||||
mod_opt_type/1, set_vcard/3, make_vcard_search/4]).
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
-include("ejabberd_sql_pt.hrl").
|
||||
|
||||
-include("jlib.hrl").
|
||||
-include("mod_vcard.hrl").
|
||||
|
||||
-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).
|
||||
|
||||
-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) ->
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
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,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(remove_user, Host, ?MODULE,
|
||||
remove_user, 50),
|
||||
IQDisc = gen_mod:get_opt(iqdisc, Opts, fun gen_iq_handler:check_type/1,
|
||||
@ -206,38 +181,10 @@ process_sm_iq(From, To,
|
||||
end.
|
||||
|
||||
get_vcard(LUser, LServer) ->
|
||||
get_vcard(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_vcard(LUser, LServer).
|
||||
|
||||
get_vcard(LUser, LServer, mnesia) ->
|
||||
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) ->
|
||||
make_vcard_search(User, LUser, LServer, VCARD) ->
|
||||
FN = fxml:get_path_s(VCARD, [{elem, <<"FN">>}, cdata]),
|
||||
Family = fxml:get_path_s(VCARD,
|
||||
[{elem, <<"N">>}, {elem, <<"FAMILY">>}, cdata]),
|
||||
@ -266,7 +213,6 @@ set_vcard(User, LServer, VCARD) ->
|
||||
<<"">> -> EMail2;
|
||||
_ -> EMail1
|
||||
end,
|
||||
LUser = jid:nodeprep(User),
|
||||
LFN = string2lower(FN),
|
||||
LFamily = string2lower(Family),
|
||||
LGiven = string2lower(Given),
|
||||
@ -278,15 +224,8 @@ set_vcard(User, LServer, VCARD) ->
|
||||
LEMail = string2lower(EMail),
|
||||
LOrgName = string2lower(OrgName),
|
||||
LOrgUnit = string2lower(OrgUnit),
|
||||
if (LUser == error) ->
|
||||
{error, badarg};
|
||||
true ->
|
||||
case gen_mod:db_type(LServer, ?MODULE) of
|
||||
mnesia ->
|
||||
US = {LUser, LServer},
|
||||
F = fun () ->
|
||||
mnesia:write(#vcard{us = US, vcard = VCARD}),
|
||||
mnesia:write(#vcard_search{us = US,
|
||||
#vcard_search{us = US,
|
||||
user = {User, LServer},
|
||||
luser = LUser, fn = FN,
|
||||
lfn = LFN,
|
||||
@ -309,47 +248,16 @@ set_vcard(User, LServer, VCARD) ->
|
||||
orgname = OrgName,
|
||||
lorgname = LOrgName,
|
||||
orgunit = OrgUnit,
|
||||
lorgunit = LOrgUnit})
|
||||
end,
|
||||
mnesia:transaction(F);
|
||||
riak ->
|
||||
US = {LUser, LServer},
|
||||
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,
|
||||
lorgunit = LOrgUnit}.
|
||||
|
||||
set_vcard(User, LServer, VCARD) ->
|
||||
case jid:nodeprep(User) of
|
||||
error ->
|
||||
{error, badarg};
|
||||
LUser ->
|
||||
VCardSearch = make_vcard_search(User, LUser, LServer, VCARD),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:set_vcard(LUser, LServer, VCARD, VCardSearch),
|
||||
ejabberd_hooks:run(vcard_set, LServer,
|
||||
[LUser, LServer, VCARD])
|
||||
end.
|
||||
@ -655,500 +563,36 @@ record_to_item(_LServer, #vcard_search{} = R) ->
|
||||
?FIELD(<<"orgunit">>, (R#vcard_search.orgunit))]}.
|
||||
|
||||
search(LServer, Data) ->
|
||||
DBType = gen_mod:db_type(LServer, ?MODULE),
|
||||
MatchSpec = make_matchspec(LServer, Data, DBType),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
AllowReturnAll = gen_mod:get_module_opt(LServer, ?MODULE, allow_return_all,
|
||||
fun(B) when is_boolean(B) -> B end,
|
||||
false),
|
||||
search(LServer, MatchSpec, AllowReturnAll, DBType).
|
||||
|
||||
search(LServer, MatchSpec, AllowReturnAll, mnesia) ->
|
||||
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 gen_mod:get_module_opt(LServer, ?MODULE, matches,
|
||||
MaxMatch = 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, $.).
|
||||
end, ?JUD_MATCHES),
|
||||
Mod:search(LServer, Data, AllowReturnAll, MaxMatch).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
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) ->
|
||||
LUser = jid:nodeprep(User),
|
||||
LServer = jid:nameprep(Server),
|
||||
remove_user(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_user(LUser, LServer).
|
||||
|
||||
remove_user(LUser, LServer, mnesia) ->
|
||||
US = {LUser, LServer},
|
||||
F = fun () ->
|
||||
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}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
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}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #vcard{} = VCard) ->
|
||||
mnesia:dirty_write(VCard);
|
||||
import(_LServer, mnesia, #vcard_search{} = S) ->
|
||||
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.
|
||||
import(LServer, DBType, VCard) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, VCard).
|
||||
|
||||
mod_opt_type(allow_return_all) ->
|
||||
fun (B) when is_boolean(B) -> B end;
|
||||
|
213
src/mod_vcard_mnesia.erl
Normal file
213
src/mod_vcard_mnesia.erl
Normal file
@ -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, $.).
|
151
src/mod_vcard_riak.erl
Normal file
151
src/mod_vcard_riak.erl
Normal file
@ -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{}}.
|
268
src/mod_vcard_sql.erl
Normal file
268
src/mod_vcard_sql.erl
Normal file
@ -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("logger.hrl").
|
||||
|
||||
-include("mod_vcard_xupdate.hrl").
|
||||
-include("jlib.hrl").
|
||||
|
||||
-record(vcard_xupdate, {us = {<<>>, <<>>} :: {binary(), binary()},
|
||||
hash = <<>> :: binary()}).
|
||||
-callback init(binary(), gen_mod:opts()) -> any().
|
||||
-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
|
||||
%%====================================================================
|
||||
|
||||
start(Host, Opts) ->
|
||||
case gen_mod:db_type(Host, Opts) of
|
||||
mnesia ->
|
||||
mnesia:create_table(vcard_xupdate,
|
||||
[{disc_copies, [node()]},
|
||||
{attributes,
|
||||
record_info(fields, vcard_xupdate)}]),
|
||||
update_table();
|
||||
_ -> ok
|
||||
end,
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, Opts),
|
||||
ejabberd_hooks:add(c2s_update_presence, Host, ?MODULE,
|
||||
update_presence, 100),
|
||||
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,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:add_xupdate(LUser, LServer, Hash).
|
||||
|
||||
get_xupdate(LUser, LServer) ->
|
||||
get_xupdate(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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.
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:get_xupdate(LUser, LServer).
|
||||
|
||||
remove_xupdate(LUser, LServer) ->
|
||||
remove_xupdate(LUser, LServer,
|
||||
gen_mod:db_type(LServer, ?MODULE)).
|
||||
|
||||
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).
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:remove_xupdate(LUser, LServer).
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Presence stanza rebuilding
|
||||
@ -184,53 +121,17 @@ build_xphotoel(User, Host) ->
|
||||
attrs = [{<<"xmlns">>, ?NS_VCARD_UPDATE}],
|
||||
children = PhotoEl}.
|
||||
|
||||
vcard_xupdate_schema() ->
|
||||
{record_info(fields, vcard_xupdate), #vcard_xupdate{}}.
|
||||
|
||||
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}].
|
||||
export(LServer) ->
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:export(LServer).
|
||||
|
||||
import(LServer) ->
|
||||
[{<<"select username, hash from vcard_xupdate;">>,
|
||||
fun([LUser, Hash]) ->
|
||||
#vcard_xupdate{us = {LUser, LServer}, hash = Hash}
|
||||
end}].
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
Mod:import(LServer).
|
||||
|
||||
import(_LServer, mnesia, #vcard_xupdate{} = R) ->
|
||||
mnesia:dirty_write(R);
|
||||
import(_LServer, riak, #vcard_xupdate{} = R) ->
|
||||
ejabberd_riak:put(R, vcard_xupdate_schema());
|
||||
import(_, _, _) ->
|
||||
pass.
|
||||
import(LServer, DBType, LA) ->
|
||||
Mod = gen_mod:db_mod(DBType, ?MODULE),
|
||||
Mod:import(LServer, LA).
|
||||
|
||||
mod_opt_type(db_type) -> fun gen_mod:v_db/1;
|
||||
mod_opt_type(_) -> [db_type].
|
||||
|
69
src/mod_vcard_xupdate_mnesia.erl
Normal file
69
src/mod_vcard_xupdate_mnesia.erl
Normal file
@ -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.
|
44
src/mod_vcard_xupdate_riak.erl
Normal file
44
src/mod_vcard_xupdate_riak.erl
Normal file
@ -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{}}.
|
79
src/mod_vcard_xupdate_sql.erl
Normal file
79
src/mod_vcard_xupdate_sql.erl
Normal file
@ -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
Block a user