aboutsummaryrefslogtreecommitdiff
path: root/src/mod_muc_mnesia.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/mod_muc_mnesia.erl')
-rw-r--r--src/mod_muc_mnesia.erl368
1 files changed, 304 insertions, 64 deletions
diff --git a/src/mod_muc_mnesia.erl b/src/mod_muc_mnesia.erl
index e3ae36978..e5b98fb78 100644
--- a/src/mod_muc_mnesia.erl
+++ b/src/mod_muc_mnesia.erl
@@ -1,39 +1,69 @@
%%%-------------------------------------------------------------------
-%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%% @copyright (C) 2016, Evgeny Khramtsov
-%%% @doc
-%%%
-%%% @end
+%%% File : mod_muc_mnesia.erl
+%%% Author : Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 13 Apr 2016 by Evgeny Khramtsov <ekhramtsov@process-one.net>
-%%%-------------------------------------------------------------------
+%%%
+%%%
+%%% ejabberd, Copyright (C) 2002-2019 ProcessOne
+%%%
+%%% This program is free software; you can redistribute it and/or
+%%% modify it under the terms of the GNU General Public License as
+%%% published by the Free Software Foundation; either version 2 of the
+%%% License, or (at your option) any later version.
+%%%
+%%% This program is distributed in the hope that it will be useful,
+%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
+%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%%% General Public License for more details.
+%%%
+%%% You should have received a copy of the GNU General Public License along
+%%% with this program; if not, write to the Free Software Foundation, Inc.,
+%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+%%%
+%%%----------------------------------------------------------------------
+
-module(mod_muc_mnesia).
-behaviour(mod_muc).
+-behaviour(mod_muc_room).
%% API
--export([init/2, import/2, store_room/4, restore_room/3, forget_room/3,
+-export([init/2, import/3, store_room/5, restore_room/3, forget_room/3,
can_use_nick/4, get_rooms/2, get_nick/3, set_nick/4]).
+-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]).
+-export([set_affiliation/6, set_affiliations/4, get_affiliation/5,
+ get_affiliations/3, search_affiliation/4]).
+%% gen_server callbacks
+-export([start_link/2, init/1, handle_cast/2, handle_call/3, handle_info/2,
+ terminate/2, code_change/3]).
+-export([need_transform/1, transform/1]).
-include("mod_muc.hrl").
-include("logger.hrl").
+-include("xmpp.hrl").
+-include_lib("stdlib/include/ms_transform.hrl").
+
+-record(state, {}).
%%%===================================================================
%%% 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) ->
+init(Host, Opts) ->
+ Spec = {?MODULE, {?MODULE, start_link, [Host, Opts]},
+ transient, 5000, worker, [?MODULE]},
+ case supervisor:start_child(ejabberd_backend_sup, Spec) of
+ {ok, _Pid} -> ok;
+ Err -> Err
+ end.
+
+start_link(Host, Opts) ->
+ Name = gen_mod:get_module_proc(Host, ?MODULE),
+ gen_server:start_link({local, Name}, ?MODULE, [Host, Opts], []).
+
+store_room(_LServer, Host, Name, Opts, _) ->
F = fun () ->
mnesia:write(#muc_room{name_host = {Name, Host},
opts = Opts})
@@ -113,51 +143,261 @@ set_nick(_LServer, Host, From, Nick) ->
end,
mnesia:transaction(F).
-import(_LServer, #muc_room{} = R) ->
- mnesia:dirty_write(R);
-import(_LServer, #muc_registered{} = R) ->
- mnesia:dirty_write(R).
+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) ->
+ F = fun() ->
+ mnesia:write(
+ #muc_online_room{name_host = {Room, Host}, pid = Pid})
+ end,
+ mnesia:transaction(F).
+
+unregister_online_room(_ServerHost, Room, Host, Pid) ->
+ F = fun () ->
+ mnesia:delete_object(
+ #muc_online_room{name_host = {Room, Host}, pid = Pid})
+ end,
+ mnesia:transaction(F).
+
+find_online_room(_ServerHost, Room, Host) ->
+ find_online_room(Room, Host).
+
+find_online_room(Room, Host) ->
+ case mnesia:dirty_read(muc_online_room, {Room, Host}) of
+ [] -> error;
+ [#muc_online_room{pid = Pid}] -> {ok, Pid}
+ end.
+
+count_online_rooms(_ServerHost, Host) ->
+ ets:select_count(
+ muc_online_room,
+ ets:fun2ms(
+ fun(#muc_online_room{name_host = {_, H}}) ->
+ H == Host
+ end)).
+
+get_online_rooms(_ServerHost, Host,
+ #rsm_set{max = Max, 'after' = After, before = undefined})
+ when is_binary(After), After /= <<"">> ->
+ lists:reverse(get_online_rooms(next, {After, Host}, Host, 0, Max, []));
+get_online_rooms(_ServerHost, Host,
+ #rsm_set{max = Max, 'after' = undefined, before = Before})
+ when is_binary(Before), Before /= <<"">> ->
+ get_online_rooms(prev, {Before, Host}, Host, 0, Max, []);
+get_online_rooms(_ServerHost, Host,
+ #rsm_set{max = Max, 'after' = undefined, before = <<"">>}) ->
+ get_online_rooms(last, {<<"">>, Host}, Host, 0, Max, []);
+get_online_rooms(_ServerHost, Host, #rsm_set{max = Max}) ->
+ lists:reverse(get_online_rooms(first, {<<"">>, Host}, Host, 0, Max, []));
+get_online_rooms(_ServerHost, Host, undefined) ->
+ mnesia:dirty_select(
+ muc_online_room,
+ ets:fun2ms(
+ fun(#muc_online_room{name_host = {Name, H}, pid = Pid})
+ when H == Host -> {Name, Host, Pid}
+ end)).
+
+-spec get_online_rooms(prev | next | last | first,
+ {binary(), binary()}, binary(),
+ non_neg_integer(), non_neg_integer() | undefined,
+ [{binary(), binary(), pid()}]) ->
+ [{binary(), binary(), pid()}].
+get_online_rooms(_Action, _Key, _Host, Count, Max, Items) when Count >= Max ->
+ Items;
+get_online_rooms(Action, Key, Host, Count, Max, Items) ->
+ Call = fun() ->
+ case Action of
+ prev -> mnesia:dirty_prev(muc_online_room, Key);
+ next -> mnesia:dirty_next(muc_online_room, Key);
+ last -> mnesia:dirty_last(muc_online_room);
+ first -> mnesia:dirty_first(muc_online_room)
+ end
+ end,
+ NewAction = case Action of
+ last -> prev;
+ first -> next;
+ _ -> Action
+ end,
+ try Call() of
+ '$end_of_table' ->
+ Items;
+ {Room, Host} = NewKey ->
+ case find_online_room(Room, Host) of
+ {ok, Pid} ->
+ get_online_rooms(NewAction, NewKey, Host,
+ Count + 1, Max, [{Room, Host, Pid}|Items]);
+ error ->
+ get_online_rooms(NewAction, NewKey, Host,
+ Count, Max, Items)
+ end;
+ NewKey ->
+ get_online_rooms(NewAction, NewKey, Host, Count, Max, Items)
+ catch _:{aborted, {badarg, _}} ->
+ Items
+ end.
+
+rsm_supported() ->
+ true.
+
+register_online_user(_ServerHost, {U, S, R}, Room, Host) ->
+ ets:insert(muc_online_users,
+ #muc_online_users{us = {U, S}, resource = R,
+ room = Room, host = Host}).
+
+unregister_online_user(_ServerHost, {U, S, R}, Room, Host) ->
+ ets:delete_object(muc_online_users,
+ #muc_online_users{us = {U, S}, resource = R,
+ room = Room, host = Host}).
+
+count_online_rooms_by_user(ServerHost, U, S) ->
+ MucHost = hd(gen_mod:get_module_opt_hosts(ServerHost, mod_muc)),
+ ets:select_count(
+ muc_online_users,
+ ets:fun2ms(
+ fun(#muc_online_users{us = {U1, S1}, host = Host}) ->
+ U == U1 andalso S == S1 andalso MucHost == Host
+ end)).
+
+get_online_rooms_by_user(ServerHost, U, S) ->
+ MucHost = hd(gen_mod:get_module_opt_hosts(ServerHost, mod_muc)),
+ ets:select(
+ muc_online_users,
+ ets:fun2ms(
+ fun(#muc_online_users{us = {U1, S1}, room = Room, host = Host})
+ when U == U1 andalso S == S1 andalso MucHost == Host -> {Room, Host}
+ end)).
+
+import(_LServer, <<"muc_room">>,
+ [Name, RoomHost, SOpts, _TimeStamp]) ->
+ Opts = mod_muc:opts_to_binary(ejabberd_sql:decode_term(SOpts)),
+ mnesia:dirty_write(
+ #muc_room{name_host = {Name, RoomHost},
+ opts = Opts});
+import(_LServer, <<"muc_registered">>,
+ [J, RoomHost, Nick, _TimeStamp]) ->
+ #jid{user = U, server = S} = jid:decode(J),
+ mnesia:dirty_write(
+ #muc_registered{us_host = {{U, S}, RoomHost},
+ nick = Nick}).
+
+%%%===================================================================
+%%% gen_server callbacks
+%%%===================================================================
+init([_Host, Opts]) ->
+ MyHosts = mod_muc_opt:hosts(Opts),
+ case gen_mod:db_mod(Opts, mod_muc) of
+ ?MODULE ->
+ ejabberd_mnesia:create(?MODULE, muc_room,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, muc_room)}]),
+ ejabberd_mnesia:create(?MODULE, muc_registered,
+ [{disc_copies, [node()]},
+ {attributes,
+ record_info(fields, muc_registered)},
+ {index, [nick]}]);
+ _ ->
+ ok
+ end,
+ case gen_mod:ram_db_mod(Opts, mod_muc) of
+ ?MODULE ->
+ ejabberd_mnesia:create(?MODULE, muc_online_room,
+ [{ram_copies, [node()]},
+ {type, ordered_set},
+ {attributes, record_info(fields, muc_online_room)}]),
+ catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
+ lists:foreach(
+ fun(MyHost) ->
+ clean_table_from_bad_node(node(), MyHost)
+ end, MyHosts),
+ mnesia:subscribe(system);
+ _ ->
+ ok
+ end,
+ {ok, #state{}}.
+
+handle_call(Request, From, State) ->
+ ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]),
+ {noreply, State}.
+
+handle_cast(Msg, State) ->
+ ?WARNING_MSG("Unexpected cast: ~p", [Msg]),
+ {noreply, State}.
+
+handle_info({mnesia_system_event, {mnesia_down, Node}}, State) ->
+ clean_table_from_bad_node(Node),
+ {noreply, State};
+handle_info({mnesia_system_event, {mnesia_up, _Node}}, State) ->
+ {noreply, State};
+handle_info(Info, State) ->
+ ?WARNING_MSG("Unexpected info: ~p", [Info]),
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
%%%===================================================================
%%% 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.
+clean_table_from_bad_node(Node) ->
+ F = fun() ->
+ Es = mnesia:select(
+ muc_online_room,
+ [{#muc_online_room{pid = '$1', _ = '_'},
+ [{'==', {node, '$1'}, Node}],
+ ['$_']}]),
+ lists:foreach(fun(E) ->
+ mnesia:delete_object(E)
+ end, Es)
+ end,
+ mnesia:async_dirty(F).
-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.
+clean_table_from_bad_node(Node, Host) ->
+ F = fun() ->
+ Es = mnesia:select(
+ muc_online_room,
+ [{#muc_online_room{pid = '$1',
+ name_host = {'_', Host},
+ _ = '_'},
+ [{'==', {node, '$1'}, Node}],
+ ['$_']}]),
+ lists:foreach(fun(E) ->
+ mnesia:delete_object(E)
+ end, Es)
+ end,
+ mnesia:async_dirty(F).
+
+need_transform({muc_room, {N, H}, _})
+ when is_list(N) orelse is_list(H) ->
+ ?INFO_MSG("Mnesia table 'muc_room' will be converted to binary", []),
+ true;
+need_transform({muc_registered, {{U, S}, H}, Nick})
+ when is_list(U) orelse is_list(S) orelse is_list(H) orelse is_list(Nick) ->
+ ?INFO_MSG("Mnesia table 'muc_registered' will be converted to binary", []),
+ true;
+need_transform(_) ->
+ false.
+
+transform(#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)};
+transform(#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)}.