diff options
Diffstat (limited to 'src/mod_muc/mod_muc.erl')
-rw-r--r-- | src/mod_muc/mod_muc.erl | 1985 |
1 files changed, 987 insertions, 998 deletions
diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl index 1f40ca0a4..09fe95bee 100644 --- a/src/mod_muc/mod_muc.erl +++ b/src/mod_muc/mod_muc.erl @@ -25,147 +25,131 @@ %%%---------------------------------------------------------------------- -module(mod_muc). + -author('alexey@process-one.net'). -behaviour(gen_server). + -behaviour(gen_mod). %% API --export([start_link/2, - start/2, - stop/1, - room_destroyed/4, - store_room/4, - restore_room/3, - forget_room/3, - create_room/5, - process_iq_disco_items/4, - broadcast_service_message/2, - register_room/3, - node_up/1, - node_down/1, - migrate/3, - get_vh_rooms/1, - is_broadcasted/1, - moderate_room_history/2, - persist_recent_messages/1, - can_use_nick/4]). +-export([start_link/2, start/2, stop/1, export/1, + room_destroyed/4, store_room/4, restore_room/3, + forget_room/3, create_room/5, process_iq_disco_items/4, + broadcast_service_message/2, register_room/3, node_up/1, + node_down/1, migrate/3, get_vh_rooms/1, + is_broadcasted/1, moderate_room_history/2, + persist_recent_messages/1, can_use_nick/4]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +-export([init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). + -include("jlib.hrl"). +-record(muc_room, {name_host = {<<"">>, <<"">>} :: {binary(), binary()} | + {'_', binary()}, + opts = [] :: list() | '_'}). + +-record(muc_online_room, + {name_host = {<<"">>, <<"">>} :: {binary(), binary()} | '$1', + pid = self() :: pid() | '$2' | '_'}). --record(muc_room, {name_host, opts}). --record(muc_online_room, {name_host, pid}). --record(muc_registered, {us_host, nick}). +-record(muc_registered, + {us_host = {{<<"">>, <<"">>}, <<"">>} :: {{binary(), binary()}, binary()} | '$1', + nick = <<"">> :: binary()}). --record(state, {host, - server_host, - access, - history_size, - persist_history, - default_room_opts, - room_shaper}). +-record(state, + {host = <<"">> :: binary(), + server_host = <<"">> :: binary(), + access = {none, none, none, none} :: {atom(), atom(), atom(), atom()}, + history_size = 20 :: non_neg_integer(), + persist_history = false :: boolean(), + default_room_opts = [] :: list(), + room_shaper = none :: shaper:shaper()}). -define(PROCNAME, ejabberd_mod_muc). -%%==================================================================== -%% API -%%==================================================================== -%%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} -%% Description: Starts the server -%%-------------------------------------------------------------------- start_link(Host, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). + gen_server:start_link({local, Proc}, ?MODULE, + [Host, Opts], []). start(Host, Opts) -> start_supervisor(Host), Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - ChildSpec = - {Proc, - {?MODULE, start_link, [Host, Opts]}, - temporary, - 1000, - worker, - [?MODULE]}, + ChildSpec = {Proc, {?MODULE, start_link, [Host, Opts]}, + temporary, 1000, worker, [?MODULE]}, supervisor:start_child(ejabberd_sup, ChildSpec). stop(Host) -> - %% if compiled with no transient supervisor, we need to manually shutdown - %% the rooms to give them a chance to store persistent messages to DB - Rooms = shutdown_rooms(Host), + Rooms = shutdown_rooms(Host), stop_supervisor(Host), Proc = gen_mod:get_module_proc(Host, ?PROCNAME), gen_server:call(Proc, stop), supervisor:delete_child(ejabberd_sup, Proc), - {wait, Rooms}. %%wait for rooms shutdown before stopping ejabberd + {wait, Rooms}. shutdown_rooms(Host) -> - MyHost = gen_mod:get_module_opt_host(Host, mod_muc, "conference.@HOST@"), + MyHost = gen_mod:get_module_opt_host(Host, mod_muc, + <<"conference.@HOST@">>), Rooms = mnesia:dirty_select(muc_online_room, - [{#muc_online_room{name_host = '$1', pid = '$2'}, - [{'==', {element, 2, '$1'}, MyHost}], - ['$2']}]), - [Pid ! 'shutdown' || Pid <- Rooms], + [{#muc_online_room{name_host = '$1', + pid = '$2'}, + [{'==', {element, 2, '$1'}, MyHost}], + ['$2']}]), + [Pid ! shutdown || Pid <- Rooms], Rooms. -%% Returns {RoomsPersisted, MessagesPersisted} persist_recent_messages(Host) -> - MyHost = gen_mod:get_module_opt_host(Host, mod_muc, "conference.@HOST@"), + MyHost = gen_mod:get_module_opt_host(Host, mod_muc, + <<"conference.@HOST@">>), Rooms = mnesia:dirty_select(muc_online_room, - [{#muc_online_room{name_host = '$1', pid = '$2'}, - [{'==', {element, 2, '$1'}, MyHost}], - ['$2']}]), - lists:foldl(fun(Pid, {NRooms, Messages}) -> - case mod_muc_room:persist_recent_messages(Pid) of - {ok, {persisted, N}} -> {NRooms +1, Messages +N}; - {ok, not_persistent} -> {NRooms, Messages} - end end, {0, 0}, Rooms). + [{#muc_online_room{name_host = '$1', + pid = '$2'}, + [{'==', {element, 2, '$1'}, MyHost}], + ['$2']}]), + lists:foldl(fun (Pid, {NRooms, Messages}) -> + case mod_muc_room:persist_recent_messages(Pid) of + {ok, {persisted, N}} -> {NRooms + 1, Messages + N}; + {ok, not_persistent} -> {NRooms, Messages} + end + end, + {0, 0}, Rooms). moderate_room_history(RoomStr, Nick) -> - Room = jlib:string_to_jid(RoomStr), - Name = Room#jid.luser, - Host = Room#jid.lserver, - case mnesia:dirty_read(muc_online_room, {Name, Host}) of - [] -> - {error, not_found}; - [R] -> - Pid = R#muc_online_room.pid, - mod_muc_room:moderate_room_history(Pid, Nick) - end. - -%% This function is called by a room in three situations: -%% A) The owner of the room destroyed it -%% B) The only participant of a temporary room leaves it -%% C) mod_muc:stop was called, and each room is being terminated -%% In this case, the mod_muc process died before the room processes -%% So the message sending must be catched + Room = jlib:string_to_jid(RoomStr), + Name = Room#jid.luser, + Host = Room#jid.lserver, + case mnesia:dirty_read(muc_online_room, {Name, Host}) of + [] -> {error, not_found}; + [R] -> + Pid = R#muc_online_room.pid, + mod_muc_room:moderate_room_history(Pid, Nick) + end. + room_destroyed(Host, Room, Pid, ServerHost) -> catch gen_mod:get_module_proc(ServerHost, ?PROCNAME) ! - {room_destroyed, {Room, Host}, Pid}, + {room_destroyed, {Room, Host}, Pid}, ok. -%% @doc Create a room. -%% If Opts = default, the default room options are used. -%% Else use the passed options as defined in mod_muc_room. create_room(Host, Name, From, Nick, Opts) -> Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - RoomHost = gen_mod:get_module_opt_host(Host, ?MODULE, "conference.@HOST@"), + RoomHost = gen_mod:get_module_opt_host(Host, ?MODULE, + <<"conference.@HOST@">>), Node = get_node({Name, RoomHost}), - gen_server:call({Proc, Node}, {create, Name, From, Nick, Opts}). + gen_server:call({Proc, Node}, + {create, Name, From, Nick, Opts}). store_room(ServerHost, Host, Name, Opts) -> LServer = jlib:nameprep(ServerHost), - store_room(LServer, Host, Name, Opts, gen_mod:db_type(LServer, ?MODULE)). + store_room(LServer, Host, Name, Opts, + gen_mod:db_type(LServer, ?MODULE)). store_room(_LServer, Host, Name, Opts, mnesia) -> - F = fun() -> + F = fun () -> mnesia:write(#muc_room{name_host = {Name, Host}, opts = Opts}) end, @@ -174,143 +158,132 @@ 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, "'"]) + F = fun () -> + odbc_queries:update_t(<<"muc_room">>, + [<<"name">>, <<"host">>, <<"opts">>], + [SName, SHost, SOpts], + [<<"name='">>, SName, <<"' and host='">>, + SHost, <<"'">>]) end, ejabberd_odbc:sql_transaction(LServer, F). restore_room(ServerHost, Host, Name) -> LServer = jlib:nameprep(ServerHost), - restore_room(LServer, Host, Name, gen_mod:db_type(LServer, ?MODULE)). + 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 + [#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}]} -> - ejabberd_odbc:decode_term(Opts); - _ -> - error + case catch ejabberd_odbc:sql_query(LServer, + [<<"select opts from muc_room where name='">>, + SName, <<"' and host='">>, SHost, + <<"';">>]) + of + {selected, [<<"opts">>], [[Opts]]} -> + opts_to_binary(ejabberd_odbc:decode_term(Opts)); + _ -> error end. forget_room(ServerHost, Host, Name) -> LServer = jlib:nameprep(ServerHost), - forget_room(LServer, Host, Name, gen_mod:db_type(LServer, ?MODULE)). + forget_room(LServer, Host, Name, + gen_mod:db_type(LServer, ?MODULE)). forget_room(_LServer, Host, Name, mnesia) -> - F = fun() -> - mnesia:delete({muc_room, {Name, Host}}) + F = fun () -> mnesia:delete({muc_room, {Name, Host}}) end, mnesia:transaction(F); forget_room(LServer, Host, Name, odbc) -> 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, "';"]) + F = fun () -> + ejabberd_odbc:sql_query_t([<<"delete from muc_room where name='">>, + SName, <<"' and host='">>, SHost, + <<"';">>]) end, ejabberd_odbc:sql_transaction(LServer, F). -process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) -> +process_iq_disco_items(Host, From, To, + #iq{lang = Lang} = IQ) -> Rsm = jlib:rsm_decode(IQ), Res = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", ?NS_DISCO_ITEMS}], - iq_disco_items(Host, From, Lang, Rsm)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)). - -can_use_nick(_ServerHost, _Host, _JID, "") -> - false; + sub_el = + [#xmlel{name = <<"query">>, + attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}], + children = iq_disco_items(Host, From, Lang, Rsm)}]}, + ejabberd_router:route(To, From, jlib:iq_to_xml(Res)). + +can_use_nick(_ServerHost, _Host, _JID, <<"">>) -> false; can_use_nick(ServerHost, Host, JID, Nick) -> LServer = jlib:nameprep(ServerHost), - can_use_nick(LServer, Host, JID, Nick, gen_mod:db_type(LServer, ?MODULE)). + can_use_nick(LServer, Host, JID, Nick, + gen_mod:db_type(LServer, ?MODULE)). can_use_nick(_LServer, Host, JID, Nick, mnesia) -> {LUser, LServer, _} = jlib: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 + 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, odbc) -> - SJID = jlib:jid_to_string( - jlib:jid_tolower( - jlib:jid_remove_resource(JID))), + SJID = + jlib:jid_to_string(jlib:jid_tolower(jlib: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 + 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. migrate(_Node, _UpOrDown, After) -> - Rs = mnesia:dirty_select( - muc_online_room, - [{#muc_online_room{name_host = '$1', pid = '$2', _ = '_'}, - [], - ['$$']}]), - lists:foreach( - fun([NameHost, Pid]) -> - case get_node(NameHost) of - Node when Node /= node() -> - mod_muc_room:migrate(Pid, Node, random:uniform(After)); - _ -> - ok - end - end, Rs). + Rs = mnesia:dirty_select(muc_online_room, + [{#muc_online_room{name_host = '$1', pid = '$2', + _ = '_'}, + [], ['$$']}]), + lists:foreach(fun ([NameHost, Pid]) -> + case get_node(NameHost) of + Node when Node /= node() -> + mod_muc_room:migrate(Pid, Node, + random:uniform(After)); + _ -> ok + end + end, + Rs). node_up(_Node) -> copy_rooms(mnesia:dirty_first(muc_online_room)). node_down(Node) when Node == node() -> copy_rooms(mnesia:dirty_first(muc_online_room)); -node_down(_) -> - ok. +node_down(_) -> ok. -copy_rooms('$end_of_table') -> - ok; +copy_rooms('$end_of_table') -> ok; copy_rooms(Key) -> case mnesia:dirty_read(muc_online_room, Key) of - [#muc_online_room{name_host = NameHost} = Room] -> - case get_node_new(NameHost) of - Node when node() /= Node -> - rpc:cast(Node, mnesia, dirty_write, [Room]); - _ -> - ok - end; - _ -> - ok + [#muc_online_room{name_host = NameHost} = Room] -> + case get_node_new(NameHost) of + Node when node() /= Node -> + rpc:cast(Node, mnesia, dirty_write, [Room]); + _ -> ok + end; + _ -> ok end, copy_rooms(mnesia:dirty_next(muc_online_room, Key)). @@ -318,1015 +291,1031 @@ copy_rooms(Key) -> %% gen_server callbacks %%==================================================================== -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- init([Host, Opts]) -> - MyHost = gen_mod:get_opt_host(Host, Opts, "conference.@HOST@"), + MyHost = gen_mod:get_opt_host(Host, Opts, + <<"conference.@HOST@">>), case gen_mod:db_type(Opts) of - mnesia -> - update_muc_online_table(), - update_tables(MyHost), - 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)}]), - mnesia:add_table_index(muc_registered, nick); - _ -> - ok + 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(), + mnesia:add_table_index(muc_registered, nick); + _ -> ok end, mnesia:create_table(muc_online_room, - [{ram_copies, [node()]}, - {local_content, true}, + [{ram_copies, [node()]}, {local_content, true}, {attributes, record_info(fields, muc_online_room)}]), - mnesia:add_table_copy(muc_online_room, node(), ram_copies), - catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]), + mnesia:add_table_copy(muc_online_room, node(), + ram_copies), + catch ets:new(muc_online_users, + [bag, named_table, public, {keypos, 2}]), mnesia:subscribe(system), - Access = gen_mod:get_opt(access, Opts, all), - AccessCreate = gen_mod:get_opt(access_create, Opts, all), - AccessAdmin = gen_mod:get_opt(access_admin, Opts, none), - AccessPersistent = gen_mod:get_opt(access_persistent, Opts, all), - HistorySize = gen_mod:get_opt(history_size, Opts, 20), - PersistHistory = gen_mod:get_opt(persist_history, Opts, false), - DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, []), - RoomShaper = gen_mod:get_opt(room_shaper, Opts, none), + Access = gen_mod:get_opt(access, Opts, + fun(A) when is_atom(A) -> A end, all), + AccessCreate = gen_mod:get_opt(access_create, Opts, + fun(A) when is_atom(A) -> A end, all), + AccessAdmin = gen_mod:get_opt(access_admin, Opts, + fun(A) when is_atom(A) -> A end, + none), + AccessPersistent = gen_mod:get_opt(access_persistent, Opts, + fun(A) when is_atom(A) -> A end, + all), + HistorySize = gen_mod:get_opt(history_size, Opts, + fun(I) when is_integer(I), I>=0 -> I end, + 20), + PersistHistory = gen_mod:get_opt(persist_history, Opts, + fun(B) when is_boolean(B) -> B end, + false), + DefRoomOpts = gen_mod:get_opt(default_room_options, Opts, + fun(L) when is_list(L) -> L end, + []), + RoomShaper = gen_mod:get_opt(room_shaper, Opts, + fun(A) when is_atom(A) -> A end, + none), ejabberd_router:register_route(MyHost), ejabberd_hooks:add(node_up, ?MODULE, node_up, 100), ejabberd_hooks:add(node_down, ?MODULE, node_down, 100), - ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, 100), + ejabberd_hooks:add(node_hash_update, ?MODULE, migrate, + 100), load_permanent_rooms(MyHost, Host, {Access, AccessCreate, AccessAdmin, AccessPersistent}, - HistorySize, - PersistHistory, - RoomShaper), - {ok, #state{host = MyHost, - server_host = Host, - access = {Access, AccessCreate, AccessAdmin, AccessPersistent}, - default_room_opts = DefRoomOpts, - history_size = HistorySize, - persist_history = PersistHistory, - room_shaper = RoomShaper}}. - -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- + HistorySize, PersistHistory, RoomShaper), + {ok, + #state{host = MyHost, server_host = Host, + access = + {Access, AccessCreate, AccessAdmin, AccessPersistent}, + default_room_opts = DefRoomOpts, + history_size = HistorySize, + persist_history = PersistHistory, + room_shaper = RoomShaper}}. + handle_call(stop, _From, State) -> {stop, normal, ok, State}; - -handle_call({create, Room, From, Nick, Opts}, - _From, - #state{host = Host, - server_host = ServerHost, - access = Access, - default_room_opts = DefOpts, +handle_call({create, Room, From, Nick, Opts}, _From, + #state{host = Host, server_host = ServerHost, + access = Access, default_room_opts = DefOpts, history_size = HistorySize, persist_history = PersistHistory, - room_shaper = RoomShaper} = State) -> + room_shaper = RoomShaper} = + State) -> ?DEBUG("MUC: create new room '~s'~n", [Room]), NewOpts = case Opts of - default -> DefOpts; - _ -> Opts + default -> DefOpts; + _ -> Opts end, - {ok, Pid} = mod_muc_room:start( - Host, ServerHost, Access, - Room, HistorySize, PersistHistory, - RoomShaper, From, - Nick, NewOpts), + {ok, Pid} = mod_muc_room:start(Host, ServerHost, Access, + Room, HistorySize, PersistHistory, + RoomShaper, From, Nick, NewOpts), register_room(Host, Room, Pid), {reply, ok, State}. -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- +handle_cast(_Msg, State) -> {noreply, State}. + handle_info({route, From, To, Packet}, - #state{host = Host, - server_host = ServerHost, - access = Access, - default_room_opts = DefRoomOpts, + #state{host = Host, server_host = ServerHost, + access = Access, default_room_opts = DefRoomOpts, history_size = HistorySize, persist_history = PersistHistory, - room_shaper = RoomShaper} = State) -> + room_shaper = RoomShaper} = + State) -> {U, S, _} = jlib:jid_tolower(To), case get_node({U, S}) of - Node when Node == node() -> - case catch do_route(Host, ServerHost, Access, HistorySize, PersistHistory, - RoomShaper, From, To, Packet, DefRoomOpts) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]); - _ -> - ok - end; - Node -> - Proc = gen_mod:get_module_proc(ServerHost, ?PROCNAME), - {Proc, Node} ! {route, From, To, Packet} + Node when Node == node() -> + case catch do_route(Host, ServerHost, Access, + HistorySize, PersistHistory, RoomShaper, From, To, + Packet, DefRoomOpts) + of + {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]); + _ -> ok + end; + Node -> + Proc = gen_mod:get_module_proc(ServerHost, ?PROCNAME), + {Proc, Node} ! {route, From, To, Packet} end, {noreply, State}; handle_info({room_destroyed, RoomHost, Pid}, State) -> - F = fun() -> - mnesia:delete_object(#muc_online_room{name_host = RoomHost, + F = fun () -> + mnesia:delete_object(#muc_online_room{name_host = + RoomHost, pid = Pid}) end, mnesia:async_dirty(F), case get_node_new(RoomHost) of - Node when Node /= node() -> - rpc:cast(Node, mnesia, dirty_delete_object, - [#muc_online_room{name_host = RoomHost, - pid = Pid}]); - _ -> - ok + Node when Node /= node() -> + rpc:cast(Node, mnesia, dirty_delete_object, + [#muc_online_room{name_host = RoomHost, pid = Pid}]); + _ -> ok end, {noreply, State}; -handle_info(_Info, State) -> - {noreply, State}. - -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- +handle_info(_Info, State) -> {noreply, State}. + terminate(_Reason, State) -> ejabberd_hooks:delete(node_up, ?MODULE, node_up, 100), - ejabberd_hooks:delete(node_down, ?MODULE, node_down, 100), - ejabberd_hooks:delete(node_hash_update, ?MODULE, migrate, 100), + ejabberd_hooks:delete(node_down, ?MODULE, node_down, + 100), + ejabberd_hooks:delete(node_hash_update, ?MODULE, + migrate, 100), ejabberd_router:unregister_route(State#state.host), ok. -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. +code_change(_OldVsn, State, _Extra) -> {ok, State}. -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- start_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup), - ChildSpec = - {Proc, - {ejabberd_tmp_sup, start_link, - [Proc, mod_muc_room]}, - permanent, - infinity, - supervisor, - [ejabberd_tmp_sup]}, + Proc = gen_mod:get_module_proc(Host, + ejabberd_mod_muc_sup), + ChildSpec = {Proc, + {ejabberd_tmp_sup, start_link, [Proc, mod_muc_room]}, + permanent, infinity, supervisor, [ejabberd_tmp_sup]}, supervisor:start_child(ejabberd_sup, ChildSpec). stop_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup), + Proc = gen_mod:get_module_proc(Host, + ejabberd_mod_muc_sup), supervisor:terminate_child(ejabberd_sup, Proc), supervisor:delete_child(ejabberd_sup, Proc). -do_route(Host, ServerHost, Access, HistorySize, PersistHistory, RoomShaper, - From, To, Packet, DefRoomOpts) -> - {AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent} = Access, +do_route(Host, ServerHost, Access, HistorySize, + PersistHistory, RoomShaper, From, To, Packet, + DefRoomOpts) -> + {AccessRoute, _AccessCreate, _AccessAdmin, + _AccessPersistent} = + Access, case acl:match_rule(ServerHost, AccessRoute, From) of - allow -> - do_route1(Host, ServerHost, Access, HistorySize, PersistHistory, RoomShaper, - From, To, Packet, DefRoomOpts); - _ -> - {xmlelement, _Name, Attrs, _Els} = Packet, - Lang = xml:get_attr_s("xml:lang", Attrs), - ErrText = "Access denied by service policy", - Err = jlib:make_error_reply(Packet, - ?ERRT_FORBIDDEN(Lang, ErrText)), - ejabberd_router:route_error(To, From, Err, Packet) + allow -> + do_route1(Host, ServerHost, Access, HistorySize, + PersistHistory, RoomShaper, From, To, Packet, + DefRoomOpts); + _ -> + #xmlel{attrs = Attrs} = Packet, + Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), + ErrText = <<"Access denied by service policy">>, + Err = jlib:make_error_reply(Packet, + ?ERRT_FORBIDDEN(Lang, ErrText)), + ejabberd_router:route_error(To, From, Err, Packet) end. - -do_route1(Host, ServerHost, Access, HistorySize, PersistHistory, RoomShaper, - From, To, Packet, DefRoomOpts) -> - {_AccessRoute, AccessCreate, AccessAdmin, _AccessPersistent} = Access, +do_route1(Host, ServerHost, Access, HistorySize, + PersistHistory, RoomShaper, From, To, Packet, + DefRoomOpts) -> + {_AccessRoute, AccessCreate, AccessAdmin, + _AccessPersistent} = + Access, {Room, _, Nick} = jlib:jid_tolower(To), - {xmlelement, Name, Attrs, _Els} = Packet, + #xmlel{name = Name, attrs = Attrs} = Packet, case Room of - "" -> - case Nick of - "" -> - case Name of - "iq" -> - case jlib:iq_query_info(Packet) of - #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, - sub_el = _SubEl, lang = Lang} = IQ -> - Info = ejabberd_hooks:run_fold( - disco_info, ServerHost, [], - [ServerHost, ?MODULE, "", ""]), - Res = IQ#iq{type = result, - sub_el = [{xmlelement, "query", - [{"xmlns", XMLNS}], - iq_disco_info(Lang) - ++Info}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{type = get, - xmlns = ?NS_DISCO_ITEMS} = IQ -> - spawn(?MODULE, - process_iq_disco_items, - [Host, From, To, IQ]); - #iq{type = get, - xmlns = ?NS_REGISTER = XMLNS, - lang = Lang, - sub_el = _SubEl} = IQ -> - Res = IQ#iq{type = result, - sub_el = - [{xmlelement, "query", - [{"xmlns", XMLNS}], - iq_get_register_info( - ServerHost, Host, From, Lang)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{type = set, - xmlns = ?NS_REGISTER = XMLNS, - lang = Lang, - sub_el = SubEl} = IQ -> - case process_iq_register_set( - ServerHost, Host, From, SubEl, Lang) of - {result, IQRes} -> - Res = IQ#iq{type = result, - sub_el = - [{xmlelement, "query", - [{"xmlns", XMLNS}], - IQRes}]}, - ejabberd_router:route( - To, From, jlib:iq_to_xml(Res)); - {error, Error} -> - Err = jlib:make_error_reply( - Packet, Error), - ejabberd_router:route( - To, From, Err) - end; - #iq{type = get, - xmlns = ?NS_VCARD = XMLNS, - lang = Lang, - sub_el = _SubEl} = IQ -> - Res = IQ#iq{type = result, - sub_el = - [{xmlelement, "vCard", - [{"xmlns", XMLNS}], - iq_get_vcard(Lang)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{type = get, - xmlns = ?NS_MUC_UNIQUE - } = IQ -> - Res = IQ#iq{type = result, - sub_el = - [{xmlelement, "unique", - [{"xmlns", ?NS_MUC_UNIQUE}], - [iq_get_unique(From)]}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)); - #iq{} -> - Err = jlib:make_error_reply( - Packet, - ?ERR_FEATURE_NOT_IMPLEMENTED), - ejabberd_router:route(To, From, Err); - _ -> - ok + <<"">> -> + case Nick of + <<"">> -> + case Name of + <<"iq">> -> + case jlib:iq_query_info(Packet) of + #iq{type = get, xmlns = (?NS_DISCO_INFO) = XMLNS, + sub_el = _SubEl, lang = Lang} = + IQ -> + Info = ejabberd_hooks:run_fold(disco_info, + ServerHost, [], + [ServerHost, ?MODULE, + <<"">>, <<"">>]), + Res = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, XMLNS}], + children = + iq_disco_info(Lang) ++ + Info}]}, + ejabberd_router:route(To, From, + jlib:iq_to_xml(Res)); + #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ -> + spawn(?MODULE, process_iq_disco_items, + [Host, From, To, IQ]); + #iq{type = get, xmlns = (?NS_REGISTER) = XMLNS, + lang = Lang, sub_el = _SubEl} = + IQ -> + Res = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, XMLNS}], + children = + iq_get_register_info(ServerHost, + Host, + From, + Lang)}]}, + ejabberd_router:route(To, From, + jlib:iq_to_xml(Res)); + #iq{type = set, xmlns = (?NS_REGISTER) = XMLNS, + lang = Lang, sub_el = SubEl} = + IQ -> + case process_iq_register_set(ServerHost, Host, From, + SubEl, Lang) + of + {result, IQRes} -> + Res = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"query">>, + attrs = + [{<<"xmlns">>, + XMLNS}], + children = IQRes}]}, + ejabberd_router:route(To, From, + jlib:iq_to_xml(Res)); + {error, Error} -> + Err = jlib:make_error_reply(Packet, Error), + ejabberd_router:route(To, From, Err) end; - "message" -> - case xml:get_attr_s("type", Attrs) of - "error" -> - ok; - _ -> - case acl:match_rule(ServerHost, AccessAdmin, From) of - allow -> - Msg = xml:get_path_s( - Packet, - [{elem, "body"}, cdata]), - broadcast_service_message(Host, Msg); - _ -> - Lang = xml:get_attr_s("xml:lang", Attrs), - ErrText = "Only service administrators " - "are allowed to send service messages", - Err = jlib:make_error_reply( - Packet, - ?ERRT_FORBIDDEN(Lang, ErrText)), - ejabberd_router:route( - To, From, Err) - end - end; - "presence" -> - ok - end; - _ -> - case xml:get_attr_s("type", Attrs) of - "error" -> - ok; - "result" -> - ok; + #iq{type = get, xmlns = (?NS_VCARD) = XMLNS, + lang = Lang, sub_el = _SubEl} = + IQ -> + Res = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"vCard">>, + attrs = + [{<<"xmlns">>, XMLNS}], + children = + iq_get_vcard(Lang)}]}, + ejabberd_router:route(To, From, + jlib:iq_to_xml(Res)); + #iq{type = get, xmlns = ?NS_MUC_UNIQUE} = IQ -> + Res = IQ#iq{type = result, + sub_el = + [#xmlel{name = <<"unique">>, + attrs = + [{<<"xmlns">>, + ?NS_MUC_UNIQUE}], + children = + [iq_get_unique(From)]}]}, + ejabberd_router:route(To, From, + jlib:iq_to_xml(Res)); + #iq{} -> + Err = jlib:make_error_reply(Packet, + ?ERR_FEATURE_NOT_IMPLEMENTED), + ejabberd_router:route(To, From, Err); + _ -> ok + end; + <<"message">> -> + case xml:get_attr_s(<<"type">>, Attrs) of + <<"error">> -> ok; _ -> - Err = jlib:make_error_reply( - Packet, ?ERR_ITEM_NOT_FOUND), - ejabberd_router:route(To, From, Err) - end - end; - _ -> - case mnesia:dirty_read(muc_online_room, {Room, Host}) of - [] -> - Type = xml:get_attr_s("type", Attrs), - case {Name, Type} of - {"presence", ""} -> - case check_user_can_create_room(ServerHost, - AccessCreate, From, - Room) of - true -> - case start_new_room( - Host, ServerHost, Access, - Room, HistorySize, PersistHistory, - RoomShaper, From, - Nick, DefRoomOpts) of - {ok, Pid} -> - mod_muc_room:route(Pid, From, Nick, Packet), - register_room(Host, Room, Pid), - ok; - _Err -> - Err = jlib:make_error_reply( - Packet, ?ERR_INTERNAL_SERVER_ERROR), - ejabberd_router:route(To, From, Err) - end; - false -> - Lang = xml:get_attr_s("xml:lang", Attrs), - ErrText = "Room creation is denied by service policy", - Err = jlib:make_error_reply( - Packet, ?ERRT_FORBIDDEN(Lang, ErrText)), - ejabberd_router:route(To, From, Err) + case acl:match_rule(ServerHost, AccessAdmin, From) + of + allow -> + Msg = xml:get_path_s(Packet, + [{elem, <<"body">>}, + cdata]), + broadcast_service_message(Host, Msg); + _ -> + Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), + ErrText = + <<"Only service administrators are allowed " + "to send service messages">>, + Err = jlib:make_error_reply(Packet, + ?ERRT_FORBIDDEN(Lang, + ErrText)), + ejabberd_router:route(To, From, Err) + end + end; + <<"presence">> -> ok + end; + _ -> + case xml:get_attr_s(<<"type">>, Attrs) of + <<"error">> -> ok; + <<"result">> -> ok; + _ -> + Err = jlib:make_error_reply(Packet, + ?ERR_ITEM_NOT_FOUND), + ejabberd_router:route(To, From, Err) + end + end; + _ -> + case mnesia:dirty_read(muc_online_room, {Room, Host}) of + [] -> + Type = xml:get_attr_s(<<"type">>, Attrs), + case {Name, Type} of + {<<"presence">>, <<"">>} -> + case check_user_can_create_room(ServerHost, + AccessCreate, From, Room) + of + true -> + case start_new_room(Host, ServerHost, Access, Room, + HistorySize, PersistHistory, + RoomShaper, From, Nick, + DefRoomOpts) + of + {ok, Pid} -> + mod_muc_room:route(Pid, From, Nick, Packet), + register_room(Host, Room, Pid), + ok; + _Err -> + Err = jlib:make_error_reply(Packet, + ?ERR_INTERNAL_SERVER_ERROR), + ejabberd_router:route(To, From, Err) end; - _ -> - Lang = xml:get_attr_s("xml:lang", Attrs), - ErrText = "Conference room does not exist", - Err = jlib:make_error_reply( - Packet, ?ERRT_ITEM_NOT_FOUND(Lang, ErrText)), + false -> + Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), + ErrText = + <<"Room creation is denied by service policy">>, + Err = jlib:make_error_reply(Packet, + ?ERRT_FORBIDDEN(Lang, + ErrText)), ejabberd_router:route(To, From, Err) - end; - [R] -> - Pid = R#muc_online_room.pid, - ?DEBUG("MUC: send to process ~p~n", [Pid]), - mod_muc_room:route(Pid, From, Nick, Packet), - ok - end + end; + _ -> + Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), + ErrText = <<"Conference room does not exist">>, + Err = jlib:make_error_reply(Packet, + ?ERRT_ITEM_NOT_FOUND(Lang, + ErrText)), + ejabberd_router:route(To, From, Err) + end; + [R] -> + Pid = R#muc_online_room.pid, + ?DEBUG("MUC: send to process ~p~n", [Pid]), + mod_muc_room:route(Pid, From, Nick, Packet), + ok + end end. -check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) -> +check_user_can_create_room(ServerHost, AccessCreate, + From, RoomID) -> case acl:match_rule(ServerHost, AccessCreate, From) of - allow -> - (length(RoomID) =< gen_mod:get_module_opt(ServerHost, ?MODULE, - max_room_id, infinite)); - _ -> - false + allow -> + byte_size(RoomID) =< + gen_mod:get_module_opt(ServerHost, ?MODULE, max_room_id, + fun(infinity) -> infinity; + (I) when is_integer(I), I>0 -> I + end, infinity); + _ -> false end. get_rooms(ServerHost, Host) -> LServer = jlib:nameprep(ServerHost), - get_rooms(LServer, Host, gen_mod:db_type(LServer, ?MODULE)). + 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 + 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, odbc) -> SHost = ejabberd_odbc:escape(Host), - case catch ejabberd_odbc:sql_query( - LServer, ["select name, opts from muc_room ", - "where host='", SHost, "';"]) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p", [Reason]), - []; - {selected, ["name", "opts"], RoomOpts} -> - lists:map( - fun({Room, Opts}) -> - #muc_room{name_host = {Room, Host}, - opts = ejabberd_odbc:decode_term(Opts)} - end, RoomOpts) + case catch ejabberd_odbc:sql_query(LServer, + [<<"select name, opts from muc_room ">>, + <<"where host='">>, SHost, <<"';">>]) + of + {'EXIT', Reason} -> ?ERROR_MSG("~p", [Reason]), []; + {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) end. -load_permanent_rooms(Host, ServerHost, Access, HistorySize, PersistHistory, RoomShaper) -> - lists:foreach( - fun(R) -> - {Room, Host} = R#muc_room.name_host, - case get_node({Room, Host}) of - Node when Node == node() -> - case mnesia:dirty_read(muc_online_room, {Room, Host}) of - [] -> - case get_room_state_if_broadcasted( - {Room, Host}) of - {ok, RoomState} -> - mod_muc_room:start( - normal_state, RoomState); - error -> - {ok, Pid} = mod_muc_room:start( - Host, - ServerHost, - Access, - Room, - HistorySize, - PersistHistory, - RoomShaper, - R#muc_room.opts), - register_room(Host, Room, Pid); - _ -> - ok - end; - _ -> - ok - end; - _ -> - ok - end - end, get_rooms(ServerHost, Host)). +load_permanent_rooms(Host, ServerHost, Access, + HistorySize, PersistHistory, RoomShaper) -> + lists:foreach(fun (R) -> + {Room, Host} = R#muc_room.name_host, + case get_node({Room, Host}) of + Node when Node == node() -> + case mnesia:dirty_read(muc_online_room, + {Room, Host}) + of + [] -> + case get_room_state_if_broadcasted({Room, + Host}) + of + {ok, RoomState} -> + mod_muc_room:start(normal_state, + RoomState); + error -> + {ok, Pid} = mod_muc_room:start(Host, + ServerHost, + Access, + Room, + HistorySize, + PersistHistory, + RoomShaper, + R#muc_room.opts), + register_room(Host, Room, Pid); + _ -> ok + end; + _ -> ok + end; + _ -> ok + end + end, + get_rooms(ServerHost, Host)). start_new_room(Host, ServerHost, Access, Room, - HistorySize, PersistHistory, RoomShaper, From, - Nick, DefRoomOpts) -> + HistorySize, PersistHistory, RoomShaper, From, Nick, + DefRoomOpts) -> case get_room_state_if_broadcasted({Room, Host}) of - {ok, RoomState} -> - ?DEBUG("MUC: restore room '~s' from other node~n", [Room]), - mod_muc_room:start(normal_state, RoomState); - error -> - case restore_room(ServerHost, Room, Host) of - error -> - ?DEBUG("MUC: open new room '~s'~n", [Room]), - mod_muc_room:start(Host, ServerHost, Access, - Room, HistorySize, PersistHistory, - RoomShaper, From, - Nick, DefRoomOpts); - Opts -> - ?DEBUG("MUC: restore room '~s'~n", [Room]), - mod_muc_room:start(Host, ServerHost, Access, - Room, HistorySize, PersistHistory, - RoomShaper, Opts) - end + {ok, RoomState} -> + ?DEBUG("MUC: restore room '~s' from other node~n", + [Room]), + mod_muc_room:start(normal_state, RoomState); + error -> + case restore_room(ServerHost, Room, Host) of + error -> + ?DEBUG("MUC: open new room '~s'~n", [Room]), + mod_muc_room:start(Host, ServerHost, Access, Room, + HistorySize, PersistHistory, RoomShaper, + From, Nick, DefRoomOpts); + Opts -> + ?DEBUG("MUC: restore room '~s'~n", [Room]), + mod_muc_room:start(Host, ServerHost, Access, Room, + HistorySize, PersistHistory, RoomShaper, + Opts) + end end. register_room(Host, Room, Pid) -> - F = fun() -> - mnesia:write(#muc_online_room{name_host = {Room, Host}, - pid = Pid}) - end, + F = fun () -> + mnesia:write(#muc_online_room{name_host = {Room, Host}, + pid = Pid}) + end, mnesia:async_dirty(F), case get_node_new({Room, Host}) of - Node when Node /= node() -> - %% New node has just been added. But we may miss MUC records - %% copy procedure, so we copy the MUC record manually just - %% to make sure - rpc:cast(Node, mnesia, dirty_write, - [#muc_online_room{name_host = {Room, Host}, - pid = Pid}]), - case get_node({Room, Host}) of - Node when node() /= Node -> - %% Migration to new node has completed, and seems like - %% we missed it, so we migrate the MUC room pid manually. - %% It is not a problem if we have already got migration - %% notification: dups are just ignored by the MUC room pid. - mod_muc_room:migrate(Pid, Node, 0); - _ -> - ok - end; - _ -> - ok + Node when Node /= node() -> + rpc:cast(Node, mnesia, dirty_write, + [#muc_online_room{name_host = {Room, Host}, + pid = Pid}]), + case get_node({Room, Host}) of + Node when node() /= Node -> + mod_muc_room:migrate(Pid, Node, 0); + _ -> ok + end; + _ -> ok end. iq_disco_info(Lang) -> - [{xmlelement, "identity", - [{"category", "conference"}, - {"type", "text"}, - {"name", translate:translate(Lang, "Chatrooms")}], []}, - {xmlelement, "feature", [{"var", ?NS_DISCO_INFO}], []}, - {xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []}, - {xmlelement, "feature", [{"var", ?NS_MUC}], []}, - {xmlelement, "feature", [{"var", ?NS_MUC_UNIQUE}], []}, - {xmlelement, "feature", [{"var", ?NS_REGISTER}], []}, - {xmlelement, "feature", [{"var", ?NS_RSM}], []}, - {xmlelement, "feature", [{"var", ?NS_VCARD}], []}]. - + [#xmlel{name = <<"identity">>, + attrs = + [{<<"category">>, <<"conference">>}, + {<<"type">>, <<"text">>}, + {<<"name">>, + translate:translate(Lang, <<"Chatrooms">>)}], + children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_DISCO_INFO}], children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_DISCO_ITEMS}], children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_MUC}], children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_MUC_UNIQUE}], children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_REGISTER}], children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_RSM}], children = []}, + #xmlel{name = <<"feature">>, + attrs = [{<<"var">>, ?NS_VCARD}], children = []}]. iq_disco_items(Host, From, Lang, none) -> - lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) -> - case catch gen_fsm:sync_send_all_state_event( - Pid, {get_disco_item, From, Lang}, 100) of - {item, Desc} -> - flush(), - {true, - {xmlelement, "item", - [{"jid", jlib:jid_to_string({Name, Host, ""})}, - {"name", Desc}], []}}; - _ -> - false + lists:zf(fun (#muc_online_room{name_host = + {Name, _Host}, + pid = Pid}) -> + case catch gen_fsm:sync_send_all_state_event(Pid, + {get_disco_item, + From, Lang}, + 100) + of + {item, Desc} -> + flush(), + {true, + #xmlel{name = <<"item">>, + attrs = + [{<<"jid">>, + jlib:jid_to_string({Name, Host, + <<"">>})}, + {<<"name">>, Desc}], + children = []}}; + _ -> false end - end, get_vh_rooms_all_nodes(Host)); - + end, + get_vh_rooms_all_nodes(Host)); iq_disco_items(Host, From, Lang, Rsm) -> {Rooms, RsmO} = get_vh_rooms(Host, Rsm), RsmOut = jlib:rsm_encode(RsmO), - lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) -> - case catch gen_fsm:sync_send_all_state_event( - Pid, {get_disco_item, From, Lang}, 100) of - {item, Desc} -> - flush(), - {true, - {xmlelement, "item", - [{"jid", jlib:jid_to_string({Name, Host, ""})}, - {"name", Desc}], []}}; - _ -> - false + lists:zf(fun (#muc_online_room{name_host = + {Name, _Host}, + pid = Pid}) -> + case catch gen_fsm:sync_send_all_state_event(Pid, + {get_disco_item, + From, Lang}, + 100) + of + {item, Desc} -> + flush(), + {true, + #xmlel{name = <<"item">>, + attrs = + [{<<"jid">>, + jlib:jid_to_string({Name, Host, + <<"">>})}, + {<<"name">>, Desc}], + children = []}}; + _ -> false end - end, Rooms) ++ RsmOut. + end, + Rooms) + ++ RsmOut. -get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})-> +get_vh_rooms(Host, + #rsm_in{max = M, direction = Direction, id = I, + index = Index}) -> AllRooms = get_vh_rooms_all_nodes(Host), Count = erlang:length(AllRooms), - L = get_vh_rooms_direction(Direction, I, Index, AllRooms), - L2 = if - Index == undefined andalso Direction == before -> - lists:reverse(lists:sublist(lists:reverse(L), 1, M)); - Index == undefined -> - lists:sublist(L, 1, M); - Index > Count orelse Index < 0 -> - []; - true -> - lists:sublist(L, Index+1, M) + L = get_vh_rooms_direction(Direction, I, Index, + AllRooms), + L2 = if Index == undefined andalso + Direction == before -> + lists:reverse(lists:sublist(lists:reverse(L), 1, M)); + Index == undefined -> lists:sublist(L, 1, M); + Index > Count orelse Index < 0 -> []; + true -> lists:sublist(L, Index + 1, M) end, - if - L2 == [] -> - {L2, #rsm_out{count=Count}}; - true -> - H = hd(L2), - NewIndex = get_room_pos(H, AllRooms), - T=lists:last(L2), - {F, _}=H#muc_online_room.name_host, - {Last, _}=T#muc_online_room.name_host, - {L2, #rsm_out{first=F, last=Last, count=Count, index=NewIndex}} + if L2 == [] -> {L2, #rsm_out{count = Count}}; + true -> + H = hd(L2), + NewIndex = get_room_pos(H, AllRooms), + T = lists:last(L2), + {F, _} = H#muc_online_room.name_host, + {Last, _} = T#muc_online_room.name_host, + {L2, + #rsm_out{first = F, last = Last, count = Count, + index = NewIndex}} end. -get_vh_rooms_direction(_Direction, _I, Index, AllRooms) when Index =/= undefined -> - AllRooms; +get_vh_rooms_direction(_Direction, _I, Index, AllRooms) + when Index =/= undefined -> + AllRooms; get_vh_rooms_direction(aft, I, _Index, AllRooms) -> - {_Before, After} = - lists:splitwith( - fun(#muc_online_room{name_host = {Na, _}}) -> - Na < I end, AllRooms), + {_Before, After} = lists:splitwith(fun + (#muc_online_room{name_host = + {Na, _}}) -> + Na < I + end, + AllRooms), case After of - [] -> []; - [#muc_online_room{name_host = {I, _Host}} | AfterTail] -> AfterTail; - _ -> After + [] -> []; + [#muc_online_room{name_host = {I, _Host}} + | AfterTail] -> + AfterTail; + _ -> After end; -get_vh_rooms_direction(before, I, _Index, AllRooms) when I =/= []-> - {Before, _} = - lists:splitwith( - fun(#muc_online_room{name_host = {Na, _}}) -> - Na < I end, AllRooms), +get_vh_rooms_direction(before, I, _Index, AllRooms) + when I =/= [] -> + {Before, _} = lists:splitwith(fun + (#muc_online_room{name_host = {Na, _}}) -> + Na < I + end, + AllRooms), Before; -get_vh_rooms_direction(_Direction, _I, _Index, AllRooms) -> +get_vh_rooms_direction(_Direction, _I, _Index, + AllRooms) -> AllRooms. -%% @doc Return the position of desired room in the list of rooms. -%% The room must exist in the list. The count starts in 0. -%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer() get_room_pos(Desired, Rooms) -> get_room_pos(Desired, Rooms, 0). + get_room_pos(Desired, [HeadRoom | _], HeadPosition) - when (Desired#muc_online_room.name_host == - HeadRoom#muc_online_room.name_host) -> + when Desired#muc_online_room.name_host == + HeadRoom#muc_online_room.name_host -> HeadPosition; get_room_pos(Desired, [_ | Rooms], HeadPosition) -> get_room_pos(Desired, Rooms, HeadPosition + 1). -flush() -> - receive - _ -> - flush() - after 0 -> - ok - end. +flush() -> receive _ -> flush() after 0 -> ok end. -define(XFIELD(Type, Label, Var, Val), - {xmlelement, "field", [{"type", Type}, - {"label", translate:translate(Lang, Label)}, - {"var", Var}], - [{xmlelement, "value", [], [{xmlcdata, Val}]}]}). - -%% @doc Get a pseudo unique Room Name. The Room Name is generated as a hash of -%% the requester JID, the local time and a random salt. -%% -%% "pseudo" because we don't verify that there is not a room -%% with the returned Name already created, nor mark the generated Name -%% as "already used". But in practice, it is unique enough. See -%% http://xmpp.org/extensions/xep-0045.html#createroom-unique + #xmlel{name = <<"field">>, + attrs = + [{<<"type">>, Type}, + {<<"label">>, translate:translate(Lang, Label)}, + {<<"var">>, Var}], + children = + [#xmlel{name = <<"value">>, attrs = [], + children = [{xmlcdata, Val}]}]}). + iq_get_unique(From) -> - {xmlcdata, sha:sha(term_to_binary([From, now(), randoms:get_string()]))}. + {xmlcdata, + sha:sha(term_to_binary([From, now(), + randoms:get_string()]))}. get_nick(ServerHost, Host, From) -> LServer = jlib:nameprep(ServerHost), - get_nick(LServer, Host, From, gen_mod:db_type(LServer, ?MODULE)). + get_nick(LServer, Host, From, + gen_mod:db_type(LServer, ?MODULE)). get_nick(_LServer, Host, From, mnesia) -> {LUser, LServer, _} = jlib: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 + 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, odbc) -> - SJID = ejabberd_odbc:escape( - jlib:jid_to_string( - jlib:jid_tolower( - jlib:jid_remove_resource(From)))), + SJID = + ejabberd_odbc:escape(jlib:jid_to_string(jlib:jid_tolower(jlib: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 + case catch ejabberd_odbc:sql_query(LServer, + [<<"select nick from muc_registered where " + "jid='">>, + SJID, <<"' and host='">>, SHost, + <<"';">>]) + of + {selected, [<<"nick">>], [[Nick]]} -> Nick; + _ -> error end. iq_get_register_info(ServerHost, Host, From, Lang) -> - {Nick, Registered} = - case get_nick(ServerHost, Host, From) of - error -> - {"", []}; - N -> - {N, [{xmlelement, "registered", [], []}]} - end, + {Nick, Registered} = case get_nick(ServerHost, Host, + From) + of + error -> {<<"">>, []}; + N -> + {N, + [#xmlel{name = <<"registered">>, attrs = [], + children = []}]} + end, Registered ++ - [{xmlelement, "instructions", [], - [{xmlcdata, - translate:translate( - Lang, "You need a client that supports x:data to register the nickname")}]}, - {xmlelement, "x", - [{"xmlns", ?NS_XDATA}], - [{xmlelement, "title", [], - [{xmlcdata, - translate:translate( - Lang, "Nickname Registration at ") ++ Host}]}, - {xmlelement, "instructions", [], - [{xmlcdata, - translate:translate( - Lang, "Enter nickname you want to register")}]}, - ?XFIELD("text-single", "Nickname", "nick", Nick)]}]. + [#xmlel{name = <<"instructions">>, attrs = [], + children = + [{xmlcdata, + translate:translate(Lang, + <<"You need a client that supports x:data " + "to register the nickname">>)}]}, + #xmlel{name = <<"x">>, + attrs = [{<<"xmlns">>, ?NS_XDATA}], + children = + [#xmlel{name = <<"title">>, attrs = [], + children = + [{xmlcdata, + <<(translate:translate(Lang, + <<"Nickname Registration at ">>))/binary, + Host/binary>>}]}, + #xmlel{name = <<"instructions">>, attrs = [], + children = + [{xmlcdata, + translate:translate(Lang, + <<"Enter nickname you want to register">>)}]}, + ?XFIELD(<<"text-single">>, <<"Nickname">>, <<"nick">>, + Nick)]}]. set_nick(ServerHost, Host, From, Nick) -> LServer = jlib:nameprep(ServerHost), - set_nick(LServer, Host, From, Nick, gen_mod:db_type(LServer, ?MODULE)). + set_nick(LServer, Host, From, Nick, + gen_mod:db_type(LServer, ?MODULE)). set_nick(_LServer, Host, From, Nick, mnesia) -> {LUser, LServer, _} = jlib:jid_tolower(From), LUS = {LUser, LServer}, - F = fun() -> + 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; + <<"">> -> + 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, + 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, odbc) -> - JID = jlib:jid_to_string( - jlib:jid_tolower( - jlib:jid_remove_resource(From))), + JID = + jlib:jid_to_string(jlib:jid_tolower(jlib: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, + F = fun () -> + case Nick of + <<"">> -> + ejabberd_odbc:sql_query_t([<<"delete from muc_registered where ">>, + <<"jid='">>, SJID, + <<"' and host='">>, Host, + <<"';">>]), + ok; + _ -> + Allow = case + ejabberd_odbc:sql_query_t([<<"select jid from muc_registered ">>, + <<"where nick='">>, + SNick, + <<"' and host='">>, + SHost, <<"';">>]) + of + {selected, [<<"jid">>], [[J]]} -> J == JID; + _ -> true + end, + if Allow -> + odbc_queries:update_t(<<"muc_registered">>, + [<<"jid">>, <<"host">>, + <<"nick">>], + [SJID, SHost, SNick], + [<<"jid='">>, SJID, + <<"' and host='">>, SHost, + <<"'">>]), + ok; + true -> false + end + end + end, ejabberd_odbc:sql_transaction(LServer, F). -iq_set_register_info(ServerHost, Host, From, Nick, Lang) -> +iq_set_register_info(ServerHost, Host, From, Nick, + Lang) -> case set_nick(ServerHost, Host, From, Nick) of - {atomic, ok} -> - {result, []}; - {atomic, false} -> - ErrText = "That nickname is registered by another person", - {error, ?ERRT_CONFLICT(Lang, ErrText)}; - _ -> - {error, ?ERR_INTERNAL_SERVER_ERROR} + {atomic, ok} -> {result, []}; + {atomic, false} -> + ErrText = <<"That nickname is registered by another " + "person">>, + {error, ?ERRT_CONFLICT(Lang, ErrText)}; + _ -> {error, ?ERR_INTERNAL_SERVER_ERROR} end. -process_iq_register_set(ServerHost, Host, From, SubEl, Lang) -> - {xmlelement, _Name, _Attrs, Els} = SubEl, - case xml:get_subtag(SubEl, "remove") of - false -> - case xml:remove_cdata(Els) of - [{xmlelement, "x", _Attrs1, _Els1} = XEl] -> - case {xml:get_tag_attr_s("xmlns", XEl), - xml:get_tag_attr_s("type", XEl)} of - {?NS_XDATA, "cancel"} -> - {result, []}; - {?NS_XDATA, "submit"} -> - XData = jlib:parse_xdata_submit(XEl), - case XData of - invalid -> - {error, ?ERR_BAD_REQUEST}; - _ -> - case lists:keysearch("nick", 1, XData) of - {value, {_, [Nick]}} when Nick /= "" -> - iq_set_register_info(ServerHost, Host, - From, Nick, Lang); - _ -> - ErrText = "You must fill in field \"Nickname\" in the form", - {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)} - end - end; +process_iq_register_set(ServerHost, Host, From, SubEl, + Lang) -> + #xmlel{children = Els} = SubEl, + case xml:get_subtag(SubEl, <<"remove">>) of + false -> + case xml:remove_cdata(Els) of + [#xmlel{name = <<"x">>} = XEl] -> + case {xml:get_tag_attr_s(<<"xmlns">>, XEl), + xml:get_tag_attr_s(<<"type">>, XEl)} + of + {?NS_XDATA, <<"cancel">>} -> {result, []}; + {?NS_XDATA, <<"submit">>} -> + XData = jlib:parse_xdata_submit(XEl), + case XData of + invalid -> {error, ?ERR_BAD_REQUEST}; _ -> - {error, ?ERR_BAD_REQUEST} - end; - _ -> - {error, ?ERR_BAD_REQUEST} - end; - _ -> - iq_set_register_info(ServerHost, Host, From, "", Lang) + case lists:keysearch(<<"nick">>, 1, XData) of + {value, {_, [Nick]}} when Nick /= <<"">> -> + iq_set_register_info(ServerHost, Host, From, + Nick, Lang); + _ -> + ErrText = + <<"You must fill in field \"Nickname\" " + "in the form">>, + {error, ?ERRT_NOT_ACCEPTABLE(Lang, ErrText)} + end + end; + _ -> {error, ?ERR_BAD_REQUEST} + end; + _ -> {error, ?ERR_BAD_REQUEST} + end; + _ -> + iq_set_register_info(ServerHost, Host, From, <<"">>, + Lang) end. iq_get_vcard(Lang) -> - [{xmlelement, "FN", [], - [{xmlcdata, "ejabberd/mod_muc"}]}, - {xmlelement, "URL", [], - [{xmlcdata, ?EJABBERD_URI}]}, - {xmlelement, "DESC", [], - [{xmlcdata, translate:translate(Lang, "ejabberd MUC module") ++ - "\nCopyright (c) 2003-2012 ProcessOne"}]}]. - + [#xmlel{name = <<"FN">>, attrs = [], + children = [{xmlcdata, <<"ejabberd/mod_muc">>}]}, + #xmlel{name = <<"URL">>, attrs = [], + children = [{xmlcdata, ?EJABBERD_URI}]}, + #xmlel{name = <<"DESC">>, attrs = [], + children = + [{xmlcdata, + <<(translate:translate(Lang, + <<"ejabberd MUC module">>))/binary, + "\nCopyright (c) 2003-2012 ProcessOne">>}]}]. broadcast_service_message(Host, Msg) -> - lists:foreach( - fun(#muc_online_room{pid = Pid}) -> - gen_fsm:send_all_state_event( - Pid, {service_message, Msg}) - end, get_vh_rooms_all_nodes(Host)). + lists:foreach(fun (#muc_online_room{pid = Pid}) -> + gen_fsm:send_all_state_event(Pid, + {service_message, Msg}) + end, + get_vh_rooms_all_nodes(Host)). get_vh_rooms_all_nodes(Host) -> - Rooms = lists:foldl( - fun(Node, Acc) when Node == node() -> - get_vh_rooms(Host) ++ Acc; - (Node, Acc) -> - case catch rpc:call(Node, ?MODULE, get_vh_rooms, - [Host], 5000) of - Res when is_list(Res) -> - Res ++ Acc; - _ -> - Acc - end - end, [], get_nodes(Host)), + Rooms = lists:foldl(fun (Node, Acc) + when Node == node() -> + get_vh_rooms(Host) ++ Acc; + (Node, Acc) -> + case catch rpc:call(Node, ?MODULE, get_vh_rooms, + [Host], 5000) + of + Res when is_list(Res) -> Res ++ Acc; + _ -> Acc + end + end, + [], get_nodes(Host)), lists:ukeysort(#muc_online_room.name_host, Rooms). get_vh_rooms(Host) -> mnesia:dirty_select(muc_online_room, [{#muc_online_room{name_host = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, Host}], - ['$_']}]). - -update_tables(Host) -> - update_muc_room_table(Host), - update_muc_registered_table(Host). + [{'==', {element, 2, '$1'}, Host}], ['$_']}]). + +opts_to_binary(Opts) -> + lists:map( + fun({title, Title}) -> + {title, iolist_to_binary(Title)}; + ({description, Desc}) -> + {description, iolist_to_binary(Desc)}; + ({password, Pass}) -> + {password, iolist_to_binary(Pass)}; + ({subject, Subj}) -> + {subject, iolist_to_binary(Subj)}; + ({subject_author, Author}) -> + {subject_author, iolist_to_binary(Author)}; + ({affiliations, Affs}) -> + {affiliations, lists:map( + fun({{U, S, R}, Aff}) -> + NewAff = + case Aff of + {A, Reason} -> + {A, iolist_to_binary(Reason)}; + _ -> + Aff + end, + {{iolist_to_binary(U), + iolist_to_binary(S), + iolist_to_binary(R)}, + NewAff} + end, Affs)}; + ({captcha_whitelist, CWList}) -> + {captcha_whitelist, lists:map( + fun({U, S, R}) -> + {iolist_to_binary(U), + iolist_to_binary(S), + iolist_to_binary(R)} + end, CWList)}; + (Opt) -> + Opt + end, Opts). + +update_tables() -> + update_muc_online_table(), + update_muc_room_table(), + update_muc_registered_table(). update_muc_online_table() -> - case catch mnesia:table_info(muc_online_room, local_content) of - false -> - mnesia:delete_table(muc_online_room); - _ -> - ok + case catch mnesia:table_info(muc_online_room, + local_content) + of + false -> mnesia:delete_table(muc_online_room); + _ -> ok end. -update_muc_room_table(Host) -> +update_muc_room_table() -> Fields = record_info(fields, muc_room), case mnesia:table_info(muc_room, attributes) of - Fields -> - ok; - [name, opts] -> - ?INFO_MSG("Converting muc_room table from " - "{name, opts} format", []), - {atomic, ok} = mnesia:create_table( - mod_muc_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, muc_room}, - {attributes, record_info(fields, muc_room)}]), - mnesia:transform_table(muc_room, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_muc_tmp_table), - mnesia:foldl( - fun(#muc_room{name_host = Name} = R, _) -> - mnesia:dirty_write( - mod_muc_tmp_table, - R#muc_room{name_host = {Name, Host}}) - end, ok, muc_room) - end, - mnesia:transaction(F1), - mnesia:clear_table(muc_room), - F2 = fun() -> - mnesia:write_lock_table(muc_room), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_muc_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_muc_tmp_table); - _ -> - ?INFO_MSG("Recreating muc_room table", []), - mnesia:transform_table(muc_room, ignore, Fields) + 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) -> +update_muc_registered_table() -> Fields = record_info(fields, muc_registered), case mnesia:table_info(muc_registered, attributes) of - Fields -> - ok; - [user, nick] -> - ?INFO_MSG("Converting muc_registered table from " - "{user, nick} format", []), - {atomic, ok} = mnesia:create_table( - mod_muc_tmp_table, - [{disc_only_copies, [node()]}, - {type, bag}, - {local_content, true}, - {record_name, muc_registered}, - {attributes, record_info(fields, muc_registered)}]), - mnesia:del_table_index(muc_registered, nick), - mnesia:transform_table(muc_registered, ignore, Fields), - F1 = fun() -> - mnesia:write_lock_table(mod_muc_tmp_table), - mnesia:foldl( - fun(#muc_registered{us_host = US} = R, _) -> - mnesia:dirty_write( - mod_muc_tmp_table, - R#muc_registered{us_host = {US, Host}}) - end, ok, muc_registered) - end, - mnesia:transaction(F1), - mnesia:clear_table(muc_registered), - F2 = fun() -> - mnesia:write_lock_table(muc_registered), - mnesia:foldl( - fun(R, _) -> - mnesia:dirty_write(R) - end, ok, mod_muc_tmp_table) - end, - mnesia:transaction(F2), - mnesia:delete_table(mod_muc_tmp_table); - _ -> - ?INFO_MSG("Recreating muc_registered table", []), - mnesia:transform_table(muc_registered, ignore, Fields) + 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. is_broadcasted(RoomHost) -> - case ejabberd_config:get_local_option({domain_balancing, RoomHost}) of - broadcast -> - true; - _ -> - false + case ejabberd_router:get_domain_balancing(RoomHost) of + broadcast -> true; + _ -> false end. get_node({_, RoomHost} = Key) -> case is_broadcasted(RoomHost) of - true -> - node(); - false -> - ejabberd_cluster:get_node(Key) + true -> node(); + false -> ejabberd_cluster:get_node(Key) end; -get_node(RoomHost) -> - get_node({"", RoomHost}). +get_node(RoomHost) -> get_node({<<"">>, RoomHost}). get_node_new({_, RoomHost} = Key) -> case is_broadcasted(RoomHost) of - true -> - node(); - false -> - ejabberd_cluster:get_node_new(Key) + true -> node(); + false -> ejabberd_cluster:get_node_new(Key) end; get_node_new(RoomHost) -> - get_node_new({"", RoomHost}). + get_node_new({<<"">>, RoomHost}). get_nodes(RoomHost) -> case is_broadcasted(RoomHost) of - true -> - [node()]; - false -> - ejabberd_cluster:get_nodes() + true -> [node()]; + false -> ejabberd_cluster:get_nodes() end. get_room_state_if_broadcasted({Room, Host}) -> case is_broadcasted(Host) of - true -> - lists:foldl( - fun(_, {ok, StateData}) -> - {ok, StateData}; - (Node, _) when Node /= node() -> - case catch rpc:call( - Node, mnesia, dirty_read, - [muc_online_room, {Room, Host}], 5000) of - [#muc_online_room{pid = Pid}] -> - case catch gen_fsm:sync_send_all_state_event( - Pid, get_state, 5000) of - {ok, StateData} -> - {ok, StateData}; - _ -> - error - end; - _ -> - error - end; - (_, Acc) -> - Acc - end, error, ejabberd_cluster:get_nodes()); - false -> - error + true -> + lists:foldl(fun (_, {ok, StateData}) -> {ok, StateData}; + (Node, _) when Node /= node() -> + case catch rpc:call(Node, mnesia, dirty_read, + [muc_online_room, + {Room, Host}], + 5000) + of + [#muc_online_room{pid = Pid}] -> + case catch + gen_fsm:sync_send_all_state_event(Pid, + get_state, + 5000) + of + {ok, StateData} -> {ok, StateData}; + _ -> error + end; + _ -> error + end; + (_, Acc) -> Acc + end, + error, ejabberd_cluster:get_nodes()); + false -> error 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( + jlib:jid_to_string( + jlib:make_jid(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}]. |