diff options
Diffstat (limited to 'src/mod_muc_sql.erl')
-rw-r--r-- | src/mod_muc_sql.erl | 357 |
1 files changed, 312 insertions, 45 deletions
diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index ff7ec1ebb..4d7e5e8dd 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -1,23 +1,46 @@ %%%------------------------------------------------------------------- -%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net> -%%% @copyright (C) 2016, Evgeny Khramtsov -%%% @doc -%%% -%%% @end +%%% File : mod_muc_sql.erl +%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net> %%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net> -%%%------------------------------------------------------------------- +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2019 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + -module(mod_muc_sql). --compile([{parse_transform, ejabberd_sql_pt}]). -behaviour(mod_muc). +-behaviour(mod_muc_room). %% API --export([init/2, store_room/4, restore_room/3, forget_room/3, +-export([init/2, store_room/5, 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]). + import/3, export/1]). +-export([register_online_room/4, unregister_online_room/4, find_online_room/3, + get_online_rooms/3, count_online_rooms/2, rsm_supported/0, + register_online_user/4, unregister_online_user/4, + count_online_rooms_by_user/3, get_online_rooms_by_user/3, + get_subscribed_rooms/3]). +-export([set_affiliation/6, set_affiliations/4, get_affiliation/5, + get_affiliations/3, search_affiliation/4]). --include("jlib.hrl"). +-include("jid.hrl"). -include("mod_muc.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). @@ -25,27 +48,87 @@ %%%=================================================================== %%% API %%%=================================================================== -init(_Host, _Opts) -> - ok. +init(Host, Opts) -> + case gen_mod:ram_db_mod(Opts, mod_muc) of + ?MODULE -> + clean_tables(Host); + _ -> + ok + end. -store_room(LServer, Host, Name, Opts) -> - SOpts = jlib:term_to_expr(Opts), +store_room(LServer, Host, Name, Opts, ChangesHints) -> + {Subs, Opts2} = case lists:keytake(subscribers, 1, Opts) of + {value, {subscribers, S}, OptN} -> {S, OptN}; + _ -> {[], Opts} + end, + SOpts = misc:term_to_expr(Opts2), F = fun () -> ?SQL_UPSERT_T( "muc_room", ["!name=%(Name)s", "!host=%(Host)s", - "opts=%(SOpts)s"]) + "server_host=%(LServer)s", + "opts=%(SOpts)s"]), + case ChangesHints of + Changes when is_list(Changes) -> + [change_room(Host, Name, Change) || Change <- Changes]; + _ -> + ejabberd_sql:sql_query_t( + ?SQL("delete from muc_room_subscribers where " + "room=%(Name)s and host=%(Host)s")), + [change_room(Host, Name, {add_subscription, JID, Nick, Nodes}) + || {JID, Nick, Nodes} <- Subs] + end end, ejabberd_sql:sql_transaction(LServer, F). +change_room(Host, Room, {add_subscription, JID, Nick, Nodes}) -> + SJID = jid:encode(JID), + SNodes = misc:term_to_expr(Nodes), + ?SQL_UPSERT_T( + "muc_room_subscribers", + ["!jid=%(SJID)s", + "!host=%(Host)s", + "!room=%(Room)s", + "nick=%(Nick)s", + "nodes=%(SNodes)s"]); +change_room(Host, Room, {del_subscription, JID}) -> + SJID = jid:encode(JID), + ejabberd_sql:sql_query_t(?SQL("delete from muc_room_subscribers where " + "room=%(Room)s and host=%(Host)s and jid=%(SJID)s")); +change_room(Host, Room, Change) -> + ?ERROR_MSG("Unsupported change on room ~ts@~ts: ~p", [Room, Host, Change]). + restore_room(LServer, Host, Name) -> case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(opts)s from muc_room where name=%(Name)s" " and host=%(Host)s")) of {selected, [{Opts}]} -> - mod_muc:opts_to_binary(ejabberd_sql:decode_term(Opts)); + OptsD = ejabberd_sql:decode_term(Opts), + case catch ejabberd_sql:sql_query( + LServer, + ?SQL("select @(jid)s, @(nick)s, @(nodes)s from muc_room_subscribers where room=%(Name)s" + " and host=%(Host)s")) of + {selected, []} -> + OptsR = mod_muc:opts_to_binary(OptsD), + case lists:keymember(subscribers, 1, OptsD) of + true -> + store_room(LServer, Host, Name, OptsR, undefined); + _ -> + ok + end, + OptsR; + {selected, Subs} -> + SubData = lists:map( + fun({Jid, Nick, Nodes}) -> + {jid:decode(Jid), Nick, ejabberd_sql:decode_term(Nodes)} + end, Subs), + Opts2 = lists:keystore(subscribers, 1, OptsD, {subscribers, SubData}), + mod_muc:opts_to_binary(Opts2); + _ -> + error + end; _ -> error end. @@ -54,12 +137,15 @@ forget_room(LServer, Host, Name) -> F = fun () -> ejabberd_sql:sql_query_t( ?SQL("delete from muc_room where name=%(Name)s" + " and host=%(Host)s")), + ejabberd_sql:sql_query_t( + ?SQL("delete from muc_room_subscribers where room=%(Name)s" " and host=%(Host)s")) end, ejabberd_sql:sql_transaction(LServer, F). can_use_nick(LServer, Host, JID, Nick) -> - SJID = jid:to_string(jid:tolower(jid:remove_resource(JID))), + SJID = jid:encode(jid:tolower(jid:remove_resource(JID))), case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(jid)s from muc_registered " @@ -75,19 +161,40 @@ get_rooms(LServer, Host) -> ?SQL("select @(name)s, @(opts)s from muc_room" " where host=%(Host)s")) of {selected, RoomOpts} -> + case catch ejabberd_sql:sql_query( + LServer, + ?SQL("select @(room)s, @(jid)s, @(nick)s, @(nodes)s from muc_room_subscribers" + " where host=%(Host)s")) of + {selected, Subs} -> + SubsD = lists:foldl( + fun({Room, Jid, Nick, Nodes}, Dict) -> + dict:append(Room, {jid:decode(Jid), + Nick, ejabberd_sql:decode_term(Nodes)}, Dict) + end, dict:new(), Subs), lists:map( fun({Room, Opts}) -> + OptsD = ejabberd_sql:decode_term(Opts), + OptsD2 = case {dict:find(Room, SubsD), lists:keymember(subscribers, 1, OptsD)} of + {_, true} -> + store_room(LServer, Host, Room, mod_muc:opts_to_binary(OptsD), undefined), + OptsD; + {{ok, SubsI}, false} -> + lists:keystore(subscribers, 1, OptsD, {subscribers, SubsI}); + _ -> + OptsD + end, #muc_room{name_host = {Room, Host}, - opts = mod_muc:opts_to_binary( - ejabberd_sql:decode_term(Opts))} + opts = mod_muc:opts_to_binary(OptsD2)} end, RoomOpts); - Err -> - ?ERROR_MSG("failed to get rooms: ~p", [Err]), + _Err -> + [] + end; + _Err -> [] end. get_nick(LServer, Host, From) -> - SJID = jid:to_string(jid:tolower(jid:remove_resource(From))), + SJID = jid:encode(jid:tolower(jid:remove_resource(From))), case catch ejabberd_sql:sql_query( LServer, ?SQL("select @(nick)s from muc_registered where" @@ -97,7 +204,7 @@ get_nick(LServer, Host, From) -> end. set_nick(LServer, Host, From, Nick) -> - JID = jid:to_string(jid:tolower(jid:remove_resource(From))), + JID = jid:encode(jid:tolower(jid:remove_resource(From))), F = fun () -> case Nick of <<"">> -> @@ -118,6 +225,7 @@ set_nick(LServer, Host, From, Nick) -> "muc_registered", ["!jid=%(JID)s", "!host=%(Host)s", + "server_host=%(LServer)s", "nick=%(Nick)s"]), ok; true -> @@ -127,17 +235,151 @@ set_nick(LServer, Host, From, Nick) -> end, ejabberd_sql:sql_transaction(LServer, F). +set_affiliation(_ServerHost, _Room, _Host, _JID, _Affiliation, _Reason) -> + {error, not_implemented}. + +set_affiliations(_ServerHost, _Room, _Host, _Affiliations) -> + {error, not_implemented}. + +get_affiliation(_ServerHost, _Room, _Host, _LUser, _LServer) -> + {error, not_implemented}. + +get_affiliations(_ServerHost, _Room, _Host) -> + {error, not_implemented}. + +search_affiliation(_ServerHost, _Room, _Host, _Affiliation) -> + {error, not_implemented}. + +register_online_room(ServerHost, Room, Host, Pid) -> + PidS = misc:encode_pid(Pid), + NodeS = erlang:atom_to_binary(node(Pid), latin1), + case ?SQL_UPSERT(ServerHost, + "muc_online_room", + ["!name=%(Room)s", + "!host=%(Host)s", + "server_host=%(ServerHost)s", + "node=%(NodeS)s", + "pid=%(PidS)s"]) of + ok -> + ok; + Err -> + Err + end. + +unregister_online_room(ServerHost, Room, Host, Pid) -> + %% TODO: report errors + PidS = misc:encode_pid(Pid), + NodeS = erlang:atom_to_binary(node(Pid), latin1), + ejabberd_sql:sql_query( + ServerHost, + ?SQL("delete from muc_online_room where name=%(Room)s and " + "host=%(Host)s and node=%(NodeS)s and pid=%(PidS)s")). + +find_online_room(ServerHost, Room, Host) -> + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("select @(pid)s, @(node)s from muc_online_room where " + "name=%(Room)s and host=%(Host)s")) of + {selected, [{PidS, NodeS}]} -> + try {ok, misc:decode_pid(PidS, NodeS)} + catch _:{bad_node, _} -> error + end; + {selected, []} -> + error; + _Err -> + error + end. + +count_online_rooms(ServerHost, Host) -> + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("select @(count(*))d from muc_online_room " + "where host=%(Host)s")) of + {selected, [{Num}]} -> + Num; + _Err -> + 0 + end. + +get_online_rooms(ServerHost, Host, _RSM) -> + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("select @(name)s, @(pid)s, @(node)s from muc_online_room " + "where host=%(Host)s")) of + {selected, Rows} -> + lists:flatmap( + fun({Room, PidS, NodeS}) -> + try [{Room, Host, misc:decode_pid(PidS, NodeS)}] + catch _:{bad_node, _} -> [] + end + end, Rows); + _Err -> + [] + end. + +rsm_supported() -> + false. + +register_online_user(ServerHost, {U, S, R}, Room, Host) -> + NodeS = erlang:atom_to_binary(node(), latin1), + case ?SQL_UPSERT(ServerHost, "muc_online_users", + ["!username=%(U)s", + "!server=%(S)s", + "!resource=%(R)s", + "!name=%(Room)s", + "!host=%(Host)s", + "server_host=%(ServerHost)s", + "node=%(NodeS)s"]) of + ok -> + ok; + Err -> + Err + end. + +unregister_online_user(ServerHost, {U, S, R}, Room, Host) -> + %% TODO: report errors + ejabberd_sql:sql_query( + ServerHost, + ?SQL("delete from muc_online_users where username=%(U)s and " + "server=%(S)s and resource=%(R)s and name=%(Room)s and " + "host=%(Host)s")). + +count_online_rooms_by_user(ServerHost, U, S) -> + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("select @(count(*))d from muc_online_users where " + "username=%(U)s and server=%(S)s")) of + {selected, [{Num}]} -> + Num; + _Err -> + 0 + end. + +get_online_rooms_by_user(ServerHost, U, S) -> + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("select @(name)s, @(host)s from muc_online_users where " + "username=%(U)s and server=%(S)s")) of + {selected, Rows} -> + Rows; + _Err -> + [] + end. + export(_Server) -> [{muc_room, fun(Host, #muc_room{name_host = {Name, RoomHost}, opts = Opts}) -> case str:suffix(Host, RoomHost) of true -> - SOpts = jlib:term_to_expr(Opts), + SOpts = misc:term_to_expr(Opts), [?SQL("delete from muc_room where name=%(Name)s" " and host=%(RoomHost)s;"), - ?SQL("insert into muc_room(name, host, opts) " - "values (" - "%(Name)s, %(RoomHost)s, %(SOpts)s);")]; + ?SQL_INSERT( + "muc_room", + ["name=%(Name)s", + "host=%(RoomHost)s", + "server_host=%(Host)s", + "opts=%(SOpts)s"])]; false -> [] end @@ -147,33 +389,58 @@ export(_Server) -> nick = Nick}) -> case str:suffix(Host, RoomHost) of true -> - SJID = jid:to_string(jid:make(U, S, <<"">>)), + SJID = jid:encode(jid:make(U, S)), [?SQL("delete from muc_registered where" " jid=%(SJID)s and host=%(RoomHost)s;"), - ?SQL("insert into muc_registered(jid, host, " - "nick) values (" - "%(SJID)s, %(RoomHost)s, %(Nick)s);")]; + ?SQL_INSERT( + "muc_registered", + ["jid=%(SJID)s", + "host=%(RoomHost)s", + "server_host=%(Host)s", + "nick=%(Nick)s"])]; false -> [] end end}]. -import(_LServer) -> - [{<<"select name, host, opts from muc_room;">>, - fun([Name, RoomHost, SOpts]) -> - Opts = mod_muc:opts_to_binary(ejabberd_sql: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(_, _, _) -> + ok. -import(_, _) -> - pass. +get_subscribed_rooms(LServer, Host, Jid) -> + JidS = jid:encode(Jid), + case ejabberd_sql:sql_query( + LServer, + ?SQL("select @(room)s, @(nodes)s from muc_room_subscribers " + "where jid=%(JidS)s and host=%(Host)s")) of + {selected, Subs} -> + {ok, [{jid:make(Room, Host), ejabberd_sql:decode_term(Nodes)} + || {Room, Nodes} <- Subs]}; + _Error -> + {error, db_failure} + end. %%%=================================================================== %%% Internal functions %%%=================================================================== +clean_tables(ServerHost) -> + NodeS = erlang:atom_to_binary(node(), latin1), + ?DEBUG("Cleaning SQL muc_online_room table...", []), + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("delete from muc_online_room where node=%(NodeS)s")) of + {updated, _} -> + ok; + Err1 -> + ?ERROR_MSG("Failed to clean 'muc_online_room' table: ~p", [Err1]), + Err1 + end, + ?DEBUG("Cleaning SQL muc_online_users table...", []), + case ejabberd_sql:sql_query( + ServerHost, + ?SQL("delete from muc_online_users where node=%(NodeS)s")) of + {updated, _} -> + ok; + Err2 -> + ?ERROR_MSG("Failed to clean 'muc_online_users' table: ~p", [Err2]), + Err2 + end. |